]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: add L3/L2VPN Virtual Network Control feature
authorLou Berger <lberger@labn.net>
Sat, 7 May 2016 18:18:56 +0000 (14:18 -0400)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Mon, 3 Oct 2016 12:17:02 +0000 (08:17 -0400)
This feature adds an L3 & L2 VPN application that makes use of the VPN
and Encap SAFIs.  This code is currently used to support IETF NVO3 style
operation.  In NVO3 terminology it provides the Network Virtualization
Authority (NVA) and the ability to import/export IP prefixes and MAC
addresses from Network Virtualization Edges (NVEs).  The code supports
per-NVE tables.

The NVE-NVA protocol used to communicate routing and Ethernet / Layer 2
(L2) forwarding information between NVAs and NVEs is referred to as the
Remote Forwarder Protocol (RFP). OpenFlow is an example RFP.  For
general background on NVO3 and RFP concepts see [1].  For information on
Openflow see [2].

RFPs are integrated with BGP via the RF API contained in the new "rfapi"
BGP sub-directory.  Currently, only a simple example RFP is included in
Quagga. Developers may use this example as a starting point to integrate
Quagga with an RFP of their choosing, e.g., OpenFlow.  The RFAPI code
also supports the ability import/export of routing information between
VNC and customer edge routers (CEs) operating within a virtual
network. Import/export may take place between BGP views or to the
default zebera VRF.

BGP, with IP VPNs and Tunnel Encapsulation, is used to distribute VPN
information between NVAs. BGP based IP VPN support is defined in
RFC4364, BGP/MPLS IP Virtual Private Networks (VPNs), and RFC4659,
BGP-MPLS IP Virtual Private Network (VPN) Extension for IPv6 VPN . Use
of both the Encapsulation Subsequent Address Family Identifier (SAFI)
and the Tunnel Encapsulation Attribute, RFC5512, The BGP Encapsulation
Subsequent Address Family Identifier (SAFI) and the BGP Tunnel
Encapsulation Attribute, are supported. MAC address distribution does
not follow any standard BGB encoding, although it was inspired by the
early IETF EVPN concepts.

The feature is conditionally compiled and disabled by default.
Use the --enable-bgp-vnc configure option to enable.

The majority of this code was authored by G. Paul Ziemba
<paulz@labn.net>.

[1] http://tools.ietf.org/html/draft-ietf-nvo3-nve-nva-cp-req
[2] https://www.opennetworking.org/sdn-resources/technical-library

Now includes changes needed to merge with cmaster-next.

106 files changed:
Makefile.am
bgpd/Makefile.am
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_ecommunity.c
bgpd/bgp_ecommunity.h
bgpd/bgp_encap.c
bgpd/bgp_encap_tlv.c
bgpd/bgp_encap_types.h
bgpd/bgp_main.c
bgpd/bgp_memory.c
bgpd/bgp_memory.h
bgpd/bgp_mplsvpn.c
bgpd/bgp_mplsvpn.h
bgpd/bgp_nexthop.h
bgpd/bgp_route.c
bgpd/bgp_route.h
bgpd/bgp_routemap.c
bgpd/bgp_vnc_types.h [new file with mode: 0644]
bgpd/bgp_zebra.c
bgpd/bgpd.c
bgpd/bgpd.conf.vnc.sample [new file with mode: 0644]
bgpd/bgpd.h
bgpd/rfapi/bgp_rfapi_cfg.c [new file with mode: 0644]
bgpd/rfapi/bgp_rfapi_cfg.h [new file with mode: 0644]
bgpd/rfapi/rfapi.c [new file with mode: 0644]
bgpd/rfapi/rfapi.h [new file with mode: 0644]
bgpd/rfapi/rfapi_ap.c [new file with mode: 0644]
bgpd/rfapi/rfapi_ap.h [new file with mode: 0644]
bgpd/rfapi/rfapi_backend.h [new file with mode: 0644]
bgpd/rfapi/rfapi_descriptor_rfp_utils.c [new file with mode: 0644]
bgpd/rfapi/rfapi_descriptor_rfp_utils.h [new file with mode: 0644]
bgpd/rfapi/rfapi_encap_tlv.c [new file with mode: 0644]
bgpd/rfapi/rfapi_encap_tlv.h [new file with mode: 0644]
bgpd/rfapi/rfapi_import.c [new file with mode: 0644]
bgpd/rfapi/rfapi_import.h [new file with mode: 0644]
bgpd/rfapi/rfapi_monitor.c [new file with mode: 0644]
bgpd/rfapi/rfapi_monitor.h [new file with mode: 0644]
bgpd/rfapi/rfapi_nve_addr.c [new file with mode: 0644]
bgpd/rfapi/rfapi_nve_addr.h [new file with mode: 0644]
bgpd/rfapi/rfapi_private.h [new file with mode: 0644]
bgpd/rfapi/rfapi_rib.c [new file with mode: 0644]
bgpd/rfapi/rfapi_rib.h [new file with mode: 0644]
bgpd/rfapi/rfapi_vty.c [new file with mode: 0644]
bgpd/rfapi/rfapi_vty.h [new file with mode: 0644]
bgpd/rfapi/vnc_debug.c [new file with mode: 0644]
bgpd/rfapi/vnc_debug.h [new file with mode: 0644]
bgpd/rfapi/vnc_export_bgp.c [new file with mode: 0644]
bgpd/rfapi/vnc_export_bgp.h [new file with mode: 0644]
bgpd/rfapi/vnc_export_bgp_p.h [new file with mode: 0644]
bgpd/rfapi/vnc_export_table.c [new file with mode: 0644]
bgpd/rfapi/vnc_export_table.h [new file with mode: 0644]
bgpd/rfapi/vnc_import_bgp.c [new file with mode: 0644]
bgpd/rfapi/vnc_import_bgp.h [new file with mode: 0644]
bgpd/rfapi/vnc_import_bgp_p.h [new file with mode: 0644]
bgpd/rfapi/vnc_zebra.c [new file with mode: 0644]
bgpd/rfapi/vnc_zebra.h [new file with mode: 0644]
bgpd/rfp-example/librfp/Makefile.am [new file with mode: 0644]
bgpd/rfp-example/librfp/rfp.h [new file with mode: 0644]
bgpd/rfp-example/librfp/rfp_example.c [new file with mode: 0644]
bgpd/rfp-example/librfp/rfp_internal.h [new file with mode: 0644]
bgpd/rfp-example/rfptest/Makefile.am [new file with mode: 0644]
bgpd/rfp-example/rfptest/rfptest.c [new file with mode: 0644]
bgpd/rfp-example/rfptest/rfptest.h [new file with mode: 0644]
configure.ac
doc/Makefile.am
doc/bgpd.texi
doc/fig-vnc-commercial-route-reflector.dia [new file with mode: 0644]
doc/fig-vnc-commercial-route-reflector.png [new file with mode: 0644]
doc/fig-vnc-commercial-route-reflector.txt [new file with mode: 0644]
doc/fig-vnc-gw-rr.dia [new file with mode: 0644]
doc/fig-vnc-gw-rr.png [new file with mode: 0644]
doc/fig-vnc-gw-rr.txt [new file with mode: 0644]
doc/fig-vnc-gw.dia [new file with mode: 0644]
doc/fig-vnc-gw.png [new file with mode: 0644]
doc/fig-vnc-gw.txt [new file with mode: 0644]
doc/fig-vnc-mesh.dia [new file with mode: 0644]
doc/fig-vnc-mesh.png [new file with mode: 0644]
doc/fig-vnc-mesh.txt [new file with mode: 0644]
doc/fig-vnc-quagga-route-reflector.dia [new file with mode: 0644]
doc/fig-vnc-quagga-route-reflector.png [new file with mode: 0644]
doc/fig-vnc-quagga-route-reflector.txt [new file with mode: 0644]
doc/fig-vnc-redundant-route-reflectors.dia [new file with mode: 0644]
doc/fig-vnc-redundant-route-reflectors.png [new file with mode: 0644]
doc/fig-vnc-redundant-route-reflectors.txt [new file with mode: 0644]
doc/ospfd.texi
doc/quagga.texi
doc/routemap.texi
doc/vnc.texi [new file with mode: 0644]
lib/command.c
lib/command.h
lib/log.c
lib/log.h
lib/prefix.c
lib/route_types.txt
lib/vty.c
lib/zebra.h
redhat/quagga.spec.in
ripd/rip_zebra.c
ripngd/ripng_zebra.c
tests/Makefile.am
vtysh/Makefile.am
vtysh/extract.pl.in
vtysh/vtysh.c
vtysh/vtysh_config.c
zebra/zserv.c

index 031afac9d134107eb01853cda0fe81f0c5e9b086..d19df6f3b679ae7c25e4b5bbe474e75b9f659032 100644 (file)
@@ -1,12 +1,13 @@
 ## Process this file with automake to produce Makefile.in.
 
-SUBDIRS = lib qpb fpm @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @LDPD@ \
+SUBDIRS = lib qpb fpm @ZEBRA@ @LIBRFP@ @RFPTEST@ \
+        @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @LDPD@ \
          @ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \
          redhat @SOLARIS@ tests tools cumulus
 
 DIST_SUBDIRS = lib qpb fpm zebra bgpd ripd ripngd ospfd ospf6d ldpd \
          isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \
-         solaris pimd tools cumulus
+         solaris pimd @LIBRFP@ @RFPTEST@ tools cumulus
 
 EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \
        update-autotools \
index fb5b2375deb8768baab71e59bd5c2f2fd10ff3ed..ebd7932bd11da0afe9431eefc0781d317e617b2a 100644 (file)
@@ -1,6 +1,64 @@
 ## Process this file with automake to produce Makefile.in.
+AUTOMAKE_OPTIONS = subdir-objects
 
-AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+if ENABLE_BGP_VNC
+#o file to keep linker happy
+BGP_VNC_RFP_LIB=rfapi/rfapi_descriptor_rfp_utils.o @top_srcdir@/$(LIBRFP)/librfp.a 
+BGP_VNC_RFP_INC=-I@top_srcdir@/$(RFPINC)
+BGP_VNC_RFP_HD=\
+       @top_srcdir@/$(RFPINC)/rfp.h
+BGP_VNC_RFP_LD_FLAGS_FILE=@top_srcdir@/$(LIBRFP)/rfp_ld_flags
+BGP_VNC_RFP_LD_FLAGS=`if [ -e  "$(BGP_VNC_RFP_LD_FLAGS_FILE)" ] ; then cat "$(BGP_VNC_RFP_LD_FLAGS_FILE)" ; fi `
+
+#BGP_VNC_RFAPI_SRCDIR=rfapi
+BGP_VNC_RFAPI_SRCDIR=
+BGP_VNC_RFAPI_INC=-Irfapi
+BGP_VNC_RFAPI_SRC=rfapi/bgp_rfapi_cfg.c \
+       rfapi/rfapi_import.c \
+       rfapi/rfapi.c \
+       rfapi/rfapi_ap.c \
+       rfapi/rfapi_descriptor_rfp_utils.c \
+       rfapi/rfapi_encap_tlv.c \
+       rfapi/rfapi_nve_addr.c \
+       rfapi/rfapi_monitor.c \
+       rfapi/rfapi_rib.c \
+       rfapi/rfapi_vty.c \
+       rfapi/vnc_debug.c \
+       rfapi/vnc_export_bgp.c \
+       rfapi/vnc_export_table.c \
+       rfapi/vnc_import_bgp.c \
+       rfapi/vnc_zebra.c
+BGP_VNC_RFAPI_HD=rfapi/bgp_rfapi_cfg.h \
+       rfapi/rfapi_import.h \
+       rfapi/rfapi.h \
+       rfapi/rfapi_ap.h \
+       rfapi/rfapi_backend.h \
+       rfapi/rfapi_descriptor_rfp_utils.h \
+       rfapi/rfapi_encap_tlv.h \
+       rfapi/rfapi_nve_addr.h \
+       rfapi/rfapi_monitor.h \
+       rfapi/rfapi_private.h \
+       rfapi/rfapi_rib.h \
+       rfapi/rfapi_vty.h \
+       rfapi/vnc_debug.h \
+       rfapi/vnc_export_bgp.h \
+       rfapi/vnc_export_table.h \
+       rfapi/vnc_import_bgp.h \
+       rfapi/vnc_zebra.h \
+       bgp_vnc_types.h $(BGP_VNC_RFP_HD)
+
+else
+BGP_VNC_RFAPI_INC=
+BGP_VNC_RFAPI_SRC=
+BGP_VNC_RFAPI_HD=
+BGP_VNC_RFP_LIB=
+BGP_VNC_RFP_INC=
+BGP_VNC_RFP_HD=
+BGP_VNC_RFP_LD_FLAGS=
+endif
+
+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \
+       $(BGP_VNC_RFAPI_INC) $(BGP_VNC_RFP_INC)
 DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
 INSTALL_SDATA=@INSTALL@ -m 600
 
@@ -18,7 +76,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_encap.c bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC)
 
 noinst_HEADERS = \
        bgp_memory.h \
@@ -27,16 +85,20 @@ noinst_HEADERS = \
        bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \
        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_updgrp.h bgp_bfd.h bgp_encap.h bgp_encap_tlv.h bgp_encap_types.h \
+       $(BGP_VNC_RFAPI_HD) 
 
 bgpd_SOURCES = bgp_main.c
-bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@
+bgpd_LDADD = libbgp.a  $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ @LIBM@
+bgpd_LDFLAGS = $(BGP_VNC_RFP_LD_FLAGS)
 
 bgp_btoa_SOURCES = bgp_btoa.c
-bgp_btoa_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@
+bgp_btoa_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ @LIBM@
+bgp_btoa_LDFLAGS = $(BGP_VNC_RFP_LD_FLAGS)
 
 examplesdir = $(exampledir)
-dist_examples_DATA = bgpd.conf.sample bgpd.conf.sample2
+dist_examples_DATA = bgpd.conf.sample bgpd.conf.sample2 \
+       bgpd.conf.vnc.sample
 
 EXTRA_DIST = BGP4-MIB.txt
 
index 3cb52ef911acfee3129ba3a935330c1e9028dac4..cd92aec230cf4c08d777a120e33a8607a95c2653 100644 (file)
@@ -43,6 +43,11 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_ecommunity.h"
 #include "bgpd/bgp_updgrp.h"
 #include "bgpd/bgp_encap_types.h"
+#if ENABLE_BGP_VNC
+# include "bgp_rfapi_cfg.h"
+# include "bgp_encap_types.h"
+# include "bgp_vnc_types.h"
+#endif
 
 /* Attribute strings for logging. */
 static const struct message attr_str [] = 
@@ -67,6 +72,9 @@ static const struct message attr_str [] =
   { BGP_ATTR_AS4_AGGREGATOR,   "AS4_AGGREGATOR" }, 
   { BGP_ATTR_AS_PATHLIMIT,     "AS_PATHLIMIT" },
   { BGP_ATTR_ENCAP,            "ENCAP" },
+#if ENABLE_BGP_VNC
+  { BGP_ATTR_VNC,              "VNC" },
+#endif
 };
 static const int attr_str_max = array_size(attr_str);
 
@@ -256,6 +264,12 @@ bgp_attr_flush_encap(struct attr *attr)
        encap_free(attr->extra->encap_subtlvs);
        attr->extra->encap_subtlvs = NULL;
     }
+#if ENABLE_BGP_VNC
+    if (attr->extra->vnc_subtlvs) {
+       encap_free(attr->extra->vnc_subtlvs);
+       attr->extra->vnc_subtlvs = NULL;
+    }
+#endif
 }
 
 /*
@@ -421,6 +435,12 @@ bgp_attr_extra_free (struct attr *attr)
        encap_free(attr->extra->encap_subtlvs);
        attr->extra->encap_subtlvs = NULL;
       }
+#if ENABLE_BGP_VNC
+      if (attr->extra->vnc_subtlvs) {
+       encap_free(attr->extra->vnc_subtlvs);
+       attr->extra->vnc_subtlvs = NULL;
+      }
+#endif
       XFREE (MTYPE_ATTR_EXTRA, attr->extra);
       attr->extra = NULL;
     }
@@ -461,6 +481,11 @@ bgp_attr_dup (struct attr *new, struct attr *orig)
         if (orig->extra->encap_subtlvs) {
           new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs);
         }
+#if ENABLE_BGP_VNC
+      if (orig->extra->vnc_subtlvs) {
+       new->extra->vnc_subtlvs = encap_tlv_dup(orig->extra->vnc_subtlvs);
+      }
+#endif
       }
     }
   else if (orig->extra)
@@ -470,6 +495,11 @@ bgp_attr_dup (struct attr *new, struct attr *orig)
       if (orig->extra->encap_subtlvs) {
        new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs);
       }
+#if ENABLE_BGP_VNC
+      if (orig->extra->vnc_subtlvs) {
+       new->extra->vnc_subtlvs = encap_tlv_dup(orig->extra->vnc_subtlvs);
+      }
+#endif
     }
 }
 
@@ -611,6 +641,9 @@ attrhash_cmp (const void *p1, const void *p2)
           && ae1->transit == ae2->transit
          && (ae1->encap_tunneltype == ae2->encap_tunneltype)
          && encap_same(ae1->encap_subtlvs, ae2->encap_subtlvs)
+#if ENABLE_BGP_VNC
+         && encap_same(ae1->vnc_subtlvs, ae2->vnc_subtlvs)
+#endif
           && IPV4_ADDR_SAME (&ae1->originator_id, &ae2->originator_id))
         return 1;
       else if (ae1 || ae2)
@@ -669,6 +702,11 @@ bgp_attr_hash_alloc (void *p)
       if (attr->extra->encap_subtlvs) {
        attr->extra->encap_subtlvs = encap_tlv_dup(attr->extra->encap_subtlvs);
       }
+#if ENABLE_BGP_VNC
+      if (attr->extra->vnc_subtlvs) {
+       attr->extra->vnc_subtlvs = encap_tlv_dup(attr->extra->vnc_subtlvs);
+      }
+#endif
     }
   attr->refcnt = 0;
   return attr;
@@ -939,6 +977,10 @@ bgp_attr_flush (struct attr *attr)
         transit_free (attre->transit);
       encap_free(attre->encap_subtlvs);
       attre->encap_subtlvs = NULL;
+#if ENABLE_BGP_VNC
+      encap_free(attre->vnc_subtlvs);
+      attre->vnc_subtlvs = NULL;
+#endif
     }
 }
 
@@ -1956,6 +1998,12 @@ bgp_attr_encap(
         subtype   = stream_getc (BGP_INPUT (peer));
         sublength = stream_getc (BGP_INPUT (peer));
         length   -= 2;
+#if ENABLE_BGP_VNC
+    } else {
+        subtype   = stream_getw (BGP_INPUT (peer));
+        sublength = stream_getw (BGP_INPUT (peer));
+        length   -= 4;
+#endif
     }
 
     if (sublength > length) {
@@ -1987,6 +2035,16 @@ bgp_attr_encap(
            } else {
                attre->encap_subtlvs = tlv;
            }
+#if ENABLE_BGP_VNC
+       } else {
+           for (stlv_last = attre->vnc_subtlvs; stlv_last && stlv_last->next;
+               stlv_last = stlv_last->next);
+           if (stlv_last) {
+               stlv_last->next = tlv;
+           } else {
+               attre->vnc_subtlvs = tlv;
+           }
+#endif
        }
     } else {
        stlv_last->next = tlv;
@@ -2300,6 +2358,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
        case BGP_ATTR_EXT_COMMUNITIES:
          ret = bgp_attr_ext_communities (&attr_args);
          break;
+#if ENABLE_BGP_VNC
+        case BGP_ATTR_VNC:
+#endif
         case BGP_ATTR_ENCAP:
           ret = bgp_attr_encap (type, peer, length, attr, flag, startp);
           break;
@@ -2569,7 +2630,9 @@ bgp_packet_mpattr_prefix_size (afi_t afi, safi_t safi, struct prefix *p)
 }
 
 /*
- * Encodes the tunnel encapsulation attribute
+ * Encodes the tunnel encapsulation attribute,
+ * and with ENABLE_BGP_VNC the VNC attribute which uses 
+ * almost the same TLV format
  */
 static void
 bgp_packet_mpattr_tea(
@@ -2603,6 +2666,15 @@ bgp_packet_mpattr_tea(
             attrhdrlen   = 1 + 1;      /* subTLV T + L */
            break;
 
+#if ENABLE_BGP_VNC
+       case BGP_ATTR_VNC:
+           attrname = "VNC";
+           subtlvs = attr->extra->vnc_subtlvs;
+           attrlenfield = 0;     /* no outer T + L */
+            attrhdrlen   = 2 + 2; /* subTLV T + L */
+           break;
+#endif
+
        default:
            assert(0);
     }
@@ -2648,6 +2720,11 @@ bgp_packet_mpattr_tea(
         if (attrtype == BGP_ATTR_ENCAP) {
             stream_putc (s, st->type);
             stream_putc (s, st->length);
+#if ENABLE_BGP_VNC
+        } else {
+            stream_putw (s, st->type);
+            stream_putw (s, st->length); 
+#endif
         }
        stream_put (s, st->value, st->length);
     }
@@ -3037,6 +3114,11 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
     {
        /* Tunnel Encap attribute */
        bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_ENCAP);
+
+#if ENABLE_BGP_VNC
+       /* VNC attribute */
+       bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_VNC);
+#endif
     }
 
   /* Unknown transit attribute. */
index 0bf8c897dee369262fecc1b4ff77fb2f4b6a33f4..002bdfa08197e4ef43c3de2aa79ce9f7e470905b 100644 (file)
@@ -63,6 +63,21 @@ struct bgp_attr_encap_subtlv {
     uint8_t                            value[1];       /* will be extended */
 };
 
+#if ENABLE_BGP_VNC
+/*
+ * old rfp<->rfapi representation
+ */
+struct bgp_tea_options {
+    struct bgp_tea_options *next;
+    uint8_t               options_count;
+    uint16_t              options_length; /* each TLV may be 256 in length */
+    uint8_t               type;
+    uint8_t               length;
+    void                 *value; /* pointer to data */
+};
+
+#endif
+
 /* Additional/uncommon BGP attributes.
  * lazily allocated as and when a struct attr
  * requires it.
@@ -107,6 +122,10 @@ struct attr_extra
 
   uint16_t                     encap_tunneltype;       /* grr */
   struct bgp_attr_encap_subtlv *encap_subtlvs;         /* rfc5512 */
+
+#if ENABLE_BGP_VNC
+  struct bgp_attr_encap_subtlv *vnc_subtlvs;           /* VNC-specific */
+#endif
 };
 
 /* BGP core attribute structure. */
index 926e2650a234bf3f95e6f1523daf613715241b62..6c72aa36d9fa7d603db65aca3f5c9941756fa385 100644 (file)
@@ -35,7 +35,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 static struct hash *ecomhash;
 
 /* Allocate a new ecommunities.  */
-static struct ecommunity *
+struct ecommunity *
 ecommunity_new (void)
 {
   return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY,
@@ -59,7 +59,7 @@ ecommunity_free (struct ecommunity **ecom)
    structure, we don't add the value.  Newly added value is sorted by
    numerical order.  When the value is added to the structure return 1
    else return 0.  */
-static int
+int
 ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval)
 {
   u_int8_t *p;
index 993fd5acfd446f7e3d192a5d07e1fc71ae0a97ad..c5c58e42609ef1b64042af7e4380f3b6ab175fc9 100644 (file)
@@ -85,4 +85,7 @@ extern char *ecommunity_ecom2str (struct ecommunity *, int);
 extern int ecommunity_match (const struct ecommunity *, const struct ecommunity *);
 extern char *ecommunity_str (struct ecommunity *);
 
+/* for vpn */
+extern struct ecommunity *ecommunity_new (void);
+extern int ecommunity_add_val (struct ecommunity *, struct ecommunity_val *);
 #endif /* _QUAGGA_BGP_ECOMMUNITY_H */
index 3f7712b24d0093cc4333726be017d06aeede600b..2cfb51029502bd4e04eadcff077af8a67209b89c 100644 (file)
@@ -45,6 +45,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_vty.h"
 #include "bgpd/bgp_encap.h"
 
+#if ENABLE_BGP_VNC
+#include "rfapi_backend.h"
+#endif
+
 static void
 ecom2prd(struct ecommunity *ecom, struct prefix_rd *prd)
 {
@@ -185,7 +189,15 @@ bgp_nlri_parse_encap(
       if (!withdraw) {
        bgp_update (peer, &p, 0, attr, afi, SAFI_ENCAP,
                    ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0);
+#if ENABLE_BGP_VNC
+       rfapiProcessUpdate(peer, NULL, &p, &prd, attr, afi, SAFI_ENCAP,
+                           ZEBRA_ROUTE_BGP,  BGP_ROUTE_NORMAL, NULL);
+#endif
       } else {
+#if ENABLE_BGP_VNC
+       rfapiProcessWithdraw(peer, NULL, &p, &prd, attr, afi, SAFI_ENCAP,
+                             ZEBRA_ROUTE_BGP, 0);
+#endif
        bgp_withdraw (peer, &p, 0, attr, afi, SAFI_ENCAP,
                      ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL);
       }
index 347b4b3ce72c70ab29581e37b58c1b0b165e7bc6..c554ade27eeb56cf2a80cfe79cd40485a7b3c6ce 100644 (file)
@@ -409,9 +409,7 @@ bgp_encap_type_mpls_to_tlv(
     struct bgp_encap_type_mpls *bet,   /* input structure */
     struct attr                                *attr)
 {
-    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
-
-    extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS;
+    return;                     /* no encap attribute for MPLS */
 }
 
 void
index 603ff9d2d6249e5c657be2231fa4282d303384da..0985446ff2a00e303a6fea5043ca7a40084deb11 100644 (file)
@@ -32,7 +32,7 @@ typedef enum {
     BGP_ENCAP_TYPE_IP_IN_IP=7,
     BGP_ENCAP_TYPE_VXLAN=8,
     BGP_ENCAP_TYPE_NVGRE=9,
-    BGP_ENCAP_TYPE_MPLS=10,
+    BGP_ENCAP_TYPE_MPLS=10,     /* NOTE: Encap SAFI&Attribute not used */
     BGP_ENCAP_TYPE_MPLS_IN_GRE=11,
     BGP_ENCAP_TYPE_VXLAN_GPE=12,
     BGP_ENCAP_TYPE_MPLS_IN_UDP=13,
index 404fe7d73a9e4cca0577623e02fb8b0e2d36cd89..9ee8838fdfac403bd36a75bdb589d8029d20d848 100644 (file)
@@ -54,6 +54,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_filter.h"
 #include "bgpd/bgp_zebra.h"
 
+#ifdef ENABLE_BGP_VNC
+#include "rfapi_backend.h"
+#endif
+
 /* bgpd options, we use GNU getopt library. */
 static const struct option longopts[] = 
 {
@@ -282,7 +286,9 @@ bgp_exit (int status)
   bgp_vrf_terminate ();
   cmd_terminate ();
   vty_terminate ();
-
+#if ENABLE_BGP_VNC
+  vnc_zebra_destroy();
+#endif
   bgp_zebra_destroy();
   if (bgp_nexthop_buf)
     stream_free (bgp_nexthop_buf);
index 166400b7458a6919eed1ea82c07bfe5d5d61edf8..72c0311c171060a062b954b14fcc12e03d545f0e 100644 (file)
@@ -108,3 +108,6 @@ DEFINE_MTYPE(BGPD, BGP_REDIST,              "BGP redistribution")
 DEFINE_MTYPE(BGPD, BGP_FILTER_NAME,    "BGP Filter Information")
 DEFINE_MTYPE(BGPD, BGP_DUMP_STR,       "BGP Dump String Information")
 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")
index b2956f07edbc26c875f26aee3db59f4bb9c4c2a9..a4ce8b891b2259d698119dde6e9dbd363b40e565 100644 (file)
@@ -105,4 +105,7 @@ DECLARE_MTYPE(BGP_FILTER_NAME)
 DECLARE_MTYPE(BGP_DUMP_STR)
 DECLARE_MTYPE(ENCAP_TLV)
 
+DECLARE_MTYPE(BGP_TEA_OPTIONS)
+DECLARE_MTYPE(BGP_TEA_OPTIONS_VALUE)
+
 #endif /* _QUAGGA_BGP_MEMORY_H */
index 36ba65af1a426c9d05c385828b87320060df866f..991a92b850b5f38f7c62b338276540e540fb8974 100644 (file)
@@ -35,16 +35,35 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_attr.h"
 #include "bgpd/bgp_mplsvpn.h"
 
+#if ENABLE_BGP_VNC
+#include "rfapi_backend.h"
+#endif
+
 u_int16_t
 decode_rd_type (u_char *pnt)
 {
   u_int16_t v;
   
   v = ((u_int16_t) *pnt++ << 8);
+#if ENABLE_BGP_VNC
+  /*
+   * VNC L2 stores LHI in lower byte, so omit it
+   */
+  if (v != RD_TYPE_VNC_ETH)
+    v |= (u_int16_t) *pnt;
+#else                           /* duplicate code for clarity */
   v |= (u_int16_t) *pnt;
+#endif
+
   return v;
 }
 
+void
+encode_rd_type (u_int16_t v, u_char *pnt)
+{
+  *((u_int16_t *)pnt) = htons(v);
+}
+
 u_int32_t
 decode_label (u_char *pnt)
 {
@@ -56,6 +75,17 @@ decode_label (u_char *pnt)
   return l;
 }
 
+void
+encode_label(u_int32_t label,
+             u_char *pnt)
+{
+    if (pnt == NULL)
+        return;
+    *pnt++ = (label>>12) & 0xff;
+    *pnt++ = (label>>4) & 0xff;
+    *pnt++ = ((label<<4)+1) & 0xff; /* S=1 */
+}
+
 /* type == RD_TYPE_AS */
 void
 decode_rd_as (u_char *pnt, struct rd_as *rd_as)
@@ -93,6 +123,17 @@ decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip)
   rd_ip->val |= (u_int16_t) *pnt;
 }
 
+#if ENABLE_BGP_VNC
+/* type == RD_TYPE_VNC_ETH */
+void
+decode_rd_vnc_eth (u_char *pnt, struct rd_vnc_eth *rd_vnc_eth)
+{
+  rd_vnc_eth->type = RD_TYPE_VNC_ETH;
+  rd_vnc_eth->local_nve_id = pnt[1];
+  memcpy (rd_vnc_eth->macaddr.octet, pnt + 2, ETHER_ADDR_LEN);
+}
+#endif
+
 int
 bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
                     struct bgp_nlri *packet)
@@ -111,6 +152,9 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
   safi_t safi;
   int addpath_encoded;
   u_int32_t addpath_id;
+#if ENABLE_BGP_VNC
+  u_int32_t label = 0;
+#endif
 
   /* Check peer status. */
   if (peer->status != Established)
@@ -184,6 +228,10 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
                   
         }
       
+#if ENABLE_BGP_VNC
+      label = decode_label (pnt);
+#endif
+
       /* Copyr label to prefix. */
       tagpnt = pnt;
 
@@ -207,21 +255,39 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
           decode_rd_ip (pnt + 5, &rd_ip);
           break;
 
+#if ENABLE_BGP_VNC
+       case RD_TYPE_VNC_ETH:
+           break;
+#endif
+
        default:
          zlog_err ("Unknown RD type %d", type);
           break;  /* just report */
       }
 
-      p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES*8;
+      p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES*8;/* exclude label & RD */
       memcpy (&p.u.prefix, pnt + VPN_PREFIXLEN_MIN_BYTES, 
               psize - VPN_PREFIXLEN_MIN_BYTES);
 
       if (attr)
-       bgp_update (peer, &p, addpath_id, attr, packet->afi, SAFI_MPLS_VPN,
-                   ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0);
+        {
+          bgp_update (peer, &p, addpath_id, attr, packet->afi, SAFI_MPLS_VPN,
+                      ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0);
+#if ENABLE_BGP_VNC
+          rfapiProcessUpdate(peer, NULL, &p, &prd, attr, packet->afi, 
+                             SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+                             &label);
+#endif
+        }
       else
-       bgp_withdraw (peer, &p, addpath_id, attr, packet->afi, SAFI_MPLS_VPN,
-                     ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt);
+        {
+#if ENABLE_BGP_VNC
+          rfapiProcessWithdraw(peer, NULL, &p, &prd, attr, packet->afi, 
+                               SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP, 0);
+#endif
+          bgp_withdraw (peer, &p, addpath_id, attr, packet->afi, SAFI_MPLS_VPN,
+                        ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt);
+        }
     }
   /* Packet length consistency check. */
   if (pnt != lim)
@@ -346,6 +412,21 @@ prefix_rd2str (struct prefix_rd *prd, char *buf, size_t size)
       snprintf (buf, size, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
       return buf;
     }
+#if ENABLE_BGP_VNC
+  else if (type == RD_TYPE_VNC_ETH)
+    {
+      snprintf(buf, size, "LHI:%d, %02x:%02x:%02x:%02x:%02x:%02x",
+           *(pnt+1),   /* LHI */
+           *(pnt+2),   /* MAC[0] */
+           *(pnt+3),
+           *(pnt+4),
+           *(pnt+5),
+           *(pnt+6),
+           *(pnt+7));
+
+      return buf;
+    }
+#endif
   return NULL;
 }
 
@@ -483,6 +564,9 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd, u
                       u_int16_t type;
                       struct rd_as rd_as;
                       struct rd_ip rd_ip = {0};
+#if ENABLE_BGP_VNC
+                      struct rd_vnc_eth rd_vnc_eth;
+#endif
                       u_char *pnt;
 
                       pnt = rn->p.u.val;
@@ -496,6 +580,10 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd, u
                         decode_rd_as4 (pnt + 2, &rd_as);
                       else if (type == RD_TYPE_IP)
                         decode_rd_ip (pnt + 2, &rd_ip);
+#if ENABLE_BGP_VNC
+                      else if (type == RD_TYPE_VNC_ETH)
+                        decode_rd_vnc_eth (pnt, &rd_vnc_eth);
+#endif
 
                       if (use_json)
                         {
@@ -514,6 +602,17 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd, u
                             vty_out (vty, "%u:%d", rd_as.as, rd_as.val);
                           else if (type == RD_TYPE_IP)
                             vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+#if ENABLE_BGP_VNC
+                          else if (type == RD_TYPE_VNC_ETH)
+                            vty_out (vty, "%u:%02x:%02x:%02x:%02x:%02x:%02x", 
+                                     rd_vnc_eth.local_nve_id, 
+                                     rd_vnc_eth.macaddr.octet[0],
+                                     rd_vnc_eth.macaddr.octet[1],
+                                     rd_vnc_eth.macaddr.octet[2],
+                                     rd_vnc_eth.macaddr.octet[3],
+                                     rd_vnc_eth.macaddr.octet[4],
+                                     rd_vnc_eth.macaddr.octet[5]);
+#endif
 
                           vty_out (vty, "%s", VTY_NEWLINE);
                         }
@@ -675,6 +774,9 @@ bgp_show_mpls_vpn (struct vty *vty, afi_t afi, struct prefix_rd *prd,
                      u_int16_t type;
                      struct rd_as rd_as;
                      struct rd_ip rd_ip = {0};
+#if ENABLE_BGP_VNC
+                      struct rd_vnc_eth rd_vnc_eth;
+#endif
                      u_char *pnt;
 
                      pnt = rn->p.u.val;
@@ -688,6 +790,10 @@ bgp_show_mpls_vpn (struct vty *vty, afi_t afi, struct prefix_rd *prd,
                        decode_rd_as4 (pnt + 2, &rd_as);
                      else if (type == RD_TYPE_IP)
                        decode_rd_ip (pnt + 2, &rd_ip);
+#if ENABLE_BGP_VNC
+                      else if (type == RD_TYPE_VNC_ETH)
+                        decode_rd_vnc_eth (pnt, &rd_vnc_eth);
+#endif
 
                       if (use_json)
                         {
@@ -706,6 +812,17 @@ bgp_show_mpls_vpn (struct vty *vty, afi_t afi, struct prefix_rd *prd,
                            vty_out (vty, "%u:%d", rd_as.as, rd_as.val);
                          else if (type == RD_TYPE_IP)
                            vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+#if ENABLE_BGP_VNC
+                          else if (type == RD_TYPE_VNC_ETH)
+                            vty_out (vty, "%u:%02x:%02x:%02x:%02x:%02x:%02x", 
+                                     rd_vnc_eth.local_nve_id, 
+                                     rd_vnc_eth.macaddr.octet[0],
+                                     rd_vnc_eth.macaddr.octet[1],
+                                     rd_vnc_eth.macaddr.octet[2],
+                                     rd_vnc_eth.macaddr.octet[3],
+                                     rd_vnc_eth.macaddr.octet[4],
+                                     rd_vnc_eth.macaddr.octet[5]);
+#endif
                          vty_out (vty, "%s", VTY_NEWLINE);
                         }
                      rd_header = 0;
index 3fbbd335405b4aaa0f40243fe273e5ace178d1a0..f75b9890573f116047b720c01baf22e494f2fbf0 100644 (file)
@@ -24,9 +24,37 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #define RD_TYPE_AS      0
 #define RD_TYPE_IP      1
 #define RD_TYPE_AS4     2
+#if ENABLE_BGP_VNC
+#define RD_TYPE_VNC_ETH        0xff00  /* VNC L2VPN */
+#endif
 
 #define RD_ADDRSTRLEN  28
 
+typedef enum {
+    MPLS_LABEL_IPV4_EXPLICIT_NULL = 0,  /* [RFC3032] */
+    MPLS_LABEL_ROUTER_ALERT       = 1,  /* [RFC3032] */
+    MPLS_LABEL_IPV6_EXPLICIT_NULL = 2,  /* [RFC3032] */
+    MPLS_LABEL_IMPLICIT_NULL      = 3,  /* [RFC3032] */
+    MPLS_LABEL_UNASSIGNED4        = 4,
+    MPLS_LABEL_UNASSIGNED5        = 5,
+    MPLS_LABEL_UNASSIGNED6        = 6,
+    MPLS_LABEL_ELI                = 7,  /* Entropy Indicator [RFC6790] */
+    MPLS_LABEL_UNASSIGNED8        = 8,
+    MPLS_LABEL_UNASSIGNED9        = 9,
+    MPLS_LABEL_UNASSIGNED10       = 10,
+    MPLS_LABEL_UNASSIGNED11       = 11,
+    MPLS_LABEL_GAL                = 13, /* [RFC5586] */
+    MPLS_LABEL_OAM_ALERT          = 14, /* [RFC3429] */
+    MPLS_LABEL_EXTENSION          = 15  /* [RFC7274] */
+} mpls_special_label_t;
+
+#define MPLS_LABEL_IS_SPECIAL(label)             \
+    ((label) <= MPLS_LABEL_EXTENSION)
+#define MPLS_LABEL_IS_NULL(label)                \
+    ((label) == MPLS_LABEL_IPV4_EXPLICIT_NULL || \
+     (label) == MPLS_LABEL_IPV6_EXPLICIT_NULL || \
+     (label) == MPLS_LABEL_IMPLICIT_NULL)
+
 struct rd_as
 {
   u_int16_t type;
@@ -41,7 +69,17 @@ struct rd_ip
   u_int16_t val;
 };
 
+#if ENABLE_BGP_VNC
+struct rd_vnc_eth
+{
+  u_int16_t type;
+  uint8_t local_nve_id;
+  struct ethaddr macaddr;
+};
+#endif
+
 extern u_int16_t decode_rd_type (u_char *);
+extern void encode_rd_type (u_int16_t, u_char *);
 extern void bgp_mplsvpn_init (void);
 extern int bgp_nlri_parse_vpn (struct peer *, struct attr *, struct bgp_nlri *);
 extern u_int32_t decode_label (u_char *);
@@ -49,6 +87,9 @@ extern void encode_label(u_int32_t, u_char *);
 extern void decode_rd_as (u_char *, struct rd_as *);
 extern void decode_rd_as4 (u_char *, struct rd_as *);
 extern void decode_rd_ip (u_char *, struct rd_ip *);
+#if ENABLE_BGP_VNC
+extern void decode_vnc_eth (u_char *, struct rd_vnc_eth *);
+#endif
 extern int str2prefix_rd (const char *, struct prefix_rd *);
 extern int str2tag (const char *, u_char *);
 extern char *prefix_rd2str (struct prefix_rd *, char *, size_t);
index 861da5740faf3e9bd6794f6b90673a993d678028..652a6813eed5d7610880fa3fb2e7f1471f7bc741 100644 (file)
@@ -34,6 +34,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   AF_UNSPEC))                         \
 )
 
+#define BGP_MP_NEXTHOP_FAMILY NEXTHOP_FAMILY
+
 /* BGP nexthop cache value structure. */
 struct bgp_nexthop_cache
 {
index f604b7658d419dd334caa5ec23a6ab0b7f6d3b04..6c97dc333b4c2644469832e6a57a6d8c2171bbaa 100644 (file)
@@ -62,6 +62,12 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_updgrp.h"
 #include "bgpd/bgp_vty.h"
 
+#if ENABLE_BGP_VNC
+#include "rfapi_backend.h"
+#include "vnc_import_bgp.h"
+#include "vnc_export_bgp.h"
+#endif
+
 /* Extern from bgp_dump.c */
 extern const char *bgp_origin_str[];
 extern const char *bgp_origin_long_str[];
@@ -132,6 +138,13 @@ bgp_info_extra_get (struct bgp_info *ri)
   return ri->extra;
 }
 
+/* Allocate new bgp info structure. */
+struct bgp_info *
+bgp_info_new (void)
+{
+  return XCALLOC (MTYPE_BGP_ROUTE, sizeof (struct bgp_info));
+}
+
 /* Free bgp route information. */
 static void
 bgp_info_free (struct bgp_info *binfo)
@@ -228,7 +241,7 @@ bgp_info_delete (struct bgp_node *rn, struct bgp_info *ri)
 /* undo the effects of a previous call to bgp_info_delete; typically
    called when a route is deleted and then quickly re-added before the
    deletion has been processed */
-static void
+void
 bgp_info_restore (struct bgp_node *rn, struct bgp_info *ri)
 {
   bgp_info_unset_flag (rn, ri, BGP_INFO_REMOVED);
@@ -332,7 +345,7 @@ bgp_info_path_with_addpath_rx_str (struct bgp_info *ri, char *buf)
 static int
 bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
              int *paths_eq, struct bgp_maxpaths_cfg *mpath_cfg, int debug,
-              char *pfx_buf)
+              const char *pfx_buf)
 {
   struct attr *newattr, *existattr;
   struct attr_extra *newattre, *existattre;
@@ -847,6 +860,31 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
   return 1;
 }
 
+/* Compare two bgp route entity.  Return -1 if new is preferred, 1 if exist
+ * is preferred, or 0 if they are the same (usually will only occur if
+ * multipath is enabled 
+ * This version is compatible with */
+int
+bgp_info_cmp_compatible (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
+                         afi_t afi, safi_t safi)
+{
+  int paths_eq;
+  struct bgp_maxpaths_cfg mpath_cfg;
+  int ret;
+  ret = bgp_info_cmp (bgp, new, exist, &paths_eq, &mpath_cfg, 0, __func__);
+
+  if (paths_eq)
+    ret = 0;
+  else 
+    {
+      if (ret == 1)
+        ret = -1;
+      else 
+        ret = 1;
+    }
+  return ret;
+}
+
 static enum filter_type
 bgp_input_filter (struct peer *peer, struct prefix *p, struct attr *attr,
                  afi_t afi, safi_t safi)
@@ -1159,6 +1197,7 @@ subgroup_announce_check (struct bgp_info *ri, struct update_subgroup *subgrp,
   int reflect;
   afi_t afi;
   safi_t safi;
+  int samepeer_safe = 0;       /* for synthetic mplsvpns routes */
 
   if (DISABLE_BGP_ANNOUNCE)
     return 0;
@@ -1175,6 +1214,22 @@ subgroup_announce_check (struct bgp_info *ri, struct update_subgroup *subgrp,
   bgp = SUBGRP_INST(subgrp);
   riattr = bgp_info_mpath_count (ri) ? bgp_info_mpath_attr (ri) : ri->attr;
 
+#if ENABLE_BGP_VNC
+  if (((afi == AFI_IP) || (afi == AFI_IP6)) && (safi == SAFI_MPLS_VPN) &&
+      ((ri->type == ZEBRA_ROUTE_BGP_DIRECT) ||
+       (ri->type == ZEBRA_ROUTE_BGP_DIRECT_EXT))) {
+
+    /*
+     * direct and direct_ext type routes originate internally even
+     * though they can have peer pointers that reference other systems
+     */
+    char    buf[BUFSIZ];
+    prefix2str(p, buf, BUFSIZ);
+    zlog_debug("%s: pfx %s bgp_direct->vpn route peer safe", __func__, buf);
+    samepeer_safe = 1;
+  }
+#endif
+
   /* 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) ||
@@ -1311,7 +1366,7 @@ subgroup_announce_check (struct bgp_info *ri, struct update_subgroup *subgrp,
     reflect = 0;
 
   /* IBGP reflection check. */
-  if (reflect)
+  if (reflect && !samepeer_safe)
     {
       /* A route from a Client peer. */
       if (CHECK_FLAG (from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT))
@@ -1869,8 +1924,13 @@ bgp_process_main (struct work_queue *wq, void *data)
       !bgp->addpath_tx_used[afi][safi])
     {
       if (bgp_zebra_has_route_changed (rn, old_select))
-        bgp_zebra_announce (p, old_select, bgp, afi, safi);
-          
+        {
+#if ENABLE_BGP_VNC
+              vnc_import_bgp_add_route(bgp, p, old_select);
+              vnc_import_bgp_exterior_add_route(bgp, p, old_select);
+#endif
+          bgp_zebra_announce (p, old_select, bgp, afi, safi);
+        }
       UNSET_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG);
       bgp_zebra_clear_route_change_flags (rn);
       UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED);
@@ -1903,6 +1963,21 @@ bgp_process_main (struct work_queue *wq, void *data)
       UNSET_FLAG (new_select->flags, BGP_INFO_MULTIPATH_CHG);
     }
 
+#if ENABLE_BGP_VNC
+  if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
+    if (old_select != new_select) {
+      if (old_select) {
+        vnc_import_bgp_exterior_del_route(bgp, p, old_select);
+        vnc_import_bgp_del_route(bgp, p, old_select);
+      }
+      if (new_select) {
+        vnc_import_bgp_exterior_add_route(bgp, p, new_select);
+        vnc_import_bgp_add_route(bgp, p, new_select);
+      }
+    }
+  }
+#endif
+
   group_announce_route(bgp, afi, safi, rn, new_select);
 
   /* FIB update. */
@@ -2136,7 +2211,7 @@ bgp_rib_remove (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer,
 
 static void
 bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer,
-                 afi_t afi, safi_t safi)
+                 afi_t afi, safi_t safi, struct prefix_rd *prd)
 {
   int status = BGP_DAMP_NONE;
 
@@ -2151,7 +2226,33 @@ bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer,
         bgp_aggregate_decrement (peer->bgp, &rn->p, ri, afi, safi);
         return;
       }
-    
+#if ENABLE_BGP_VNC
+    if (safi == SAFI_MPLS_VPN) {
+       struct bgp_node         *prn = NULL;
+       struct bgp_table        *table = NULL;
+
+       prn = bgp_node_get(peer->bgp->rib[afi][safi], (struct prefix *) prd);
+       if (prn->info) {
+           table = (struct bgp_table *)(prn->info);
+
+           vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
+               peer->bgp,
+               prd,
+               table,
+               &rn->p,
+               ri);
+       }
+       bgp_unlock_node(prn);
+    }
+    if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
+        if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) {
+
+           vnc_import_bgp_del_route(peer->bgp, &rn->p, ri);
+           vnc_import_bgp_exterior_del_route(peer->bgp, &rn->p, ri);
+       }
+    }
+#endif    
   bgp_rib_remove (rn, ri, peer, afi, safi);
 }
 
@@ -2254,6 +2355,9 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
   char buf[SU_ADDRSTRLEN];
   char buf2[30];
   int connected = 0;
+#if ENABLE_BGP_VNC
+  int vnc_implicit_withdraw = 0;
+#endif
 
   bgp = peer->bgp;
   rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
@@ -2443,6 +2547,35 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
          if (! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
            bgp_damp_withdraw (ri, rn, afi, safi, 1);  
        }
+#if ENABLE_BGP_VNC
+    if (safi == SAFI_MPLS_VPN) {
+       struct bgp_node         *prn = NULL;
+       struct bgp_table        *table = NULL;
+
+       prn = bgp_node_get(bgp->rib[afi][safi], (struct prefix *) prd);
+       if (prn->info) {
+           table = (struct bgp_table *)(prn->info);
+
+           vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
+               bgp,
+               prd,
+               table,
+               p,
+               ri);
+       }
+       bgp_unlock_node(prn);
+    }
+    if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
+        if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) {
+           /*
+            * Implicit withdraw case.
+            */
+           ++vnc_implicit_withdraw;
+           vnc_import_bgp_del_route(bgp, p, ri);
+           vnc_import_bgp_exterior_del_route(bgp, p, ri);
+       }
+    }
+#endif
        
       /* Update to new attribute.  */
       bgp_attr_unintern (&ri->attr);
@@ -2452,6 +2585,25 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
       if (safi == SAFI_MPLS_VPN)
         memcpy ((bgp_info_extra_get (ri))->tag, tag, 3);
 
+#if ENABLE_BGP_VNC
+      if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) 
+        {
+          if (vnc_implicit_withdraw) 
+            {
+              /*
+               * Add back the route with its new attributes (e.g., nexthop).
+               * The route is still selected, until the route selection
+               * queued by bgp_process actually runs. We have to make this
+               * update to the VNC side immediately to avoid racing against
+               * configuration changes (e.g., route-map changes) which
+               * trigger re-importation of the entire RIB.
+               */
+              vnc_import_bgp_add_route(bgp, p, ri);
+              vnc_import_bgp_exterior_add_route(bgp, p, ri);
+            }
+        }
+#endif
+
       /* Update bgp route dampening information.  */
       if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
          && peer->sort == BGP_PEER_EBGP)
@@ -2491,6 +2643,28 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
       else
        bgp_info_set_flag (rn, ri, BGP_INFO_VALID);
 
+#if ENABLE_BGP_VNC
+      if (safi == SAFI_MPLS_VPN) 
+        {
+          struct bgp_node              *prn = NULL;
+          struct bgp_table     *table = NULL;
+
+          prn = bgp_node_get(bgp->rib[afi][safi], (struct prefix *) prd);
+          if (prn->info) 
+            {
+              table = (struct bgp_table *)(prn->info);
+
+              vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
+               bgp,
+                    prd,
+                    table,
+                    p,
+                    ri);
+            }
+          bgp_unlock_node(prn);
+        }
+#endif
+
       /* Process change. */
       bgp_aggregate_increment (bgp, p, ri, afi, safi);
 
@@ -2561,6 +2735,28 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
   /* route_node_get lock */
   bgp_unlock_node (rn);
 
+#if ENABLE_BGP_VNC
+  if (safi == SAFI_MPLS_VPN) 
+    {
+      struct bgp_node          *prn = NULL;
+      struct bgp_table *table = NULL;
+    
+      prn = bgp_node_get(bgp->rib[afi][safi], (struct prefix *) prd);
+      if (prn->info) 
+        {
+          table = (struct bgp_table *)(prn->info);
+      
+          vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
+                             bgp,
+                             prd,
+                             table,
+                             p,
+                             new);
+        }
+      bgp_unlock_node(prn);
+    }
+#endif
+
   /* If maximum prefix count is configured and current prefix
      count exeed it. */
   if (bgp_maximum_prefix_overflow (peer, afi, safi, 0))
@@ -2653,7 +2849,7 @@ 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))
-    bgp_rib_withdraw (rn, ri, peer, afi, safi);
+    bgp_rib_withdraw (rn, ri, peer, afi, safi, prd);
   else if (bgp_debug_update(peer, p, NULL, 1))
     zlog_debug ("%s Can't find the route %s/%d", peer->host,
                inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN),
@@ -3047,6 +3243,10 @@ bgp_clear_route_all (struct peer *peer)
   for (afi = AFI_IP; afi < AFI_MAX; afi++)
     for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
       bgp_clear_route (peer, afi, safi);
+
+#if ENABLE_BGP_VNC
+  rfapiProcessPeerDown(peer);
+#endif
 }
 
 void
@@ -3117,7 +3317,13 @@ bgp_cleanup_table(struct bgp_table *table, safi_t safi)
             && ri->type == ZEBRA_ROUTE_BGP
             && (ri->sub_type == BGP_ROUTE_NORMAL ||
                 ri->sub_type == BGP_ROUTE_AGGREGATE))
-          bgp_zebra_withdraw (&rn->p, ri, safi);
+          {
+#if ENABLE_BGP_VNC
+            if (table->owner && table->owner->bgp)
+              vnc_import_bgp_del_route(table->owner->bgp, &rn->p, ri);
+#endif
+            bgp_zebra_withdraw (&rn->p, ri, safi);
+          }
       }
 }
 
@@ -3417,6 +3623,9 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p,
   struct attr attr;
   struct attr *attr_new;
   int ret;
+#if ENABLE_BGP_VNC
+  int vnc_implicit_withdraw = 0;
+#endif
 
   assert (bgp_static);
   if (!bgp_static)
@@ -3489,9 +3698,34 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p,
            bgp_info_restore(rn, ri);
          else
            bgp_aggregate_decrement (bgp, p, ri, afi, safi);
+#if ENABLE_BGP_VNC
+          if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) 
+            {
+              if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) 
+                {
+                  /*
+                   * Implicit withdraw case.
+                   * We have to do this before ri is changed
+                   */
+                  ++vnc_implicit_withdraw;
+                  vnc_import_bgp_del_route(bgp, p, ri);
+                  vnc_import_bgp_exterior_del_route(bgp, p, ri);
+                }
+            }
+#endif
          bgp_attr_unintern (&ri->attr);
          ri->attr = attr_new;
          ri->uptime = bgp_clock ();
+#if ENABLE_BGP_VNC
+          if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) 
+            {
+              if (vnc_implicit_withdraw) 
+                {
+                  vnc_import_bgp_add_route(bgp, p, ri);
+                  vnc_import_bgp_exterior_add_route(bgp, p, ri);
+                }
+            }
+#endif
 
          /* Nexthop reachability check. */
          if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK))
@@ -3637,6 +3871,18 @@ bgp_static_withdraw_safi (struct bgp *bgp, struct prefix *p, afi_t afi,
   /* Withdraw static BGP route from routing table. */
   if (ri)
     {
+#if ENABLE_BGP_VNC
+       rfapiProcessWithdraw(
+           ri->peer,
+           NULL,
+           p,
+           prd,
+           ri->attr,
+           afi,
+           safi,
+           ri->type,
+           1);         /* Kill, since it is an administrative change */
+#endif
       bgp_aggregate_decrement (bgp, p, ri, afi, safi);
       bgp_info_delete (rn, ri);
       bgp_process (bgp, rn, afi, safi);
@@ -3655,6 +3901,9 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
   struct attr *attr_new;
   struct attr attr = { 0 };
   struct bgp_info *ri;
+#if ENABLE_BGP_VNC
+  u_int32_t        label = 0;
+#endif
 
   assert (bgp_static);
 
@@ -3731,10 +3980,19 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
           bgp_attr_unintern (&ri->attr);
           ri->attr = attr_new;
           ri->uptime = bgp_clock ();
+#if ENABLE_BGP_VNC
+          if (ri->extra)
+              label = decode_label (ri->extra->tag);
+#endif
 
           /* Process change. */
           bgp_aggregate_increment (bgp, p, ri, afi, safi);
           bgp_process (bgp, rn, afi, safi);
+#if ENABLE_BGP_VNC
+          rfapiProcessUpdate(ri->peer, NULL, p, &bgp_static->prd,
+                             ri->attr, afi, safi, 
+                             ri->type, ri->sub_type, &label);
+#endif
           bgp_unlock_node (rn);
           aspath_unintern (&attr.aspath);
           bgp_attr_extra_free (&attr);
@@ -3749,6 +4007,9 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
   SET_FLAG (new->flags, BGP_INFO_VALID);
   new->extra = bgp_info_extra_new();
   memcpy (new->extra->tag, bgp_static->tag, 3);
+#if ENABLE_BGP_VNC
+  label = decode_label (bgp_static->tag);
+#endif
 
   /* Aggregate address increment. */
   bgp_aggregate_increment (bgp, p, new, afi, safi);
@@ -3762,6 +4023,12 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
   /* Process change. */
   bgp_process (bgp, rn, afi, safi);
 
+#if ENABLE_BGP_VNC
+  rfapiProcessUpdate(new->peer, NULL, p, &bgp_static->prd,
+                     new->attr, afi, safi, 
+                     new->type, new->sub_type, &label);
+#endif
+
   /* Unintern original. */
   aspath_unintern (&attr.aspath);
   bgp_attr_extra_free (&attr);
@@ -6107,7 +6374,14 @@ route_vty_out (struct vty *vty, struct prefix *p,
       json_object_array_add(json_paths, json_path);
     }
   else
-    vty_out (vty, "%s", VTY_NEWLINE);
+    {
+      vty_out (vty, "%s", VTY_NEWLINE);
+#if ENABLE_BGP_VNC
+      /* prints an additional line, indented, with VNC info, if present */
+      if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_UNICAST))
+        rfapi_vty_out_vncinfo(vty, p, binfo, safi);
+#endif
+    }
 }  
 
 /* called from terminal list command */
index 60c406775d1ccef8ffe25eccd98249cfc57ef5e3..3d65b4b0a90696255b82b98552496e16b6e1360f 100644 (file)
@@ -49,6 +49,30 @@ struct bgp_info_extra
 
   /* MPLS label.  */
   u_char tag[3];  
+
+#if ENABLE_BGP_VNC
+  union {
+
+      struct {
+         void *rfapi_handle;   /* export: NVE advertising this route */
+         struct list   *local_nexthops; /* optional, for static routes */
+      } export;
+
+      struct {
+         void *timer;
+         void *hme;            /* encap monitor, if this is a VPN route */
+         struct prefix_rd rd;  /* import: route's route-distinguisher */
+         u_char un_family;     /* family of cached un address, 0 if unset */
+         union {
+           struct in_addr addr4;
+           struct in6_addr addr6;
+         } un;                 /* cached un address */
+         time_t create_time;
+         struct prefix aux_prefix; /* AFI_ETHER: the IP addr, if family set */
+      } import;
+
+  } vnc;
+#endif
 };
 
 struct bgp_info
@@ -111,6 +135,9 @@ struct bgp_info
 #define BGP_ROUTE_STATIC       1
 #define BGP_ROUTE_AGGREGATE    2
 #define BGP_ROUTE_REDISTRIBUTE 3 
+#ifdef ENABLE_BGP_VNC
+# define BGP_ROUTE_RFP          4 
+#endif
 
   u_short instance;
 
@@ -309,4 +336,14 @@ extern int subgroup_announce_check(struct bgp_info *ri,
 extern void bgp_peer_clear_node_queue_drain_immediate (struct peer *peer);
 extern void bgp_process_queues_drain_immediate (void);
 
+/* for encap/vpn */
+extern struct bgp_node *
+bgp_afi_node_get (struct bgp_table *, afi_t , safi_t , struct prefix *,
+                 struct prefix_rd *);
+extern struct bgp_info *bgp_info_new (void);
+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 );
+
 #endif /* _QUAGGA_BGP_ROUTE_H */
index fea9ae6b184ef1f80f8e874f58c11a577d4431df..efed71ebe9cde9595928506f167aa5b6a95808d8 100644 (file)
@@ -58,6 +58,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_vty.h"
 #include "bgpd/bgp_debug.h"
 
+#if ENABLE_BGP_VNC
+# include "bgp_rfapi_cfg.h"
+#endif
 
 /* Memo of route-map commands.
 
@@ -2924,6 +2927,10 @@ bgp_route_map_process_update_cb (char *rmap_name)
   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__);
+#endif
   return 0;
 }
 
@@ -2960,6 +2967,10 @@ bgp_route_map_mark_update (const char *rmap_name)
         {
           for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
             bgp_route_map_process_update(bgp, rmap_name, 0);
+ #if ENABLE_BGP_VNC
+          zlog_debug("%s: calling vnc_routemap_update", __func__);
+          vnc_routemap_update(bgp, __func__);
+#endif
         }
     }
 }
diff --git a/bgpd/bgp_vnc_types.h b/bgpd/bgp_vnc_types.h
new file mode 100644 (file)
index 0000000..8bc9cb6
--- /dev/null
@@ -0,0 +1,41 @@
+/* 
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_BGP_VNC_TYPES_H
+#define _QUAGGA_BGP_VNC_TYPES_H
+
+#if ENABLE_BGP_VNC
+typedef enum {
+    BGP_VNC_SUBTLV_TYPE_LIFETIME=1,
+    BGP_VNC_SUBTLV_TYPE_RFPOPTION=2,    /* deprecated */
+} bgp_vnc_subtlv_types;
+
+/*
+ * VNC Attribute subtlvs
+ */
+struct bgp_vnc_subtlv_lifetime {
+    uint32_t   lifetime;
+};
+
+struct bgp_vnc_subtlv_unaddr {
+    struct prefix      un_address;     /* IPv4 or IPv6; pfx length ignored */
+};
+
+#endif  /* ENABLE_BGP_VNC */
+#endif /* _QUAGGA_BGP_VNC_TYPES_H */
index 7089d140b9fc5a6f93888002135edf4ee6f52756..cb241540612f0949ba7104cf3a3fcc1a1c96034b 100644 (file)
@@ -46,6 +46,10 @@ Boston, MA 02111-1307, USA.  */
 #include "bgpd/bgp_nexthop.h"
 #include "bgpd/bgp_nht.h"
 #include "bgpd/bgp_bfd.h"
+#if ENABLE_BGP_VNC
+# include "rfapi_backend.h"
+# include "vnc_export_bgp.h"
+#endif
 
 /* All information about zebra. */
 struct zclient *zclient = NULL;
@@ -1806,6 +1810,13 @@ bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type, u_short instance)
       if (vrf_bitmap_check (zclient->redist[afi][type], bgp->vrf_id))
         return CMD_WARNING;
 
+#if ENABLE_BGP_VNC
+      if (bgp->vrf_id == VRF_DEFAULT &&
+          type == ZEBRA_ROUTE_VNC_DIRECT) {
+        vnc_export_bgp_enable(bgp, afi);       /* only enables if mode bits cfg'd */
+      }
+#endif
+
       vrf_bitmap_set (zclient->redist[afi][type], bgp->vrf_id);
     }
 
@@ -1933,6 +1944,13 @@ bgp_redistribute_unreg (struct bgp *bgp, afi_t afi, int type, u_short instance)
       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. */
index f4a16d6ba29d55711cfe147eb39229f7c965d6da..291483a8665ef22470c42559f911aed4c93e2066 100644 (file)
@@ -63,6 +63,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_damp.h"
 #include "bgpd/bgp_mplsvpn.h"
 #include "bgpd/bgp_encap.h"
+#if ENABLE_BGP_VNC
+#include "bgp_rfapi_cfg.h"
+#include "rfapi_backend.h"
+#endif
 #include "bgpd/bgp_advertise.h"
 #include "bgpd/bgp_network.h"
 #include "bgpd/bgp_vty.h"
@@ -1127,7 +1131,7 @@ peer_unlock_with_caller (const char *name, struct peer *peer)
 }
 
 /* Allocate new peer object, implicitely locked.  */
-static struct peer *
+struct peer *
 peer_new (struct bgp *bgp)
 {
   afi_t afi;
@@ -2885,6 +2889,12 @@ bgp_create (as_t *as, const char *name, enum bgp_instance_type inst_type)
 
   bgp->as = *as;
 
+#if ENABLE_BGP_VNC
+  bgp->rfapi = bgp_rfapi_new(bgp);
+  assert(bgp->rfapi);
+  assert(bgp->rfapi_cfg);
+#endif /* ENABLE_BGP_VNC */
+
   if (name)
     {
       bgp->name = XSTRDUP(MTYPE_BGP, name);
@@ -3165,6 +3175,11 @@ bgp_delete (struct bgp *bgp)
 
   /* TODO - Other memory may need to be freed - e.g., NHT */
 
+#if ENABLE_BGP_VNC
+  rfapi_delete(bgp);
+  bgp_cleanup_routes();         /* rfapi cleanup can create route entries! */
+#endif
+
   /* Remove visibility via the master list - there may however still be
    * routes to be processed still referencing the struct bgp.
    */
@@ -5233,6 +5248,9 @@ peer_distribute_update (struct access_list *access)
                  }
              }
        }
+#if ENABLE_BGP_VNC
+      vnc_prefix_list_update(bgp);
+#endif
     }
 }
 
@@ -7337,6 +7355,12 @@ bgp_config_write (struct vty *vty)
       /* ENCAPv6 configuration.  */
       write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_ENCAP);
 
+#if ENABLE_BGP_VNC
+      write += bgp_rfapi_cfg_write(vty, bgp);
+#endif
+
+      vty_out (vty, " exit%s", VTY_NEWLINE);
+
       write++;
     }
   return write;
@@ -7407,6 +7431,10 @@ bgp_init (void)
   /* Init zebra. */
   bgp_zebra_init(bm->master);
 
+#if ENABLE_BGP_VNC
+  vnc_zebra_init (bm->master);
+#endif
+
   /* BGP VTY commands installation.  */
   bgp_vty_init ();
 
@@ -7419,6 +7447,9 @@ bgp_init (void)
   bgp_scan_vty_init();
   bgp_mplsvpn_init ();
   bgp_encap_init ();
+#if ENABLE_BGP_VNC
+  rfapi_init ();
+#endif
 
   /* Access list initialize. */
   access_list_init ();
diff --git a/bgpd/bgpd.conf.vnc.sample b/bgpd/bgpd.conf.vnc.sample
new file mode 100644 (file)
index 0000000..863abde
--- /dev/null
@@ -0,0 +1,89 @@
+hostname H192.1.1.1
+password zebra
+#enable password zebra
+log stdout notifications
+log monitor notifications
+#debug bgp
+
+line vty
+exec-timeout 1000
+exit
+
+
+router bgp 64512
+
+    # Must set a router-id if no zebra (default 0.0.0.0)
+    bgp router-id 192.1.1.1
+
+    neighbor 192.1.1.2 remote-as 64512
+    neighbor 192.1.1.2 description H192.1.1.2
+    neighbor 192.1.1.2 update-source 192.1.1.1
+    neighbor 192.1.1.2 advertisement-interval 1
+    no neighbor 192.1.1.2 activate
+
+    neighbor 192.1.1.3 remote-as 64512
+    neighbor 192.1.1.3 description H192.1.1.3
+    neighbor 192.1.1.3 update-source 192.1.1.1
+    neighbor 192.1.1.3 advertisement-interval 1
+    no neighbor 192.1.1.3 activate
+
+    address-family vpnv4
+        neighbor 192.1.1.2 activate
+        neighbor 192.1.1.3 activate
+        exit-address-family
+
+    address-family vpnv6
+        neighbor 192.1.1.2 activate
+        neighbor 192.1.1.3 activate
+        exit-address-family
+
+    vnc defaults
+       rd auto:vn:5226
+       response-lifetime 45
+       rt both 1000:1 1000:2
+       exit-vnc
+
+    vnc nve-group group1
+       prefix vn 172.16.0.0/16
+       exit-vnc
+
+    vnc nve-group red
+       prefix vn 10.0.0.0/8
+       rd auto:vn:10
+       rt both 1000:10
+       exit-vnc
+
+    vnc nve-group blue
+       prefix vn 20.0.0.0/8
+       rd auto:vn:20
+       rt both 1000:20
+       exit-vnc
+
+    vnc nve-group green
+       prefix vn 30.0.0.0/8
+       rd auto:vn:20
+       rt both 1000:30
+       exit-vnc
+
+    vnc nve-group rfc4291v6c
+       prefix vn ::ac10:0/112
+       rd auto:vn:5227
+       rt both 2000:1
+       exit-vnc
+
+    vnc nve-group rfc4291v6m
+       prefix vn ::ffff:ac10:0/112
+       rd auto:vn:5528
+       rt both 3000:1
+       exit-vnc
+
+    vnc nve-group rfc6052v6
+       prefix vn 64:ff9b::ac10:0/112
+       rd auto:vn:5529
+       rt both 4000:1
+       exit-vnc
+
+exit
+
+
+
index a6d3b61e550271b84884e446cfedf820f54e8a77..2aa90e4877cdb88e379775f8c3418df003a17b78 100644 (file)
@@ -351,6 +351,11 @@ struct bgp
 
   u_int32_t addpath_tx_id;
   int addpath_tx_used[AFI_MAX][SAFI_MAX];
+
+#if ENABLE_BGP_VNC
+  struct rfapi_cfg *rfapi_cfg;
+  struct rfapi *rfapi;
+#endif
 };
 
 #define BGP_ROUTE_ADV_HOLD(bgp) (bgp->main_peers_update_hold)
@@ -417,6 +422,8 @@ struct bgp_rd
 #define RMAP_OUT 1
 #define RMAP_MAX 2
 
+#include "filter.h"
+
 /* BGP filter structure. */
 struct bgp_filter
 {
@@ -657,6 +664,9 @@ struct peer
 #define PEER_FLAG_DYNAMIC_NEIGHBOR          (1 << 12) /* dynamic neighbor */
 #define PEER_FLAG_CAPABILITY_ENHE           (1 << 13) /* Extended next-hop (rfc 5549)*/
 #define PEER_FLAG_IFPEER_V6ONLY             (1 << 14) /* if-based peer is v6 only */
+#if ENABLE_BGP_VNC
+#define PEER_FLAG_IS_RFAPI_HD              (1 << 15) /* attached to rfapi HD */
+#endif
 
   /* NSF mode (graceful restart) */
   u_char nsf[AFI_MAX][SAFI_MAX];
@@ -940,6 +950,9 @@ struct bgp_nlri
 #define BGP_ATTR_AS4_AGGREGATOR                 18
 #define BGP_ATTR_AS_PATHLIMIT                   21
 #define BGP_ATTR_ENCAP                          23
+#if ENABLE_BGP_VNC
+#define BGP_ATTR_VNC                           255
+#endif
 
 /* BGP update origin.  */
 #define BGP_ORIGIN_IGP                           0
@@ -1054,6 +1067,7 @@ struct bgp_nlri
 
 /* RFC4364 */
 #define SAFI_MPLS_LABELED_VPN                  128
+#define BGP_SAFI_VPN                           128
 
 /* BGP uptime string length.  */
 #define BGP_UPTIME_LEN 25
@@ -1506,4 +1520,8 @@ bgp_vrf_unlink (struct bgp *bgp, struct vrf *vrf)
 }
 
 extern void bgp_update_redist_vrf_bitmaps (struct bgp*, vrf_id_t);
+
+/* For benefit of rfapi */
+extern struct peer * peer_new (struct bgp *bgp);
+
 #endif /* _QUAGGA_BGPD_H */
diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c
new file mode 100644 (file)
index 0000000..57fb04d
--- /dev/null
@@ -0,0 +1,4706 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "memory.h"
+#include "linklist.h"
+#include "table.h"
+#include "plist.h"
+#include "routemap.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_route.h"
+
+#include "bgpd/bgp_ecommunity.h"
+#include "rfapi.h"
+#include "bgp_rfapi_cfg.h"
+#include "rfapi_backend.h"
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_monitor.h"
+#include "vnc_zebra.h"
+#include "vnc_export_bgp.h"
+#include "vnc_export_bgp_p.h"
+#include "rfapi_vty.h"
+#include "vnc_import_bgp.h"
+
+#if ENABLE_BGP_VNC
+
+#undef BGP_VNC_DEBUG_MATCH_GROUP
+
+
+DEFINE_MGROUP(RFAPI, "rfapi")
+DEFINE_MTYPE(RFAPI, RFAPI_CFG,                   "NVE Configuration")
+DEFINE_MTYPE(RFAPI, RFAPI_GROUP_CFG,             "NVE Group Configuration")
+DEFINE_MTYPE(RFAPI, RFAPI_L2_CFG,                "RFAPI L2 Group Configuration")
+DEFINE_MTYPE(RFAPI, RFAPI_RFP_GROUP_CFG,         "RFAPI RFP Group Configuration")
+DEFINE_MTYPE(RFAPI, RFAPI,                       "RFAPI Generic")
+DEFINE_MTYPE(RFAPI, RFAPI_DESC,                          "RFAPI Descriptor")
+DEFINE_MTYPE(RFAPI, RFAPI_IMPORTTABLE,           "RFAPI Import Table")
+DEFINE_MTYPE(RFAPI, RFAPI_MONITOR,               "RFAPI Monitor VPN")
+DEFINE_MTYPE(RFAPI, RFAPI_MONITOR_ENCAP,         "RFAPI Monitor Encap")
+DEFINE_MTYPE(RFAPI, RFAPI_NEXTHOP,               "RFAPI Next Hop")
+DEFINE_MTYPE(RFAPI, RFAPI_VN_OPTION,             "RFAPI VN Option")
+DEFINE_MTYPE(RFAPI, RFAPI_UN_OPTION,             "RFAPI UN Option")
+DEFINE_MTYPE(RFAPI, RFAPI_WITHDRAW,              "RFAPI Withdraw")
+DEFINE_MTYPE(RFAPI, RFAPI_RFG_NAME,              "RFAPI RFGName")
+DEFINE_MTYPE(RFAPI, RFAPI_ADB,                   "RFAPI Advertisement Data")
+DEFINE_MTYPE(RFAPI, RFAPI_ETI,                   "RFAPI Export Table Info")
+DEFINE_MTYPE(RFAPI, RFAPI_NVE_ADDR,              "RFAPI NVE Address")
+DEFINE_MTYPE(RFAPI, RFAPI_PREFIX_BAG,            "RFAPI Prefix Bag")
+DEFINE_MTYPE(RFAPI, RFAPI_IT_EXTRA,              "RFAPI IT Extra")
+DEFINE_MTYPE(RFAPI, RFAPI_INFO,                          "RFAPI Info")
+DEFINE_MTYPE(RFAPI, RFAPI_ADDR,                          "RFAPI Addr")
+DEFINE_MTYPE(RFAPI, RFAPI_UPDATED_RESPONSE_QUEUE, "RFAPI Updated Rsp Queue")
+DEFINE_MTYPE(RFAPI, RFAPI_RECENT_DELETE,         "RFAPI Recently Deleted Route")
+DEFINE_MTYPE(RFAPI, RFAPI_L2ADDR_OPT,            "RFAPI L2 Address Option")
+DEFINE_MTYPE(RFAPI, RFAPI_AP,                    "RFAPI Advertised Prefix")
+DEFINE_MTYPE(RFAPI, RFAPI_MONITOR_ETH,           "RFAPI Monitor Ethernet")
+
+/***********************************************************************
+ *                     RFAPI Support
+ ***********************************************************************/
+
+
+/* 
+ * compaitibility to old quagga_time call
+ * time_t value in terms of stabilised absolute time. 
+ * replacement for POSIX time()
+ */
+time_t 
+rfapi_time (time_t *t)
+{
+  time_t clock = bgp_clock();
+  if (t)
+    *t = clock;
+  return clock;
+}
+
+void
+nve_group_to_nve_list (
+  struct rfapi_nve_group_cfg   *rfg,
+  struct list                  **nves,
+  uint8_t                      family)     /* AF_INET, AF_INET6 */
+{
+  struct listnode *hln;
+  struct rfapi_descriptor *rfd;
+
+  /*
+   * loop over nves in this grp, add to list
+   */
+  for (ALL_LIST_ELEMENTS_RO (rfg->nves, hln, rfd))
+    {
+      if (rfd->vn_addr.addr_family == family)
+        {
+          if (!*nves)
+            *nves = list_new ();
+          listnode_add (*nves, rfd);
+        }
+    }
+}
+
+
+struct rfapi_nve_group_cfg *
+bgp_rfapi_cfg_match_group (
+  struct rfapi_cfg     *hc,
+  struct prefix                *vn,
+  struct prefix                *un)
+{
+  struct rfapi_nve_group_cfg *rfg_vn = NULL;
+  struct rfapi_nve_group_cfg *rfg_un = NULL;
+
+  struct route_table *rt_vn;
+  struct route_table *rt_un;
+  struct route_node *rn_vn;
+  struct route_node *rn_un;
+
+  struct rfapi_nve_group_cfg *rfg;
+  struct listnode *node, *nnode;
+
+  switch (vn->family)
+    {
+    case AF_INET:
+      rt_vn = &(hc->nve_groups_vn[AFI_IP]);
+      break;
+    case AF_INET6:
+      rt_vn = &(hc->nve_groups_vn[AFI_IP6]);
+      break;
+    default:
+      return NULL;
+    }
+
+  switch (un->family)
+    {
+    case AF_INET:
+      rt_un = &(hc->nve_groups_un[AFI_IP]);
+      break;
+    case AF_INET6:
+      rt_un = &(hc->nve_groups_un[AFI_IP6]);
+      break;
+    default:
+      return NULL;
+    }
+
+  rn_vn = route_node_match (rt_vn, vn); /* NB locks node */
+  if (rn_vn)
+    {
+      rfg_vn = rn_vn->info;
+      route_unlock_node (rn_vn);
+    }
+
+  rn_un = route_node_match (rt_un, un); /* NB locks node */
+  if (rn_un)
+    {
+      rfg_un = rn_un->info;
+      route_unlock_node (rn_un);
+    }
+
+#if BGP_VNC_DEBUG_MATCH_GROUP
+  {
+    char buf[BUFSIZ];
+
+    prefix2str (vn, buf, BUFSIZ);
+    zlog_debug ("%s: vn prefix: %s", __func__, buf);
+
+    prefix2str (un, buf, BUFSIZ);
+    zlog_debug ("%s: un prefix: %s", __func__, buf);
+
+    zlog_debug ("%s: rn_vn=%p, rn_un=%p, rfg_vn=%p, rfg_un=%p",
+                __func__, rn_vn, rn_un, rfg_vn, rfg_un);
+  }
+#endif
+
+
+  if (rfg_un == rfg_vn)         /* same group */
+    return rfg_un;
+  if (!rfg_un)                  /* un doesn't match, return vn-matched grp */
+    return rfg_vn;
+  if (!rfg_vn)                  /* vn doesn't match, return un-matched grp */
+    return rfg_un;
+
+  /* 
+   * Two different nve groups match: the group configured earlier wins.
+   * For now, just walk the sequential list and pick the first one.
+   * If this approach is too slow, then store serial numbers in the
+   * nve group structures as they are defined and just compare
+   * serial numbers.
+   */
+  for (ALL_LIST_ELEMENTS (hc->nve_groups_sequential, node, nnode, rfg))
+    {
+      if ((rfg == rfg_un) || (rfg == rfg_vn))
+        {
+          return rfg;
+        }
+    }
+  zlog_debug ("%s: shouldn't happen, returning NULL when un and vn match",
+              __func__);
+  return NULL;                  /* shouldn't happen */
+}
+
+/*------------------------------------------
+ * rfapi_get_rfp_start_val
+ *
+ * Returns value passed to rfapi on rfp_start
+ *
+ * input:
+ *     void *          bgp structure
+ *
+ * returns:
+ *     void *          
+ *------------------------------------------*/
+void *
+rfapi_get_rfp_start_val (void *bgpv)
+{
+  struct bgp *bgp = bgpv;
+  if (bgp == NULL || bgp->rfapi == NULL)
+    return NULL;
+  return bgp->rfapi->rfp;
+}
+
+/*------------------------------------------
+ * bgp_rfapi_is_vnc_configured
+ *
+ * Returns if VNC (BGP VPN messaging /VPN & encap SAFIs) are configured
+ *
+ * input: 
+ *    bgp        NULL (=use default instance)
+ *
+ * output:
+ *
+ * return value: If VNC is configured for the bgpd instance
+ *     0               Success
+ *     ENXIO           VNC not configured
+ --------------------------------------------*/
+int
+bgp_rfapi_is_vnc_configured (struct bgp *bgp)
+{
+  if (bgp == NULL)
+    bgp = bgp_get_default ();
+
+  if (bgp && bgp->rfapi_cfg)
+    {
+      struct peer *peer;
+      struct peer_group *group;
+      struct listnode *node, *nnode;
+      /* if have configured VPN neighbors, assume running VNC */
+      for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group))
+        {
+          if (group->conf->afc[AFI_IP][SAFI_MPLS_VPN] ||
+              group->conf->afc[AFI_IP6][SAFI_MPLS_VPN])
+            return 0;
+        }
+      for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+        {
+          if (peer->afc[AFI_IP][SAFI_MPLS_VPN] ||
+              peer->afc[AFI_IP6][SAFI_MPLS_VPN])
+            return 0;
+        }
+    }
+  return ENXIO;
+}
+
+/***********************************************************************
+ *                     VNC Configuration/CLI
+ ***********************************************************************/
+
+
+DEFUN (vnc_advertise_un_method,
+       vnc_advertise_un_method_cmd,
+       "vnc advertise-un-method (encap-safi|encap-attr)",
+       VNC_CONFIG_STR
+       "Method of advertising UN addresses\n"
+       "Via Encapsulation SAFI\n"
+       "Via Tunnel Encap attribute (in VPN SAFI)\n")
+{
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "VNC not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+
+  if (!strncmp (argv[0], "encap-safi", 7))
+    {
+      bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP;
+    }
+  else
+    {
+      bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/*-------------------------------------------------------------------------
+ *                     RFG defaults
+ *-----------------------------------------------------------------------*/
+
+
+DEFUN (vnc_defaults,
+       vnc_defaults_cmd,
+       "vnc defaults", VNC_CONFIG_STR "Configure default NVE group\n")
+{
+  vty->node = BGP_VNC_DEFAULTS_NODE;
+  return CMD_SUCCESS;
+}
+
+static int
+set_ecom_list (
+  struct vty           *vty,
+  int                  argc,
+  const char           **argv,
+  struct ecommunity    **list)
+{
+  struct ecommunity *ecom = NULL;
+  struct ecommunity *ecomadd;
+
+  for (; argc; --argc, ++argv)
+    {
+
+      ecomadd = ecommunity_str2com (*argv, ECOMMUNITY_ROUTE_TARGET, 0);
+      if (!ecomadd)
+        {
+          vty_out (vty, "Malformed community-list value%s", VTY_NEWLINE);
+          if (ecom)
+            ecommunity_free (&ecom);
+          return CMD_WARNING;
+        }
+
+      if (ecom)
+        {
+          ecommunity_merge (ecom, ecomadd);
+          ecommunity_free (&ecomadd);
+        }
+      else
+        {
+          ecom = ecomadd;
+        }
+    }
+
+  if (*list)
+    {
+      ecommunity_free (&*list);
+    }
+  *list = ecom;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_defaults_rt_import,
+       vnc_defaults_rt_import_cmd,
+       "rt import .RTLIST",
+       "Specify default route targets\n"
+       "Import filter\n"
+       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+  struct bgp *bgp = vty->index;
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return set_ecom_list (vty, argc, argv,
+                        &bgp->rfapi_cfg->default_rt_import_list);
+}
+
+DEFUN (vnc_defaults_rt_export,
+       vnc_defaults_rt_export_cmd,
+       "rt export .RTLIST",
+       "Configure default route targets\n"
+       "Export filter\n"
+       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+  struct bgp *bgp = vty->index;
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return set_ecom_list (vty, argc, argv,
+                        &bgp->rfapi_cfg->default_rt_export_list);
+}
+
+DEFUN (vnc_defaults_rt_both,
+       vnc_defaults_rt_both_cmd,
+       "rt both .RTLIST",
+       "Configure default route targets\n"
+       "Export+import filters\n"
+       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+  int rc;
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  rc =
+    set_ecom_list (vty, argc, argv, &bgp->rfapi_cfg->default_rt_import_list);
+  if (rc != CMD_SUCCESS)
+    return rc;
+  return set_ecom_list (vty, argc, argv,
+                        &bgp->rfapi_cfg->default_rt_export_list);
+}
+
+DEFUN (vnc_defaults_rd,
+       vnc_defaults_rd_cmd,
+       "rd ASN:nn_or_IP-address:nn",
+       "Specify default route distinguisher\n"
+       "Route Distinguisher (<as-number>:<number> | <ip-address>:<number> | auto:vn:<number> )\n")
+{
+  int ret;
+  struct prefix_rd prd;
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strncmp (argv[0], "auto:vn:", 8))
+    {
+      /*
+       * use AF_UNIX to designate automatically-assigned RD
+       * auto:vn:nn where nn is a 2-octet quantity
+       */
+      char *end = NULL;
+      uint32_t value32 = strtoul (argv[0] + 8, &end, 10);
+      uint16_t value = value32 & 0xffff;
+
+      if (!*(argv[0] + 5) || *end)
+        {
+          vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      if (value32 > 0xffff)
+        {
+          vty_out (vty, "%% Malformed rd (must be less than %u%s",
+                   0x0ffff, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+
+      memset (&prd, 0, sizeof (prd));
+      prd.family = AF_UNIX;
+      prd.prefixlen = 64;
+      prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff;
+      prd.val[1] = RD_TYPE_IP & 0x0ff;
+      prd.val[6] = (value >> 8) & 0x0ff;
+      prd.val[7] = value & 0x0ff;
+
+    }
+  else
+    {
+
+      ret = str2prefix_rd (argv[0], &prd);
+      if (!ret)
+        {
+          vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  bgp->rfapi_cfg->default_rd = prd;
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_defaults_l2rd,
+       vnc_defaults_l2rd_cmd,
+       "l2rd (ID|auto:vn)",
+       "Specify default Local Nve ID value to use in RD for L2 routes\n"
+       "Fixed value 1-255\n"
+       "use the low-order octet of the NVE's VN address\n")
+{
+  struct bgp *bgp = vty->index;
+  uint8_t value = 0;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[0], "auto:vn"))
+    {
+      value = 0;
+    }
+  else
+    {
+      char *end = NULL;
+      unsigned long value_l = strtoul (argv[0], &end, 10);
+
+      value = value_l & 0xff;
+      if (!*(argv[0]) || *end)
+        {
+          vty_out (vty, "%% Malformed l2 nve ID \"%s\"%s", argv[0],
+                   VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      if ((value_l < 1) || (value_l > 0xff))
+        {
+          vty_out (vty,
+                   "%% Malformed l2 nve id (must be greater than 0 and less than %u%s",
+                   0x100, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+  bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_L2RD;
+  bgp->rfapi_cfg->default_l2rd = value;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_defaults_no_l2rd,
+       vnc_defaults_no_l2rd_cmd,
+       "no l2rd",
+       NO_STR
+       "Specify default Local Nve ID value to use in RD for L2 routes\n")
+{
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  bgp->rfapi_cfg->default_l2rd = 0;
+  bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_L2RD;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_defaults_responselifetime,
+       vnc_defaults_responselifetime_cmd,
+       "response-lifetime (LIFETIME|infinite)",
+       "Specify default response lifetime\n"
+       "Response lifetime in seconds\n" "Infinite response lifetime\n")
+{
+  uint32_t rspint;
+  struct bgp *bgp = vty->index;
+  struct rfapi *h = NULL;
+  struct listnode *hdnode;
+  struct rfapi_descriptor *rfd;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  h = bgp->rfapi;
+  if (!h)
+    return CMD_WARNING;
+
+  if (!strcmp (argv[0], "infinite"))
+    {
+      rspint = RFAPI_INFINITE_LIFETIME;
+    }
+  else
+    {
+      VTY_GET_INTEGER ("Response Lifetime", rspint, argv[0]);
+      if (rspint > INT32_MAX)
+        rspint = INT32_MAX;     /* is really an int, not an unsigned int */
+    }
+
+  bgp->rfapi_cfg->default_response_lifetime = rspint;
+
+  for (ALL_LIST_ELEMENTS_RO (&h->descriptors, hdnode, rfd))
+    if (rfd->rfg && !(rfd->rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME))
+      rfd->response_lifetime = rfd->rfg->response_lifetime = rspint;
+
+  return CMD_SUCCESS;
+}
+
+static struct rfapi_nve_group_cfg *
+rfapi_group_lookup_byname (struct bgp *bgp, const char *name)
+{
+  struct rfapi_nve_group_cfg *rfg;
+  struct listnode *node, *nnode;
+
+  for (ALL_LIST_ELEMENTS
+       (bgp->rfapi_cfg->nve_groups_sequential, node, nnode, rfg))
+    {
+      if (!strcmp (rfg->name, name))
+        return rfg;
+    }
+  return NULL;
+}
+
+static struct rfapi_nve_group_cfg *
+rfapi_group_new ()
+{
+  return XCALLOC (MTYPE_RFAPI_GROUP_CFG, sizeof (struct rfapi_nve_group_cfg));
+}
+
+static struct rfapi_l2_group_cfg *
+rfapi_l2_group_lookup_byname (struct bgp *bgp, const char *name)
+{
+  struct rfapi_l2_group_cfg *rfg;
+  struct listnode *node, *nnode;
+
+  if (bgp->rfapi_cfg->l2_groups == NULL)        /* not the best place for this */
+    bgp->rfapi_cfg->l2_groups = list_new ();
+
+  for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->l2_groups, node, nnode, rfg))
+    {
+      if (!strcmp (rfg->name, name))
+        return rfg;
+    }
+  return NULL;
+}
+
+static struct rfapi_l2_group_cfg *
+rfapi_l2_group_new ()
+{
+  return XCALLOC (MTYPE_RFAPI_L2_CFG, sizeof (struct rfapi_l2_group_cfg));
+}
+
+static void
+rfapi_l2_group_del (struct rfapi_l2_group_cfg *rfg)
+{
+  XFREE (MTYPE_RFAPI_L2_CFG, rfg);
+}
+
+static int
+rfapi_str2route_type (
+  const char   *l3str,
+  const char   *pstr,
+  afi_t                *afi,
+  int          *type)
+{
+  if (!l3str || !pstr)
+    return EINVAL;
+
+  if (!strcmp (l3str, "ipv4"))
+    {
+      *afi = AFI_IP;
+    }
+  else
+    {
+      if (!strcmp (l3str, "ipv6"))
+        *afi = AFI_IP6;
+      else
+        return ENOENT;
+    }
+
+  if (!strcmp (pstr, "connected"))
+    *type = ZEBRA_ROUTE_CONNECT;
+  if (!strcmp (pstr, "kernel"))
+    *type = ZEBRA_ROUTE_KERNEL;
+  if (!strcmp (pstr, "static"))
+    *type = ZEBRA_ROUTE_STATIC;
+  if (!strcmp (pstr, "bgp"))
+    *type = ZEBRA_ROUTE_BGP;
+  if (!strcmp (pstr, "bgp-direct"))
+    *type = ZEBRA_ROUTE_BGP_DIRECT;
+  if (!strcmp (pstr, "bgp-direct-to-nve-groups"))
+    *type = ZEBRA_ROUTE_BGP_DIRECT_EXT;
+
+  if (!strcmp (pstr, "rip"))
+    {
+      if (*afi == AFI_IP)
+        *type = ZEBRA_ROUTE_RIP;
+      else
+        *type = ZEBRA_ROUTE_RIPNG;
+    }
+
+  if (!strcmp (pstr, "ripng"))
+    {
+      if (*afi == AFI_IP)
+        return EAFNOSUPPORT;
+      *type = ZEBRA_ROUTE_RIPNG;
+    }
+
+  if (!strcmp (pstr, "ospf"))
+    {
+      if (*afi == AFI_IP)
+        *type = ZEBRA_ROUTE_OSPF;
+      else
+        *type = ZEBRA_ROUTE_OSPF6;
+    }
+
+  if (!strcmp (pstr, "ospf6"))
+    {
+      if (*afi == AFI_IP)
+        return EAFNOSUPPORT;
+      *type = ZEBRA_ROUTE_OSPF6;
+    }
+
+  return 0;
+}
+
+/*-------------------------------------------------------------------------
+ *                     redistribute
+ *-----------------------------------------------------------------------*/
+
+#define VNC_REDIST_ENABLE(bgp, afi, type) do {                 \
+    switch (type) {                                            \
+       case ZEBRA_ROUTE_BGP_DIRECT:                            \
+           vnc_import_bgp_redist_enable((bgp), (afi));         \
+           break;                                              \
+       case ZEBRA_ROUTE_BGP_DIRECT_EXT:                        \
+           vnc_import_bgp_exterior_redist_enable((bgp), (afi));\
+           break;                                              \
+       default:                                                \
+           vnc_redistribute_set((bgp), (afi), (type));         \
+           break;                                              \
+    }                                                          \
+} while (0)
+
+#define VNC_REDIST_DISABLE(bgp, afi, type) do {                        \
+    switch (type) {                                            \
+       case ZEBRA_ROUTE_BGP_DIRECT:                            \
+           vnc_import_bgp_redist_disable((bgp), (afi));        \
+           break;                                              \
+       case ZEBRA_ROUTE_BGP_DIRECT_EXT:                        \
+           vnc_import_bgp_exterior_redist_disable((bgp), (afi));\
+           break;                                              \
+       default:                                                \
+           vnc_redistribute_unset((bgp), (afi), (type));       \
+           break;                                              \
+    }                                                          \
+} while (0)
+
+static uint8_t redist_was_enabled[AFI_MAX][ZEBRA_ROUTE_MAX];
+
+static void
+vnc_redistribute_prechange (struct bgp *bgp)
+{
+  afi_t afi;
+  int type;
+
+  zlog_debug ("%s: entry", __func__);
+  memset (redist_was_enabled, 0, sizeof (redist_was_enabled));
+
+  /*
+   * Look to see if we have any redistribution enabled. If so, flush
+   * the corresponding routes and turn off redistribution temporarily.
+   * We need to do it because the RD's used for the redistributed
+   * routes depend on the nve group.
+   */
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+      for (type = 0; type < ZEBRA_ROUTE_MAX; ++type)
+        {
+          if (bgp->rfapi_cfg->redist[afi][type])
+            {
+              redist_was_enabled[afi][type] = 1;
+              VNC_REDIST_DISABLE (bgp, afi, type);
+            }
+        }
+    }
+  zlog_debug ("%s: return", __func__);
+}
+
+static void
+vnc_redistribute_postchange (struct bgp *bgp)
+{
+  afi_t afi;
+  int type;
+
+  zlog_debug ("%s: entry", __func__);
+  /*
+   * If we turned off redistribution above, turn it back on. Doing so
+   * will tell zebra to resend the routes to us
+   */
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+      for (type = 0; type < ZEBRA_ROUTE_MAX; ++type)
+        {
+          if (redist_was_enabled[afi][type])
+            {
+              VNC_REDIST_ENABLE (bgp, afi, type);
+            }
+        }
+    }
+  zlog_debug ("%s: return", __func__);
+}
+
+DEFUN (vnc_redistribute_rh_roo_localadmin,
+       vnc_redistribute_rh_roo_localadmin_cmd,
+       "vnc redistribute resolve-nve roo-ec-local-admin <0-65535>",
+       VNC_CONFIG_STR
+       "Redistribute routes into VNC\n"
+       "Resolve-NVE mode\n"
+       "Route Origin Extended Community Local Admin Field\n" "Field value\n")
+{
+  struct bgp *bgp = vty->index;
+  uint32_t localadmin;
+  char *endptr;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  localadmin = strtoul (argv[0], &endptr, 0);
+  if (!*(argv[0]) || *endptr)
+    {
+      vty_out (vty, "%% Malformed value%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (localadmin > 0xffff)
+    {
+      vty_out (vty, "%% Value out of range (0-%d)%s", 0xffff, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (bgp->rfapi_cfg->resolve_nve_roo_local_admin == localadmin)
+    return CMD_SUCCESS;
+
+  if ((bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) ==
+      BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE)
+    {
+
+      vnc_export_bgp_prechange (bgp);
+    }
+  vnc_redistribute_prechange (bgp);
+
+  bgp->rfapi_cfg->resolve_nve_roo_local_admin = localadmin;
+
+  if ((bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) ==
+      BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE)
+    {
+
+      vnc_export_bgp_postchange (bgp);
+    }
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (vnc_redistribute_mode,
+       vnc_redistribute_mode_cmd,
+       "vnc redistribute mode (nve-group|plain|resolve-nve)",
+       VNC_CONFIG_STR
+       "Redistribute routes into VNC\n"
+       "Redistribution mode\n"
+       "Based on redistribute nve-group\n"
+       "Unmodified\n" "Resolve each nexthop to connected NVEs\n")
+{
+  struct bgp *bgp = vty->index;
+  vnc_redist_mode_t newmode;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+
+  switch (*argv[0])
+    {
+    case 'n':
+      newmode = VNC_REDIST_MODE_RFG;
+      break;
+
+    case 'p':
+      newmode = VNC_REDIST_MODE_PLAIN;
+      break;
+
+    case 'r':
+      newmode = VNC_REDIST_MODE_RESOLVE_NVE;
+      break;
+
+    default:
+      vty_out (vty, "unknown redistribute mode%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (newmode != bgp->rfapi_cfg->redist_mode)
+    {
+      vnc_redistribute_prechange (bgp);
+      bgp->rfapi_cfg->redist_mode = newmode;
+      vnc_redistribute_postchange (bgp);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redistribute_protocol,
+       vnc_redistribute_protocol_cmd,
+       "vnc redistribute (ipv4|ipv6) (bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static)",
+       VNC_CONFIG_STR
+       "Redistribute routes into VNC\n"
+       "IPv4 routes\n"
+       "IPv6 routes\n"
+       "From BGP\n"
+       "From BGP without Zebra\n"
+       "From BGP without Zebra, only to configured NVE groups\n"
+       "Connected interfaces\n"
+       "From kernel routes\n"
+       "From Open Shortest Path First (OSPF)\n"
+       "From Routing Information Protocol (RIP)\n" "From Static routes\n")
+{
+  int type = ZEBRA_ROUTE_MAX;     /* init to bogus value */
+  struct bgp *bgp = vty->index;
+  afi_t afi;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (rfapi_str2route_type (argv[0], argv[1], &afi, &type))
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT)
+    {
+      if (bgp->rfapi_cfg->redist_bgp_exterior_view_name)
+        {
+          VNC_REDIST_DISABLE (bgp, afi, type);  /* disabled view implicitly */
+          free (bgp->rfapi_cfg->redist_bgp_exterior_view_name);
+          bgp->rfapi_cfg->redist_bgp_exterior_view_name = NULL;
+        }
+      bgp->rfapi_cfg->redist_bgp_exterior_view = bgp;
+    }
+
+  VNC_REDIST_ENABLE (bgp, afi, type);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_no_redistribute_protocol,
+       vnc_no_redistribute_protocol_cmd,
+       "no vnc redistribute (ipv4|ipv6) (bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static)",
+       NO_STR
+       VNC_CONFIG_STR
+       "Redistribute from other protocol\n"
+       "IPv4 routes\n"
+       "IPv6 routes\n"
+       "From BGP\n"
+       "From BGP without Zebra\n"
+       "From BGP without Zebra, only to configured NVE groups\n"
+       "Connected interfaces\n"
+       "From kernel routes\n"
+       "From Open Shortest Path First (OSPF)\n"
+       "From Routing Information Protocol (RIP)\n" "From Static routes\n")
+{
+  int type;
+  struct bgp *bgp = vty->index;
+  afi_t afi;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (rfapi_str2route_type (argv[0], argv[1], &afi, &type))
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VNC_REDIST_DISABLE (bgp, afi, type);
+
+  if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT)
+    {
+      if (bgp->rfapi_cfg->redist_bgp_exterior_view_name)
+        {
+          free (bgp->rfapi_cfg->redist_bgp_exterior_view_name);
+          bgp->rfapi_cfg->redist_bgp_exterior_view_name = NULL;
+        }
+      bgp->rfapi_cfg->redist_bgp_exterior_view = NULL;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redistribute_bgp_exterior,
+       vnc_redistribute_bgp_exterior_cmd,
+       "vnc redistribute (ipv4|ipv6) bgp-direct-to-nve-groups view NAME",
+       VNC_CONFIG_STR
+       "Redistribute routes into VNC\n"
+       "IPv4 routes\n"
+       "IPv6 routes\n"
+       "From BGP without Zebra, only to configured NVE groups\n"
+       "From BGP view\n" "BGP view name\n")
+{
+  int type;
+  struct bgp *bgp = vty->index;
+  afi_t afi;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (rfapi_str2route_type (argv[0], "bgp-direct-to-nve-groups", &afi, &type))
+    {
+      vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (bgp->rfapi_cfg->redist_bgp_exterior_view_name)
+    free (bgp->rfapi_cfg->redist_bgp_exterior_view_name);
+  bgp->rfapi_cfg->redist_bgp_exterior_view_name = strdup (argv[1]);
+  /* could be NULL if name is not defined yet */
+  bgp->rfapi_cfg->redist_bgp_exterior_view = bgp_lookup_by_name (argv[1]);
+
+  VNC_REDIST_ENABLE (bgp, afi, type);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redistribute_nvegroup,
+       vnc_redistribute_nvegroup_cmd,
+       "vnc redistribute nve-group NAME",
+       VNC_CONFIG_STR
+       "Assign a NVE group to routes redistributed from another routing protocol\n"
+       "NVE group\n" "Group name\n")
+{
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  /*
+   * OK if nve group doesn't exist yet; we'll set the pointer
+   * when the group is defined later
+   */
+  bgp->rfapi_cfg->rfg_redist = rfapi_group_lookup_byname (bgp, argv[0]);
+  if (bgp->rfapi_cfg->rfg_redist_name)
+    free (bgp->rfapi_cfg->rfg_redist_name);
+  bgp->rfapi_cfg->rfg_redist_name = strdup (argv[0]);
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redistribute_no_nvegroup,
+       vnc_redistribute_no_nvegroup_cmd,
+       "no vnc redistribute nve-group",
+       NO_STR
+       VNC_CONFIG_STR
+       "Redistribute from other protocol\n"
+       "Assign a NVE group to routes redistributed from another routing protocol\n")
+{
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  bgp->rfapi_cfg->rfg_redist = NULL;
+  if (bgp->rfapi_cfg->rfg_redist_name)
+    free (bgp->rfapi_cfg->rfg_redist_name);
+  bgp->rfapi_cfg->rfg_redist_name = NULL;
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (vnc_redistribute_lifetime,
+       vnc_redistribute_lifetime_cmd,
+       "vnc redistribute lifetime (LIFETIME|infinite)",
+       VNC_CONFIG_STR
+       "Assign a lifetime to routes redistributed from another routing protocol\n"
+       "lifetime value (32 bit)\n")
+{
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  if (!strcmp (argv[0], "infinite"))
+    {
+      bgp->rfapi_cfg->redist_lifetime = RFAPI_INFINITE_LIFETIME;
+    }
+  else
+    {
+      VTY_GET_INTEGER ("Response Lifetime", bgp->rfapi_cfg->redist_lifetime,
+                       argv[0]);
+    }
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+/*-- redist policy, non-nvegroup start --*/
+
+DEFUN (vnc_redist_bgpdirect_no_prefixlist,
+       vnc_redist_bgpdirect_no_prefixlist_cmd,
+       "no vnc redistribute (bgp-direct|bgp-direct-to-nve-groups) (ipv4|ipv6) prefix-list",
+       NO_STR
+       VNC_CONFIG_STR
+       "Redistribute from other protocol\n"
+       "Redistribute from BGP directly\n"
+       "Redistribute from BGP without Zebra, only to configured NVE groups\n"
+       "IPv4 routes\n"
+       "IPv6 routes\n" "Prefix-list for filtering redistributed routes\n")
+{
+  struct bgp *bgp = vty->index;
+  afi_t afi;
+  struct rfapi_cfg *hc;
+  uint8_t route_type = 0;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[0], "bgp-direct"))
+    {
+      route_type = ZEBRA_ROUTE_BGP_DIRECT;
+    }
+  else
+    {
+      route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT;
+    }
+
+  if (!strcmp (argv[1], "ipv4"))
+    {
+      afi = AFI_IP;
+    }
+  else
+    {
+      afi = AFI_IP6;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  if (hc->plist_redist_name[route_type][afi])
+    free (hc->plist_redist_name[route_type][afi]);
+  hc->plist_redist_name[route_type][afi] = NULL;
+  hc->plist_redist[route_type][afi] = NULL;
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redist_bgpdirect_prefixlist,
+       vnc_redist_bgpdirect_prefixlist_cmd,
+       "vnc redistribute (bgp-direct|bgp-direct-to-nve-groups) (ipv4|ipv6) prefix-list NAME",
+       VNC_CONFIG_STR
+       "Redistribute from other protocol\n"
+       "Redistribute from BGP directly\n"
+       "Redistribute from BGP without Zebra, only to configured NVE groups\n"
+       "IPv4 routes\n"
+       "IPv6 routes\n"
+       "Prefix-list for filtering redistributed routes\n"
+       "prefix list name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_cfg *hc;
+  afi_t afi;
+  uint8_t route_type = 0;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[0], "bgp-direct"))
+    {
+      route_type = ZEBRA_ROUTE_BGP_DIRECT;
+    }
+  else
+    {
+      route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT;
+    }
+
+  if (!strcmp (argv[1], "ipv4"))
+    {
+      afi = AFI_IP;
+    }
+  else
+    {
+      afi = AFI_IP6;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  if (hc->plist_redist_name[route_type][afi])
+    free (hc->plist_redist_name[route_type][afi]);
+  hc->plist_redist_name[route_type][afi] = strdup (argv[2]);
+  hc->plist_redist[route_type][afi] = prefix_list_lookup (afi, argv[2]);
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redist_bgpdirect_no_routemap,
+       vnc_redist_bgpdirect_no_routemap_cmd,
+       "no vnc redistribute (bgp-direct|bgp-direct-to-nve-groups) route-map",
+       NO_STR
+       VNC_CONFIG_STR
+       "Redistribute from other protocols\n"
+       "Redistribute from BGP directly\n"
+       "Redistribute from BGP without Zebra, only to configured NVE groups\n"
+       "Route-map for filtering redistributed routes\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_cfg *hc;
+  uint8_t route_type = 0;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[0], "bgp-direct"))
+    {
+      route_type = ZEBRA_ROUTE_BGP_DIRECT;
+    }
+  else
+    {
+      route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  if (hc->routemap_redist_name[route_type])
+    free (hc->routemap_redist_name[route_type]);
+  hc->routemap_redist_name[route_type] = NULL;
+  hc->routemap_redist[route_type] = NULL;
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redist_bgpdirect_routemap,
+       vnc_redist_bgpdirect_routemap_cmd,
+       "vnc redistribute (bgp-direct|bgp-direct-to-nve-groups) route-map NAME",
+       VNC_CONFIG_STR
+       "Redistribute from other protocols\n"
+       "Redistribute from BGP directly\n"
+       "Redistribute from BGP without Zebra, only to configured NVE groups\n"
+       "Route-map for filtering exported routes\n" "route map name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_cfg *hc;
+  uint8_t route_type = 0;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[0], "bgp-direct"))
+    {
+      route_type = ZEBRA_ROUTE_BGP_DIRECT;
+    }
+  else
+    {
+      route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  if (hc->routemap_redist_name[route_type])
+    free (hc->routemap_redist_name[route_type]);
+  hc->routemap_redist_name[route_type] = strdup (argv[1]);
+  hc->routemap_redist[route_type] = route_map_lookup_by_name (argv[1]);
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+/*-- redist policy, non-nvegroup end --*/
+
+/*-- redist policy, nvegroup start --*/
+
+DEFUN (vnc_nve_group_redist_bgpdirect_no_prefixlist,
+       vnc_nve_group_redist_bgpdirect_no_prefixlist_cmd,
+       "no redistribute bgp-direct (ipv4|ipv6) prefix-list",
+       NO_STR
+       "Redistribute from other protocol\n"
+       "Redistribute from BGP directly\n"
+       "Disable redistribute filter\n"
+       "IPv4 routes\n"
+       "IPv6 routes\n" "Prefix-list for filtering redistributed routes\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_nve_group_cfg *rfg;
+  afi_t afi;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[0], "ipv4"))
+    {
+      afi = AFI_IP;
+    }
+  else
+    {
+      afi = AFI_IP6;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi])
+    free (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]);
+  rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi] = NULL;
+  rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi] = NULL;
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_redist_bgpdirect_prefixlist,
+       vnc_nve_group_redist_bgpdirect_prefixlist_cmd,
+       "redistribute bgp-direct (ipv4|ipv6) prefix-list NAME",
+       "Redistribute from other protocol\n"
+       "Redistribute from BGP directly\n"
+       "IPv4 routes\n"
+       "IPv6 routes\n"
+       "Prefix-list for filtering redistributed routes\n"
+       "prefix list name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_nve_group_cfg *rfg;
+  afi_t afi;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[0], "ipv4"))
+    {
+      afi = AFI_IP;
+    }
+  else
+    {
+      afi = AFI_IP6;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi])
+    free (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]);
+  rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi] = strdup (argv[1]);
+  rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi] =
+    prefix_list_lookup (afi, argv[1]);
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_redist_bgpdirect_no_routemap,
+       vnc_nve_group_redist_bgpdirect_no_routemap_cmd,
+       "no redistribute bgp-direct route-map",
+       NO_STR
+       "Redistribute from other protocols\n"
+       "Redistribute from BGP directly\n"
+       "Disable redistribute filter\n"
+       "Route-map for filtering redistributed routes\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_nve_group_cfg *rfg;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT])
+    free (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]);
+  rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT] = NULL;
+  rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT] = NULL;
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_redist_bgpdirect_routemap,
+       vnc_nve_group_redist_bgpdirect_routemap_cmd,
+       "redistribute bgp-direct route-map NAME",
+       "Redistribute from other protocols\n"
+       "Redistribute from BGP directly\n"
+       "Route-map for filtering exported routes\n" "route map name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_nve_group_cfg *rfg;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vnc_redistribute_prechange (bgp);
+
+  if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT])
+    free (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]);
+  rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT] = strdup (argv[0]);
+  rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT] =
+    route_map_lookup_by_name (argv[0]);
+
+  vnc_redistribute_postchange (bgp);
+
+  return CMD_SUCCESS;
+}
+
+/*-- redist policy, nvegroup end --*/
+
+/*-------------------------------------------------------------------------
+ *                     export
+ *-----------------------------------------------------------------------*/
+
+DEFUN (vnc_export_mode,
+       vnc_export_mode_cmd,
+       "vnc export (bgp|zebra) mode (group-nve|ce|none|registering-nve)",
+       VNC_CONFIG_STR
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "Select export mode\n"
+       "Export routes with nve-group next-hops\n"
+       "Export routes with NVE connected router next-hops\n"
+       "Disable export\n" "Export routes with registering NVE as next-hop\n")
+{
+  struct bgp *bgp = vty->index;
+  uint32_t oldmode = 0;
+  uint32_t newmode = 0;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "VNC not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (*argv[0] == 'b')
+    {
+      oldmode = bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS;
+      switch (*argv[1])
+        {
+        case 'g':
+          newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP;
+          break;
+        case 'c':
+          newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE;
+          break;
+        case 'n':
+          newmode = 0;
+          break;
+        case 'r':
+          newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH;
+          break;
+        default:
+          vty_out (vty, "Invalid mode specified%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+
+      if (newmode == oldmode)
+        {
+          vty_out (vty, "Mode unchanged%s", VTY_NEWLINE);
+          return CMD_SUCCESS;
+        }
+
+      vnc_export_bgp_prechange (bgp);
+
+      bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS;
+      bgp->rfapi_cfg->flags |= newmode;
+
+      vnc_export_bgp_postchange (bgp);
+
+
+    }
+  else
+    {
+      /*
+       * export to zebra with RH mode is not yet implemented
+       */
+      vty_out (vty, "Changing modes for zebra export not implemented yet%s",
+               VTY_NEWLINE);
+      return CMD_WARNING;
+
+      oldmode = bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS;
+      bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS;
+      switch (*argv[1])
+        {
+        case 'g':
+          if (oldmode == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH)
+            {
+              /* TBD */
+            }
+          bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP;
+          if (oldmode != BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP)
+            {
+              /* TBD */
+            }
+          break;
+        case 'n':
+          if (oldmode == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH)
+            {
+              /* TBD */
+            }
+          if (oldmode == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP)
+            {
+              /* TBD */
+            }
+          break;
+        case 'r':
+          if (oldmode == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP)
+            {
+              /* TBD */
+            }
+          bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH;
+          if (oldmode != BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH)
+            {
+              /* TBD */
+            }
+          break;
+        default:
+          vty_out (vty, "Invalid mode%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+static struct rfapi_rfg_name *
+rfgn_new ()
+{
+  return XCALLOC (MTYPE_RFAPI_RFG_NAME, sizeof (struct rfapi_rfg_name));
+}
+
+static void
+rfgn_free (struct rfapi_rfg_name *rfgn)
+{
+  XFREE (MTYPE_RFAPI_RFG_NAME, rfgn);
+}
+
+DEFUN (vnc_export_nvegroup,
+       vnc_export_nvegroup_cmd,
+       "vnc export (bgp|zebra) group-nve group NAME",
+       VNC_CONFIG_STR
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "NVE group, used in 'group-nve' export mode\n"
+       "NVE group\n" "Group name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_nve_group_cfg *rfg_new;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rfg_new = rfapi_group_lookup_byname (bgp, argv[1]);
+
+  if (*argv[0] == 'b')
+    {
+
+      struct listnode *node;
+      struct rfapi_rfg_name *rfgn;
+
+      /*
+       * Set group for export to BGP Direct
+       */
+
+      /* see if group is already included in export list */
+      for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                                 node, rfgn))
+        {
+
+          if (!strcmp (rfgn->name, argv[1]))
+            {
+              /* already in the list: we're done */
+              return CMD_SUCCESS;
+            }
+        }
+
+      rfgn = rfgn_new ();
+      rfgn->name = strdup (argv[1]);
+      rfgn->rfg = rfg_new;      /* OK if not set yet */
+
+      listnode_add (bgp->rfapi_cfg->rfg_export_direct_bgp_l, rfgn);
+
+      zlog_debug ("%s: testing rfg_new", __func__);
+      if (rfg_new)
+        {
+          zlog_debug ("%s: testing bgp grp mode enabled", __func__);
+          if (VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg))
+            zlog_debug ("%s: calling vnc_direct_bgp_add_group", __func__);
+          vnc_direct_bgp_add_group (bgp, rfg_new);
+        }
+
+    }
+  else
+    {
+
+      struct listnode *node;
+      struct rfapi_rfg_name *rfgn;
+
+      /*
+       * Set group for export to Zebra
+       */
+
+      /* see if group is already included in export list */
+      for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l,
+                                 node, rfgn))
+        {
+
+          if (!strcmp (rfgn->name, argv[1]))
+            {
+              /* already in the list: we're done */
+              return CMD_SUCCESS;
+            }
+        }
+
+      rfgn = rfgn_new ();
+      rfgn->name = strdup (argv[1]);
+      rfgn->rfg = rfg_new;      /* OK if not set yet */
+
+      listnode_add (bgp->rfapi_cfg->rfg_export_zebra_l, rfgn);
+
+      if (rfg_new)
+        {
+          if (VNC_EXPORT_ZEBRA_GRP_ENABLED (bgp->rfapi_cfg))
+            vnc_zebra_add_group (bgp, rfg_new);
+        }
+    }
+
+  return CMD_SUCCESS;
+}
+
+/*
+ * This command applies to routes exported from VNC to BGP directly
+ * without going though zebra
+ */
+DEFUN (vnc_no_export_nvegroup,
+       vnc_no_export_nvegroup_cmd,
+       "vnc export (bgp|zebra) group-nve no group NAME",
+       VNC_CONFIG_STR
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "NVE group, used in 'group-nve' export mode\n"
+       "Disable export of VNC routes\n" "NVE group\n" "Group name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct listnode *node, *nnode;
+  struct rfapi_rfg_name *rfgn;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (*argv[0] == 'b')
+    {
+      for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                              node, nnode, rfgn))
+        {
+
+          if (rfgn->name && !strcmp (rfgn->name, argv[1]))
+            {
+              zlog_debug ("%s: matched \"%s\"", __func__, rfgn->name);
+              if (rfgn->rfg)
+                vnc_direct_bgp_del_group (bgp, rfgn->rfg);
+              free (rfgn->name);
+              list_delete_node (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                                node);
+              rfgn_free (rfgn);
+              break;
+            }
+        }
+    }
+  else
+    {
+      for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_zebra_l,
+                              node, nnode, rfgn))
+        {
+
+          zlog_debug ("does rfg \"%s\" match?", rfgn->name);
+          if (rfgn->name && !strcmp (rfgn->name, argv[1]))
+            {
+              if (rfgn->rfg)
+                vnc_zebra_del_group (bgp, rfgn->rfg);
+              free (rfgn->name);
+              list_delete_node (bgp->rfapi_cfg->rfg_export_zebra_l, node);
+              rfgn_free (rfgn);
+              break;
+            }
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_export_no_prefixlist,
+       vnc_nve_group_export_no_prefixlist_cmd,
+       "no export (bgp|zebra) (ipv4|ipv6) prefix-list [NAME]",
+       NO_STR
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "IPv4 routes\n"
+       "IPv6 routes\n"
+       "Prefix-list for filtering exported routes\n" "prefix list name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_nve_group_cfg *rfg;
+  afi_t afi;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[1], "ipv4"))
+    {
+      afi = AFI_IP;
+    }
+  else
+    {
+      afi = AFI_IP6;
+    }
+
+  if (*argv[0] == 'b')
+    {
+      if (((argc >= 3) && !strcmp (argv[2], rfg->plist_export_bgp_name[afi]))
+          || (argc < 3))
+        {
+
+          if (rfg->plist_export_bgp_name[afi])
+            free (rfg->plist_export_bgp_name[afi]);
+          rfg->plist_export_bgp_name[afi] = NULL;
+          rfg->plist_export_bgp[afi] = NULL;
+
+          vnc_direct_bgp_reexport_group_afi (bgp, rfg, afi);
+        }
+    }
+  else
+    {
+      if (((argc >= 3)
+           && !strcmp (argv[2], rfg->plist_export_zebra_name[afi]))
+          || (argc < 3))
+        {
+          if (rfg->plist_export_zebra_name[afi])
+            free (rfg->plist_export_zebra_name[afi]);
+          rfg->plist_export_zebra_name[afi] = NULL;
+          rfg->plist_export_zebra[afi] = NULL;
+
+          vnc_zebra_reexport_group_afi (bgp, rfg, afi);
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_export_prefixlist,
+       vnc_nve_group_export_prefixlist_cmd,
+       "export (bgp|zebra) (ipv4|ipv6) prefix-list NAME",
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "IPv4 routes\n"
+       "IPv6 routes\n"
+       "Prefix-list for filtering exported routes\n" "prefix list name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_nve_group_cfg *rfg;
+  afi_t afi;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[1], "ipv4"))
+    {
+      afi = AFI_IP;
+    }
+  else
+    {
+      afi = AFI_IP6;
+    }
+
+  if (*argv[0] == 'b')
+    {
+      if (rfg->plist_export_bgp_name[afi])
+        free (rfg->plist_export_bgp_name[afi]);
+      rfg->plist_export_bgp_name[afi] = strdup (argv[2]);
+      rfg->plist_export_bgp[afi] = prefix_list_lookup (afi, argv[2]);
+
+      vnc_direct_bgp_reexport_group_afi (bgp, rfg, afi);
+
+    }
+  else
+    {
+      if (rfg->plist_export_zebra_name[afi])
+        free (rfg->plist_export_zebra_name[afi]);
+      rfg->plist_export_zebra_name[afi] = strdup (argv[2]);
+      rfg->plist_export_zebra[afi] = prefix_list_lookup (afi, argv[2]);
+
+      vnc_zebra_reexport_group_afi (bgp, rfg, afi);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_export_no_routemap,
+       vnc_nve_group_export_no_routemap_cmd,
+       "no export (bgp|zebra) route-map [NAME]",
+       NO_STR
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "Route-map for filtering exported routes\n" "route map name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_nve_group_cfg *rfg;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (*argv[0] == 'b')
+    {
+      if (((argc >= 2) && !strcmp (argv[1], rfg->routemap_export_bgp_name)) ||
+          (argc < 2))
+        {
+
+          if (rfg->routemap_export_bgp_name)
+            free (rfg->routemap_export_bgp_name);
+          rfg->routemap_export_bgp_name = NULL;
+          rfg->routemap_export_bgp = NULL;
+
+          vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP);
+          vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP6);
+        }
+    }
+  else
+    {
+      if (((argc >= 2) && !strcmp (argv[1], rfg->routemap_export_zebra_name))
+          || (argc < 2))
+        {
+          if (rfg->routemap_export_zebra_name)
+            free (rfg->routemap_export_zebra_name);
+          rfg->routemap_export_zebra_name = NULL;
+          rfg->routemap_export_zebra = NULL;
+
+          vnc_zebra_reexport_group_afi (bgp, rfg, AFI_IP);
+          vnc_zebra_reexport_group_afi (bgp, rfg, AFI_IP6);
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_export_routemap,
+       vnc_nve_group_export_routemap_cmd,
+       "export (bgp|zebra) route-map NAME",
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "Route-map for filtering exported routes\n" "route map name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_nve_group_cfg *rfg;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (*argv[0] == 'b')
+    {
+      if (rfg->routemap_export_bgp_name)
+        free (rfg->routemap_export_bgp_name);
+      rfg->routemap_export_bgp_name = strdup (argv[1]);
+      rfg->routemap_export_bgp = route_map_lookup_by_name (argv[1]);
+      vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP);
+      vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP6);
+    }
+  else
+    {
+      if (rfg->routemap_export_zebra_name)
+        free (rfg->routemap_export_zebra_name);
+      rfg->routemap_export_zebra_name = strdup (argv[1]);
+      rfg->routemap_export_zebra = route_map_lookup_by_name (argv[1]);
+      vnc_zebra_reexport_group_afi (bgp, rfg, AFI_IP);
+      vnc_zebra_reexport_group_afi (bgp, rfg, AFI_IP6);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_export_no_prefixlist,
+       vnc_nve_export_no_prefixlist_cmd,
+       "no vnc export (bgp|zebra) (ipv4|ipv6) prefix-list [NAME]",
+       NO_STR
+       VNC_CONFIG_STR
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "IPv4 prefixes\n"
+       "IPv6 prefixes\n"
+       "Prefix-list for filtering exported routes\n" "Prefix list name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_cfg *hc;
+  afi_t afi;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[1], "ipv4"))
+    {
+      afi = AFI_IP;
+    }
+  else
+    {
+      afi = AFI_IP6;
+    }
+
+  if (*argv[0] == 'b')
+    {
+      if (((argc >= 3) && !strcmp (argv[2], hc->plist_export_bgp_name[afi]))
+          || (argc < 3))
+        {
+
+          if (hc->plist_export_bgp_name[afi])
+            free (hc->plist_export_bgp_name[afi]);
+          hc->plist_export_bgp_name[afi] = NULL;
+          hc->plist_export_bgp[afi] = NULL;
+          vnc_direct_bgp_reexport (bgp, afi);
+        }
+    }
+  else
+    {
+      if (((argc >= 3) && !strcmp (argv[2], hc->plist_export_zebra_name[afi]))
+          || (argc < 3))
+        {
+
+          if (hc->plist_export_zebra_name[afi])
+            free (hc->plist_export_zebra_name[afi]);
+          hc->plist_export_zebra_name[afi] = NULL;
+          hc->plist_export_zebra[afi] = NULL;
+          /* TBD vnc_zebra_rh_reexport(bgp, afi); */
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_export_prefixlist,
+       vnc_nve_export_prefixlist_cmd,
+       "vnc export (bgp|zebra) (ipv4|ipv6) prefix-list NAME",
+       VNC_CONFIG_STR
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "Filters, used in 'registering-nve' export mode\n"
+       "IPv4 prefixes\n"
+       "IPv6 prefixes\n"
+       "Prefix-list for filtering exported routes\n" "Prefix list name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_cfg *hc;
+  afi_t afi;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[1], "ipv4"))
+    {
+      afi = AFI_IP;
+    }
+  else
+    {
+      afi = AFI_IP6;
+    }
+
+  if (*argv[0] == 'b')
+    {
+      if (hc->plist_export_bgp_name[afi])
+        free (hc->plist_export_bgp_name[afi]);
+      hc->plist_export_bgp_name[afi] = strdup (argv[2]);
+      hc->plist_export_bgp[afi] = prefix_list_lookup (afi, argv[2]);
+      vnc_direct_bgp_reexport (bgp, afi);
+    }
+  else
+    {
+      if (hc->plist_export_zebra_name[afi])
+        free (hc->plist_export_zebra_name[afi]);
+      hc->plist_export_zebra_name[afi] = strdup (argv[2]);
+      hc->plist_export_zebra[afi] = prefix_list_lookup (afi, argv[2]);
+      /* TBD vnc_zebra_rh_reexport(bgp, afi); */
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_export_no_routemap,
+       vnc_nve_export_no_routemap_cmd,
+       "no vnc export (bgp|zebra) route-map [NAME]",
+       NO_STR
+       VNC_CONFIG_STR
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "Route-map for filtering exported routes\n" "Route map name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_cfg *hc;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (*argv[0] == 'b')
+    {
+      if (((argc >= 2) && !strcmp (argv[1], hc->routemap_export_bgp_name)) ||
+          (argc < 2))
+        {
+
+          if (hc->routemap_export_bgp_name)
+            free (hc->routemap_export_bgp_name);
+          hc->routemap_export_bgp_name = NULL;
+          hc->routemap_export_bgp = NULL;
+          vnc_direct_bgp_reexport (bgp, AFI_IP);
+          vnc_direct_bgp_reexport (bgp, AFI_IP6);
+        }
+    }
+  else
+    {
+      if (((argc >= 2) && !strcmp (argv[1], hc->routemap_export_zebra_name))
+          || (argc < 2))
+        {
+
+          if (hc->routemap_export_zebra_name)
+            free (hc->routemap_export_zebra_name);
+          hc->routemap_export_zebra_name = NULL;
+          hc->routemap_export_zebra = NULL;
+          /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */
+          /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */
+        }
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_export_routemap,
+       vnc_nve_export_routemap_cmd,
+       "vnc export (bgp|zebra) route-map NAME",
+       VNC_CONFIG_STR
+       "Export to other protocols\n"
+       "Export to BGP\n"
+       "Export to Zebra (experimental)\n"
+       "Filters, used in 'registering-nve' export mode\n"
+       "Route-map for filtering exported routes\n" "Route map name\n")
+{
+  struct bgp *bgp = vty->index;
+  struct rfapi_cfg *hc;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      vty_out (vty, "rfapi not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (*argv[0] == 'b')
+    {
+      if (hc->routemap_export_bgp_name)
+        free (hc->routemap_export_bgp_name);
+      hc->routemap_export_bgp_name = strdup (argv[1]);
+      hc->routemap_export_bgp = route_map_lookup_by_name (argv[1]);
+      vnc_direct_bgp_reexport (bgp, AFI_IP);
+      vnc_direct_bgp_reexport (bgp, AFI_IP6);
+    }
+  else
+    {
+      if (hc->routemap_export_zebra_name)
+        free (hc->routemap_export_zebra_name);
+      hc->routemap_export_zebra_name = strdup (argv[1]);
+      hc->routemap_export_zebra = route_map_lookup_by_name (argv[1]);
+      /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */
+      /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */
+    }
+  return CMD_SUCCESS;
+}
+
+
+/*
+ * respond to changes in the global prefix list configuration
+ */
+void
+vnc_prefix_list_update (struct bgp *bgp)
+{
+  afi_t afi;
+  struct listnode *n;
+  struct rfapi_nve_group_cfg *rfg;
+  struct rfapi_cfg *hc;
+  int i;
+
+  if (!bgp)
+    {
+      zlog_debug ("%s: No BGP process is configured", __func__);
+      return;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: rfapi not configured", __func__);
+      return;
+    }
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    {
+      /*
+       * Loop over nve groups
+       */
+      for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->nve_groups_sequential,
+                                 n, rfg))
+        {
+
+          if (rfg->plist_export_bgp_name[afi])
+            {
+              rfg->plist_export_bgp[afi] =
+                prefix_list_lookup (afi, rfg->plist_export_bgp_name[afi]);
+            }
+          if (rfg->plist_export_zebra_name[afi])
+            {
+              rfg->plist_export_zebra[afi] =
+                prefix_list_lookup (afi, rfg->plist_export_zebra_name[afi]);
+            }
+          for (i = 0; i < ZEBRA_ROUTE_MAX; ++i)
+            {
+              if (rfg->plist_redist_name[i][afi])
+                {
+                  rfg->plist_redist[i][afi] =
+                    prefix_list_lookup (afi, rfg->plist_redist_name[i][afi]);
+                }
+            }
+
+          vnc_direct_bgp_reexport_group_afi (bgp, rfg, afi);
+          /* TBD vnc_zebra_reexport_group_afi(bgp, rfg, afi); */
+        }
+
+      /*
+       * RH config, too
+       */
+      if (hc->plist_export_bgp_name[afi])
+        {
+          hc->plist_export_bgp[afi] =
+            prefix_list_lookup (afi, hc->plist_export_bgp_name[afi]);
+        }
+      if (hc->plist_export_zebra_name[afi])
+        {
+          hc->plist_export_zebra[afi] =
+            prefix_list_lookup (afi, hc->plist_export_zebra_name[afi]);
+        }
+
+      for (i = 0; i < ZEBRA_ROUTE_MAX; ++i)
+        {
+          if (hc->plist_redist_name[i][afi])
+            {
+              hc->plist_redist[i][afi] =
+                prefix_list_lookup (afi, hc->plist_redist_name[i][afi]);
+            }
+        }
+
+    }
+
+  vnc_direct_bgp_reexport (bgp, AFI_IP);
+  vnc_direct_bgp_reexport (bgp, AFI_IP6);
+
+  /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */
+  /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */
+
+  vnc_redistribute_prechange (bgp);
+  vnc_redistribute_postchange (bgp);
+}
+
+/*
+ * respond to changes in the global route map configuration
+ */
+void
+vnc_routemap_update (struct bgp *bgp, const char *unused)
+{
+  struct listnode *n;
+  struct rfapi_nve_group_cfg *rfg;
+  struct rfapi_cfg *hc;
+  int i;
+
+  zlog_debug ("%s(arg=%s)", __func__, unused);
+
+  if (!bgp)
+    {
+      zlog_debug ("%s: No BGP process is configured", __func__);
+      return;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: rfapi not configured", __func__);
+      return;
+    }
+
+  /*
+   * Loop over nve groups
+   */
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->nve_groups_sequential, n, rfg))
+    {
+
+      if (rfg->routemap_export_bgp_name)
+        {
+          rfg->routemap_export_bgp =
+            route_map_lookup_by_name (rfg->routemap_export_bgp_name);
+        }
+      if (rfg->routemap_export_zebra_name)
+        {
+          rfg->routemap_export_bgp =
+            route_map_lookup_by_name (rfg->routemap_export_zebra_name);
+        }
+      for (i = 0; i < ZEBRA_ROUTE_MAX; ++i)
+        {
+          if (rfg->routemap_redist_name[i])
+            {
+              rfg->routemap_redist[i] =
+                route_map_lookup_by_name (rfg->routemap_redist_name[i]);
+            }
+        }
+
+      vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP);
+      vnc_direct_bgp_reexport_group_afi (bgp, rfg, AFI_IP6);
+      /* TBD vnc_zebra_reexport_group_afi(bgp, rfg, afi); */
+    }
+
+  /*
+   * RH config, too
+   */
+  if (hc->routemap_export_bgp_name)
+    {
+      hc->routemap_export_bgp =
+        route_map_lookup_by_name (hc->routemap_export_bgp_name);
+    }
+  if (hc->routemap_export_zebra_name)
+    {
+      hc->routemap_export_bgp =
+        route_map_lookup_by_name (hc->routemap_export_zebra_name);
+    }
+  for (i = 0; i < ZEBRA_ROUTE_MAX; ++i)
+    {
+      if (hc->routemap_redist_name[i])
+        {
+          hc->routemap_redist[i] =
+            route_map_lookup_by_name (hc->routemap_redist_name[i]);
+        }
+    }
+
+  vnc_direct_bgp_reexport (bgp, AFI_IP);
+  vnc_direct_bgp_reexport (bgp, AFI_IP6);
+
+  /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */
+  /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */
+
+  vnc_redistribute_prechange (bgp);
+  vnc_redistribute_postchange (bgp);
+
+  zlog_debug ("%s done", __func__);
+}
+
+static void
+vnc_routemap_event (route_map_event_t type,     /* ignored */
+                    const char *rmap_name)      /* ignored */
+{
+  struct listnode *mnode, *mnnode;
+  struct bgp *bgp;
+
+  zlog_debug ("%s(event type=%d)", __func__, type);
+  if (bm->bgp == NULL)          /* may be called during cleanup */
+    return;
+
+  for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp))
+    vnc_routemap_update (bgp, rmap_name);
+
+  zlog_debug ("%s: done", __func__);
+}
+
+/*-------------------------------------------------------------------------
+ *                     nve-group
+ *-----------------------------------------------------------------------*/
+
+
+DEFUN (vnc_nve_group,
+       vnc_nve_group_cmd,
+       "vnc nve-group NAME",
+       VNC_CONFIG_STR "Configure a NVE group\n" "Group name\n")
+{
+  struct rfapi_nve_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+  struct listnode *node, *nnode;
+  struct rfapi_rfg_name *rfgn;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Search for name */
+  rfg = rfapi_group_lookup_byname (bgp, argv[0]);
+
+  if (!rfg)
+    {
+      rfg = rfapi_group_new ();
+      if (!rfg)
+        {
+          /* Error out of memory */
+          vty_out (vty, "Can't allocate memory for NVE group%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      rfg->name = strdup (argv[0]);
+      /* add to tail of list */
+      listnode_add (bgp->rfapi_cfg->nve_groups_sequential, rfg);
+
+      /* Copy defaults from struct rfapi_cfg */
+      rfg->rd = bgp->rfapi_cfg->default_rd;
+      if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_L2RD)
+        {
+          rfg->l2rd = bgp->rfapi_cfg->default_l2rd;
+          rfg->flags |= RFAPI_RFG_L2RD;
+        }
+      rfg->rd = bgp->rfapi_cfg->default_rd;
+      rfg->response_lifetime = bgp->rfapi_cfg->default_response_lifetime;
+
+      if (bgp->rfapi_cfg->default_rt_export_list)
+        {
+          rfg->rt_export_list =
+            ecommunity_dup (bgp->rfapi_cfg->default_rt_export_list);
+        }
+
+      if (bgp->rfapi_cfg->default_rt_import_list)
+        {
+          rfg->rt_import_list =
+            ecommunity_dup (bgp->rfapi_cfg->default_rt_import_list);
+          rfg->rfapi_import_table =
+            rfapiImportTableRefAdd (bgp, rfg->rt_import_list);
+        }
+
+      /*
+       * If a redist nve group was named but the group was not defined,
+       * make the linkage now
+       */
+      if (!bgp->rfapi_cfg->rfg_redist)
+        {
+          if (bgp->rfapi_cfg->rfg_redist_name &&
+              !strcmp (bgp->rfapi_cfg->rfg_redist_name, rfg->name))
+            {
+
+              vnc_redistribute_prechange (bgp);
+              bgp->rfapi_cfg->rfg_redist = rfg;
+              vnc_redistribute_postchange (bgp);
+
+            }
+        }
+
+      /*
+       * Same treatment for bgp-direct export group
+       */
+      for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                              node, nnode, rfgn))
+        {
+
+          if (!strcmp (rfgn->name, rfg->name))
+            {
+              rfgn->rfg = rfg;
+              vnc_direct_bgp_add_group (bgp, rfg);
+              break;
+            }
+        }
+
+      /*
+       * Same treatment for zebra export group
+       */
+      for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_zebra_l,
+                              node, nnode, rfgn))
+        {
+
+          zlog_debug ("%s: ezport zebra: checking if \"%s\" == \"%s\"",
+                      __func__, rfgn->name, rfg->name);
+          if (!strcmp (rfgn->name, rfg->name))
+            {
+              rfgn->rfg = rfg;
+              vnc_zebra_add_group (bgp, rfg);
+              break;
+            }
+        }
+    }
+
+  /*
+   * XXX subsequent calls will need to make sure this item is still
+   * in the linked list and has the same name
+   */
+  vty->index_sub = rfg;
+
+  vty->node = BGP_VNC_NVE_GROUP_NODE;
+  return CMD_SUCCESS;
+}
+
+static void
+bgp_rfapi_delete_nve_group (
+  struct vty                   *vty,    /* NULL = no output */
+  struct bgp                   *bgp,
+  struct rfapi_nve_group_cfg   *rfg)
+{
+  struct list *orphaned_nves = NULL;
+  struct listnode *node, *nnode;
+
+  /*
+   * If there are currently-open NVEs that belong to this group,
+   * zero out their references to this group structure.
+   */
+  if (rfg->nves)
+    {
+      struct rfapi_descriptor *rfd;
+      orphaned_nves = list_new ();
+      while ((rfd = listnode_head (rfg->nves)))
+        {
+          rfd->rfg = NULL;
+          listnode_delete (rfg->nves, rfd);
+          listnode_add (orphaned_nves, rfd);
+        }
+      list_delete (rfg->nves);
+      rfg->nves = NULL;
+    }
+
+  /* delete it */
+  free (rfg->name);
+  if (rfg->rfapi_import_table)
+    rfapiImportTableRefDelByIt (bgp, rfg->rfapi_import_table);
+  if (rfg->rt_import_list)
+    ecommunity_free (&rfg->rt_import_list);
+  if (rfg->rt_export_list)
+    ecommunity_free (&rfg->rt_export_list);
+
+  if (rfg->vn_node)
+    {
+      rfg->vn_node->info = NULL;
+      route_unlock_node (rfg->vn_node); /* frees */
+    }
+  if (rfg->un_node)
+    {
+      rfg->un_node->info = NULL;
+      route_unlock_node (rfg->un_node); /* frees */
+    }
+  if (rfg->rfp_cfg)
+    XFREE (MTYPE_RFAPI_RFP_GROUP_CFG, rfg->rfp_cfg);
+  listnode_delete (bgp->rfapi_cfg->nve_groups_sequential, rfg);
+
+  XFREE (MTYPE_RFAPI_GROUP_CFG, rfg);
+
+  /*
+   * Attempt to reassign the orphaned nves to a new group. If
+   * a NVE can not be reassigned, its rfd->rfg will remain NULL
+   * and it will become a zombie until released by rfapi_close().
+   */
+  if (orphaned_nves)
+    {
+      struct rfapi_descriptor *rfd;
+
+      for (ALL_LIST_ELEMENTS (orphaned_nves, node, nnode, rfd))
+        {
+          /*
+           * 1. rfapi_close() equivalent except:
+           *          a. don't free original descriptor
+           *          b. remember query list
+           *          c. remember advertised route list
+           * 2. rfapi_open() equivalent except:
+           *          a. reuse original descriptor
+           * 3. rfapi_register() on remembered advertised route list
+           * 4. rfapi_query on rememebred query list
+           */
+
+          int rc;
+
+          rc = rfapi_reopen (rfd, bgp);
+
+          if (!rc)
+            {
+              list_delete_node (orphaned_nves, node);
+              if (vty)
+                vty_out (vty, "WARNING: reassigned NVE vn=");
+              rfapiPrintRfapiIpAddr (vty, &rfd->vn_addr);
+              if (vty)
+                vty_out (vty, " un=");
+              rfapiPrintRfapiIpAddr (vty, &rfd->un_addr);
+              if (vty)
+                vty_out (vty, " to new group \"%s\"%s", rfd->rfg->name,
+                         VTY_NEWLINE);
+
+            }
+        }
+
+      for (ALL_LIST_ELEMENTS_RO (orphaned_nves, node, rfd))
+        {
+          if (vty)
+            vty_out (vty, "WARNING: orphaned NVE vn=");
+          rfapiPrintRfapiIpAddr (vty, &rfd->vn_addr);
+          if (vty)
+            vty_out (vty, " un=");
+          rfapiPrintRfapiIpAddr (vty, &rfd->un_addr);
+          if (vty)
+            vty_out (vty, "%s", VTY_NEWLINE);
+        }
+      list_delete (orphaned_nves);
+    }
+}
+
+static int
+bgp_rfapi_delete_named_nve_group (
+  struct vty *vty,      /* NULL = no output */
+  struct bgp *bgp,
+  const char *rfg_name)        /* NULL = any */
+{
+  struct rfapi_nve_group_cfg *rfg = NULL;
+  struct listnode *node, *nnode;
+  struct rfapi_rfg_name *rfgn;
+
+  /* Search for name */
+  if (rfg_name)
+    {
+      rfg = rfapi_group_lookup_byname (bgp, rfg_name);
+      if (!rfg)
+        {
+          if (vty)
+            vty_out (vty, "No NVE group named \"%s\"%s", rfg_name,
+                     VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  /*
+   * If this group is the redist nve group, unlink it
+   */
+  if (rfg_name == NULL || bgp->rfapi_cfg->rfg_redist == rfg)
+    {
+      vnc_redistribute_prechange (bgp);
+      bgp->rfapi_cfg->rfg_redist = NULL;
+      vnc_redistribute_postchange (bgp);
+    }
+
+
+  /*
+   * remove reference from bgp direct export list
+   */
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                             node, rfgn))
+    {
+      if (rfg_name == NULL || !strcmp (rfgn->name, rfg_name))
+        {
+          rfgn->rfg = NULL;
+          /* remove exported routes from this group */
+          vnc_direct_bgp_del_group (bgp, rfg);
+          break;
+        }
+    }
+
+  /*
+   * remove reference from zebra export list
+   */
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn))
+    {
+
+      if (rfg_name == NULL || !strcmp (rfgn->name, rfg_name))
+        {
+          rfgn->rfg = NULL;
+          /* remove exported routes from this group */
+          vnc_zebra_del_group (bgp, rfg);
+          break;
+        }
+    }
+  if (rfg)
+    bgp_rfapi_delete_nve_group (vty, bgp, rfg);
+  else                          /* must be delete all */
+    for (ALL_LIST_ELEMENTS
+         (bgp->rfapi_cfg->nve_groups_sequential, node, nnode, rfg))
+      bgp_rfapi_delete_nve_group (vty, bgp, rfg);
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_no_nve_group,
+       vnc_no_nve_group_cmd,
+       "no vnc nve-group NAME",
+       NO_STR
+       VNC_CONFIG_STR
+       "Configure a NVE group\n"
+       "Group name\n")
+{
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_rfapi_delete_named_nve_group (vty, bgp, argv[0]);
+}
+
+DEFUN (vnc_nve_group_prefix,
+       vnc_nve_group_prefix_cmd,
+       "prefix (vn|un) (A.B.C.D/M|X:X::X:X/M)",
+       "Specify prefixes matching NVE VN or UN interfaces\n"
+       "VN prefix\n"
+       "UN prefix\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n")
+{
+  struct rfapi_nve_group_cfg *rfg;
+  struct prefix p;
+  int afi;
+  struct route_table *rt;
+  struct route_node *rn;
+  int is_un_prefix = 0;
+
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!str2prefix (argv[1], &p))
+    {
+      vty_out (vty, "Malformed prefix \"%s\"%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  afi = family2afi (p.family);
+  if (!afi)
+    {
+      vty_out (vty, "Unsupported address family%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (*(argv[0]) == 'u')
+    {
+      rt = &(bgp->rfapi_cfg->nve_groups_un[afi]);
+      is_un_prefix = 1;
+    }
+  else
+    {
+      rt = &(bgp->rfapi_cfg->nve_groups_vn[afi]);
+    }
+
+  rn = route_node_get (rt, &p); /* NB locks node */
+  if (rn->info)
+    {
+      /*
+       * There is already a group with this prefix
+       */
+      route_unlock_node (rn);
+      if (rn->info != rfg)
+        {
+          /*
+           * different group name: fail
+           */
+          vty_out (vty, "nve group \"%s\" already has \"%s\" prefix %s%s",
+                   ((struct rfapi_nve_group_cfg *) (rn->info))->name,
+                   argv[0], argv[1], VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      else
+        {
+          /*
+           * same group name: it's already in the correct place
+           * in the table, so we're done.
+           *
+           * Implies rfg->(vn|un)_prefix is already correct.
+           */
+          return CMD_SUCCESS;
+        }
+    }
+
+  if (bgp->rfapi_cfg->rfg_redist == rfg)
+    {
+      vnc_redistribute_prechange (bgp);
+    }
+
+  /* New prefix, new node */
+
+  if (is_un_prefix)
+    {
+
+      /* detach rfg from previous route table location */
+      if (rfg->un_node)
+        {
+          rfg->un_node->info = NULL;
+          route_unlock_node (rfg->un_node);     /* frees */
+        }
+      rfg->un_node = rn;        /* back ref */
+      rfg->un_prefix = p;
+
+    }
+  else
+    {
+
+      /* detach rfg from previous route table location */
+      if (rfg->vn_node)
+        {
+          rfg->vn_node->info = NULL;
+          route_unlock_node (rfg->vn_node);     /* frees */
+        }
+      rfg->vn_node = rn;        /* back ref */
+      rfg->vn_prefix = p;
+    }
+
+  /* attach */
+  rn->info = rfg;
+
+  if (bgp->rfapi_cfg->rfg_redist == rfg)
+    {
+      vnc_redistribute_postchange (bgp);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_rt_import,
+       vnc_nve_group_rt_import_cmd,
+       "rt import .RTLIST",
+       "Specify route targets\n"
+       "Import filter\n"
+       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+  struct rfapi_nve_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+  int rc;
+  struct listnode *node;
+  struct rfapi_rfg_name *rfgn;
+  int is_export_bgp = 0;
+  int is_export_zebra = 0;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rc = set_ecom_list (vty, argc, argv, &rfg->rt_import_list);
+  if (rc != CMD_SUCCESS)
+    return rc;
+
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                             node, rfgn))
+    {
+
+      if (rfgn->rfg == rfg)
+        {
+          is_export_bgp = 1;
+          break;
+        }
+    }
+
+  if (is_export_bgp)
+    vnc_direct_bgp_del_group (bgp, rfg);
+
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn))
+    {
+
+      if (rfgn->rfg == rfg)
+        {
+          is_export_zebra = 1;
+          break;
+        }
+    }
+
+  if (is_export_zebra)
+    vnc_zebra_del_group (bgp, rfg);
+
+  /*
+   * stop referencing old import table, now reference new one
+   */
+  if (rfg->rfapi_import_table)
+    rfapiImportTableRefDelByIt (bgp, rfg->rfapi_import_table);
+  rfg->rfapi_import_table = rfapiImportTableRefAdd (bgp, rfg->rt_import_list);
+
+  if (is_export_bgp)
+    vnc_direct_bgp_add_group (bgp, rfg);
+
+  if (is_export_zebra)
+    vnc_zebra_add_group (bgp, rfg);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_rt_export,
+       vnc_nve_group_rt_export_cmd,
+       "rt export .RTLIST",
+       "Specify route targets\n"
+       "Export filter\n"
+       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+  struct rfapi_nve_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+  int rc;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (bgp->rfapi_cfg->rfg_redist == rfg)
+    {
+      vnc_redistribute_prechange (bgp);
+    }
+
+  rc = set_ecom_list (vty, argc, argv, &rfg->rt_export_list);
+
+  if (bgp->rfapi_cfg->rfg_redist == rfg)
+    {
+      vnc_redistribute_postchange (bgp);
+    }
+
+  return rc;
+}
+
+DEFUN (vnc_nve_group_rt_both,
+       vnc_nve_group_rt_both_cmd,
+       "rt both .RTLIST",
+       "Specify route targets\n"
+       "Export+import filters\n"
+       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+  struct rfapi_nve_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+  int rc;
+  int is_export_bgp = 0;
+  int is_export_zebra = 0;
+  struct listnode *node;
+  struct rfapi_rfg_name *rfgn;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rc = set_ecom_list (vty, argc, argv, &rfg->rt_import_list);
+  if (rc != CMD_SUCCESS)
+    return rc;
+
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                             node, rfgn))
+    {
+
+      if (rfgn->rfg == rfg)
+        {
+          is_export_bgp = 1;
+          break;
+        }
+    }
+
+  if (is_export_bgp)
+    vnc_direct_bgp_del_group (bgp, rfg);
+
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn))
+    {
+
+      if (rfgn->rfg == rfg)
+        {
+          is_export_zebra = 1;
+          break;
+        }
+    }
+
+  if (is_export_zebra)
+    {
+      zlog_debug ("%s: is_export_zebra", __func__);
+      vnc_zebra_del_group (bgp, rfg);
+    }
+
+  /*
+   * stop referencing old import table, now reference new one
+   */
+  if (rfg->rfapi_import_table)
+    rfapiImportTableRefDelByIt (bgp, rfg->rfapi_import_table);
+  rfg->rfapi_import_table = rfapiImportTableRefAdd (bgp, rfg->rt_import_list);
+
+  if (is_export_bgp)
+    vnc_direct_bgp_add_group (bgp, rfg);
+
+  if (is_export_zebra)
+    vnc_zebra_add_group (bgp, rfg);
+
+  if (bgp->rfapi_cfg->rfg_redist == rfg)
+    {
+      vnc_redistribute_prechange (bgp);
+    }
+
+  rc = set_ecom_list (vty, argc, argv, &rfg->rt_export_list);
+
+  if (bgp->rfapi_cfg->rfg_redist == rfg)
+    {
+      vnc_redistribute_postchange (bgp);
+    }
+
+  return rc;
+
+}
+
+DEFUN (vnc_nve_group_l2rd,
+       vnc_nve_group_l2rd_cmd,
+       "l2rd (ID|auto:vn)",
+       "Specify default Local Nve ID value to use in RD for L2 routes\n"
+       "Fixed value 1-255\n"
+       "use the low-order octet of the NVE's VN address\n")
+{
+  struct rfapi_nve_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[0], "auto:vn"))
+    {
+      rfg->l2rd = 0;
+    }
+  else
+    {
+      char *end = NULL;
+      unsigned long value_l = strtoul (argv[0], &end, 10);
+      uint8_t value = value_l & 0xff;
+
+      if (!*(argv[0]) || *end)
+        {
+          vty_out (vty, "%% Malformed l2 nve ID \"%s\"%s", argv[0],
+                   VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      if ((value_l < 1) || (value_l > 0xff))
+        {
+          vty_out (vty,
+                   "%% Malformed l2 nve id (must be greater than 0 and less than %u%s",
+                   0x100, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+
+      rfg->l2rd = value;
+    }
+  rfg->flags |= RFAPI_RFG_L2RD;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_no_l2rd,
+       vnc_nve_group_no_l2rd_cmd,
+       "no l2rd",
+       NO_STR
+       "Specify default Local Nve ID value to use in RD for L2 routes\n")
+{
+  struct rfapi_nve_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rfg->l2rd = 0;
+  rfg->flags &= ~RFAPI_RFG_L2RD;
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_rd,
+       vnc_nve_group_rd_cmd,
+       "rd ASN:nn_or_IP-address:nn",
+       "Specify route distinguisher\n"
+       "Route Distinguisher (<as-number>:<number> | <ip-address>:<number> | auto:vn:<number> )\n")
+{
+  int ret;
+  struct prefix_rd prd;
+  struct rfapi_nve_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strncmp (argv[0], "auto:vn:", 8))
+    {
+      /*
+       * use AF_UNIX to designate automatically-assigned RD
+       * auto:vn:nn where nn is a 2-octet quantity
+       */
+      char *end = NULL;
+      uint32_t value32 = strtoul (argv[0] + 8, &end, 10);
+      uint16_t value = value32 & 0xffff;
+
+      if (!*(argv[0] + 5) || *end)
+        {
+          vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      if (value32 > 0xffff)
+        {
+          vty_out (vty, "%% Malformed rd (must be less than %u%s",
+                   0x0ffff, VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+
+      memset (&prd, 0, sizeof (prd));
+      prd.family = AF_UNIX;
+      prd.prefixlen = 64;
+      prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff;
+      prd.val[1] = RD_TYPE_IP & 0x0ff;
+      prd.val[6] = (value >> 8) & 0x0ff;
+      prd.val[7] = value & 0x0ff;
+
+    }
+  else
+    {
+
+      ret = str2prefix_rd (argv[0], &prd);
+      if (!ret)
+        {
+          vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  if (bgp->rfapi_cfg->rfg_redist == rfg)
+    {
+      vnc_redistribute_prechange (bgp);
+    }
+
+  rfg->rd = prd;
+
+  if (bgp->rfapi_cfg->rfg_redist == rfg)
+    {
+      vnc_redistribute_postchange (bgp);
+    }
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_responselifetime,
+       vnc_nve_group_responselifetime_cmd,
+       "response-lifetime (LIFETIME|infinite)",
+       "Specify response lifetime\n"
+       "Response lifetime in seconds\n" "Infinite response lifetime\n")
+{
+  unsigned int rspint;
+  struct rfapi_nve_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+  struct rfapi_descriptor *rfd;
+  struct listnode *hdnode;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[0], "infinite"))
+    {
+      rspint = RFAPI_INFINITE_LIFETIME;
+    }
+  else
+    {
+      VTY_GET_INTEGER ("Response Lifetime", rspint, argv[0]);
+    }
+
+  rfg->response_lifetime = rspint;
+  rfg->flags |= RFAPI_RFG_RESPONSE_LIFETIME;
+  if (rfg->nves)
+    for (ALL_LIST_ELEMENTS_RO (rfg->nves, hdnode, rfd))
+      rfd->response_lifetime = rspint;
+  return CMD_SUCCESS;
+}
+
+/*
+ * Sigh. This command, like exit-address-family, is a hack to deal
+ * with the lack of rigorous level control in the command handler. 
+ * TBD fix command handler.
+ */
+DEFUN (exit_vnc,
+       exit_vnc_cmd,
+       "exit-vnc",
+       "Exit VNC configuration mode\n")
+{
+  if (vty->node == BGP_VNC_DEFAULTS_NODE ||
+      vty->node == BGP_VNC_NVE_GROUP_NODE ||
+      vty->node == BGP_VNC_L2_GROUP_NODE)
+    {
+
+      vty->node = BGP_NODE;
+    }
+  return CMD_SUCCESS;
+}
+
+static struct cmd_node bgp_vnc_defaults_node = {
+  BGP_VNC_DEFAULTS_NODE,
+  "%s(config-router-vnc-defaults)# ",
+  1
+};
+
+static struct cmd_node bgp_vnc_nve_group_node = {
+  BGP_VNC_NVE_GROUP_NODE,
+  "%s(config-router-vnc-nve-group)# ",
+  1
+};
+
+/*-------------------------------------------------------------------------
+ *                     vnc-l2-group
+ *-----------------------------------------------------------------------*/
+
+
+DEFUN (vnc_l2_group,
+       vnc_l2_group_cmd,
+       "vnc l2-group NAME",
+       VNC_CONFIG_STR "Configure a L2 group\n" "Group name\n")
+{
+  struct rfapi_l2_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Search for name */
+  rfg = rfapi_l2_group_lookup_byname (bgp, argv[0]);
+
+  if (!rfg)
+    {
+      rfg = rfapi_l2_group_new ();
+      if (!rfg)
+        {
+          /* Error out of memory */
+          vty_out (vty, "Can't allocate memory for L2 group%s", VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      rfg->name = strdup (argv[0]);
+      /* add to tail of list */
+      listnode_add (bgp->rfapi_cfg->l2_groups, rfg);
+    }
+
+  /*
+   * XXX subsequent calls will need to make sure this item is still
+   * in the linked list and has the same name
+   */
+  vty->index_sub = rfg;
+
+  vty->node = BGP_VNC_L2_GROUP_NODE;
+  return CMD_SUCCESS;
+}
+
+static void
+bgp_rfapi_delete_l2_group (
+  struct vty                   *vty,     /* NULL = no output */
+  struct bgp                   *bgp,
+  struct rfapi_l2_group_cfg    *rfg)
+{
+  /* delete it */
+  free (rfg->name);
+  if (rfg->rt_import_list)
+    ecommunity_free (&rfg->rt_import_list);
+  if (rfg->rt_export_list)
+    ecommunity_free (&rfg->rt_export_list);
+  if (rfg->labels)
+    list_delete (rfg->labels);
+  if (rfg->rfp_cfg)
+    XFREE (MTYPE_RFAPI_RFP_GROUP_CFG, rfg->rfp_cfg);
+  listnode_delete (bgp->rfapi_cfg->l2_groups, rfg);
+
+  rfapi_l2_group_del (rfg);
+}
+
+static int
+bgp_rfapi_delete_named_l2_group (
+  struct vty *vty,       /* NULL = no output */
+  struct bgp *bgp,
+  const char *rfg_name) /* NULL = any */
+{
+  struct rfapi_l2_group_cfg *rfg = NULL;
+  struct listnode *node, *nnode;
+
+  /* Search for name */
+  if (rfg_name)
+    {
+      rfg = rfapi_l2_group_lookup_byname (bgp, rfg_name);
+      if (!rfg)
+        {
+          if (vty)
+            vty_out (vty, "No L2 group named \"%s\"%s", rfg_name,
+                     VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+    }
+
+  if (rfg)
+    bgp_rfapi_delete_l2_group (vty, bgp, rfg);
+  else                          /* must be delete all */
+    for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->l2_groups, node, nnode, rfg))
+      bgp_rfapi_delete_l2_group (vty, bgp, rfg);
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_no_l2_group,
+       vnc_no_l2_group_cmd,
+       "no vnc l2-group NAME",
+       NO_STR
+       VNC_CONFIG_STR
+       "Configure a L2 group\n"
+       "Group name\n")
+{
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_rfapi_delete_named_l2_group (vty, bgp, argv[0]);
+}
+
+
+DEFUN (vnc_l2_group_lni,
+       vnc_l2_group_lni_cmd,
+       "logical-network-id <0-4294967295>",
+       "Specify Logical Network ID associated with group\n"
+       "value\n")
+{
+  struct rfapi_l2_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER ("logical-network-id", rfg->logical_net_id, argv[0]);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_l2_group_labels,
+       vnc_l2_group_labels_cmd,
+       "labels .LABELLIST",
+       "Specify label values associated with group\n"
+       "Space separated list of label values <0-1048575>\n")
+{
+  struct rfapi_l2_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+  struct list *ll;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ll = rfg->labels;
+  if (ll == NULL)
+    {
+      ll = list_new ();
+      rfg->labels = ll;
+    }
+  for (; argc; --argc, ++argv)
+    {
+      uint32_t label;
+      VTY_GET_INTEGER_RANGE ("Label value", label, argv[0], 0, 1048575);
+      if (!listnode_lookup (ll, (void *) (uintptr_t) label))
+        listnode_add (ll, (void *) (uintptr_t) label);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_l2_group_no_labels,
+       vnc_l2_group_no_labels_cmd,
+       "no labels .LABELLIST",
+       NO_STR
+       "Remove label values associated with L2 group\n"
+       "Specify label values associated with L2 group\n"
+       "Space separated list of label values <0-1048575>\n")
+{
+  struct rfapi_l2_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+  struct list *ll;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ll = rfg->labels;
+  if (ll == NULL)
+    {
+      vty_out (vty, "Label no longer associated with group%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (; argc; --argc, ++argv)
+    {
+      uint32_t label;
+      VTY_GET_INTEGER_RANGE ("Label value", label, argv[0], 0, 1048575);
+      listnode_delete (ll, (void *) (uintptr_t) label);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_l2_group_rt,
+       vnc_l2_group_rt_cmd,
+       "rt (both|export|import) ASN:nn_or_IP-address:nn",
+       "Specify route targets\n"
+       "Export+import filters\n"
+       "Export filters\n"
+       "Import filters\n"
+       "A route target\n")
+{
+  struct rfapi_l2_group_cfg *rfg;
+  struct bgp *bgp = vty->index;
+  int rc = CMD_SUCCESS;
+  int do_import = 0;
+  int do_export = 0;
+
+  switch (argv[0][0])
+    {
+    case 'b':
+      do_export = 1;            /* fall through */
+    case 'i':
+      do_import = 1;
+      break;
+    case 'e':
+      do_export = 1;
+      break;
+    default:
+      vty_out (vty, "Unknown option, %s%s", argv[0], VTY_NEWLINE);
+      return CMD_ERR_NO_MATCH;
+    }
+  argc--;
+  argv++;
+  if (argc < 1)
+    return CMD_ERR_INCOMPLETE;
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* get saved pointer */
+  rfg = vty->index_sub;
+
+  /* make sure it's still in list */
+  if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (do_import)
+    rc = set_ecom_list (vty, argc, argv, &rfg->rt_import_list);
+  if (rc == CMD_SUCCESS && do_export)
+    rc = set_ecom_list (vty, argc, argv, &rfg->rt_export_list);
+  return rc;
+}
+
+
+static struct cmd_node bgp_vnc_l2_group_node = {
+  BGP_VNC_L2_GROUP_NODE,
+  "%s(config-router-vnc-l2-group)# ",
+  1
+};
+
+static struct rfapi_l2_group_cfg *
+bgp_rfapi_get_group_by_lni_label (
+  struct bgp   *bgp,
+  uint32_t     logical_net_id,
+  uint32_t     label)
+{
+  struct rfapi_l2_group_cfg *rfg;
+  struct listnode *node;
+
+  if (bgp->rfapi_cfg->l2_groups == NULL)        /* not the best place for this */
+    return NULL;
+
+  label = label & 0xfffff;      /* label is 20 bits! */
+
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->l2_groups, node, rfg))
+    {
+      if (rfg->logical_net_id == logical_net_id)
+        {
+          struct listnode *lnode;
+          void *data;
+          for (ALL_LIST_ELEMENTS_RO (rfg->labels, lnode, data))
+            if (((uint32_t) ((uintptr_t) data)) == label)
+              {                 /* match! */
+                return rfg;
+              }
+        }
+    }
+  return NULL;
+}
+
+struct list *
+bgp_rfapi_get_labellist_by_lni_label (
+  struct bgp   *bgp,
+  uint32_t     logical_net_id,
+  uint32_t     label)
+{
+  struct rfapi_l2_group_cfg *rfg;
+  rfg = bgp_rfapi_get_group_by_lni_label (bgp, logical_net_id, label);
+  if (rfg)
+    {
+      return rfg->labels;
+    }
+  return NULL;
+}
+
+struct ecommunity *
+bgp_rfapi_get_ecommunity_by_lni_label (
+  struct bgp   *bgp,
+  uint32_t     is_import,
+  uint32_t     logical_net_id,
+  uint32_t     label)
+{
+  struct rfapi_l2_group_cfg *rfg;
+  rfg = bgp_rfapi_get_group_by_lni_label (bgp, logical_net_id, label);
+  if (rfg)
+    {
+      if (is_import)
+        return rfg->rt_import_list;
+      else
+        return rfg->rt_export_list;
+    }
+  return NULL;
+}
+
+void
+bgp_rfapi_cfg_init (void)
+{
+  /* main bgpd code does not use this hook, but vnc does */
+  route_map_event_hook (vnc_routemap_event);
+
+  install_node (&bgp_vnc_defaults_node, NULL);
+  install_node (&bgp_vnc_nve_group_node, NULL);
+  install_node (&bgp_vnc_l2_group_node, NULL);
+  install_default (BGP_VNC_DEFAULTS_NODE);
+  install_default (BGP_VNC_NVE_GROUP_NODE);
+  install_default (BGP_VNC_L2_GROUP_NODE);
+
+  /*
+   * Add commands
+   */
+  install_element (BGP_NODE, &vnc_defaults_cmd);
+  install_element (BGP_NODE, &vnc_nve_group_cmd);
+  install_element (BGP_NODE, &vnc_no_nve_group_cmd);
+  install_element (BGP_NODE, &vnc_l2_group_cmd);
+  install_element (BGP_NODE, &vnc_no_l2_group_cmd);
+  install_element (BGP_NODE, &vnc_advertise_un_method_cmd);
+  install_element (BGP_NODE, &vnc_export_mode_cmd);
+
+  install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_import_cmd);
+  install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_export_cmd);
+  install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_both_cmd);
+  install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rd_cmd);
+  install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_l2rd_cmd);
+  install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_no_l2rd_cmd);
+  install_element (BGP_VNC_DEFAULTS_NODE, &vnc_defaults_responselifetime_cmd);
+  install_element (BGP_VNC_DEFAULTS_NODE, &exit_vnc_cmd);
+
+  install_element (BGP_NODE, &vnc_redistribute_protocol_cmd);
+  install_element (BGP_NODE, &vnc_no_redistribute_protocol_cmd);
+  install_element (BGP_NODE, &vnc_redistribute_nvegroup_cmd);
+  install_element (BGP_NODE, &vnc_redistribute_no_nvegroup_cmd);
+  install_element (BGP_NODE, &vnc_redistribute_lifetime_cmd);
+  install_element (BGP_NODE, &vnc_redistribute_rh_roo_localadmin_cmd);
+  install_element (BGP_NODE, &vnc_redistribute_mode_cmd);
+  install_element (BGP_NODE, &vnc_redistribute_bgp_exterior_cmd);
+
+  install_element (BGP_NODE, &vnc_redist_bgpdirect_no_prefixlist_cmd);
+  install_element (BGP_NODE, &vnc_redist_bgpdirect_prefixlist_cmd);
+  install_element (BGP_NODE, &vnc_redist_bgpdirect_no_routemap_cmd);
+  install_element (BGP_NODE, &vnc_redist_bgpdirect_routemap_cmd);
+
+  install_element (BGP_VNC_NVE_GROUP_NODE,
+                   &vnc_nve_group_redist_bgpdirect_no_prefixlist_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE,
+                   &vnc_nve_group_redist_bgpdirect_prefixlist_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE,
+                   &vnc_nve_group_redist_bgpdirect_no_routemap_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE,
+                   &vnc_nve_group_redist_bgpdirect_routemap_cmd);
+
+  install_element (BGP_NODE, &vnc_export_nvegroup_cmd);
+  install_element (BGP_NODE, &vnc_no_export_nvegroup_cmd);
+  install_element (BGP_NODE, &vnc_nve_export_prefixlist_cmd);
+  install_element (BGP_NODE, &vnc_nve_export_routemap_cmd);
+  install_element (BGP_NODE, &vnc_nve_export_no_prefixlist_cmd);
+  install_element (BGP_NODE, &vnc_nve_export_no_routemap_cmd);
+
+  install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_l2rd_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_no_l2rd_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_prefix_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_import_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_export_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_both_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rd_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE,
+                   &vnc_nve_group_responselifetime_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE,
+                   &vnc_nve_group_export_prefixlist_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE,
+                   &vnc_nve_group_export_routemap_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE,
+                   &vnc_nve_group_export_no_prefixlist_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE,
+                   &vnc_nve_group_export_no_routemap_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &exit_vnc_cmd);
+
+  install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_lni_cmd);
+  install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_labels_cmd);
+  install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_no_labels_cmd);
+  install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_rt_cmd);
+  install_element (BGP_VNC_L2_GROUP_NODE, &exit_vnc_cmd);
+}
+
+struct rfapi_cfg *
+bgp_rfapi_cfg_new (struct rfapi_rfp_cfg *cfg)
+{
+  struct rfapi_cfg *h;
+  int afi;
+
+  h =
+    (struct rfapi_cfg *) XCALLOC (MTYPE_RFAPI_CFG, sizeof (struct rfapi_cfg));
+  assert (h);
+
+  h->nve_groups_sequential = list_new ();
+  assert (h->nve_groups_sequential);
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    {
+      /* ugly, to deal with addition of delegates, part of 0.99.24.1 merge */
+      h->nve_groups_vn[afi].delegate = route_table_get_default_delegate ();
+      h->nve_groups_un[afi].delegate = route_table_get_default_delegate ();
+    }
+  h->default_response_lifetime = BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT;
+  h->rfg_export_direct_bgp_l = list_new ();
+  h->rfg_export_zebra_l = list_new ();
+  h->resolve_nve_roo_local_admin =
+    BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT;
+
+  SET_FLAG (h->flags, BGP_VNC_CONFIG_FLAGS_DEFAULT);
+
+  if (cfg == NULL)
+    {
+      h->rfp_cfg.download_type = RFAPI_RFP_DOWNLOAD_PARTIAL;
+      h->rfp_cfg.ftd_advertisement_interval =
+        RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL;
+      h->rfp_cfg.holddown_factor = RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR;
+      h->rfp_cfg.use_updated_response = 0;
+      h->rfp_cfg.use_removes = 0;
+    }
+  else
+    {
+      h->rfp_cfg.download_type = cfg->download_type;
+      h->rfp_cfg.ftd_advertisement_interval = cfg->ftd_advertisement_interval;
+      h->rfp_cfg.holddown_factor = cfg->holddown_factor;
+      h->rfp_cfg.use_updated_response = cfg->use_updated_response;
+      h->rfp_cfg.use_removes = cfg->use_removes;
+      if (cfg->use_updated_response)
+        h->flags &= ~BGP_VNC_CONFIG_CALLBACK_DISABLE;
+      else
+        h->flags |= BGP_VNC_CONFIG_CALLBACK_DISABLE;
+      if (cfg->use_removes)
+        h->flags &= ~BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE;
+      else
+        h->flags |= BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE;
+    }
+  return h;
+}
+
+void
+bgp_rfapi_cfg_destroy (struct bgp *bgp, struct rfapi_cfg *h)
+{
+  if (h == NULL)
+    return;
+
+  bgp_rfapi_delete_named_nve_group (NULL, bgp, NULL);
+  bgp_rfapi_delete_named_l2_group (NULL, bgp, NULL);
+  if (h->l2_groups != NULL)
+    list_delete (h->l2_groups);
+  list_delete (h->nve_groups_sequential);
+  list_delete (h->rfg_export_direct_bgp_l);
+  list_delete (h->rfg_export_zebra_l);
+  if (h->default_rt_export_list)
+    ecommunity_free (&h->default_rt_export_list);
+  if (h->default_rt_import_list)
+    ecommunity_free (&h->default_rt_import_list);
+  if (h->default_rfp_cfg)
+    XFREE (MTYPE_RFAPI_RFP_GROUP_CFG, h->default_rfp_cfg);
+  XFREE (MTYPE_RFAPI_CFG, h);
+
+}
+
+int
+bgp_rfapi_cfg_write (struct vty *vty, struct bgp *bgp)
+{
+  struct listnode *node, *nnode;
+  struct rfapi_nve_group_cfg *rfg;
+  struct rfapi_cfg *hc = bgp->rfapi_cfg;
+  struct rfapi_rfg_name *rfgn;
+  int write = 0;
+  afi_t afi;
+  int type;
+
+  if (hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP)
+    {
+      vty_out (vty, " vnc advertise-un-method encap-safi%s", VTY_NEWLINE);
+      write++;
+    }
+
+  {                             /* was based on listen ports */
+    /* for now allow both old and new */
+    if (bgp->rfapi->rfp_methods.cfg_cb)
+      write += (bgp->rfapi->rfp_methods.cfg_cb) (vty, bgp->rfapi->rfp);
+
+    if (write)
+      vty_out (vty, "!%s", VTY_NEWLINE);
+
+    if (hc->l2_groups)
+      {
+        struct rfapi_l2_group_cfg *rfg = NULL;
+        struct listnode *gnode;
+        for (ALL_LIST_ELEMENTS_RO (hc->l2_groups, gnode, rfg))
+          {
+            struct listnode *lnode;
+            void *data;
+            ++write;
+            vty_out (vty, " vnc l2-group %s%s", rfg->name, VTY_NEWLINE);
+            if (rfg->logical_net_id != 0)
+              vty_out (vty, "   logical-network-id %u%s", rfg->logical_net_id,
+                       VTY_NEWLINE);
+            if (rfg->labels != NULL && listhead (rfg->labels) != NULL)
+              {
+                vty_out (vty, "   labels ");
+                for (ALL_LIST_ELEMENTS_RO (rfg->labels, lnode, data))
+                  {
+                    vty_out (vty, "%hu ", (uint16_t) ((uintptr_t) data));
+                  }
+                vty_out (vty, "%s", VTY_NEWLINE);
+              }
+
+            if (rfg->rt_import_list && rfg->rt_export_list &&
+                ecommunity_cmp (rfg->rt_import_list, rfg->rt_export_list))
+              {
+                char *b = ecommunity_ecom2str (rfg->rt_import_list,
+                                               ECOMMUNITY_FORMAT_ROUTE_MAP);
+                vty_out (vty, "   rt both %s%s", b, VTY_NEWLINE);
+                XFREE (MTYPE_ECOMMUNITY_STR, b);
+              }
+            else
+              {
+                if (rfg->rt_import_list)
+                  {
+                    char *b = ecommunity_ecom2str (rfg->rt_import_list,
+                                                   ECOMMUNITY_FORMAT_ROUTE_MAP);
+                    vty_out (vty, "  rt import %s%s", b, VTY_NEWLINE);
+                    XFREE (MTYPE_ECOMMUNITY_STR, b);
+                  }
+                if (rfg->rt_export_list)
+                  {
+                    char *b = ecommunity_ecom2str (rfg->rt_export_list,
+                                                   ECOMMUNITY_FORMAT_ROUTE_MAP);
+                    vty_out (vty, "  rt export %s%s", b, VTY_NEWLINE);
+                    XFREE (MTYPE_ECOMMUNITY_STR, b);
+                  }
+              }
+            if (bgp->rfapi->rfp_methods.cfg_group_cb)
+              write +=
+                (bgp->rfapi->rfp_methods.cfg_group_cb) (vty,
+                                                        bgp->rfapi->rfp,
+                                                        RFAPI_RFP_CFG_GROUP_L2,
+                                                        rfg->name,
+                                                        rfg->rfp_cfg);
+            vty_out (vty, "   exit-vnc%s", VTY_NEWLINE);
+            vty_out (vty, "!%s", VTY_NEWLINE);
+          }
+      }
+
+    if (hc->default_rd.family ||
+        hc->default_response_lifetime ||
+        hc->default_rt_import_list ||
+        hc->default_rt_export_list || hc->nve_groups_sequential->count)
+      {
+
+
+        ++write;
+        vty_out (vty, " vnc defaults%s", VTY_NEWLINE);
+
+        if (hc->default_rd.prefixlen)
+          {
+            char buf[BUFSIZ];
+            buf[0] = buf[BUFSIZ - 1] = 0;
+
+            if (AF_UNIX == hc->default_rd.family)
+              {
+                uint16_t value = 0;
+
+                value = ((hc->default_rd.val[6] << 8) & 0x0ff00) |
+                  (hc->default_rd.val[7] & 0x0ff);
+
+                vty_out (vty, "  rd auto:vn:%d%s", value, VTY_NEWLINE);
+
+              }
+            else
+              {
+
+                if (!prefix_rd2str (&hc->default_rd, buf, BUFSIZ) ||
+                    !buf[0] || buf[BUFSIZ - 1])
+                  {
+
+                    vty_out (vty, "!Error: Can't convert rd%s", VTY_NEWLINE);
+                  }
+                else
+                  {
+                    vty_out (vty, "  rd %s%s", buf, VTY_NEWLINE);
+                  }
+              }
+          }
+        if (hc->default_response_lifetime)
+          {
+            vty_out (vty, "  response-lifetime ");
+            if (hc->default_response_lifetime != UINT32_MAX)
+              vty_out (vty, "%d", hc->default_response_lifetime);
+            else
+              vty_out (vty, "infinite");
+            vty_out (vty, "%s", VTY_NEWLINE);
+          }
+        if (hc->default_rt_import_list && hc->default_rt_export_list &&
+            ecommunity_cmp (hc->default_rt_import_list,
+                            hc->default_rt_export_list))
+          {
+            char *b = ecommunity_ecom2str (hc->default_rt_import_list,
+                                           ECOMMUNITY_FORMAT_ROUTE_MAP);
+            vty_out (vty, "  rt both %s%s", b, VTY_NEWLINE);
+            XFREE (MTYPE_ECOMMUNITY_STR, b);
+          }
+        else
+          {
+            if (hc->default_rt_import_list)
+              {
+                char *b = ecommunity_ecom2str (hc->default_rt_import_list,
+                                               ECOMMUNITY_FORMAT_ROUTE_MAP);
+                vty_out (vty, "  rt import %s%s", b, VTY_NEWLINE);
+                XFREE (MTYPE_ECOMMUNITY_STR, b);
+              }
+            if (hc->default_rt_export_list)
+              {
+                char *b = ecommunity_ecom2str (hc->default_rt_export_list,
+                                               ECOMMUNITY_FORMAT_ROUTE_MAP);
+                vty_out (vty, "  rt export %s%s", b, VTY_NEWLINE);
+                XFREE (MTYPE_ECOMMUNITY_STR, b);
+              }
+          }
+        if (bgp->rfapi->rfp_methods.cfg_group_cb)
+          write +=
+            (bgp->rfapi->rfp_methods.cfg_group_cb) (vty,
+                                                    bgp->rfapi->rfp,
+                                                    RFAPI_RFP_CFG_GROUP_DEFAULT,
+                                                    NULL,
+                                                    bgp->rfapi_cfg->default_rfp_cfg);
+        vty_out (vty, "  exit-vnc%s", VTY_NEWLINE);
+        vty_out (vty, "!%s", VTY_NEWLINE);
+      }
+
+    for (ALL_LIST_ELEMENTS (hc->nve_groups_sequential, node, nnode, rfg))
+      {
+        ++write;
+        vty_out (vty, " vnc nve-group %s%s", rfg->name, VTY_NEWLINE);
+
+        if (rfg->vn_prefix.family && rfg->vn_node)
+          {
+            char buf[BUFSIZ];
+            buf[0] = buf[BUFSIZ - 1] = 0;
+
+            prefix2str (&rfg->vn_prefix, buf, BUFSIZ);
+            if (!buf[0] || buf[BUFSIZ - 1])
+              {
+                vty_out (vty, "!Error: Can't convert prefix%s", VTY_NEWLINE);
+              }
+            else
+              {
+                vty_out (vty, "  prefix %s %s%s", "vn", buf, VTY_NEWLINE);
+              }
+          }
+
+        if (rfg->un_prefix.family && rfg->un_node)
+          {
+            char buf[BUFSIZ];
+            buf[0] = buf[BUFSIZ - 1] = 0;
+            prefix2str (&rfg->un_prefix, buf, BUFSIZ);
+            if (!buf[0] || buf[BUFSIZ - 1])
+              {
+                vty_out (vty, "!Error: Can't convert prefix%s", VTY_NEWLINE);
+              }
+            else
+              {
+                vty_out (vty, "  prefix %s %s%s", "un", buf, VTY_NEWLINE);
+              }
+          }
+
+
+        if (rfg->rd.prefixlen)
+          {
+            char buf[BUFSIZ];
+            buf[0] = buf[BUFSIZ - 1] = 0;
+
+            if (AF_UNIX == rfg->rd.family)
+              {
+
+                uint16_t value = 0;
+
+                value = ((rfg->rd.val[6] << 8) & 0x0ff00) |
+                  (rfg->rd.val[7] & 0x0ff);
+
+                vty_out (vty, "  rd auto:vn:%d%s", value, VTY_NEWLINE);
+
+              }
+            else
+              {
+
+                if (!prefix_rd2str (&rfg->rd, buf, BUFSIZ) ||
+                    !buf[0] || buf[BUFSIZ - 1])
+                  {
+
+                    vty_out (vty, "!Error: Can't convert rd%s", VTY_NEWLINE);
+                  }
+                else
+                  {
+                    vty_out (vty, "  rd %s%s", buf, VTY_NEWLINE);
+                  }
+              }
+          }
+        if (rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME)
+          {
+            vty_out (vty, "  response-lifetime ");
+            if (rfg->response_lifetime != UINT32_MAX)
+              vty_out (vty, "%d", rfg->response_lifetime);
+            else
+              vty_out (vty, "infinite");
+            vty_out (vty, "%s", VTY_NEWLINE);
+          }
+
+        if (rfg->rt_import_list && rfg->rt_export_list &&
+            ecommunity_cmp (rfg->rt_import_list, rfg->rt_export_list))
+          {
+            char *b = ecommunity_ecom2str (rfg->rt_import_list,
+                                           ECOMMUNITY_FORMAT_ROUTE_MAP);
+            vty_out (vty, "  rt both %s%s", b, VTY_NEWLINE);
+            XFREE (MTYPE_ECOMMUNITY_STR, b);
+          }
+        else
+          {
+            if (rfg->rt_import_list)
+              {
+                char *b = ecommunity_ecom2str (rfg->rt_import_list,
+                                               ECOMMUNITY_FORMAT_ROUTE_MAP);
+                vty_out (vty, "  rt import %s%s", b, VTY_NEWLINE);
+                XFREE (MTYPE_ECOMMUNITY_STR, b);
+              }
+            if (rfg->rt_export_list)
+              {
+                char *b = ecommunity_ecom2str (rfg->rt_export_list,
+                                               ECOMMUNITY_FORMAT_ROUTE_MAP);
+                vty_out (vty, "  rt export %s%s", b, VTY_NEWLINE);
+                XFREE (MTYPE_ECOMMUNITY_STR, b);
+              }
+          }
+
+        /*
+         * route filtering: prefix-lists and route-maps
+         */
+        for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+          {
+
+            const char *afistr = (afi == AFI_IP) ? "ipv4" : "ipv6";
+
+            if (rfg->plist_export_bgp_name[afi])
+              {
+                vty_out (vty, "  export bgp %s prefix-list %s%s",
+                         afistr, rfg->plist_export_bgp_name[afi],
+                         VTY_NEWLINE);
+              }
+            if (rfg->plist_export_zebra_name[afi])
+              {
+                vty_out (vty, "  export zebra %s prefix-list %s%s",
+                         afistr, rfg->plist_export_zebra_name[afi],
+                         VTY_NEWLINE);
+              }
+            /*
+             * currently we only support redist plists for bgp-direct.
+             * If we later add plist support for redistributing other
+             * protocols, we'll need to loop over protocols here
+             */
+            if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi])
+              {
+                vty_out (vty, "  redistribute bgp-direct %s prefix-list %s%s",
+                         afistr,
+                         rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi],
+                         VTY_NEWLINE);
+              }
+            if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT][afi])
+              {
+                vty_out (vty,
+                         "  redistribute bgp-direct-to-nve-groups %s prefix-list %s%s",
+                         afistr,
+                         rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT]
+                         [afi], VTY_NEWLINE);
+              }
+          }
+
+        if (rfg->routemap_export_bgp_name)
+          {
+            vty_out (vty, "  export bgp route-map %s%s",
+                     rfg->routemap_export_bgp_name, VTY_NEWLINE);
+          }
+        if (rfg->routemap_export_zebra_name)
+          {
+            vty_out (vty, "  export zebra route-map %s%s",
+                     rfg->routemap_export_zebra_name, VTY_NEWLINE);
+          }
+        if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT])
+          {
+            vty_out (vty, "  redistribute bgp-direct route-map %s%s",
+                     rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT],
+                     VTY_NEWLINE);
+          }
+        if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT])
+          {
+            vty_out (vty,
+                     "  redistribute bgp-direct-to-nve-groups route-map %s%s",
+                     rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT],
+                     VTY_NEWLINE);
+          }
+        if (bgp->rfapi->rfp_methods.cfg_group_cb)
+          write +=
+            (bgp->rfapi->rfp_methods.cfg_group_cb) (vty,
+                                                    bgp->rfapi->rfp,
+                                                    RFAPI_RFP_CFG_GROUP_NVE,
+                                                    rfg->name, rfg->rfp_cfg);
+        vty_out (vty, "  exit-vnc%s", VTY_NEWLINE);
+        vty_out (vty, "!%s", VTY_NEWLINE);
+      }
+  }                             /* have listen ports */
+
+  /*
+   * route export to other protocols
+   */
+  if (VNC_EXPORT_BGP_GRP_ENABLED (hc))
+    {
+      vty_out (vty, " vnc export bgp mode group-nve%s", VTY_NEWLINE);
+    }
+  else if (VNC_EXPORT_BGP_RH_ENABLED (hc))
+    {
+      vty_out (vty, " vnc export bgp mode registering-nve%s", VTY_NEWLINE);
+    }
+  else if (VNC_EXPORT_BGP_CE_ENABLED (hc))
+    {
+      vty_out (vty, " vnc export bgp mode ce%s", VTY_NEWLINE);
+    }
+
+  if (VNC_EXPORT_ZEBRA_GRP_ENABLED (hc))
+    {
+      vty_out (vty, " vnc export zebra mode group-nve%s", VTY_NEWLINE);
+    }
+  else if (VNC_EXPORT_ZEBRA_RH_ENABLED (hc))
+    {
+      vty_out (vty, " vnc export zebra mode registering-nve%s", VTY_NEWLINE);
+    }
+
+  if (hc->rfg_export_direct_bgp_l)
+    {
+      for (ALL_LIST_ELEMENTS (hc->rfg_export_direct_bgp_l, node, nnode, rfgn))
+        {
+
+          vty_out (vty, " vnc export bgp group-nve group %s%s",
+                   rfgn->name, VTY_NEWLINE);
+        }
+    }
+
+  if (hc->rfg_export_zebra_l)
+    {
+      for (ALL_LIST_ELEMENTS (hc->rfg_export_zebra_l, node, nnode, rfgn))
+        {
+
+          vty_out (vty, " vnc export zebra group-nve group %s%s",
+                   rfgn->name, VTY_NEWLINE);
+        }
+    }
+
+
+  if (hc->rfg_redist_name)
+    {
+      vty_out (vty, " vnc redistribute nve-group %s%s",
+               hc->rfg_redist_name, VTY_NEWLINE);
+    }
+  if (hc->redist_lifetime)
+    {
+      vty_out (vty, " vnc redistribute lifetime %d%s",
+               hc->redist_lifetime, VTY_NEWLINE);
+    }
+  if (hc->resolve_nve_roo_local_admin !=
+      BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT)
+    {
+
+      vty_out (vty, " vnc redistribute resolve-nve roo-ec-local-admin %d%s",
+               hc->resolve_nve_roo_local_admin, VTY_NEWLINE);
+    }
+
+  if (hc->redist_mode)          /* ! default */
+    {
+      const char *s = "";
+
+      switch (hc->redist_mode)
+        {
+        case VNC_REDIST_MODE_PLAIN:
+          s = "plain";
+          break;
+        case VNC_REDIST_MODE_RFG:
+          s = "nve-group";
+          break;
+        case VNC_REDIST_MODE_RESOLVE_NVE:
+          s = "resolve-nve";
+          break;
+        }
+      if (s)
+        {
+          vty_out (vty, " vnc redistribute mode %s%s", s, VTY_NEWLINE);
+        }
+    }
+
+  /*
+   * route filtering: prefix-lists and route-maps
+   */
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+
+      const char *afistr = (afi == AFI_IP) ? "ipv4" : "ipv6";
+
+      if (hc->plist_export_bgp_name[afi])
+        {
+          vty_out (vty, " vnc export bgp %s prefix-list %s%s",
+                   afistr, hc->plist_export_bgp_name[afi], VTY_NEWLINE);
+        }
+      if (hc->plist_export_zebra_name[afi])
+        {
+          vty_out (vty, " vnc export zebra %s prefix-list %s%s",
+                   afistr, hc->plist_export_zebra_name[afi], VTY_NEWLINE);
+        }
+      if (hc->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi])
+        {
+          vty_out (vty, " vnc redistribute bgp-direct %s prefix-list %s%s",
+                   afistr, hc->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi],
+                   VTY_NEWLINE);
+        }
+    }
+
+  if (hc->routemap_export_bgp_name)
+    {
+      vty_out (vty, " vnc export bgp route-map %s%s",
+               hc->routemap_export_bgp_name, VTY_NEWLINE);
+    }
+  if (hc->routemap_export_zebra_name)
+    {
+      vty_out (vty, " vnc export zebra route-map %s%s",
+               hc->routemap_export_zebra_name, VTY_NEWLINE);
+    }
+  if (hc->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT])
+    {
+      vty_out (vty, " vnc redistribute bgp-direct route-map %s%s",
+               hc->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT], VTY_NEWLINE);
+    }
+
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+      for (type = 0; type < ZEBRA_ROUTE_MAX; ++type)
+        {
+          if (hc->redist[afi][type])
+            {
+              if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT &&
+                  hc->redist_bgp_exterior_view_name)
+                {
+                  vty_out (vty, " vnc redistribute %s %s view %s%s",
+                           ((afi == AFI_IP) ? "ipv4" : "ipv6"),
+                           zebra_route_string (type),
+                           hc->redist_bgp_exterior_view_name, VTY_NEWLINE);
+                }
+              else
+                {
+                  vty_out (vty, " vnc redistribute %s %s%s",
+                           ((afi == AFI_IP) ? "ipv4" : "ipv6"),
+                           zebra_route_string (type), VTY_NEWLINE);
+                }
+            }
+        }
+    }
+  return write;
+}
+
+void
+bgp_rfapi_show_summary (struct bgp *bgp, struct vty *vty)
+{
+  struct rfapi_cfg *hc = bgp->rfapi_cfg;
+  int afi, type, redist = 0;
+  char tmp[40];
+  if (hc == NULL)
+    return;
+
+  vty_out (vty, "%-39s %-19s %s%s", "VNC Advertise method:",
+           (hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP
+            ? "Encapsulation SAFI" : "Tunnel Encap attribute"),
+           ((hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP) ==
+            (BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP &
+             BGP_VNC_CONFIG_FLAGS_DEFAULT) ? "(default)" : ""), VTY_NEWLINE);
+  /* export */
+  vty_out (vty, "%-39s ", "Export from VNC:");
+  /*
+   * route export to other protocols
+   */
+  if (VNC_EXPORT_BGP_GRP_ENABLED (hc))
+    {
+      redist++;
+      vty_out (vty, "ToBGP Groups={");
+      if (hc->rfg_export_direct_bgp_l)
+        {
+          int cnt = 0;
+          struct listnode *node, *nnode;
+          struct rfapi_rfg_name *rfgn;
+          for (ALL_LIST_ELEMENTS (hc->rfg_export_direct_bgp_l,
+                                  node, nnode, rfgn))
+            {
+              if (cnt++ != 0)
+                vty_out (vty, ",");
+
+              vty_out (vty, "%s", rfgn->name);
+            }
+        }
+      vty_out (vty, "}");
+    }
+  else if (VNC_EXPORT_BGP_RH_ENABLED (hc))
+    {
+      redist++;
+      vty_out (vty, "ToBGP {Registering NVE}");
+      /* note filters, route-maps not shown */
+    }
+  else if (VNC_EXPORT_BGP_CE_ENABLED (hc))
+    {
+      redist++;
+      vty_out (vty, "ToBGP {NVE connected router:%d}",
+               hc->resolve_nve_roo_local_admin);
+      /* note filters, route-maps not shown */
+    }
+
+  if (VNC_EXPORT_ZEBRA_GRP_ENABLED (hc))
+    {
+      redist++;
+      vty_out (vty, "%sToZebra Groups={", (redist == 1 ? "" : " "));
+      if (hc->rfg_export_direct_bgp_l)
+        {
+          int cnt = 0;
+          struct listnode *node, *nnode;
+          struct rfapi_rfg_name *rfgn;
+          for (ALL_LIST_ELEMENTS (hc->rfg_export_zebra_l, node, nnode, rfgn))
+            {
+              if (cnt++ != 0)
+                vty_out (vty, ",");
+              vty_out (vty, "%s", rfgn->name);
+            }
+        }
+      vty_out (vty, "}");
+    }
+  else if (VNC_EXPORT_ZEBRA_RH_ENABLED (hc))
+    {
+      redist++;
+      vty_out (vty, "%sToZebra {Registering NVE}", (redist == 1 ? "" : " "));
+      /* note filters, route-maps not shown */
+    }
+  vty_out (vty, "%-19s %s%s", (redist ? "" : "Off"),
+           (redist ? "" : "(default)"), VTY_NEWLINE);
+
+  /* Redistribution */
+  redist = 0;
+  vty_out (vty, "%-39s ", "Redistribution into VNC:");
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+      for (type = 0; type < ZEBRA_ROUTE_MAX; ++type)
+        {
+          if (hc->redist[afi][type])
+            {
+              vty_out (vty, "{%s,%s} ",
+                       ((afi == AFI_IP) ? "ipv4" : "ipv6"),
+                       zebra_route_string (type));
+              redist++;
+            }
+        }
+    }
+  vty_out (vty, "%-19s %s%s", (redist ? "" : "Off"),
+           (redist ? "" : "(default)"), VTY_NEWLINE);
+
+  vty_out (vty, "%-39s %3u%-16s %s%s", "RFP Registration Hold-Down Factor:",
+           hc->rfp_cfg.holddown_factor, "%",
+           (hc->rfp_cfg.holddown_factor ==
+            RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR ? "(default)" : ""),
+           VTY_NEWLINE);
+  vty_out (vty, "%-39s %-19s %s%s", "RFP Updated responses:",
+           (hc->rfp_cfg.use_updated_response == 0 ? "Off" : "On"),
+           (hc->rfp_cfg.use_updated_response == 0 ? "(default)" : ""),
+           VTY_NEWLINE);
+  vty_out (vty, "%-39s %-19s %s%s", "RFP Removal responses:",
+           (hc->rfp_cfg.use_removes == 0 ? "Off" : "On"),
+           (hc->rfp_cfg.use_removes == 0 ? "(default)" : ""), VTY_NEWLINE);
+  vty_out (vty, "%-39s %-19s %s%s", "RFP Full table download:",
+           (hc->rfp_cfg.download_type ==
+            RFAPI_RFP_DOWNLOAD_FULL ? "On" : "Off"),
+           (hc->rfp_cfg.download_type ==
+            RFAPI_RFP_DOWNLOAD_PARTIAL ? "(default)" : ""), VTY_NEWLINE);
+  sprintf (tmp, "%u seconds", hc->rfp_cfg.ftd_advertisement_interval);
+  vty_out (vty, "%-39s %-19s %s%s", "    Advertisement Interval:", tmp,
+           (hc->rfp_cfg.ftd_advertisement_interval ==
+            RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL
+            ? "(default)" : ""), VTY_NEWLINE);
+  vty_out (vty, "%-39s %d seconds%s", "Default RFP response lifetime:",
+           hc->default_response_lifetime, VTY_NEWLINE);
+  vty_out (vty, "%s", VTY_NEWLINE);
+  return;
+}
+
+struct rfapi_cfg *
+bgp_rfapi_get_config (struct bgp *bgp)
+{
+  struct rfapi_cfg *hc = NULL;
+  if (bgp == NULL)
+    bgp = bgp_get_default ();
+  if (bgp != NULL)
+    hc = bgp->rfapi_cfg;
+  return hc;
+}
+
+#endif /* ENABLE_BGP_VNC */
diff --git a/bgpd/rfapi/bgp_rfapi_cfg.h b/bgpd/rfapi/bgp_rfapi_cfg.h
new file mode 100644 (file)
index 0000000..cd3a28f
--- /dev/null
@@ -0,0 +1,312 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_CFG_H
+#define _QUAGGA_BGP_RFAPI_CFG_H
+
+#include "table.h"
+#include "routemap.h"
+
+#if ENABLE_BGP_VNC
+#include "rfapi.h"
+
+struct rfapi_l2_group_cfg
+{
+  char *name;
+  uint32_t logical_net_id;
+  struct list *labels;          /* list of uint32_t */
+  struct ecommunity *rt_import_list;
+  struct ecommunity *rt_export_list;
+  void *rfp_cfg;                /* rfp owned group config */
+};
+
+struct rfapi_nve_group_cfg
+{
+  struct route_node *vn_node;   /* backref */
+  struct route_node *un_node;   /* backref */
+
+  char *name;
+  struct prefix vn_prefix;
+  struct prefix un_prefix;
+
+  struct prefix_rd rd;
+  uint8_t l2rd;                 /* 0 = VN addr LSB */
+  uint32_t response_lifetime;
+  uint32_t flags;
+#define RFAPI_RFG_RESPONSE_LIFETIME    0x1
+#define RFAPI_RFG_L2RD                 0x02
+  struct ecommunity *rt_import_list;
+  struct ecommunity *rt_export_list;
+  struct rfapi_import_table *rfapi_import_table;
+
+  void *rfp_cfg;                /* rfp owned group config */
+  /*
+   * List of NVE descriptors that are assigned to this NVE group
+   *
+   * Currently (Mar 2010) this list is used only by the route
+   * export code to generate per-NVE nexthops for each route.
+   *
+   * The nve descriptors listed here have pointers back to
+   * this nve group config structure to enable them to delete
+   * their own list entries when they are closed. Consequently,
+   * if an instance of this nve group config structure is deleted,
+   * we must first set the nve descriptor references to it to NULL.
+   */
+  struct list *nves;
+
+  /*
+   * Route filtering
+   *
+   * Prefix lists are segregated by afi (part of the base plist code)
+   * Route-maps are not segregated
+   */
+  char *plist_export_bgp_name[AFI_MAX];
+  struct prefix_list *plist_export_bgp[AFI_MAX];
+
+  char *plist_export_zebra_name[AFI_MAX];
+  struct prefix_list *plist_export_zebra[AFI_MAX];
+
+  char *plist_redist_name[ZEBRA_ROUTE_MAX][AFI_MAX];
+  struct prefix_list *plist_redist[ZEBRA_ROUTE_MAX][AFI_MAX];
+
+  char *routemap_export_bgp_name;
+  struct route_map *routemap_export_bgp;
+
+  char *routemap_export_zebra_name;
+  struct route_map *routemap_export_zebra;
+
+  char *routemap_redist_name[ZEBRA_ROUTE_MAX];
+  struct route_map *routemap_redist[ZEBRA_ROUTE_MAX];
+};
+
+struct rfapi_rfg_name
+{
+  struct rfapi_nve_group_cfg *rfg;
+  char *name;
+};
+
+typedef enum
+{
+  VNC_REDIST_MODE_PLAIN = 0,    /* 0 = default */
+  VNC_REDIST_MODE_RFG,
+  VNC_REDIST_MODE_RESOLVE_NVE
+} vnc_redist_mode_t;
+
+struct rfapi_cfg
+{
+  struct prefix_rd default_rd;
+  uint8_t default_l2rd;
+  struct ecommunity *default_rt_import_list;
+  struct ecommunity *default_rt_export_list;
+  uint32_t default_response_lifetime;
+#define BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT 3600
+  void *default_rfp_cfg;        /* rfp owned group config */
+
+  struct list *l2_groups;       /* rfapi_l2_group_cfg list */
+  /* three views into the same collection of rfapi_nve_group_cfg */
+  struct list *nve_groups_sequential;
+  struct route_table nve_groups_vn[AFI_MAX];
+  struct route_table nve_groups_un[AFI_MAX];
+
+  /*
+   * For Single VRF export to ordinary routing protocols. This is
+   * the nve-group that the ordinary protocols belong to. We use it
+   * to set the RD when sending unicast Zebra routes to VNC
+   */
+  uint8_t redist[AFI_MAX][ZEBRA_ROUTE_MAX];
+  uint32_t redist_lifetime;
+  vnc_redist_mode_t redist_mode;
+
+  /*
+   * view name of BGP unicast instance that holds
+   * exterior routes 
+   */
+  char *redist_bgp_exterior_view_name;
+  struct bgp *redist_bgp_exterior_view;
+
+  /* 
+   * nve group for redistribution of routes from zebra to VNC
+   * (which is probably not useful for production networks)
+   */
+  char *rfg_redist_name;
+  struct rfapi_nve_group_cfg *rfg_redist;
+
+  /*
+   * List of NVE groups on whose behalf we will export VNC
+   * routes to zebra. ((NB: it's actually a list of <struct rfapi_rfg_name>)
+   * This list is used when BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS is
+   * BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP
+   */
+  struct list *rfg_export_zebra_l;
+
+  /*
+   * List of NVE groups on whose behalf we will export VNC
+   * routes directly to the bgp unicast RIB. (NB: it's actually
+   * a list of <struct rfapi_rfg_name>)
+   * This list is used when BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS is
+   * BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP
+   */
+  struct list *rfg_export_direct_bgp_l;
+
+  /*
+   * Exported Route filtering
+   *
+   * Prefix lists are segregated by afi (part of the base plist code)
+   * Route-maps are not segregated
+   */
+  char *plist_export_bgp_name[AFI_MAX];
+  struct prefix_list *plist_export_bgp[AFI_MAX];
+
+  char *plist_export_zebra_name[AFI_MAX];
+  struct prefix_list *plist_export_zebra[AFI_MAX];
+
+  char *routemap_export_bgp_name;
+  struct route_map *routemap_export_bgp;
+
+  char *routemap_export_zebra_name;
+  struct route_map *routemap_export_zebra;
+
+  /*
+   * Redistributed route filtering (routes from other
+   * protocols into VNC)
+   */
+  char *plist_redist_name[ZEBRA_ROUTE_MAX][AFI_MAX];
+  struct prefix_list *plist_redist[ZEBRA_ROUTE_MAX][AFI_MAX];
+
+  char *routemap_redist_name[ZEBRA_ROUTE_MAX];
+  struct route_map *routemap_redist[ZEBRA_ROUTE_MAX];
+
+  /* 
+   * For importing bgp unicast routes to VNC, we encode the CE
+   * (route nexthop) in a Route Origin extended community. The
+   * local part (16-bit) is user-configurable.
+   */
+  uint16_t resolve_nve_roo_local_admin;
+#define BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT 5226
+
+  uint32_t flags;
+#define BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP     0x00000001
+#define BGP_VNC_CONFIG_CALLBACK_DISABLE                0x00000002
+#define BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE        0x00000004
+
+#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS    0x000000f0
+#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS  0x00000f00
+
+#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE    0x00000000
+#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP     0x00000010
+#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH      0x00000020      /* registerd nve */
+#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE      0x00000040
+
+#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_NONE  0x00000000
+#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP   0x00000100
+#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH    0x00000200
+
+#define BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP    0x00001000
+#define BGP_VNC_CONFIG_L2RD                    0x00002000
+
+/* Use new NVE RIB to filter callback routes */
+/* Filter querying NVE's registrations from responses */
+/* Default to updated-responses off */
+/* Default to removal-responses off */
+#define BGP_VNC_CONFIG_FLAGS_DEFAULT            \
+    (BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP       |\
+     BGP_VNC_CONFIG_CALLBACK_DISABLE           |\
+     BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)
+
+  struct rfapi_rfp_cfg rfp_cfg; /* rfp related configuration  */
+};
+
+#define VNC_EXPORT_ZEBRA_GRP_ENABLED(hc)                       \
+    (((hc)->flags & BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS) ==  \
+    BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP)
+
+#define VNC_EXPORT_ZEBRA_RH_ENABLED(hc)                                \
+    (((hc)->flags & BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS) ==  \
+    BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH)
+
+#define VNC_EXPORT_BGP_GRP_ENABLED(hc)                         \
+    (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) ==    \
+    BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP)
+
+#define VNC_EXPORT_BGP_RH_ENABLED(hc)                          \
+    (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) ==    \
+    BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH)
+
+#define VNC_EXPORT_BGP_CE_ENABLED(hc)                          \
+    (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) ==    \
+    BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE)
+
+
+void
+bgp_rfapi_cfg_init (void);
+
+struct rfapi_cfg *
+bgp_rfapi_cfg_new (struct rfapi_rfp_cfg *cfg);
+
+void
+bgp_rfapi_cfg_destroy (struct bgp *bgp, struct rfapi_cfg *h);
+
+int
+bgp_rfapi_cfg_write (struct vty *vty, struct bgp *bgp);
+
+extern int
+bgp_rfapi_is_vnc_configured (struct bgp *bgp);
+
+extern void
+nve_group_to_nve_list (
+  struct rfapi_nve_group_cfg   *rfg,
+  struct list                  **nves,
+  uint8_t                      family);        /* AF_INET, AF_INET6 */
+
+struct rfapi_nve_group_cfg *
+bgp_rfapi_cfg_match_group (
+  struct rfapi_cfg     *hc,
+  struct prefix                *vn,
+  struct prefix                *un);
+
+extern void
+vnc_prefix_list_update (struct bgp *bgp);
+
+extern void
+vnc_routemap_update (struct bgp *bgp, const char *unused);
+
+extern void
+bgp_rfapi_show_summary (struct bgp *bgp, struct vty *vty);
+
+extern struct rfapi_cfg *
+bgp_rfapi_get_config (struct bgp *bgp);
+
+extern struct ecommunity *
+bgp_rfapi_get_ecommunity_by_lni_label (
+  struct bgp   *bgp,
+  uint32_t     is_import,
+  uint32_t     logical_net_id,
+  uint32_t     label); /* note, 20bit label! */
+
+extern struct list *
+bgp_rfapi_get_labellist_by_lni_label (
+  struct bgp   *bgp,
+  uint32_t     logical_net_id,
+  uint32_t     label); /* note, 20bit label! */
+
+#endif /* ENABLE_BGP_VNC */
+
+#endif /* _QUAGGA_BGP_RFAPI_CFG_H */
diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c
new file mode 100644 (file)
index 0000000..3360ad8
--- /dev/null
@@ -0,0 +1,4412 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+
+#include <errno.h>
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "memory.h"
+#include "routemap.h"
+#include "log.h"
+#include "linklist.h"
+#include "command.h"
+#include "stream.h"
+
+#include "bgpd.h"
+#include "bgp_ecommunity.h"
+#include "bgp_attr.h"
+#include "bgp_mplsvpn.h"
+
+#include "bgp_rfapi_cfg.h"
+#include "rfapi.h"
+#include "rfapi_backend.h"
+
+#include "bgp_route.h"
+#include "bgp_aspath.h"
+#include "bgp_advertise.h"
+#include "bgp_vnc_types.h"
+#include "bgp_zebra.h"
+
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_monitor.h"
+#include "rfapi_vty.h"
+#include "vnc_export_bgp.h"
+#include "vnc_export_bgp_p.h"
+#include "vnc_zebra.h"
+#include "vnc_import_bgp.h"
+#include "rfapi_rib.h"
+#include "rfapi_ap.h"
+#include "rfapi_encap_tlv.h"
+#include "vnc_debug.h"
+
+#ifdef HAVE_GLIBC_BACKTRACE
+/* for backtrace and friends */
+#include <execinfo.h>
+#endif /* HAVE_GLIBC_BACKTRACE */
+
+struct ethaddr rfapi_ethaddr0 = { {0} };
+
+#define DEBUG_RFAPI_STR "RF API debugging/testing command\n"
+
+const char *
+rfapi_error_str (int code)
+{
+  switch (code)
+    {
+    case 0:
+      return "Success";
+    case ENXIO:
+      return "BGP or VNC not configured";
+    case ENOENT:
+      return "No match";
+    case EEXIST:
+      return "Handle already open";
+    case ENOMSG:
+      return "Incomplete configuration";
+    case EAFNOSUPPORT:
+      return "Invalid address family";
+    case EDEADLK:
+      return "Called from within a callback procedure";
+    case EBADF:
+      return "Invalid handle";
+    case EINVAL:
+      return "Invalid argument";
+    case ESTALE:
+      return "Stale descriptor";
+    default:
+      return "Unknown error";
+    }
+}
+
+/*------------------------------------------
+ * rfapi_get_response_lifetime_default
+ *
+ * Returns the default lifetime for a response.
+ *    rfp_start_val     value returned by rfp_start or
+ *                      NULL (=use default instance)
+ *
+ * input: 
+ *    None
+ *
+ * output:
+ *
+ * return value: The bgp instance default lifetime for a response.
+ --------------------------------------------*/
+int
+rfapi_get_response_lifetime_default (void *rfp_start_val)
+{
+  struct bgp *bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val);
+  if (bgp)
+    return bgp->rfapi_cfg->default_response_lifetime;
+  return BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT;
+}
+
+/*------------------------------------------
+ * rfapi_is_vnc_configured
+ *
+ * Returns if VNC (BGP VPN messaging /VPN & encap SAFIs) are configured
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start or
+ *                      NULL (=use default instance)
+ *
+ * output:
+ *
+ * return value: If VNC is configured for the bgpd instance
+ *     0               Success
+ *     ENXIO           VNC not configured
+ --------------------------------------------*/
+int
+rfapi_is_vnc_configured (void *rfp_start_val)
+{
+  struct bgp *bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val);
+  return bgp_rfapi_is_vnc_configured (bgp);
+}
+
+
+/*------------------------------------------
+ * rfapi_get_vn_addr
+ *
+ * Get the virtual network address used by an NVE based on it's RFD
+ *
+ * input: 
+ *    rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value: 
+ *     vn              NVE virtual network address
+ *------------------------------------------*/
+struct rfapi_ip_addr *
+rfapi_get_vn_addr (void *rfd)
+{
+  struct rfapi_descriptor *rrfd = (struct rfapi_descriptor *) rfd;
+  return &rrfd->vn_addr;
+}
+
+/*------------------------------------------
+ * rfapi_get_un_addr
+ *
+ * Get the underlay network address used by an NVE based on it's RFD
+ *
+ * input: 
+ *    rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value: 
+ *     un              NVE underlay network address
+ *------------------------------------------*/
+struct rfapi_ip_addr *
+rfapi_get_un_addr (void *rfd)
+{
+  struct rfapi_descriptor *rrfd = (struct rfapi_descriptor *) rfd;
+  return &rrfd->un_addr;
+}
+
+int
+rfapi_ip_addr_cmp (struct rfapi_ip_addr *a1, struct rfapi_ip_addr *a2)
+{
+  if (a1->addr_family != a2->addr_family)
+    return a1->addr_family - a2->addr_family;
+
+  if (a1->addr_family == AF_INET)
+    {
+      return IPV4_ADDR_CMP (&a1->addr.v4, &a2->addr.v4);
+    }
+
+  if (a1->addr_family == AF_INET6)
+    {
+      return IPV6_ADDR_CMP (&a1->addr.v6, &a2->addr.v6);
+    }
+
+  assert (1);
+  /* NOTREACHED */
+  return 1;
+}
+
+static int
+rfapi_find_node (
+  struct bgp           *bgp,
+  struct rfapi_ip_addr *vn_addr,
+  struct rfapi_ip_addr *un_addr,
+  struct route_node    **node)
+{
+  struct rfapi *h;
+  struct prefix p;
+  struct route_node *rn;
+  int rc;
+  int afi;
+
+  if (!bgp)
+    {
+      return ENXIO;
+    }
+
+  h = bgp->rfapi;
+  if (!h)
+    {
+      return ENXIO;
+    }
+
+  afi = family2afi (un_addr->addr_family);
+  if (!afi)
+    {
+      return EAFNOSUPPORT;
+    }
+
+  if ((rc = rfapiRaddr2Qprefix (un_addr, &p)))
+    return rc;
+
+  rn = route_node_lookup (&h->un[afi], &p);
+
+  if (!rn)
+    return ENOENT;
+
+  route_unlock_node (rn);
+
+  *node = rn;
+
+  return 0;
+}
+
+
+int
+rfapi_find_rfd (
+  struct bgp                   *bgp,
+  struct rfapi_ip_addr         *vn_addr,
+  struct rfapi_ip_addr         *un_addr,
+  struct rfapi_descriptor      **rfd)
+{
+  struct route_node *rn;
+  int rc;
+
+  rc = rfapi_find_node (bgp, vn_addr, un_addr, &rn);
+
+  if (rc)
+    return rc;
+
+  for (*rfd = (struct rfapi_descriptor *) (rn->info); *rfd;
+       *rfd = (*rfd)->next)
+    {
+      if (!rfapi_ip_addr_cmp (&(*rfd)->vn_addr, vn_addr))
+        break;
+    }
+
+  if (!*rfd)
+    return ENOENT;
+
+  return 0;
+}
+
+/*------------------------------------------
+ * rfapi_find_handle
+ *
+ * input: 
+ *     un              underlay network address
+ *     vn              virtual network address
+ *
+ * output:
+ *     pHandle         pointer to location to store handle
+ *
+ * return value: 
+ *     0               Success
+ *     ENOENT          no matching handle
+ *     ENXIO           BGP or VNC not configured
+ *------------------------------------------*/
+static int
+rfapi_find_handle (
+  struct bgp           *bgp,
+  struct rfapi_ip_addr *vn_addr,
+  struct rfapi_ip_addr *un_addr,
+  rfapi_handle         *handle)
+{
+  struct rfapi_descriptor **rfd;
+
+  rfd = (struct rfapi_descriptor **) handle;
+
+  return rfapi_find_rfd (bgp, vn_addr, un_addr, rfd);
+}
+
+static int
+rfapi_find_handle_vty (
+  struct vty           *vty,
+  struct rfapi_ip_addr *vn_addr,
+  struct rfapi_ip_addr *un_addr,
+  rfapi_handle         *handle)
+{
+  struct bgp *bgp;
+  struct rfapi_descriptor **rfd;
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+
+  rfd = (struct rfapi_descriptor **) handle;
+
+  return rfapi_find_rfd (bgp, vn_addr, un_addr, rfd);
+}
+
+static int
+is_valid_rfd (struct rfapi_descriptor *rfd)
+{
+  rfapi_handle hh;
+
+  if (!rfd || rfd->bgp == NULL)
+    return 0;
+
+  if (rfapi_find_handle (rfd->bgp, &rfd->vn_addr, &rfd->un_addr, &hh))
+    return 0;
+
+  if (rfd != hh)
+    return 0;
+
+  return 1;
+}
+
+/*
+ * check status of descriptor
+ */
+int
+rfapi_check (void *handle)
+{
+  struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle;
+  rfapi_handle hh;
+  int rc;
+
+  if (!rfd || rfd->bgp == NULL)
+    return EINVAL;
+
+  if ((rc = rfapi_find_handle (rfd->bgp, &rfd->vn_addr, &rfd->un_addr, &hh)))
+    return rc;
+
+  if (rfd != hh)
+    return ENOENT;
+
+  if (!rfd->rfg)
+    return ESTALE;
+
+  return 0;
+}
+
+
+
+void
+del_vnc_route (
+  struct rfapi_descriptor      *rfd,
+  struct peer                  *peer, /* rfd->peer for RFP regs */
+  struct bgp                   *bgp,
+  safi_t                       safi,
+  struct prefix                        *p,
+  struct prefix_rd             *prd,
+  uint8_t                      type,
+  uint8_t                      sub_type,
+  struct rfapi_nexthop         *lnh,
+  int                          kill)
+{
+  afi_t afi;                    /* of the VN address */
+  struct bgp_node *bn;
+  struct bgp_info *bi;
+  char buf[BUFSIZ];
+  char buf2[BUFSIZ];
+  struct prefix_rd prd0;
+
+  prefix2str (p, buf, BUFSIZ);
+  buf[BUFSIZ - 1] = 0;          /* guarantee NUL-terminated */
+
+  prefix_rd2str (prd, buf2, BUFSIZ);
+  buf2[BUFSIZ - 1] = 0;
+
+  afi = family2afi (p->family);
+  assert (afi == AFI_IP || afi == AFI_IP6);
+
+  if (safi == SAFI_ENCAP)
+    {
+      memset (&prd0, 0, sizeof (prd0));
+      prd0.family = AF_UNSPEC;
+      prd0.prefixlen = 64;
+      prd = &prd0;
+    }
+  bn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
+
+  zlog_debug
+    ("%s: peer=%p, prefix=%s, prd=%s afi=%d, safi=%d bn=%p, bn->info=%p",
+     __func__, peer, buf, buf2, afi, safi, bn, (bn ? bn->info : NULL));
+
+  for (bi = (bn ? bn->info : NULL); bi; bi = bi->next)
+    {
+
+      zlog_debug
+        ("%s: trying bi=%p, bi->peer=%p, bi->type=%d, bi->sub_type=%d, bi->extra->vnc.export.rfapi_handle=%p",
+         __func__, bi, bi->peer, bi->type, bi->sub_type,
+         (bi->extra ? bi->extra->vnc.export.rfapi_handle : NULL));
+
+      if (bi->peer == peer &&
+          bi->type == type &&
+          bi->sub_type == sub_type &&
+          bi->extra && bi->extra->vnc.export.rfapi_handle == (void *) rfd)
+        {
+
+          zlog_debug ("%s: matched it", __func__);
+
+          break;
+        }
+    }
+
+  if (lnh)
+    {
+      /*
+       * lnh set means to JUST delete the local nexthop from this
+       * route. Leave the route itself in place.
+       * TBD add return code reporting of success/failure
+       */
+      if (!bi || !bi->extra || !bi->extra->vnc.export.local_nexthops)
+        {
+          /*
+           * no local nexthops
+           */
+          zlog_debug ("%s: lnh list already empty at prefix %s",
+                      __func__, buf);
+          goto done;
+        }
+
+      /*
+       * look for it
+       */
+      struct listnode *node;
+      struct rfapi_nexthop *pLnh = NULL;
+
+      for (ALL_LIST_ELEMENTS_RO (bi->extra->vnc.export.local_nexthops,
+                                 node, pLnh))
+        {
+
+          if (prefix_same (&pLnh->addr, &lnh->addr))
+            {
+              break;
+            }
+        }
+
+      if (pLnh)
+        {
+          listnode_delete (bi->extra->vnc.export.local_nexthops, pLnh);
+
+          /* silly rabbit, listnode_delete doesn't invoke list->del on data */
+          rfapi_nexthop_free (pLnh);
+        }
+      else
+        {
+          zlog_debug ("%s: desired lnh not found %s", __func__, buf);
+        }
+      goto done;
+    }
+
+  /*
+   * loop back to import tables
+   * Do this before removing from BGP RIB because rfapiProcessWithdraw
+   * might refer to it
+   */
+  rfapiProcessWithdraw (peer, rfd, p, prd, NULL, afi, safi, type, kill);
+
+  if (bi)
+    {
+      char buf[BUFSIZ];
+
+      prefix2str (p, buf, BUFSIZ);
+      buf[BUFSIZ - 1] = 0;      /* guarantee NUL-terminated */
+
+      zlog_debug ("%s: Found route (safi=%d) to delete at prefix %s",
+                  __func__, safi, buf);
+
+      if (safi == SAFI_MPLS_VPN)
+        {
+          struct bgp_node *prn = NULL;
+          struct bgp_table *table = NULL;
+
+          prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd);
+          if (prn->info)
+            {
+              table = (struct bgp_table *) (prn->info);
+
+              vnc_import_bgp_del_vnc_host_route_mode_resolve_nve (bgp,
+                                                                  prd,
+                                                                  table,
+                                                                  p, bi);
+            }
+          bgp_unlock_node (prn);
+        }
+
+      /*
+       * Delete local_nexthops list
+       */
+      if (bi->extra && bi->extra->vnc.export.local_nexthops)
+        {
+          list_delete (bi->extra->vnc.export.local_nexthops);
+        }
+
+      bgp_aggregate_decrement (bgp, p, bi, afi, safi);
+      bgp_info_delete (bn, bi);
+      bgp_process (bgp, bn, afi, safi);
+    }
+  else
+    {
+      zlog_debug ("%s: Couldn't find route (safi=%d) at prefix %s",
+                  __func__, safi, buf);
+    }
+done:
+  bgp_unlock_node (bn);
+}
+
+struct rfapi_nexthop *
+rfapi_nexthop_new (struct rfapi_nexthop *copyme)
+{
+  struct rfapi_nexthop *new =
+    XCALLOC (MTYPE_RFAPI_NEXTHOP, sizeof (struct rfapi_nexthop));
+  if (copyme)
+    *new = *copyme;
+  return new;
+}
+
+void
+rfapi_nexthop_free (void *p)
+{
+  struct rfapi_nexthop *goner = p;
+  XFREE (MTYPE_RFAPI_NEXTHOP, goner);
+}
+
+struct rfapi_vn_option *
+rfapi_vn_options_dup (struct rfapi_vn_option *existing)
+{
+  struct rfapi_vn_option *p;
+  struct rfapi_vn_option *head = NULL;
+  struct rfapi_vn_option *tail = NULL;
+
+  for (p = existing; p; p = p->next)
+    {
+      struct rfapi_vn_option *new;
+
+      new = XCALLOC (MTYPE_RFAPI_VN_OPTION, sizeof (struct rfapi_vn_option));
+      *new = *p;
+      new->next = NULL;
+      if (tail)
+        (tail)->next = new;
+      tail = new;
+      if (!head)
+        {
+          head = new;
+        }
+    }
+  return head;
+}
+
+void
+rfapi_un_options_free (struct rfapi_un_option *p)
+{
+  struct rfapi_un_option *next;
+
+  while (p)
+    {
+      next = p->next;
+      XFREE (MTYPE_RFAPI_UN_OPTION, p);
+      p = next;
+    }
+}
+
+void
+rfapi_vn_options_free (struct rfapi_vn_option *p)
+{
+  struct rfapi_vn_option *next;
+
+  while (p)
+    {
+      next = p->next;
+      XFREE (MTYPE_RFAPI_VN_OPTION, p);
+      p = next;
+    }
+}
+
+/* Based on bgp_redistribute_add() */
+void
+add_vnc_route (
+  struct rfapi_descriptor      *rfd,           /* cookie, VPN UN addr, peer */
+  struct bgp                   *bgp,
+  int                          safi,
+  struct prefix                        *p,
+  struct prefix_rd             *prd,
+  struct rfapi_ip_addr         *nexthop,
+  uint32_t                     *local_pref,
+  uint32_t                     *lifetime,      /* NULL => dont send lifetime */
+  struct bgp_tea_options       *rfp_options,
+  struct rfapi_un_option       *options_un,
+  struct rfapi_vn_option       *options_vn,
+  struct ecommunity            *rt_export_list,/* Copied, not consumed */
+  uint32_t                     *med,           /* NULL => don't set med */
+  uint32_t                     *label,         /* low order 3 bytes */
+  uint8_t                      type,
+  uint8_t                      sub_type,       /* RFP, NORMAL or REDIST */
+  int                          flags)
+{
+  afi_t afi;                    /* of the VN address */
+  struct bgp_info *new;
+  struct bgp_info *bi;
+  struct bgp_node *bn;
+
+  struct attr attr = { 0 };
+  struct attr *new_attr;
+  uint32_t    label_val;
+
+  struct bgp_attr_encap_subtlv *encaptlv;
+  char buf[BUFSIZ];
+  char buf2[BUFSIZ];
+#if 0   /* unused? */
+  struct prefix pfx_buf;
+#endif
+
+  struct rfapi_nexthop *lnh = NULL;     /* local nexthop */
+  struct rfapi_vn_option *vo;
+  struct rfapi_l2address_option *l2o = NULL;
+  struct rfapi_ip_addr         *un_addr = &rfd->un_addr;
+
+  bgp_encap_types TunnelType = BGP_ENCAP_TYPE_RESERVED;
+  struct bgp_redist *red;
+
+  if (safi == SAFI_ENCAP &&
+      !(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP))
+    {
+
+      /*
+       * Encap mode not enabled. UN addresses will be communicated
+       * via VNC Tunnel subtlv instead.
+       */
+      zlog_debug ("%s: encap mode not enabled, not adding SAFI_ENCAP route",
+                  __func__);
+      return;
+    }
+
+#if 0   /* unused? */
+  if ((safi == SAFI_MPLS_VPN) && (flags & RFAPI_AHR_SET_PFX_TO_NEXTHOP))
+    {
+
+      if (rfapiRaddr2Qprefix (nexthop, &pfx_buf))
+        {
+          zlog_debug
+            ("%s: can't set pfx to vn addr, not adding SAFI_MPLS_VPN route",
+             __func__);
+          return;
+        }
+      p = &pfx_buf;
+    }
+#endif
+  for (vo = options_vn; vo; vo = vo->next)
+    {
+      if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type)
+        {
+          l2o = &vo->v.l2addr;
+          if (RFAPI_0_ETHERADDR (&l2o->macaddr))
+            l2o = NULL;         /* not MAC resolution */
+        }
+      if (RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP == vo->type)
+        {
+          lnh = &vo->v.local_nexthop;
+        }
+    }
+
+  if (label)
+    label_val = *label;
+  else 
+    label_val = MPLS_LABEL_IMPLICIT_NULL;
+
+  prefix_rd2str (prd, buf2, BUFSIZ);
+  buf2[BUFSIZ - 1] = 0;
+
+
+  afi = family2afi (p->family);
+  assert (afi == AFI_IP || afi == AFI_IP6);
+
+  zlog_debug ("%s: afi=%s, safi=%s", __func__, afi2str (afi),
+              safi2str (safi));
+
+  /* Make default attribute. Produces already-interned attr.aspath */
+  /* Cripes, the memory management of attributes is byzantine */
+
+  bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE);
+  assert (attr.extra);
+
+  /* 
+   * At this point:
+   * attr: static
+   *  extra: dynamically allocated, owned by attr
+   *  aspath: points to interned hash from aspath hash table
+   */
+
+
+  /*
+   * Route-specific un_options get added to the VPN SAFI
+   * advertisement tunnel encap attribute.  (the per-NVE
+   * "default" un_options are put into the 1-per-NVE ENCAP
+   * SAFI advertisement). The VPN SAFI also gets the
+   * default un_options if there are no route-specific options.
+   */
+  if (options_un)
+    {
+      struct rfapi_un_option *uo;
+
+      for (uo = options_un; uo; uo = uo->next)
+        {
+          if (RFAPI_UN_OPTION_TYPE_TUNNELTYPE == uo->type)
+            {
+              TunnelType = rfapi_tunneltype_option_to_tlv (
+               bgp, un_addr, &uo->v.tunnel, &attr, l2o != NULL);
+            }
+        }
+    }
+  else
+    {
+      /*
+       * Add encap attr
+       * These are the NVE-specific "default" un_options which are
+       * put into the 1-per-NVE ENCAP advertisement.
+       */
+      if (rfd->default_tunneltype_option.type)
+        {
+          TunnelType = rfapi_tunneltype_option_to_tlv (
+           bgp, un_addr, &rfd->default_tunneltype_option, &attr,
+           l2o != NULL);
+        }
+      else
+        TunnelType = rfapi_tunneltype_option_to_tlv (
+         bgp, un_addr, NULL,
+         /* create one to carry un_addr */ &attr, l2o != NULL);
+    }
+
+  if (TunnelType == BGP_ENCAP_TYPE_MPLS)
+    {
+      if (safi == SAFI_ENCAP)
+        {
+          /* Encap SAFI not used with MPLS  */
+          zlog_debug ("%s: mpls tunnel type, encap safi omitted", __func__);
+          aspath_unintern (&attr.aspath);       /* Unintern original. */
+          bgp_attr_extra_free (&attr);
+          return;
+        }
+      nexthop = un_addr;    /* UN used as MPLS NLRI nexthop */
+    }
+
+  if (local_pref)
+    {
+      attr.local_pref = *local_pref;
+      attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
+    }
+
+  if (med)
+    {
+      attr.med = *med;
+      attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+    }
+
+  /* override default weight assigned by bgp_attr_default_set() */
+  attr.extra->weight = (rfd->peer ? rfd->peer->weight : 0);
+
+  /*
+   * NB: ticket 81: do not reset attr.aspath here because it would
+   * cause iBGP peers to drop route
+   */
+
+  /*
+   * Set originator ID for routes imported from BGP directly.
+   * These routes could be synthetic, and therefore could
+   * reuse the peer pointers of the routes they are derived
+   * from. Setting the originator ID to "us" prevents the
+   * wrong originator ID from being sent when this route is
+   * sent from a route reflector.
+   */
+  if (type == ZEBRA_ROUTE_BGP_DIRECT || type == ZEBRA_ROUTE_BGP_DIRECT_EXT)
+    {
+      attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID);
+      attr.extra->originator_id = bgp->router_id;
+    }
+
+
+  /* Set up vnc attribute (sub-tlv for Prefix Lifetime) */
+  if (lifetime && *lifetime != RFAPI_INFINITE_LIFETIME)
+    {
+      uint32_t lt;
+
+      encaptlv =
+        XCALLOC (MTYPE_ENCAP_TLV,
+                 sizeof (struct bgp_attr_encap_subtlv) - 1 + 4);
+      assert (encaptlv);
+      encaptlv->type = BGP_VNC_SUBTLV_TYPE_LIFETIME;    /* prefix lifetime */
+      encaptlv->length = 4;
+      lt = htonl (*lifetime);
+      memcpy (encaptlv->value, &lt, 4);
+      attr.extra->vnc_subtlvs = encaptlv;
+      zlog_debug ("%s: set Encap Attr Prefix Lifetime to %d",
+                  __func__, *lifetime);
+    }
+
+  /* add rfp options to vnc attr */
+  if (rfp_options)
+    {
+
+      if (flags & RFAPI_AHR_RFPOPT_IS_VNCTLV)
+        {
+
+          /*
+           * this flag means we're passing a pointer to an
+           * existing encap tlv chain which we should copy.
+           * It's a hack to avoid adding yet another argument
+           * to add_vnc_route()
+           */
+          encaptlv =
+            encap_tlv_dup ((struct bgp_attr_encap_subtlv *) rfp_options);
+          if (attr.extra->vnc_subtlvs)
+            {
+              attr.extra->vnc_subtlvs->next = encaptlv;
+            }
+          else
+            {
+              attr.extra->vnc_subtlvs = encaptlv;
+            }
+
+        }
+      else
+        {
+          struct bgp_tea_options *hop;
+          /* XXX max of one tlv present so far from above code */
+          struct bgp_attr_encap_subtlv *tail = attr.extra->vnc_subtlvs;
+
+          for (hop = rfp_options; hop; hop = hop->next)
+            {
+
+              /*
+               * Construct subtlv
+               */
+              encaptlv = XCALLOC (MTYPE_ENCAP_TLV,
+                                  sizeof (struct bgp_attr_encap_subtlv) - 1 +
+                                  2 + hop->length);
+              assert (encaptlv);
+              encaptlv->type = BGP_VNC_SUBTLV_TYPE_RFPOPTION;   /* RFP option */
+              encaptlv->length = 2 + hop->length;
+              *((uint8_t *) (encaptlv->value) + 0) = hop->type;
+              *((uint8_t *) (encaptlv->value) + 1) = hop->length;
+              memcpy (((uint8_t *) encaptlv->value) + 2, hop->value,
+                      hop->length);
+
+              /*
+               * add to end of subtlv chain
+               */
+              if (tail)
+                {
+                  tail->next = encaptlv;
+                }
+              else
+                {
+                  attr.extra->vnc_subtlvs = encaptlv;
+                }
+              tail = encaptlv;
+            }
+        }
+    }
+
+  /* 
+   * At this point:
+   * attr: static
+   *  extra: dynamically allocated, owned by attr
+   *      vnc_subtlvs: dynamic chain, length 1
+   *  aspath: points to interned hash from aspath hash table
+   */
+
+
+  attr.extra->ecommunity = ecommunity_new ();
+  assert (attr.extra->ecommunity);
+
+  if (TunnelType != BGP_ENCAP_TYPE_MPLS && 
+      TunnelType != BGP_ENCAP_TYPE_RESERVED)
+    {
+      /*
+       * Add BGP Encapsulation Extended Community. Format described in
+       * section 4.5 of RFC 5512.
+       * Always include when not MPLS type, to disambiguate this case.
+       */
+      struct ecommunity_val beec;
+
+      memset (&beec, 0, sizeof (beec));
+      beec.val[0] = ECOMMUNITY_ENCODE_OPAQUE;
+      beec.val[1] = ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP;
+      beec.val[6] = ((TunnelType) >> 8) & 0xff;
+      beec.val[7] = (TunnelType) & 0xff;
+      ecommunity_add_val (attr.extra->ecommunity, &beec);
+    }
+
+  /*
+   * Add extended community attributes to match rt export list
+   */
+  if (rt_export_list)
+    {
+      attr.extra->ecommunity =
+        ecommunity_merge (attr.extra->ecommunity, rt_export_list);
+    }
+
+  if (attr.extra->ecommunity->size)
+    {
+      attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
+    }
+  else
+    {
+      ecommunity_free (&attr.extra->ecommunity);
+      attr.extra->ecommunity = NULL;
+    }
+  zlog_debug ("%s: attr.extra->ecommunity=%p", __func__,
+              attr.extra->ecommunity);
+
+
+  /* 
+   * At this point:
+   * attr: static
+   *  extra: dynamically allocated, owned by attr
+   *      vnc_subtlvs: dynamic chain, length 1
+   *      ecommunity: dynamic 2-part
+   *  aspath: points to interned hash from aspath hash table
+   */
+
+  /* stuff nexthop in attr_extra; which field depends on IPv4 or IPv6 */
+  switch (nexthop->addr_family)
+    {
+    case AF_INET:
+      /*
+       * set this field to prevent bgp_route.c code from setting
+       * mp_nexthop_global_in to self
+       */
+      attr.nexthop.s_addr = nexthop->addr.v4.s_addr;
+
+      attr.extra->mp_nexthop_global_in = nexthop->addr.v4;
+      attr.extra->mp_nexthop_len = 4;
+      break;
+
+    case AF_INET6:
+      attr.extra->mp_nexthop_global = nexthop->addr.v6;
+      attr.extra->mp_nexthop_len = 16;
+      break;
+
+    default:
+      assert (0);
+    }
+
+
+  prefix2str (p, buf, BUFSIZ);
+  buf[BUFSIZ - 1] = 0;          /* guarantee NUL-terminated */
+
+  /* 
+   * At this point:
+   *
+   * attr: static
+   *  extra: dynamically allocated, owned by attr
+   *      vnc_subtlvs: dynamic chain, length 1
+   *      ecommunity: dynamic 2-part
+   *  aspath: points to interned hash from aspath hash table
+   */
+
+  red = bgp_redist_lookup(bgp, afi, type, VRF_DEFAULT);
+
+  if (red && red->redist_metric_flag)
+    {
+      attr.med = red->redist_metric;
+      attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+    }
+
+  bn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
+
+  /*
+   * bgp_attr_intern creates a new reference to a cached
+   * attribute, but leaves the following bits of trash:
+   * - old attr
+   * - old attr->extra (free via bgp_attr_extra_free(attr))
+   *
+   * Note that it frees the original attr->extra->ecommunity
+   * but leaves the new attribute pointing to the ORIGINAL
+   * vnc options (which therefore we needn't free from the
+   * static attr)
+   */
+  new_attr = bgp_attr_intern (&attr);
+
+  aspath_unintern (&attr.aspath);       /* Unintern original. */
+  bgp_attr_extra_free (&attr);
+
+  /*
+   * At this point:
+   *
+   * attr: static
+   *  extra: dynamically allocated, owned by attr
+   *      vnc_subtlvs: dynamic chain, length 1
+   *      ecommunity: POINTS TO INTERNED ecom, THIS REF NOT COUNTED
+   *
+   * new_attr: an attr that is part of the hash table, distinct
+   *           from attr which is static.
+   *  extra: dynamically allocated, owned by new_attr (in hash table)
+   *      vnc_subtlvs: POINTS TO SAME dynamic chain AS attr
+   *      ecommunity: POINTS TO interned/refcounted dynamic 2-part AS attr
+   *  aspath: POINTS TO interned/refcounted hashed block
+   */
+  for (bi = bn->info; bi; bi = bi->next)
+    {
+      /* probably only need to check bi->extra->vnc.export.rfapi_handle */
+      if (bi->peer == rfd->peer &&
+          bi->type == type &&
+          bi->sub_type == sub_type &&
+          bi->extra && bi->extra->vnc.export.rfapi_handle == (void *) rfd)
+        {
+
+          break;
+        }
+    }
+
+  if (bi)
+    {
+
+      /*
+       * Adding new local_nexthop, which does not by itself change
+       * what is advertised via BGP
+       */
+      if (lnh)
+        {
+          if (!bi->extra->vnc.export.local_nexthops)
+            {
+              /* TBD make arrangements to free when needed */
+              bi->extra->vnc.export.local_nexthops = list_new ();
+              bi->extra->vnc.export.local_nexthops->del = rfapi_nexthop_free;
+            }
+
+          /*
+           * already present?
+           */
+          struct listnode *node;
+          struct rfapi_nexthop *pLnh = NULL;
+
+          for (ALL_LIST_ELEMENTS_RO (bi->extra->vnc.export.local_nexthops,
+                                     node, pLnh))
+            {
+
+              if (prefix_same (&pLnh->addr, &lnh->addr))
+                {
+                  break;
+                }
+            }
+
+          /*
+           * Not present, add new one
+           */
+          if (!pLnh)
+            {
+              pLnh = rfapi_nexthop_new (lnh);
+              listnode_add (bi->extra->vnc.export.local_nexthops, pLnh);
+            }
+        }
+
+      if (attrhash_cmp (bi->attr, new_attr) &&
+          !CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+        {
+          bgp_attr_unintern (&new_attr);
+          bgp_unlock_node (bn);
+
+          zlog_info ("%s: Found route (safi=%d) at prefix %s, no change",
+                     __func__, safi, buf);
+
+          goto done;
+        }
+      else
+        {
+          /* The attribute is changed. */
+          bgp_info_set_flag (bn, bi, BGP_INFO_ATTR_CHANGED);
+
+          if (safi == SAFI_MPLS_VPN)
+            {
+              struct bgp_node *prn = NULL;
+              struct bgp_table *table = NULL;
+
+              prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd);
+              if (prn->info)
+                {
+                  table = (struct bgp_table *) (prn->info);
+
+                  vnc_import_bgp_del_vnc_host_route_mode_resolve_nve (
+                   bgp, prd, table, p, bi);
+                }
+              bgp_unlock_node (prn);
+            }
+
+          /* 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 ();
+
+
+          if (safi == SAFI_MPLS_VPN)
+            {
+              struct bgp_node *prn = NULL;
+              struct bgp_table *table = NULL;
+
+              prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd);
+              if (prn->info)
+                {
+                  table = (struct bgp_table *) (prn->info);
+
+                  vnc_import_bgp_add_vnc_host_route_mode_resolve_nve (
+                   bgp, prd, table, p, bi);
+                }
+              bgp_unlock_node (prn);
+            }
+
+          /* Process change. */
+          bgp_aggregate_increment (bgp, p, bi, afi, safi);
+          bgp_process (bgp, bn, afi, safi);
+          bgp_unlock_node (bn);
+
+          zlog_info ("%s: Found route (safi=%d) at prefix %s, changed attr",
+                     __func__, safi, buf);
+
+          goto done;
+        }
+    }
+
+
+  new = bgp_info_new ();
+  new->type = type;
+  new->sub_type = sub_type;
+  new->peer = rfd->peer;
+  SET_FLAG (new->flags, BGP_INFO_VALID);
+  new->attr = new_attr;
+  new->uptime = bgp_clock ();
+
+  /* save backref to rfapi handle */
+  assert (bgp_info_extra_get (new));
+  new->extra->vnc.export.rfapi_handle = (void *) rfd;
+  encode_label (label_val, new->extra->tag);
+
+  /* debug */
+  zlog_debug ("%s: printing BI", __func__);
+  rfapiPrintBi (NULL, new);
+
+  bgp_aggregate_increment (bgp, p, new, afi, safi);
+  bgp_info_add (bn, new);
+
+  if (safi == SAFI_MPLS_VPN)
+    {
+      struct bgp_node *prn = NULL;
+      struct bgp_table *table = NULL;
+
+      prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd);
+      if (prn->info)
+        {
+          table = (struct bgp_table *) (prn->info);
+
+          vnc_import_bgp_add_vnc_host_route_mode_resolve_nve (
+           bgp, prd, table, p, new);
+        }
+      bgp_unlock_node (prn);
+    }
+
+  bgp_unlock_node (bn);
+  bgp_process (bgp, bn, afi, safi);
+
+  zlog_info ("%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%s)",
+             __func__, safi2str (safi), buf, bn, buf2);
+
+done:
+  /* Loop back to import tables */
+  rfapiProcessUpdate (rfd->peer,
+                      rfd,
+                      p, prd, new_attr, afi, safi, type, sub_type, &label_val);
+  zlog_debug ("%s: looped back import route (safi=%d)", __func__, safi);
+}
+
+uint32_t
+rfp_cost_to_localpref (uint8_t cost)
+{
+  return 255 - cost;
+}
+
+static void
+rfapiTunnelRouteAnnounce (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  uint32_t                     *pLifetime)
+{
+  struct prefix_rd prd;
+  struct prefix pfx_vn;
+  int rc;
+  uint32_t local_pref = rfp_cost_to_localpref (0);
+
+  rc = rfapiRaddr2Qprefix (&(rfd->vn_addr), &pfx_vn);
+  assert (!rc);
+
+  /*
+   * Construct route distinguisher = 0
+   */
+  memset (&prd, 0, sizeof (prd));
+  prd.family = AF_UNSPEC;
+  prd.prefixlen = 64;
+
+  add_vnc_route (rfd,           /* rfapi descr, for export list  & backref */
+                 bgp,           /* which bgp instance */
+                 SAFI_ENCAP,    /* which SAFI */
+                 &pfx_vn,       /* prefix to advertise */
+                 &prd,          /* route distinguisher to use */
+                 &rfd->un_addr, /* nexthop */
+                 &local_pref,
+                pLifetime,     /* max lifetime of child VPN routes */
+                 NULL,          /* no rfp options  for ENCAP safi */
+                 NULL,          /* rfp un options */
+                 NULL,          /* rfp vn options */
+                 rfd->rt_export_list,
+                NULL,          /* med */
+                 NULL,          /* label: default */
+                 ZEBRA_ROUTE_BGP,
+                BGP_ROUTE_RFP,
+                0);
+}
+
+
+/***********************************************************************
+ *                     RFP processing behavior configuration
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_rfp_set_configuration
+ *
+ * This is used to change rfapi's processing behavior based on 
+ * RFP requirements. 
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    rfapi_rfp_cfg     Pointer to configuration structure
+ *
+ * output:
+ *    none
+ *
+ * return value: 
+ *     0               Success
+ *     ENXIO           Unabled to locate configured BGP/VNC
+--------------------------------------------*/
+int
+rfapi_rfp_set_configuration (void *rfp_start_val, struct rfapi_rfp_cfg *new)
+{
+  struct rfapi_rfp_cfg *rcfg;
+  struct bgp *bgp;
+
+  bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val);
+
+  if (!new || !bgp || !bgp->rfapi_cfg)
+    return ENXIO;
+
+  rcfg = &bgp->rfapi_cfg->rfp_cfg;
+  rcfg->download_type = new->download_type;
+  rcfg->ftd_advertisement_interval = new->ftd_advertisement_interval;
+  rcfg->holddown_factor = new->holddown_factor;
+
+  if (rcfg->use_updated_response != new->use_updated_response)
+    {
+      rcfg->use_updated_response = new->use_updated_response;
+      if (rcfg->use_updated_response)
+        rfapiMonitorCallbacksOn (bgp);
+      else
+        rfapiMonitorCallbacksOff (bgp);
+    }
+  if (rcfg->use_removes != new->use_removes)
+    {
+      rcfg->use_removes = new->use_removes;
+      if (rcfg->use_removes)
+        rfapiMonitorResponseRemovalOn (bgp);
+      else
+        rfapiMonitorResponseRemovalOff (bgp);
+    }
+  return 0;
+}
+
+/*------------------------------------------
+ * rfapi_rfp_set_cb_methods
+ *
+ * Change registered callback functions for asynchronous notifications 
+ * from RFAPI to the RFP client.
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    methods          Pointer to struct rfapi_rfp_cb_methods containing
+ *                     pointers to callback methods as described above
+ *
+ * return value: 
+ *     0               Success
+ *     ENXIO           BGP or VNC not configured
+ *------------------------------------------*/
+int
+rfapi_rfp_set_cb_methods (void *rfp_start_val,
+                          struct rfapi_rfp_cb_methods *methods)
+{
+  struct rfapi *h;
+  struct bgp *bgp;
+
+  bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val);
+  if (!bgp)
+    return ENXIO;
+
+  h = bgp->rfapi;
+  if (!h)
+    return ENXIO;
+
+  h->rfp_methods = *methods;
+
+  return 0;
+}
+
+/***********************************************************************
+ *                     NVE Sessions
+ ***********************************************************************/
+
+/*
+ * Caller must supply an already-allocated rfd with the "caller"
+ * fields already set (vn_addr, un_addr, callback, cookie)
+ * The advertised_prefixes[] array elements should be NULL to
+ * have this function set them to newly-allocated radix trees.
+ */
+static int
+rfapi_open_inner (
+  struct rfapi_descriptor      *rfd,
+  struct bgp                   *bgp,
+  struct rfapi                 *h,
+  struct rfapi_nve_group_cfg   *rfg)
+{
+  int ret;
+
+  if (h->flags & RFAPI_INCALLBACK)
+    return EDEADLK;
+
+  /*
+   * Fill in configured fields
+   */
+
+  /*
+   * If group's RD is specified as "auto", then fill in based
+   * on NVE's VN address
+   */
+  rfd->rd = rfg->rd;
+
+  if (rfd->rd.family == AF_UNIX)
+    {
+      ret = rfapi_set_autord_from_vn (&rfd->rd, &rfd->vn_addr);
+      if (ret != 0)
+        return ret;
+    }
+  rfd->rt_export_list = (rfg->rt_export_list) ?
+    ecommunity_dup (rfg->rt_export_list) : NULL;
+  rfd->response_lifetime = rfg->response_lifetime;
+  rfd->rfg = rfg;
+
+  /*
+   * Fill in BGP peer structure
+   */
+  rfd->peer = peer_new (bgp);
+  rfd->peer->status = Established; /* keep bgp core happy */
+  bgp_sync_delete (rfd->peer);  /* don't need these */
+  if (rfd->peer->ibuf)
+    {
+      stream_free (rfd->peer->ibuf);    /* don't need it */
+      rfd->peer->ibuf = NULL;
+    }
+  if (rfd->peer->obuf)
+    {
+      stream_fifo_free (rfd->peer->obuf);       /* don't need it */
+      rfd->peer->obuf = NULL;
+    }
+  if (rfd->peer->work)
+    {
+      stream_free (rfd->peer->work);    /* don't need it */
+      rfd->peer->work = NULL;
+    }
+  {                             /* base code assumes have valid host pointer */
+    char buf[BUFSIZ];
+    buf[0] = 0;
+
+    if (rfd->vn_addr.addr_family == AF_INET)
+      {
+        inet_ntop (AF_INET, &rfd->vn_addr.addr.v4, buf, BUFSIZ);
+      }
+    else if (rfd->vn_addr.addr_family == AF_INET6)
+      {
+        inet_ntop (AF_INET6, &rfd->vn_addr.addr.v6, buf, BUFSIZ);
+      }
+    rfd->peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);
+  }
+  /* Mark peer as belonging to HD */
+  SET_FLAG (rfd->peer->flags, PEER_FLAG_IS_RFAPI_HD);
+
+  /*
+   * Set min prefix lifetime to max value so it will get set
+   * upon first rfapi_register()
+   */
+  rfd->min_prefix_lifetime = UINT32_MAX;
+
+  /*
+   * Allocate response tables if needed
+   */
+#define RFD_RTINIT_AFI(rh, ary, afi) do {\
+  if (!ary[afi]) {    \
+    ary[afi] = route_table_init ();\
+    ary[afi]->info = rh;\
+  }\
+} while (0)
+
+#define RFD_RTINIT(rh, ary) do {\
+    RFD_RTINIT_AFI(rh, ary, AFI_IP);\
+    RFD_RTINIT_AFI(rh, ary, AFI_IP6);\
+    RFD_RTINIT_AFI(rh, ary, AFI_ETHER);\
+} while(0)
+
+  RFD_RTINIT(rfd, rfd->rib);
+  RFD_RTINIT(rfd, rfd->rib_pending);
+  RFD_RTINIT(rfd, rfd->rsp_times);
+
+  /*
+   * Link to Import Table
+   */
+  rfd->import_table = rfg->rfapi_import_table;
+  rfd->import_table->refcount += 1;
+
+  rfapiApInit (&rfd->advertised);
+
+  /*
+   * add this NVE descriptor to the list of NVEs in the NVE group
+   */
+  if (!rfg->nves)
+    {
+      rfg->nves = list_new ();
+    }
+  listnode_add (rfg->nves, rfd);
+
+  vnc_direct_bgp_add_nve (bgp, rfd);
+  vnc_zebra_add_nve (bgp, rfd);
+
+  return 0;
+}
+
+struct rfapi_vn_option *
+rfapiVnOptionsDup (struct rfapi_vn_option *orig)
+{
+  struct rfapi_vn_option *head = NULL;
+  struct rfapi_vn_option *tail = NULL;
+  struct rfapi_vn_option *vo = NULL;
+
+  for (vo = orig; vo; vo = vo->next)
+    {
+      struct rfapi_vn_option *new;
+
+      new = XCALLOC (MTYPE_RFAPI_VN_OPTION, sizeof (struct rfapi_vn_option));
+      memcpy (new, vo, sizeof (struct rfapi_vn_option));
+      new->next = NULL;
+
+      if (tail)
+        {
+          tail->next = new;
+        }
+      else
+        {
+          head = tail = new;
+        }
+    }
+  return head;
+}
+
+struct rfapi_un_option *
+rfapiUnOptionsDup (struct rfapi_un_option *orig)
+{
+  struct rfapi_un_option *head = NULL;
+  struct rfapi_un_option *tail = NULL;
+  struct rfapi_un_option *uo = NULL;
+
+  for (uo = orig; uo; uo = uo->next)
+    {
+      struct rfapi_un_option *new;
+
+      new = XCALLOC (MTYPE_RFAPI_UN_OPTION, sizeof (struct rfapi_un_option));
+      memcpy (new, uo, sizeof (struct rfapi_un_option));
+      new->next = NULL;
+
+      if (tail)
+        {
+          tail->next = new;
+        }
+      else
+        {
+          head = tail = new;
+        }
+    }
+  return head;
+}
+
+struct bgp_tea_options *
+rfapiOptionsDup (struct bgp_tea_options *orig)
+{
+  struct bgp_tea_options *head = NULL;
+  struct bgp_tea_options *tail = NULL;
+  struct bgp_tea_options *hop = NULL;
+
+  for (hop = orig; hop; hop = hop->next)
+    {
+      struct bgp_tea_options *new;
+
+      new = XCALLOC (MTYPE_BGP_TEA_OPTIONS, sizeof (struct bgp_tea_options));
+      memcpy (new, hop, sizeof (struct bgp_tea_options));
+      new->next = NULL;
+      if (hop->value)
+        {
+          new->value = XCALLOC (MTYPE_BGP_TEA_OPTIONS_VALUE, hop->length);
+          memcpy (new->value, hop->value, hop->length);
+        }
+      if (tail)
+        {
+          tail->next = new;
+        }
+      else
+        {
+          head = tail = new;
+        }
+    }
+  return head;
+}
+
+void
+rfapiFreeBgpTeaOptionChain (struct bgp_tea_options *p)
+{
+  struct bgp_tea_options *next;
+
+  while (p)
+    {
+      next = p->next;
+
+      if (p->value)
+        {
+          XFREE (MTYPE_BGP_TEA_OPTIONS_VALUE, p->value);
+          p->value = NULL;
+        }
+      XFREE (MTYPE_BGP_TEA_OPTIONS, p);
+
+      p = next;
+    }
+}
+
+void
+rfapiAdbFree (struct rfapi_adb *adb)
+{
+  XFREE (MTYPE_RFAPI_ADB, adb);
+}
+
+static int
+rfapi_query_inner (
+  void                         *handle,
+  struct rfapi_ip_addr         *target,
+  struct rfapi_l2address_option        *l2o,      /* may be NULL */
+  struct rfapi_next_hop_entry  **ppNextHopEntry)
+{
+  afi_t                         afi;
+  struct prefix                 p;
+  struct prefix                 p_original;
+  struct route_node             *rn;
+  struct rfapi_descriptor       *rfd = (struct rfapi_descriptor *) handle;
+  struct bgp                    *bgp = rfd->bgp;
+  struct rfapi_next_hop_entry   *pNHE = NULL;
+  struct rfapi_ip_addr          *self_vn_addr = NULL;
+  int                           eth_is_0 = 0;
+  int                           use_eth_resolution = 0;
+  struct rfapi_next_hop_entry   *i_nhe;
+
+  /* preemptive */
+  if (!bgp)
+    {
+      zlog_debug ("%s: No BGP instance, returning ENXIO", __func__);
+      return ENXIO;
+    }
+  if (!bgp->rfapi)
+    {
+      zlog_debug ("%s: No RFAPI instance, returning ENXIO", __func__);
+      return ENXIO;
+    }
+  if (bgp->rfapi->flags & RFAPI_INCALLBACK)
+    {
+      zlog_debug ("%s: Called during calback, returning EDEADLK", __func__);
+      return EDEADLK;
+    }
+
+  if (!is_valid_rfd (rfd))
+    {
+      zlog_debug ("%s: invalid handle, returning EBADF", __func__);
+      return EBADF;
+    }
+
+  rfd->rsp_counter++;                 /* dedup: identify this generation */
+  rfd->rsp_time = rfapi_time (NULL); /* response content dedup */
+  rfd->ftd_last_allowed_time =
+    bgp_clock() - bgp->rfapi_cfg->rfp_cfg.ftd_advertisement_interval;
+
+  if (l2o)
+    {
+      if (!memcmp (l2o->macaddr.octet, rfapi_ethaddr0.octet, ETHER_ADDR_LEN))
+        {
+          eth_is_0 = 1;
+        }
+      /* per t/c Paul/Lou 151022 */
+      if (!eth_is_0 || l2o->logical_net_id)
+        {
+          use_eth_resolution = 1;
+        }
+    }
+
+  if (ppNextHopEntry)
+    *ppNextHopEntry = NULL;
+
+  /*
+   * Save original target in prefix form. In case of L2-based queries,
+   * p_original will be modified to reflect the L2 target
+   */
+  assert(!rfapiRaddr2Qprefix (target, &p_original));
+
+  if (bgp->rfapi_cfg->rfp_cfg.download_type == RFAPI_RFP_DOWNLOAD_FULL)
+    {
+      /* convert query to 0/0 when full-table download is enabled */
+      memset ((char *) &p, 0, sizeof (p));
+      p.family = target->addr_family;
+    }
+  else
+    {
+      p = p_original;
+    }
+
+  {
+    char buf[BUFSIZ];
+
+    prefix2str (&p, buf, BUFSIZ);
+    buf[BUFSIZ - 1] = 0;        /* guarantee NUL-terminated */
+    zlog_debug ("%s(rfd=%p, target=%s, ppNextHop=%p)",
+                __func__, rfd, buf, ppNextHopEntry);
+  }
+
+  afi = family2afi (p.family);
+  assert (afi);
+
+  if (CHECK_FLAG (bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP))
+    {
+      self_vn_addr = &rfd->vn_addr;
+    }
+
+  if (use_eth_resolution)
+    {
+      uint32_t logical_net_id = l2o->logical_net_id;
+      struct ecommunity *l2com;
+
+      /*
+       * fix up p_original to contain L2 address
+       */
+      rfapiL2o2Qprefix (l2o, &p_original);
+
+      l2com =
+        bgp_rfapi_get_ecommunity_by_lni_label (bgp, 1, logical_net_id,
+                                               l2o->label);
+      if (l2com)
+        {
+          uint8_t *v = l2com->val;
+          logical_net_id = (v[5] << 16) + (v[6] << 8) + (v[7]);
+        }
+      /*
+       * Ethernet/L2-based lookup
+       *
+       * Always returns IT node corresponding to route
+       */
+
+      if (RFAPI_RFP_DOWNLOAD_FULL == bgp->rfapi_cfg->rfp_cfg.download_type)
+        {
+          eth_is_0 = 1;
+        }
+
+      rn = rfapiMonitorEthAdd (bgp,
+                               rfd,
+                               (eth_is_0 ? &rfapi_ethaddr0 : &l2o->macaddr),
+                               logical_net_id);
+
+      if (eth_is_0)
+        {
+          struct rfapi_ip_prefix rprefix;
+
+          memset (&rprefix, 0, sizeof (rprefix));
+          rprefix.prefix.addr_family = target->addr_family;
+          if (target->addr_family == AF_INET)
+            {
+              rprefix.length = 32;
+            }
+          else
+            {
+              rprefix.length = 128;
+            }
+
+          pNHE = rfapiEthRouteTable2NextHopList (logical_net_id, &rprefix,
+            rfd->response_lifetime, self_vn_addr, rfd->rib[afi], &p_original);
+          goto done;
+        }
+
+    }
+  else
+    {
+
+      /*
+       * IP-based lookup
+       */
+
+      rn = rfapiMonitorAdd (bgp, rfd, &p);
+
+      /*
+       * If target address is 0, this request is special: means to
+       * return ALL routes in the table
+       * 
+       * Monitors for All-Routes queries get put on a special list,
+       * not in the VPN tree
+       */
+      if (RFAPI_0_PREFIX (&p))
+        {
+
+          zlog_debug ("%s: 0-prefix", __func__);
+
+          /*
+           * Generate nexthop list for caller
+           */
+          pNHE = rfapiRouteTable2NextHopList (
+           rfd->import_table->imported_vpn[afi], rfd->response_lifetime,
+            self_vn_addr, rfd->rib[afi], &p_original);
+          goto done;
+        }
+
+      if (rn)
+        {
+          route_lock_node (rn); /* so we can unlock below */
+        }
+      else
+        {
+          /*
+           * returns locked node. Don't unlock yet because the unlock
+           * might free it before we're done with it. This situation
+           * could occur when rfapiMonitorGetAttachNode() returns a
+           * newly-created default node.
+           */
+          rn = rfapiMonitorGetAttachNode (rfd, &p);
+        }
+    }
+
+  assert (rn);
+  if (!rn->info)
+    {
+      route_unlock_node (rn);
+      zlog_debug ("%s: VPN route not found, returning ENOENT", __func__);
+      return ENOENT;
+    }
+
+  if (VNC_DEBUG(RFAPI_QUERY))
+    {
+      rfapiShowImportTable (NULL, "query", rfd->import_table->imported_vpn[afi],
+                        1);
+    }
+
+  if (use_eth_resolution)
+    {
+
+      struct rfapi_ip_prefix rprefix;
+
+      memset (&rprefix, 0, sizeof (rprefix));
+      rprefix.prefix.addr_family = target->addr_family;
+      if (target->addr_family == AF_INET)
+        {
+          rprefix.length = 32;
+        }
+      else
+        {
+          rprefix.length = 128;
+        }
+
+      pNHE = rfapiEthRouteNode2NextHopList (rn, &rprefix,
+        rfd->response_lifetime, self_vn_addr, rfd->rib[afi], &p_original);
+
+
+    }
+  else
+    {
+      /* 
+       * Generate answer to query
+       */
+      pNHE = rfapiRouteNode2NextHopList(rn, rfd->response_lifetime,
+        self_vn_addr, rfd->rib[afi], &p_original);
+    }
+
+  route_unlock_node (rn);
+
+done:
+  if (ppNextHopEntry)
+    {
+       /* only count if caller gets it */
+       ++bgp->rfapi->response_immediate_count;
+    }
+
+  if (!pNHE)
+    {
+      zlog_debug ("%s: NO NHEs, returning ENOENT", __func__);
+      return ENOENT;
+    }
+
+  /*
+   * count nexthops for statistics
+   */
+  for (i_nhe = pNHE; i_nhe; i_nhe = i_nhe->next)
+    {
+      ++rfd->stat_count_nh_reachable;
+    }
+
+  if (ppNextHopEntry)
+    {
+      *ppNextHopEntry = pNHE;
+    }
+  else
+    {
+      rfapi_free_next_hop_list (pNHE);
+    }
+
+  zlog_debug ("%s: success", __func__);
+  return 0;
+}
+
+/*
+ * support on-the-fly reassignment of an already-open nve to a new
+ * nve-group in the event that its original nve-group is
+ * administratively deleted.
+ */
+static int
+rfapi_open_rfd (struct rfapi_descriptor *rfd, struct bgp *bgp)
+{
+  struct prefix pfx_vn;
+  struct prefix pfx_un;
+  struct rfapi_nve_group_cfg *rfg;
+  struct rfapi *h;
+  struct rfapi_cfg *hc;
+  struct prefix_rd prd;
+  int rc;
+
+  h = bgp->rfapi;
+  if (!h)
+    return ENXIO;
+
+  hc = bgp->rfapi_cfg;
+  if (!hc)
+    return ENXIO;
+
+  rc = rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx_vn);
+  assert (!rc);
+
+  rc = rfapiRaddr2Qprefix (&rfd->un_addr, &pfx_un);
+  assert (!rc);
+
+  /*
+   * Find the matching nve group config block
+   */
+  rfg = bgp_rfapi_cfg_match_group (hc, &pfx_vn, &pfx_un);
+  if (!rfg)
+    {
+      return ENOENT;
+    }
+
+  /*
+   * check nve group config block for required values
+   */
+  if (!rfg->rt_export_list || !rfg->rfapi_import_table)
+    {
+
+      return ENOMSG;
+    }
+
+  rc = rfapi_open_inner (rfd, bgp, h, rfg);
+  if (rc)
+    {
+      return rc;
+    }
+
+
+  /*
+   * Construct route distinguisher for VPN routes
+   */
+  prd = rfd->rd;
+  prd.family = AF_UNSPEC;
+  prd.prefixlen = 64;
+
+  /*
+   * re-advertise registered routes, this time as part of new NVE-group
+   */
+  rfapiApReadvertiseAll (bgp, rfd);
+
+  /*
+   * re-attach callbacks to import table
+   */
+  if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE))
+    {
+      rfapiMonitorAttachImportHd (rfd);
+    }
+
+  return 0;
+}
+
+/*------------------------------------------
+ * rfapi_open
+ *
+ * This function initializes a NVE record and associates it with
+ * the specified VN and underlay network addresses
+ *
+ * input: 
+ *      rfp_start_val   value returned by rfp_start
+ *     vn              NVE virtual network address
+ *
+ *     un              NVE underlay network address
+ *
+ *     default_options Default options to use on registrations.
+ *                     For now only tunnel type is supported.
+ *                     May be overridden per-prefix in rfapi_register().
+ *                     Caller owns (rfapi_open() does not free)
+ *
+ *     response_cb     Pointer to next hop list update callback function or
+ *                     NULL when no callbacks are desired.
+ *
+ *     userdata        Passed to subsequent response_cb invocations.
+ *
+ * output:
+ *     response_lifetime The length of time that responses sent to this
+ *                     NVE are valid. 
+ *
+ *     pHandle         pointer to location to store rfapi handle. The
+ *                     handle must be passed on subsequent rfapi_ calls.
+ *
+ *
+ * return value: 
+ *     0               Success
+ *     EEXIST          NVE with this {vn,un} already open
+ *     ENOENT          No matching nve group config
+ *     ENOMSG          Matched nve group config was incomplete
+ *     ENXIO           BGP or VNC not configured
+ *     EAFNOSUPPORT    Matched nve group specifies auto-assignment of RD,
+ *                     but underlay network address is not IPv4
+ *     EDEADLK         Called from within a callback procedure
+ *------------------------------------------*/
+int
+rfapi_open (
+  void                         *rfp_start_val,
+  struct rfapi_ip_addr         *vn,
+  struct rfapi_ip_addr         *un,
+  struct rfapi_un_option       *default_options,
+  uint32_t                     *response_lifetime,
+  void                         *userdata,     /* callback cookie */
+  rfapi_handle                 *pHandle)
+{
+  struct bgp *bgp;
+  struct rfapi *h;
+  struct rfapi_descriptor *rfd;
+  struct rfapi_cfg *hc;
+  struct rfapi_nve_group_cfg *rfg;
+
+  struct prefix pfx_vn;
+  struct prefix pfx_un;
+
+  struct route_node *rn;
+  int rc;
+  rfapi_handle hh = NULL;
+  int reusing_provisional = 0;
+
+  afi_t afi_vn;
+  afi_t afi_un;
+
+  {
+    char buf[2][INET_ADDRSTRLEN];
+    zlog_debug ("%s: VN=%s UN=%s", __func__,
+                rfapiRfapiIpAddr2Str (vn, buf[0], INET_ADDRSTRLEN),
+                rfapiRfapiIpAddr2Str (un, buf[1], INET_ADDRSTRLEN));
+  }
+
+  assert (pHandle);
+  *pHandle = NULL;
+
+  bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val);
+  if (!bgp)
+    return ENXIO;
+
+  h = bgp->rfapi;
+  if (!h)
+    return ENXIO;
+
+  hc = bgp->rfapi_cfg;
+  if (!hc)
+    return ENXIO;
+
+  if (h->flags & RFAPI_INCALLBACK)
+    return EDEADLK;
+
+  rc = rfapiRaddr2Qprefix (vn, &pfx_vn);
+  assert (!rc);
+
+  rc = rfapiRaddr2Qprefix (un, &pfx_un);
+  assert (!rc);
+
+  /*
+   * already have a descriptor with VN and UN?
+   */
+  if (!rfapi_find_handle (bgp, vn, un, &hh))
+    {
+      /*
+       * we might have set up a handle for static routes before
+       * this NVE was opened. In that case, reuse the handle
+       */
+      rfd = hh;
+      if (!CHECK_FLAG (rfd->flags, RFAPI_HD_FLAG_PROVISIONAL))
+        {
+          return EEXIST;
+        }
+
+      /*
+       * reuse provisional descriptor
+       * hh is not NULL
+       */
+      reusing_provisional = 1;
+    }
+
+  /*
+   * Find the matching nve group config block
+   */
+  rfg = bgp_rfapi_cfg_match_group (hc, &pfx_vn, &pfx_un);
+  if (!rfg)
+    {
+      ++h->stat.count_unknown_nves;
+      {
+        char buf[2][INET_ADDRSTRLEN];
+        zlog_notice ("%s: no matching group VN=%s UN=%s", __func__,
+                     rfapiRfapiIpAddr2Str (vn, buf[0], INET_ADDRSTRLEN),
+                     rfapiRfapiIpAddr2Str (un, buf[1], INET_ADDRSTRLEN));
+      }
+      return ENOENT;
+    }
+
+  /*
+   * check nve group config block for required values
+   */
+  if (!rfg->rt_export_list || !rfg->rfapi_import_table)
+    {
+
+      ++h->stat.count_unknown_nves;
+      return ENOMSG;
+    }
+
+  /*
+   * If group config specifies auto-rd assignment, check that
+   * VN address is IPv4|v6 so we don't fail in rfapi_open_inner().
+   * Check here so we don't need to unwind memory allocations, &c.
+   */
+  if ((rfg->rd.family == AF_UNIX) && (vn->addr_family != AF_INET)
+      && (vn->addr_family != AF_INET6))
+    {
+      return EAFNOSUPPORT;
+    }
+
+  if (hh)
+    {
+      /*
+       * reusing provisional rfd
+       */
+      rfd = hh;
+    }
+  else
+    {
+      rfd = XCALLOC (MTYPE_RFAPI_DESC, sizeof (struct rfapi_descriptor));
+    }
+  assert (rfd);
+
+  rfd->bgp = bgp;
+  if (default_options)
+    {
+      struct rfapi_un_option *p;
+
+      for (p = default_options; p; p = p->next)
+        {
+          if ((RFAPI_UN_OPTION_TYPE_PROVISIONAL == p->type))
+            {
+              rfd->flags |= RFAPI_HD_FLAG_PROVISIONAL;
+            }
+          if ((RFAPI_UN_OPTION_TYPE_TUNNELTYPE == p->type))
+            {
+              rfd->default_tunneltype_option = p->v.tunnel;
+            }
+        }
+    }
+
+  /*
+   * Fill in caller fields
+   */
+  rfd->vn_addr = *vn;
+  rfd->un_addr = *un;
+  rfd->cookie = userdata;
+
+  if (!reusing_provisional)
+    {
+      rfapi_time (&rfd->open_time);
+
+      {
+        char buf_vn[BUFSIZ];
+        char buf_un[BUFSIZ];
+
+        rfapiRfapiIpAddr2Str (vn, buf_vn, BUFSIZ);
+        rfapiRfapiIpAddr2Str (un, buf_un, BUFSIZ);
+
+        zlog_debug ("%s: new HD with VN=%s UN=%s cookie=%p",
+                    __func__, buf_vn, buf_un, userdata);
+      }
+
+      listnode_add (&h->descriptors, rfd);
+      if (h->descriptors.count > h->stat.max_descriptors)
+        {
+          h->stat.max_descriptors = h->descriptors.count;
+        }
+
+      /*
+       * attach to UN radix tree
+       */
+      afi_vn = family2afi (rfd->vn_addr.addr_family);
+      afi_un = family2afi (rfd->un_addr.addr_family);
+      assert (afi_vn && afi_un);
+      assert (!rfapiRaddr2Qprefix (&rfd->un_addr, &pfx_un));
+
+      rn = route_node_get (&(h->un[afi_un]), &pfx_un);
+      assert (rn);
+      rfd->next = rn->info;
+      rn->info = rfd;
+      rfd->un_node = rn;
+
+      rc = rfapi_open_inner (rfd, bgp, h, rfg);
+      /*
+       * This can fail only if the VN address is IPv6 and the group
+       * specified auto-assignment of RDs, which only works for v4,
+       * and the check above should catch it.
+       *
+       * Another failure possibility is that we were called
+       * during an rfapi callback. Also checked above.
+       */
+      assert (!rc);
+    }
+
+  if (response_lifetime)
+    *response_lifetime = rfd->response_lifetime;
+  *pHandle = rfd;
+  return 0;
+}
+
+/*
+ * For use with debug functions
+ */
+static int
+rfapi_set_response_cb (struct rfapi_descriptor *rfd,
+                       rfapi_response_cb_t * response_cb)
+{
+  if (!is_valid_rfd (rfd))
+    return EBADF;
+  rfd->response_cb = response_cb;
+  return 0;
+}
+
+/*
+ * rfapi_close_inner
+ *
+ * Does almost all the work of rfapi_close, except:
+ *     1. preserves the descriptor (doesn't free it)
+ *     2. preserves the prefix query list (i.e., rfd->mon list)
+ *     3. preserves the advertised prefix list (rfd->advertised)
+ *     4. preserves the rib and rib_pending tables
+ *
+ * The purpose of organizing it this way is to support on-the-fly
+ * reassignment of an already-open nve to a new nve-group in the
+ * event that its original nve-group is administratively deleted.
+ */
+static int
+rfapi_close_inner (struct rfapi_descriptor *rfd, struct bgp *bgp)
+{
+  int rc;
+  struct prefix pfx_vn;
+  struct prefix_rd prd;         /* currently always 0 for VN->UN */
+
+  if (!is_valid_rfd (rfd))
+    return EBADF;
+
+  rc = rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx_vn);
+  assert (!rc);                 /* should never have bad AF in stored vn address */
+
+  /*
+   * update exported routes to reflect disappearance of this NVE as nexthop
+   */
+  vnc_direct_bgp_del_nve (bgp, rfd);
+  vnc_zebra_del_nve (bgp, rfd);
+
+  /*
+   * unlink this HD's monitors from import table
+   */
+  rfapiMonitorDetachImportHd (rfd);
+
+  /*
+   * Unlink from Import Table
+   * NB rfd->import_table will be NULL if we are closing a stale descriptor
+   */
+  if (rfd->import_table)
+    rfapiImportTableRefDelByIt (bgp, rfd->import_table);
+  rfd->import_table = NULL;
+
+  /*
+   * Construct route distinguisher
+   */
+  memset (&prd, 0, sizeof (prd));
+  prd = rfd->rd;
+  prd.family = AF_UNSPEC;
+  prd.prefixlen = 64;
+
+  /*
+   * withdraw tunnel
+   */
+  del_vnc_route (
+    rfd,
+    rfd->peer,
+    bgp,
+    SAFI_ENCAP,
+    &pfx_vn,           /* prefix being advertised */
+    &prd,              /* route distinguisher to use (0 for ENCAP) */
+    ZEBRA_ROUTE_BGP,
+    BGP_ROUTE_RFP,
+    NULL,
+    0);                        /* no kill */
+
+  /*
+   * Construct route distinguisher for VPN routes
+   */
+  prd = rfd->rd;
+  prd.family = AF_UNSPEC;
+  prd.prefixlen = 64;
+
+  /*
+   * find all VPN routes associated with this rfd and delete them, too
+   */
+  rfapiApWithdrawAll (bgp, rfd);
+
+  /*
+   * remove this nve descriptor from the list of nves
+   * associated with the nve group
+   */
+  if (rfd->rfg)
+    {
+      listnode_delete (rfd->rfg->nves, rfd);
+      rfd->rfg = NULL;          /* XXX mark as orphaned/stale */
+    }
+
+  if (rfd->rt_export_list)
+    ecommunity_free (&rfd->rt_export_list);
+  rfd->rt_export_list = NULL;
+
+  /*
+   * free peer structure (possibly delayed until its
+   * refcount reaches zero)
+   */
+  if (rfd->peer)
+    {
+      zlog_debug ("%s: calling peer_delete(%p), #%d",
+                  __func__, rfd->peer, rfd->peer->lock);
+      peer_delete (rfd->peer);
+    }
+  rfd->peer = NULL;
+
+  return 0;
+}
+
+int
+rfapi_close (void *handle)
+{
+  struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle;
+  int rc;
+  struct route_node *node;
+  struct bgp *bgp;
+  struct rfapi *h;
+  struct rfapi_cfg *hc;
+
+  zlog_debug ("%s: rfd=%p", __func__, rfd);
+
+#if RFAPI_WHO_IS_CALLING_ME
+#ifdef HAVE_GLIBC_BACKTRACE
+#define RFAPI_DEBUG_BACKTRACE_NENTRIES 5
+  {
+    void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES];
+    char **syms;
+    int i;
+    size_t size;
+
+    size = backtrace (buf, RFAPI_DEBUG_BACKTRACE_NENTRIES);
+    syms = backtrace_symbols (buf, size);
+    for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES; ++i)
+      {
+        zlog_debug ("backtrace[%2d]: %s", i, syms[i]);
+      }
+    free (syms);
+  }
+#endif
+#endif
+
+  bgp = rfd->bgp;
+  if (!bgp)
+    return ENXIO;
+
+  h = bgp->rfapi;
+  if (!h)
+    return ENXIO;
+
+  if (!is_valid_rfd (rfd))
+    return EBADF;
+
+  if (h->flags & RFAPI_INCALLBACK)
+    {
+      /*
+       * Queue these close requests for processing after callback
+       * is finished
+       */
+      if (!CHECK_FLAG (rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY))
+        {
+          work_queue_add (h->deferred_close_q, handle);
+          zlog_debug ("%s: added handle %p to deferred close queue",
+                      __func__, handle);
+        }
+      return 0;
+    }
+
+  hc = bgp->rfapi_cfg;
+
+  if (CHECK_FLAG (rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY))
+    {
+
+      zlog_debug ("%s administrative close rfd=%p", __func__, rfd);
+
+      if (h && h->rfp_methods.close_cb)
+        {
+          zlog_debug ("%s calling close callback rfd=%p", __func__, rfd);
+
+          /*
+           * call the callback fairly early so that it can still lookup un/vn
+           * from handle, etc.
+           *
+           * NB RFAPI_INCALLBACK is tested above, so if we reach this point
+           * we are not already in the context of a callback.
+           */
+          h->flags |= RFAPI_INCALLBACK;
+          (*h->rfp_methods.close_cb) (handle, EIDRM);
+          h->flags &= ~RFAPI_INCALLBACK;
+        }
+    }
+
+  if (rfd->rfg)
+    {
+      /* 
+       * Orphaned descriptors have already done this part, so do
+       * only for non-orphaned descriptors.
+       */
+      if ((rc = rfapi_close_inner (rfd, bgp)))
+        return rc;
+    }
+
+  /*
+   * Remove descriptor from UN index
+   * (remove from chain at node)
+   */
+  rc = rfapi_find_node (bgp, &rfd->vn_addr, &rfd->un_addr, &node);
+  if (!rc)
+    {
+      struct rfapi_descriptor *hh;
+
+      if (node->info == rfd)
+        {
+          node->info = rfd->next;
+        }
+      else
+        {
+
+          for (hh = node->info; hh; hh = hh->next)
+            {
+              if (hh->next == rfd)
+                {
+                  hh->next = rfd->next;
+                  break;
+                }
+            }
+        }
+      route_unlock_node (node);
+    }
+
+  /*
+   * remove from descriptor list
+   */
+  listnode_delete (&h->descriptors, rfd);
+
+  /*
+   * Delete monitor list items and free monitor structures
+   */
+  (void) rfapiMonitorDelHd (rfd);
+
+  /*
+   * release advertised prefix data
+   */
+  rfapiApRelease (&rfd->advertised);
+
+  /*
+   * Release RFP callback RIB
+   */
+  rfapiRibFree (rfd);
+
+  /*
+   * free descriptor
+   */
+  memset (rfd, 0, sizeof (struct rfapi_descriptor));
+  XFREE (MTYPE_RFAPI_DESC, rfd);
+
+  return 0;
+}
+
+/*
+ * Reopen a nve descriptor. If the descriptor's NVE-group
+ * does not exist (e.g., if it has been administratively removed),
+ * reassignment to a new NVE-group is attempted.
+ *
+ * If NVE-group reassignment fails, the descriptor becomes "stale"
+ * (rfd->rfg == NULL implies "stale:). The only permissible API operation
+ * on a stale descriptor is rfapi_close(). Any other rfapi_* API operation
+ * on the descriptor will return ESTALE.
+ *
+ * Reopening a descriptor is a potentially expensive operation, because
+ * it involves withdrawing any routes advertised by the NVE, withdrawing
+ * the NVE's route queries, and then re-adding them all after a new
+ * NVE-group is assigned. There are also possible route-export affects
+ * caused by deleting and then adding the NVE: advertised prefixes
+ * and nexthop lists for exported routes can turn over.
+ */
+int
+rfapi_reopen (struct rfapi_descriptor *rfd, struct bgp *bgp)
+{
+  struct rfapi *h;
+  struct rfapi_cfg *hc;
+  int rc;
+
+  if ((rc = rfapi_close_inner (rfd, bgp)))
+    {
+      return rc;
+    }
+  if ((rc = rfapi_open_rfd (rfd, bgp)))
+    {
+
+      h = bgp->rfapi;
+      hc = bgp->rfapi_cfg;
+
+      assert (!CHECK_FLAG (h->flags, RFAPI_INCALLBACK));
+
+      if (CHECK_FLAG (rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY) &&
+          h && h->rfp_methods.close_cb)
+        {
+
+          /*
+           * NB RFAPI_INCALLBACK is tested above, so if we reach this point
+           * we are not already in the context of a callback.
+           */
+          h->flags |= RFAPI_INCALLBACK;
+          (*h->rfp_methods.close_cb) ((rfapi_handle) rfd, ESTALE);
+          h->flags &= ~RFAPI_INCALLBACK;
+        }
+      return rc;
+    }
+  return 0;
+}
+
+/***********************************************************************
+ *                     NVE Routes
+ ***********************************************************************/
+/* 
+ * Announce reachability to this prefix via the NVE
+ */
+int
+rfapi_register (
+  void                         *handle,
+  struct rfapi_ip_prefix       *prefix,
+  uint32_t                     lifetime,        /* host byte order */
+  struct rfapi_un_option       *options_un,
+  struct rfapi_vn_option       *options_vn,
+  rfapi_register_action                action)
+{
+  struct rfapi_descriptor      *rfd = (struct rfapi_descriptor *) handle;
+  struct bgp                   *bgp;
+  struct prefix                        p;
+  struct prefix                        *pfx_ip = NULL;
+  struct prefix_rd             prd;
+  int                          afi;
+  struct prefix                        pfx_mac_buf;
+  struct prefix                        *pfx_mac = NULL;
+  struct prefix                        pfx_vn_buf;
+  const char                   *action_str = NULL;
+  uint32_t                     *label = NULL;
+  struct rfapi_vn_option       *vo;
+  struct rfapi_l2address_option        *l2o = NULL;
+  struct rfapi_nexthop         *lnh = NULL;
+  struct prefix_rd             *prd_override = NULL;
+
+  switch (action)
+    {
+    case RFAPI_REGISTER_ADD:
+      action_str = "add";
+      break;
+    case RFAPI_REGISTER_WITHDRAW:
+      action_str = "withdraw";
+      break;
+    case RFAPI_REGISTER_KILL:
+      action_str = "kill";
+      break;
+    default:
+      assert (0);
+      break;
+    }
+
+  /*
+   * Inspect VN options
+   */
+  for (vo = options_vn; vo; vo = vo->next)
+    {
+      if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type)
+        {
+          l2o = &vo->v.l2addr;
+        }
+      if (RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP == vo->type)
+        {
+          lnh = &vo->v.local_nexthop;
+        }
+      if (RFAPI_VN_OPTION_TYPE_INTERNAL_RD == vo->type)
+        {
+          prd_override = &vo->v.internal_rd;
+        }
+    }
+
+    /*********************************************************************
+     *                 advertise prefix
+     *********************************************************************/
+
+  /*
+   * set <p> based on <prefix>
+   */
+  assert (!rfapiRprefix2Qprefix (prefix, &p));
+
+  afi = family2afi (prefix->prefix.addr_family);
+  assert (afi);
+
+
+  {
+    char buf[BUFSIZ];
+
+    prefix2str (&p, buf, BUFSIZ);
+    buf[BUFSIZ - 1] = 0;        /* guarantee NUL-terminated */
+    zlog_debug
+      ("%s(rfd=%p, pfx=%s, lifetime=%d, opts_un=%p, opts_vn=%p, action=%s)",
+       __func__, rfd, buf, lifetime, options_un, options_vn, action_str);
+  }
+
+  /*
+   * These tests come after the prefix conversion so that we can
+   * print the prefix in a debug message before failing
+   */
+
+  bgp = rfd->bgp;
+  if (!bgp)
+    {
+      zlog_debug ("%s: no BGP instance: returning ENXIO", __func__);
+      return ENXIO;
+    }
+  if (!bgp->rfapi)
+    {
+      zlog_debug ("%s: no RFAPI instance: returning ENXIO", __func__);
+      return ENXIO;
+    }
+  if (!rfd->rfg)
+    {
+      if (RFAPI_REGISTER_ADD == action)
+        {
+          ++bgp->rfapi->stat.count_registrations_failed;
+        }
+      zlog_debug ("%s: rfd=%p, no RF GRP instance: returning ESTALE",
+                  __func__, rfd);
+      return ESTALE;
+    }
+
+  if (bgp->rfapi->flags & RFAPI_INCALLBACK)
+    {
+      if (RFAPI_REGISTER_ADD == action)
+        {
+          ++bgp->rfapi->stat.count_registrations_failed;
+        }
+      zlog_debug ("%s: in callback: returning EDEADLK", __func__);
+      return EDEADLK;
+    }
+
+  if (!is_valid_rfd (rfd))
+    {
+      if (RFAPI_REGISTER_ADD == action)
+        {
+          ++bgp->rfapi->stat.count_registrations_failed;
+        }
+      zlog_debug ("%s: invalid handle: returning EBADF", __func__);
+      return EBADF;
+    }
+
+  /*
+   * Is there a MAC address in this registration?
+   */
+  if (l2o && !RFAPI_0_ETHERADDR (&l2o->macaddr))
+    {
+      rfapiL2o2Qprefix (l2o, &pfx_mac_buf);
+      pfx_mac = &pfx_mac_buf;
+    }
+
+  /*
+   * Is there an IP prefix in this registration?
+   */
+  if (!(RFAPI_0_PREFIX (&p) && RFAPI_HOST_PREFIX (&p)))
+    {
+      pfx_ip = &p;
+    }
+  else
+    {
+      if (!pfx_mac)
+        {
+          zlog_debug ("%s: missing mac addr that is required for host 0 pfx",
+                      __func__);
+          if (RFAPI_REGISTER_ADD == action)
+            {
+              ++bgp->rfapi->stat.count_registrations_failed;
+            }
+          return EINVAL;
+        }
+      if (rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx_vn_buf))
+        {
+          zlog_debug ("%s: handle has bad vn_addr: returning EBADF",
+                      __func__);
+          if (RFAPI_REGISTER_ADD == action)
+            {
+              ++bgp->rfapi->stat.count_registrations_failed;
+            }
+          return EBADF;
+        }
+    }
+
+  if (RFAPI_REGISTER_ADD == action)
+    {
+      ++bgp->rfapi->stat.count_registrations;
+    }
+
+  /*
+   * Figure out if this registration is missing an IP address
+   *
+   * MAC-addr based:
+   *
+   *  In RFAPI, we use prefixes in family AF_LINK to store
+   *  the MAC addresses. These prefixes are used for the
+   *  list of advertised prefixes and in the RFAPI import
+   *  tables.
+   *
+   *  In BGP proper, we use the prefix matching the NVE's
+   *  VN address with a host prefix-length (i.e., 32 or 128).
+   *  
+   */
+  if (l2o && l2o->logical_net_id && RFAPI_0_PREFIX (&p) &&
+      RFAPI_HOST_PREFIX (&p))
+    {
+
+      rfapiL2o2Qprefix (l2o, &pfx_mac_buf);
+      pfx_mac = &pfx_mac_buf;
+    }
+
+  /*
+   * Construct route distinguisher
+   */
+  if (prd_override)
+    {
+      prd = *prd_override;
+    }
+  else
+    {
+      memset (&prd, 0, sizeof (prd));
+      if (pfx_mac)
+        {
+          prd.family = AF_UNSPEC;
+          prd.prefixlen = 64;
+          encode_rd_type(RD_TYPE_VNC_ETH, prd.val);
+          if (l2o->local_nve_id || !(rfd->rfg->flags & RFAPI_RFG_L2RD))
+            {
+              /*
+               * If Local NVE ID is specified in message, use it.
+               * (if no local default configured, also use it even if 0)
+               */
+              prd.val[1] = l2o->local_nve_id;
+            }
+          else
+            {
+              if (rfd->rfg->l2rd)
+                {
+                  /*
+                   * locally-configured literal value
+                   */
+                  prd.val[1] = rfd->rfg->l2rd;
+                }
+              else
+                {
+                  /* 
+                   * 0 means auto:vn, which means use LSB of VN addr
+                   */
+                  if (rfd->vn_addr.addr_family == AF_INET)
+                    {
+                      prd.val[1] =
+                        *(((char *) &rfd->vn_addr.addr.v4.s_addr) + 3);
+                    }
+                  else
+                    {
+                      prd.val[1] =
+                        *(((char *) &rfd->vn_addr.addr.v6.s6_addr) + 15);
+                    }
+                }
+            }
+          memcpy (prd.val + 2, pfx_mac->u.prefix_eth.octet, 6);
+        }
+      else
+        {
+          prd = rfd->rd;
+          prd.family = AF_UNSPEC;
+          prd.prefixlen = 64;
+        }
+    }
+
+
+  if (action == RFAPI_REGISTER_WITHDRAW || action == RFAPI_REGISTER_KILL)
+    {
+
+      int adv_tunnel = 0;
+
+      /*
+       * withdraw previous advertisement
+       */
+      del_vnc_route (
+       rfd,
+       rfd->peer,
+       bgp,
+       SAFI_MPLS_VPN,
+       pfx_ip ? pfx_ip : &pfx_vn_buf,  /* prefix being advertised */
+       &prd,                           /* route distinguisher (0 for ENCAP) */
+       ZEBRA_ROUTE_BGP,
+       BGP_ROUTE_RFP,
+       NULL,
+       action == RFAPI_REGISTER_KILL);
+
+      if (0 == rfapiApDelete (bgp, rfd, &p, pfx_mac, &adv_tunnel))
+        {
+          if (adv_tunnel)
+            rfapiTunnelRouteAnnounce (bgp, rfd, &rfd->max_prefix_lifetime);
+        }
+
+    }
+  else
+    {
+
+      int adv_tunnel = 0;
+      uint32_t local_pref;
+      struct ecommunity *rtlist = NULL;
+      struct ecommunity_val ecom_value;
+
+      if (!rfapiApCount (rfd))
+        {
+          /* 
+           * make sure we advertise tunnel route upon adding the
+           * first VPN route
+           */
+          adv_tunnel = 1;
+        }
+
+      if (rfapiApAdd (bgp, rfd, &p, pfx_mac, &prd, lifetime, prefix->cost,
+                      l2o))
+        {
+          adv_tunnel = 1;
+        }
+
+      zlog_debug ("%s: adv_tunnel = %d", __func__, adv_tunnel);
+      if (adv_tunnel)
+        {
+          zlog_debug ("%s: announcing tunnel route", __func__);
+          rfapiTunnelRouteAnnounce (bgp, rfd, &rfd->max_prefix_lifetime);
+        }
+
+      zlog_debug ("%s: calling add_vnc_route", __func__);
+
+      local_pref = rfp_cost_to_localpref (prefix->cost);
+
+      if (l2o && l2o->label)
+        label = &l2o->label;
+
+      if (pfx_mac)
+        {
+          struct ecommunity *l2com = NULL;
+
+          if (label)
+            {
+              l2com = bgp_rfapi_get_ecommunity_by_lni_label (bgp, 1,
+                                                             l2o->logical_net_id,
+                                                             *label);
+            }
+          if (l2com)
+            {
+              rtlist = ecommunity_dup (l2com);
+            }
+          else
+            {
+              /*
+               * If mac address is set, add an RT based on the registered LNI
+               */
+              memset ((char *) &ecom_value, 0, sizeof (ecom_value));
+              ecom_value.val[1] = 0x02;
+              ecom_value.val[5] = (l2o->logical_net_id >> 16) & 0xff;
+              ecom_value.val[6] = (l2o->logical_net_id >> 8) & 0xff;
+              ecom_value.val[7] = (l2o->logical_net_id >> 0) & 0xff;
+              rtlist = ecommunity_new();
+              ecommunity_add_val (rtlist, &ecom_value);
+            }
+        }
+
+      /*
+       * advertise prefix via tunnel endpoint
+       */
+      add_vnc_route (
+       rfd,                    /* rfapi descr, for export list & backref */
+       bgp,                    /* which bgp instance */
+       SAFI_MPLS_VPN,          /* which SAFI */
+       (pfx_ip ? pfx_ip : &pfx_vn_buf),        /* prefix being advertised */
+       &prd,                   /* route distinguisher to use (0 for ENCAP) */
+       &rfd->vn_addr,          /* nexthop */
+       &local_pref,
+       &lifetime,              /* prefix lifetime -> Tunnel Encap attr */
+       NULL,
+       options_un,             /* rfapi un options */
+       options_vn,             /* rfapi vn options */
+       (rtlist ? rtlist : rfd->rt_export_list),
+       NULL,                   /* med */
+       label,                  /* label: default */
+       ZEBRA_ROUTE_BGP,
+       BGP_ROUTE_RFP,
+       0);
+
+      if (rtlist)
+        ecommunity_free (&rtlist);      /* sets rtlist = NULL */
+    }
+
+  zlog_debug ("%s: success", __func__);
+  return 0;
+}
+
+int
+rfapi_query (
+  void                         *handle,
+  struct rfapi_ip_addr         *target,
+  struct rfapi_l2address_option        *l2o,    /* may be NULL */
+  struct rfapi_next_hop_entry  **ppNextHopEntry)
+{
+  struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle;
+  struct bgp *bgp = rfd->bgp;
+  int rc;
+
+  assert (ppNextHopEntry);
+  *ppNextHopEntry = NULL;
+
+  if (bgp && bgp->rfapi)
+    {
+      bgp->rfapi->stat.count_queries++;
+    }
+
+  if (!rfd->rfg)
+    {
+      if (bgp && bgp->rfapi)
+        ++bgp->rfapi->stat.count_queries_failed;
+      return ESTALE;
+    }
+
+  if ((rc = rfapi_query_inner (handle, target, l2o, ppNextHopEntry)))
+    {
+      if (bgp && bgp->rfapi)
+        ++bgp->rfapi->stat.count_queries_failed;
+    }
+  return rc;
+}
+
+int
+rfapi_query_done (rfapi_handle handle, struct rfapi_ip_addr *target)
+{
+  struct prefix p;
+  int rc;
+  struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle;
+  struct bgp *bgp = rfd->bgp;
+
+  if (!rfd->rfg)
+    return ESTALE;
+
+  assert (target);
+  rc = rfapiRaddr2Qprefix (target, &p);
+  assert (!rc);
+
+  if (!is_valid_rfd (rfd))
+    return EBADF;
+
+  /* preemptive */
+  if (!bgp || !bgp->rfapi)
+    return ENXIO;
+
+  if (bgp->rfapi->flags & RFAPI_INCALLBACK)
+    return EDEADLK;
+
+  rfapiMonitorDel (bgp, rfd, &p);
+
+  return 0;
+}
+
+int
+rfapi_query_done_all (rfapi_handle handle, int *count)
+{
+  struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle;
+  struct bgp *bgp = rfd->bgp;;
+  int num;
+
+  if (!rfd->rfg)
+    return ESTALE;
+
+  if (!is_valid_rfd (rfd))
+    return EBADF;
+
+  /* preemptive */
+  if (!bgp || !bgp->rfapi)
+    return ENXIO;
+
+  if (bgp->rfapi->flags & RFAPI_INCALLBACK)
+    return EDEADLK;
+
+  num = rfapiMonitorDelHd (rfd);
+
+  if (count)
+    *count = num;
+
+  return 0;
+}
+
+void
+rfapi_free_next_hop_list (struct rfapi_next_hop_entry *list)
+{
+  struct rfapi_next_hop_entry *nh;
+  struct rfapi_next_hop_entry *next;
+
+  for (nh = list; nh; nh = next)
+    {
+      next = nh->next;
+      rfapi_un_options_free (nh->un_options);
+      nh->un_options = NULL;
+      rfapi_vn_options_free (nh->vn_options);
+      nh->vn_options = NULL;
+      XFREE (MTYPE_RFAPI_NEXTHOP, nh);
+    }
+}
+
+/*
+ * NULL handle => return total count across all nves
+ */
+uint32_t
+rfapi_monitor_count (void *handle)
+{
+  struct bgp *bgp = bgp_get_default ();
+  uint32_t count;
+
+  if (handle)
+    {
+      struct rfapi_descriptor *rfd = (struct rfapi_descriptor *) handle;
+      count = rfd->monitor_count;
+    }
+  else
+    {
+
+      if (!bgp || !bgp->rfapi)
+        return 0;
+
+      count = bgp->rfapi->monitor_count;
+    }
+
+  return count;
+}
+
+/***********************************************************************
+ *                     CLI/CONFIG
+ ***********************************************************************/
+
+DEFUN (debug_rfapi_show_nves,
+       debug_rfapi_show_nves_cmd,
+       "debug rfapi-dev show nves",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       SHOW_STR
+       "NVE Information\n")
+{
+  rfapiPrintMatchingDescriptors (vty, NULL, NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (
+  debug_rfapi_show_nves_vn_un,
+  debug_rfapi_show_nves_vn_un_cmd,
+  "debug rfapi-dev show nves (vn|un) (A.B.C.D|X:X::X:X)", /* prefix also ok */
+  DEBUG_STR
+  DEBUG_RFAPI_STR
+  SHOW_STR
+  "NVE Information\n"
+  "Specify virtual network or underlay network interface\n"
+  "IPv4 or IPv6 address\n")
+{
+  struct prefix pfx;
+
+  if (!str2prefix (argv[1], &pfx))
+    {
+      vty_out (vty, "Malformed address \"%s\"%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (pfx.family != AF_INET && pfx.family != AF_INET6)
+    {
+      vty_out (vty, "Invalid address \"%s\"%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (*(argv[0]) == 'c')
+    {
+      rfapiPrintMatchingDescriptors (vty, NULL, &pfx);
+    }
+  else
+    {
+      rfapiPrintMatchingDescriptors (vty, &pfx, NULL);
+    }
+  return CMD_SUCCESS;
+}
+
+/*
+ * Note: this function does not flush vty output, so if it is called
+ * with a stream pointing to a vty, the user will have to type something
+ * before the callback output shows up
+ */
+static void
+test_nexthops_callback (
+//    struct rfapi_ip_addr        *target,
+                         struct rfapi_next_hop_entry *next_hops,
+                         void *userdata)
+{
+  void *stream = userdata;
+
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  fp (out, "Nexthops Callback, Target=(");
+  //rfapiPrintRfapiIpAddr(stream, target);
+  fp (out, ")%s", VTY_NEWLINE);
+
+  rfapiPrintNhl (stream, next_hops);
+
+  rfapi_free_next_hop_list (next_hops);
+}
+
+DEFUN (debug_rfapi_open,
+       debug_rfapi_open_cmd,
+       "debug rfapi-dev open vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X)",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       "rfapi_open\n"
+       "indicate vn addr follows\n"
+       "virtual network interface address\n"
+       "indicate xt addr follows\n" "underlay network interface address\n")
+{
+  struct rfapi_ip_addr vn;
+  struct rfapi_ip_addr un;
+  uint32_t lifetime;
+  int rc;
+  rfapi_handle handle;
+
+  /*
+   * Get VN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn)))
+    return rc;
+
+  /*
+   * Get UN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un)))
+    return rc;
+
+  rc = rfapi_open (rfapi_get_rfp_start_val_by_bgp (bgp_get_default ()),
+                   &vn, &un, /*&uo */ NULL, &lifetime, NULL, &handle);
+
+  vty_out (vty, "rfapi_open: status %d, handle %p, lifetime %d%s",
+           rc, handle, lifetime, VTY_NEWLINE);
+
+  rc = rfapi_set_response_cb (handle, test_nexthops_callback);
+
+  vty_out (vty, "rfapi_set_response_cb: status %d%s", rc, VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (debug_rfapi_close_vn_un,
+       debug_rfapi_close_vn_un_cmd,
+       "debug rfapi-dev close vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X)",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       "rfapi_close\n"
+       "indicate vn addr follows\n"
+       "virtual network interface address\n"
+       "indicate xt addr follows\n" "underlay network interface address\n")
+{
+  struct rfapi_ip_addr vn;
+  struct rfapi_ip_addr un;
+  rfapi_handle handle;
+  int rc;
+
+  /*
+   * Get VN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn)))
+    return rc;
+
+
+  /*
+   * Get UN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un)))
+    return rc;
+
+
+  if (rfapi_find_handle_vty (vty, &vn, &un, &handle))
+    {
+      vty_out (vty, "can't locate handle matching vn=%s, un=%s%s",
+               argv[0], argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rc = rfapi_close (handle);
+
+  vty_out (vty, "rfapi_close(handle=%p): status %d%s", handle, rc,
+           VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_close_rfd,
+       debug_rfapi_close_rfd_cmd,
+       "debug rfapi-dev close rfd HANDLE",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       "rfapi_close\n"
+       "indicate handle follows\n" "rfapi handle in hexadecimal\n")
+{
+  rfapi_handle handle;
+  int rc;
+  char *endptr = NULL;
+
+#if (UINTPTR_MAX == ULONG_MAX)
+  handle = (void *) (uintptr_t) (strtoul (argv[0], &endptr, 16));
+#elif (UINTPTR_MAX == ULLONG_MAX)
+  handle = (rfapi_handle) (uintptr_t) (strtoull (argv[0], &endptr, 16));
+#else
+  /* give up */
+  assert (0);
+#endif
+
+  if (*endptr != '\0' || (uintptr_t) handle == UINTPTR_MAX)
+    {
+      vty_out (vty, "Invalid value: %s%s", argv[0], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rc = rfapi_close (handle);
+
+  vty_out (vty, "rfapi_close(handle=%p): status %d%s", handle, rc,
+           VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_register_vn_un,
+       debug_rfapi_register_vn_un_cmd,
+       "debug rfapi-dev register vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M) lifetime SECONDS",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       "rfapi_register\n"
+       "indicate vn addr follows\n"
+       "virtual network IPv4 interface address\n"
+       "virtual network IPv6 interface address\n"
+       "indicate un addr follows\n"
+       "underlay network IPv4 interface address\n"
+       "underlay network IPv6 interface address\n"
+       "indicate prefix follows\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n" "indicate lifetime follows\n" "lifetime\n")
+{
+  struct rfapi_ip_addr vn;
+  struct rfapi_ip_addr un;
+  rfapi_handle handle;
+  struct prefix pfx;
+  uint32_t lifetime;
+  struct rfapi_ip_prefix hpfx;
+  int rc;
+
+  /*
+   * Get VN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn)))
+    return rc;
+
+
+  /*
+   * Get UN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un)))
+    return rc;
+
+
+  if (rfapi_find_handle_vty (vty, &vn, &un, &handle))
+    {
+      vty_out (vty, "can't locate handle matching vn=%s, un=%s%s",
+               argv[0], argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /*
+   * Get prefix to advertise
+   */
+  if (!str2prefix (argv[2], &pfx))
+    {
+      vty_out (vty, "Malformed prefix \"%s\"%s", argv[2], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (pfx.family != AF_INET && pfx.family != AF_INET6)
+    {
+      vty_out (vty, "Bad family for prefix \"%s\"%s", argv[2], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  rfapiQprefix2Rprefix (&pfx, &hpfx);
+
+  if (!strcmp (argv[3], "infinite"))
+    {
+      lifetime = RFAPI_INFINITE_LIFETIME;
+    }
+  else
+    {
+      VTY_GET_INTEGER ("Lifetime", lifetime, argv[3]);
+    }
+
+
+  rc = rfapi_register (handle, &hpfx, lifetime, NULL, NULL, 0);
+  if (rc)
+    {
+      vty_out (vty, "rfapi_register failed with rc=%d (%s)%s", rc,
+               strerror (rc), VTY_NEWLINE);
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_register_vn_un_l2o,
+       debug_rfapi_register_vn_un_l2o_cmd,
+       "debug rfapi-dev register"
+       " vn (A.B.C.D|X:X::X:X)"
+       " un (A.B.C.D|X:X::X:X)"
+       " prefix (A.B.C.D/M|X:X::X:X/M)"
+       " lifetime SECONDS"
+       " macaddr YY:YY:YY:YY:YY:YY"
+       " lni <0-16777215>",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       "rfapi_register\n"
+       "indicate vn addr follows\n"
+       "virtual network IPv4 interface address\n"
+       "virtual network IPv6 interface address\n"
+       "indicate un addr follows\n"
+       "underlay network IPv4 interface address\n"
+       "underlay network IPv6 interface address\n"
+       "indicate prefix follows\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n" "indicate lifetime follows\n" "lifetime\n")
+{
+  struct rfapi_ip_addr vn;
+  struct rfapi_ip_addr un;
+  rfapi_handle handle;
+  struct prefix pfx;
+  uint32_t lifetime;
+  struct rfapi_ip_prefix hpfx;
+  int rc;
+  struct rfapi_vn_option optary[10];    /* XXX must be big enough */
+  struct rfapi_vn_option *opt = NULL;
+  int opt_next = 0;
+
+  /*
+   * Get VN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn)))
+    return rc;
+
+
+  /*
+   * Get UN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un)))
+    return rc;
+
+
+  if (rfapi_find_handle_vty (vty, &vn, &un, &handle))
+    {
+      vty_out (vty, "can't locate handle matching vn=%s, un=%s%s",
+               argv[0], argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /*
+   * Get prefix to advertise
+   */
+  if (!str2prefix (argv[2], &pfx))
+    {
+      vty_out (vty, "Malformed prefix \"%s\"%s", argv[2], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (pfx.family != AF_INET && pfx.family != AF_INET6)
+    {
+      vty_out (vty, "Bad family for prefix \"%s\"%s", argv[2], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  rfapiQprefix2Rprefix (&pfx, &hpfx);
+
+  if (!strcmp (argv[3], "infinite"))
+    {
+      lifetime = RFAPI_INFINITE_LIFETIME;
+    }
+  else
+    {
+      VTY_GET_INTEGER ("Lifetime", lifetime, argv[3]);
+    }
+
+  /* L2 option parsing START */
+  memset (optary, 0, sizeof (optary));
+  VTY_GET_INTEGER ("Logical Network ID",
+                   optary[opt_next].v.l2addr.logical_net_id, argv[5]);
+  if ((rc = rfapiStr2EthAddr (argv[4], &optary[opt_next].v.l2addr.macaddr)))
+    {
+      vty_out (vty, "Bad mac address \"%s\"%s", argv[4], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+  if (opt_next)
+    {
+      optary[opt_next - 1].next = optary + opt_next;
+    }
+  else
+    {
+      opt = optary;
+    }
+  ++opt_next;
+  /* L2 option parsing END */
+
+  /* TBD fixme */
+  rc = rfapi_register (handle, &hpfx, lifetime, NULL /* &uo */ , opt, 0);
+  if (rc)
+    {
+      vty_out (vty, "rfapi_register failed with rc=%d (%s)%s", rc,
+               strerror (rc), VTY_NEWLINE);
+    }
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (debug_rfapi_unregister_vn_un,
+       debug_rfapi_unregister_vn_un_cmd,
+       "debug rfapi-dev unregister vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M)",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       "rfapi_register\n"
+       "indicate vn addr follows\n"
+       "virtual network interface address\n"
+       "indicate xt addr follows\n"
+       "underlay network interface address\n"
+       "indicate prefix follows\n" "prefix")
+{
+  struct rfapi_ip_addr vn;
+  struct rfapi_ip_addr un;
+  rfapi_handle handle;
+  struct prefix pfx;
+  struct rfapi_ip_prefix hpfx;
+  int rc;
+
+  /*
+   * Get VN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn)))
+    return rc;
+
+
+  /*
+   * Get UN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un)))
+    return rc;
+
+
+  if (rfapi_find_handle_vty (vty, &vn, &un, &handle))
+    {
+      vty_out (vty, "can't locate handle matching vn=%s, un=%s%s",
+               argv[0], argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /*
+   * Get prefix to advertise
+   */
+  if (!str2prefix (argv[2], &pfx))
+    {
+      vty_out (vty, "Malformed prefix \"%s\"%s", argv[2], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (pfx.family != AF_INET && pfx.family != AF_INET6)
+    {
+      vty_out (vty, "Bad family for prefix \"%s\"%s", argv[2], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  rfapiQprefix2Rprefix (&pfx, &hpfx);
+
+  rfapi_register (handle, &hpfx, 0, NULL, NULL, 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_query_vn_un,
+       debug_rfapi_query_vn_un_cmd,
+       "debug rfapi-dev query vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) target (A.B.C.D|X:X::X:X)",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       "rfapi_query\n"
+       "indicate vn addr follows\n"
+       "virtual network interface address\n"
+       "indicate xt addr follows\n"
+       "underlay network interface address\n"
+       "indicate target follows\n" "target\n")
+{
+  struct rfapi_ip_addr vn;
+  struct rfapi_ip_addr un;
+  struct rfapi_ip_addr target;
+  rfapi_handle handle;
+  int rc;
+  struct rfapi_next_hop_entry *pNextHopEntry;
+
+  /*
+   * Get VN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn)))
+    return rc;
+
+
+  /*
+   * Get UN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un)))
+    return rc;
+
+
+  /*
+   * Get target addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[2], &target)))
+    return rc;
+
+
+  if (rfapi_find_handle_vty (vty, &vn, &un, &handle))
+    {
+      vty_out (vty, "can't locate handle matching vn=%s, un=%s%s",
+               argv[0], argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /*
+   * options parameter not used? Set to NULL for now
+   */
+  rc = rfapi_query (handle, &target, NULL, &pNextHopEntry);
+
+  if (rc)
+    {
+      vty_out (vty, "rfapi_query failed with rc=%d (%s)%s", rc,
+               strerror (rc), VTY_NEWLINE);
+    }
+  else
+    {
+      /*
+       * print nexthop list
+       */
+      test_nexthops_callback ( /*&target, */ pNextHopEntry, vty);       /* frees nh list! */
+    }
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (debug_rfapi_query_vn_un_l2o,
+       debug_rfapi_query_vn_un_l2o_cmd,
+       "debug rfapi-dev query vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lni LNI target YY:YY:YY:YY:YY:YY",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       "rfapi_query\n"
+       "indicate vn addr follows\n"
+       "virtual network interface address\n"
+       "indicate xt addr follows\n"
+       "underlay network interface address\n"
+       "logical network ID follows\n"
+       "logical network ID\n"
+       "indicate target MAC addr follows\n" "target MAC addr\n")
+{
+  struct rfapi_ip_addr vn;
+  struct rfapi_ip_addr un;
+  struct rfapi_ip_addr target;
+  rfapi_handle handle;
+  int rc;
+  struct rfapi_next_hop_entry *pNextHopEntry;
+  struct rfapi_l2address_option l2o_buf;
+  struct bgp_tea_options hopt;
+  uint8_t valbuf[14];
+
+  /*
+   * Get VN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn)))
+    return rc;
+
+
+  /*
+   * Get UN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un)))
+    return rc;
+
+
+  /*
+   * Get target addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[2], &target)))
+    return rc;
+
+
+  if (rfapi_find_handle_vty (vty, &vn, &un, &handle))
+    {
+      vty_out (vty, "can't locate handle matching vn=%s, un=%s%s",
+               argv[0], argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /*
+   * Set up L2 parameters
+   */
+  memset (&l2o_buf, 0, sizeof (l2o_buf));
+  if (rfapiStr2EthAddr (argv[3], &l2o_buf.macaddr))
+    {
+      vty_out (vty, "Bad mac address \"%s\"%s", argv[3], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  VTY_GET_INTEGER ("Logical Network ID", l2o_buf.logical_net_id, argv[2]);
+
+  /* construct option chain */
+
+  memset (valbuf, 0, sizeof (valbuf));
+  memcpy (valbuf, &l2o_buf.macaddr.octet, ETHER_ADDR_LEN);
+  valbuf[11] = (l2o_buf.logical_net_id >> 16) & 0xff;
+  valbuf[12] = (l2o_buf.logical_net_id >> 8) & 0xff;
+  valbuf[13] = l2o_buf.logical_net_id & 0xff;
+
+  memset (&hopt, 0, sizeof (hopt));
+  hopt.options_count = 1;
+  hopt.options_length = sizeof (valbuf);        /* is this right? */
+  hopt.type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+  hopt.length = sizeof (valbuf);
+  hopt.value = valbuf;
+
+
+  /*
+   * options parameter not used? Set to NULL for now
+   */
+  rc = rfapi_query (handle, &target, &l2o_buf, &pNextHopEntry);
+
+  if (rc)
+    {
+      vty_out (vty, "rfapi_query failed with rc=%d (%s)%s", rc,
+               strerror (rc), VTY_NEWLINE);
+    }
+  else
+    {
+      /*
+       * print nexthop list
+       */
+      /* TBD enhance to print L2 information */
+      test_nexthops_callback ( /*&target, */ pNextHopEntry, vty);       /* frees nh list! */
+    }
+
+  return CMD_SUCCESS;
+}
+
+
+DEFUN (debug_rfapi_query_done_vn_un,
+       debug_rfapi_query_vn_un_done_cmd,
+       "debug rfapi-dev query done vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) target (A.B.C.D|X:X::X:X)",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       "rfapi_query_done\n"
+       "indicate vn addr follows\n"
+       "virtual network interface address\n"
+       "indicate xt addr follows\n"
+       "underlay network interface address\n"
+       "indicate prefix follows\n" "prefix\n")
+{
+  struct rfapi_ip_addr vn;
+  struct rfapi_ip_addr un;
+  struct rfapi_ip_addr target;
+  rfapi_handle handle;
+  int rc;
+
+  /*
+   * Get VN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn)))
+    return rc;
+
+
+  /*
+   * Get UN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un)))
+    return rc;
+
+
+  /*
+   * Get target addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[2], &target)))
+    return rc;
+
+
+  if (rfapi_find_handle_vty (vty, &vn, &un, &handle))
+    {
+      vty_out (vty, "can't locate handle matching vn=%s, un=%s%s",
+               argv[0], argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /*
+   * options parameter not used? Set to NULL for now
+   */
+  rc = rfapi_query_done (handle, &target);
+
+  vty_out (vty, "rfapi_query_done returned %d%s", rc, VTY_NEWLINE);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_show_import,
+       debug_rfapi_show_import_cmd,
+       "debug rfapi-dev show import",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       SHOW_STR
+       "import\n")
+{
+  struct bgp *bgp;
+  struct rfapi *h;
+  struct rfapi_import_table *it;
+  char *s;
+  int first_l2 = 1;
+
+  /*
+   * Show all import tables
+   */
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP instance%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  h = bgp->rfapi;
+  if (!h)
+    {
+      vty_out (vty, "No RFAPI instance%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /*
+   * Iterate over all import tables; do a filtered import
+   * for the afi/safi combination
+   */
+
+
+  for (it = h->imports; it; it = it->next)
+    {
+      s = ecommunity_ecom2str (it->rt_import_list,
+                               ECOMMUNITY_FORMAT_ROUTE_MAP);
+      vty_out (vty, "Import Table %p, RTs: %s%s", it, s, VTY_NEWLINE);
+      XFREE (MTYPE_ECOMMUNITY_STR, s);
+
+      rfapiShowImportTable (vty, "IP VPN", it->imported_vpn[AFI_IP], 1);
+      rfapiShowImportTable (vty, "IP ENCAP", it->imported_encap[AFI_IP], 0);
+      rfapiShowImportTable (vty, "IP6 VPN", it->imported_vpn[AFI_IP6], 1);
+      rfapiShowImportTable (vty, "IP6 ENCAP", it->imported_encap[AFI_IP6], 0);
+    }
+
+  if (h->import_mac)
+    {
+      void *cursor = NULL;
+      uint32_t lni;
+      uintptr_t lni_as_ptr;
+      int rc;
+      char buf[BUFSIZ];
+
+      for (rc =
+           skiplist_next (h->import_mac, (void **) &lni_as_ptr, (void **) &it,
+                          &cursor); !rc;
+           rc =
+           skiplist_next (h->import_mac, (void **) &lni_as_ptr, (void **) &it,
+                          &cursor))
+        {
+
+          if (it->imported_vpn[AFI_ETHER])
+            {
+              lni = lni_as_ptr;
+              if (first_l2)
+                {
+                  vty_out (vty, "%sLNI-based Ethernet Tables:%s",
+                           VTY_NEWLINE, VTY_NEWLINE);
+                  first_l2 = 0;
+                }
+              snprintf (buf, BUFSIZ, "L2VPN LNI=%u", lni);
+              rfapiShowImportTable (vty, buf, it->imported_vpn[AFI_ETHER], 1);
+            }
+        }
+    }
+
+  rfapiShowImportTable (vty, "CE IT - IP VPN",
+                        h->it_ce->imported_vpn[AFI_IP], 1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_show_import_vn_un,
+       debug_rfapi_show_import_vn_un_cmd,
+       "debug rfapi-dev show import vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X)",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       SHOW_STR
+       "import\n"
+       "indicate vn addr follows\n"
+       "virtual network interface address\n"
+       "indicate xt addr follows\n" "underlay network interface address\n")
+{
+  struct rfapi_ip_addr vn;
+  struct rfapi_ip_addr un;
+  rfapi_handle handle;
+  int rc;
+  struct rfapi_descriptor *rfd;
+
+  /*
+   * Get VN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn)))
+    return rc;
+
+
+  /*
+   * Get UN addr
+   */
+  if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un)))
+    return rc;
+
+
+  if (rfapi_find_handle_vty (vty, &vn, &un, &handle))
+    {
+      vty_out (vty, "can't locate handle matching vn=%s, un=%s%s",
+               argv[0], argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  rfd = (struct rfapi_descriptor *) handle;
+
+  rfapiShowImportTable (vty, "IP VPN",
+                        rfd->import_table->imported_vpn[AFI_IP], 1);
+  rfapiShowImportTable (vty, "IP ENCAP",
+                        rfd->import_table->imported_encap[AFI_IP], 0);
+  rfapiShowImportTable (vty, "IP6 VPN",
+                        rfd->import_table->imported_vpn[AFI_IP6], 1);
+  rfapiShowImportTable (vty, "IP6 ENCAP",
+                        rfd->import_table->imported_encap[AFI_IP6], 0);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_response_omit_self,
+       debug_rfapi_response_omit_self_cmd,
+       "debug rfapi-dev response-omit-self (on|off)",
+       DEBUG_STR
+       DEBUG_RFAPI_STR
+       "Omit self in RFP responses\n"
+       "filter out self from responses\n" "leave self in responses\n")
+{
+  struct bgp *bgp = bgp_get_default ();
+
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (!bgp->rfapi_cfg)
+    {
+      vty_out (vty, "VNC not configured%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!strcmp (argv[0], "on"))
+    SET_FLAG (bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP);
+  else
+    UNSET_FLAG (bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP);
+
+  return CMD_SUCCESS;
+}
+
+
+#ifdef RFAPI_DEBUG_SKIPLIST_CLI
+
+#include "skiplist.h"
+DEFUN (skiplist_test_cli,
+       skiplist_test_cli_cmd,
+       "skiplist test",
+       "skiplist command\n"
+       "test\n")
+{
+  skiplist_test (vty);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (skiplist_debug_cli,
+       skiplist_debug_cli_cmd,
+       "skiplist debug",
+       "skiplist command\n"
+       "debug\n")
+{
+  skiplist_debug (vty, NULL);
+  return CMD_SUCCESS;
+}
+
+#endif /* RFAPI_DEBUG_SKIPLIST_CLI */
+
+void
+rfapi_init (void)
+{
+  bgp_rfapi_cfg_init ();
+  vnc_debug_init();
+
+  install_element (ENABLE_NODE, &debug_rfapi_show_import_cmd);
+  install_element (ENABLE_NODE, &debug_rfapi_show_import_vn_un_cmd);
+
+  install_element (ENABLE_NODE, &debug_rfapi_open_cmd);
+  install_element (ENABLE_NODE, &debug_rfapi_close_vn_un_cmd);
+  install_element (ENABLE_NODE, &debug_rfapi_close_rfd_cmd);
+  install_element (ENABLE_NODE, &debug_rfapi_register_vn_un_cmd);
+  install_element (ENABLE_NODE, &debug_rfapi_unregister_vn_un_cmd);
+  install_element (ENABLE_NODE, &debug_rfapi_query_vn_un_cmd);
+  install_element (ENABLE_NODE, &debug_rfapi_query_vn_un_done_cmd);
+  install_element (ENABLE_NODE, &debug_rfapi_query_vn_un_l2o_cmd);
+
+  install_element (ENABLE_NODE, &debug_rfapi_response_omit_self_cmd);
+
+  /* Need the following show commands for gpz test scripts */
+  install_element (ENABLE_NODE, &debug_rfapi_show_nves_cmd);
+  install_element (ENABLE_NODE, &debug_rfapi_show_nves_vn_un_cmd);
+  install_element (ENABLE_NODE, &debug_rfapi_register_vn_un_l2o_cmd);
+
+#ifdef RFAPI_DEBUG_SKIPLIST_CLI
+  install_element (ENABLE_NODE, &skiplist_test_cli_cmd);
+  install_element (ENABLE_NODE, &skiplist_debug_cli_cmd);
+#endif
+
+  rfapi_vty_init ();
+}
+
+#ifdef DEBUG_RFAPI
+static void
+rfapi_print_exported (struct bgp *bgp)
+{
+  struct bgp_node *rdn;
+  struct bgp_node *rn;
+  struct bgp_info *bi;
+
+  if (!bgp)
+    return;
+
+  for (rdn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rdn;
+       rdn = bgp_route_next (rdn))
+    {
+      if (!rdn->info)
+        continue;
+      fprintf (stderr, "%s: vpn rdn=%p\n", __func__, rdn);
+      for (rn = bgp_table_top (rdn->info); rn; rn = bgp_route_next (rn))
+        {
+          if (!rn->info)
+            continue;
+          fprintf (stderr, "%s: rn=%p\n", __func__, rn);
+          for (bi = rn->info; bi; bi = bi->next)
+            {
+              rfapiPrintBi ((void *) 2, bi);    /* 2 => stderr */
+            }
+        }
+    }
+  for (rdn = bgp_table_top (bgp->rib[AFI_IP][SAFI_ENCAP]); rdn;
+       rdn = bgp_route_next (rdn))
+    {
+      if (!rdn->info)
+        continue;
+      fprintf (stderr, "%s: encap rdn=%p\n", __func__, rdn);
+      for (rn = bgp_table_top (rdn->info); rn; rn = bgp_route_next (rn))
+        {
+          if (!rn->info)
+            continue;
+          fprintf (stderr, "%s: rn=%p\n", __func__, rn);
+          for (bi = rn->info; bi; bi = bi->next)
+            {
+              rfapiPrintBi ((void *) 2, bi);    /* 2 => stderr */
+            }
+        }
+    }
+
+}
+#endif /* defined(DEBUG_RFAPI) */
+
+/*
+ * Free all memory to prepare for clean exit as seen by valgrind memcheck
+ */
+void
+rfapi_delete (struct bgp *bgp)
+{
+  extern void rfp_clear_vnc_nve_all (void);     /* can't fix correctly yet */
+
+  /*
+   * This clears queries and registered routes, and closes nves
+   */
+  if (bgp->rfapi)
+    rfp_clear_vnc_nve_all ();
+  bgp_rfapi_cfg_destroy (bgp, bgp->rfapi_cfg);
+  bgp->rfapi_cfg = NULL;
+  bgp_rfapi_destroy (bgp, bgp->rfapi);
+  bgp->rfapi = NULL;
+#ifdef DEBUG_RFAPI
+  /*
+   * show what's left in the BGP MPLSVPN RIB
+   */
+  rfapi_print_exported (bgp);
+#endif
+
+}
+
+int
+rfapi_set_autord_from_vn (struct prefix_rd *rd, struct rfapi_ip_addr *vn)
+{
+  zlog_debug ("%s: auto-assigning RD", __func__);
+  if (vn->addr_family != AF_INET
+      && vn->addr_family != AF_INET6)
+    {
+      zlog_debug ("%s: can't auto-assign RD, VN addr family is not IPv4"
+                  "|v6"
+                  , __func__);
+      return EAFNOSUPPORT;
+    }
+  rd->family = AF_UNSPEC;
+  rd->prefixlen = 64;
+  rd->val[1] = RD_TYPE_IP;
+  if (vn->addr_family == AF_INET)
+    {
+      memcpy (rd->val + 2, &vn->addr.v4.s_addr, 4);
+    }
+  else
+    {                           /* is v6 */
+      memcpy (rd->val + 2, &vn->addr.v6.s6_addr32[3], 4);/* low order 4 bytes */
+    }
+  {
+    char buf[BUFSIZ];
+    buf[0] = 0;
+    prefix_rd2str (rd, buf, BUFSIZ);
+    buf[BUFSIZ - 1] = 0;
+    zlog_debug ("%s: auto-RD is set to %s", __func__, buf);
+  }
+  return 0;
+}
+
+/*------------------------------------------
+ * rfapi_bgp_lookup_by_rfp
+ *
+ * Find bgp instance pointer based on value returned by rfp_start
+ *
+ * input: 
+ *      rfp_start_val     value returned by rfp_startor
+ *                        NULL (=get default instance)
+ *
+ * output:
+ *     none
+ *
+ * return value:
+ *     bgp             bgp instance pointer
+ *      NULL = not found
+ *
+ --------------------------------------------*/
+struct bgp *
+rfapi_bgp_lookup_by_rfp (void *rfp_start_val)
+{
+  struct bgp *bgp = NULL;
+  struct listnode *node, *nnode;
+
+  if (rfp_start_val == NULL)
+    bgp = bgp_get_default ();
+  else
+    for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp))
+      if (bgp->rfapi != NULL && bgp->rfapi->rfp == rfp_start_val)
+        return bgp;
+  return bgp;
+}
+
+/*------------------------------------------
+ * rfapi_get_rfp_start_val_by_bgp
+ *
+ * Find bgp instance pointer based on value returned by rfp_start
+ *
+ * input: 
+ *     bgp             bgp instance pointer
+ *
+ * output:
+ *     none
+ *
+ * return value:
+ *     rfp_start_val
+ *      NULL = not found
+ *
+ --------------------------------------------*/
+void *
+rfapi_get_rfp_start_val_by_bgp (struct bgp *bgp)
+{
+  if (!bgp || !bgp->rfapi)
+    return NULL;
+  return bgp->rfapi->rfp;
+}
+
+/***********************************************************************
+ *              RFP group specific configuration
+ ***********************************************************************/
+static void *
+rfapi_rfp_get_or_init_group_config_default (
+  struct rfapi_cfg     *rfc,
+  struct vty           *vty,
+  uint32_t             size)
+{
+  if (rfc->default_rfp_cfg == NULL && size > 0)
+    {
+      rfc->default_rfp_cfg = XCALLOC (MTYPE_RFAPI_RFP_GROUP_CFG, size);
+      zlog_debug ("%s: allocated, size=%d", __func__, size);
+
+    }
+  return rfc->default_rfp_cfg;
+}
+
+static void *
+rfapi_rfp_get_or_init_group_config_nve (
+  struct rfapi_cfg     *rfc,
+  struct vty           *vty,
+  uint32_t             size)
+{
+  struct rfapi_nve_group_cfg *rfg = vty->index_sub;
+
+  /* make sure group is still in list */
+  if (!listnode_lookup (rfc->nve_groups_sequential, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE);
+      return NULL;
+    }
+
+  if (rfg->rfp_cfg == NULL && size > 0)
+    {
+      rfg->rfp_cfg = XCALLOC (MTYPE_RFAPI_RFP_GROUP_CFG, size);
+      zlog_debug ("%s: allocated, size=%d", __func__, size);
+
+    }
+  return rfg->rfp_cfg;
+}
+
+static void *
+rfapi_rfp_get_or_init_group_config_l2 (
+  struct rfapi_cfg     *rfc,
+  struct vty           *vty,
+  uint32_t             size)
+{
+  struct rfapi_l2_group_cfg *rfg = vty->index_sub;
+
+  /* make sure group is still in list */
+  if (!listnode_lookup (rfc->l2_groups, rfg))
+    {
+      /* Not in list anymore */
+      vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE);
+      return NULL;
+    }
+  if (rfg->rfp_cfg == NULL && size > 0)
+    {
+      rfg->rfp_cfg = XCALLOC (MTYPE_RFAPI_RFP_GROUP_CFG, size);
+      zlog_debug ("%s: allocated, size=%d", __func__, size);
+
+    }
+  return rfg->rfp_cfg;
+}
+
+/*------------------------------------------
+ * rfapi_rfp_init_group_config_ptr_vty
+ *
+ * This is used to init or return a previously init'ed group specific
+ * configuration pointer. Group is identified by vty context. 
+ * NOTE: size is ignored when a previously init'ed value is returned.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    type              group type
+ *    vty               quagga vty context
+ *    size              number of bytes to allocation
+ *
+ * output:
+ *    none
+ *
+ * return value: 
+ *    rfp_cfg_group     NULL or Pointer to configuration structure
+--------------------------------------------*/
+void *
+rfapi_rfp_init_group_config_ptr_vty (
+  void                         *rfp_start_val,
+  rfapi_rfp_cfg_group_type     type,
+  struct vty                   *vty,
+  uint32_t                     size)
+{
+  struct bgp *bgp;
+  void *ret = NULL;
+
+  if (rfp_start_val == NULL || vty == NULL)
+    return NULL;
+
+  bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val);
+  if (!bgp || !bgp->rfapi_cfg || !vty->index_sub)
+    return NULL;
+
+  switch (type)
+    {
+    case RFAPI_RFP_CFG_GROUP_DEFAULT:
+      ret = rfapi_rfp_get_or_init_group_config_default (bgp->rfapi_cfg,
+                                                        vty, size);
+      break;
+    case RFAPI_RFP_CFG_GROUP_NVE:
+      ret = rfapi_rfp_get_or_init_group_config_nve (bgp->rfapi_cfg,
+                                                    vty, size);
+      break;
+    case RFAPI_RFP_CFG_GROUP_L2:
+      ret = rfapi_rfp_get_or_init_group_config_l2 (bgp->rfapi_cfg, vty, size);
+      break;
+    default:
+      zlog_err ("%s: Unknown group type=%d", __func__, type);
+      /* should never happen */
+      assert ("Unknown type" == NULL);
+      break;
+    }
+  return ret;
+}
+
+/*------------------------------------------
+ * rfapi_rfp_get_group_config_ptr_vty
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and vty context.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown. 
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    type              group type
+ *    vty               quagga vty context
+ *
+ * output:
+ *    none
+ *
+ * return value:
+ *    rfp_cfg_group     Pointer to configuration structure 
+--------------------------------------------*/
+void *
+rfapi_rfp_get_group_config_ptr_vty (
+  void                         *rfp_start_val,
+  rfapi_rfp_cfg_group_type     type,
+  struct vty                   *vty)
+{
+  return rfapi_rfp_init_group_config_ptr_vty (rfp_start_val, type, vty, 0);
+}
+
+static void *
+rfapi_rfp_get_group_config_name_nve (
+  struct rfapi_cfg             *rfc,
+  const char                   *name,
+  void                         *criteria,
+  rfp_group_config_search_cb_t *search_cb)
+{
+  struct rfapi_nve_group_cfg *rfg;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (rfc->nve_groups_sequential, node, rfg))
+    {
+      if (!strcmp (rfg->name, name) &&  /* name match */
+          (search_cb == NULL || !search_cb (criteria, rfg->rfp_cfg)))
+        return rfg->rfp_cfg;
+    }
+  return NULL;
+}
+
+static void *
+rfapi_rfp_get_group_config_name_l2 (
+  struct rfapi_cfg             *rfc,
+  const char                   *name,
+  void                         *criteria,
+  rfp_group_config_search_cb_t *search_cb)
+{
+  struct rfapi_l2_group_cfg *rfg;
+  struct listnode *node;
+
+  for (ALL_LIST_ELEMENTS_RO (rfc->l2_groups, node, rfg))
+    {
+      if (!strcmp (rfg->name, name) &&  /* name match */
+          (search_cb == NULL || !search_cb (criteria, rfg->rfp_cfg)))
+        return rfg->rfp_cfg;
+    }
+  return NULL;
+}
+
+/*------------------------------------------
+ * rfapi_rfp_get_group_config_ptr_name
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and name context. 
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    type              group type
+ *    name              group name
+ *    criteria          RFAPI caller provided serach criteria
+ *    search_cb         optional rfp_group_config_search_cb_t
+ *
+ * output:
+ *    none
+ *
+ * return value:
+ *    rfp_cfg_group     Pointer to configuration structure 
+--------------------------------------------*/
+void *
+rfapi_rfp_get_group_config_ptr_name (
+  void                         *rfp_start_val,
+  rfapi_rfp_cfg_group_type     type,
+  const char                   *name,
+  void                         *criteria,
+  rfp_group_config_search_cb_t *search_cb)
+{
+  struct bgp *bgp;
+  void *ret = NULL;
+
+  if (rfp_start_val == NULL || name == NULL)
+    return NULL;
+
+  bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val);
+  if (!bgp || !bgp->rfapi_cfg)
+    return NULL;
+
+  switch (type)
+    {
+    case RFAPI_RFP_CFG_GROUP_DEFAULT:
+      ret = bgp->rfapi_cfg->default_rfp_cfg;
+      break;
+    case RFAPI_RFP_CFG_GROUP_NVE:
+      ret = rfapi_rfp_get_group_config_name_nve (bgp->rfapi_cfg,
+                                                 name, criteria, search_cb);
+      break;
+    case RFAPI_RFP_CFG_GROUP_L2:
+      ret = rfapi_rfp_get_group_config_name_l2 (bgp->rfapi_cfg,
+                                                name, criteria, search_cb);
+      break;
+    default:
+      zlog_err ("%s: Unknown group type=%d", __func__, type);
+      /* should never happen */
+      assert ("Unknown type" == NULL);
+      break;
+    }
+  return ret;
+}
+
+/*------------------------------------------
+ * rfapi_rfp_get_l2_group_config_ptr_lni
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and logical network identifier.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    type              group type
+ *    logical_net_id    group logical network identifier
+ *    criteria          RFAPI caller provided serach criteria
+ *    search_cb         optional rfp_group_config_search_cb_t
+ *
+ * output:
+ *    none
+ *
+ * return value:
+ *    rfp_cfg_group     Pointer to configuration structure 
+--------------------------------------------*/
+void *
+rfapi_rfp_get_l2_group_config_ptr_lni (
+  void                         *rfp_start_val,
+  uint32_t                     logical_net_id,
+  void                         *criteria,
+  rfp_group_config_search_cb_t *search_cb)
+{
+  struct bgp *bgp;
+  struct rfapi_l2_group_cfg *rfg;
+  struct listnode *node;
+
+  if (rfp_start_val == NULL)
+    return NULL;
+
+  bgp = rfapi_bgp_lookup_by_rfp (rfp_start_val);
+  if (!bgp || !bgp->rfapi_cfg)
+    return NULL;
+
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->l2_groups, node, rfg))
+    {
+      if (rfg->logical_net_id == logical_net_id &&
+          (search_cb == NULL || !search_cb (criteria, rfg->rfp_cfg)))
+        {
+          if (rfg->rfp_cfg == NULL)
+            zlog_debug ("%s: returning rfp group config for lni=0", __func__);
+          return rfg->rfp_cfg;
+        }
+    }
+  return NULL;
+}
diff --git a/bgpd/rfapi/rfapi.h b/bgpd/rfapi/rfapi.h
new file mode 100644 (file)
index 0000000..6e1baa6
--- /dev/null
@@ -0,0 +1,980 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_H
+#define _QUAGGA_BGP_RFAPI_H
+
+#if ENABLE_BGP_VNC
+
+#include <stdint.h>
+#include <netinet/in.h>
+#include <zebra.h>
+#include "vty.h"
+#include "prefix.h"
+#include "../bgpd.h"
+#include "../bgp_encap_types.h"
+
+/* probably ought to have a field-specific define in config.h */
+# ifndef s6_addr32              /* for solaris/bsd */
+#  ifdef SOLARIS_IPV6
+#   define     s6_addr32       _S6_un._S6_u32
+#  else
+#   define     s6_addr32       __u6_addr.__u6_addr32
+#  endif
+# endif
+
+#define RFAPI_V4_ADDR 0x04
+#define RFAPI_V6_ADDR 0x06
+#define RFAPI_SHOW_STR "VNC information\n"
+
+struct rfapi_ip_addr
+{
+  uint8_t addr_family;          /* AF_INET | AF_INET6 */
+  union
+  {
+    struct in_addr v4;          /* in network order */
+    struct in6_addr v6;         /* in network order */
+  } addr;
+};
+
+struct rfapi_ip_prefix
+{
+  uint8_t length;
+  uint8_t cost;                 /* bgp local pref = 255 - cost */
+  struct rfapi_ip_addr prefix;
+};
+
+struct rfapi_nexthop
+{
+  struct prefix addr;
+  uint8_t cost;
+};
+
+struct rfapi_next_hop_entry
+{
+  struct rfapi_next_hop_entry  *next;
+  struct rfapi_ip_prefix       prefix;
+  uint32_t                     lifetime;
+  struct rfapi_ip_addr         un_address;
+  struct rfapi_ip_addr         vn_address;
+  struct rfapi_vn_option       *vn_options;
+  struct rfapi_un_option       *un_options;
+};
+
+#define RFAPI_REMOVE_RESPONSE_LIFETIME  0
+#define RFAPI_INFINITE_LIFETIME         0xFFFFFFFF
+
+struct rfapi_l2address_option
+{
+  struct ethaddr       macaddr;        /* use 0 to assign label to IP prefix */
+  uint32_t             label;          /* 20bit label in low bits, no TC, S, or TTL  */
+  uint32_t             logical_net_id; /* ~= EVPN Ethernet Segment Id,
+                                   must not be zero for mac regis. */
+  uint8_t              local_nve_id;
+};
+
+typedef enum
+{
+  RFAPI_UN_OPTION_TYPE_PROVISIONAL,     /* internal use only */
+  RFAPI_UN_OPTION_TYPE_TUNNELTYPE,
+} rfapi_un_option_type;
+
+struct rfapi_tunneltype_option
+{
+  bgp_encap_types type;
+  union
+  {
+    struct bgp_encap_type_reserved                     reserved;
+    struct bgp_encap_type_l2tpv3_over_ip               l2tpv3_ip;
+    struct bgp_encap_type_gre                          gre;
+    struct bgp_encap_type_transmit_tunnel_endpoint     transmit_tunnel_endpoint;
+    struct bgp_encap_type_ipsec_in_tunnel_mode         ipsec_tunnel;
+    struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode    ip_ipsec;
+    struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode  mpls_ipsec;
+    struct bgp_encap_type_ip_in_ip                     ip_ip;
+    struct bgp_encap_type_vxlan                                vxlan;
+    struct bgp_encap_type_nvgre                                nvgre;
+    struct bgp_encap_type_mpls                         mpls;
+    struct bgp_encap_type_mpls_in_gre                  mpls_gre;
+    struct bgp_encap_type_vxlan_gpe                    vxlan_gpe;
+    struct bgp_encap_type_mpls_in_udp                  mpls_udp;
+    struct bgp_encap_type_pbb                          pbb;
+  } bgpinfo;
+};
+
+struct rfapi_un_option
+{
+  struct rfapi_un_option               *next;
+  rfapi_un_option_type                 type;
+  union
+  {
+    struct rfapi_tunneltype_option     tunnel;
+  } v;
+};
+
+typedef enum
+{
+  RFAPI_VN_OPTION_TYPE_L2ADDR = 3,      /* Layer 2 address, 3 for legacy compatibility */
+  RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP,   /* for static routes */
+  RFAPI_VN_OPTION_TYPE_INTERNAL_RD,     /* internal use only */
+} rfapi_vn_option_type;
+
+struct rfapi_vn_option
+{
+  struct rfapi_vn_option               *next;
+
+  rfapi_vn_option_type                 type;
+
+  union
+  {
+    struct rfapi_l2address_option      l2addr;
+
+    /*
+     * If this option is present, the next hop is local to the
+     * client NVE (i.e., not via a tunnel).
+     */
+    struct rfapi_nexthop               local_nexthop;
+
+    /*
+     * For rfapi internal use only
+     */
+    struct prefix_rd                   internal_rd;
+  } v;
+};
+
+struct rfapi_l2address_option_match
+{
+  struct rfapi_l2address_option o;
+  uint32_t flags;
+
+#define RFAPI_L2O_MACADDR              0x00000001
+#define RFAPI_L2O_LABEL                        0x00000002
+#define RFAPI_L2O_LNI                  0x00000004
+#define RFAPI_L2O_LHI                  0x00000008
+};
+
+#define VNC_CONFIG_STR "VNC/RFP related configuration\n"
+
+typedef void *rfapi_handle;
+
+/***********************************************************************
+ *                     RFP Callbacks
+ ***********************************************************************/
+/*------------------------------------------
+ * rfapi_response_cb_t (callback typedef)
+ *
+ * Callbacks of this type are used to provide asynchronous 
+ * route updates from RFAPI to the RFP client.
+ *
+ * response_cb
+ *     called to notify the rfp client that a next hop list
+ *     that has previously been provided in response to an
+ *     rfapi_query call has been updated. Deleted routes are indicated
+ *     with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME.
+ *
+ *     By default, the routes an NVE receives via this callback include
+ *     its own routes (that it has registered). However, these may be
+ *     filtered out if the global BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP
+ *     flag is set.
+ *
+ * local_cb
+ *     called to notify the rfp client that a local route
+ *     has been added or deleted. Deleted routes are indicated
+ *     with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME.
+ *
+ * input: 
+ *     next_hops       a list of possible next hops.
+ *                     This is a linked list allocated within the
+ *                     rfapi. The response_cb callback function is responsible
+ *                     for freeing this memory via rfapi_free_next_hop_list()
+ *                     in order to avoid memory leaks.
+ *
+ *     userdata        value (cookie) originally specified in call to
+ *                     rfapi_open()
+ *
+ *------------------------------------------*/
+typedef void (rfapi_response_cb_t) (struct rfapi_next_hop_entry * next_hops,
+                                    void *userdata);
+
+/*------------------------------------------
+ * rfapi_nve_close_cb_t (callback typedef)
+ *
+ * Callbacks of this type are used to provide asynchronous 
+ * notification that an rfapi_handle was invalidated
+ *
+ * input: 
+ *     pHandle         Firmerly valid rfapi_handle returned to
+ *                     client via rfapi_open().
+ *
+ *     reason          EIDRM   handle administratively closed (clear nve ...)
+ *                     ESTALE  handle invalidated by configuration change
+ *
+ *------------------------------------------*/
+typedef void (rfapi_nve_close_cb_t) (rfapi_handle pHandle, int reason);
+
+/*------------------------------------------
+ * rfp_cfg_write_cb_t (callback typedef)
+ *
+ * This callback is used to generate output for any config parameters
+ * that may supported by RFP  via RFP defined vty commands at the bgp 
+ * level.  See loglevel as an example.
+ *
+ * input: 
+ *    vty           -- quagga vty context
+ *    rfp_start_val -- value returned by rfp_start
+ *
+ * output:
+ *    to vty, rfp related configuration
+ *
+ * return value: 
+ *    lines written
+--------------------------------------------*/
+typedef int (rfp_cfg_write_cb_t) (struct vty * vty, void *rfp_start_val);
+
+/*------------------------------------------
+ * rfp_cfg_group_write_cb_t (callback typedef)
+ *
+ * This callback is used to generate output for any config parameters
+ * that may supported by RFP via RFP defined vty commands at the 
+ * L2 or NVE level.  See loglevel as an example.
+ *
+ * input: 
+ *    vty              quagga vty context
+ *    rfp_start_val    value returned by rfp_start
+ *    type             group type
+ *    name             group name
+ *    rfp_cfg_group    Pointer to configuration structure 
+ *
+ * output:
+ *    to vty, rfp related configuration
+ *
+ * return value: 
+ *    lines written
+--------------------------------------------*/
+typedef enum
+{
+  RFAPI_RFP_CFG_GROUP_DEFAULT,
+  RFAPI_RFP_CFG_GROUP_NVE,
+  RFAPI_RFP_CFG_GROUP_L2
+} rfapi_rfp_cfg_group_type;
+
+typedef int (rfp_cfg_group_write_cb_t) (struct vty * vty,
+                                        void *rfp_start_val,
+                                        rfapi_rfp_cfg_group_type type,
+                                        const char *name,
+                                        void *rfp_cfg_group);
+
+/***********************************************************************
+ * Configuration related defines and structures
+ ***********************************************************************/
+
+struct rfapi_rfp_cb_methods
+{
+  rfp_cfg_write_cb_t *cfg_cb;   /* show top level config */
+  rfp_cfg_group_write_cb_t *cfg_group_cb;       /* show group level config */
+  rfapi_response_cb_t *response_cb;     /* unsolicited responses */
+  rfapi_response_cb_t *local_cb;        /* local route add/delete */
+  rfapi_nve_close_cb_t *close_cb;       /* handle closed */
+
+};
+
+/*
+ * If a route with infinite lifetime is withdrawn, this is
+ * how long (in seconds) to wait before expiring it (because
+ * RFAPI_LIFETIME_MULTIPLIER_PCT * infinity is too long to wait)
+ */
+#define RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY (60*120)
+
+/* 
+ * the factor that should be applied to a prefix's <lifetime> value
+ * before using it to expire a withdrawn prefix, expressed as a percent.
+ * Thus, a value of 100 means to use the exact value of <lifetime>,
+ * a value of 200 means to use twice the value of <lifetime>, etc.
+ */
+#define RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR  150
+
+/*
+ * This is used by rfapi to determine if RFP is using/supports 
+ * a partial (i.e., cache) or full table download approach for
+ * mapping information.  When  full table download approach is
+ * used all information is passed to RFP after an initial
+ * rfapi_query.  When partial table download is used, only 
+ * information matching a query is passed.  
+ */
+typedef enum
+{
+  RFAPI_RFP_DOWNLOAD_PARTIAL = 0,
+  RFAPI_RFP_DOWNLOAD_FULL
+} rfapi_rfp_download_type;
+
+#define RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL 1
+
+struct rfapi_rfp_cfg
+{
+  /* partial or full table download */
+  rfapi_rfp_download_type download_type;        /* default=partial */
+  /*
+   * When full-table-download is enabled, this is the minimum
+   * number of seconds between times a non-queried prefix will
+   * be updated to a particular NVE.
+   * default: RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL
+   */
+  uint32_t ftd_advertisement_interval;
+  /*
+   * percentage of registration lifetime to continue to use information
+   * post soft-state refresh timeout
+   default: RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR
+   */
+  uint32_t holddown_factor;
+  /* Control generation of updated RFP responses */
+  uint8_t use_updated_response; /* default=0/no */
+  /* when use_updated_response, also generate remove responses */
+  uint8_t use_removes;          /* default=0/no */
+};
+
+/***********************************************************************
+ * Process related functions -- MUST be provided by the RFAPI user <<=== 
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfp_start
+ *
+ * This function will start the RFP code
+ *
+ * input: 
+ *    master    quagga thread_master to tie into bgpd threads
+ * 
+ * output:
+ *    cfgp      Pointer to rfapi_rfp_cfg (null = use defaults), 
+ *              copied by caller, updated via rfp_set_configuration
+ *    cbmp      Pointer to rfapi_rfp_cb_methods, may be null
+ *              copied by caller, updated via rfapi_rfp_set_cb_methods
+ * return value: 
+ *    rfp_start_val rfp returned value passed on rfp_stop and other rfapi calls
+--------------------------------------------*/
+extern void *
+rfp_start (
+  struct thread_master         *master,
+  struct rfapi_rfp_cfg         **cfgp,
+  struct rfapi_rfp_cb_methods  **cbmp);
+
+/*------------------------------------------
+ * rfp_stop
+ *
+ * This function is called on shutdown to trigger RFP cleanup
+ *
+ * input: 
+ *    rfp_start_val
+ *
+ * output:
+ *    none
+ *
+ * return value: 
+--------------------------------------------*/
+extern void
+rfp_stop (void *rfp_start_val);
+
+/***********************************************************************
+ *              RFP processing behavior configuration
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_rfp_set_configuration
+ *
+ * This is used to change rfapi's processing behavior based on 
+ * RFP requirements. 
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    rfapi_rfp_cfg     Pointer to configuration structure
+ *
+ * output:
+ *    none
+ *
+ * return value: 
+ *     0               Success
+ *     ENXIO           Unabled to locate configured BGP/VNC
+--------------------------------------------*/
+extern int
+rfapi_rfp_set_configuration (
+  void                 *rfp_start_val,
+  struct rfapi_rfp_cfg *rfp_cfg);
+
+/*------------------------------------------
+ * rfapi_rfp_set_cb_methods
+ *
+ * Change registered callback functions for asynchronous notifications 
+ * from RFAPI to the RFP client.
+ *
+ * input: 
+ *    rfp_start_val     value by rfp_start
+ *    methods          Pointer to struct rfapi_rfp_cb_methods containing
+ *                     pointers to callback methods as described above
+ *
+ * return value: 
+ *     0               Success
+ *     ENXIO           BGP or VNC not configured
+ *------------------------------------------*/
+extern int
+rfapi_rfp_set_cb_methods (
+  void                         *rfp_start_val,
+  struct rfapi_rfp_cb_methods  *methods);
+
+/***********************************************************************
+ *              RFP group specific configuration
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_rfp_init_group_config_ptr_vty
+ *
+ * This is used to init or return a previously init'ed group specific
+ * configuration pointer. Group is identified by vty context. 
+ * NOTE: size is ignored when a previously init'ed value is returned.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    type              group type
+ *    vty               quagga vty context
+ *    size              number of bytes to allocation
+ *
+ * output:
+ *    none
+ *
+ * return value: 
+ *    rfp_cfg_group     NULL or Pointer to configuration structure
+--------------------------------------------*/
+extern void *
+rfapi_rfp_init_group_config_ptr_vty (
+  void                         *rfp_start_val,
+  rfapi_rfp_cfg_group_type     type,
+  struct vty                   *vty,
+  uint32_t                     size);
+
+/*------------------------------------------
+ * rfapi_rfp_get_group_config_ptr_vty
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and vty context. 
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    type              group type
+ *    vty               quagga vty context
+ *
+ * output:
+ *    none
+ *
+ * return value:
+ *    rfp_cfg_group     Pointer to configuration structure 
+--------------------------------------------*/
+extern void *
+rfapi_rfp_get_group_config_ptr_vty (
+  void                         *rfp_start_val,
+  rfapi_rfp_cfg_group_type     type,
+  struct vty                   *vty);
+
+/*------------------------------------------
+ * rfp_group_config_search_cb_t (callback typedef)
+ *
+ * This callback is used to called from within a 
+ * rfapi_rfp_get_group_config_ptr to check if the rfp_cfg_group
+ * matches the search criteria
+ *
+ * input: 
+ *    criteria          RFAPI caller provided serach criteria
+ *    rfp_cfg_group     Pointer to configuration structure | NULL
+ *
+ * output:
+ *
+ * return value: 
+ *      0               Match/Success
+ *     ENOENT          No matching
+--------------------------------------------*/
+typedef int (rfp_group_config_search_cb_t) (void *criteria,
+                                            void *rfp_cfg_group);
+
+/*------------------------------------------
+ * rfapi_rfp_get_group_config_ptr_name
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and name context. 
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    type              group type
+ *    name              group name
+ *    criteria          RFAPI caller provided serach criteria
+ *    search_cb         optional rfp_group_config_search_cb_t
+ *
+ * output:
+ *    none
+ *
+ * return value:
+ *    rfp_cfg_group     Pointer to configuration structure 
+--------------------------------------------*/
+extern void *
+rfapi_rfp_get_group_config_ptr_name (
+  void                         *rfp_start_val,
+  rfapi_rfp_cfg_group_type     type,
+  const char                   *name,
+  void                         *criteria,
+  rfp_group_config_search_cb_t *search_cb);
+
+/*------------------------------------------
+ * rfapi_rfp_get_l2_group_config_ptr_lni
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and logical network identifier.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown. 
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start
+ *    logical_net_id    group logical network identifier
+ *    criteria          RFAPI caller provided serach criteria
+ *    search_cb         optional rfp_group_config_search_cb_t
+ *
+ * output:
+ *    none
+ *
+ * return value:
+ *    rfp_cfg_group     Pointer to configuration structure 
+--------------------------------------------*/
+extern void *
+rfapi_rfp_get_l2_group_config_ptr_lni (
+  void                         *rfp_start_val,
+  uint32_t                     logical_net_id,
+  void                         *criteria,
+  rfp_group_config_search_cb_t *search_cb);
+
+/***********************************************************************
+ *                     NVE Sessions
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_open
+ *
+ * This function initializes a NVE record and associates it with
+ * the specified VN and underlay network addresses
+ *
+ * input: 
+ *      rfp_start_val   value returned by rfp_start
+ *     vn              NVE virtual network address
+ *
+ *     un              NVE underlay network address
+ *
+ *     default_options Default options to use on registrations.
+ *                     For now only tunnel type is supported.
+ *                     May be overridden per-prefix in rfapi_register().
+ *                     Caller owns (rfapi_open() does not free)
+ *
+ *     response_cb     Pointer to next hop list update callback function or
+ *                     NULL when no callbacks are desired.
+ *
+ *     userdata        Passed to subsequent response_cb invocations.
+ *
+ * output:
+ *     response_lifetime The length of time that responses sent to this
+ *                     NVE are valid. 
+ *
+ *     pHandle         pointer to location to store rfapi handle. The
+ *                     handle must be passed on subsequent rfapi_ calls.
+ *
+ *
+ * return value: 
+ *     0               Success
+ *     EEXIST          NVE with this {vn,un} already open
+ *     ENOENT          No matching nve group config
+ *     ENOMSG          Matched nve group config was incomplete
+ *     ENXIO           BGP or VNC not configured
+ *     EAFNOSUPPORT    Matched nve group specifies auto-assignment of RD,
+ *                     but underlay network address is not IPv4
+ *     EDEADLK         Called from within a callback procedure
+ *------------------------------------------*/
+extern int
+rfapi_open (
+  void                         *rfp_start_val,
+  struct rfapi_ip_addr         *vn,
+  struct rfapi_ip_addr         *un,
+  struct rfapi_un_option       *default_options,
+  uint32_t                     *response_lifetime,
+  void                         *userdata,
+  rfapi_handle                 *pHandle);
+
+
+/*------------------------------------------
+ * rfapi_close
+ *
+ * Shut down NVE session and release associated data. Calling
+ * from within a rfapi callback procedure is permitted (the close
+ * will be completed asynchronously after the callback finishes).
+ *
+ * input: 
+ *    rfd: rfapi descriptor returned by rfapi_open
+ *
+ * output:
+ *
+ * return value:
+ *     0               Success
+ *     EBADF           invalid handle
+ *     ENXIO           BGP or VNC not configured
+ *------------------------------------------*/
+extern int
+rfapi_close (rfapi_handle rfd);
+
+/*------------------------------------------
+ * rfapi_check
+ *
+ * Test rfapi descriptor
+ *
+ * input: 
+ *    rfd: rfapi descriptor returned by rfapi_open
+ *
+ * output:
+ *
+ * return value:
+ *     0               Success: handle is valid and usable
+ *     EINVAL          null argument
+ *     ESTALE          formerly valid handle invalidated by config, needs close
+ *     EBADF           invalid handle
+ *     ENXIO           BGP or VNC not configured
+ *     EAFNOSUPPORT    Internal addressing error
+ *------------------------------------------*/
+extern int
+rfapi_check (rfapi_handle rfd);
+
+/***********************************************************************
+ *                     NVE Routes
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_query
+ *
+ * This function queries the RIB for a 
+ * particular route.  Note that this call may result in subsequent 
+ * callbacks to response_cb.  Response callbacks can be cancelled
+ * by calling rfapi_query_done.  A duplicate query using the same target
+ * will result in only one callback per change in next_hops. (i.e.,
+ * cancel/replace the prior query results.)
+ *
+ * input: 
+ *    rfd:     rfapi descriptor returned by rfapi_open
+ *    target:  the destination address
+ *    l2o      ptr to L2 Options struct, NULL if not present in query
+ *
+ * output:
+ *     ppNextHopEntry  pointer to a location to store a pointer
+ *                     to the returned list of nexthops. It is the
+ *                     caller's responsibility to free this list
+ *                     via rfapi_free_next_hop_list().
+ *
+ *
+ * return value:
+ *     0               Success
+ *     EBADF           invalid handle
+ *     ENOENT          no valid route
+ *     ENXIO           BGP or VNC not configured
+ *     ESTALE          descriptor is no longer usable; should be closed
+ *     EDEADLK         Called from within a callback procedure
+--------------------------------------------*/
+extern int
+rfapi_query (
+  rfapi_handle                 rfd,
+  struct rfapi_ip_addr         *target,
+  struct rfapi_l2address_option        *l2o,
+  struct rfapi_next_hop_entry  **ppNextHopEntry);
+
+/*------------------------------------------
+ * rfapi_query_done
+ *
+ * Notifies the rfapi that the user is no longer interested 
+ * in the specified target. 
+ *
+ * input: 
+ *    rfd:     rfapi descriptor returned by rfapi_open
+ *    target:  the destination address
+ *
+ * output:
+ *
+ * return value:
+ *     0               Success
+ *     EBADF           invalid handle
+ *     ENOENT          no match found for target
+ *     ENXIO           BGP or VNC not configured
+ *     ESTALE          descriptor is no longer usable; should be closed
+ *     EDEADLK         Called from within a callback procedure
+--------------------------------------------*/
+extern int
+rfapi_query_done (rfapi_handle rfd, struct rfapi_ip_addr *target);
+
+/*------------------------------------------
+ * rfapi_query_done_all
+ *
+ * Notifies the rfapi that the user is no longer interested 
+ * in any target. 
+ *
+ * input: 
+ *    rfd:     rfapi descriptor returned by rfapi_open
+ *
+ * output:
+ *    count:   number of queries cleared
+ *
+ * return value:
+ *     0               Success
+ *     EBADF           invalid handle
+ *     ENXIO           BGP or VNC not configured
+ *     ESTALE          descriptor is no longer usable; should be closed
+ *     EDEADLK         Called from within a callback procedure
+--------------------------------------------*/
+extern int
+rfapi_query_done_all (rfapi_handle rfd, int *count);
+
+/*------------------------------------------
+ * rfapi_register
+ *
+ * Requests that reachability to the indicated prefix via this NVE
+ * be advertised by BGP. If <unregister> is non-zero, then the previously-
+ * advertised prefix should be withdrawn.
+ * 
+ * (This function should NOT be called if the rfapi_open() function 
+ * returns NULL)
+ *
+ * input: 
+ *    rfd:             rfapi descriptor returned by rfapi_open
+ *    prefix:           A prefix to be registered or deregistered
+ *    lifetime         Prefix lifetime in seconds, host byte order
+ *    options_un       underlay netowrk options, may include tunnel-type
+ *                     Caller owns (rfapi_register() does not free).
+ *    options_vn       virtual network options, may include layer 2 address
+ *                     option and local-nexthop option
+ *                     Caller owns (rfapi_register() does not free).
+ *
+ *    action:          RFAPI_REGISTER_ADD      add the route
+ *                      RFAPI_REGISTER_WITHDRAW        withdraw route
+ *                     RFAPI_REGISTER_KILL     withdraw without holddown
+ *
+ * return value:
+ *     0               Success
+ *     EBADF           invalid handle
+ *     ENXIO           BGP or VNC not configured
+ *     ESTALE          descriptor is no longer usable; should be closed
+ *     EDEADLK         Called from within a callback procedure
+ --------------------------------------------*/
+
+typedef enum
+{
+  RFAPI_REGISTER_ADD,
+  RFAPI_REGISTER_WITHDRAW,
+  RFAPI_REGISTER_KILL
+} rfapi_register_action;
+
+extern int
+rfapi_register (
+    rfapi_handle               rfd,
+    struct rfapi_ip_prefix     *prefix,
+    uint32_t                   lifetime,
+    struct rfapi_un_option     *options_un,
+    struct rfapi_vn_option     *options_vn,
+    rfapi_register_action      action);
+
+/***********************************************************************
+ *                     Helper / Utility functions
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_get_vn_addr
+ *
+ * Get the virtual network address used by an NVE based on it's RFD
+ *
+ * input: 
+ *    rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value: 
+ *     vn              NVE virtual network address
+ *------------------------------------------*/
+extern struct rfapi_ip_addr *
+rfapi_get_vn_addr (void *);
+
+/*------------------------------------------
+ * rfapi_get_un_addr
+ *
+ * Get the underlay network address used by an NVE based on it's RFD
+ *
+ * input: 
+ *    rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value: 
+ *     un              NVE underlay network address
+ *------------------------------------------*/
+extern struct rfapi_ip_addr *
+rfapi_get_un_addr (void *);
+
+/*------------------------------------------
+ * rfapi_error_str
+ *
+ * Returns a string describing the rfapi error code.
+ *
+ * input:
+ *
+ *     code            Error code returned by rfapi function
+ *
+ * returns:
+ *
+ *     const char *    String
+ *------------------------------------------*/
+extern const char *
+rfapi_error_str (int code);
+
+/*------------------------------------------
+ * rfapi_get_rfp_start_val
+ *
+ * Returns value passed to rfapi on rfp_start
+ *
+ * input:
+ *     void *          bgp structure
+ *
+ * returns:
+ *     void *          
+ *------------------------------------------*/
+extern void *
+rfapi_get_rfp_start_val (void *bgpv);
+
+/*------------------------------------------
+ * rfapi_compare_rfds
+ *
+ * Compare two generic rfapi descriptors.
+ *
+ * input: 
+ *    rfd1: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *    rfd2: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value:
+ *     0               Mismatch
+ *     1               Match
+ *------------------------------------------*/
+extern int
+rfapi_compare_rfds (void *rfd1, void *rfd2);
+
+/*------------------------------------------
+ * rfapi_free_next_hop_list
+ *
+ * Frees a next_hop_list returned by a rfapi_query invocation
+ *
+ * input: 
+ *    list:   a pointer to a response list (as a 
+ *            struct rfapi_next_hop_entry) to free.
+ *
+ * output:
+ *
+ * return value: None
+ --------------------------------------------*/
+extern void
+rfapi_free_next_hop_list (struct rfapi_next_hop_entry *list);
+
+/*------------------------------------------
+ * rfapi_get_response_lifetime_default
+ *
+ * Returns the default lifetime for a response.
+ *    rfp_start_val     value returned by rfp_start or
+ *                      NULL (=use default instance)
+ *
+ * input: 
+ *    None
+ *
+ * output:
+ *
+ * return value: The bgp instance default lifetime for a response.
+ --------------------------------------------*/
+extern int
+rfapi_get_response_lifetime_default (void *rfp_start_val);
+
+/*------------------------------------------
+ * rfapi_is_vnc_configured
+ *
+ * Returns if VNC (BGP VPN messaging /VPN & encap SAFIs) are configured
+ *
+ * input: 
+ *    rfp_start_val     value returned by rfp_start or
+ *                      NULL (=use default instance)
+ *
+ * output:
+ *
+ * return value: If VNC is configured for the bgpd instance
+ *     0               Success
+ *     ENXIO           VNC not configured
+ --------------------------------------------*/
+extern int
+rfapi_is_vnc_configured (void *rfp_start_val);
+
+/*------------------------------------------
+ * rfapi_bgp_lookup_by_rfp
+ *
+ * Find bgp instance pointer based on value returned by rfp_start
+ *
+ * input: 
+ *      rfp_start_val     value returned by rfp_startor
+ *                        NULL (=get default instance)
+ *
+ * output:
+ *     none
+ *
+ * return value:
+ *     bgp             bgp instance pointer
+ *      NULL = not found
+ *
+ --------------------------------------------*/
+extern struct bgp *
+rfapi_bgp_lookup_by_rfp (void *rfp_start_val);
+
+/*------------------------------------------
+ * rfapi_get_rfp_start_val_by_bgp
+ *
+ * Find bgp instance pointer based on value returned by rfp_start
+ *
+ * input: 
+ *     bgp             bgp instance pointer
+ *
+ * output:
+ *     none
+ *
+ * return value:
+ *     rfp_start_val
+ *      NULL = not found
+ *
+ --------------------------------------------*/
+extern void *
+rfapi_get_rfp_start_val_by_bgp (struct bgp *bgp);
+
+#endif /* ENABLE_BGP_VNC */
+
+#endif /* _QUAGGA_BGP_RFAPI_H */
diff --git a/bgpd/rfapi/rfapi_ap.c b/bgpd/rfapi/rfapi_ap.c
new file mode 100644 (file)
index 0000000..94b6dea
--- /dev/null
@@ -0,0 +1,629 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <errno.h>
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "memory.h"
+#include "routemap.h"
+#include "log.h"
+#include "linklist.h"
+#include "command.h"
+#include "stream.h"
+
+#include "bgpd.h"
+#include "bgp_ecommunity.h"
+#include "bgp_attr.h"
+#include "bgp_mplsvpn.h"
+
+#include "bgp_rfapi_cfg.h"
+#include "rfapi.h"
+#include "rfapi_backend.h"
+
+#include "bgp_route.h"
+#include "bgp_aspath.h"
+#include "bgp_advertise.h"
+
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_monitor.h"
+#include "rfapi_vty.h"
+#include "vnc_export_bgp.h"
+#include "vnc_export_bgp_p.h"
+#include "vnc_zebra.h"
+#include "vnc_import_bgp.h"
+#include "rfapi_rib.h"
+
+#include "rfapi_ap.h"
+
+/*
+ * Per-NVE Advertised prefixes
+ *
+ * We maintain a list of prefixes advertised by each NVE.
+ * There are two indices: by prefix and by lifetime.
+ *
+ * BY-PREFIX skiplist
+ *
+ *  key:       ptr to struct prefix (when storing, point to prefix that
+ *             is part of rfapi_adb).
+ *
+ *  value:     ptr to struct rfapi_adb
+ *
+ * BY-LIFETIME skiplist
+ *
+ *  key:       ptr to struct rfapi_adb
+ *  value:     ptr to struct rfapi_adb
+ *
+ */
+
+/*
+ * Skiplist sort function that sorts first according to lifetime
+ * and then according to adb pointer value. The adb pointer
+ * is used to spread out the sort for adbs with the same lifetime
+ * and thereby make the skip list operations more efficient.
+ */
+static int
+sl_adb_lifetime_cmp (void *adb1, void *adb2)
+{
+  struct rfapi_adb *a1 = adb1;
+  struct rfapi_adb *a2 = adb2;
+
+  if (a1->lifetime < a2->lifetime)
+    return -1;
+  if (a1->lifetime > a2->lifetime)
+    return 1;
+
+  if (a1 < a2)
+    return -1;
+  if (a1 > a2)
+    return 1;
+
+  return 0;
+}
+
+
+void
+rfapiApInit (struct rfapi_advertised_prefixes *ap)
+{
+  ap->ipN_by_prefix = skiplist_new (0, vnc_prefix_cmp, NULL);
+  ap->ip0_by_ether = skiplist_new (0, vnc_prefix_cmp, NULL);
+  ap->by_lifetime = skiplist_new (0, sl_adb_lifetime_cmp, NULL);
+}
+
+void
+rfapiApRelease (struct rfapi_advertised_prefixes *ap)
+{
+  struct rfapi_adb *adb;
+
+  /* Free ADBs and lifetime items */
+  while (0 == skiplist_first (ap->by_lifetime, NULL, (void **) &adb))
+    {
+      rfapiAdbFree (adb);
+      skiplist_delete_first (ap->by_lifetime);
+    }
+
+  while (0 == skiplist_delete_first (ap->ipN_by_prefix));
+  while (0 == skiplist_delete_first (ap->ip0_by_ether));
+
+  /* Free lists */
+  skiplist_free (ap->ipN_by_prefix);
+  skiplist_free (ap->ip0_by_ether);
+  skiplist_free (ap->by_lifetime);
+
+  ap->ipN_by_prefix = NULL;
+  ap->ip0_by_ether = NULL;
+  ap->by_lifetime = NULL;
+}
+
+int
+rfapiApCount (struct rfapi_descriptor *rfd)
+{
+  if (!rfd->advertised.by_lifetime)
+    return 0;
+
+  return skiplist_count (rfd->advertised.by_lifetime);
+}
+
+int
+rfapiApCountAll (struct bgp *bgp)
+{
+  struct rfapi *h;
+  struct listnode *node;
+  struct rfapi_descriptor *rfd;
+  int total = 0;
+
+  h = bgp->rfapi;
+  if (h)
+    {
+      for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd))
+        {
+          total += rfapiApCount (rfd);
+        }
+    }
+  return total;
+}
+
+
+void
+rfapiApReadvertiseAll (struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+  struct rfapi_adb *adb;
+  void *cursor;
+  int rc;
+
+  for (rc =
+       skiplist_next (rfd->advertised.by_lifetime, NULL, (void **) &adb,
+                      &cursor); rc == 0;
+       rc =
+       skiplist_next (rfd->advertised.by_lifetime, NULL, (void **) &adb,
+                      &cursor))
+    {
+
+      struct prefix_rd prd;
+      uint32_t local_pref = rfp_cost_to_localpref (adb->cost);
+
+      prd = rfd->rd;
+      prd.family = AF_UNSPEC;
+      prd.prefixlen = 64;
+
+      /*
+       * TBD this is not quite right. When pfx_ip is 0/32 or 0/128,
+       * we need to substitute the VN address as the prefix
+       */
+      add_vnc_route (rfd, bgp, SAFI_MPLS_VPN, &adb->prefix_ip, &prd,    /* RD to use (0 for ENCAP) */
+                     &rfd->vn_addr,     /* nexthop */
+                     &local_pref, &adb->lifetime, NULL, NULL,   /* struct rfapi_un_option */
+                     NULL,      /* struct rfapi_vn_option */
+                     rfd->rt_export_list, NULL, /* med */
+                     NULL, ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0);
+    }
+}
+
+void
+rfapiApWithdrawAll (struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+  struct rfapi_adb *adb;
+  void *cursor;
+  int rc;
+
+
+  cursor = NULL;
+  for (rc =
+       skiplist_next (rfd->advertised.by_lifetime, NULL, (void **) &adb,
+                      &cursor); rc == 0;
+       rc =
+       skiplist_next (rfd->advertised.by_lifetime, NULL, (void **) &adb,
+                      &cursor))
+    {
+
+      struct prefix pfx_vn_buf;
+      struct prefix *pfx_ip;
+
+      if (!(RFAPI_0_PREFIX (&adb->prefix_ip) &&
+            RFAPI_HOST_PREFIX (&adb->prefix_ip)))
+        {
+
+          pfx_ip = &adb->prefix_ip;
+
+        }
+      else
+        {
+
+          pfx_ip = NULL;
+
+          /*
+           * 0/32 or 0/128 => mac advertisement
+           */
+          if (rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx_vn_buf))
+            {
+              /*
+               * Bad: it means we can't delete the route
+               */
+              zlog_debug ("%s: BAD: handle has bad vn_addr: skipping",
+                          __func__);
+              continue;
+            }
+        }
+
+      del_vnc_route (rfd, rfd->peer, bgp, SAFI_MPLS_VPN, pfx_ip ? pfx_ip : &pfx_vn_buf, &adb->prd,      /* RD to use (0 for ENCAP) */
+                     ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0);
+    }
+}
+
+/*
+ * returns nonzero if tunnel readvertisement is needed, 0 otherwise
+ */
+static int
+rfapiApAdjustLifetimeStats (
+  struct rfapi_descriptor      *rfd,
+  uint32_t                     *old_lifetime,  /* set if removing/replacing */
+  uint32_t                     *new_lifetime)  /* set if replacing/adding */
+{
+  int advertise = 0;
+  int find_max = 0;
+  int find_min = 0;
+
+  zlog_debug ("%s: rfd=%p, pOldLife=%p, pNewLife=%p",
+              __func__, rfd, old_lifetime, new_lifetime);
+  if (old_lifetime)
+    zlog_debug ("%s: OldLife=%d", __func__, *old_lifetime);
+  if (new_lifetime)
+    zlog_debug ("%s: NewLife=%d", __func__, *new_lifetime);
+
+  if (new_lifetime)
+    {
+      /*
+       * Adding new lifetime
+       */
+      if (old_lifetime)
+        {
+          /*
+           * replacing existing lifetime
+           */
+
+
+          /* old and new are same */
+          if (*old_lifetime == *new_lifetime)
+            return 0;
+
+          if (*old_lifetime == rfd->min_prefix_lifetime)
+            {
+              find_min = 1;
+            }
+          if (*old_lifetime == rfd->max_prefix_lifetime)
+            {
+              find_max = 1;
+            }
+
+          /* no need to search if new value is at or equals min|max  */
+          if (*new_lifetime <= rfd->min_prefix_lifetime)
+            {
+              rfd->min_prefix_lifetime = *new_lifetime;
+              find_min = 0;
+            }
+          if (*new_lifetime >= rfd->max_prefix_lifetime)
+            {
+              rfd->max_prefix_lifetime = *new_lifetime;
+              advertise = 1;
+              find_max = 0;
+            }
+
+        }
+      else
+        {
+          /*
+           * Just adding new lifetime
+           */
+          if (*new_lifetime < rfd->min_prefix_lifetime)
+            {
+              rfd->min_prefix_lifetime = *new_lifetime;
+            }
+          if (*new_lifetime > rfd->max_prefix_lifetime)
+            {
+              advertise = 1;
+              rfd->max_prefix_lifetime = *new_lifetime;
+            }
+
+        }
+    }
+  else
+    {
+      /*
+       * Deleting
+       */
+
+      /*
+       * See if the max prefix lifetime for this NVE has decreased.
+       * The easy optimization: track min & max; walk the table only
+       * if they are different.
+       * The general optimization: index the advertised_prefixes
+       * table by lifetime.
+       *
+       * Note: for a given nve_descriptor, only one of the
+       * advertised_prefixes[] tables will be used: viz., the
+       * address family that matches the VN address.
+       *
+       */
+      if (rfd->max_prefix_lifetime == rfd->min_prefix_lifetime)
+        {
+
+          /*
+           * Common case: all lifetimes are the same. Only
+           * thing we need to do here is check if there are
+           * no exported routes left. In that case, reinitialize
+           * the max and min values.
+           */
+          if (!rfapiApCount (rfd))
+            {
+              rfd->max_prefix_lifetime = 0;
+              rfd->min_prefix_lifetime = UINT32_MAX;
+            }
+
+
+        }
+      else
+        {
+          if (old_lifetime)
+            {
+              if (*old_lifetime == rfd->min_prefix_lifetime)
+                {
+                  find_min = 1;
+                }
+              if (*old_lifetime == rfd->max_prefix_lifetime)
+                {
+                  find_max = 1;
+                }
+            }
+        }
+    }
+
+  if (find_min || find_max)
+    {
+      uint32_t min = UINT32_MAX;
+      uint32_t max = 0;
+
+      struct rfapi_adb *adb_min;
+      struct rfapi_adb *adb_max;
+
+      if (!skiplist_first
+          (rfd->advertised.by_lifetime, (void **) &adb_min, NULL)
+          && !skiplist_last (rfd->advertised.by_lifetime, (void **) &adb_max,
+                             NULL))
+        {
+
+          /*
+           * This should always work
+           */
+          min = adb_min->lifetime;
+          max = adb_max->lifetime;
+
+        }
+      else
+        {
+
+          void *cursor;
+          struct prefix *prefix;
+          struct rfapi_adb *adb;
+          int rc;
+
+          zlog_debug ("%s: walking to find new min/max", __func__);
+
+          cursor = NULL;
+          for (rc = skiplist_next (rfd->advertised.ipN_by_prefix,
+                                   (void **) &prefix, (void **) &adb,
+                                   &cursor); !rc;
+               rc =
+               skiplist_next (rfd->advertised.ipN_by_prefix,
+                              (void **) &prefix, (void **) &adb, &cursor))
+            {
+
+              uint32_t lt = adb->lifetime;
+
+              if (lt > max)
+                max = lt;
+              if (lt < min)
+                min = lt;
+            }
+          cursor = NULL;
+          for (rc = skiplist_next (rfd->advertised.ip0_by_ether,
+                                   (void **) &prefix, (void **) &adb,
+                                   &cursor); !rc;
+               rc =
+               skiplist_next (rfd->advertised.ip0_by_ether, (void **) &prefix,
+                              (void **) &adb, &cursor))
+            {
+
+              uint32_t lt = adb->lifetime;
+
+              if (lt > max)
+                max = lt;
+              if (lt < min)
+                min = lt;
+            }
+        }
+
+      /*
+       * trigger tunnel route update
+       * but only if we found a VPN route and it had
+       * a lifetime greater than 0
+       */
+      if (max && rfd->max_prefix_lifetime != max)
+        advertise = 1;
+      rfd->max_prefix_lifetime = max;
+      rfd->min_prefix_lifetime = min;
+    }
+
+  zlog_debug ("%s: returning advertise=%d, min=%d, max=%d",
+              __func__, advertise, rfd->min_prefix_lifetime,
+              rfd->max_prefix_lifetime);
+
+  return (advertise != 0);
+}
+
+/* 
+ * Return Value
+ *
+ *     0       No need to advertise tunnel route
+ *     non-0   advertise tunnel route
+ */
+int
+rfapiApAdd (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct prefix                        *pfx_ip,
+  struct prefix                        *pfx_eth,
+  struct prefix_rd             *prd,
+  uint32_t                     lifetime,
+  uint8_t                      cost,
+  struct rfapi_l2address_option        *l2o)   /* other options TBD */
+{
+  int rc;
+  struct rfapi_adb *adb;
+  uint32_t old_lifetime = 0;
+  int use_ip0 = 0;
+
+  if (RFAPI_0_PREFIX (pfx_ip) && RFAPI_HOST_PREFIX (pfx_ip))
+    {
+      use_ip0 = 1;
+      assert (pfx_eth);
+
+      rc =
+        skiplist_search (rfd->advertised.ip0_by_ether, pfx_eth,
+                         (void **) &adb);
+
+    }
+  else
+    {
+
+      /* find prefix in advertised prefixes list */
+      rc =
+        skiplist_search (rfd->advertised.ipN_by_prefix, pfx_ip,
+                         (void **) &adb);
+    }
+
+
+  if (rc)
+    {
+      /* Not found */
+      adb = XCALLOC (MTYPE_RFAPI_ADB, sizeof (struct rfapi_adb));
+      assert (adb);
+      adb->lifetime = lifetime;
+      adb->prefix_ip = *pfx_ip;
+      if (pfx_eth)
+        adb->prefix_eth = *pfx_eth;
+
+      if (use_ip0)
+        {
+          assert (pfx_eth);
+          skiplist_insert (rfd->advertised.ip0_by_ether, &adb->prefix_eth,
+                           adb);
+        }
+      else
+        {
+          skiplist_insert (rfd->advertised.ipN_by_prefix, &adb->prefix_ip,
+                           adb);
+        }
+
+      skiplist_insert (rfd->advertised.by_lifetime, adb, adb);
+    }
+  else
+    {
+      old_lifetime = adb->lifetime;
+      if (old_lifetime != lifetime)
+        {
+          assert (!skiplist_delete (rfd->advertised.by_lifetime, adb, NULL));
+          adb->lifetime = lifetime;
+          assert (!skiplist_insert (rfd->advertised.by_lifetime, adb, adb));
+        }
+
+      if (!use_ip0 && pfx_eth && prefix_cmp (&adb->prefix_eth, pfx_eth))
+        {
+          /* mac address changed */
+          adb->prefix_eth = *pfx_eth;
+        }
+    }
+  adb->cost = cost;
+  if (l2o)
+    adb->l2o = *l2o;
+  else
+    memset (&adb->l2o, 0, sizeof (struct rfapi_l2address_option));
+  adb->prd = *prd;
+
+  if (rfapiApAdjustLifetimeStats
+      (rfd, (rc ? NULL : &old_lifetime), &lifetime))
+    return 1;
+
+  return 0;
+}
+
+/*
+ * After this function returns successfully, caller should call
+ * rfapiAdjustLifetimeStats() and possibly rfapiTunnelRouteAnnounce()
+ */
+int
+rfapiApDelete (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct prefix                        *pfx_ip,
+  struct prefix                        *pfx_eth,
+  int                          *advertise_tunnel)      /* out */
+{
+  int rc;
+  struct rfapi_adb *adb;
+  uint32_t old_lifetime;
+  int use_ip0 = 0;
+
+  if (advertise_tunnel)
+    *advertise_tunnel = 0;
+
+  /* find prefix in advertised prefixes list */
+  if (RFAPI_0_PREFIX (pfx_ip) && RFAPI_HOST_PREFIX (pfx_ip))
+    {
+      use_ip0 = 1;
+      assert (pfx_eth);
+
+      rc =
+        skiplist_search (rfd->advertised.ip0_by_ether, pfx_eth,
+                         (void **) &adb);
+
+    }
+  else
+    {
+
+      /* find prefix in advertised prefixes list */
+      rc =
+        skiplist_search (rfd->advertised.ipN_by_prefix, pfx_ip,
+                         (void **) &adb);
+    }
+
+  if (rc)
+    {
+      return ENOENT;
+    }
+
+  old_lifetime = adb->lifetime;
+
+  if (use_ip0)
+    {
+      rc = skiplist_delete (rfd->advertised.ip0_by_ether, pfx_eth, NULL);
+    }
+  else
+    {
+      rc = skiplist_delete (rfd->advertised.ipN_by_prefix, pfx_ip, NULL);
+    }
+  assert (!rc);
+
+  rc = skiplist_delete (rfd->advertised.by_lifetime, adb, NULL);
+  assert (!rc);
+
+  rfapiAdbFree (adb);
+
+  if (rfapiApAdjustLifetimeStats (rfd, &old_lifetime, NULL))
+    {
+      if (advertise_tunnel)
+        *advertise_tunnel = 1;
+    }
+
+  return 0;
+}
diff --git a/bgpd/rfapi/rfapi_ap.h b/bgpd/rfapi/rfapi_ap.h
new file mode 100644 (file)
index 0000000..3bb08a4
--- /dev/null
@@ -0,0 +1,99 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+#ifndef _QUAGGA_BGP_RFAPI_AP_H
+#define _QUAGGA_BGP_RFAPI_AP_H
+
+/* TBD delete some of these #includes */
+
+#include <errno.h>
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "memory.h"
+#include "routemap.h"
+#include "log.h"
+#include "linklist.h"
+#include "command.h"
+#include "stream.h"
+
+#include "bgpd.h"
+
+#include "bgp_rfapi_cfg.h"
+#include "rfapi.h"
+#include "rfapi_backend.h"
+
+#include "bgp_route.h"
+#include "bgp_aspath.h"
+#include "bgp_advertise.h"
+
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_monitor.h"
+#include "rfapi_vty.h"
+#include "vnc_export_bgp.h"
+#include "vnc_export_bgp_p.h"
+#include "vnc_zebra.h"
+#include "vnc_import_bgp.h"
+#include "rfapi_rib.h"
+
+
+extern void
+rfapiApInit (struct rfapi_advertised_prefixes *ap);
+
+extern void
+rfapiApRelease (struct rfapi_advertised_prefixes *ap);
+
+extern int
+rfapiApCount (struct rfapi_descriptor *rfd);
+
+
+extern int
+rfapiApCountAll (struct bgp *bgp);
+
+extern void
+rfapiApReadvertiseAll (struct bgp *bgp, struct rfapi_descriptor *rfd);
+
+extern void
+rfapiApWithdrawAll (struct bgp *bgp, struct rfapi_descriptor *rfd);
+
+extern int
+rfapiApAdd (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct prefix                        *pfx_ip,
+  struct prefix                        *pfx_eth,
+  struct prefix_rd             *prd,
+  uint32_t                     lifetime,
+  uint8_t                      cost,
+  struct rfapi_l2address_option        *l2o);       /* other options TBD */
+
+extern int
+rfapiApDelete (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct prefix                        *pfx_ip,
+  struct prefix                        *pfx_eth,
+  int                          *advertise_tunnel); /* out */
+
+
+#endif /* _QUAGGA_BGP_RFAPI_AP_H */
diff --git a/bgpd/rfapi/rfapi_backend.h b/bgpd/rfapi/rfapi_backend.h
new file mode 100644 (file)
index 0000000..451f5c2
--- /dev/null
@@ -0,0 +1,92 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_BACKEND_H
+#define _QUAGGA_BGP_RFAPI_BACKEND_H
+
+#if ENABLE_BGP_VNC
+
+#include "bgp_route.h"
+#include "bgp_nexthop.h"
+
+extern void rfapi_init (void);
+extern void vnc_zebra_init (struct thread_master *master);
+extern void vnc_zebra_destroy (void);
+
+extern void rfapi_delete (struct bgp *);
+
+struct rfapi *bgp_rfapi_new (struct bgp *bgp);
+void bgp_rfapi_destroy (struct bgp *bgp, struct rfapi *h);
+
+struct rfapi_import_table *rfapiImportTableRefAdd (struct bgp *bgp,
+                                                   struct ecommunity
+                                                   *rt_import_list);
+
+void
+rfapiImportTableRefDelByIt (struct bgp *bgp,
+                            struct rfapi_import_table *it_target);
+
+
+extern void
+rfapiProcessUpdate (struct peer *peer,
+                    void *rfd,
+                    struct prefix *p,
+                    struct prefix_rd *prd,
+                    struct attr *attr,
+                    afi_t afi,
+                    safi_t safi,
+                    u_char type, u_char sub_type, uint32_t * label);
+
+
+extern void
+rfapiProcessWithdraw (struct peer *peer,
+                      void *rfd,
+                      struct prefix *p,
+                      struct prefix_rd *prd,
+                      struct attr *attr,
+                      afi_t afi, safi_t safi, u_char type, int kill);
+
+extern void rfapiProcessPeerDown (struct peer *peer);
+
+extern void
+vnc_zebra_announce (struct prefix *p,
+                    struct bgp_info *new_select, struct bgp *bgp);
+
+extern void
+vnc_zebra_withdraw (struct prefix *p, struct bgp_info *old_select);
+
+
+extern void
+rfapi_vty_out_vncinfo (struct vty *vty,
+                       struct prefix *p, struct bgp_info *bi, safi_t safi);
+
+
+extern void vnc_direct_bgp_vpn_enable (struct bgp *bgp, afi_t afi);
+
+extern void vnc_direct_bgp_vpn_disable (struct bgp *bgp, afi_t afi);
+
+extern void vnc_direct_bgp_rh_vpn_enable (struct bgp *bgp, afi_t afi);
+
+extern void vnc_direct_bgp_rh_vpn_disable (struct bgp *bgp, afi_t afi);
+
+#endif /* ENABLE_BGP_VNC */
+
+#endif /* _QUAGGA_BGP_RFAPI_BACKEND_H */
diff --git a/bgpd/rfapi/rfapi_descriptor_rfp_utils.c b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c
new file mode 100644 (file)
index 0000000..c2d1182
--- /dev/null
@@ -0,0 +1,131 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+
+#include <errno.h>
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "memory.h"
+#include "log.h"
+
+#include "bgpd.h"
+
+#include "rfapi.h"
+#include "rfapi_private.h"
+#include "rfapi_descriptor_rfp_utils.h"
+
+
+void *
+rfapi_create_generic (struct rfapi_ip_addr *vn, struct rfapi_ip_addr *un)
+{
+  struct rfapi_descriptor *rfd;
+  rfd = XCALLOC (MTYPE_RFAPI_DESC, sizeof (struct rfapi_descriptor));
+  zlog_debug ("%s: rfd=%p", __func__, rfd);
+  rfd->vn_addr = *vn;
+  rfd->un_addr = *un;
+  return (void *) rfd;
+}
+
+/*------------------------------------------
+ * rfapi_free_generic
+ *
+ * Compare two generic rfapi descriptors.
+ *
+ * input: 
+ *    grfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value: 
+ *
+ *------------------------------------------*/
+void
+rfapi_free_generic (void *grfd)
+{
+  struct rfapi_descriptor *rfd;
+  rfd = (struct rfapi_descriptor *) grfd;
+  XFREE (MTYPE_RFAPI_DESC, rfd);
+}
+
+
+/*------------------------------------------
+ * rfapi_compare_rfds
+ *
+ * Compare two generic rfapi descriptors.
+ *
+ * input: 
+ *    rfd1: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *    rfd2: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value:
+ *     0               Mismatch
+ *     1               Match
+ *------------------------------------------*/
+int
+rfapi_compare_rfds (void *rfd1, void *rfd2)
+{
+  struct rfapi_descriptor *rrfd1, *rrfd2;
+  int match = 0;
+
+  rrfd1 = (struct rfapi_descriptor *) rfd1;
+  rrfd2 = (struct rfapi_descriptor *) rfd2;
+
+  if (rrfd1->vn_addr.addr_family == rrfd2->vn_addr.addr_family)
+    {
+      if (rrfd1->vn_addr.addr_family == AF_INET)
+        match = IPV4_ADDR_SAME (&(rrfd1->vn_addr.addr.v4),
+                                &(rrfd2->vn_addr.addr.v4));
+      else
+        match = IPV6_ADDR_SAME (&(rrfd1->vn_addr.addr.v6),
+                                &(rrfd2->vn_addr.addr.v6));
+    }
+
+  /* 
+   * If the VN addresses don't match in all forms, 
+   * give up.
+   */
+  if (!match)
+    return 0;
+
+  /* 
+   * do the process again for the UN addresses. 
+   */
+  match = 0;
+  if (rrfd1->un_addr.addr_family == rrfd2->un_addr.addr_family)
+    {
+      /* VN addresses match
+       * UN address families match 
+       * now check the actual UN addresses
+       */
+      if (rrfd1->un_addr.addr_family == AF_INET)
+        match = IPV4_ADDR_SAME (&(rrfd1->un_addr.addr.v4),
+                                &(rrfd2->un_addr.addr.v4));
+      else
+        match = IPV6_ADDR_SAME (&(rrfd1->un_addr.addr.v6),
+                                &(rrfd2->un_addr.addr.v6));
+    }
+  return match;
+}
diff --git a/bgpd/rfapi/rfapi_descriptor_rfp_utils.h b/bgpd/rfapi/rfapi_descriptor_rfp_utils.h
new file mode 100644 (file)
index 0000000..9067cdf
--- /dev/null
@@ -0,0 +1,39 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+
+extern void *rfapi_create_generic (struct rfapi_ip_addr *vn,
+                                   struct rfapi_ip_addr *un);
+
+/*------------------------------------------
+ * rfapi_free_generic
+ *
+ * Compare two generic rfapi descriptors.
+ *
+ * input: 
+ *    grfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value: 
+ *
+ *------------------------------------------*/
+extern void rfapi_free_generic (void *grfd);
diff --git a/bgpd/rfapi/rfapi_encap_tlv.c b/bgpd/rfapi/rfapi_encap_tlv.c
new file mode 100644 (file)
index 0000000..17fee2c
--- /dev/null
@@ -0,0 +1,812 @@
+/* 
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <zebra.h>
+
+#include <memory.h>
+#include <prefix.h>
+#include <table.h>
+#include <vty.h>
+
+#include "bgpd.h"
+#include "bgp_attr.h"
+
+#include "bgp_encap_types.h"
+#include "bgp_encap_tlv.h"
+
+#include "rfapi.h"
+#include "rfapi_encap_tlv.h"
+#include "rfapi_private.h"
+#include "rfapi_monitor.h"
+#include "rfapi_vty.h"
+#include "bgp_rfapi_cfg.h"
+
+static void
+rfapi_add_endpoint_address_to_subtlv (
+  struct bgp                           *bgp,
+  struct rfapi_ip_addr                 *ea,
+  struct bgp_tea_subtlv_remote_endpoint        *subtlv)
+{
+  subtlv->family = ea->addr_family;
+  if (subtlv->family == AF_INET)
+    subtlv->ip_address.v4 = ea->addr.v4;
+  else
+    subtlv->ip_address.v6 = ea->addr.v6;
+  subtlv->as4 = htonl (bgp->as);
+}
+
+bgp_encap_types
+rfapi_tunneltype_option_to_tlv (
+  struct bgp                           *bgp,
+  struct rfapi_ip_addr                 *ea,
+  struct rfapi_tunneltype_option       *tto,
+  struct attr                          *attr,
+  int                                  always_add)
+{
+
+#define _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(ttype)                               \
+    if ((always_add || (bgp->rfapi_cfg &&                                     \
+                        !CHECK_FLAG(bgp->rfapi_cfg->flags,                    \
+                                    BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP))) &&  \
+        ea && !CHECK_SUBTLV_FLAG(&tto->bgpinfo.ttype,                         \
+                                 BGP_TEA_SUBTLV_REMOTE_ENDPOINT)) {           \
+        rfapi_add_endpoint_address_to_subtlv(bgp, ea,                         \
+                                           &tto->bgpinfo.ttype.st_endpoint);  \
+        SET_SUBTLV_FLAG(&tto->bgpinfo.ttype, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); \
+    }
+
+  struct rfapi_tunneltype_option dto;
+  if (tto == NULL)
+    {                           /* create default type */
+      tto = &dto;
+      memset (tto, 0, sizeof (dto));
+      tto->type = RFAPI_BGP_ENCAP_TYPE_DEFAULT;
+    }
+  switch (tto->type)
+    {
+    case BGP_ENCAP_TYPE_L2TPV3_OVER_IP:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (l2tpv3_ip);
+      bgp_encap_type_l2tpv3overip_to_tlv (&tto->bgpinfo.l2tpv3_ip, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_GRE:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (gre);
+      bgp_encap_type_gre_to_tlv (&tto->bgpinfo.gre, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (transmit_tunnel_endpoint);
+      bgp_encap_type_transmit_tunnel_endpoint (&tto->bgpinfo.transmit_tunnel_endpoint,
+                                               attr);
+      break;
+
+    case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (ipsec_tunnel);
+      bgp_encap_type_ipsec_in_tunnel_mode_to_tlv (&tto->bgpinfo.ipsec_tunnel,
+                                                  attr);
+      break;
+
+    case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (ip_ipsec);
+      bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv
+        (&tto->bgpinfo.ip_ipsec, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (mpls_ipsec);
+      bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv
+        (&tto->bgpinfo.mpls_ipsec, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_IP_IN_IP:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (ip_ip);
+      bgp_encap_type_ip_in_ip_to_tlv (&tto->bgpinfo.ip_ip, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_VXLAN:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (vxlan);
+      bgp_encap_type_vxlan_to_tlv (&tto->bgpinfo.vxlan, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_NVGRE:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (nvgre);
+      bgp_encap_type_nvgre_to_tlv (&tto->bgpinfo.nvgre, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (mpls);
+      bgp_encap_type_mpls_to_tlv (&tto->bgpinfo.mpls, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS_IN_GRE:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (mpls_gre);
+      bgp_encap_type_mpls_in_gre_to_tlv (&tto->bgpinfo.mpls_gre, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_VXLAN_GPE:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (vxlan_gpe);
+      bgp_encap_type_vxlan_gpe_to_tlv (&tto->bgpinfo.vxlan_gpe, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS_IN_UDP:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (mpls_udp);
+      bgp_encap_type_mpls_in_udp_to_tlv (&tto->bgpinfo.mpls_udp, attr);
+      break;
+
+    case BGP_ENCAP_TYPE_PBB:
+      _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS (pbb);
+      bgp_encap_type_pbb_to_tlv (&tto->bgpinfo.pbb, attr);
+      break;
+
+    default:
+      assert (0);
+    }
+  return tto->type;
+}
+
+struct rfapi_un_option *
+rfapi_encap_tlv_to_un_option (struct attr *attr)
+{
+  struct attr_extra *attre = attr->extra;
+  struct rfapi_un_option *uo = NULL;
+  struct rfapi_tunneltype_option *tto;
+  int rc;
+  struct bgp_attr_encap_subtlv *stlv;
+
+  if (!attre)
+    return NULL;
+
+  /* no tunnel encap attr stored */
+  if (!attre->encap_tunneltype)
+    return NULL;
+
+  stlv = attre->encap_subtlvs;
+
+  uo = XCALLOC (MTYPE_RFAPI_UN_OPTION, sizeof (struct rfapi_un_option));
+  assert (uo);
+  uo->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE;
+  uo->v.tunnel.type = attre->encap_tunneltype;
+  tto = &uo->v.tunnel;
+
+  switch (attre->encap_tunneltype)
+    {
+    case BGP_ENCAP_TYPE_L2TPV3_OVER_IP:
+      rc = tlv_to_bgp_encap_type_l2tpv3overip (stlv, &tto->bgpinfo.l2tpv3_ip);
+      break;
+
+    case BGP_ENCAP_TYPE_GRE:
+      rc = tlv_to_bgp_encap_type_gre (stlv, &tto->bgpinfo.gre);
+      break;
+
+    case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT:
+      rc = tlv_to_bgp_encap_type_transmit_tunnel_endpoint (stlv,
+                                                           &tto->bgpinfo.transmit_tunnel_endpoint);
+      break;
+
+    case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE:
+      rc = tlv_to_bgp_encap_type_ipsec_in_tunnel_mode (stlv,
+                                                       &tto->bgpinfo.ipsec_tunnel);
+      break;
+
+    case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+      rc =
+        tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode (stlv,
+                                                                         &tto->bgpinfo.ip_ipsec);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+      rc =
+        tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode
+        (stlv, &tto->bgpinfo.mpls_ipsec);
+      break;
+
+    case BGP_ENCAP_TYPE_IP_IN_IP:
+      rc = tlv_to_bgp_encap_type_ip_in_ip (stlv, &tto->bgpinfo.ip_ip);
+      break;
+
+    case BGP_ENCAP_TYPE_VXLAN:
+      rc = tlv_to_bgp_encap_type_vxlan (stlv, &tto->bgpinfo.vxlan);
+      break;
+
+    case BGP_ENCAP_TYPE_NVGRE:
+      rc = tlv_to_bgp_encap_type_nvgre (stlv, &tto->bgpinfo.nvgre);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS:
+      rc = tlv_to_bgp_encap_type_mpls (stlv, &tto->bgpinfo.mpls);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS_IN_GRE:
+      rc = tlv_to_bgp_encap_type_mpls_in_gre (stlv, &tto->bgpinfo.mpls_gre);
+      break;
+
+    case BGP_ENCAP_TYPE_VXLAN_GPE:
+      rc = tlv_to_bgp_encap_type_vxlan_gpe (stlv, &tto->bgpinfo.vxlan_gpe);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS_IN_UDP:
+      rc = tlv_to_bgp_encap_type_mpls_in_udp (stlv, &tto->bgpinfo.mpls_udp);
+      break;
+
+    case BGP_ENCAP_TYPE_PBB:
+      rc = tlv_to_bgp_encap_type_pbb (stlv, &tto->bgpinfo.pbb);
+      break;
+
+    default:
+      zlog_debug ("%s: unknown tunnel type %d",
+                  __func__, attre->encap_tunneltype);
+      rc = -1;
+      break;
+    }
+  if (rc)
+    {
+      XFREE (MTYPE_RFAPI_UN_OPTION, uo);
+      uo = NULL;
+    }
+  return uo;
+}
+
+/***********************************************************************
+ *                     SUBTLV PRINT
+ ***********************************************************************/
+
+static void
+subtlv_print_encap_l2tpv3_over_ip (
+  void                                         *stream,
+  int                                          column_offset,
+  struct bgp_tea_subtlv_encap_l2tpv3_over_ip   *st)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!st)
+    return;
+
+  fp (out, "%*s%s%s", column_offset, "", "SubTLV: Encap(L2TPv3 over IP)",
+      vty_newline);
+  fp (out, "%*s  SessionID: %d%s", column_offset, "", st->sessionid,
+      vty_newline);
+  fp (out, "%*s  Cookie: (length %d)%s", column_offset, "", st->cookie_length,
+      vty_newline);
+}
+
+static void
+subtlv_print_encap_gre (
+  void                                 *stream,
+  int                                  column_offset,
+  struct bgp_tea_subtlv_encap_gre_key  *st)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!st)
+    return;
+
+  fp (out, "%*s%s%s", column_offset, "", "SubTLV: Encap(GRE)", vty_newline);
+  fp (out, "%*s  GRE key: %d (0x%x)%s", column_offset, "", st->gre_key,
+      st->gre_key, vty_newline);
+}
+
+static void
+subtlv_print_encap_pbb (
+  void                                 *stream,
+  int                                  column_offset,
+  struct bgp_tea_subtlv_encap_pbb      *st)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!st)
+    return;
+
+  fp (out, "%*s%s%s", column_offset, "", "SubTLV: Encap(PBB)", vty_newline);
+  if (st->flag_isid)
+    {
+      fp (out, "%*s  ISID: %d (0x%x)%s", column_offset, "", st->isid,
+          st->isid, vty_newline);
+    }
+  if (st->flag_vid)
+    {
+      fp (out, "%*s  VID: %d (0x%x)%s", column_offset, "", st->vid, st->vid,
+          vty_newline);
+    }
+  fp (out, "%*s  MACADDR %02x:%02x:%02x:%02x:%02x:%02x%s",
+      column_offset, "",
+      st->macaddr[0],
+      st->macaddr[1],
+      st->macaddr[2],
+      st->macaddr[3], st->macaddr[4], st->macaddr[5], vty_newline);
+}
+
+static void
+subtlv_print_proto_type (
+  void                                 *stream,
+  int                                  column_offset,
+  struct bgp_tea_subtlv_proto_type     *st)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!st)
+    return;
+
+  fp (out, "%*s%s%s", column_offset, "", "SubTLV: Encap(Proto Type)",
+      vty_newline);
+  fp (out, "%*s  Proto %d (0x%x)%s", column_offset, "", st->proto, st->proto,
+      vty_newline);
+}
+
+static void
+subtlv_print_color (
+  void                         *stream,
+  int                          column_offset,
+  struct bgp_tea_subtlv_color  *st)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!st)
+    return;
+
+  fp (out, "%*s%s%s", column_offset, "", "SubTLV: Color", vty_newline);
+  fp (out, "%*s  Color: %d (0x%x)", column_offset, "", st->color, st->color,
+      vty_newline);
+}
+
+static void
+subtlv_print_ipsec_ta (
+  void                                 *stream,
+  int                                  column_offset,
+  struct bgp_tea_subtlv_ipsec_ta       *st)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!st)
+    return;
+
+  fp (out, "%*s%s%s", column_offset, "", "SubTLV: IPSEC TA", vty_newline);
+  fp (out, "%*s  Authenticator Type: %d (0x%x)", column_offset, "",
+      st->authenticator_type, st->authenticator_type, vty_newline);
+  fp (out, "%*s  Authenticator: (length %d)", column_offset, "",
+      st->authenticator_length, vty_newline);
+}
+
+/***********************************************************************
+ *                     TLV PRINT
+ ***********************************************************************/
+
+static void
+print_encap_type_l2tpv3overip (
+  void                                 *stream,
+  int                                  column_offset,
+  struct bgp_encap_type_l2tpv3_over_ip *bet)
+{
+  const char *type = "L2TPv3 over IP";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  subtlv_print_encap_l2tpv3_over_ip (stream, column_offset + 2,
+                                     &bet->st_encap);
+  subtlv_print_proto_type (stream, column_offset + 2, &bet->st_proto);
+  subtlv_print_color (stream, column_offset + 2, &bet->st_color);
+}
+
+static void
+print_encap_type_gre (
+  void                         *stream,
+  int                          column_offset,
+  struct bgp_encap_type_gre    *bet)
+{
+  const char *type = "GRE";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  subtlv_print_encap_gre (stream, column_offset + 2, &bet->st_encap);
+  subtlv_print_proto_type (stream, column_offset + 2, &bet->st_proto);
+  subtlv_print_color (stream, column_offset + 2, &bet->st_color);
+}
+
+static void
+print_encap_type_ip_in_ip (
+  void                                 *stream,
+  int                                  column_offset,
+  struct bgp_encap_type_ip_in_ip       *bet)
+{
+  const char *type = "IP in IP";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  subtlv_print_proto_type (stream, column_offset + 2, &bet->st_proto);
+  subtlv_print_color (stream, column_offset + 2, &bet->st_color);
+}
+
+static void
+print_encap_type_transmit_tunnel_endpoint (
+  void                                                 *stream,
+  int                                                  column_offset,
+  struct bgp_encap_type_transmit_tunnel_endpoint       *bet)
+{
+  const char *type = "Transmit Tunnel Endpoint";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  /* no subtlvs for this type */
+}
+
+static void
+print_encap_type_ipsec_in_tunnel_mode (
+  void                                         *stream,
+  int                                          column_offset,
+  struct bgp_encap_type_ipsec_in_tunnel_mode   *bet)
+{
+  const char *type = "IPSEC in Tunnel mode";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+  subtlv_print_ipsec_ta (stream, column_offset + 2, &bet->st_ipsec_ta);
+}
+
+static void
+print_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode (
+  void *stream,
+  int  column_offset,
+  struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode      *bet)
+{
+  const char *type = "IP in IP Tunnel with IPSEC transport mode";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  subtlv_print_ipsec_ta (stream, column_offset + 2, &bet->st_ipsec_ta);
+}
+
+static void
+print_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode (
+  void *stream,
+  int  column_offset,
+  struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode    *bet)
+{
+  const char *type = "MPLS in IP Tunnel with IPSEC transport mode";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  subtlv_print_ipsec_ta (stream, column_offset + 2, &bet->st_ipsec_ta);
+}
+
+
+static void
+print_encap_type_pbb (
+  void                         *stream,
+  int                          column_offset,
+  struct bgp_encap_type_pbb    *bet)
+{
+  const char *type = "PBB";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  subtlv_print_encap_pbb (stream, column_offset + 2, &bet->st_encap);
+}
+
+
+static void
+print_encap_type_vxlan (
+  void                         *stream,
+  int                          column_offset,
+  struct bgp_encap_type_vxlan  *bet)
+{
+  const char *type = "VXLAN";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  /* no subtlvs for this type */
+}
+
+
+static void
+print_encap_type_nvgre (
+  void                         *stream,
+  int                          column_offset,
+  struct bgp_encap_type_nvgre  *bet)
+{
+  const char *type = "NVGRE";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  /* no subtlvs for this type */
+}
+
+static void
+print_encap_type_mpls (
+  void                         *stream,
+  int                          column_offset,
+  struct bgp_encap_type_mpls   *bet)
+{
+  const char *type = "MPLS";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  /* no subtlvs for this type */
+}
+
+static void
+print_encap_type_mpls_in_gre (
+  void                                 *stream,
+  int                                  column_offset,
+  struct bgp_encap_type_mpls_in_gre    *bet)
+{
+  const char *type = "MPLS in GRE";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  /* no subtlvs for this type */
+}
+
+static void
+print_encap_type_vxlan_gpe (
+  void                                 *stream,
+  int                                  column_offset,
+  struct bgp_encap_type_vxlan_gpe      *bet)
+{
+  const char *type = "VXLAN GPE";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  /* no subtlvs for this type */
+}
+
+static void
+print_encap_type_mpls_in_udp (
+  void                                 *stream,
+  int                                  column_offset,
+  struct bgp_encap_type_mpls_in_udp    *bet)
+{
+  const char *type = "MPLS in UDP";
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  if (!bet)
+    return;
+
+  fp (out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+  /* no subtlvs for this type */
+}
+
+void
+rfapi_print_tunneltype_option (
+  void                                 *stream,
+  int                                  column_offset,
+  struct rfapi_tunneltype_option       *tto)
+{
+  switch (tto->type)
+    {
+    case BGP_ENCAP_TYPE_L2TPV3_OVER_IP:
+      print_encap_type_l2tpv3overip (stream, column_offset,
+                                     &tto->bgpinfo.l2tpv3_ip);
+      break;
+
+    case BGP_ENCAP_TYPE_GRE:
+      print_encap_type_gre (stream, column_offset, &tto->bgpinfo.gre);
+      break;
+
+    case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT:
+      print_encap_type_transmit_tunnel_endpoint (stream, column_offset,
+                                                 &tto->bgpinfo.transmit_tunnel_endpoint);
+      break;
+
+    case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE:
+      print_encap_type_ipsec_in_tunnel_mode (stream, column_offset,
+                                             &tto->bgpinfo.ipsec_tunnel);
+      break;
+
+    case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+      print_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode (stream,
+                                                                  column_offset,
+                                                                  &tto->bgpinfo.ip_ipsec);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+      print_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode (stream,
+                                                                    column_offset,
+                                                                    &tto->bgpinfo.mpls_ipsec);
+      break;
+
+    case BGP_ENCAP_TYPE_IP_IN_IP:
+      print_encap_type_ip_in_ip (stream, column_offset, &tto->bgpinfo.ip_ip);
+      break;
+
+    case BGP_ENCAP_TYPE_VXLAN:
+      print_encap_type_vxlan (stream, column_offset, &tto->bgpinfo.vxlan);
+      break;
+
+    case BGP_ENCAP_TYPE_NVGRE:
+      print_encap_type_nvgre (stream, column_offset, &tto->bgpinfo.nvgre);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS:
+      print_encap_type_mpls (stream, column_offset, &tto->bgpinfo.mpls);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS_IN_GRE:
+      print_encap_type_mpls_in_gre (stream, column_offset,
+                                    &tto->bgpinfo.mpls_gre);
+      break;
+
+    case BGP_ENCAP_TYPE_VXLAN_GPE:
+      print_encap_type_vxlan_gpe (stream, column_offset,
+                                  &tto->bgpinfo.vxlan_gpe);
+      break;
+
+    case BGP_ENCAP_TYPE_MPLS_IN_UDP:
+      print_encap_type_mpls_in_udp (stream, column_offset,
+                                    &tto->bgpinfo.mpls_udp);
+      break;
+
+    case BGP_ENCAP_TYPE_PBB:
+      print_encap_type_pbb (stream, column_offset, &tto->bgpinfo.pbb);
+      break;
+
+    default:
+      assert (0);
+    }
+}
diff --git a/bgpd/rfapi/rfapi_encap_tlv.h b/bgpd/rfapi/rfapi_encap_tlv.h
new file mode 100644 (file)
index 0000000..9678655
--- /dev/null
@@ -0,0 +1,43 @@
+/* 
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_ENCAP_TLV_H
+#define _QUAGGA_BGP_RFAPI_ENCAP_TLV_H
+
+#define RFAPI_BGP_ENCAP_TYPE_DEFAULT BGP_ENCAP_TYPE_IP_IN_IP
+
+extern bgp_encap_types
+rfapi_tunneltype_option_to_tlv (
+  struct bgp                           *bgp,
+  struct rfapi_ip_addr                 *ea,
+  struct rfapi_tunneltype_option       *tto,
+  struct attr                          *attr,
+  int                                  always_add);
+
+extern struct rfapi_un_option *
+rfapi_encap_tlv_to_un_option (struct attr *attr);
+
+extern void
+rfapi_print_tunneltype_option (
+  void                                 *stream,
+  int                                  column_offset,
+  struct rfapi_tunneltype_option       *tto);
+
+
+#endif /* _QUAGGA_BGP_RFAPI_ENCAP_TLV_H */
diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c
new file mode 100644 (file)
index 0000000..63c1079
--- /dev/null
@@ -0,0 +1,5154 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/*
+ * File:       rfapi_import.c
+ * Purpose:    Handle import of routes from BGP to RFAPI
+ */
+
+#include <errno.h>
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "memory.h"
+#include "log.h"
+#include "skiplist.h"
+#include "thread.h"
+
+#include "bgpd.h"
+#include "bgp_ecommunity.h"
+#include "bgp_attr.h"
+#include "bgp_route.h"
+#include "bgp_mplsvpn.h"        /* prefix_rd2str() */
+#include "bgp_vnc_types.h"
+
+#include "rfapi.h"
+#include "bgp_rfapi_cfg.h"
+#include "rfapi_backend.h"
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_monitor.h"
+#include "rfapi_nve_addr.h"
+#include "rfapi_vty.h"
+#include "vnc_export_bgp.h"
+#include "vnc_export_bgp_p.h"
+#include "vnc_zebra.h"
+#include "vnc_import_bgp.h"
+#include "vnc_import_bgp_p.h"
+#include "rfapi_rib.h"
+#include "rfapi_encap_tlv.h"
+#include "vnc_debug.h"
+
+#ifdef HAVE_GLIBC_BACKTRACE
+/* for backtrace and friends */
+#include <execinfo.h>
+#endif /* HAVE_GLIBC_BACKTRACE */
+
+#undef DEBUG_MONITOR_MOVE_SHORTER
+#undef DEBUG_RETURNED_NHL
+#undef DEBUG_ROUTE_COUNTERS
+#undef DEBUG_ENCAP_MONITOR
+#undef DEBUG_L2_EXTRA
+#undef DEBUG_IT_NODES
+#undef DEBUG_BI_SEARCH
+
+/*
+ * Allocated for each withdraw timer instance; freed when the timer
+ * expires or is canceled
+ */
+struct rfapi_withdraw
+{
+  struct rfapi_import_table *import_table;
+  struct route_node *node;
+  struct bgp_info *info;
+  safi_t safi;                  /* used only for bulk operations */
+  /*
+   * For import table node reference count checking (i.e., debugging).
+   * Normally when a timer expires, lockoffset should be 0. However, if
+   * the timer expiration function is called directly (e.g.,
+   * rfapiExpireVpnNow), the node could be locked by a preceding
+   * route_top() or route_next() in a loop, so we need to pass this
+   * value in.
+   */
+  int lockoffset;
+};
+
+/* 
+ * DEBUG FUNCTION
+ * It's evil and fiendish. It's compiler-dependent.
+ * ? Might need LDFLAGS -rdynamic to produce all function names
+ */
+void
+rfapiDebugBacktrace (void)
+{
+#ifdef HAVE_GLIBC_BACKTRACE
+#define RFAPI_DEBUG_BACKTRACE_NENTRIES 200
+  void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES];
+  char **syms;
+  size_t i;
+  size_t size;
+
+  size = backtrace (buf, RFAPI_DEBUG_BACKTRACE_NENTRIES);
+  syms = backtrace_symbols (buf, size);
+
+  for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES; ++i)
+    {
+      zlog_debug ("backtrace[%2lu]: %s", i, syms[i]);
+    }
+
+  free (syms);
+#else
+#endif
+}
+
+/*
+ * DEBUG FUNCTION
+ * Count remote routes and compare with actively-maintained values.
+ * Abort if they disagree.
+ */
+void
+rfapiCheckRouteCount ()
+{
+  struct bgp *bgp = bgp_get_default ();
+  struct rfapi *h;
+  struct rfapi_import_table *it;
+  afi_t afi;
+
+  assert (bgp);
+
+  h = bgp->rfapi;
+  assert (h);
+
+  for (it = h->imports; it; it = it->next)
+    {
+      for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+        {
+
+          struct route_table *rt;
+          struct route_node *rn;
+
+          int holddown_count = 0;
+          int local_count = 0;
+          int imported_count = 0;
+          int remote_count = 0;
+
+          rt = it->imported_vpn[afi];
+
+          for (rn = route_top (rt); rn; rn = route_next (rn))
+            {
+              struct bgp_info *bi;
+              struct bgp_info *next;
+
+              for (bi = rn->info; bi; bi = next)
+                {
+                  next = bi->next;
+
+                  if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+                    {
+                      ++holddown_count;
+
+                    }
+                  else
+                    {
+                      if (RFAPI_LOCAL_BI (bi))
+                        {
+                          ++local_count;
+                        }
+                      else
+                        {
+                          if (RFAPI_DIRECT_IMPORT_BI (bi))
+                            {
+                              ++imported_count;
+                            }
+                          else
+                            {
+                              ++remote_count;
+                            }
+                        }
+                    }
+                }
+            }
+
+          if (it->holddown_count[afi] != holddown_count)
+            {
+              zlog_debug ("%s: it->holddown_count %d != holddown_count %d",
+                          __func__, it->holddown_count[afi], holddown_count);
+              assert (0);
+            }
+          if (it->remote_count[afi] != remote_count)
+            {
+              zlog_debug ("%s: it->remote_count %d != remote_count %d",
+                          __func__, it->remote_count[afi], remote_count);
+              assert (0);
+            }
+          if (it->imported_count[afi] != imported_count)
+            {
+              zlog_debug ("%s: it->imported_count %d != imported_count %d",
+                          __func__, it->imported_count[afi], imported_count);
+              assert (0);
+            }
+        }
+    }
+}
+
+#if DEBUG_ROUTE_COUNTERS
+#define VNC_ITRCCK do {rfapiCheckRouteCount();} while (0)
+#else
+#define VNC_ITRCCK
+#endif
+
+/*
+ * Validate reference count for a node in an import table
+ *
+ * Normally lockoffset is 0 for nodes in quiescent state. However,
+ * route_unlock_node will delete the node if it is called when
+ * node->lock == 1, and we have to validate the refcount before
+ * the node is deleted. In this case, we specify lockoffset 1.
+ */
+void
+rfapiCheckRefcount (struct route_node *rn, safi_t safi, int lockoffset)
+{
+  unsigned int count_bi = 0;
+  unsigned int count_monitor = 0;
+  struct bgp_info *bi;
+  struct rfapi_monitor_encap *hme;
+  struct rfapi_monitor_vpn *hmv;
+
+  for (bi = rn->info; bi; bi = bi->next)
+    ++count_bi;
+
+
+  if (rn->aggregate)
+    {
+      ++count_monitor;          /* rfapi_it_extra */
+
+      switch (safi)
+        {
+          void *cursor;
+          int rc;
+
+        case SAFI_ENCAP:
+          for (hme = RFAPI_MONITOR_ENCAP (rn); hme; hme = hme->next)
+            ++count_monitor;
+          break;
+
+        case SAFI_MPLS_VPN:
+
+          for (hmv = RFAPI_MONITOR_VPN (rn); hmv; hmv = hmv->next)
+            ++count_monitor;
+
+          if (RFAPI_MONITOR_EXTERIOR (rn)->source)
+            {
+              ++count_monitor;  /* sl */
+              cursor = NULL;
+              for (rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn)->source,
+                                       NULL, NULL, &cursor);
+                   !rc;
+                   rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn)->source,
+                                       NULL, NULL, &cursor))
+                {
+
+                  ++count_monitor;      /* sl entry */
+                }
+            }
+          break;
+
+        default:
+          assert (0);
+        }
+    }
+
+  if (count_bi + count_monitor + lockoffset != rn->lock)
+    {
+      zlog_debug
+        ("%s: count_bi=%d, count_monitor=%d, lockoffset=%d, rn->lock=%d",
+         __func__, count_bi, count_monitor, lockoffset, rn->lock);
+      assert (0);
+    }
+}
+
+/*
+ * Perform deferred rfapi_close operations that were queued
+ * during callbacks.
+ */
+static wq_item_status
+rfapi_deferred_close_workfunc (struct work_queue *q, void *data)
+{
+  struct rfapi_descriptor *rfd = data;
+  struct rfapi *h = q->spec.data;
+
+  assert (!(h->flags & RFAPI_INCALLBACK));
+  rfapi_close (rfd);
+  zlog_debug ("%s: completed deferred close on handle %p", __func__, rfd);
+  return WQ_SUCCESS;
+}
+
+/*
+ * Extract layer 2 option from Encap TLVS in BGP attrs
+ */
+int
+rfapiGetL2o (struct attr *attr, struct rfapi_l2address_option *l2o)
+{
+  if (attr && attr->extra)
+    {
+
+      struct bgp_attr_encap_subtlv *pEncap;
+
+      for (pEncap = attr->extra->vnc_subtlvs; pEncap; pEncap = pEncap->next)
+        {
+
+          if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION)
+            {
+              if (pEncap->value[0] == RFAPI_VN_OPTION_TYPE_L2ADDR)
+                {
+
+                  if (pEncap->value[1] == 14)
+                    {
+                      memcpy (l2o->macaddr.octet, pEncap->value + 2,
+                              ETHER_ADDR_LEN);
+                      l2o->label =
+                        ((pEncap->value[10] >> 4) & 0x0f) +
+                        ((pEncap->value[9] << 4) & 0xff0) +
+                        ((pEncap->value[8] << 12) & 0xff000);
+
+                      l2o->local_nve_id = pEncap->value[12];
+
+                      l2o->logical_net_id =
+                        (pEncap->value[15] & 0xff) +
+                        ((pEncap->value[14] << 8) & 0xff00) +
+                        ((pEncap->value[13] << 16) & 0xff0000);
+                    }
+
+                  return 0;
+                }
+            }
+        }
+    }
+
+  return ENOENT;
+}
+
+/*
+ * Extract the lifetime from the Tunnel Encap attribute of a route in
+ * an import table
+ */
+int
+rfapiGetVncLifetime (struct attr *attr, uint32_t * lifetime)
+{
+  struct bgp_attr_encap_subtlv *pEncap;
+
+  *lifetime = RFAPI_INFINITE_LIFETIME;        /* default to infinite */
+
+  if (attr && attr->extra)
+    {
+
+      for (pEncap = attr->extra->vnc_subtlvs; pEncap; pEncap = pEncap->next)
+        {
+
+          if (pEncap->type == BGP_VNC_SUBTLV_TYPE_LIFETIME)
+            {                   /* lifetime */
+              if (pEncap->length == 4)
+                {
+                  memcpy (lifetime, pEncap->value, 4);
+                  *lifetime = ntohl (*lifetime);
+                  return 0;
+                }
+            }
+        }
+    }
+
+  return ENOENT;
+}
+
+/*
+ * Extract the tunnel type from the extended community
+ */
+int
+rfapiGetTunnelType (struct attr     *attr, 
+                    bgp_encap_types *type)
+{
+  *type = BGP_ENCAP_TYPE_MPLS;  /* default to MPLS */
+  if (attr && attr->extra && attr->extra->ecommunity)
+    {
+      struct ecommunity *ecom = attr->extra->ecommunity;
+      int i;
+
+      for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); i += ECOMMUNITY_SIZE)
+        {
+          uint8_t *ep;
+
+          ep = ecom->val + i;
+          if (ep[0] == ECOMMUNITY_ENCODE_OPAQUE &&
+              ep[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)
+            {
+              *type = (ep[6]<<8) + ep[7];
+              return 0;
+            }
+        }
+    }
+
+  return ENOENT;
+}
+
+
+/*
+ * Look for UN address in Encap attribute
+ */
+int
+rfapiGetVncTunnelUnAddr (struct attr *attr, struct prefix *p)
+{
+  struct bgp_attr_encap_subtlv *pEncap;
+  bgp_encap_types               tun_type;
+  
+  rfapiGetTunnelType (attr, &tun_type);
+  if (p && tun_type == BGP_ENCAP_TYPE_MPLS) 
+    {
+      /* MPLS carries UN address in next hop */
+      rfapiNexthop2Prefix (attr, p);
+      if (p->family != 0)
+        return 0;
+    }
+  if (attr && attr->extra)
+    {
+      for (pEncap = attr->extra->encap_subtlvs; pEncap; pEncap = pEncap->next)
+        {
+
+          if (pEncap->type == BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT)
+            {                   /* un addr */
+              switch (pEncap->length)
+                {
+                case 8:
+                  if (p)
+                    {
+                      p->family = AF_INET;
+                      p->prefixlen = 32;
+                      memcpy (p->u.val, pEncap->value, 4);
+                    }
+                  return 0;
+
+                case 20:
+                  if (p)
+                    {
+                      p->family = AF_INET6;
+                      p->prefixlen = 128;
+                      memcpy (p->u.val, pEncap->value, 16);
+                    }
+                  return 0;
+                }
+            }
+        }
+    }
+
+  return ENOENT;
+}
+
+/*
+ * Get UN address wherever it might be
+ */
+int
+rfapiGetUnAddrOfVpnBi (struct bgp_info *bi, struct prefix *p)
+{
+  /* If it's in this route's VNC attribute, we're done */
+  if (!rfapiGetVncTunnelUnAddr (bi->attr, p))
+    return 0;
+  /*
+   * Otherwise, see if it's cached from a corresponding ENCAP SAFI
+   * advertisement
+   */
+  if (bi->extra)
+    {
+      switch (bi->extra->vnc.import.un_family)
+        {
+        case AF_INET:
+          if (p)
+            {
+              p->family = bi->extra->vnc.import.un_family;
+              p->u.prefix4 = bi->extra->vnc.import.un.addr4;
+              p->prefixlen = 32;
+            }
+          return 0;
+        case AF_INET6:
+          if (p)
+            {
+              p->family = bi->extra->vnc.import.un_family;
+              p->u.prefix6 = bi->extra->vnc.import.un.addr6;
+              p->prefixlen = 128;
+            }
+          return 0;
+        default:
+          if (p)
+            p->family = 0;
+#if DEBUG_ENCAP_MONITOR
+          zlog_debug ("%s: bi->extra->vnc.import.un_family is 0, no UN addr",
+                      __func__);
+#endif
+          break;
+        }
+    }
+
+  return ENOENT;
+}
+
+
+/*
+ * Make a new bgp_info from gathered parameters
+ */
+static struct bgp_info *
+rfapiBgpInfoCreate (
+  struct attr          *attr,
+  struct peer          *peer,
+  void                 *rfd,
+  struct prefix_rd     *prd,
+  u_char               type,
+  u_char               sub_type,
+  uint32_t             *label)
+{
+  struct bgp_info *new;
+
+  new = bgp_info_new ();
+  assert (new);
+
+  if (attr)
+    {
+      if (!new->attr)
+        new->attr = bgp_attr_intern (attr);
+    }
+  bgp_info_extra_get (new);
+  if (prd)
+    {
+      new->extra->vnc.import.rd = *prd;
+      rfapi_time (&new->extra->vnc.import.create_time);
+    }
+  if (label)
+    encode_label (*label, new->extra->tag);
+  new->type = type;
+  new->sub_type = sub_type;
+  new->peer = peer;
+  peer_lock (peer);
+
+  return new;
+}
+
+/*
+ * Frees bgp_info as used in import tables (parts are not
+ * allocated exactly the way they are in the main RIBs)
+ */
+static void
+rfapiBgpInfoFree (struct bgp_info *goner)
+{
+  if (!goner)
+    return;
+
+  if (goner->peer)
+    {
+      zlog_debug ("%s: calling peer_unlock(%p), #%d",
+                  __func__, goner->peer, goner->peer->lock);
+      peer_unlock (goner->peer);
+    }
+
+  if (goner->attr)
+    {
+      bgp_attr_unintern (&goner->attr);
+    }
+  if (goner->extra)
+    {
+      assert (!goner->extra->damp_info);        /* Not used in import tbls */
+      XFREE (MTYPE_BGP_ROUTE_EXTRA, goner->extra);
+      goner->extra = NULL;
+    }
+  XFREE (MTYPE_BGP_ROUTE, goner);
+}
+
+struct rfapi_import_table *
+rfapiMacImportTableGetNoAlloc (struct bgp *bgp, uint32_t lni)
+{
+  struct rfapi *h;
+  struct rfapi_import_table *it = NULL;
+  uintptr_t lni_as_ptr = lni;
+
+  h = bgp->rfapi;
+  if (!h)
+    return NULL;
+
+  if (!h->import_mac)
+    return NULL;
+
+  if (skiplist_search (h->import_mac, (void *) lni_as_ptr, (void **) &it))
+    return NULL;
+
+  return it;
+}
+
+struct rfapi_import_table *
+rfapiMacImportTableGet (struct bgp *bgp, uint32_t lni)
+{
+  struct rfapi *h;
+  struct rfapi_import_table *it = NULL;
+  uintptr_t lni_as_ptr = lni;
+
+  h = bgp->rfapi;
+  assert (h);
+
+  if (!h->import_mac)
+    {
+      /* default cmp is good enough for LNI */
+      h->import_mac = skiplist_new (0, NULL, NULL);
+    }
+
+  if (skiplist_search (h->import_mac, (void *) lni_as_ptr, (void **) &it))
+    {
+
+      struct ecommunity *enew;
+      struct ecommunity_val eval;
+      afi_t afi;
+
+      it =
+        XCALLOC (MTYPE_RFAPI_IMPORTTABLE, sizeof (struct rfapi_import_table));
+      /* set RT list of new import table based on LNI */
+      memset ((char *) &eval, 0, sizeof (eval));
+      eval.val[0] = 0;          /* VNC L2VPN */
+      eval.val[1] = 2;          /* VNC L2VPN */
+      eval.val[5] = (lni >> 16) & 0xff;
+      eval.val[6] = (lni >> 8) & 0xff;
+      eval.val[7] = (lni >> 0) & 0xff;
+
+      enew = ecommunity_new ();
+      ecommunity_add_val (enew, &eval);
+      it->rt_import_list = enew;
+
+      for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+        {
+          it->imported_vpn[afi] = route_table_init ();
+          it->imported_encap[afi] = route_table_init ();
+        }
+
+      it->l2_logical_net_id = lni;
+
+      skiplist_insert (h->import_mac, (void *) lni_as_ptr, it);
+    }
+
+  assert (it);
+  return it;
+}
+
+/*
+ * Implement MONITOR_MOVE_SHORTER(original_node) from
+ * RFAPI-Import-Event-Handling.txt
+ * 
+ * Returns pointer to the list of moved monitors
+ */
+static struct rfapi_monitor_vpn *
+rfapiMonitorMoveShorter (struct route_node *original_vpn_node, int lockoffset)
+{
+  struct bgp_info *bi;
+  struct route_node *par;
+  struct rfapi_monitor_vpn *m;
+  struct rfapi_monitor_vpn *mlast;
+  struct rfapi_monitor_vpn *moved;
+  int movecount = 0;
+  int parent_already_refcounted = 0;
+
+  RFAPI_CHECK_REFCOUNT (original_vpn_node, SAFI_MPLS_VPN, lockoffset);
+
+#if DEBUG_MONITOR_MOVE_SHORTER
+  {
+    char buf[BUFSIZ];
+
+    prefix2str (&original_vpn_node->p, buf, BUFSIZ);
+    buf[BUFSIZ - 1] = 0;
+    zlog_debug ("%s: called with node pfx=%s", __func__, buf);
+  }
+#endif
+
+  /*
+   * 1. If there is at least one bi (either regular route or
+   *    route marked as withdrawn, with a pending timer) at
+   *    original_node with a valid UN address, we're done. Return.
+   */
+  for (bi = original_vpn_node->info; bi; bi = bi->next)
+    {
+      struct prefix pfx;
+
+      if (!rfapiGetUnAddrOfVpnBi (bi, &pfx))
+        {
+#if DEBUG_MONITOR_MOVE_SHORTER
+          zlog_debug ("%s: have valid UN at original node, no change",
+                      __func__);
+#endif
+          return NULL;
+        }
+    }
+
+  /*
+   * 2. Travel up the tree (toward less-specific prefixes) from
+   *    original_node to find the first node that has at least
+   *    one route (even if it is only a withdrawn route) with a
+   *    valid UN address. Call this node "Node P."
+   */
+  for (par = original_vpn_node->parent; par; par = par->parent)
+    {
+      for (bi = par->info; bi; bi = bi->next)
+        {
+          struct prefix pfx;
+          if (!rfapiGetUnAddrOfVpnBi (bi, &pfx))
+            {
+              break;
+            }
+        }
+      if (bi)
+        break;
+    }
+
+  if (par)
+    {
+      RFAPI_CHECK_REFCOUNT (par, SAFI_MPLS_VPN, 0);
+    }
+
+  /*
+   * If no less-specific routes, try to use the 0/0 node
+   */
+  if (!par)
+    {
+      /* this isn't necessarily 0/0 */
+      par = route_top (original_vpn_node->table);
+
+      /*
+       * If we got the top node but it wasn't 0/0,
+       * ignore it
+       */
+      if (par && par->p.prefixlen)
+        {
+          route_unlock_node (par);      /* maybe free */
+          par = NULL;
+        }
+
+      if (par)
+        {
+          ++parent_already_refcounted;
+        }
+    }
+
+  /* 
+   * Create 0/0 node if it isn't there
+   */
+  if (!par)
+    {
+      struct prefix pfx_default;
+
+      memset (&pfx_default, 0, sizeof (pfx_default));
+      pfx_default.family = original_vpn_node->p.family;
+
+      /* creates default node if none exists */
+      par = route_node_get (original_vpn_node->table, &pfx_default);
+      ++parent_already_refcounted;
+    }
+
+  /*
+   * 3. Move each of the monitors found at original_node to Node P.
+   *    These are "Moved Monitors."
+   *
+   */
+
+  /*
+   * Attach at end so that the list pointer we return points
+   * only to the moved routes
+   */
+  for (m = RFAPI_MONITOR_VPN (par), mlast = NULL; m; mlast = m, m = m->next);
+
+  if (mlast)
+    {
+      moved = mlast->next = RFAPI_MONITOR_VPN (original_vpn_node);
+    }
+  else
+    {
+      moved = RFAPI_MONITOR_VPN_W_ALLOC (par) =
+        RFAPI_MONITOR_VPN (original_vpn_node);
+    }
+  if (RFAPI_MONITOR_VPN (original_vpn_node))    /* check agg, so not allocated */
+    RFAPI_MONITOR_VPN_W_ALLOC (original_vpn_node) = NULL;
+
+  /*
+   * update the node pointers on the monitors
+   */
+  for (m = moved; m; m = m->next)
+    {
+      ++movecount;
+      m->node = par;
+    }
+
+  RFAPI_CHECK_REFCOUNT (par, SAFI_MPLS_VPN,
+                        parent_already_refcounted - movecount);
+  while (movecount > parent_already_refcounted)
+    {
+      route_lock_node (par);
+      ++parent_already_refcounted;
+    }
+  while (movecount < parent_already_refcounted)
+    {
+      /* unlikely, but code defensively */
+      route_unlock_node (par);
+      --parent_already_refcounted;
+    }
+  RFAPI_CHECK_REFCOUNT (original_vpn_node, SAFI_MPLS_VPN,
+                        movecount + lockoffset);
+  while (movecount--)
+    {
+      route_unlock_node (original_vpn_node);
+    }
+
+#if DEBUG_MONITOR_MOVE_SHORTER
+  {
+    char buf[BUFSIZ];
+
+    prefix2str (&par->p, buf, BUFSIZ);
+    buf[BUFSIZ - 1] = 0;
+    zlog_debug ("%s: moved to node pfx=%s", __func__, buf);
+  }
+#endif
+
+
+  return moved;
+}
+
+/*
+ * Implement MONITOR_MOVE_LONGER(new_node) from
+ * RFAPI-Import-Event-Handling.txt
+ */
+static void
+rfapiMonitorMoveLonger (struct route_node *new_vpn_node)
+{
+  struct rfapi_monitor_vpn *monitor;
+  struct rfapi_monitor_vpn *mlast;
+  struct bgp_info *bi;
+  struct route_node *par;
+
+  RFAPI_CHECK_REFCOUNT (new_vpn_node, SAFI_MPLS_VPN, 0);
+
+  /*
+   * Make sure we have at least one valid route at the new node
+   */
+  for (bi = new_vpn_node->info; bi; bi = bi->next)
+    {
+      struct prefix pfx;
+      if (!rfapiGetUnAddrOfVpnBi (bi, &pfx))
+        break;
+    }
+
+  if (!bi)
+    {
+      zlog_debug ("%s: no valid routes at node %p, so not attempting moves",
+                  __func__, new_vpn_node);
+      return;
+    }
+
+  /*
+   * Find first parent node that has monitors
+   */
+  for (par = new_vpn_node->parent; par; par = par->parent)
+    {
+      if (RFAPI_MONITOR_VPN (par))
+        break;
+    }
+
+  if (!par)
+    {
+      zlog_debug ("%s: no parent nodes with monitors, done", __func__);
+      return;
+    }
+
+  /*
+   * Check each of these monitors to see of their longest-match
+   * is now the updated node. Move any such monitors to the more-
+   * specific updated node
+   */
+  for (mlast = NULL, monitor = RFAPI_MONITOR_VPN (par); monitor;)
+    {
+
+      /*
+       * If new longest match for monitor prefix is the new
+       * route's prefix, move monitor to new route's prefix
+       */
+      if (prefix_match (&new_vpn_node->p, &monitor->p))
+        {
+          /* detach */
+          if (mlast)
+            {
+              mlast->next = monitor->next;
+            }
+          else
+            {
+              RFAPI_MONITOR_VPN_W_ALLOC (par) = monitor->next;
+            }
+
+
+          /* attach */
+          monitor->next = RFAPI_MONITOR_VPN (new_vpn_node);
+          RFAPI_MONITOR_VPN_W_ALLOC (new_vpn_node) = monitor;
+          monitor->node = new_vpn_node;
+
+          route_lock_node (new_vpn_node);       /* incr refcount */
+
+          monitor = mlast ? mlast->next : RFAPI_MONITOR_VPN (par);
+
+          RFAPI_CHECK_REFCOUNT (par, SAFI_MPLS_VPN, 1);
+          /* decr refcount after we're done with par as this might free it */
+          route_unlock_node (par);
+
+          continue;
+        }
+      mlast = monitor;
+      monitor = monitor->next;
+    }
+
+  RFAPI_CHECK_REFCOUNT (new_vpn_node, SAFI_MPLS_VPN, 0);
+}
+
+
+static void
+rfapiBgpInfoChainFree (struct bgp_info *bi)
+{
+  struct bgp_info *next;
+
+  while (bi)
+    {
+
+      /*
+       * If there is a timer waiting to delete this bi, cancel
+       * the timer and delete immediately
+       */
+      if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) &&
+          bi->extra->vnc.import.timer)
+        {
+
+          struct thread *t = (struct thread *) bi->extra->vnc.import.timer;
+          struct rfapi_withdraw *wcb = t->arg;
+
+          XFREE (MTYPE_RFAPI_WITHDRAW, wcb);
+          thread_cancel (t);
+        }
+
+      next = bi->next;
+      bi->next = NULL;
+      rfapiBgpInfoFree (bi);
+      bi = next;
+    }
+}
+
+static void
+rfapiImportTableFlush (struct rfapi_import_table *it)
+{
+  afi_t afi;
+
+  /*
+   * Free ecommunity
+   */
+  ecommunity_free (&it->rt_import_list);
+  it->rt_import_list = NULL;
+
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+
+      struct route_node *rn;
+
+      for (rn = route_top (it->imported_vpn[afi]); rn; rn = route_next (rn))
+        {
+          /*
+           * Each route_node has:
+           * aggregate: points to rfapi_it_extra with monitor chain(s)
+           * info: points to chain of bgp_info
+           */
+          /* free bgp_info and its children */
+          rfapiBgpInfoChainFree (rn->info);
+          rn->info = NULL;
+
+          rfapiMonitorExtraFlush (SAFI_MPLS_VPN, rn);
+        }
+
+      for (rn = route_top (it->imported_encap[afi]); rn; rn = route_next (rn))
+        {
+          /* free bgp_info and its children */
+          rfapiBgpInfoChainFree (rn->info);
+          rn->info = NULL;
+
+          rfapiMonitorExtraFlush (SAFI_ENCAP, rn);
+        }
+
+      route_table_finish (it->imported_vpn[afi]);
+      route_table_finish (it->imported_encap[afi]);
+    }
+  if (it->monitor_exterior_orphans)
+    {
+      skiplist_free (it->monitor_exterior_orphans);
+    }
+}
+
+void
+rfapiImportTableRefDelByIt (
+  struct bgp                   *bgp,
+  struct rfapi_import_table    *it_target)
+{
+  struct rfapi *h;
+  struct rfapi_import_table *it;
+  struct rfapi_import_table *prev = NULL;
+
+  assert (it_target);
+
+  h = bgp->rfapi;
+  assert (h);
+
+  for (it = h->imports; it; prev = it, it = it->next)
+    {
+      if (it == it_target)
+        break;
+    }
+
+  assert (it);
+  assert (it->refcount);
+
+  it->refcount -= 1;
+
+  if (!it->refcount)
+    {
+      if (prev)
+        {
+          prev->next = it->next;
+        }
+      else
+        {
+          h->imports = it->next;
+        }
+      rfapiImportTableFlush (it);
+      XFREE (MTYPE_RFAPI_IMPORTTABLE, it);
+    }
+}
+
+#if RFAPI_REQUIRE_ENCAP_BEEC
+/*
+ * Look for magic BGP Encapsulation Extended Community value
+ * Format in RFC 5512 Sect. 4.5
+ */
+static int
+rfapiEcommunitiesMatchBeec (struct ecommunity *ecom,
+                            bgp_encap_types    type)
+{
+  int i;
+
+  if (!ecom)
+    return 0;
+
+  for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); i += ECOMMUNITY_SIZE)
+    {
+
+      uint8_t *ep;
+
+      ep = ecom->val + i;
+
+      if (ep[0] == ECOMMUNITY_ENCODE_OPAQUE && 
+          ep[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP && 
+          ep[6] == ((type && 0xff00)>>8) &&
+          ep[7] == (type&0xff))
+        {
+
+          return 1;
+        }
+    }
+  return 0;
+
+}
+#endif
+
+int
+rfapiEcommunitiesIntersect (struct ecommunity *e1, struct ecommunity *e2)
+{
+  int i, j;
+
+  if (!e1 || !e2)
+    return 0;
+
+  {
+    char *s1, *s2;
+    s1 = ecommunity_ecom2str (e1, ECOMMUNITY_FORMAT_DISPLAY);
+    s2 = ecommunity_ecom2str (e2, ECOMMUNITY_FORMAT_DISPLAY);
+    zlog_debug ("%s: e1[%s], e2[%s]", __func__, s1, s2);
+    XFREE (MTYPE_ECOMMUNITY_STR, s1);
+    XFREE (MTYPE_ECOMMUNITY_STR, s2);
+  }
+
+  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;
+}
+
+int
+rfapiEcommunityGetLNI (struct ecommunity *ecom, uint32_t * lni)
+{
+  if (ecom)
+    {
+      int i;
+      for (i = 0; i < ecom->size; ++i)
+        {
+          uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE);
+
+          if ((*(p + 0) == 0x00) && (*(p + 1) == 0x02))
+            {
+
+              *lni = (*(p + 5) << 16) | (*(p + 6) << 8) | (*(p + 7));
+              return 0;
+            }
+        }
+    }
+  return ENOENT;
+}
+
+static int
+rfapiVpnBiNhEqualsPt (struct bgp_info *bi, struct rfapi_ip_addr *hpt)
+{
+  uint8_t family;
+
+  if (!hpt || !bi)
+    return 0;
+
+  family = BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len);
+
+  if (hpt->addr_family != family)
+    return 0;
+
+  switch (family)
+    {
+    case AF_INET:
+      if (bi->attr->extra->mp_nexthop_global_in.s_addr != hpt->addr.v4.s_addr)
+        return 0;
+      break;
+
+    case AF_INET6:
+      if (IPV6_ADDR_CMP (&bi->attr->extra->mp_nexthop_global, &hpt->addr.v6))
+        return 0;
+      break;
+
+    default:
+      return 0;
+      break;
+    }
+
+  return 1;
+}
+
+
+/*
+ * Compare 2 VPN BIs. Return true if they have the same VN and UN addresses
+ */
+static int
+rfapiVpnBiSamePtUn (struct bgp_info *bi1, struct bgp_info *bi2)
+{
+  struct prefix pfx_un1;
+  struct prefix pfx_un2;
+
+  if (!bi1 || !bi2)
+    return 0;
+
+  if (!bi1->attr || !bi2->attr)
+    return 0;
+
+  if (!bi1->attr->extra || !bi2->attr->extra)
+    return 0;
+
+  /*
+   * VN address comparisons
+   */
+
+  if (BGP_MP_NEXTHOP_FAMILY (bi1->attr->extra->mp_nexthop_len) !=
+      BGP_MP_NEXTHOP_FAMILY (bi2->attr->extra->mp_nexthop_len))
+    {
+      return 0;
+    }
+
+  switch (BGP_MP_NEXTHOP_FAMILY (bi1->attr->extra->mp_nexthop_len))
+    {
+
+    case AF_INET:
+      if (bi1->attr->extra->mp_nexthop_global_in.s_addr !=
+          bi2->attr->extra->mp_nexthop_global_in.s_addr)
+        return 0;
+      break;
+
+    case AF_INET6:
+      if (IPV6_ADDR_CMP (&bi1->attr->extra->mp_nexthop_global,
+                         &bi2->attr->extra->mp_nexthop_global))
+        return 0;
+      break;
+
+    default:
+      return 0;
+      break;
+    }
+
+  /*
+   * UN address comparisons
+   */
+  if (rfapiGetVncTunnelUnAddr (bi1->attr, &pfx_un1))
+    {
+      if (bi1->extra)
+        {
+          pfx_un1.family = bi1->extra->vnc.import.un_family;
+          switch (bi1->extra->vnc.import.un_family)
+            {
+            case AF_INET:
+              pfx_un1.u.prefix4 = bi1->extra->vnc.import.un.addr4;
+              break;
+            case AF_INET6:
+              pfx_un1.u.prefix6 = bi1->extra->vnc.import.un.addr6;
+              break;
+            default:
+              pfx_un1.family = 0;
+              break;
+            }
+        }
+    }
+
+  if (rfapiGetVncTunnelUnAddr (bi2->attr, &pfx_un2))
+    {
+      if (bi2->extra)
+        {
+          pfx_un2.family = bi2->extra->vnc.import.un_family;
+          switch (bi2->extra->vnc.import.un_family)
+            {
+            case AF_INET:
+              pfx_un2.u.prefix4 = bi2->extra->vnc.import.un.addr4;
+              break;
+            case AF_INET6:
+              pfx_un2.u.prefix6 = bi2->extra->vnc.import.un.addr6;
+              break;
+            default:
+              pfx_un2.family = 0;
+              break;
+            }
+        }
+    }
+
+  if (!pfx_un1.family || !pfx_un2.family)
+    return 0;
+
+  if (pfx_un1.family != pfx_un2.family)
+    return 0;
+
+  switch (pfx_un1.family)
+    {
+    case AF_INET:
+      if (!IPV4_ADDR_SAME
+          (&pfx_un1.u.prefix4.s_addr, &pfx_un2.u.prefix4.s_addr))
+        return 0;
+      break;
+    case AF_INET6:
+      if (!IPV6_ADDR_SAME (&pfx_un1.u.prefix6, &pfx_un2.u.prefix6))
+        return 0;
+      break;
+    }
+
+
+
+  return 1;
+}
+
+uint8_t
+rfapiRfpCost (struct attr * attr)
+{
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+    {
+      if (attr->local_pref > 255)
+        {
+          return 0;
+        }
+      return 255 - attr->local_pref;
+    }
+
+  return 255;
+}
+
+/*------------------------------------------
+ * rfapi_extract_l2o
+ *
+ * Find Layer 2 options in an option chain 
+ *
+ * input: 
+ *     pHop            option chain
+ *
+ * output:
+ *     l2o             layer 2 options extracted
+ *
+ * return value:
+ *     0               OK
+ *     1               no options found
+ *
+ --------------------------------------------*/
+int
+rfapi_extract_l2o (struct bgp_tea_options *pHop,        /* chain of options */
+                   struct rfapi_l2address_option *l2o)  /* return extracted value */
+{
+  struct bgp_tea_options *p;
+
+  for (p = pHop; p; p = p->next)
+    {
+      if ((p->type == RFAPI_VN_OPTION_TYPE_L2ADDR) && (p->length >= 8))
+        {
+
+          char *v = p->value;
+
+          memcpy (&l2o->macaddr, v, 6);
+
+          l2o->label =
+            ((v[6] << 12) & 0xff000) +
+            ((v[7] << 4) & 0xff0) + ((v[8] >> 4) & 0xf);
+
+          l2o->local_nve_id = (uint8_t) v[10];
+
+          l2o->logical_net_id = (v[11] << 16) + (v[12] << 8) + (v[13] << 0);
+
+          return 0;
+        }
+    }
+  return 1;
+}
+
+static struct rfapi_next_hop_entry *
+rfapiRouteInfo2NextHopEntry (
+  struct rfapi_ip_prefix       *rprefix,
+  struct bgp_info              *bi,      /* route to encode */
+  uint32_t                     lifetime, /* use this in nhe */
+  struct route_node            *rn)      /* req for L2 eth addr */
+{
+  struct rfapi_next_hop_entry *new;
+  int have_vnc_tunnel_un = 0;
+
+#if DEBUG_ENCAP_MONITOR
+  zlog_debug ("%s: entry, bi %p, rn %p", __func__, bi, rn);
+#endif
+
+  new = XCALLOC (MTYPE_RFAPI_NEXTHOP, sizeof (struct rfapi_next_hop_entry));
+  assert (new);
+
+  new->prefix = *rprefix;
+
+  if (bi->extra && 
+      decode_rd_type(bi->extra->vnc.import.rd.val) == RD_TYPE_VNC_ETH)
+    {
+      /* ethernet */
+
+      struct rfapi_vn_option *vo;
+
+      vo = XCALLOC (MTYPE_RFAPI_VN_OPTION, sizeof (struct rfapi_vn_option));
+      assert (vo);
+
+      vo->type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+
+      memcpy (&vo->v.l2addr.macaddr, &rn->p.u.prefix_eth.octet,
+              ETHER_ADDR_LEN);
+      /* only low 3 bytes of this are significant */
+      if (bi->attr && bi->attr->extra)
+        {
+          (void) rfapiEcommunityGetLNI (bi->attr->extra->ecommunity,
+                                        &vo->v.l2addr.logical_net_id);
+        }
+
+      /* local_nve_id comes from lower byte of RD type */
+      vo->v.l2addr.local_nve_id = bi->extra->vnc.import.rd.val[1];
+
+      /* label comes from MP_REACH_NLRI label */
+      vo->v.l2addr.label = decode_label (bi->extra->tag);
+
+      new->vn_options = vo;
+
+      /*
+       * If there is an auxiliary prefix (i.e., host IP address),
+       * use it as the nexthop prefix instead of the query prefix
+       */
+      if (bi->extra->vnc.import.aux_prefix.family)
+        {
+          rfapiQprefix2Rprefix (&bi->extra->vnc.import.aux_prefix,
+                                &new->prefix);
+        }
+    }
+
+  if (bi->attr)
+    {
+      bgp_encap_types  tun_type;
+      new->prefix.cost = rfapiRfpCost (bi->attr);
+
+      if (bi->attr->extra)
+        {
+
+          struct bgp_attr_encap_subtlv *pEncap;
+
+          switch (BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len))
+            {
+            case AF_INET:
+              new->vn_address.addr_family = AF_INET;
+              new->vn_address.addr.v4 = bi->attr->extra->mp_nexthop_global_in;
+              break;
+
+            case AF_INET6:
+              new->vn_address.addr_family = AF_INET6;
+              new->vn_address.addr.v6 = bi->attr->extra->mp_nexthop_global;
+              break;
+
+            default:
+              zlog_warn ("%s: invalid vpn nexthop length: %d",
+                         __func__, bi->attr->extra->mp_nexthop_len);
+              rfapi_free_next_hop_list (new);
+              return NULL;
+            }
+
+          for (pEncap = bi->attr->extra->vnc_subtlvs; pEncap;
+               pEncap = pEncap->next)
+            {
+              switch (pEncap->type)
+                {
+                case BGP_VNC_SUBTLV_TYPE_LIFETIME:
+                  /* use configured lifetime, not attr lifetime */
+                  break;
+
+                default:
+                  zlog_warn ("%s: unknown VNC option type %d",
+                             __func__, pEncap->type);
+
+
+                  break;
+                }
+            }
+
+          rfapiGetTunnelType (bi->attr, &tun_type);
+          if (tun_type == BGP_ENCAP_TYPE_MPLS) 
+            {
+              struct prefix p;
+              /* MPLS carries UN address in next hop */
+              rfapiNexthop2Prefix (bi->attr, &p);
+              if (p.family != 0) 
+                {
+                  rfapiQprefix2Raddr(&p, &new->un_address);
+                  have_vnc_tunnel_un = 1;
+                }
+            }
+
+          for (pEncap = bi->attr->extra->encap_subtlvs; pEncap;
+               pEncap = pEncap->next)
+            {
+              switch (pEncap->type)
+                {
+                case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+                  /*
+                   * Overrides ENCAP UN address, if any
+                   */
+                  switch (pEncap->length)
+                    {
+
+                    case 8:
+                      new->un_address.addr_family = AF_INET;
+                      memcpy (&new->un_address.addr.v4, pEncap->value, 4);
+                      have_vnc_tunnel_un = 1;
+                      break;
+
+                    case 20:
+                      new->un_address.addr_family = AF_INET6;
+                      memcpy (&new->un_address.addr.v6, pEncap->value, 16);
+                      have_vnc_tunnel_un = 1;
+                      break;
+
+                    default:
+                      zlog_warn
+                        ("%s: invalid tunnel subtlv UN addr length (%d) for bi %p",
+                         __func__, pEncap->length, bi);
+                    }
+                  break;
+
+                default:
+                  zlog_warn ("%s: unknown Encap Attribute option type %d",
+                             __func__, pEncap->type);
+
+
+                  break;
+                }
+            }
+
+          new->un_options = rfapi_encap_tlv_to_un_option (bi->attr);
+
+#if DEBUG_ENCAP_MONITOR
+          zlog_debug ("%s: line %d: have_vnc_tunnel_un=%d",
+                      __func__, __LINE__, have_vnc_tunnel_un);
+#endif
+
+          if (!have_vnc_tunnel_un && bi && bi->extra)
+            {
+              /*
+               * use cached UN address from ENCAP route
+               */
+              new->un_address.addr_family = bi->extra->vnc.import.un_family;
+              switch (new->un_address.addr_family)
+                {
+                case AF_INET:
+                  new->un_address.addr.v4 = bi->extra->vnc.import.un.addr4;
+                  break;
+                case AF_INET6:
+                  new->un_address.addr.v6 = bi->extra->vnc.import.un.addr6;
+                  break;
+                default:
+                  zlog_warn ("%s: invalid UN addr family (%d) for bi %p",
+                             __func__, new->un_address.addr_family, bi);
+                  rfapi_free_next_hop_list (new);
+                  return NULL;
+                  break;
+                }
+            }
+        }
+    }
+  new->lifetime = lifetime;
+  return new;
+}
+
+int
+rfapiHasNonRemovedRoutes (struct route_node *rn)
+{
+  struct bgp_info *bi;
+
+  for (bi = rn->info; bi; bi = bi->next)
+    {
+      struct prefix pfx;
+
+      if (!CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) &&
+          (bi->extra && !rfapiGetUnAddrOfVpnBi (bi, &pfx)))
+        {
+
+          return 1;
+        }
+    }
+  return 0;
+}
+
+#if DEBUG_IT_NODES
+/* 
+ * DEBUG FUNCTION
+ */
+void
+rfapiDumpNode (struct route_node *rn)
+{
+  struct bgp_info *bi;
+
+  zlog_debug ("%s: rn=%p", __func__, rn);
+  for (bi = rn->info; bi; bi = bi->next)
+    {
+      struct prefix pfx;
+      int ctrc = rfapiGetUnAddrOfVpnBi (bi, &pfx);
+      int nr;
+
+      if (!CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) && (bi->extra && !ctrc))
+        {
+
+          nr = 1;
+        }
+      else
+        {
+          nr = 0;
+        }
+
+      zlog_debug ("  bi=%p, nr=%d, flags=0x%x, extra=%p, ctrc=%d",
+                  bi, nr, bi->flags, bi->extra, ctrc);
+    }
+}
+#endif
+
+static int
+rfapiNhlAddNodeRoutes (
+  struct route_node            *rn,         /* in */
+  struct rfapi_ip_prefix       *rprefix,    /* in */
+  uint32_t                     lifetime,    /* in */
+  int                          removed,     /* in */
+  struct rfapi_next_hop_entry  **head,      /* in/out */
+  struct rfapi_next_hop_entry  **tail,      /* in/out */
+  struct rfapi_ip_addr         *exclude_vnaddr,  /* omit routes to same NVE */
+  struct route_node            *rfd_rib_node,/* preload this NVE rib node */
+  struct prefix                        *pfx_target_original) /* query target */
+{
+  struct bgp_info               *bi;
+  struct rfapi_next_hop_entry   *new;
+  struct prefix                 pfx_un;
+  struct skiplist               *seen_nexthops;
+  int                           count = 0;
+  int                           is_l2 = (rn->p.family == AF_ETHERNET);
+
+  if (rfapiRibFTDFilterRecentPrefix(
+    (struct rfapi_descriptor *)(rfd_rib_node->table->info), rn,
+    pfx_target_original))
+    {
+      return 0;
+    }
+
+  seen_nexthops =
+    skiplist_new (0, vnc_prefix_cmp, (void (*)(void *)) prefix_free);
+
+  for (bi = rn->info; bi; bi = bi->next)
+    {
+
+      struct prefix pfx_vn;
+      struct prefix *newpfx;
+
+      if (removed && !CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+        {
+#if DEBUG_RETURNED_NHL
+          zlog_debug ("%s: want holddown, this route not holddown, skip",
+                      __func__);
+#endif
+          continue;
+        }
+      if (!removed && CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+        {
+          continue;
+        }
+
+      if (!bi->extra)
+        {
+          continue;
+        }
+
+      /*
+       * Check for excluded VN address
+       */
+      if (rfapiVpnBiNhEqualsPt (bi, exclude_vnaddr))
+        continue;
+
+      /*
+       * Check for VN address (nexthop) copied already
+       */
+      if (is_l2)
+        {
+          /* L2 routes: semantic nexthop in aux_prefix; VN addr ain't it */
+          pfx_vn = bi->extra->vnc.import.aux_prefix;
+        }
+      else
+        {
+          rfapiNexthop2Prefix (bi->attr, &pfx_vn);
+        }
+      if (!skiplist_search (seen_nexthops, &pfx_vn, NULL))
+        {
+#if DEBUG_RETURNED_NHL
+          char buf[BUFSIZ];
+
+          prefix2str (&pfx_vn, buf, BUFSIZ);
+          buf[BUFSIZ - 1] = 0;  /* guarantee NUL-terminated */
+          zlog_debug ("%s: already put VN/nexthop %s, skip", __func__, buf);
+#endif
+          continue;
+        }
+
+      if (rfapiGetUnAddrOfVpnBi (bi, &pfx_un))
+        {
+#if DEBUG_ENCAP_MONITOR
+          zlog_debug ("%s: failed to get UN address of this VPN bi",
+                      __func__);
+#endif
+          continue;
+        }
+
+      newpfx = prefix_new ();
+      *newpfx = pfx_vn;
+      skiplist_insert (seen_nexthops, newpfx, newpfx);
+
+      new = rfapiRouteInfo2NextHopEntry(rprefix, bi, lifetime, rn);
+      if (new)
+       {
+         if (rfapiRibPreloadBi(rfd_rib_node, &pfx_vn, &pfx_un, lifetime, bi))
+           {
+             /* duplicate filtered by RIB */
+             rfapi_free_next_hop_list (new);
+             new = NULL;
+           }
+        }
+
+      if (new)
+        {
+          if (*tail)
+            {
+              (*tail)->next = new;
+            }
+          else
+            {
+              *head = new;
+            }
+          *tail = new;
+          ++count;
+        }
+    }
+
+  skiplist_free (seen_nexthops);
+
+  return count;
+}
+
+
+/*
+ * Breadth-first
+ *
+ * omit_node is meant for the situation where we are adding a subtree
+ * of a parent of some original requested node. The response already
+ * contains the original requested node, and we don't want to duplicate
+ * its routes in the list, so we skip it if the right or left node
+ * matches (of course, we still travel down its child subtrees).
+ */
+static int
+rfapiNhlAddSubtree (
+    struct route_node          *rn,            /* in */
+    uint32_t                   lifetime,       /* in */
+    struct rfapi_next_hop_entry        **head,         /* in/out */
+    struct rfapi_next_hop_entry        **tail,         /* in/out */
+    struct route_node          *omit_node,     /* in */
+    struct rfapi_ip_addr       *exclude_vnaddr,/* omit routes to same NVE */
+    struct route_table          *rfd_rib_table,/* preload here */
+    struct prefix              *pfx_target_original) /* query target */
+{
+  struct rfapi_ip_prefix rprefix;
+  int                    rcount = 0;
+
+  if (rn->l_left && rn->l_left != omit_node)
+    {
+      if (rn->l_left->info)
+        {
+          int               count = 0;
+          struct route_node *rib_rn = NULL;
+
+          rfapiQprefix2Rprefix (&rn->l_left->p, &rprefix);
+          if (rfd_rib_table)
+            {
+              rib_rn = route_node_get(rfd_rib_table, &rn->l_left->p);
+            }
+
+          count = rfapiNhlAddNodeRoutes (rn->l_left, &rprefix, lifetime, 0,
+            head, tail, exclude_vnaddr, rib_rn, pfx_target_original);
+          if (!count)
+            {
+              count = rfapiNhlAddNodeRoutes (rn->l_left, &rprefix, lifetime, 1,
+                head, tail, exclude_vnaddr, rib_rn, pfx_target_original);
+            }
+          rcount += count;
+         if (rib_rn)
+           route_unlock_node(rib_rn);
+        }
+    }
+
+  if (rn->l_right && rn->l_right != omit_node)
+    {
+      if (rn->l_right->info)
+        {
+          int               count = 0;
+          struct route_node *rib_rn = NULL;
+
+          rfapiQprefix2Rprefix (&rn->l_right->p, &rprefix);
+          if (rfd_rib_table)
+            {
+              rib_rn = route_node_get(rfd_rib_table, &rn->l_right->p);
+            }
+          count = rfapiNhlAddNodeRoutes (rn->l_right, &rprefix, lifetime, 0,
+            head, tail, exclude_vnaddr, rib_rn, pfx_target_original);
+          if (!count)
+            {
+              count = rfapiNhlAddNodeRoutes (rn->l_right, &rprefix, lifetime, 1,
+                head, tail, exclude_vnaddr, rib_rn, pfx_target_original);
+            }
+          rcount += count;
+         if (rib_rn)
+           route_unlock_node(rib_rn);
+        }
+    }
+
+  if (rn->l_left)
+    {
+      rcount += rfapiNhlAddSubtree (rn->l_left, lifetime, head, tail, omit_node,
+        exclude_vnaddr, rfd_rib_table, pfx_target_original);
+    }
+  if (rn->l_right)
+    {
+      rcount += rfapiNhlAddSubtree (rn->l_right, lifetime, head, tail,
+        omit_node, exclude_vnaddr, rfd_rib_table, pfx_target_original);
+    }
+
+  return rcount;
+}
+
+/*
+ * Implementation of ROUTE_LIST(node) from RFAPI-Import-Event-Handling.txt
+ *
+ * Construct an rfapi nexthop list based on the routes attached to
+ * the specified node.
+ *
+ * If there are any routes that do NOT have BGP_INFO_REMOVED set,
+ * return those only. If there are ONLY routes with BGP_INFO_REMOVED,
+ * then return those, and also include all the non-removed routes from the
+ * next less-specific node (i.e., this node's parent) at the end.
+ */
+struct rfapi_next_hop_entry *
+rfapiRouteNode2NextHopList (
+  struct route_node    *rn,
+  uint32_t             lifetime,       /* put into nexthop entries */
+  struct rfapi_ip_addr *exclude_vnaddr,/* omit routes to same NVE */
+  struct route_table   *rfd_rib_table,/* preload here */
+  struct prefix                *pfx_target_original) /* query target */
+{
+  struct rfapi_ip_prefix      rprefix;
+  struct rfapi_next_hop_entry *answer = NULL;
+  struct rfapi_next_hop_entry *last = NULL;
+  struct route_node           *parent;
+  int                         count = 0;
+  struct route_node           *rib_rn;
+
+#if DEBUG_RETURNED_NHL
+  {
+    char buf[BUFSIZ];
+
+    prefix2str (&rn->p, buf, BUFSIZ);
+    buf[BUFSIZ - 1] = 0;
+    zlog_debug ("%s: called with node pfx=%s", __func__, buf);
+  }
+  rfapiDebugBacktrace ();
+#endif
+
+  rfapiQprefix2Rprefix (&rn->p, &rprefix);
+
+  rib_rn = rfd_rib_table? route_node_get(rfd_rib_table, &rn->p): NULL;
+
+  /*
+   * Add non-withdrawn routes at this node
+   */
+  count = rfapiNhlAddNodeRoutes (rn, &rprefix, lifetime, 0, &answer, &last,
+    exclude_vnaddr, rib_rn, pfx_target_original);
+
+  /*
+   * If the list has at least one entry, it's finished
+   */
+  if (count)
+    {
+      count += rfapiNhlAddSubtree (rn, lifetime, &answer, &last, NULL,
+        exclude_vnaddr, rfd_rib_table, pfx_target_original);
+      zlog_debug ("%s: %d nexthops, answer=%p", __func__, count, answer);
+#if DEBUG_RETURNED_NHL
+      rfapiPrintNhl (NULL, answer);
+#endif
+      if (rib_rn)
+        route_unlock_node(rib_rn);
+      return answer;
+    }
+
+  /*
+   * Add withdrawn routes at this node
+   */
+  count = rfapiNhlAddNodeRoutes (rn, &rprefix, lifetime, 1, &answer, &last,
+    exclude_vnaddr, rib_rn, pfx_target_original);
+  if (rib_rn)
+    route_unlock_node(rib_rn);
+
+  // rfapiPrintNhl(NULL, answer);
+
+  /*
+   * walk up the tree until we find a node with non-deleted
+   * routes, then add them
+   */
+  for (parent = rn->parent; parent; parent = parent->parent)
+    {
+      if (rfapiHasNonRemovedRoutes (parent))
+        {
+          break;
+        }
+    }
+
+  /*
+   * Add non-withdrawn routes from less-specific prefix
+   */
+  if (parent)
+    {
+      rib_rn = rfd_rib_table? route_node_get(rfd_rib_table, &parent->p): NULL;
+      rfapiQprefix2Rprefix (&parent->p, &rprefix);
+      count += rfapiNhlAddNodeRoutes (parent, &rprefix, lifetime, 0,
+        &answer, &last, exclude_vnaddr, rib_rn, pfx_target_original);
+      count += rfapiNhlAddSubtree (parent, lifetime, &answer, &last, rn,
+        exclude_vnaddr, rfd_rib_table, pfx_target_original);
+      if (rib_rn)
+        route_unlock_node(rib_rn);
+    }
+  else
+    {
+      /*
+       * There is no parent with non-removed routes. Still need to
+       * add subtree of original node if it contributed routes to the
+       * answer.
+       */
+      if (count)
+        count += rfapiNhlAddSubtree (rn, lifetime, &answer, &last, rn,
+          exclude_vnaddr, rfd_rib_table, pfx_target_original);
+    }
+
+  zlog_debug ("%s: %d nexthops, answer=%p", __func__, count, answer);
+#if DEBUG_RETURNED_NHL
+  rfapiPrintNhl (NULL, answer);
+#endif
+  return answer;
+}
+
+/*
+ * Construct nexthop list of all routes in table
+ */
+struct rfapi_next_hop_entry *
+rfapiRouteTable2NextHopList (
+    struct route_table         *rt,
+    uint32_t                   lifetime,       /* put into nexthop entries */
+    struct rfapi_ip_addr       *exclude_vnaddr,/* omit routes to same NVE */
+    struct route_table         *rfd_rib_table, /* preload this NVE rib table */
+    struct prefix              *pfx_target_original) /* query target */
+{
+  struct route_node *rn;
+  struct rfapi_next_hop_entry *biglist = NULL;
+  struct rfapi_next_hop_entry *nhl;
+  struct rfapi_next_hop_entry *tail = NULL;
+  int count = 0;
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    {
+
+      nhl = rfapiRouteNode2NextHopList (rn, lifetime, exclude_vnaddr,
+       rfd_rib_table, pfx_target_original);
+      if (!tail)
+        {
+          tail = biglist = nhl;
+          if (tail)
+            count = 1;
+        }
+      else
+        {
+          tail->next = nhl;
+        }
+      if (tail)
+        {
+          while (tail->next)
+            {
+              ++count;
+              tail = tail->next;
+            }
+        }
+    }
+
+  zlog_debug ("%s: returning %d routes", __func__, count);
+  return biglist;
+}
+
+struct rfapi_next_hop_entry *
+rfapiEthRouteNode2NextHopList (
+  struct route_node            *rn,
+  struct rfapi_ip_prefix       *rprefix,
+  uint32_t                     lifetime,       /* put into nexthop entries */
+  struct rfapi_ip_addr         *exclude_vnaddr,/* omit routes to same NVE */
+  struct route_table           *rfd_rib_table,/* preload NVE rib table */
+  struct prefix                        *pfx_target_original) /* query target */
+{
+  int count = 0;
+  struct rfapi_next_hop_entry *answer = NULL;
+  struct rfapi_next_hop_entry *last = NULL;
+  struct route_node           *rib_rn;
+
+  rib_rn = rfd_rib_table? route_node_get(rfd_rib_table, &rn->p): NULL;
+
+  count = rfapiNhlAddNodeRoutes (rn, rprefix, lifetime, 0, &answer, &last,
+    NULL, rib_rn, pfx_target_original);
+
+#if DEBUG_ENCAP_MONITOR
+  zlog_debug ("%s: node %p: %d non-holddown routes", __func__, rn, count);
+#endif
+
+  if (!count)
+    {
+      count = rfapiNhlAddNodeRoutes (rn, rprefix, lifetime, 1, &answer, &last,
+       exclude_vnaddr, rib_rn, pfx_target_original);
+      zlog_debug ("%s: node %p: %d holddown routes", __func__, rn, count);
+    }
+
+    if (rib_rn)
+      route_unlock_node(rib_rn);
+
+#if DEBUG_RETURNED_NHL
+  rfapiPrintNhl (NULL, answer);
+#endif
+
+  return answer;
+}
+
+
+/*
+ * Construct nexthop list of all routes in table
+ */
+struct rfapi_next_hop_entry *
+rfapiEthRouteTable2NextHopList (
+  uint32_t                     logical_net_id,
+  struct rfapi_ip_prefix       *rprefix,
+  uint32_t                     lifetime,       /* put into nexthop entries */
+  struct rfapi_ip_addr         *exclude_vnaddr,/* omit routes to same NVE */
+  struct route_table           *rfd_rib_table, /* preload NVE rib node */
+  struct prefix                        *pfx_target_original) /* query target */
+{
+  struct rfapi_import_table *it;
+  struct bgp *bgp = bgp_get_default ();
+  struct route_table *rt;
+  struct route_node *rn;
+  struct rfapi_next_hop_entry *biglist = NULL;
+  struct rfapi_next_hop_entry *nhl;
+  struct rfapi_next_hop_entry *tail = NULL;
+  int count = 0;
+
+
+  it = rfapiMacImportTableGet (bgp, logical_net_id);
+  rt = it->imported_vpn[AFI_ETHER];
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    {
+
+      nhl = rfapiEthRouteNode2NextHopList(rn, rprefix, lifetime,
+       exclude_vnaddr, rfd_rib_table, pfx_target_original);
+      if (!tail)
+        {
+          tail = biglist = nhl;
+          if (tail)
+            count = 1;
+        }
+      else
+        {
+          tail->next = nhl;
+        }
+      if (tail)
+        {
+          while (tail->next)
+            {
+              ++count;
+              tail = tail->next;
+            }
+        }
+    }
+
+  zlog_debug ("%s: returning %d routes", __func__, count);
+  return biglist;
+}
+
+/*
+ * Insert a new bi to the imported route table node,
+ * keeping the list of BIs sorted best route first
+ */
+static void
+rfapiBgpInfoAttachSorted (
+  struct route_node    *rn,
+  struct bgp_info      *info_new,
+  afi_t                        afi,
+  safi_t               safi)
+{
+  struct bgp           *bgp;
+  struct bgp_info      *prev;
+  struct bgp_info      *next;
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+
+  if (VNC_DEBUG(IMPORT_BI_ATTACH))
+    {
+  zlog_debug ("%s: info_new->peer=%p", __func__, info_new->peer);
+  zlog_debug ("%s: info_new->peer->su_remote=%p", __func__,
+              info_new->peer->su_remote);
+    }
+
+  for (prev = NULL, next = rn->info; next; prev = next, next = next->next)
+    {
+      if (!bgp ||
+          (!CHECK_FLAG (info_new->flags, BGP_INFO_REMOVED) &&
+           CHECK_FLAG (next->flags, BGP_INFO_REMOVED)) ||
+          bgp_info_cmp_compatible (bgp, info_new, next, afi, safi) == -1)
+        {                       /* -1 if 1st is better */
+          break;
+        }
+    }
+  zlog_debug ("%s: prev=%p, next=%p", __func__, prev, next);
+  if (prev)
+    {
+      prev->next = info_new;
+    }
+  else
+    {
+      rn->info = info_new;
+    }
+  info_new->prev = prev;
+  info_new->next = next;
+  if (next)
+    next->prev = info_new;
+}
+
+static void
+rfapiBgpInfoDetach (struct route_node *rn, struct bgp_info *bi)
+{
+  /*
+   * Remove the route (doubly-linked)
+   */
+  if (bi->next)
+    bi->next->prev = bi->prev;
+  if (bi->prev)
+    bi->prev->next = bi->next;
+  else
+    rn->info = bi->next;
+}
+
+/*
+ * For L3-indexed import tables
+ */
+static int
+rfapi_bi_peer_rd_cmp (void *b1, void *b2)
+{
+  struct bgp_info *bi1 = b1;
+  struct bgp_info *bi2 = b2;
+
+  /*
+   * Compare peers
+   */
+  if (bi1->peer < bi2->peer)
+    return -1;
+  if (bi1->peer > bi2->peer)
+    return 1;
+
+  /*
+   * compare RDs
+   */
+  return vnc_prefix_cmp ((struct prefix *) &bi1->extra->vnc.import.rd,
+                         (struct prefix *) &bi2->extra->vnc.import.rd);
+}
+
+/*
+ * For L2-indexed import tables
+ * The BIs in these tables should ALWAYS have an aux_prefix set because
+ * they arrive via IPv4 or IPv6 advertisements.
+ */
+static int
+rfapi_bi_peer_rd_aux_cmp (void *b1, void *b2)
+{
+  struct bgp_info *bi1 = b1;
+  struct bgp_info *bi2 = b2;
+  int rc;
+
+  /*
+   * Compare peers
+   */
+  if (bi1->peer < bi2->peer)
+    return -1;
+  if (bi1->peer > bi2->peer)
+    return 1;
+
+  /*
+   * compare RDs
+   */
+  rc = vnc_prefix_cmp ((struct prefix *) &bi1->extra->vnc.import.rd,
+                       (struct prefix *) &bi2->extra->vnc.import.rd);
+  if (rc)
+    {
+      return rc;
+    }
+
+  /*
+   * L2 import tables can have multiple entries with the
+   * same MAC address, same RD, but different L3 addresses.
+   *
+   * Use presence of aux_prefix with AF=ethernet and prefixlen=1
+   * as magic value to signify explicit wildcarding of the aux_prefix.
+   * This magic value will not appear in bona fide bi entries in
+   * the import table, but is allowed in the "fake" bi used to
+   * probe the table when searching. (We have to test both b1 and b2
+   * because there is no guarantee of the order the test key and
+   * the real key will be passed)
+   */
+  if ((bi1->extra->vnc.import.aux_prefix.family == AF_ETHERNET &&
+       (bi1->extra->vnc.import.aux_prefix.prefixlen == 1)) ||
+      (bi2->extra->vnc.import.aux_prefix.family == AF_ETHERNET &&
+       (bi2->extra->vnc.import.aux_prefix.prefixlen == 1)))
+    {
+
+      /*
+       * wildcard aux address specified
+       */
+      return 0;
+    }
+
+  return vnc_prefix_cmp (&bi1->extra->vnc.import.aux_prefix,
+                         &bi2->extra->vnc.import.aux_prefix);
+}
+
+
+/*
+ * Index on RD and Peer
+ */
+static void
+rfapiItBiIndexAdd (
+  struct route_node    *rn,    /* Import table VPN node */
+  struct bgp_info      *bi)    /* new BI */
+{
+  struct skiplist *sl;
+
+  assert (rn);
+  assert (bi);
+  assert (bi->extra);
+
+  {
+    char buf[BUFSIZ];
+    prefix_rd2str (&bi->extra->vnc.import.rd, buf, BUFSIZ);
+    zlog_debug ("%s: bi %p, peer %p, rd %s", __func__, bi, bi->peer, buf);
+  }
+
+  sl = RFAPI_RDINDEX_W_ALLOC (rn);
+  if (!sl)
+    {
+      if (AF_ETHERNET == rn->p.family)
+        {
+          sl = skiplist_new (0, rfapi_bi_peer_rd_aux_cmp, NULL);
+        }
+      else
+        {
+          sl = skiplist_new (0, rfapi_bi_peer_rd_cmp, NULL);
+        }
+      RFAPI_IT_EXTRA_GET (rn)->u.vpn.idx_rd = sl;
+      route_lock_node (rn);     /* for skiplist */
+    }
+  assert (!skiplist_insert (sl, (void *) bi, (void *) bi));
+  route_lock_node (rn);         /* for skiplist entry */
+
+  /* NB: BIs in import tables are not refcounted */
+}
+
+static void
+rfapiItBiIndexDump (struct route_node *rn)
+{
+  struct skiplist *sl;
+  void *cursor = NULL;
+  struct bgp_info *k;
+  struct bgp_info *v;
+  int rc;
+
+  sl = RFAPI_RDINDEX (rn);
+  if (!sl)
+    return;
+
+  for (rc = skiplist_next (sl, (void **) &k, (void **) &v, &cursor);
+       !rc; rc = skiplist_next (sl, (void **) &k, (void **) &v, &cursor))
+    {
+
+      char buf[BUFSIZ];
+      char buf_aux_pfx[BUFSIZ];
+
+      prefix_rd2str (&k->extra->vnc.import.rd, buf, BUFSIZ);
+      buf_aux_pfx[0] = 0;
+      if (k->extra->vnc.import.aux_prefix.family)
+        {
+          prefix2str (&k->extra->vnc.import.aux_prefix, buf_aux_pfx, BUFSIZ);
+        }
+      else
+        {
+          strncpy (buf_aux_pfx, "(none)", BUFSIZ);
+          buf_aux_pfx[BUFSIZ - 1] = 0;
+        }
+
+      zlog_debug ("bi %p, peer %p, rd %s, aux_prefix %s", k, k->peer, buf,
+                  buf_aux_pfx);
+    }
+}
+
+static struct bgp_info *
+rfapiItBiIndexSearch (
+  struct route_node    *rn,            /* Import table VPN node */
+  struct prefix_rd     *prd,
+  struct peer          *peer,
+  struct prefix                *aux_prefix)    /* optional L3 addr for L2 ITs */
+{
+  struct skiplist      *sl;
+  int                  rc;
+  struct bgp_info      bi_fake;
+  struct bgp_info_extra        bi_extra;
+  struct bgp_info      *bi_result;
+
+  sl = RFAPI_RDINDEX (rn);
+  if (!sl)
+    return NULL;
+
+#if DEBUG_BI_SEARCH
+  {
+    char buf[BUFSIZ];
+    char buf_aux_pfx[BUFSIZ];
+
+    prefix_rd2str (prd, buf, BUFSIZ);
+    if (aux_prefix)
+      {
+        prefix2str (aux_prefix, buf_aux_pfx, BUFSIZ);
+      }
+    else
+      {
+        strncpy (buf_aux_pfx, "(nil)", BUFSIZ - 1);
+        buf_aux_pfx[BUFSIZ - 1] = 0;
+      }
+
+    zlog_debug ("%s want prd=%s, peer=%p, aux_prefix=%s",
+                __func__, buf, peer, buf_aux_pfx);
+    rfapiItBiIndexDump (rn);
+  }
+#endif
+
+  /* threshold is a WAG */
+  if (sl->count < 3)
+    {
+#if DEBUG_BI_SEARCH
+      zlog_debug ("%s: short list algorithm", __func__);
+#endif
+      /* if short list, linear search might be faster */
+      for (bi_result = rn->info; bi_result; bi_result = bi_result->next)
+        {
+#if DEBUG_BI_SEARCH
+          {
+            char buf[BUFSIZ];
+            prefix_rd2str (&bi_result->extra->vnc.import.rd, buf, BUFSIZ);
+            zlog_debug ("%s: bi has prd=%s, peer=%p", __func__,
+                        buf, bi_result->peer);
+          }
+#endif
+          if (peer == bi_result->peer &&
+              !prefix_cmp ((struct prefix *) &bi_result->extra->vnc.import.rd,
+                           (struct prefix *) prd))
+            {
+
+#if DEBUG_BI_SEARCH
+              zlog_debug ("%s: peer and RD same, doing aux_prefix check",
+                          __func__);
+#endif
+              if (!aux_prefix ||
+                  !prefix_cmp (aux_prefix,
+                               &bi_result->extra->vnc.import.aux_prefix))
+                {
+
+#if DEBUG_BI_SEARCH
+                  zlog_debug ("%s: match", __func__);
+#endif
+                  break;
+                }
+
+            }
+        }
+      return bi_result;
+    }
+
+  bi_fake.peer = peer;
+  bi_fake.extra = &bi_extra;
+  bi_fake.extra->vnc.import.rd = *(struct prefix_rd *) prd;
+  if (aux_prefix)
+    {
+      bi_fake.extra->vnc.import.aux_prefix = *aux_prefix;
+    }
+  else
+    {
+      /* wildcard */
+      bi_fake.extra->vnc.import.aux_prefix.family = AF_ETHERNET;
+      bi_fake.extra->vnc.import.aux_prefix.prefixlen = 1;
+    }
+
+  rc = skiplist_search (sl, (void *) &bi_fake, (void *) &bi_result);
+
+  if (rc)
+    {
+#if DEBUG_BI_SEARCH
+      zlog_debug ("%s: no match", __func__);
+#endif
+      return NULL;
+    }
+
+#if DEBUG_BI_SEARCH
+  zlog_debug ("%s: matched bi=%p", __func__, bi_result);
+#endif
+
+  return bi_result;
+}
+
+static void
+rfapiItBiIndexDel (
+  struct route_node    *rn,    /* Import table VPN node */
+  struct bgp_info      *bi)    /* old BI */
+{
+  struct skiplist *sl;
+  int rc;
+
+  {
+    char buf[BUFSIZ];
+    prefix_rd2str (&bi->extra->vnc.import.rd, buf, BUFSIZ);
+    zlog_debug ("%s: bi %p, peer %p, rd %s", __func__, bi, bi->peer, buf);
+  }
+
+  sl = RFAPI_RDINDEX (rn);
+  assert (sl);
+
+  rc = skiplist_delete (sl, (void *) (bi), (void *) bi);
+  if (rc)
+    {
+      rfapiItBiIndexDump (rn);
+    }
+  assert (!rc);
+
+  route_unlock_node (rn);       /* for skiplist entry */
+
+  /* NB: BIs in import tables are not refcounted */
+}
+
+/*
+ * Add a backreference at the ENCAP node to the VPN route that
+ * refers to it
+ */
+static void
+rfapiMonitorEncapAdd (
+  struct rfapi_import_table    *import_table,
+  struct prefix                        *p,             /* VN address */
+  struct route_node            *vpn_rn,        /* VPN node */
+  struct bgp_info              *vpn_bi)        /* VPN bi/route */
+{
+  afi_t                                afi = family2afi (p->family);
+  struct route_node            *rn;
+  struct rfapi_monitor_encap   *m;
+
+  assert (afi);
+  rn = route_node_get (import_table->imported_encap[afi], p);   /* locks rn */
+  assert (rn);
+
+  m =
+    XCALLOC (MTYPE_RFAPI_MONITOR_ENCAP, sizeof (struct rfapi_monitor_encap));
+  assert (m);
+
+  m->node = vpn_rn;
+  m->bi = vpn_bi;
+  m->rn = rn;
+
+  /* insert to encap node's list */
+  m->next = RFAPI_MONITOR_ENCAP (rn);
+  if (m->next)
+    m->next->prev = m;
+  RFAPI_MONITOR_ENCAP_W_ALLOC (rn) = m;
+
+  /* for easy lookup when deleting vpn route */
+  vpn_bi->extra->vnc.import.hme = m;
+
+  zlog_debug
+    ("%s: it=%p, vpn_bi=%p, afi=%d, encap rn=%p, setting vpn_bi->extra->vnc.import.hme=%p",
+     __func__, import_table, vpn_bi, afi, rn, m);
+
+  RFAPI_CHECK_REFCOUNT (rn, SAFI_ENCAP, 0);
+}
+
+static void
+rfapiMonitorEncapDelete (struct bgp_info *vpn_bi)
+{
+  /*
+   * Remove encap monitor
+   */
+  zlog_debug ("%s: vpn_bi=%p", __func__, vpn_bi);
+  if (vpn_bi->extra)
+    {
+      struct rfapi_monitor_encap *hme = vpn_bi->extra->vnc.import.hme;
+
+      if (hme)
+        {
+
+          zlog_debug ("%s: hme=%p", __func__, hme);
+
+          /* Refcount checking takes too long here */
+          //RFAPI_CHECK_REFCOUNT(hme->rn, SAFI_ENCAP, 0);
+          if (hme->next)
+            hme->next->prev = hme->prev;
+          if (hme->prev)
+            hme->prev->next = hme->next;
+          else
+            RFAPI_MONITOR_ENCAP_W_ALLOC (hme->rn) = hme->next;
+          /* Refcount checking takes too long here */
+          //RFAPI_CHECK_REFCOUNT(hme->rn, SAFI_ENCAP, 1);
+
+          /* see if the struct rfapi_it_extra is empty and can be freed */
+          rfapiMonitorExtraPrune (SAFI_ENCAP, hme->rn);
+
+          route_unlock_node (hme->rn);  /* decr ref count */
+          XFREE (MTYPE_RFAPI_MONITOR_ENCAP, hme);
+          vpn_bi->extra->vnc.import.hme = NULL;
+        }
+    }
+}
+
+/*
+ * quagga lib/thread.h says this must return int even though
+ * it doesn't do anything with the return value
+ */
+static int
+rfapiWithdrawTimerVPN (struct thread *t)
+{
+  struct rfapi_withdraw *wcb = t->arg;
+  struct bgp_info *bi = wcb->info;
+  struct bgp *bgp = bgp_get_default ();
+
+  struct rfapi_monitor_vpn *moved;
+  afi_t afi;
+
+  assert (wcb->node);
+  assert (bi);
+  assert (wcb->import_table);
+  assert (bi->extra);
+
+  RFAPI_CHECK_REFCOUNT (wcb->node, SAFI_MPLS_VPN, wcb->lockoffset);
+
+  {
+    char buf[BUFSIZ];
+
+    zlog_debug ("%s: removing bi %p at prefix %s/%d",
+                __func__,
+                bi,
+                rfapi_ntop (wcb->node->p.family, &wcb->node->p.u.prefix, buf,
+                            BUFSIZ), wcb->node->p.prefixlen);
+  }
+
+  /*
+   * Remove the route (doubly-linked)
+   */
+  if (CHECK_FLAG (bi->flags, BGP_INFO_VALID)
+      && VALID_INTERIOR_TYPE (bi->type))
+    RFAPI_MONITOR_EXTERIOR (wcb->node)->valid_interior_count--;
+
+  afi = family2afi (wcb->node->p.family);
+  wcb->import_table->holddown_count[afi] -= 1;  /* keep count consistent */
+  rfapiItBiIndexDel (wcb->node, bi);
+  rfapiBgpInfoDetach (wcb->node, bi);   /* with removed bi */
+
+  vnc_import_bgp_exterior_del_route_interior (bgp, wcb->import_table,
+                                              wcb->node, bi);
+
+
+  /*
+   * If VNC is configured to send response remove messages, AND
+   * if the removed route had a UN address, do response removal
+   * processing.
+   */
+  if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE))
+    {
+
+      int has_valid_duplicate = 0;
+      struct bgp_info *bii;
+
+      /*
+       * First check if there are any OTHER routes at this node
+       * that have the same nexthop and a valid UN address. If
+       * there are (e.g., from other peers), then the route isn't
+       * really gone, so skip sending a response removal message.
+       */
+      for (bii = wcb->node->info; bii; bii = bii->next)
+        {
+          if (rfapiVpnBiSamePtUn (bi, bii))
+            {
+              has_valid_duplicate = 1;
+              break;
+            }
+        }
+
+      zlog_debug ("%s: has_valid_duplicate=%d", __func__,
+                  has_valid_duplicate);
+
+      if (!has_valid_duplicate)
+        {
+         rfapiRibPendingDeleteRoute (bgp, wcb->import_table, afi, wcb->node);
+        }
+    }
+
+  rfapiMonitorEncapDelete (bi);
+
+  /*
+   * If there are no VPN monitors at this VPN Node A,
+   * we are done
+   */
+  if (!RFAPI_MONITOR_VPN (wcb->node))
+    {
+      zlog_debug ("%s: no VPN monitors at this node", __func__);
+      goto done;
+    }
+
+  /*
+   * rfapiMonitorMoveShorter only moves monitors if there are
+   * no remaining valid routes at the current node
+   */
+  moved = rfapiMonitorMoveShorter (wcb->node, 1);
+
+  if (moved)
+    {
+      rfapiMonitorMovedUp (wcb->import_table, wcb->node, moved->node, moved);
+    }
+
+done:
+  /*
+   * Free VPN bi
+   */
+  rfapiBgpInfoFree (bi);
+  wcb->info = NULL;
+
+  /*
+   * If route count at this node has gone to 0, withdraw exported prefix
+   */
+  if (!wcb->node->info)
+    {
+      /* see if the struct rfapi_it_extra is empty and can be freed */
+      rfapiMonitorExtraPrune (SAFI_MPLS_VPN, wcb->node);
+      vnc_direct_bgp_del_prefix (bgp, wcb->import_table, wcb->node);
+      vnc_zebra_del_prefix (bgp, wcb->import_table, wcb->node);
+    }
+  else
+    {
+      /*
+       * nexthop change event
+       * vnc_direct_bgp_add_prefix() will recompute the VN addr ecommunity
+       */
+      vnc_direct_bgp_add_prefix (bgp, wcb->import_table, wcb->node);
+    }
+
+  RFAPI_CHECK_REFCOUNT (wcb->node, SAFI_MPLS_VPN, 1 + wcb->lockoffset);
+  route_unlock_node (wcb->node);        /* decr ref count */
+  XFREE (MTYPE_RFAPI_WITHDRAW, wcb);
+  return 0;
+}
+
+/*
+ * This works for multiprotocol extension, but not for plain ol'
+ * unicast IPv4 because that nexthop is stored in attr->nexthop
+ */
+void
+rfapiNexthop2Prefix (struct attr *attr, struct prefix *p)
+{
+  assert (p);
+  assert (attr);
+  assert (attr->extra);
+
+  memset (p, 0, sizeof (struct prefix));
+
+  switch (p->family = BGP_MP_NEXTHOP_FAMILY (attr->extra->mp_nexthop_len))
+    {
+    case AF_INET:
+      p->u.prefix4 = attr->extra->mp_nexthop_global_in;
+      p->prefixlen = 32;
+      break;
+
+    case AF_INET6:
+      p->u.prefix6 = attr->extra->mp_nexthop_global;
+      p->prefixlen = 128;
+      break;
+
+    default:
+      zlog_debug ("%s: Family is unknown = %d",
+                  __func__, p->family);
+    }
+}
+
+void
+rfapiUnicastNexthop2Prefix (afi_t afi, struct attr *attr, struct prefix *p)
+{
+  if (afi == AFI_IP)
+    {
+      p->family = AF_INET;
+      p->prefixlen = 32;
+      p->u.prefix4 = attr->nexthop;
+    }
+  else
+    {
+      rfapiNexthop2Prefix (attr, p);
+    }
+}
+
+static int
+rfapiAttrNexthopAddrDifferent (struct prefix *p1, struct prefix *p2)
+{
+  if (!p1 || !p2)
+    {
+      zlog_debug ("%s: p1 or p2 is NULL", __func__);
+      return 1;
+    }
+
+  /*
+   * Are address families the same?
+   */
+  if (p1->family != p2->family)
+    {
+      return 1;
+    }
+
+  switch (p1->family)
+    {
+    case AF_INET:
+      if (IPV4_ADDR_SAME (&p1->u.prefix4, &p2->u.prefix4))
+        return 0;
+      break;
+
+    case AF_INET6:
+      if (IPV6_ADDR_SAME (&p1->u.prefix6, &p2->u.prefix6))
+        return 0;
+      break;
+
+    default:
+      assert (1);
+
+    }
+
+  return 1;
+}
+
+static void
+rfapiCopyUnEncap2VPN (struct bgp_info *encap_bi, struct bgp_info *vpn_bi)
+{
+  struct attr_extra *attre;
+
+  if (!encap_bi->attr || !encap_bi->attr->extra)
+    {
+      zlog_warn ("%s: no encap bi attr/extra, can't copy UN address",
+                 __func__);
+      return;
+    }
+
+  if (!vpn_bi || !vpn_bi->extra)
+    {
+      zlog_warn ("%s: no vpn  bi attr/extra, can't copy UN address",
+                 __func__);
+      return;
+    }
+
+  attre = encap_bi->attr->extra;
+
+  switch (BGP_MP_NEXTHOP_FAMILY (attre->mp_nexthop_len))
+    {
+    case AF_INET:
+
+      /*
+       * instrumentation to debug segfault of 091127
+       */
+      zlog_debug ("%s: vpn_bi=%p", __func__, vpn_bi);
+      if (vpn_bi)
+        {
+          zlog_debug ("%s: vpn_bi->extra=%p", __func__, vpn_bi->extra);
+        }
+
+      vpn_bi->extra->vnc.import.un_family = AF_INET;
+      vpn_bi->extra->vnc.import.un.addr4 = attre->mp_nexthop_global_in;
+      break;
+
+    case AF_INET6:
+      vpn_bi->extra->vnc.import.un_family = AF_INET6;
+      vpn_bi->extra->vnc.import.un.addr6 = attre->mp_nexthop_global;
+      break;
+
+    default:
+      zlog_warn ("%s: invalid encap nexthop length: %d",
+                 __func__, attre->mp_nexthop_len);
+      vpn_bi->extra->vnc.import.un_family = 0;
+      break;
+    }
+}
+
+/*
+ * returns 0 on success, nonzero on error
+ */
+static int
+rfapiWithdrawEncapUpdateCachedUn (
+  struct rfapi_import_table    *import_table,
+  struct bgp_info              *encap_bi,
+  struct route_node            *vpn_rn,
+  struct bgp_info              *vpn_bi)
+{
+  if (!encap_bi)
+    {
+
+      /* 
+       * clear cached UN address
+       */
+      if (!vpn_bi || !vpn_bi->extra)
+        {
+          zlog_warn ("%s: missing VPN bi/extra, can't clear UN addr",
+                     __func__);
+          return 1;
+        }
+      vpn_bi->extra->vnc.import.un_family = 0;
+      memset (&vpn_bi->extra->vnc.import.un, 0,
+              sizeof (vpn_bi->extra->vnc.import.un));
+      if (CHECK_FLAG (vpn_bi->flags, BGP_INFO_VALID))
+        {
+          if (rfapiGetVncTunnelUnAddr (vpn_bi->attr, NULL))
+            {
+              UNSET_FLAG (vpn_bi->flags, BGP_INFO_VALID);
+              if (VALID_INTERIOR_TYPE (vpn_bi->type))
+                RFAPI_MONITOR_EXTERIOR (vpn_rn)->valid_interior_count--;
+              /* signal interior route withdrawal to import-exterior */
+              vnc_import_bgp_exterior_del_route_interior (bgp_get_default (),
+                                                          import_table,
+                                                          vpn_rn, vpn_bi);
+            }
+        }
+
+    }
+  else
+    {
+      if (!vpn_bi)
+        {
+          zlog_warn ("%s: missing VPN bi, can't clear UN addr", __func__);
+          return 1;
+        }
+      rfapiCopyUnEncap2VPN (encap_bi, vpn_bi);
+      if (!CHECK_FLAG (vpn_bi->flags, BGP_INFO_VALID))
+        {
+          SET_FLAG (vpn_bi->flags, BGP_INFO_VALID);
+          if (VALID_INTERIOR_TYPE (vpn_bi->type))
+            RFAPI_MONITOR_EXTERIOR (vpn_rn)->valid_interior_count++;
+          /* signal interior route withdrawal to import-exterior */
+          vnc_import_bgp_exterior_add_route_interior (bgp_get_default (),
+                                                      import_table,
+                                                      vpn_rn, vpn_bi);
+        }
+    }
+  return 0;
+}
+
+static int
+rfapiWithdrawTimerEncap (struct thread *t)
+{
+  struct rfapi_withdraw *wcb = t->arg;
+  struct bgp_info *bi = wcb->info;
+  int was_first_route = 0;
+  struct rfapi_monitor_encap *em;
+  struct skiplist *vpn_node_sl = skiplist_new (0, NULL, NULL);
+
+  assert (wcb->node);
+  assert (bi);
+  assert (wcb->import_table);
+
+  RFAPI_CHECK_REFCOUNT (wcb->node, SAFI_ENCAP, 0);
+
+  if (wcb->node->info == bi)
+    was_first_route = 1;
+
+  /*
+   * Remove the route/bi and free it
+   */
+  rfapiBgpInfoDetach (wcb->node, bi);
+  rfapiBgpInfoFree (bi);
+
+  if (!was_first_route)
+    goto done;
+
+  for (em = RFAPI_MONITOR_ENCAP (wcb->node); em; em = em->next)
+    {
+
+      /*
+       * Update monitoring VPN BIs with new encap info at the
+       * head of the encap bi chain (which could be NULL after
+       * removing the expiring bi above)
+       */
+      if (rfapiWithdrawEncapUpdateCachedUn
+          (wcb->import_table, wcb->node->info, em->node, em->bi))
+        continue;
+
+      /*
+       * Build a list of unique VPN nodes referenced by these monitors.
+       * Use a skiplist for speed.
+       */
+      skiplist_insert (vpn_node_sl, em->node, em->node);
+    }
+
+
+  /*
+   * for each VPN node referenced in the ENCAP monitors:
+   */
+  struct route_node *rn;
+  while (!skiplist_first (vpn_node_sl, (void **) &rn, NULL))
+    {
+      if (!wcb->node->info)
+        {
+          struct rfapi_monitor_vpn *moved;
+
+          moved = rfapiMonitorMoveShorter (rn, 0);
+          if (moved)
+            {
+              //rfapiDoRouteCallback(wcb->import_table, moved->node, moved);
+              rfapiMonitorMovedUp (wcb->import_table, rn, moved->node, moved);
+            }
+        }
+      else
+        {
+          //rfapiDoRouteCallback(wcb->import_table, rn, NULL);
+          rfapiMonitorItNodeChanged (wcb->import_table, rn, NULL);
+        }
+      skiplist_delete_first (vpn_node_sl);
+    }
+
+done:
+  RFAPI_CHECK_REFCOUNT (wcb->node, SAFI_ENCAP, 1);
+  route_unlock_node (wcb->node);        /* decr ref count */
+  XFREE (MTYPE_RFAPI_WITHDRAW, wcb);
+  skiplist_free (vpn_node_sl);
+  return 0;
+}
+
+
+/*
+ * Works for both VPN and ENCAP routes; timer_service_func is different
+ * in each case
+ */
+static void
+rfapiBiStartWithdrawTimer (
+  struct rfapi_import_table    *import_table,
+  struct route_node            *rn,
+  struct bgp_info              *bi,
+  afi_t                                afi,
+  safi_t                       safi,
+  int                          (*timer_service_func) (struct thread *))
+{
+  uint32_t lifetime;
+  struct rfapi_withdraw *wcb;
+
+  if CHECK_FLAG
+    (bi->flags, BGP_INFO_REMOVED)
+    {
+      /*
+       * Already on the path to being withdrawn,
+       * should already have a timer set up to
+       * delete it.
+       */
+      zlog_debug ("%s: already being withdrawn, do nothing", __func__);
+      return;
+    }
+
+  rfapiGetVncLifetime (bi->attr, &lifetime);
+  zlog_debug ("%s: VNC lifetime is %u", __func__, lifetime);
+
+  /*
+   * withdrawn routes get to hang around for a while
+   */
+  SET_FLAG (bi->flags, BGP_INFO_REMOVED);
+
+  /* set timer to remove the route later */
+  lifetime = rfapiGetHolddownFromLifetime (lifetime);
+  zlog_debug ("%s: using timeout %u", __func__, lifetime);
+
+  /*
+   * Stash import_table, node, and info for use by timer
+   * service routine, which is supposed to free the wcb.
+   */
+  wcb = XCALLOC (MTYPE_RFAPI_WITHDRAW, sizeof (struct rfapi_withdraw));
+  assert (wcb);
+  wcb->node = rn;
+  wcb->info = bi;
+  wcb->import_table = import_table;
+
+  zlog_debug
+    ("%s: wcb values: node=%p, info=%p, import_table=%p (bi follows)",
+     __func__, wcb->node, wcb->info, wcb->import_table);
+  rfapiPrintBi (NULL, bi);
+
+
+  assert (bi->extra);
+  if (lifetime > UINT32_MAX / 1001)
+    {
+      /* sub-optimal case, but will probably never happen */
+      bi->extra->vnc.import.timer = thread_add_timer (bm->master,
+                                                      timer_service_func,
+                                                      wcb, lifetime);
+    }
+  else
+    {
+      static uint32_t jitter;
+      uint32_t lifetime_msec;
+
+      /* 
+       * the goal here is to spread out the timers so they are
+       * sortable in the skip list
+       */
+      if (++jitter >= 1000)
+        jitter = 0;
+
+      lifetime_msec = (lifetime * 1000) + jitter;
+
+      bi->extra->vnc.import.timer = thread_add_background (bm->master,
+                                                           timer_service_func,
+                                                           wcb,
+                                                           lifetime_msec);
+    }
+
+  /* re-sort route list (BGP_INFO_REMOVED routes are last) */
+  if (((struct bgp_info *) rn->info)->next)
+    {
+      rfapiBgpInfoDetach (rn, bi);
+      rfapiBgpInfoAttachSorted (rn, bi, afi, safi);
+    }
+}
+
+
+typedef void (rfapi_bi_filtered_import_f) (struct rfapi_import_table *,
+                                           int,
+                                           struct peer *,
+                                           void *,
+                                           struct prefix *,
+                                           struct prefix *,
+                                           afi_t,
+                                           struct prefix_rd *,
+                                           struct attr *,
+                                           u_char, u_char, uint32_t *);
+
+
+static void
+rfapiExpireEncapNow (
+  struct rfapi_import_table    *it,
+  struct route_node            *rn,
+  struct bgp_info              *bi)
+{
+  struct rfapi_withdraw *wcb;
+  struct thread t;
+
+  /*
+   * pretend we're an expiring timer
+   */
+  wcb = XCALLOC (MTYPE_RFAPI_WITHDRAW, sizeof (struct rfapi_withdraw));
+  wcb->info = bi;
+  wcb->node = rn;
+  wcb->import_table = it;
+  memset (&t, 0, sizeof (t));
+  t.arg = wcb;
+  rfapiWithdrawTimerEncap (&t); /* frees wcb */
+}
+
+static int
+rfapiGetNexthop (struct attr *attr, struct prefix *prefix)
+{
+  switch (BGP_MP_NEXTHOP_FAMILY (attr->extra->mp_nexthop_len))
+    {
+    case AF_INET:
+      prefix->family = AF_INET;
+      prefix->prefixlen = 32;
+      prefix->u.prefix4 = attr->extra->mp_nexthop_global_in;
+      break;
+    case AF_INET6:
+      prefix->family = AF_INET6;
+      prefix->prefixlen = 128;
+      prefix->u.prefix6 = attr->extra->mp_nexthop_global;
+      break;
+    default:
+      zlog_debug ("%s: unknown attr->extra->mp_nexthop_len %d", __func__,
+                  attr->extra->mp_nexthop_len);
+      return EINVAL;
+    }
+  return 0;
+}
+
+/* 
+ * import a bgp_info if its route target list intersects with the
+ * import table's route target list
+ */
+static void
+rfapiBgpInfoFilteredImportEncap (
+  struct rfapi_import_table    *import_table,
+  int                          action,
+  struct peer                  *peer,
+  void                         *rfd,           /* set for looped back routes */
+  struct prefix                        *p,
+  struct prefix                        *aux_prefix,    /* Unused for encap routes */
+  afi_t                                afi,
+  struct prefix_rd             *prd,
+  struct attr                  *attr,          /* part of bgp_info */
+  u_char                       type,           /* part of bgp_info */
+  u_char                       sub_type,       /* part of bgp_info */
+  uint32_t                     *label)         /* part of bgp_info */
+{
+  struct route_table *rt = NULL;
+  struct route_node *rn;
+  struct bgp_info *info_new;
+  struct bgp_info *bi;
+  struct bgp_info *next;
+  char buf[BUFSIZ];
+
+  struct prefix p_firstbi_old;
+  struct prefix p_firstbi_new;
+  int replacing = 0;
+  const char *action_str = NULL;
+  struct prefix un_prefix;
+
+  struct bgp *bgp;
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+
+  switch (action)
+    {
+    case FIF_ACTION_UPDATE:
+      action_str = "update";
+      break;
+    case FIF_ACTION_WITHDRAW:
+      action_str = "withdraw";
+      break;
+    case FIF_ACTION_KILL:
+      action_str = "kill";
+      break;
+    default:
+      assert (0);
+      break;
+    }
+
+  zlog_debug ("%s: entry: %s: prefix %s/%d", __func__,
+              action_str,
+              inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen);
+
+  memset (&p_firstbi_old, 0, sizeof (p_firstbi_old));
+  memset (&p_firstbi_new, 0, sizeof (p_firstbi_new));
+
+  if (action == FIF_ACTION_UPDATE)
+    {
+      /*
+       * Compare rt lists. If no intersection, don't import this route
+       * On a withdraw, peer and RD are sufficient to determine if
+       * we should act.
+       */
+      if (!attr || !attr->extra || !attr->extra->ecommunity)
+        {
+
+          zlog_debug ("%s: attr, extra, or ecommunity missing, not importing",
+                      __func__);
+          return;
+        }
+#if RFAPI_REQUIRE_ENCAP_BEEC
+      if (!rfapiEcommunitiesMatchBeec (attr->extra->ecommunity))
+        {
+          zlog_debug ("%s: it=%p: no match for BGP Encapsulation ecommunity",
+                      __func__, import_table);
+          return;
+        }
+#endif
+      if (!rfapiEcommunitiesIntersect (import_table->rt_import_list,
+                                       attr->extra->ecommunity))
+        {
+
+          zlog_debug ("%s: it=%p: no ecommunity intersection",
+                      __func__, import_table);
+          return;
+        }
+
+      /*
+       * Updates must also have a nexthop address
+       */
+      memset (&un_prefix, 0, sizeof (un_prefix));       /* keep valgrind happy */
+      if (rfapiGetNexthop (attr, &un_prefix))
+        {
+          zlog_debug ("%s: missing nexthop address", __func__);
+          return;
+        }
+    }
+
+  /*
+   * Figure out which radix tree the route would go into
+   */
+  switch (afi)
+    {
+    case AFI_IP:
+    case AFI_IP6:
+      rt = import_table->imported_encap[afi];
+      break;
+
+    default:
+      zlog_err ("%s: bad afi %d", __func__, afi);
+      return;
+    }
+
+  /*
+   * route_node_lookup returns a node only if there is at least
+   * one route attached.
+   */
+  rn = route_node_lookup (rt, p);
+
+#if DEBUG_ENCAP_MONITOR
+  zlog_debug ("%s: initial encap lookup (it=%p) rn=%p",
+              __func__, import_table, rn);
+#endif
+
+  if (rn)
+    {
+
+      RFAPI_CHECK_REFCOUNT (rn, SAFI_ENCAP, 1);
+      route_unlock_node (rn);   /* undo lock in route_node_lookup */
+
+
+      /*
+       * capture nexthop of first bi
+       */
+      if (rn->info)
+        {
+          rfapiNexthop2Prefix (((struct bgp_info *) (rn->info))->attr,
+                               &p_firstbi_old);
+        }
+
+      for (bi = rn->info; bi; bi = bi->next)
+        {
+
+          /*
+           * Does this bgp_info refer to the same route
+           * as we are trying to add?
+           */
+          zlog_debug ("%s: comparing BI %p", __func__, bi);
+
+
+          /*
+           * Compare RDs
+           *
+           * RD of import table bi is in bi->extra->vnc.import.rd
+           * RD of info_orig is in prd
+           */
+          if (!bi->extra)
+            {
+              zlog_debug ("%s: no bi->extra", __func__);
+              continue;
+            }
+          if (prefix_cmp ((struct prefix *) &bi->extra->vnc.import.rd,
+                          (struct prefix *) prd))
+            {
+
+              zlog_debug ("%s: prd does not match", __func__);
+              continue;
+            }
+
+          /*
+           * Compare peers
+           */
+          if (bi->peer != peer)
+            {
+              zlog_debug ("%s: peer does not match", __func__);
+              continue;
+            }
+
+          zlog_debug ("%s: found matching bi", __func__);
+
+          /* Same route. Delete this bi, replace with new one */
+
+          if (action == FIF_ACTION_WITHDRAW)
+            {
+
+              zlog_debug ("%s: withdrawing at prefix %s/%d",
+                          __func__,
+                          inet_ntop (rn->p.family, &rn->p.u.prefix, buf,
+                                     BUFSIZ), rn->p.prefixlen);
+
+              rfapiBiStartWithdrawTimer (import_table, rn, bi,
+                                         afi, SAFI_ENCAP,
+                                         rfapiWithdrawTimerEncap);
+
+            }
+          else
+            {
+              zlog_debug ("%s: %s at prefix %s/%d",
+                          __func__,
+                          ((action ==
+                            FIF_ACTION_KILL) ? "killing" : "replacing"),
+                          inet_ntop (rn->p.family, &rn->p.u.prefix, buf,
+                                     BUFSIZ), rn->p.prefixlen);
+
+              /*
+               * If this route is waiting to be deleted because of
+               * a previous withdraw, we must cancel its timer.
+               */
+              if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)
+                  && bi->extra->vnc.import.timer)
+                {
+
+                  struct thread *t =
+                    (struct thread *) bi->extra->vnc.import.timer;
+                  struct rfapi_withdraw *wcb = t->arg;
+
+                  XFREE (MTYPE_RFAPI_WITHDRAW, wcb);
+                  thread_cancel (t);
+                }
+
+              if (action == FIF_ACTION_UPDATE)
+                {
+                  rfapiBgpInfoDetach (rn, bi);
+                  rfapiBgpInfoFree (bi);
+                  replacing = 1;
+                }
+              else
+                {
+                  /*
+                   * Kill: do export stuff when removing bi
+                   */
+                  struct rfapi_withdraw *wcb;
+                  struct thread t;
+
+                  /*
+                   * pretend we're an expiring timer
+                   */
+                  wcb =
+                    XCALLOC (MTYPE_RFAPI_WITHDRAW,
+                             sizeof (struct rfapi_withdraw));
+                  wcb->info = bi;
+                  wcb->node = rn;
+                  wcb->import_table = import_table;
+                  memset (&t, 0, sizeof (t));
+                  t.arg = wcb;
+                  rfapiWithdrawTimerEncap (&t); /* frees wcb */
+                }
+            }
+
+          break;
+        }
+    }
+
+  if (rn)
+    RFAPI_CHECK_REFCOUNT (rn, SAFI_ENCAP, replacing ? 1 : 0);
+
+  if (action == FIF_ACTION_WITHDRAW || action == FIF_ACTION_KILL)
+    return;
+
+  info_new = rfapiBgpInfoCreate (attr, peer, rfd, prd, type, sub_type, NULL);
+
+  if (rn)
+    {
+      if (!replacing)
+        route_lock_node (rn);   /* incr ref count for new BI */
+    }
+  else
+    {
+      rn = route_node_get (rt, p);
+    }
+
+  zlog_debug ("%s: (afi=%d, rn=%p) inserting at prefix %s/%d",
+              __func__,
+              afi,
+              rn,
+              inet_ntop (rn->p.family, &rn->p.u.prefix, buf, BUFSIZ),
+              rn->p.prefixlen);
+
+  rfapiBgpInfoAttachSorted (rn, info_new, afi, SAFI_ENCAP);
+
+  /*
+   * Delete holddown routes from same NVE. See details in
+   * rfapiBgpInfoFilteredImportVPN()
+   */
+  for (bi = info_new->next; bi; bi = next)
+    {
+
+      struct prefix pfx_un;
+      int un_match = 0;
+
+      next = bi->next;
+      if (!CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+        continue;
+
+      /*
+       * We already match the VN address (it is the prefix
+       * of the route node)
+       */
+
+      if (!rfapiGetNexthop (bi->attr, &pfx_un) &&
+          prefix_same (&pfx_un, &un_prefix))
+        {
+
+          un_match = 1;
+        }
+
+      if (!un_match)
+        continue;
+
+      zlog_debug ("%s: removing holddown bi matching NVE of new route",
+                  __func__);
+      if (bi->extra->vnc.import.timer)
+        {
+          struct thread *t = (struct thread *) bi->extra->vnc.import.timer;
+          struct rfapi_withdraw *wcb = t->arg;
+
+          XFREE (MTYPE_RFAPI_WITHDRAW, wcb);
+          thread_cancel (t);
+        }
+      rfapiExpireEncapNow (import_table, rn, bi);
+    }
+
+  rfapiNexthop2Prefix (((struct bgp_info *) (rn->info))->attr,
+                       &p_firstbi_new);
+
+  /*
+   * If the nexthop address of the selected Encap route (i.e.,
+   * the UN address) has changed, then we must update the VPN
+   * routes that refer to this Encap route and possibly force
+   * rfapi callbacks.
+   */
+  if (rfapiAttrNexthopAddrDifferent (&p_firstbi_old, &p_firstbi_new))
+    {
+
+      struct rfapi_monitor_encap *m;
+      struct rfapi_monitor_encap *mnext;
+
+      struct route_node *referenced_vpn_prefix;
+
+      /*
+       * Optimized approach: build radix tree on the fly to
+       * hold list of VPN nodes referenced by the ENCAP monitors
+       *
+       * The nodes in this table correspond to prefixes of VPN routes.
+       * The "info" pointer of the node points to a chain of
+       * struct rfapi_monitor_encap, each of which refers to a
+       * specific VPN node.
+       */
+      struct route_table *referenced_vpn_table;
+
+      referenced_vpn_table = route_table_init ();
+      assert (referenced_vpn_table);
+
+      /*
+       * iterate over the set of monitors at this ENCAP node.
+       */
+#if DEBUG_ENCAP_MONITOR
+      zlog_debug ("%s: examining monitors at rn=%p", __func__, rn);
+#endif
+      for (m = RFAPI_MONITOR_ENCAP (rn); m; m = m->next)
+        {
+
+          /*
+           * For each referenced bi/route, copy the ENCAP route's
+           * nexthop to the VPN route's cached UN address field and set
+           * the address family of the cached UN address field.
+           */
+          rfapiCopyUnEncap2VPN (info_new, m->bi);
+          if (!CHECK_FLAG (m->bi->flags, BGP_INFO_VALID))
+            {
+              SET_FLAG (m->bi->flags, BGP_INFO_VALID);
+              if (VALID_INTERIOR_TYPE (m->bi->type))
+                RFAPI_MONITOR_EXTERIOR (m->node)->valid_interior_count++;
+              vnc_import_bgp_exterior_add_route_interior (bgp,
+                                                          import_table,
+                                                          m->node, m->bi);
+            }
+
+          /*
+           * Build a list of unique VPN nodes referenced by these monitors
+           *
+           * There could be more than one VPN node here with a given
+           * prefix. Those are currently in an unsorted linear list
+           * per prefix.
+           */
+
+          referenced_vpn_prefix =
+            route_node_get (referenced_vpn_table, &m->node->p);
+          assert (referenced_vpn_prefix);
+          for (mnext = referenced_vpn_prefix->info; mnext;
+               mnext = mnext->next)
+            {
+
+              if (mnext->node == m->node)
+                break;
+            }
+
+          if (mnext)
+            {
+              /*
+               * already have an entry for this VPN node
+               */
+              route_unlock_node (referenced_vpn_prefix);
+            }
+          else
+            {
+              mnext = XCALLOC (MTYPE_RFAPI_MONITOR_ENCAP,
+                               sizeof (struct rfapi_monitor_encap));
+              assert (mnext);
+              mnext->node = m->node;
+              mnext->next = referenced_vpn_prefix->info;
+              referenced_vpn_prefix->info = mnext;
+            }
+
+        }
+
+      /*
+       * for each VPN node referenced in the ENCAP monitors:
+       */
+      for (referenced_vpn_prefix = route_top (referenced_vpn_table);
+           referenced_vpn_prefix;
+           referenced_vpn_prefix = route_next (referenced_vpn_prefix))
+        {
+
+          while ((m = referenced_vpn_prefix->info))
+            {
+
+              struct route_node *n;
+
+              rfapiMonitorMoveLonger (m->node);
+              for (n = m->node; n; n = n->parent)
+                {
+                  //rfapiDoRouteCallback(import_table, n, NULL);
+                }
+              rfapiMonitorItNodeChanged (import_table, m->node, NULL);
+
+              referenced_vpn_prefix->info = m->next;
+              route_unlock_node (referenced_vpn_prefix);
+              XFREE (MTYPE_RFAPI_MONITOR_ENCAP, m);
+            }
+
+        }
+      route_table_finish (referenced_vpn_table);
+    }
+
+  RFAPI_CHECK_REFCOUNT (rn, SAFI_ENCAP, 0);
+}
+
+static void
+rfapiExpireVpnNow (
+  struct rfapi_import_table    *it,
+  struct route_node            *rn,
+  struct bgp_info              *bi,
+  int                          lockoffset)
+{
+  struct rfapi_withdraw *wcb;
+  struct thread t;
+
+  /*
+   * pretend we're an expiring timer
+   */
+  wcb = XCALLOC (MTYPE_RFAPI_WITHDRAW, sizeof (struct rfapi_withdraw));
+  wcb->info = bi;
+  wcb->node = rn;
+  wcb->import_table = it;
+  wcb->lockoffset = lockoffset;
+  memset (&t, 0, sizeof (t));
+  t.arg = wcb;
+  rfapiWithdrawTimerVPN (&t);   /* frees wcb */
+}
+
+
+/* 
+ * import a bgp_info if its route target list intersects with the
+ * import table's route target list
+ */
+void
+rfapiBgpInfoFilteredImportVPN (
+  struct rfapi_import_table    *import_table,
+  int                          action,
+  struct peer                  *peer,
+  void                         *rfd,           /* set for looped back routes */
+  struct prefix                        *p,
+  struct prefix                        *aux_prefix,    /* AFI_ETHER: optional IP */
+  afi_t                                afi,
+  struct prefix_rd             *prd,
+  struct attr                  *attr,          /* part of bgp_info */
+  u_char                       type,           /* part of bgp_info */
+  u_char                       sub_type,       /* part of bgp_info */
+  uint32_t                     *label)         /* part of bgp_info */
+{
+  struct route_table   *rt = NULL;
+  struct route_node    *rn;
+  struct route_node    *n;
+  struct bgp_info      *info_new;
+  struct bgp_info      *bi;
+  struct bgp_info      *next;
+  char                 buf[BUFSIZ];
+  struct prefix                vn_prefix;
+  struct prefix                un_prefix;
+  int                  un_prefix_valid = 0;
+  struct route_node    *ern;
+  int                  replacing = 0;
+  int                  original_had_routes = 0;
+  struct prefix                original_nexthop;
+  const char           *action_str = NULL;
+  int                  is_it_ce = 0;
+
+  struct bgp *bgp;
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+
+  switch (action)
+    {
+    case FIF_ACTION_UPDATE:
+      action_str = "update";
+      break;
+    case FIF_ACTION_WITHDRAW:
+      action_str = "withdraw";
+      break;
+    case FIF_ACTION_KILL:
+      action_str = "kill";
+      break;
+    default:
+      assert (0);
+      break;
+    }
+
+  if (import_table == bgp->rfapi->it_ce)
+    is_it_ce = 1;
+
+  zlog_debug ("%s: entry: %s%s: prefix %s/%d: it %p, afi %s", __func__,
+              (is_it_ce ? "CE-IT " : ""),
+              action_str,
+              rfapi_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+              p->prefixlen, import_table, afi2str (afi));
+
+  VNC_ITRCCK;
+
+  /*
+   * Compare rt lists. If no intersection, don't import this route
+   * On a withdraw, peer and RD are sufficient to determine if
+   * we should act.
+   */
+  if (action == FIF_ACTION_UPDATE)
+    {
+      if (!attr || !attr->extra || !attr->extra->ecommunity)
+        {
+
+          zlog_debug ("%s: attr, extra, or ecommunity missing, not importing",
+                      __func__);
+          return;
+        }
+      if ((import_table != bgp->rfapi->it_ce) &&
+          !rfapiEcommunitiesIntersect (import_table->rt_import_list,
+                                       attr->extra->ecommunity))
+        {
+
+          zlog_debug ("%s: it=%p: no ecommunity intersection",
+                      __func__, import_table);
+          return;
+        }
+
+      memset (&vn_prefix, 0, sizeof (vn_prefix));       /* keep valgrind happy */
+      if (rfapiGetNexthop (attr, &vn_prefix))
+        {
+          /* missing nexthop address would be a bad, bad thing */
+          zlog_debug ("%s: missing nexthop", __func__);
+          return;
+        }
+    }
+
+  /*
+   * Figure out which radix tree the route would go into
+   */
+  switch (afi)
+    {
+    case AFI_IP:
+    case AFI_IP6:
+    case AFI_ETHER:
+      rt = import_table->imported_vpn[afi];
+      break;
+
+    default:
+      zlog_err ("%s: bad afi %d", __func__, afi);
+      return;
+    }
+
+  /* clear it */
+  memset (&original_nexthop, 0, sizeof (original_nexthop));
+
+  /*
+   * route_node_lookup returns a node only if there is at least
+   * one route attached.
+   */
+  rn = route_node_lookup (rt, p);
+
+  zlog_debug ("%s: rn=%p", __func__, rn);
+
+  if (rn)
+    {
+
+      RFAPI_CHECK_REFCOUNT (rn, SAFI_MPLS_VPN, 1);
+      route_unlock_node (rn);   /* undo lock in route_node_lookup */
+
+      if (rn->info)
+        original_had_routes = 1;
+
+      /*
+       * Look for same route (will have same RD and peer)
+       */
+      bi = rfapiItBiIndexSearch (rn, prd, peer, aux_prefix);
+
+      if (bi)
+        {
+
+          /*
+           * This was an old test when we iterated over the
+           * BIs linearly. Since we're now looking up with
+           * RD and peer, comparing types should not be
+           * needed. Changed to assertion.
+           *
+           * Compare types. Doing so prevents a RFP-originated
+           * route from matching an imported route, for example.
+           */
+          assert (bi->type == type);
+
+          zlog_debug ("%s: found matching bi", __func__);
+
+          /*
+           * In the special CE table, withdrawals occur without holddown
+           */
+          if (import_table == bgp->rfapi->it_ce)
+            {
+              vnc_direct_bgp_del_route_ce (bgp, rn, bi);
+              if (action == FIF_ACTION_WITHDRAW)
+                action = FIF_ACTION_KILL;
+            }
+
+          if (action == FIF_ACTION_WITHDRAW)
+            {
+
+              int washolddown = CHECK_FLAG (bi->flags, BGP_INFO_REMOVED);
+
+              zlog_debug ("%s: withdrawing at prefix %s/%d%s",
+                          __func__,
+                          rfapi_ntop (rn->p.family, &rn->p.u.prefix, buf,
+                                      BUFSIZ), rn->p.prefixlen,
+                          (washolddown ? " (already being withdrawn)" : ""));
+
+              VNC_ITRCCK;
+              if (!washolddown)
+                {
+                  rfapiBiStartWithdrawTimer (import_table, rn, bi,
+                                             afi, SAFI_MPLS_VPN,
+                                             rfapiWithdrawTimerVPN);
+
+                  RFAPI_UPDATE_ITABLE_COUNT (bi, import_table, afi, -1);
+                  import_table->holddown_count[afi] += 1;
+                }
+              VNC_ITRCCK;
+            }
+          else
+            {
+              int washolddown = 0;
+
+              zlog_debug ("%s: %s at prefix %s/%d",
+                          __func__,
+                          ((action ==
+                            FIF_ACTION_KILL) ? "killing" : "replacing"),
+                          rfapi_ntop (rn->p.family, &rn->p.u.prefix, buf,
+                                      BUFSIZ), rn->p.prefixlen);
+
+              /*
+               * If this route is waiting to be deleted because of
+               * a previous withdraw, we must cancel its timer.
+               */
+              if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) &&
+                  bi->extra->vnc.import.timer)
+                {
+
+                  struct thread *t =
+                    (struct thread *) bi->extra->vnc.import.timer;
+                  struct rfapi_withdraw *wcb = t->arg;
+
+                  XFREE (MTYPE_RFAPI_WITHDRAW, wcb);
+                  thread_cancel (t);
+
+                  import_table->holddown_count[afi] -= 1;
+                  RFAPI_UPDATE_ITABLE_COUNT (bi, import_table, afi, 1);
+
+                  washolddown = 1;
+                }
+              /*
+               * decrement remote count (if route is remote) because
+               * we are going to remove it below
+               */
+              RFAPI_UPDATE_ITABLE_COUNT (bi, import_table, afi, -1);
+              if (action == FIF_ACTION_UPDATE)
+                {
+                  replacing = 1;
+
+                  /*
+                   * make copy of original nexthop so we can see if it changed
+                   */
+                  rfapiGetNexthop (bi->attr, &original_nexthop);
+
+                  /*
+                   * remove bi without doing any export processing
+                   */
+                  if (CHECK_FLAG (bi->flags, BGP_INFO_VALID)
+                      && VALID_INTERIOR_TYPE (bi->type))
+                    RFAPI_MONITOR_EXTERIOR (rn)->valid_interior_count--;
+                  rfapiItBiIndexDel (rn, bi);
+                  rfapiBgpInfoDetach (rn, bi);
+                  rfapiMonitorEncapDelete (bi);
+                  vnc_import_bgp_exterior_del_route_interior (bgp,
+                                                              import_table,
+                                                              rn, bi);
+                  rfapiBgpInfoFree (bi);
+                }
+              else
+                {
+                  /* Kill */
+                  /*
+                   * remove bi and do export processing
+                   */
+                  import_table->holddown_count[afi] += 1;
+                  rfapiExpireVpnNow (import_table, rn, bi, 0);
+                }
+
+            }
+        }
+
+    }
+
+  if (rn)
+    RFAPI_CHECK_REFCOUNT (rn, SAFI_MPLS_VPN, replacing ? 1 : 0);
+
+  if (action == FIF_ACTION_WITHDRAW || action == FIF_ACTION_KILL)
+    {
+      VNC_ITRCCK;
+      return;
+    }
+
+  info_new = rfapiBgpInfoCreate (attr, peer, rfd, prd, type, sub_type, label);
+
+  /*
+   * lookup un address in encap table
+   */
+  ern = route_node_match (import_table->imported_encap[afi], &vn_prefix);
+  if (ern)
+    {
+      rfapiCopyUnEncap2VPN (ern->info, info_new);
+      route_unlock_node (ern);  /* undo lock in route_note_match */
+    }
+  else
+    {
+      char buf[BUFSIZ];
+      prefix2str (&vn_prefix, buf, sizeof (buf));
+      buf[BUFSIZ - 1] = 0;
+      /* Not a big deal, just means VPN route got here first */
+      zlog_debug ("%s: no encap route for vn addr %s", __func__, buf);
+      info_new->extra->vnc.import.un_family = 0;
+    }
+
+  if (rn)
+    {
+      if (!replacing)
+        route_lock_node (rn);
+    }
+  else
+    {
+      /*
+       * No need to increment reference count, so only "get"
+       * if the node is not there already
+       */
+      rn = route_node_get (rt, p);
+    }
+
+  /*
+   * For ethernet routes, if there is an accompanying IP address,
+   * save it in the bi
+   */
+  if ((AFI_ETHER == afi) && aux_prefix)
+    {
+
+      zlog_debug ("%s: setting BI's aux_prefix", __func__);
+      info_new->extra->vnc.import.aux_prefix = *aux_prefix;
+    }
+
+  zlog_debug ("%s: inserting bi %p at prefix %s/%d #%d",
+              __func__,
+              info_new,
+              rfapi_ntop (rn->p.family, &rn->p.u.prefix, buf, BUFSIZ),
+              rn->p.prefixlen, rn->lock);
+
+  rfapiBgpInfoAttachSorted (rn, info_new, afi, SAFI_MPLS_VPN);
+  rfapiItBiIndexAdd (rn, info_new);
+  if (!rfapiGetUnAddrOfVpnBi (info_new, NULL))
+    {
+      if (VALID_INTERIOR_TYPE (info_new->type))
+        RFAPI_MONITOR_EXTERIOR (rn)->valid_interior_count++;
+      SET_FLAG (info_new->flags, BGP_INFO_VALID);
+    }
+  RFAPI_UPDATE_ITABLE_COUNT (info_new, import_table, afi, 1);
+  vnc_import_bgp_exterior_add_route_interior (bgp, import_table, rn,
+                                              info_new);
+
+  if (import_table == bgp->rfapi->it_ce)
+    vnc_direct_bgp_add_route_ce (bgp, rn, info_new);
+
+  zlog_debug ("%s: showing IT node", __func__);
+  rfapiShowItNode (NULL, rn);   /* debug */
+
+  rfapiMonitorEncapAdd (import_table, &vn_prefix, rn, info_new);
+
+  if (!rfapiGetUnAddrOfVpnBi (info_new, &un_prefix))
+    {
+
+      /* 
+       * if we have a valid UN address (either via Encap route
+       * or via tunnel attribute), then we should attempt
+       * to move any monitors at less-specific nodes to this node
+       */
+      rfapiMonitorMoveLonger (rn);
+
+      un_prefix_valid = 1;
+
+    }
+
+  /*
+   * 101129 Enhancement: if we add a route (implication: it is not
+   * in holddown), delete all other routes from this nve at this
+   * node that are in holddown, regardless of peer.
+   *
+   * Reasons it's OK to do that:
+   *
+   * - if the holddown route being deleted originally came from BGP VPN, 
+   *   it is already gone from BGP (implication of holddown), so there
+   *   won't be any added inconsistency with the BGP RIB.
+   *
+   * - once a fresh route is added at a prefix, any routes in holddown
+   *   at that prefix will not show up in RFP responses, so deleting
+   *   the holddown routes won't affect the contents of responses.
+   *
+   * - lifetimes are supposed to be consistent, so there should not
+   *   be a case where the fresh route has a shorter lifetime than
+   *   the holddown route, so we don't expect the fresh route to
+   *   disappear and complete its holddown time before the existing
+   *   holddown routes time out. Therefore, we won't have a situation
+   *   where we expect the existing holddown routes to be hidden and
+   *   then  to reappear sometime later (as holddown routes) in a
+   *   RFP response.
+   *
+   * Among other things, this would enable us to skirt the problem
+   * of local holddown routes that refer to NVE descriptors that
+   * have already been closed (if the same NVE triggers a subsequent
+   * rfapi_open(), the new peer is different and doesn't match the
+   * peer of the holddown route, so the stale holddown route still
+   * hangs around until it times out instead of just being replaced
+   * by the fresh route).
+   */
+  /*
+   * We know that the new bi will have been inserted before any routes
+   * in holddown, so we can skip any that came before it
+   */
+  for (bi = info_new->next; bi; bi = next)
+    {
+
+      struct prefix pfx_vn;
+      struct prefix pfx_un;
+      int un_match = 0;
+      int remote_peer_match = 0;
+
+      next = bi->next;
+
+      /*
+       * Must be holddown
+       */
+      if (!CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+        continue;
+
+      /*
+       * Must match VN address (nexthop of VPN route)
+       */
+      if (rfapiGetNexthop (bi->attr, &pfx_vn))
+        continue;
+      if (!prefix_same (&pfx_vn, &vn_prefix))
+        continue;
+
+      if (un_prefix_valid &&    /* new route UN addr */
+          !rfapiGetUnAddrOfVpnBi (bi, &pfx_un) &&       /* old route UN addr */
+          prefix_same (&pfx_un, &un_prefix))
+        {                       /* compare */
+          un_match = 1;
+        }
+      if (!RFAPI_LOCAL_BI (bi) && !RFAPI_LOCAL_BI (info_new) &&
+          sockunion_same (&bi->peer->su, &info_new->peer->su))
+        {
+          /* old & new are both remote, same peer */
+          remote_peer_match = 1;
+        }
+
+      if (!un_match & !remote_peer_match)
+        continue;
+
+      zlog_debug ("%s: removing holddown bi matching NVE of new route",
+                  __func__);
+      if (bi->extra->vnc.import.timer)
+        {
+          struct thread *t = (struct thread *) bi->extra->vnc.import.timer;
+          struct rfapi_withdraw *wcb = t->arg;
+
+          XFREE (MTYPE_RFAPI_WITHDRAW, wcb);
+          thread_cancel (t);
+        }
+      rfapiExpireVpnNow (import_table, rn, bi, 0);
+    }
+
+  if (!original_had_routes)
+    {
+      /*
+       * We went from 0 usable routes to 1 usable route. Perform the
+       * "Adding a Route" export process.
+       */
+      vnc_direct_bgp_add_prefix (bgp, import_table, rn);
+      vnc_zebra_add_prefix (bgp, import_table, rn);
+    }
+  else
+    {
+      /*
+       * Check for nexthop change event
+       * Note: the prefix_same() test below detects two situations:
+       * 1. route is replaced, new route has different nexthop
+       * 2. new route is added (original_nexthop is 0)
+       */
+      struct prefix new_nexthop;
+
+      rfapiGetNexthop (attr, &new_nexthop);
+      if (!prefix_same (&original_nexthop, &new_nexthop))
+        {
+          /*
+           * nexthop change event
+           * vnc_direct_bgp_add_prefix() will recompute VN addr ecommunity
+           */
+          vnc_direct_bgp_add_prefix (bgp, import_table, rn);
+        }
+    }
+
+  if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE))
+    {
+      for (n = rn; n; n = n->parent)
+        {
+          //rfapiDoRouteCallback(import_table, n, NULL);
+        }
+      rfapiMonitorItNodeChanged (import_table, rn, NULL);
+    }
+  RFAPI_CHECK_REFCOUNT (rn, SAFI_MPLS_VPN, 0);
+  VNC_ITRCCK;
+}
+
+static rfapi_bi_filtered_import_f *
+rfapiBgpInfoFilteredImportFunction (safi_t safi)
+{
+  switch (safi)
+    {
+    case SAFI_MPLS_VPN:
+    case BGP_SAFI_VPN:
+      return rfapiBgpInfoFilteredImportVPN;
+
+    case SAFI_ENCAP:
+      return rfapiBgpInfoFilteredImportEncap;
+    }
+  zlog_err ("%s: bad safi %d", __func__, safi);
+  return NULL;
+}
+
+void
+rfapiProcessUpdate (
+  struct peer          *peer,
+  void                 *rfd,   /* set when looped from RFP/RFAPI */
+  struct prefix                *p,
+  struct prefix_rd     *prd,
+  struct attr          *attr,
+  afi_t                        afi,
+  safi_t               safi,
+  u_char               type,
+  u_char               sub_type,
+  uint32_t             *label)
+{
+  struct bgp                   *bgp;
+  struct rfapi                 *h;
+  struct rfapi_import_table    *it;
+  int                          has_ip_route = 1;
+  uint32_t                     lni = 0;
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+  assert (bgp);
+
+  h = bgp->rfapi;
+  assert (h);
+
+  /*
+   * look at high-order byte of RD. FF means MAC
+   * address is present (VNC L2VPN)
+   */
+  if ((safi == SAFI_MPLS_VPN) && 
+      (decode_rd_type(prd->val) == RD_TYPE_VNC_ETH))
+    {
+      struct prefix pfx_mac_buf;
+      struct prefix pfx_nexthop_buf;
+      int rc;
+
+      /*
+       * Set flag if prefix and nexthop are the same - don't
+       * add the route to normal IP-based import tables
+       */
+      if (!rfapiGetNexthop (attr, &pfx_nexthop_buf))
+        {
+          if (!prefix_cmp (&pfx_nexthop_buf, p))
+            {
+              has_ip_route = 0;
+            }
+        }
+
+      memset (&pfx_mac_buf, 0, sizeof (pfx_mac_buf));
+      pfx_mac_buf.family = AF_ETHERNET;
+      pfx_mac_buf.prefixlen = 48;
+      memcpy (&pfx_mac_buf.u.prefix_eth.octet, prd->val + 2, 6);
+
+      /*
+       * Find rt containing LNI (Logical Network ID), which
+       * _should_ always be present when mac address is present
+       */
+      rc = rfapiEcommunityGetLNI (attr->extra->ecommunity, &lni);
+
+      zlog_debug
+        ("%s: rfapiEcommunityGetLNI returned %d, lni=%d, attr=%p, attr->extra=%p",
+         __func__, rc, lni, attr, attr->extra);
+      if (attr && attr->extra && !rc)
+        {
+          it = rfapiMacImportTableGet (bgp, lni);
+
+          rfapiBgpInfoFilteredImportVPN (
+           it,
+           FIF_ACTION_UPDATE,
+           peer,
+           rfd,
+           &pfx_mac_buf,       /* prefix */
+           p,                  /* aux prefix: IP addr */
+           AFI_ETHER,
+           prd,
+           attr,
+           type,
+           sub_type,
+           label);
+        }
+
+    }
+
+  if (!has_ip_route)
+    return;
+
+  /*
+   * Iterate over all import tables; do a filtered import
+   * for the afi/safi combination
+   */
+  for (it = h->imports; it; it = it->next)
+    {
+      (*rfapiBgpInfoFilteredImportFunction (safi)) (
+       it,
+       FIF_ACTION_UPDATE,
+       peer,
+       rfd,
+       p,        /* prefix */
+       NULL,
+       afi,
+       prd,
+       attr,
+       type,
+       sub_type,
+       label);
+    }
+
+  if (safi == SAFI_MPLS_VPN || safi == BGP_SAFI_VPN)
+    {
+      vnc_direct_bgp_rh_add_route (bgp, afi, p, peer, attr);
+    }
+
+  if (safi == SAFI_MPLS_VPN)
+    {
+      rfapiBgpInfoFilteredImportVPN (
+       bgp->rfapi->it_ce,
+       FIF_ACTION_UPDATE,
+       peer,
+       rfd,
+       p,        /* prefix */
+       NULL,
+       afi,
+       prd,
+       attr,
+       type,
+       sub_type,
+       label);
+    }
+}
+
+
+void
+rfapiProcessWithdraw (
+  struct peer          *peer,
+  void                 *rfd,
+  struct prefix                *p,
+  struct prefix_rd     *prd,
+  struct attr          *attr,
+  afi_t                        afi,
+  safi_t               safi,
+  u_char               type,
+  int                  kill)
+{
+  struct bgp *bgp;
+  struct rfapi *h;
+  struct rfapi_import_table *it;
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+  assert (bgp);
+
+  h = bgp->rfapi;
+  assert (h);
+
+  /*
+   * look at high-order byte of RD. FF means MAC
+   * address is present (VNC L2VPN)
+   */
+  if (h->import_mac != NULL && safi == SAFI_MPLS_VPN &&
+      decode_rd_type(prd->val) == RD_TYPE_VNC_ETH)
+    {
+      struct prefix pfx_mac_buf;
+      void *cursor = NULL;
+      int rc;
+
+      memset (&pfx_mac_buf, 0, sizeof (pfx_mac_buf));
+      pfx_mac_buf.family = AF_ETHERNET;
+      pfx_mac_buf.prefixlen = 48;
+      memcpy (&pfx_mac_buf.u.prefix_eth, prd->val + 2, 6);
+
+      /*
+       * withdraw does not contain attrs, so we don't have
+       * access to the route's LNI, which would ordinarily
+       * select the specific mac-based import table. Instead,
+       * we must iterate over all mac-based tables and rely
+       * on the RD to match.
+       *
+       * If this approach is too slow, add an index where
+       * key is {RD, peer} and value is the import table
+       */
+      for (rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor);
+           rc == 0;
+           rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor))
+        {
+
+#if DEBUG_L2_EXTRA
+          zlog_debug
+            ("%s: calling rfapiBgpInfoFilteredImportVPN(it=%p, afi=AFI_ETHER)",
+             __func__, it);
+#endif
+
+          rfapiBgpInfoFilteredImportVPN (
+           it,
+           (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW),
+           peer,
+           rfd,
+           &pfx_mac_buf,       /* prefix */
+           p,                  /* aux_prefix: IP */
+           AFI_ETHER,
+           prd,
+           attr,
+           type,
+           0,
+           NULL);              /* sub_type & label unused for withdraw */
+        }
+    }
+
+  /*
+   * XXX For the case where the withdraw involves an L2
+   * route with no IP information, we rely on the lack
+   * of RT-list intersection to filter out the withdraw
+   * from the IP-based import tables below
+   */
+
+  /*
+   * Iterate over all import tables; do a filtered import
+   * for the afi/safi combination
+   */
+
+  for (it = h->imports; it; it = it->next)
+    {
+      (*rfapiBgpInfoFilteredImportFunction (safi)) (
+       it,
+       (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW),
+       peer,
+       rfd,
+       p,      /* prefix */
+       NULL,
+       afi,
+       prd,
+       attr,
+       type,
+       0,
+       NULL);  /* sub_type & label unused for withdraw */
+    }
+
+  /* TBD the deletion should happen after the lifetime expires */
+  if (safi == SAFI_MPLS_VPN || safi == BGP_SAFI_VPN)
+    vnc_direct_bgp_rh_del_route (bgp, afi, p, peer);
+
+  if (safi == SAFI_MPLS_VPN)
+    {
+      rfapiBgpInfoFilteredImportVPN (
+       bgp->rfapi->it_ce,
+       (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW),
+       peer,
+       rfd,
+       p,      /* prefix */
+       NULL,
+       afi,
+       prd,
+       attr,
+       type,
+       0,
+       NULL);  /* sub_type & label unused for withdraw */
+    }
+}
+
+/*
+ * TBD optimized withdraw timer algorithm for case of many
+ * routes expiring at the same time due to peer drop.
+ */
+/*
+ * 1. Visit all BIs in all ENCAP import tables.
+ *
+ *    a. If a bi's peer is the failed peer, remove the bi.
+ *       b. If the removed ENCAP bi was first in the list of
+ *       BIs at this ENCAP node, loop over all monitors
+ *       at this node:
+ *
+ *       (1) for each ENCAP monitor, loop over all its
+ *           VPN node monitors and set their RFAPI_MON_FLAG_NEEDCALLBACK
+ *           flags.
+ *
+ * 2. Visit all BIs in all VPN import tables.
+ *    a. If a bi's peer is the failed peer, remove the bi.
+ *    b. loop over all the VPN node monitors and set their
+ *       RFAPI_MON_FLAG_NEEDCALLBACK flags
+ *    c. If there are no BIs left at this VPN node,
+ *
+ */
+
+
+/* surprise, this gets called from peer_delete(), from rfapi_close() */
+static void
+rfapiProcessPeerDownRt (
+  struct peer                  *peer,
+  struct rfapi_import_table    *import_table,
+  afi_t                                afi,
+  safi_t                       safi)
+{
+  struct route_node    *rn;
+  struct bgp_info      *bi;
+  struct route_table   *rt;
+  int                  (*timer_service_func) (struct thread *);
+
+  assert (afi == AFI_IP || afi == AFI_IP6);
+
+  VNC_ITRCCK;
+
+  switch (safi)
+    {
+    case SAFI_MPLS_VPN:
+      rt = import_table->imported_vpn[afi];
+      timer_service_func = rfapiWithdrawTimerVPN;
+      break;
+    case SAFI_ENCAP:
+      rt = import_table->imported_encap[afi];
+      timer_service_func = rfapiWithdrawTimerEncap;
+      break;
+    default:
+      assert (0);
+    }
+
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    {
+      for (bi = rn->info; bi; bi = bi->next)
+        {
+          if (bi->peer == peer)
+            {
+
+              if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+                {
+                  /* already in holddown, skip */
+                  continue;
+                }
+
+              if (safi == SAFI_MPLS_VPN)
+                {
+                  RFAPI_UPDATE_ITABLE_COUNT (bi, import_table, afi, -1);
+                  import_table->holddown_count[afi] += 1;
+                }
+              rfapiBiStartWithdrawTimer (import_table, rn, bi,
+                                         afi, safi,
+                                         timer_service_func);
+            }
+        }
+    }
+  VNC_ITRCCK;
+}
+
+/* 
+ * This gets called when a peer connection drops. We have to remove
+ * all the routes from this peer.
+ *
+ * Current approach is crude. TBD Optimize by setting fewer timers and
+ * grouping withdrawn routes so we can generate callbacks more
+ * efficiently.
+ */
+void
+rfapiProcessPeerDown (struct peer *peer)
+{
+  struct bgp *bgp;
+  struct rfapi *h;
+  struct rfapi_import_table *it;
+
+  /*
+   * If this peer is a "dummy" peer structure atached to a RFAPI
+   * nve_descriptor, we don't need to walk the import tables
+   * because the routes are already withdrawn by rfapi_close()
+   */
+  if (CHECK_FLAG (peer->flags, PEER_FLAG_IS_RFAPI_HD))
+    return;
+
+  /*
+   * 1. Visit all BIs in all ENCAP import tables.
+   *    Start withdraw timer on the BIs that match peer.
+   *
+   * 2. Visit All BIs in all VPN import tables.
+   *    Start withdraw timer on the BIs that match peer.
+   */
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+  assert (bgp);
+
+  h = bgp->rfapi;
+  assert (h);
+
+  for (it = h->imports; it; it = it->next)
+    {
+      rfapiProcessPeerDownRt (peer, it, AFI_IP, SAFI_ENCAP);
+      rfapiProcessPeerDownRt (peer, it, AFI_IP6, SAFI_ENCAP);
+      rfapiProcessPeerDownRt (peer, it, AFI_IP, SAFI_MPLS_VPN);
+      rfapiProcessPeerDownRt (peer, it, AFI_IP6, SAFI_MPLS_VPN);
+    }
+
+  if (h->it_ce)
+    {
+      rfapiProcessPeerDownRt (peer, h->it_ce, AFI_IP, SAFI_MPLS_VPN);
+      rfapiProcessPeerDownRt (peer, h->it_ce, AFI_IP6, SAFI_MPLS_VPN);
+    }
+}
+
+/*
+ * Import an entire RIB (for an afi/safi) to an import table RIB,
+ * filtered according to the import table's RT list
+ *
+ * TBD: does this function need additions to match rfapiProcessUpdate()
+ * for, e.g., L2 handling? 
+ */
+static void
+rfapiBgpTableFilteredImport (
+  struct bgp                   *bgp,
+  struct rfapi_import_table    *it,
+  afi_t                                afi,
+  safi_t                       safi)
+{
+  struct bgp_node *rn1;
+  struct bgp_node *rn2;
+
+  /* Only these SAFIs have 2-level RIBS */
+  assert (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP);
+
+  /* 
+   * Now visit all the rd nodes and the nodes of all the
+   * route tables attached to them, and import the routes
+   * if they have matching route targets
+   */
+  for (rn1 = bgp_table_top (bgp->rib[afi][safi]);
+       rn1; rn1 = bgp_route_next (rn1))
+    {
+
+      if (rn1->info)
+        {
+          for (rn2 = bgp_table_top (rn1->info);
+               rn2; rn2 = bgp_route_next (rn2))
+            {
+
+              struct bgp_info *bi;
+
+              for (bi = rn2->info; bi; bi = bi->next)
+                {
+                  u_int32_t label = 0;
+
+                  if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+                    continue;
+
+                  if (bi->extra)
+                    label = decode_label (bi->extra->tag);
+                  (*rfapiBgpInfoFilteredImportFunction (safi)) (
+                   it,                         /* which import table */
+                   FIF_ACTION_UPDATE,
+                   bi->peer,
+                   NULL,
+                   &rn2->p,                    /* prefix */
+                   NULL,
+                   afi,
+                   (struct prefix_rd *) &rn1->p,
+                   bi->attr,
+                   bi->type,
+                   bi->sub_type,
+                   &label);
+                }
+            }
+        }
+    }
+}
+
+
+/* per-bgp-instance rfapi data */
+struct rfapi *
+bgp_rfapi_new (struct bgp *bgp)
+{
+  struct rfapi *h;
+  int afi;
+  struct rfapi_rfp_cfg *cfg = NULL;
+  struct rfapi_rfp_cb_methods *cbm = NULL;
+
+  assert (bgp->rfapi_cfg == NULL);
+
+  h = (struct rfapi *) XCALLOC (MTYPE_RFAPI, sizeof (struct rfapi));
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    {
+      /* ugly, to deal with addition of delegates, part of 0.99.24.1 merge */
+      h->un[afi].delegate = route_table_get_default_delegate ();
+    }
+
+  /*
+   * initialize the ce import table
+   */
+  h->it_ce =
+    XCALLOC (MTYPE_RFAPI_IMPORTTABLE, sizeof (struct rfapi_import_table));
+  h->it_ce->imported_vpn[AFI_IP] = route_table_init ();
+  h->it_ce->imported_vpn[AFI_IP6] = route_table_init ();
+  h->it_ce->imported_encap[AFI_IP] = route_table_init ();
+  h->it_ce->imported_encap[AFI_IP6] = route_table_init ();
+  rfapiBgpTableFilteredImport (bgp, h->it_ce, AFI_IP, SAFI_MPLS_VPN);
+  rfapiBgpTableFilteredImport (bgp, h->it_ce, AFI_IP6, SAFI_MPLS_VPN);
+
+  /* 
+   * Set up work queue for deferred rfapi_close operations
+   */
+  h->deferred_close_q = work_queue_new (bm->master, "rfapi deferred close");
+  h->deferred_close_q->spec.workfunc = rfapi_deferred_close_workfunc;
+  h->deferred_close_q->spec.data = h;
+
+  h->rfp = rfp_start (bm->master, &cfg, &cbm);
+  bgp->rfapi_cfg = bgp_rfapi_cfg_new (cfg);
+  if (cbm != NULL)
+    {
+      h->rfp_methods = *cbm;
+    }
+  return h;
+}
+
+void
+bgp_rfapi_destroy (struct bgp *bgp, struct rfapi *h)
+{
+  if (bgp == NULL || h == NULL)
+    return;
+
+  if (h->resolve_nve_nexthop)
+    {
+      skiplist_free (h->resolve_nve_nexthop);
+      h->resolve_nve_nexthop = NULL;
+    }
+
+  route_table_finish (h->it_ce->imported_vpn[AFI_IP]);
+  route_table_finish (h->it_ce->imported_vpn[AFI_IP6]);
+  route_table_finish (h->it_ce->imported_encap[AFI_IP]);
+  route_table_finish (h->it_ce->imported_encap[AFI_IP6]);
+
+  if (h->import_mac)
+    {
+      struct rfapi_import_table *it;
+      void *cursor;
+      int rc;
+
+      for (cursor = NULL,
+           rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor);
+           !rc;
+           rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor))
+        {
+
+          rfapiImportTableFlush (it);
+          XFREE (MTYPE_RFAPI_IMPORTTABLE, it);
+        }
+      skiplist_free (h->import_mac);
+      h->import_mac = NULL;
+    }
+
+  work_queue_free (h->deferred_close_q);
+
+  if (h->rfp != NULL)
+    rfp_stop (h->rfp);
+  XFREE (MTYPE_RFAPI_IMPORTTABLE, h->it_ce);
+  XFREE (MTYPE_RFAPI, h);
+}
+
+struct rfapi_import_table *
+rfapiImportTableRefAdd (struct bgp *bgp, struct ecommunity *rt_import_list)
+{
+  struct rfapi *h;
+  struct rfapi_import_table *it;
+  afi_t afi;
+
+  h = bgp->rfapi;
+  assert (h);
+
+  for (it = h->imports; it; it = it->next)
+    {
+      if (ecommunity_cmp (it->rt_import_list, rt_import_list))
+        break;
+    }
+
+  zlog_debug ("%s: matched it=%p", __func__, it);
+
+  if (!it)
+    {
+      it =
+        XCALLOC (MTYPE_RFAPI_IMPORTTABLE, sizeof (struct rfapi_import_table));
+      assert (it);
+      it->next = h->imports;
+      h->imports = it;
+
+      it->rt_import_list = ecommunity_dup (rt_import_list);
+      it->monitor_exterior_orphans =
+        skiplist_new (0, NULL, (void (*)(void *)) prefix_free);
+
+      /*
+       * fill import route tables from RIBs
+       *
+       * Potential area for optimization. If this occurs when
+       * tables are large (e.g., the operator adds a nve group
+       * with a new RT list to a running system), it could take
+       * a while.
+       * 
+       */
+      for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+        {
+
+          it->imported_vpn[afi] = route_table_init ();
+          it->imported_encap[afi] = route_table_init ();
+
+          rfapiBgpTableFilteredImport (bgp, it, afi, SAFI_MPLS_VPN);
+          rfapiBgpTableFilteredImport (bgp, it, afi, SAFI_ENCAP);
+
+          vnc_import_bgp_exterior_redist_enable_it (bgp, afi, it);
+        }
+    }
+
+  it->refcount += 1;
+
+  return it;
+}
+
+/*
+ * skiplist element free function
+ */
+static void
+delete_rem_pfx_na_free (void *na)
+{
+  uint32_t *pCounter = ((struct rfapi_nve_addr *) na)->info;
+
+  *pCounter += 1;
+  XFREE (MTYPE_RFAPI_NVE_ADDR, na);
+}
+
+/*
+ * Common deleter for IP and MAC import tables
+ */
+static void
+rfapiDeleteRemotePrefixesIt (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *it,
+    struct prefix              *un,
+    struct prefix              *vn,
+    struct prefix              *p,
+    int                                delete_active,
+    int                                delete_holddown,
+    uint32_t                   *pARcount,
+    uint32_t                   *pAHcount,
+    uint32_t                   *pHRcount,
+    uint32_t                   *pHHcount,
+    struct skiplist            *uniq_active_nves,
+    struct skiplist            *uniq_holddown_nves)
+{
+  afi_t afi;
+
+#if DEBUG_L2_EXTRA
+  {
+    char buf_pfx[BUFSIZ];
+
+    if (p)
+      {
+        prefix2str (p, buf_pfx, BUFSIZ);
+      }
+    else
+      {
+        buf_pfx[0] = '*';
+        buf_pfx[1] = 0;
+      }
+
+    zlog_debug ("%s: entry, p=%s, delete_active=%d, delete_holddown=%d",
+                __func__, buf_pfx, delete_active, delete_holddown);
+  }
+#endif
+
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+
+      struct route_table *rt;
+      struct route_node *rn;
+
+      if (p && (family2afi (p->family) != afi))
+        {
+          continue;
+        }
+
+      rt = it->imported_vpn[afi];
+      if (!rt)
+        continue;
+
+      zlog_debug ("%s: scanning rt for afi=%d", __func__, afi);
+
+      for (rn = route_top (rt); rn; rn = route_next (rn))
+        {
+          struct bgp_info *bi;
+          struct bgp_info *next;
+
+         if (VNC_DEBUG(IMPORT_DEL_REMOTE))
+           {
+             char p1line[BUFSIZ];
+             char p2line[BUFSIZ];
+
+             prefix2str (p, p1line, BUFSIZ);
+             prefix2str (&rn->p, p2line, BUFSIZ);
+             zlog_debug ("%s: want %s, have %s", __func__, p1line, p2line);
+           }
+
+          if (p && prefix_cmp (p, &rn->p))
+            continue;
+
+          {
+            char buf_pfx[BUFSIZ];
+            prefix2str (&rn->p, buf_pfx, BUFSIZ);
+            zlog_debug ("%s: rn pfx=%s", __func__, buf_pfx);
+          }
+
+          /* TBD is this valid for afi == AFI_ETHER? */
+          RFAPI_CHECK_REFCOUNT (rn, SAFI_MPLS_VPN, 1);
+
+          for (bi = rn->info; bi; bi = next)
+            {
+              next = bi->next;
+
+              struct prefix qpt;
+              struct prefix qct;
+              int qpt_valid = 0;
+              int qct_valid = 0;
+              int is_active = 0;
+
+              zlog_debug ("%s: examining bi %p", __func__, bi);
+
+              if (bi->attr)
+                {
+                  if (!rfapiGetNexthop (bi->attr, &qpt))
+                    qpt_valid = 1;
+                }
+              if (vn)
+                {
+                  if (!qpt_valid || !prefix_match (vn, &qpt))
+                    {
+#if DEBUG_L2_EXTRA
+                      zlog_debug
+                        ("%s: continue at vn && !qpt_valid || !prefix_match(vn, &qpt)",
+                         __func__);
+#endif
+                      continue;
+                    }
+                }
+
+              if (!rfapiGetUnAddrOfVpnBi (bi, &qct))
+                qct_valid = 1;
+
+              if (un)
+                {
+                  if (!qct_valid || !prefix_match (un, &qct))
+                    {
+#if DEBUG_L2_EXTRA
+                      zlog_debug
+                        ("%s: continue at un && !qct_valid || !prefix_match(un, &qct)",
+                         __func__);
+#endif
+                      continue;
+                    }
+                }
+
+
+              /*
+               * Blow bi away
+               */
+              /*
+               * If this route is waiting to be deleted because of
+               * a previous withdraw, we must cancel its timer.
+               */
+              if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+                {
+                  if (!delete_holddown)
+                    continue;
+                  if (bi->extra->vnc.import.timer)
+                    {
+
+                      struct thread *t =
+                        (struct thread *) bi->extra->vnc.import.timer;
+                      struct rfapi_withdraw *wcb = t->arg;
+
+                      wcb->import_table->holddown_count[afi] -= 1;
+                      RFAPI_UPDATE_ITABLE_COUNT (bi, wcb->import_table, afi,
+                                                 1);
+                      XFREE (MTYPE_RFAPI_WITHDRAW, wcb);
+                      thread_cancel (t);
+                    }
+                }
+              else
+                {
+                  if (!delete_active)
+                    continue;
+                  is_active = 1;
+                }
+
+              zlog_debug
+                ("%s: deleting bi %p (qct_valid=%d, qpt_valid=%d, delete_holddown=%d, delete_active=%d)",
+                 __func__, bi, qct_valid, qpt_valid, delete_holddown,
+                 delete_active);
+
+
+              /*
+               * add nve to list
+               */
+              if (qct_valid && qpt_valid)
+                {
+
+                  struct rfapi_nve_addr na;
+                  struct rfapi_nve_addr *nap;
+
+                  memset (&na, 0, sizeof (na));
+                  assert (!rfapiQprefix2Raddr (&qct, &na.un));
+                  assert (!rfapiQprefix2Raddr (&qpt, &na.vn));
+
+                  if (skiplist_search ((is_active ? uniq_active_nves :
+                                        uniq_holddown_nves), &na,
+                                       (void **) &nap))
+                    {
+                      char line[BUFSIZ];
+
+                      nap = XCALLOC (MTYPE_RFAPI_NVE_ADDR,
+                                     sizeof (struct rfapi_nve_addr));
+                      assert (nap);
+                      *nap = na;
+                      nap->info = is_active ? pAHcount : pHHcount;
+                      skiplist_insert ((is_active ? uniq_active_nves :
+                                        uniq_holddown_nves), nap, nap);
+
+                      rfapiNveAddr2Str (nap, line, BUFSIZ);
+                    }
+                }
+
+              vnc_direct_bgp_rh_del_route (bgp, afi, &rn->p, bi->peer);
+
+              RFAPI_UPDATE_ITABLE_COUNT (bi, it, afi, -1);
+              it->holddown_count[afi] += 1;
+              rfapiExpireVpnNow (it, rn, bi, 1);
+
+              zlog_debug ("%s: incrementing count (is_active=%d)",
+                          __func__, is_active);
+
+              if (is_active)
+                ++ * pARcount;
+              else
+                ++ * pHRcount;
+            }
+        }
+    }
+}
+
+
+/*
+ * For use by the "clear vnc prefixes" command
+ */
+/*------------------------------------------
+ * rfapiDeleteRemotePrefixes
+ *
+ * UI helper: For use by the "clear vnc prefixes" command
+ *
+ * input: 
+ *     un                      if set, tunnel must match this prefix
+ *     vn                      if set, nexthop prefix must match this prefix
+ *     p                       if set, prefix must match this prefix
+ *
+ * output
+ *     pARcount                number of active routes deleted
+ *     pAHcount                number of active nves deleted
+ *     pHRcount                number of holddown routes deleted
+ *     pHHcount                number of holddown nves deleted
+ *
+ * return value:
+ *     void
+ --------------------------------------------*/
+void
+rfapiDeleteRemotePrefixes (
+    struct prefix      *un,
+    struct prefix      *vn,
+    struct prefix      *p,
+    int                        delete_active,
+    int                        delete_holddown,
+    uint32_t           *pARcount,
+    uint32_t           *pAHcount,
+    uint32_t           *pHRcount,
+    uint32_t           *pHHcount)
+{
+  struct bgp                   *bgp;
+  struct rfapi                 *h;
+  struct rfapi_import_table    *it;
+  uint32_t                     deleted_holddown_route_count = 0;
+  uint32_t                     deleted_active_route_count = 0;
+  uint32_t                     deleted_holddown_nve_count = 0;
+  uint32_t                     deleted_active_nve_count = 0;
+  struct skiplist              *uniq_holddown_nves;
+  struct skiplist              *uniq_active_nves;
+
+  VNC_ITRCCK;
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+  /* If no bgp instantiated yet, no vnc prefixes exist */
+  if (!bgp)
+    return;
+
+  h = bgp->rfapi;
+  assert (h);
+
+  uniq_holddown_nves =
+    skiplist_new (0, rfapi_nve_addr_cmp, delete_rem_pfx_na_free);
+  uniq_active_nves =
+    skiplist_new (0, rfapi_nve_addr_cmp, delete_rem_pfx_na_free);
+
+  /*
+   * Iterate over all import tables; do a filtered import
+   * for the afi/safi combination
+   */
+
+  for (it = h->imports; it; it = it->next)
+    {
+
+      zlog_debug
+        ("%s: calling rfapiDeleteRemotePrefixesIt() on (IP) import %p",
+         __func__, it);
+
+      rfapiDeleteRemotePrefixesIt (
+       bgp,
+       it,
+       un,
+       vn,
+       p,
+       delete_active,
+       delete_holddown,
+       &deleted_active_route_count,
+       &deleted_active_nve_count,
+       &deleted_holddown_route_count,
+       &deleted_holddown_nve_count,
+       uniq_active_nves,
+       uniq_holddown_nves);
+    }
+
+  /*
+   * Now iterate over L2 import tables
+   */
+  if (h->import_mac && !(p && (p->family != AF_ETHERNET)))
+    {
+
+      void *cursor = NULL;
+      int rc;
+
+      for (cursor = NULL,
+           rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor);
+           !rc;
+           rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor))
+        {
+
+          zlog_debug
+            ("%s: calling rfapiDeleteRemotePrefixesIt() on import_mac %p",
+             __func__, it);
+
+          rfapiDeleteRemotePrefixesIt (
+           bgp,
+           it,
+           un,
+           vn,
+           p,
+           delete_active,
+           delete_holddown,
+           &deleted_active_route_count,
+           &deleted_active_nve_count,
+           &deleted_holddown_route_count,
+           &deleted_holddown_nve_count,
+           uniq_active_nves,
+           uniq_holddown_nves);
+        }
+    }
+
+  /*
+   * our custom element freeing function above counts as it deletes
+   */
+  skiplist_free (uniq_holddown_nves);
+  skiplist_free (uniq_active_nves);
+
+  if (pARcount)
+    *pARcount = deleted_active_route_count;
+  if (pAHcount)
+    *pAHcount = deleted_active_nve_count;
+  if (pHRcount)
+    *pHRcount = deleted_holddown_route_count;
+  if (pHHcount)
+    *pHHcount = deleted_holddown_nve_count;
+
+  VNC_ITRCCK;
+}
+
+/*------------------------------------------
+ * rfapiCountRemoteRoutes
+ *
+ * UI helper: count VRF routes from BGP side
+ *
+ * input: 
+ *
+ * output
+ *     pALRcount               count of active local routes
+ *     pARRcount               count of active remote routes
+ *     pHRcount                count of holddown routes
+ *     pIRcount                count of direct imported routes
+ *
+ * return value:
+ *     void
+ --------------------------------------------*/
+void
+rfapiCountAllItRoutes (int *pALRcount,  /* active local routes */
+                       int *pARRcount,  /* active remote routes */
+                       int *pHRcount,   /* holddown routes */
+                       int *pIRcount)   /* imported routes */
+{
+  struct bgp *bgp;
+  struct rfapi *h;
+  struct rfapi_import_table *it;
+  afi_t afi;
+
+  int total_active_local = 0;
+  int total_active_remote = 0;
+  int total_holddown = 0;
+  int total_imported = 0;
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+  assert (bgp);
+
+  h = bgp->rfapi;
+  assert (h);
+
+  /*
+   * Iterate over all import tables; do a filtered import
+   * for the afi/safi combination
+   */
+
+  for (it = h->imports; it; it = it->next)
+    {
+
+      for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+        {
+
+          total_active_local += it->local_count[afi];
+          total_active_remote += it->remote_count[afi];
+          total_holddown += it->holddown_count[afi];
+          total_imported += it->imported_count[afi];
+
+        }
+    }
+
+  void *cursor;
+  int rc;
+
+  if (h->import_mac)
+    {
+      for (cursor = NULL,
+           rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor);
+           !rc;
+           rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor))
+        {
+
+          total_active_local += it->local_count[AFI_ETHER];
+          total_active_remote += it->remote_count[AFI_ETHER];
+          total_holddown += it->holddown_count[AFI_ETHER];
+          total_imported += it->imported_count[AFI_ETHER];
+
+        }
+    }
+
+
+  if (pALRcount)
+    {
+      *pALRcount = total_active_local;
+    }
+  if (pARRcount)
+    {
+      *pARRcount = total_active_remote;
+    }
+  if (pHRcount)
+    {
+      *pHRcount = total_holddown;
+    }
+  if (pIRcount)
+    {
+      *pIRcount = total_imported;
+    }
+}
+
+/*------------------------------------------
+ * rfapiGetHolddownFromLifetime
+ *
+ * calculate holddown value based on lifetime
+ *
+ * input: 
+ *     lifetime                lifetime
+ *
+ * return value:
+ *     Holddown value based on lifetime, holddown_factor,
+ *     and RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY
+ *
+ --------------------------------------------*/
+/* hold down time maxes out at RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY */
+uint32_t
+rfapiGetHolddownFromLifetime (uint32_t lifetime)
+{
+  uint32_t factor;
+  struct bgp *bgp;
+
+  bgp = bgp_get_default ();
+  if (bgp && bgp->rfapi_cfg)
+    factor = bgp->rfapi_cfg->rfp_cfg.holddown_factor;
+  else
+    factor = RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR;
+
+  if (factor < 100 || lifetime < RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY)
+    lifetime = lifetime * factor / 100;
+  if (lifetime < RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY)
+    return lifetime;
+  else
+    return RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY;
+}
diff --git a/bgpd/rfapi/rfapi_import.h b/bgpd/rfapi/rfapi_import.h
new file mode 100644 (file)
index 0000000..9e88b52
--- /dev/null
@@ -0,0 +1,283 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/*
+ * File:       rfapi_import.h
+ * Purpose:    Handle import of routes from BGP to RFAPI
+ */
+
+#ifndef QUAGGA_HGP_RFAPI_IMPORT_H
+#define QUAGGA_HGP_RFAPI_IMPORT_H
+
+#include "thread.h"
+
+/*
+ * These are per-rt-import-list
+ *
+ * routes are not segregated by RD - the RD is stored in bgp_info_extra
+ * and is needed to determine if two prefixes are the same.
+ */
+struct rfapi_import_table
+{
+  struct rfapi_import_table *next;
+  struct ecommunity *rt_import_list;    /* copied from nve grp */
+  int refcount;                 /* nve grps and nves */
+  uint32_t l2_logical_net_id;   /* L2 only: EVPN Eth Seg Id */
+  struct route_table *imported_vpn[AFI_MAX];
+  struct rfapi_monitor_vpn *vpn0_queries[AFI_MAX];
+  struct rfapi_monitor_eth *eth0_queries;
+  struct route_table *imported_encap[AFI_MAX];
+  struct skiplist *monitor_exterior_orphans;
+  int local_count[AFI_MAX];
+  int remote_count[AFI_MAX];
+  int holddown_count[AFI_MAX];
+  int imported_count[AFI_MAX];
+};
+
+#define RFAPI_LOCAL_BI(bi) \
+    (((bi)->type == ZEBRA_ROUTE_BGP) && ((bi)->sub_type == BGP_ROUTE_RFP))
+
+#define RFAPI_DIRECT_IMPORT_BI(bi) \
+    (((bi)->type ==  ZEBRA_ROUTE_BGP_DIRECT) || ((bi)->type ==  ZEBRA_ROUTE_BGP_DIRECT_EXT))
+
+#define RFAPI_UPDATE_ITABLE_COUNT(bi, itable, afi, cnt) \
+    if (RFAPI_LOCAL_BI(bi)) {                          \
+       (itable)->local_count[(afi)] += (cnt);          \
+    } else {                                           \
+        if (RFAPI_DIRECT_IMPORT_BI(bi))                 \
+            (itable)->imported_count[(afi)] += (cnt);   \
+        else                                            \
+            (itable)->remote_count[(afi)]   += (cnt);   \
+    }
+
+extern uint8_t
+rfapiRfpCost (struct attr *attr);
+
+extern void
+rfapiDebugBacktrace (void);
+
+extern void
+rfapiCheckRouteCount (void);
+
+/*
+ * Print BI in an Import Table
+ */
+extern void
+rfapiPrintBi (void *stream, struct bgp_info *bi);
+
+extern void
+rfapiShowImportTable (
+  void                 *stream,
+  const char           *label,
+  struct route_table   *rt,
+  int                  isvpn);
+
+
+extern void
+rfapiImportTableRefDelByIt (
+  struct bgp                   *bgp,
+  struct rfapi_import_table    *it_target);
+
+
+/*
+ * Construct an rfapi nexthop list based on the routes attached to
+ * the specified node.
+ *
+ * If there are any routes that do NOT have BGP_INFO_REMOVED set,
+ * return those only. If there are ONLY routes with BGP_INFO_REMOVED,
+ * then return those, and also include all the non-removed routes from the
+ * next less-specific node (i.e., this node's parent) at the end.
+ */
+extern struct rfapi_next_hop_entry *
+rfapiRouteNode2NextHopList (
+  struct route_node    *rn,
+  uint32_t             lifetime,               /* put into nexthop entries */
+  struct rfapi_ip_addr *exclude_vnaddr,        /* omit routes to same NVE */
+  struct route_table    *rfd_rib_table,                /* preload this NVE rib table */
+  struct prefix                *pfx_target_original);  /* query target */
+
+extern struct rfapi_next_hop_entry *
+rfapiRouteTable2NextHopList (
+  struct route_table   *rt,
+  uint32_t             lifetime,               /* put into nexthop entries */
+  struct rfapi_ip_addr *exclude_vnaddr,        /* omit routes to same NVE */
+  struct route_table    *rfd_rib_table,                /* preload this NVE rib table */
+  struct prefix                *pfx_target_original);  /* query target */
+
+extern struct rfapi_next_hop_entry *
+rfapiEthRouteTable2NextHopList (
+  uint32_t                     logical_net_id,
+  struct rfapi_ip_prefix       *rprefix,
+  uint32_t                     lifetime,        /* put into nexthop entries */
+  struct rfapi_ip_addr         *exclude_vnaddr, /* omit routes to same NVE */
+  struct route_table            *rib_route_table,/* preload NVE rib node */
+  struct prefix                        *pfx_target_original);  /* query target */
+
+extern int
+rfapiEcommunitiesIntersect (struct ecommunity *e1, struct ecommunity *e2);
+
+extern void
+rfapiCheckRefcount (struct route_node *rn, safi_t safi, int lockoffset);
+
+extern int
+rfapiHasNonRemovedRoutes (struct route_node *rn);
+
+extern int
+rfapiProcessDeferredClose (struct thread *t);
+
+extern int
+rfapiGetUnAddrOfVpnBi (struct bgp_info *bi, struct prefix *p);
+
+extern void
+rfapiNexthop2Prefix (struct attr *attr, struct prefix *p);
+
+extern void
+rfapiUnicastNexthop2Prefix (
+  afi_t                afi,
+  struct attr  *attr,
+  struct prefix        *p);
+
+/* Filtered Import Function actions */
+#define FIF_ACTION_UPDATE      0
+#define FIF_ACTION_WITHDRAW    1
+#define FIF_ACTION_KILL                2
+
+extern void
+rfapiBgpInfoFilteredImportVPN (
+  struct rfapi_import_table    *import_table,
+  int                          action,
+  struct peer                  *peer,
+  void                         *rfd,           /* set for looped back routes */
+  struct prefix                        *p,
+  struct prefix                        *aux_prefix,    /* AFI_ETHER: optional IP */
+  afi_t                                afi,
+  struct prefix_rd             *prd,
+  struct attr                  *attr,          /* part of bgp_info */
+  u_char                       type,           /* part of bgp_info */
+  u_char                       sub_type,       /* part of bgp_info */
+  uint32_t                     *label);        /* part of bgp_info */
+
+extern struct rfapi_next_hop_entry *
+rfapiEthRouteNode2NextHopList (
+  struct route_node            *rn,
+  struct rfapi_ip_prefix       *rprefix,
+  uint32_t                     lifetime,        /* put into nexthop entries */
+  struct rfapi_ip_addr         *exclude_vnaddr, /* omit routes to same NVE */
+  struct route_table            *rib_route_table,/* preload NVE rib table */
+  struct prefix                        *pfx_target_original); /* query target */
+
+extern struct rfapi_import_table *
+rfapiMacImportTableGetNoAlloc (
+  struct bgp   *bgp,
+  uint32_t     lni);
+
+extern struct rfapi_import_table *
+rfapiMacImportTableGet (
+  struct bgp   *bgp,
+  uint32_t     lni);
+
+extern int
+rfapiGetL2o (
+  struct attr                  *attr,
+  struct rfapi_l2address_option *l2o);
+
+extern int rfapiEcommunityGetLNI (
+  struct ecommunity    *ecom,
+  uint32_t             *lni);
+
+
+/* enable for debugging; disable for performance */
+#if 0
+#define RFAPI_CHECK_REFCOUNT(rn, safi, lo) rfapiCheckRefcount((rn),(safi),(lo))
+#else
+#define RFAPI_CHECK_REFCOUNT(rn, safi, lo) {}
+#endif
+
+/*------------------------------------------
+ * rfapiDeleteRemotePrefixes
+ *
+ * UI helper: For use by the "clear vnc prefixes" command
+ *
+ * input: 
+ *     un                      if set, tunnel must match this prefix
+ *     vn                      if set, nexthop prefix must match this prefix
+ *     p                       if set, prefix must match this prefix
+ *
+ * output
+ *     pARcount                number of active routes deleted
+ *     pAHcount                number of active nves deleted
+ *     pHRcount                number of holddown routes deleted
+ *     pHHcount                number of holddown nves deleted
+ *
+ * return value:
+ *     void
+ --------------------------------------------*/
+extern void
+rfapiDeleteRemotePrefixes (
+  struct prefix        *un,
+  struct prefix        *vn,
+  struct prefix        *p,
+  int          delete_active,
+  int          delete_holddown,
+  uint32_t     *pARcount,     /* active routes */
+  uint32_t     *pAHcount,     /* active nves */
+  uint32_t     *pHRcount,     /* holddown routes */
+  uint32_t     *pHHcount);    /* holddown nves */
+
+/*------------------------------------------
+ * rfapiCountAllItRoutes
+ *
+ * UI helper: count VRF routes from BGP side
+ *
+ * input: 
+ *
+ * output
+ *     pARcount                count of active routes
+ *     pHRcount                count of holddown routes
+ *     pIRcount                count of holddown routes
+ *
+ * return value:
+ *     void
+ --------------------------------------------*/
+extern void
+rfapiCountAllItRoutes (
+  int *pALRcount,      /* active local routes */
+  int *pARRcount,      /* active remote routes */
+  int *pHRcount,       /* holddown routes */
+  int *pIRcount);      /* direct imported routes */
+
+/*------------------------------------------
+ * rfapiGetHolddownFromLifetime
+ *
+ * calculate holddown value based on lifetime
+ *
+ * input: 
+ *     lifetime                lifetime
+ *
+ * return value:
+ *     Holddown value based on lifetime, holddown_factor,
+ *     and RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY
+ *
+ --------------------------------------------*/
+extern uint32_t
+rfapiGetHolddownFromLifetime (uint32_t lifetime);
+
+#endif /* QUAGGA_HGP_RFAPI_IMPORT_H */
diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c
new file mode 100644 (file)
index 0000000..4677d1f
--- /dev/null
@@ -0,0 +1,1701 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/*
+ * File:       rfapi_monitor.c
+ */
+
+/* TBD remove unneeded includes */
+
+#include <errno.h>
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "memory.h"
+#include "log.h"
+#include "table.h"
+#include "skiplist.h"
+
+#include "bgpd.h"
+
+#include "bgp_rfapi_cfg.h"
+#include "rfapi.h"
+#include "rfapi_backend.h"
+
+#include "rfapi.h"
+#include "rfapi_import.h"
+#include "vnc_import_bgp.h"
+#include "rfapi_private.h"
+#include "rfapi_monitor.h"
+#include "rfapi_vty.h"
+#include "rfapi_rib.h"
+
+#define DEBUG_L2_EXTRA 0
+#define DEBUG_DUP_CHECK 0
+#define DEBUG_ETH_SL 0
+
+static void
+rfapiMonitorTimerRestart (struct rfapi_monitor_vpn *m);
+
+static void
+rfapiMonitorEthTimerRestart (struct rfapi_monitor_eth *m);
+
+/*
+ * Forward declarations
+ */
+static void
+rfapiMonitorEthDetachImport (struct bgp *bgp, struct rfapi_monitor_eth *mon);
+
+#if DEBUG_ETH_SL
+/*
+ * Debug function, special case
+ */
+void
+rfapiMonitorEthSlCheck(
+    struct route_node  *rn,
+    const char         *tag1,
+    const char         *tag2)
+{
+    struct route_node      *rn_saved = NULL;
+    static struct skiplist *sl_saved = NULL;
+    struct skiplist        *sl;
+
+    if (!rn)
+       return;
+
+    if (rn_saved && (rn != rn_saved))
+       return;
+
+    if (!rn_saved)
+       rn_saved = rn;
+
+    sl = RFAPI_MONITOR_ETH(rn);
+    if (sl || sl_saved)
+      {
+       zlog_debug("%s[%s%s]: rn=%p, rn->lock=%d, old sl=%p, new sl=%p",
+           __func__, (tag1? tag1: ""), (tag2? tag2: ""), rn, rn->lock,
+           sl_saved, sl);
+       sl_saved = sl;
+      }
+}
+#endif
+
+/*
+ * Debugging function that aborts when it finds monitors whose
+ * "next" pointer * references themselves
+ */
+void
+rfapiMonitorLoopCheck (struct rfapi_monitor_vpn *mchain)
+{
+  struct rfapi_monitor_vpn *m;
+
+  for (m = mchain; m; m = m->next)
+    assert (m != m->next);
+}
+
+#if DEBUG_DUP_CHECK
+/*
+ * Debugging code: see if a monitor is mentioned more than once
+ * in a HD's monitor list
+ */
+void
+rfapiMonitorDupCheck (struct bgp *bgp)
+{
+  struct listnode *hnode;
+  struct rfapi_descriptor *rfd;
+
+  for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, hnode, rfd))
+    {
+      struct route_node *mrn;
+
+      if (!rfd->mon)
+        continue;
+
+      for (mrn = route_top (rfd->mon); mrn; mrn = route_next (mrn))
+        {
+          struct rfapi_monitor_vpn *m;
+          for (m = (struct rfapi_monitor_vpn *) (mrn->info); m; m = m->next)
+            m->dcount = 0;
+        }
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, hnode, rfd))
+    {
+      struct route_node *mrn;
+
+      if (!rfd->mon)
+        continue;
+
+      for (mrn = route_top (rfd->mon); mrn; mrn = route_next (mrn))
+        {
+          struct rfapi_monitor_vpn *m;
+
+          for (m = (struct rfapi_monitor_vpn *) (mrn->info); m; m = m->next)
+            assert (++m->dcount == 1);
+        }
+    }
+}
+#endif
+
+/* debug */
+void
+rfapiMonitorCleanCheck (struct bgp *bgp)
+{
+  struct listnode *hnode;
+  struct rfapi_descriptor *rfd;
+
+  for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, hnode, rfd))
+    {
+      assert (!rfd->import_table->vpn0_queries[AFI_IP]);
+      assert (!rfd->import_table->vpn0_queries[AFI_IP6]);
+
+      struct route_node *rn;
+
+      for (rn = route_top (rfd->import_table->imported_vpn[AFI_IP]); rn;
+           rn = route_next (rn))
+        {
+
+          assert (!RFAPI_MONITOR_VPN (rn));
+        }
+      for (rn = route_top (rfd->import_table->imported_vpn[AFI_IP6]); rn;
+           rn = route_next (rn))
+        {
+
+          assert (!RFAPI_MONITOR_VPN (rn));
+        }
+    }
+}
+
+/* debug */
+void
+rfapiMonitorCheckAttachAllowed (void)
+{
+  struct bgp *bgp = bgp_get_default ();
+  assert (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE));
+}
+
+void
+rfapiMonitorExtraFlush (safi_t safi, struct route_node *rn)
+{
+  struct rfapi_it_extra *hie;
+  struct rfapi_monitor_vpn *v;
+  struct rfapi_monitor_vpn *v_next;
+  struct rfapi_monitor_encap *e = NULL;
+  struct rfapi_monitor_encap *e_next = NULL;
+
+  if (!rn)
+    return;
+
+  if (!rn->aggregate)
+    return;
+
+  hie = (struct rfapi_it_extra *) (rn->aggregate);
+
+  switch (safi)
+    {
+    case SAFI_ENCAP:
+      for (e = hie->u.encap.e; e; e = e_next)
+        {
+          e_next = e->next;
+          e->next = NULL;
+          XFREE (MTYPE_RFAPI_MONITOR_ENCAP, e);
+          route_unlock_node (rn);
+        }
+      hie->u.encap.e = NULL;
+      break;
+
+    case SAFI_MPLS_VPN:
+      for (v = hie->u.vpn.v; v; v = v_next)
+        {
+          v_next = v->next;
+          v->next = NULL;
+          XFREE (MTYPE_RFAPI_MONITOR, e);
+          route_unlock_node (rn);
+        }
+      hie->u.vpn.v = NULL;
+      if (hie->u.vpn.e.source)
+        {
+          while (!skiplist_delete_first (hie->u.vpn.e.source))
+            {
+              route_unlock_node (rn);
+            }
+          skiplist_free (hie->u.vpn.e.source);
+          hie->u.vpn.e.source = NULL;
+          route_unlock_node (rn);
+        }
+      if (hie->u.vpn.idx_rd)
+        {
+          /* looping through bi->extra->vnc.import.rd is tbd */
+          while (!skiplist_delete_first (hie->u.vpn.idx_rd))
+            {
+              route_unlock_node (rn);
+            }
+          skiplist_free (hie->u.vpn.idx_rd);
+          hie->u.vpn.idx_rd = NULL;
+          route_unlock_node (rn);
+        }
+      if (hie->u.vpn.mon_eth)
+        {
+          while (!skiplist_delete_first (hie->u.vpn.mon_eth))
+            {
+              route_unlock_node (rn);
+            }
+          skiplist_free (hie->u.vpn.mon_eth);
+          hie->u.vpn.mon_eth = NULL;
+          route_unlock_node (rn);
+        }
+      break;
+
+    default:
+      assert (0);
+    }
+  XFREE (MTYPE_RFAPI_IT_EXTRA, hie);
+  rn->aggregate = NULL;
+  route_unlock_node (rn);
+}
+
+/*
+ * If the child lists are empty, release the rfapi_it_extra struct
+ */
+void
+rfapiMonitorExtraPrune (safi_t safi, struct route_node *rn)
+{
+  struct rfapi_it_extra *hie;
+
+  if (!rn)
+    return;
+
+  if (!rn->aggregate)
+    return;
+
+  hie = (struct rfapi_it_extra *) (rn->aggregate);
+
+  switch (safi)
+    {
+    case SAFI_ENCAP:
+      if (hie->u.encap.e)
+        return;
+      break;
+
+    case SAFI_MPLS_VPN:
+      if (hie->u.vpn.v)
+        return;
+      if (hie->u.vpn.mon_eth)
+       {
+         if (skiplist_count (hie->u.vpn.mon_eth))
+           return;
+          skiplist_free (hie->u.vpn.mon_eth);
+          hie->u.vpn.mon_eth = NULL;
+          route_unlock_node (rn);      /* uncount skiplist */
+        }
+      if (hie->u.vpn.e.source)
+        {
+          if (skiplist_count (hie->u.vpn.e.source))
+            return;
+          skiplist_free (hie->u.vpn.e.source);
+          hie->u.vpn.e.source = NULL;
+          route_unlock_node (rn);
+        }
+      if (hie->u.vpn.idx_rd)
+        {
+          if (skiplist_count (hie->u.vpn.idx_rd))
+            return;
+          skiplist_free (hie->u.vpn.idx_rd);
+          hie->u.vpn.idx_rd = NULL;
+          route_unlock_node (rn);
+        }
+      if (hie->u.vpn.mon_eth)
+        {
+          if (skiplist_count (hie->u.vpn.mon_eth))
+            return;
+          skiplist_free (hie->u.vpn.mon_eth);
+          hie->u.vpn.mon_eth = NULL;
+          route_unlock_node (rn);
+        }
+      break;
+
+    default:
+      assert (0);
+    }
+  XFREE (MTYPE_RFAPI_IT_EXTRA, hie);
+  rn->aggregate = NULL;
+  route_unlock_node (rn);
+}
+
+/*
+ * returns locked node
+ */
+struct route_node *
+rfapiMonitorGetAttachNode (struct rfapi_descriptor *rfd, struct prefix *p)
+{
+  afi_t afi;
+  struct route_node *rn;
+
+  if (RFAPI_0_PREFIX (p))
+    {
+      assert (1);
+    }
+
+  afi = family2afi (p->family);
+  assert (afi);
+
+  /*
+   * It's possible that even though there is a route at this node,
+   * there are no routes with valid UN addresses (i.e,. with no
+   * valid tunnel routes). Check for that and walk back up the
+   * tree if necessary.
+   *
+   * When the outer loop completes, the matched node, if any, is
+   * locked (i.e., its reference count has been incremented) to
+   * account for the VPN monitor we are about to attach.
+   *
+   * if a monitor is moved to another node, there must be
+   * corresponding unlock/locks
+   */
+  for (rn = route_node_match (rfd->import_table->imported_vpn[afi], p); rn;)
+    {
+
+      struct bgp_info *bi;
+      struct prefix pfx_dummy;
+
+      /* TBD update this code to use new valid_interior_count */
+      for (bi = rn->info; bi; bi = bi->next)
+        {
+          /*
+           * If there is a cached ENCAP UN address, it's a usable
+           * VPN route
+           */
+          if (bi->extra && bi->extra->vnc.import.un_family)
+            {
+              break;
+            }
+
+          /*
+           * Or if there is a valid Encap Attribute tunnel subtlv address,
+           * it's a usable VPN route.
+           */
+          if (!rfapiGetVncTunnelUnAddr (bi->attr, &pfx_dummy))
+            {
+              break;
+            }
+        }
+      if (bi)
+        break;
+
+      route_unlock_node (rn);
+      if ((rn = rn->parent))
+        {
+          route_lock_node (rn);
+        }
+    }
+
+  if (!rn)
+    {
+      struct prefix pfx_default;
+
+      memset (&pfx_default, 0, sizeof (pfx_default));
+      pfx_default.family = p->family;
+
+      /* creates default node if none exists, and increments ref count */
+      rn =
+        route_node_get (rfd->import_table->imported_vpn[afi], &pfx_default);
+    }
+
+  return rn;
+}
+
+/* 
+ * If this function happens to attach the monitor to a radix tree
+ * node (as opposed to the 0-prefix list), the node pointer is
+ * returned (for the benefit of caller which might like to use it
+ * to generate an immediate query response).
+ */
+static struct route_node *
+rfapiMonitorAttachImport (struct rfapi_descriptor *rfd,
+                          struct rfapi_monitor_vpn *m)
+{
+  struct route_node *rn;
+
+  rfapiMonitorCheckAttachAllowed ();
+
+  if (RFAPI_0_PREFIX (&m->p))
+    {
+      /*
+       * Add new monitor entry to vpn0 list
+       */
+      afi_t afi;
+
+      afi = family2afi (m->p.family);
+      assert (afi);
+
+      m->next = rfd->import_table->vpn0_queries[afi];
+      rfd->import_table->vpn0_queries[afi] = m;
+      zlog_debug ("%s: attached monitor %p to vpn0 list", __func__, m);
+      return NULL;
+    }
+
+  /*
+   * Attach new monitor entry to import table node
+   */
+  rn = rfapiMonitorGetAttachNode (rfd, &m->p);  /* returns locked rn */
+  m->node = rn;
+  m->next = RFAPI_MONITOR_VPN (rn);
+  RFAPI_MONITOR_VPN_W_ALLOC (rn) = m;
+  RFAPI_CHECK_REFCOUNT (rn, SAFI_MPLS_VPN, 0);
+  zlog_debug ("%s: attached monitor %p to rn %p", __func__, m, rn);
+  return rn;
+}
+
+
+/*
+ * reattach monitors for this HD to import table
+ */
+void
+rfapiMonitorAttachImportHd (struct rfapi_descriptor *rfd)
+{
+  struct route_node *mrn;
+
+  if (!rfd->mon)
+    {
+      /*
+       * No monitors for this HD
+       */
+      return;
+    }
+
+  for (mrn = route_top (rfd->mon); mrn; mrn = route_next (mrn))
+    {
+
+      if (!mrn->info)
+        continue;
+
+      (void) rfapiMonitorAttachImport (rfd,
+                                       (struct rfapi_monitor_vpn
+                                        *) (mrn->info));
+    }
+}
+
+/*
+ * Adds a monitor for a query to the NVE descriptor's list
+ * and, if callbacks are enabled, attaches it to the import table.
+ *
+ * If we happened to locate the import table radix tree attachment
+ * point, return it so the caller can use it to generate a query
+ * response without repeating the lookup. Note that when callbacks
+ * are disabled, this function will not perform a lookup, and the
+ * caller will have to do its own lookup.
+ */
+struct route_node *
+rfapiMonitorAdd (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct prefix                        *p)
+{
+  struct rfapi_monitor_vpn *m;
+  struct route_node *rn;
+
+  /*
+   * Initialize nve's monitor list if needed
+   * NB use the same radix tree for IPv4 and IPv6 targets.
+   * The prefix will always have full-length mask (/32, /128)
+   * or be 0/0 so they won't get mixed up. 
+   */
+  if (!rfd->mon)
+    {
+      rfd->mon = route_table_init ();
+    }
+  rn = route_node_get (rfd->mon, p);
+  if (rn->info)
+    {
+      /*
+       * received this query before, no further action needed
+       */
+      rfapiMonitorTimerRestart ((struct rfapi_monitor_vpn *) rn->info);
+      route_unlock_node (rn);
+      return NULL;
+    }
+
+  /*
+   * New query for this nve, record it in the HD
+   */
+  rn->info = XCALLOC (MTYPE_RFAPI_MONITOR, sizeof (struct rfapi_monitor_vpn));
+  m = (struct rfapi_monitor_vpn *) (rn->info);
+  m->rfd = rfd;
+  prefix_copy (&m->p, p);
+
+  ++rfd->monitor_count;
+  ++bgp->rfapi->monitor_count;
+
+  rfapiMonitorTimerRestart (m);
+
+  if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)
+    {
+      /*
+       * callbacks turned off, so don't attach monitor to import table
+       */
+      return NULL;
+    }
+
+
+  /*
+   * attach to import table
+   */
+  return rfapiMonitorAttachImport (rfd, m);
+}
+
+/*
+ * returns monitor pointer if found, NULL if not
+ */
+static struct rfapi_monitor_vpn *
+rfapiMonitorDetachImport (struct rfapi_monitor_vpn *m)
+{
+  struct rfapi_monitor_vpn *prev;
+  struct rfapi_monitor_vpn *this = NULL;
+
+  if (RFAPI_0_PREFIX (&m->p))
+    {
+      afi_t afi;
+
+      /*
+       * 0-prefix monitors are stored in a special list and not
+       * in the import VPN tree
+       */
+
+      afi = family2afi (m->p.family);
+      assert (afi);
+
+      if (m->rfd->import_table)
+        {
+          for (prev = NULL, this = m->rfd->import_table->vpn0_queries[afi];
+               this; prev = this, this = this->next)
+            {
+
+              if (this == m)
+                break;
+            }
+          if (this)
+            {
+              if (!prev)
+                {
+                  m->rfd->import_table->vpn0_queries[afi] = this->next;
+                }
+              else
+                {
+                  prev->next = this->next;
+                }
+            }
+        }
+    }
+  else
+    {
+
+      if (m->node)
+        {
+          for (prev = NULL,
+               this = RFAPI_MONITOR_VPN (m->node);
+               this; prev = this, this = this->next)
+            {
+
+              if (this == m)
+                break;
+            }
+          if (this)
+            {
+              if (prev)
+                {
+                  prev->next = this->next;
+                }
+              else
+                {
+                  RFAPI_MONITOR_VPN_W_ALLOC (m->node) = this->next;
+                }
+              RFAPI_CHECK_REFCOUNT (m->node, SAFI_MPLS_VPN, 1);
+              route_unlock_node (m->node);
+            }
+          m->node = NULL;
+        }
+    }
+  return this;
+}
+
+
+void
+rfapiMonitorDetachImportHd (struct rfapi_descriptor *rfd)
+{
+  struct route_node *rn;
+
+  if (!rfd->mon)
+    return;
+
+  for (rn = route_top (rfd->mon); rn; rn = route_next (rn))
+    {
+      if (rn->info)
+        {
+          rfapiMonitorDetachImport ((struct rfapi_monitor_vpn *) (rn->info));
+        }
+    }
+}
+
+void
+rfapiMonitorDel (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct prefix                        *p)
+{
+  struct route_node *rn;
+  struct rfapi_monitor_vpn *m;
+
+  assert (rfd->mon);
+  rn = route_node_get (rfd->mon, p);    /* locks node */
+  m = rn->info;
+
+  assert (m);
+
+  /*
+   * remove from import table
+   */
+  if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE))
+    {
+      rfapiMonitorDetachImport (m);
+    }
+
+  if (m->timer)
+    {
+      thread_cancel (m->timer);
+      m->timer = NULL;
+    }
+
+  /*
+   * remove from rfd list
+   */
+  XFREE (MTYPE_RFAPI_MONITOR, m);
+  rn->info = NULL;
+  route_unlock_node (rn);       /* undo original lock when created */
+  route_unlock_node (rn);       /* undo lock in route_node_get */
+
+  --rfd->monitor_count;
+  --bgp->rfapi->monitor_count;
+}
+
+/*
+ * returns count of monitors deleted
+ */
+int
+rfapiMonitorDelHd (struct rfapi_descriptor *rfd)
+{
+  struct route_node *rn;
+  struct bgp *bgp;
+  int count = 0;
+
+  zlog_debug ("%s: entry rfd=%p", __func__, rfd);
+
+  bgp = bgp_get_default ();
+
+  if (rfd->mon)
+    {
+      for (rn = route_top (rfd->mon); rn; rn = route_next (rn))
+        {
+          struct rfapi_monitor_vpn *m;
+          if ((m = rn->info))
+            {
+              if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE))
+                {
+                  rfapiMonitorDetachImport (m);
+                }
+
+              if (m->timer)
+                {
+                  thread_cancel (m->timer);
+                  m->timer = NULL;
+                }
+
+              XFREE (MTYPE_RFAPI_MONITOR, m);
+              rn->info = NULL;
+              route_unlock_node (rn);   /* undo original lock when created */
+              ++count;
+              --rfd->monitor_count;
+              --bgp->rfapi->monitor_count;
+            }
+        }
+      route_table_finish (rfd->mon);
+      rfd->mon = NULL;
+    }
+
+  if (rfd->mon_eth)
+    {
+
+      struct rfapi_monitor_eth *mon_eth;
+
+      while (!skiplist_first (rfd->mon_eth, NULL, (void **) &mon_eth))
+        {
+
+          int rc;
+
+          if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE))
+            {
+              rfapiMonitorEthDetachImport (bgp, mon_eth);
+            }
+          else
+            {
+#if DEBUG_L2_EXTRA
+              zlog_debug
+                ("%s: callbacks disabled, not attempting to detach mon_eth %p",
+                 __func__, mon_eth);
+#endif
+            }
+
+          if (mon_eth->timer)
+            {
+              thread_cancel (mon_eth->timer);
+              mon_eth->timer = NULL;
+            }
+
+          /*
+           * remove from rfd list
+           */
+          rc = skiplist_delete (rfd->mon_eth, mon_eth, mon_eth);
+          assert (!rc);
+
+          zlog_debug ("%s: freeing mon_eth %p", __func__, mon_eth);
+          XFREE (MTYPE_RFAPI_MONITOR_ETH, mon_eth);
+
+          ++count;
+          --rfd->monitor_count;
+          --bgp->rfapi->monitor_count;
+        }
+      skiplist_free (rfd->mon_eth);
+      rfd->mon_eth = NULL;
+
+    }
+
+  return count;
+}
+
+void
+rfapiMonitorResponseRemovalOff (struct bgp *bgp)
+{
+  if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)
+    {
+      return;
+    }
+  bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE;
+}
+
+void
+rfapiMonitorResponseRemovalOn (struct bgp *bgp)
+{
+  if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE))
+    {
+      return;
+    }
+  bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE;
+}
+
+static int
+rfapiMonitorTimerExpire (struct thread *t)
+{
+  struct rfapi_monitor_vpn *m = t->arg;
+
+  /* forget reference to thread, it's gone */
+  m->timer = NULL;
+
+  /* delete the monitor */
+  rfapiMonitorDel (bgp_get_default (), m->rfd, &m->p);
+
+  return 0;
+}
+
+static void
+rfapiMonitorTimerRestart (struct rfapi_monitor_vpn *m)
+{
+  if (m->timer)
+    {
+      unsigned long remain = thread_timer_remain_second (m->timer);
+
+      /* unexpected case, but avoid wraparound problems below */
+      if (remain > m->rfd->response_lifetime)
+        return;
+
+      /* don't restart if we just restarted recently */
+      if (m->rfd->response_lifetime - remain < 2)
+        return;
+
+      thread_cancel (m->timer);
+      m->timer = NULL;
+    }
+
+  {
+    char buf[BUFSIZ];
+
+    zlog_debug ("%s: target %s life %u", __func__,
+                rfapi_ntop (m->p.family, m->p.u.val, buf, BUFSIZ),
+                m->rfd->response_lifetime);
+  }
+  m->timer = thread_add_timer (bm->master, rfapiMonitorTimerExpire, m,
+                               m->rfd->response_lifetime);
+}
+
+/* 
+ * called when an updated response is sent to the NVE. Per
+ * ticket 255, restart timers for any monitors that could have
+ * been responsible for the response, i.e., any monitors for
+ * the exact prefix or a parent of it.
+ */
+void
+rfapiMonitorTimersRestart (struct rfapi_descriptor *rfd, struct prefix *p)
+{
+  struct route_node *rn;
+
+  if (AF_ETHERNET == p->family)
+    {
+      struct rfapi_monitor_eth *mon_eth;
+      int rc;
+      void *cursor;
+
+      /*
+       * XXX match any LNI
+       */
+      for (cursor = NULL,
+           rc =
+           skiplist_next (rfd->mon_eth, NULL, (void **) &mon_eth, &cursor);
+           rc == 0;
+           rc =
+           skiplist_next (rfd->mon_eth, NULL, (void **) &mon_eth, &cursor))
+        {
+
+          if (!memcmp (mon_eth->macaddr.octet, p->u.prefix_eth.octet,
+                       ETHER_ADDR_LEN))
+            {
+
+              rfapiMonitorEthTimerRestart (mon_eth);
+
+            }
+        }
+
+    }
+  else
+    {
+      for (rn = route_top (rfd->mon); rn; rn = route_next (rn))
+        {
+          struct rfapi_monitor_vpn *m;
+
+          if (!((m = rn->info)))
+            continue;
+
+          /* NB order of test is significant ! */
+          if (!m->node || prefix_match (&m->node->p, p))
+            {
+              rfapiMonitorTimerRestart (m);
+            }
+        }
+    }
+}
+
+/*
+ * Find monitors at this node and all its parents. Call
+ * rfapiRibUpdatePendingNode with this node and all corresponding NVEs.
+ */
+void
+rfapiMonitorItNodeChanged (
+  struct rfapi_import_table    *import_table,
+  struct route_node            *it_node,
+  struct rfapi_monitor_vpn     *monitor_list) /* for base it node, NULL=all */
+{
+  struct skiplist   *nves_seen;
+  struct route_node *rn = it_node;
+  struct bgp        *bgp = bgp_get_default ();
+  afi_t             afi = family2afi (rn->p.family);
+#if DEBUG_L2_EXTRA
+  char              buf_prefix[BUFSIZ];
+#endif
+
+  assert (bgp);
+  assert (import_table);
+
+  nves_seen = skiplist_new (0, NULL, NULL);
+
+#if DEBUG_L2_EXTRA
+  prefix2str (&it_node->p, buf_prefix, BUFSIZ);
+  zlog_debug ("%s: it=%p, it_node=%p, it_node->prefix=%s",
+              __func__, import_table, it_node, buf_prefix);
+#endif
+
+  if (AFI_ETHER == afi)
+    {
+      struct rfapi_monitor_eth *m;
+      struct skiplist *sl;
+      void *cursor;
+      int rc;
+
+      if ((sl = RFAPI_MONITOR_ETH (rn)))
+        {
+
+          for (cursor = NULL,
+            rc = skiplist_next (sl, NULL, (void **) &m, (void **) &cursor);
+            !rc;
+            rc = skiplist_next (sl, NULL, (void **) &m, (void **) &cursor))
+            {
+
+              if (skiplist_search (nves_seen, m->rfd, NULL))
+                {
+                  /*
+                   * Haven't done this NVE yet. Add to "seen" list.
+                   */
+                  assert (!skiplist_insert (nves_seen, m->rfd, NULL));
+
+                  /*
+                   * update its RIB
+                   */
+                  rfapiRibUpdatePendingNode(bgp, m->rfd, import_table,
+                    it_node, m->rfd->response_lifetime);
+                }
+            }
+        }
+
+    }
+  else
+    {
+
+      struct rfapi_monitor_vpn *m;
+
+      if (monitor_list)
+        {
+          m = monitor_list;
+        }
+      else
+        {
+          m = RFAPI_MONITOR_VPN (rn);
+        }
+
+      do
+        {
+          /* 
+           * If we have reached the root node (parent==NULL) and there
+           * are no routes here (info==NULL), and the IT node that
+           * changed was not the root node (it_node->parent != NULL),
+           * then any monitors at this node are here because they had
+           * no match at all. Therefore, do not send route updates to them
+           * because we haven't sent them an initial route.
+           */
+          if (!rn->parent && !rn->info && it_node->parent)
+            break;
+
+          for (; m; m = m->next)
+            {
+
+              if (RFAPI_0_PREFIX (&m->p))
+                {
+                  /* shouldn't happen, but be safe */
+                  continue;
+                }
+              if (skiplist_search (nves_seen, m->rfd, NULL))
+                {
+                  /*
+                   * Haven't done this NVE yet. Add to "seen" list.
+                   */
+                  assert (!skiplist_insert (nves_seen, m->rfd, NULL));
+
+                  {
+                    char buf_attach_pfx[BUFSIZ];
+                    char buf_target_pfx[BUFSIZ];
+
+                    prefix2str (&m->node->p, buf_attach_pfx, BUFSIZ);
+                    prefix2str (&m->p, buf_target_pfx, BUFSIZ);
+                    zlog_debug
+                      ("%s: update rfd %p attached to pfx %s (targ=%s)",
+                       __func__, m->rfd, buf_attach_pfx, buf_target_pfx);
+                  }
+
+                  /*
+                   * update its RIB
+                   */
+                  rfapiRibUpdatePendingNode(bgp, m->rfd, import_table,
+                    it_node, m->rfd->response_lifetime);
+                }
+            }
+          rn = rn->parent;
+          if (rn)
+            m = RFAPI_MONITOR_VPN (rn);
+        }
+      while (rn);
+    }
+
+  /*
+   * All-routes L2 monitors
+   */
+  if (AFI_ETHER == afi)
+    {
+      struct rfapi_monitor_eth *e;
+
+#if DEBUG_L2_EXTRA
+      zlog_debug ("%s: checking L2 all-routes monitors", __func__);
+#endif
+
+      for (e = import_table->eth0_queries; e; e = e->next)
+        {
+#if DEBUG_L2_EXTRA
+          zlog_debug ("%s: checking eth0 mon=%p", __func__, e);
+#endif
+          if (skiplist_search (nves_seen, e->rfd, NULL))
+            {
+              /*
+               * Haven't done this NVE yet. Add to "seen" list.
+               */
+              assert (!skiplist_insert (nves_seen, e->rfd, NULL));
+
+              /*
+               * update its RIB
+               */
+#if DEBUG_L2_EXTRA
+              zlog_debug ("%s: found L2 all-routes monitor %p", __func__, e);
+#endif
+              rfapiRibUpdatePendingNode (bgp, e->rfd, import_table, it_node,
+                e->rfd->response_lifetime);
+            }
+        }
+    }
+  else
+    {
+      struct rfapi_monitor_vpn *m;
+
+      /*
+       * All-routes IPv4. IPv6 monitors
+       */
+      for (m = import_table->vpn0_queries[afi]; m; m = m->next)
+        {
+          if (skiplist_search (nves_seen, m->rfd, NULL))
+            {
+              /*
+               * Haven't done this NVE yet. Add to "seen" list.
+               */
+              assert (!skiplist_insert (nves_seen, m->rfd, NULL));
+
+              /*
+               * update its RIB
+               */
+              rfapiRibUpdatePendingNode (bgp, m->rfd, import_table, it_node,
+                m->rfd->response_lifetime);
+            }
+        }
+    }
+
+  skiplist_free (nves_seen);
+}
+
+/*
+ * For the listed monitors, update new node and its subtree, but
+ * omit old node and its subtree
+ */
+void
+rfapiMonitorMovedUp (
+  struct rfapi_import_table    *import_table,
+  struct route_node            *old_node,
+  struct route_node            *new_node,
+  struct rfapi_monitor_vpn     *monitor_list)
+{
+  struct bgp *bgp = bgp_get_default ();
+  struct rfapi_monitor_vpn *m;
+
+  assert (new_node);
+  assert (old_node);
+  assert (new_node != old_node);
+
+  /*
+   * If new node is 0/0 and there is no route there, don't
+   * generate an update because it will not contain any
+   * routes including the target.
+   */
+  if (!new_node->parent && !new_node->info)
+    {
+      zlog_debug ("%s: new monitor at 0/0 and no routes, no updates",
+                  __func__);
+      return;
+    }
+
+  for (m = monitor_list; m; m = m->next)
+    {
+      rfapiRibUpdatePendingNode (bgp, m->rfd, import_table, new_node,
+                                 m->rfd->response_lifetime);
+      rfapiRibUpdatePendingNodeSubtree (bgp, m->rfd, import_table, new_node,
+                                        old_node, m->rfd->response_lifetime);
+    }
+}
+
+static int
+rfapiMonitorEthTimerExpire (struct thread *t)
+{
+  struct rfapi_monitor_eth *m = t->arg;
+
+  /* forget reference to thread, it's gone */
+  m->timer = NULL;
+
+  /* delete the monitor */
+  rfapiMonitorEthDel (bgp_get_default (), m->rfd, &m->macaddr,
+                      m->logical_net_id);
+
+  return 0;
+}
+
+static void
+rfapiMonitorEthTimerRestart (struct rfapi_monitor_eth *m)
+{
+  if (m->timer)
+    {
+      unsigned long remain = thread_timer_remain_second (m->timer);
+
+      /* unexpected case, but avoid wraparound problems below */
+      if (remain > m->rfd->response_lifetime)
+        return;
+
+      /* don't restart if we just restarted recently */
+      if (m->rfd->response_lifetime - remain < 2)
+        return;
+
+      thread_cancel (m->timer);
+      m->timer = NULL;
+    }
+
+  {
+    char buf[BUFSIZ];
+
+    zlog_debug ("%s: target %s life %u", __func__,
+                rfapiEthAddr2Str (&m->macaddr, buf, BUFSIZ),
+                m->rfd->response_lifetime);
+  }
+  m->timer = thread_add_timer (bm->master, rfapiMonitorEthTimerExpire, m,
+                               m->rfd->response_lifetime);
+}
+
+static int
+mon_eth_cmp (void *a, void *b)
+{
+  struct rfapi_monitor_eth *m1;
+  struct rfapi_monitor_eth *m2;
+
+  int i;
+
+  m1 = (struct rfapi_monitor_eth *) a;
+  m2 = (struct rfapi_monitor_eth *) b;
+
+  /*
+   * compare ethernet addresses
+   */
+  for (i = 0; i < ETHER_ADDR_LEN; ++i)
+    {
+      if (m1->macaddr.octet[i] != m2->macaddr.octet[i])
+        return (m1->macaddr.octet[i] - m2->macaddr.octet[i]);
+    }
+
+  /*
+   * compare LNIs
+   */
+  return (m1->logical_net_id - m2->logical_net_id);
+}
+
+static void
+rfapiMonitorEthAttachImport (
+  struct rfapi_import_table    *it,
+  struct route_node            *rn,      /* it node attach point if non-0 */
+  struct rfapi_monitor_eth     *mon)     /* monitor struct to attach */
+{
+  struct skiplist *sl;
+  int rc;
+
+  zlog_debug ("%s: it=%p", __func__, it);
+
+  rfapiMonitorCheckAttachAllowed ();
+
+  if (RFAPI_0_ETHERADDR (&mon->macaddr))
+    {
+      /*
+       * These go on a different list
+       */
+      mon->next = it->eth0_queries;
+      it->eth0_queries = mon;
+#if DEBUG_L2_EXTRA
+      zlog_debug ("%s: attached monitor %p to eth0 list", __func__, mon);
+#endif
+      return;
+    }
+
+  if (rn == NULL)
+    {
+#if DEBUG_L2_EXTRA
+      zlog_debug ("%s: rn is null!", __func__);
+#endif
+      return;
+    }
+
+  /*
+   * Get sl to attach to
+   */
+  sl = RFAPI_MONITOR_ETH_W_ALLOC (rn);
+  if (!sl)
+    {
+      sl = RFAPI_MONITOR_ETH_W_ALLOC (rn) = skiplist_new (0, NULL, NULL);
+      route_lock_node(rn);     /* count skiplist mon_eth */
+    }
+
+#if DEBUG_L2_EXTRA
+  zlog_debug ("%s: rn=%p, rn->lock=%d, sl=%p, attaching eth mon %p",
+    __func__, rn, rn->lock, sl, mon);
+#endif
+
+  rc = skiplist_insert (sl, (void *) mon, (void *) mon);
+  assert (!rc);
+
+  /* count eth monitor */
+  route_lock_node(rn);
+}
+
+/*
+ * reattach monitors for this HD to import table
+ */
+static void
+rfapiMonitorEthAttachImportHd (struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+  void *cursor;
+  struct rfapi_monitor_eth *mon;
+  int rc;
+
+  if (!rfd->mon_eth)
+    {
+      /*
+       * No monitors for this HD
+       */
+      return;
+    }
+
+  for (cursor = NULL,
+       rc = skiplist_next (rfd->mon_eth, NULL, (void **) &mon, &cursor);
+       rc == 0;
+       rc = skiplist_next (rfd->mon_eth, NULL, (void **) &mon, &cursor))
+    {
+
+      struct rfapi_import_table *it;
+      struct prefix pfx_mac_buf;
+      struct route_node *rn;
+
+      it = rfapiMacImportTableGet (bgp, mon->logical_net_id);
+      assert (it);
+
+      memset ((void *) &pfx_mac_buf, 0, sizeof (struct prefix));
+      pfx_mac_buf.family = AF_ETHERNET;
+      pfx_mac_buf.prefixlen = 48;
+      pfx_mac_buf.u.prefix_eth = mon->macaddr;
+
+      rn = route_node_get (it->imported_vpn[AFI_ETHER], &pfx_mac_buf);
+      assert (rn);
+
+      (void) rfapiMonitorEthAttachImport (it, rn, mon);
+    }
+}
+
+static void
+rfapiMonitorEthDetachImport (
+  struct bgp                   *bgp,
+  struct rfapi_monitor_eth     *mon)    /* monitor struct to detach */
+{
+  struct rfapi_import_table *it;
+  struct prefix pfx_mac_buf;
+  struct skiplist *sl;
+  struct route_node *rn;
+  int rc;
+
+  it = rfapiMacImportTableGet (bgp, mon->logical_net_id);
+  assert (it);
+
+  if (RFAPI_0_ETHERADDR (&mon->macaddr))
+    {
+      struct rfapi_monitor_eth *prev;
+      struct rfapi_monitor_eth *this = NULL;
+
+      for (prev = NULL,
+           this = it->eth0_queries; this; prev = this, this = this->next)
+        {
+
+          if (this == mon)
+            break;
+        }
+      if (this)
+        {
+          if (!prev)
+            {
+              it->eth0_queries = this->next;
+            }
+          else
+            {
+              prev->next = this->next;
+            }
+        }
+#if DEBUG_L2_EXTRA
+      zlog_debug ("%s: it=%p, LNI=%d, detached eth0 mon %p",
+                  __func__, it, mon->logical_net_id, mon);
+#endif
+      return;
+    }
+
+  memset ((void *) &pfx_mac_buf, 0, sizeof (struct prefix));
+  pfx_mac_buf.family = AF_ETHERNET;
+  pfx_mac_buf.prefixlen = 48;
+  pfx_mac_buf.u.prefix_eth = mon->macaddr;
+
+  rn = route_node_get (it->imported_vpn[AFI_ETHER], &pfx_mac_buf);
+  assert (rn);
+
+#if DEBUG_L2_EXTRA
+  char buf_prefix[BUFSIZ];
+  prefix2str (&rn->p, buf_prefix, BUFSIZ);
+#endif
+
+  /*
+   * Get sl to detach from
+   */
+  sl = RFAPI_MONITOR_ETH (rn);
+#if DEBUG_L2_EXTRA
+  zlog_debug ("%s: it=%p, rn=%p, rn->lock=%d, sl=%p, pfx=%s, LNI=%d, detaching eth mon %p",
+              __func__, it, rn, rn->lock, sl, buf_prefix, mon->logical_net_id, mon);
+#endif
+  assert (sl);
+
+
+  rc = skiplist_delete (sl, (void *) mon, (void *) mon);
+  assert (!rc);
+
+  /* uncount eth monitor */
+  route_unlock_node(rn);
+}
+
+struct route_node *
+rfapiMonitorEthAdd (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct ethaddr               *macaddr,
+  uint32_t                     logical_net_id)
+{
+  int                           rc;
+  struct rfapi_monitor_eth      mon_buf;
+  struct rfapi_monitor_eth      *val;
+  struct rfapi_import_table     *it;
+  struct route_node             *rn = NULL;
+  struct prefix                 pfx_mac_buf;
+
+  if (!rfd->mon_eth)
+    {
+      rfd->mon_eth = skiplist_new (0, mon_eth_cmp, NULL);
+    }
+
+  it = rfapiMacImportTableGet (bgp, logical_net_id);
+  assert (it);
+
+  /*
+   * Get route node in import table. Here is where we attach the
+   * monitor.
+   *
+   * Look it up now because we return it to caller regardless of
+   * whether we create a new monitor or not.
+   */
+  memset ((void *) &pfx_mac_buf, 0, sizeof (struct prefix));
+  pfx_mac_buf.family = AF_ETHERNET;
+  pfx_mac_buf.prefixlen = 48;
+  pfx_mac_buf.u.prefix_eth = *macaddr;
+
+  if (!RFAPI_0_ETHERADDR (macaddr))
+    {
+      rn = route_node_get (it->imported_vpn[AFI_ETHER], &pfx_mac_buf);
+      assert (rn);
+    }
+
+  memset ((void *) &mon_buf, 0, sizeof (mon_buf));
+  mon_buf.rfd = rfd;
+  mon_buf.macaddr = *macaddr;
+  mon_buf.logical_net_id = logical_net_id;
+
+  {
+    char buf[BUFSIZ];
+
+    zlog_debug ("%s: LNI=%d: rfd=%p, pfx=%s",
+                __func__, logical_net_id, rfd,
+                rfapi_ntop (pfx_mac_buf.family, pfx_mac_buf.u.val, buf,
+                            BUFSIZ));
+  }
+
+
+  /*
+   * look up query
+   */
+  rc = skiplist_search (rfd->mon_eth, (void *) &mon_buf, (void **) &val);
+  if (!rc)
+    {
+      /*
+       * Found monitor - we have seen this query before
+       * restart timer
+       */
+      zlog_debug ("%s: already present in rfd->mon_eth, not adding",
+                  __func__);
+      rfapiMonitorEthTimerRestart (val);
+      return rn;
+    }
+
+  /*
+   * New query
+   */
+  val = XCALLOC (MTYPE_RFAPI_MONITOR_ETH, sizeof (struct rfapi_monitor_eth));
+  assert (val);
+  *val = mon_buf;
+
+  ++rfd->monitor_count;
+  ++bgp->rfapi->monitor_count;
+
+  rc = skiplist_insert (rfd->mon_eth, val, val);
+
+#if DEBUG_L2_EXTRA
+  zlog_debug ("%s: inserted rfd=%p mon_eth=%p, rc=%d", __func__, rfd, val,
+              rc);
+#endif
+
+  /*
+   * start timer
+   */
+  rfapiMonitorEthTimerRestart (val);
+
+  if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)
+    {
+      /*
+       * callbacks turned off, so don't attach monitor to import table
+       */
+#if DEBUG_L2_EXTRA
+      zlog_debug
+        ("%s: callbacks turned off, not attaching mon_eth %p to import table",
+         __func__, val);
+#endif
+      return rn;
+    }
+
+  /*
+   * attach to import table
+   */
+  rfapiMonitorEthAttachImport (it, rn, val);
+
+  return rn;
+}
+
+void
+rfapiMonitorEthDel (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct ethaddr               *macaddr,
+  uint32_t                     logical_net_id)
+{
+  struct rfapi_monitor_eth *val;
+  struct rfapi_monitor_eth mon_buf;
+  int rc;
+
+  zlog_debug ("%s: entry rfd=%p", __func__, rfd);
+
+  assert (rfd->mon_eth);
+
+  memset ((void *) &mon_buf, 0, sizeof (mon_buf));
+  mon_buf.macaddr = *macaddr;
+  mon_buf.logical_net_id = logical_net_id;
+
+  rc = skiplist_search (rfd->mon_eth, (void *) &mon_buf, (void **) &val);
+  assert (!rc);
+
+  /*
+   * remove from import table
+   */
+  if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE))
+    {
+      rfapiMonitorEthDetachImport (bgp, val);
+    }
+
+  if (val->timer)
+    {
+      thread_cancel (val->timer);
+      val->timer = NULL;
+    }
+
+  /*
+   * remove from rfd list
+   */
+  rc = skiplist_delete (rfd->mon_eth, val, val);
+  assert (!rc);
+
+#if DEBUG_L2_EXTRA
+  zlog_debug ("%s: freeing mon_eth %p", __func__, val);
+#endif
+  XFREE (MTYPE_RFAPI_MONITOR_ETH, val);
+
+  --rfd->monitor_count;
+  --bgp->rfapi->monitor_count;
+}
+
+
+void
+rfapiMonitorCallbacksOff (struct bgp *bgp)
+{
+  struct rfapi_import_table *it;
+  afi_t                     afi;
+  struct route_table        *rt;
+  struct route_node         *rn;
+  void                      *cursor;
+  int                       rc;
+  struct rfapi              *h = bgp->rfapi;
+
+  if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)
+    {
+      /*
+       * Already off.
+       */
+      return;
+    }
+  bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_CALLBACK_DISABLE;
+
+#if DEBUG_L2_EXTRA
+  zlog_debug ("%s: turned off callbacks", __func__);
+#endif
+
+  if (h == NULL)
+    return;
+  /*
+   * detach monitors from import VPN tables. The monitors
+   * will still be linked in per-nve monitor lists.
+   */
+  for (it = h->imports; it; it = it->next)
+    {
+      for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+        {
+
+          struct rfapi_monitor_vpn *m;
+          struct rfapi_monitor_vpn *next;
+
+          rt = it->imported_vpn[afi];
+
+          for (rn = route_top (rt); rn; rn = route_next (rn))
+            {
+              m = RFAPI_MONITOR_VPN (rn);
+              if (RFAPI_MONITOR_VPN (rn))
+                RFAPI_MONITOR_VPN_W_ALLOC (rn) = NULL;
+              for (; m; m = next)
+                {
+                  next = m->next;
+                  m->next = NULL;       /* gratuitous safeness */
+                  m->node = NULL;
+                  route_unlock_node (rn);       /* uncount */
+                }
+            }
+
+          for (m = it->vpn0_queries[afi]; m; m = next)
+            {
+              next = m->next;
+              m->next = NULL;   /* gratuitous safeness */
+              m->node = NULL;
+            }
+          it->vpn0_queries[afi] = NULL; /* detach first monitor */
+        }
+    }
+
+  /*
+   * detach monitors from import Eth tables. The monitors
+   * will still be linked in per-nve monitor lists.
+   */
+
+  /*
+   * Loop over ethernet import tables
+   */
+  for (cursor = NULL,
+       rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor);
+       !rc; rc = skiplist_next (h->import_mac, NULL, (void **) &it, &cursor))
+    {
+      struct rfapi_monitor_eth *e;
+      struct rfapi_monitor_eth *enext;
+
+      /*
+       * The actual route table
+       */
+      rt = it->imported_vpn[AFI_ETHER];
+
+      /* 
+       * Find non-0 monitors (i.e., actual addresses, not FTD monitors)
+       */
+      for (rn = route_top (rt); rn; rn = route_next (rn))
+        {
+         struct skiplist *sl;
+
+         sl = RFAPI_MONITOR_ETH (rn);
+         while (!skiplist_delete_first(sl))
+           {
+              route_unlock_node (rn);   /* uncount monitor */
+           }
+        }
+
+      /*
+       * Find 0-monitors (FTD queries)
+       */
+      for (e = it->eth0_queries; e; e = enext)
+        {
+#if DEBUG_L2_EXTRA
+          zlog_debug ("%s: detaching eth0 mon %p", __func__, e);
+#endif
+          enext = e->next;
+          e->next = NULL;       /* gratuitous safeness */
+        }
+      it->eth0_queries = NULL;  /* detach first monitor */
+    }
+}
+
+void
+rfapiMonitorCallbacksOn (struct bgp *bgp)
+{
+  struct listnode *hnode;
+  struct rfapi_descriptor *rfd;
+
+  if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE))
+    {
+      /*
+       * Already on. It's important that we don't try to reattach
+       * monitors that are already attached because, in the interest
+       * of performance, there is no checking at the lower level 
+       * whether a monitor is already attached. It leads to
+       * corrupted chains (e.g., looped pointers)
+       */
+      return;
+    }
+  bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_CALLBACK_DISABLE;
+#if DEBUG_L2_EXTRA
+  zlog_debug ("%s: turned on callbacks", __func__);
+#endif
+  if (bgp->rfapi == NULL)
+    return;
+
+  /*
+   * reattach monitors
+   */
+  for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, hnode, rfd))
+    {
+
+      rfapiMonitorAttachImportHd (rfd);
+      rfapiMonitorEthAttachImportHd (bgp, rfd);
+    }
+}
diff --git a/bgpd/rfapi/rfapi_monitor.h b/bgpd/rfapi/rfapi_monitor.h
new file mode 100644 (file)
index 0000000..b08a6e6
--- /dev/null
@@ -0,0 +1,217 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef QUAGGA_HGP_RFAPI_MONITOR_H
+#define QUAGGA_HGP_RFAPI_MONITOR_H
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+
+/*
+ * These get attached to the nodes in an import table (using "aggregate" ptr)
+ * to indicate which nves are interested in a prefix/target
+ */
+struct rfapi_monitor_vpn
+{
+  struct rfapi_monitor_vpn     *next;  /* chain from struct route_node */
+  struct rfapi_descriptor      *rfd;   /* which NVE requested the route */
+  struct prefix                        p;      /* constant: pfx in original request */
+  struct route_node            *node;  /* node we're currently attached to */
+  uint32_t                     flags;
+#define RFAPI_MON_FLAG_NEEDCALLBACK    0x00000001      /* deferred callback */
+
+  //int                                dcount; /* debugging counter */
+  void                         *timer;
+};
+
+struct rfapi_monitor_encap
+{
+  struct rfapi_monitor_encap   *next;
+  struct rfapi_monitor_encap   *prev;
+  struct route_node            *node;  /* VPN node */
+  struct bgp_info              *bi;    /* VPN bi */
+  struct route_node            *rn;    /* parent node */
+};
+
+struct rfapi_monitor_eth
+{
+  struct rfapi_monitor_eth     *next;  /* for use in vpn0_queries list */
+  struct rfapi_descriptor      *rfd;   /* which NVE requested the route */
+  struct ethaddr               macaddr;
+  uint32_t                     logical_net_id;
+  void                         *timer;
+};
+
+/*
+ * This is referenced by the "aggregate" field of a route node
+ * in an RFAPI import table.
+ *
+ * node lock/unlock:
+ *     - one lock increment for this structure itself
+ *     - one lock per chained struct rfapi_monitor_vpn
+ *     - one lock for the mon_eth skiplist itself
+ *     - one lock per mon_eth skiplist entry
+ *     - one lock for the ext skiplist itself
+ *     - one lock for each ext skiplist entry
+ *             remember to free skiplist when freeing rfapi_it_extra
+ *     - one lock per chained struct rfapi_monitor_encap
+ *
+ */
+struct rfapi_it_extra
+{
+  union
+  {
+    struct
+    {
+      struct rfapi_monitor_vpn *v;
+      struct skiplist          *idx_rd;  /* RD index */
+      struct skiplist          *mon_eth; /* ether queries */
+      struct
+      {
+        /* routes with UN addrs, either cached encap or Encap TLV */
+        int                    valid_interior_count;
+
+        /* unicast exterior routes, key=bi, val=allocated prefix */
+        struct skiplist                *source;
+      } e;
+    } vpn;
+    struct
+    {
+      struct rfapi_monitor_encap *e;
+    } encap;
+  } u;
+};
+
+#define RFAPI_IT_EXTRA_GET(rn) ((struct rfapi_it_extra *)(     \
+    (rn)->aggregate? (rn)->aggregate:                          \
+       (route_lock_node(rn), (rn)->aggregate =                 \
+       XCALLOC(MTYPE_RFAPI_IT_EXTRA,sizeof(struct rfapi_it_extra)))))
+
+#define RFAPI_RDINDEX(rn)                                      \
+    ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd : NULL)
+
+#define RFAPI_RDINDEX_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd)
+
+#define RFAPI_MONITOR_ETH(rn)                                  \
+    ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.mon_eth : NULL)
+
+#define RFAPI_MONITOR_ETH_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.mon_eth)
+
+#define RFAPI_MONITOR_VPN(rn)                                   \
+    ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.v : NULL)
+
+#define RFAPI_MONITOR_VPN_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.v)
+
+#define RFAPI_MONITOR_ENCAP(rn)                                 \
+    ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.encap.e : NULL)
+
+#define RFAPI_MONITOR_ENCAP_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.encap.e)
+
+#define RFAPI_MONITOR_EXTERIOR(rn) (&(RFAPI_IT_EXTRA_GET(rn)->u.vpn.e))
+
+#define RFAPI_HAS_MONITOR_EXTERIOR(rn) (rn && rn->aggregate &&         \
+    ((struct rfapi_it_extra *)(rn->aggregate))->u.vpn.e.source &&      \
+    !skiplist_first(((struct rfapi_it_extra *)(rn->aggregate))->       \
+    u.vpn.e.source, NULL, NULL))
+
+extern void
+rfapiMonitorLoopCheck (struct rfapi_monitor_vpn *mchain);
+
+extern void
+rfapiMonitorCleanCheck (struct bgp *bgp);
+
+extern void
+rfapiMonitorCheckAttachAllowed (void);
+
+extern void
+rfapiMonitorExtraFlush (safi_t safi, struct route_node *rn);
+
+extern struct route_node *
+rfapiMonitorGetAttachNode (struct rfapi_descriptor *rfd, struct prefix *p);
+
+extern void
+rfapiMonitorAttachImportHd (struct rfapi_descriptor *rfd);
+
+extern struct route_node *
+rfapiMonitorAdd (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct prefix                        *p);
+
+extern void
+rfapiMonitorDetachImportHd (struct rfapi_descriptor *rfd);
+
+extern void
+rfapiMonitorDel (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct prefix                        *p);
+
+extern int
+rfapiMonitorDelHd (struct rfapi_descriptor *rfd);
+
+extern void
+rfapiMonitorCallbacksOff (struct bgp *bgp);
+
+extern void
+rfapiMonitorCallbacksOn (struct bgp *bgp);
+
+extern void
+rfapiMonitorResponseRemovalOff (struct bgp *bgp);
+
+extern void
+rfapiMonitorResponseRemovalOn (struct bgp *bgp);
+
+extern void
+rfapiMonitorExtraPrune (safi_t safi, struct route_node *rn);
+
+extern void
+rfapiMonitorTimersRestart (struct rfapi_descriptor *rfd, struct prefix *p);
+
+extern void
+rfapiMonitorItNodeChanged (
+  struct rfapi_import_table    *import_table,
+  struct route_node            *it_node,
+  struct rfapi_monitor_vpn     *monitor_list);
+
+extern void
+rfapiMonitorMovedUp (
+  struct rfapi_import_table    *import_table,
+  struct route_node            *old_node,
+  struct route_node            *new_node,
+  struct rfapi_monitor_vpn     *monitor_list);
+
+extern struct route_node *
+rfapiMonitorEthAdd (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct ethaddr               *macaddr,
+  uint32_t                     logical_net_id);
+
+extern void
+rfapiMonitorEthDel (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct ethaddr               *macaddr,
+  uint32_t                     logical_net_id);
+
+#endif /* QUAGGA_HGP_RFAPI_MONITOR_H */
diff --git a/bgpd/rfapi/rfapi_nve_addr.c b/bgpd/rfapi/rfapi_nve_addr.c
new file mode 100644 (file)
index 0000000..835c2d2
--- /dev/null
@@ -0,0 +1,175 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; 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 "table.h"
+#include "vty.h"
+#include "memory.h"
+#include "skiplist.h"
+
+
+#include "bgpd.h"
+
+#include "bgp_rfapi_cfg.h"
+#include "rfapi.h"
+#include "rfapi_backend.h"
+
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_nve_addr.h"
+#include "rfapi_vty.h"
+
+#define DEBUG_NVE_ADDR 0
+
+void rfapiNveAddr2Str (struct rfapi_nve_addr *, char *, int);
+
+
+#if DEBUG_NVE_ADDR
+static void
+logdifferent (const char *tag,
+              struct rfapi_nve_addr *a, struct rfapi_nve_addr *b)
+{
+  char a_str[BUFSIZ];
+  char b_str[BUFSIZ];
+
+  rfapiNveAddr2Str (a, a_str, BUFSIZ);
+  rfapiNveAddr2Str (b, b_str, BUFSIZ);
+  zlog_debug ("%s: [%s] [%s]", tag, a_str, b_str);
+}
+#endif
+
+
+int
+rfapi_nve_addr_cmp (void *k1, void *k2)
+{
+  struct rfapi_nve_addr *a = (struct rfapi_nve_addr *) k1;
+  struct rfapi_nve_addr *b = (struct rfapi_nve_addr *) k2;
+  int ret = 0;
+
+  if (!a || !b)
+    {
+#if DEBUG_NVE_ADDR
+      zlog_debug ("%s: missing address a=%p b=%p", __func__, a, b);
+#endif
+      return (a - b);
+    }
+  if (a->un.addr_family != b->un.addr_family)
+    {
+#if DEBUG_NVE_ADDR
+      zlog_debug ("diff: UN addr fam a->un.af=%d, b->un.af=%d",
+                  a->un.addr_family, b->un.addr_family);
+#endif
+      return (a->un.addr_family - b->un.addr_family);
+    }
+  if (a->un.addr_family == AF_INET)
+    {
+      ret = IPV4_ADDR_CMP (&a->un.addr.v4, &b->un.addr.v4);
+      if (ret != 0)
+        {
+#if DEBUG_NVE_ADDR
+          logdifferent ("diff: UN addr", a, b);
+#endif
+          return ret;
+        }
+    }
+  else if (a->un.addr_family == AF_INET6)
+    {
+      ret = IPV6_ADDR_CMP (&a->un.addr.v6, &b->un.addr.v6);
+      if (ret == 0)
+        {
+#if DEBUG_NVE_ADDR
+          logdifferent ("diff: UN addr", a, b);
+#endif
+          return ret;
+        }
+    }
+  else
+    {
+      assert (0);
+    }
+  if (a->vn.addr_family != b->vn.addr_family)
+    {
+#if DEBUG_NVE_ADDR
+      zlog_debug ("diff: pT addr fam a->vn.af=%d, b->vn.af=%d",
+                  a->vn.addr_family, b->vn.addr_family);
+#endif
+      return (a->vn.addr_family - b->vn.addr_family);
+    }
+  if (a->vn.addr_family == AF_INET)
+    {
+      ret = IPV4_ADDR_CMP (&a->vn.addr.v4, &b->vn.addr.v4);
+      if (ret != 0)
+        {
+#if DEBUG_NVE_ADDR
+          logdifferent ("diff: VN addr", a, b);
+#endif
+          return ret;
+        }
+    }
+  else if (a->vn.addr_family == AF_INET6)
+    {
+      ret = IPV6_ADDR_CMP (&a->vn.addr.v6, &b->vn.addr.v6);
+      if (ret == 0)
+        {
+#if DEBUG_NVE_ADDR
+          logdifferent ("diff: VN addr", a, b);
+#endif
+          return ret;
+        }
+    }
+  else
+    {
+      assert (0);
+    }
+  return 0;
+}
+
+void
+rfapiNveAddr2Str (struct rfapi_nve_addr *na, char *buf, int bufsize)
+{
+  char *p = buf;
+  int r;
+
+#define REMAIN (bufsize - (p-buf))
+#define INCP {p += (r > REMAIN)? REMAIN: r;}
+
+  if (bufsize < 1)
+    return;
+
+  r = snprintf (p, REMAIN, "VN=");
+  INCP;
+
+  if (!rfapiRfapiIpAddr2Str (&na->vn, p, REMAIN))
+    goto done;
+
+  buf[bufsize - 1] = 0;
+  p = buf + strlen (buf);
+
+  r = snprintf (p, REMAIN, ", UN=");
+  INCP;
+
+  rfapiRfapiIpAddr2Str (&na->un, p, REMAIN);
+
+done:
+  buf[bufsize - 1] = 0;
+}
diff --git a/bgpd/rfapi/rfapi_nve_addr.h b/bgpd/rfapi/rfapi_nve_addr.h
new file mode 100644 (file)
index 0000000..2b2d2b5
--- /dev/null
@@ -0,0 +1,43 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_NVE_ADDR_H
+#define _QUAGGA_BGP_RFAPI_NVE_ADDR_H
+
+#include "rfapi.h"
+
+struct rfapi_nve_addr
+{
+  struct rfapi_ip_addr vn;
+  struct rfapi_ip_addr un;
+  void *info;
+};
+
+
+extern int
+rfapi_nve_addr_cmp (void *k1, void *k2);
+
+extern void
+rfapiNveAddr2Str (struct rfapi_nve_addr *na, char *buf, int bufsize);
+
+
+
+#endif /* _QUAGGA_BGP_RFAPI_NVE_ADDR_H */
diff --git a/bgpd/rfapi/rfapi_private.h b/bgpd/rfapi/rfapi_private.h
new file mode 100644 (file)
index 0000000..aca034b
--- /dev/null
@@ -0,0 +1,455 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/* 
+ * Internal definitions for RFAPI. Not for use by other code
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_PRIVATE_H
+#define _QUAGGA_BGP_RFAPI_PRIVATE_H
+
+#include "linklist.h"
+#include "skiplist.h"
+#include "workqueue.h"
+
+#include "bgp_attr.h"
+
+#include "rfapi.h"
+
+/*
+ * RFAPI Advertisement Data Block
+ *
+ * Holds NVE prefix advertisement information
+ */
+struct rfapi_adb
+{
+  struct prefix                        prefix_ip;
+  struct prefix                        prefix_eth;     /* now redundant with l2o */
+  struct prefix_rd             prd;
+  uint32_t                     lifetime;
+  uint8_t                      cost;
+  struct rfapi_l2address_option        l2o;
+};
+
+/*
+ * Lists of rfapi_adb. Each rfapi_adb is referenced twice:
+ *
+ * 1. each is referenced in by_lifetime
+ * 2. each is referenced by exactly one of: ipN_by_prefix, ip0_by_ether
+ */
+struct rfapi_advertised_prefixes
+{
+  struct skiplist *ipN_by_prefix; /* all except 0/32, 0/128 */
+  struct skiplist *ip0_by_ether;  /* ip prefix 0/32, 0/128 */
+  struct skiplist *by_lifetime;   /* all */
+};
+
+
+struct rfapi_descriptor
+{
+  struct route_node            *un_node;       /* backref to un table */
+
+  struct rfapi_descriptor      *next;          /* next vn_addr */
+
+  /* supplied by client */
+  struct bgp                   *bgp;           /* from rfp_start_val */
+  struct rfapi_ip_addr         vn_addr;
+  struct rfapi_ip_addr         un_addr;
+  rfapi_response_cb_t          *response_cb;   /* override per-bgp response_cb */
+  void                         *cookie;        /* for callbacks */
+  struct rfapi_tunneltype_option default_tunneltype_option;
+
+  /* supplied by matched configuration */
+  struct prefix_rd             rd;
+  struct ecommunity            *rt_export_list;
+  uint32_t                     response_lifetime;
+
+  /* list of prefixes currently being advertised by this nve */
+  struct rfapi_advertised_prefixes     advertised;
+
+  time_t                       open_time;
+
+  uint32_t                     max_prefix_lifetime;
+  uint32_t                     min_prefix_lifetime;
+
+  /* reference to this nve's import table */
+  struct rfapi_import_table    *import_table;
+
+  uint32_t                     monitor_count;
+  struct route_table           *mon;           /* rfapi_monitors */
+  struct skiplist              *mon_eth;       /* ethernet monitors */
+
+  /*
+   * rib            RIB as seen by NVE
+   * rib_pending    RIB containing nodes with updated info chains
+   * rsp_times      last time we sent response containing pfx
+   */
+  uint32_t                     rib_prefix_count;       /* pfxes with routes */
+  struct route_table           *rib[AFI_MAX];
+  struct route_table           *rib_pending[AFI_MAX];
+  struct work_queue            *updated_responses_queue;
+  struct route_table           *rsp_times[AFI_MAX];
+
+  uint32_t                     rsp_counter;            /* dedup initial rsp */
+  time_t                       rsp_time;               /* dedup initial rsp */
+  time_t                       ftd_last_allowed_time;  /* FTD filter */
+
+  unsigned int                 stat_count_nh_reachable;
+  unsigned int                 stat_count_nh_removal;
+
+  /*
+   * points to the original nve group structure that matched
+   * when this nve_descriptor was created. We use this pointer
+   * in rfapi_close() to find the nve group structure and
+   * delete its reference back to us.
+   *
+   * If the nve group structure is deleted (via configuration
+   * change) while this nve_descriptor exists, this rfg pointer
+   * will be set to NULL.
+   */
+  struct rfapi_nve_group_cfg   *rfg;
+
+  /*
+   * This ~7kB structure is here to permit multiple routes for
+   * a prefix to be injected to BGP. There are at least two
+   * situations where such conditions obtain:
+   *
+   * When an VNC route is exported to BGP on behalf of the set of
+   * NVEs that belong to the export NVE group, it is replicated
+   * so that there is one route per NVE (and the route's nexthop
+   * is the NVE's VN address).
+   *
+   * Each of these routes being injected to BGP must have a distinct
+   * peer pointer (otherwise, if they have the same peer pointer, each
+   * route will be considered an implicit waithdraw of the previous
+   * route injected from that peer, and the new route will replace
+   * rather than augment the old one(s)).
+   */
+  struct peer                  *peer;
+
+  uint32_t                     flags;
+#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP                0x00000001
+#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP6       0x00000002
+#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_ETHER     0x00000004
+#define RFAPI_HD_FLAG_PROVISIONAL                      0x00000008
+#define RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY         0x00000010
+};
+
+#define RFAPI_QUEUED_FLAG(afi) (                                       \
+    ((afi) == AFI_IP)? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP:        \
+    (((afi) == AFI_IP6)? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP6:     \
+    (((afi) == AFI_ETHER)? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_ETHER: \
+    (assert(0), 0) )))
+
+
+struct rfapi_global_stats
+{
+  time_t               last_reset;
+  unsigned int         max_descriptors;
+
+  unsigned int         count_unknown_nves;
+
+  unsigned int         count_queries;
+  unsigned int         count_queries_failed;
+
+  unsigned int         max_responses;   /* semantics? */
+
+  unsigned int         count_registrations;
+  unsigned int         count_registrations_failed;
+
+  unsigned int         count_updated_response_updates;
+  unsigned int         count_updated_response_deletes;
+};
+
+/*
+ * There is one of these per BGP instance.
+ *
+ * Radix tree is indexed by un address; follow chain and
+ * check vn address to get exact match.
+ */
+struct rfapi
+{
+  struct route_table           un[AFI_MAX];
+  struct rfapi_import_table    *imports;       /* IPv4, IPv6 */
+  struct list                  descriptors;/* debug & resolve-nve imports */
+
+  struct rfapi_global_stats    stat;
+
+  /* 
+   * callbacks into RFP, set at startup time (bgp_rfapi_new() gets
+   * values from rfp_start()) or via rfapi_rfp_set_cb_methods()
+   * (otherwise NULL). Note that the response_cb method can also
+   * be overridden per-rfd (currently used only for debug/test scenarios)
+   */
+  struct rfapi_rfp_cb_methods  rfp_methods;
+
+  /*
+   * Import tables for Ethernet over IPSEC
+   *
+   * The skiplist keys are LNIs. Values are pointers
+   * to struct rfapi_import_table.
+   */
+  struct skiplist              *import_mac;  /* L2 */
+
+  /*
+   * when exporting plain routes ("registered-nve" mode) to
+   * bgp unicast or zebra, we need to keep track of information
+   * related to expiring the routes according to the VNC lifetime
+   */
+  struct route_table           *rt_export_bgp[AFI_MAX];
+  struct route_table           *rt_export_zebra[AFI_MAX];
+
+  /*
+   * For VNC->BGP unicast exports in CE mode, we need a
+   * routing table that collects all of the VPN routes
+   * in a single tree. The VPN rib is split up according
+   * to RD first, so we can't use that. This is an import
+   * table that matches all RTs.
+   */
+  struct rfapi_import_table    *it_ce;
+
+  /*
+   * when importing bgp-direct routes in resolve-nve mode,
+   * this list maps unicast route nexthops to their bgp_infos
+   * in the unicast table
+   */
+  struct skiplist              *resolve_nve_nexthop;
+
+  /* 
+   * Descriptors for which rfapi_close() was called during a callback.
+   * They will be closed after the callback finishes.
+   */
+  struct work_queue            *deferred_close_q;
+
+  /*
+   * For "show vnc responses"
+   */
+  uint32_t                     response_immediate_count;
+  uint32_t                     response_updated_count;
+  uint32_t                     monitor_count;
+
+  uint32_t                     rib_prefix_count_total;
+  uint32_t                     rib_prefix_count_total_max;
+
+  uint32_t                     flags;
+#define RFAPI_INCALLBACK       0x00000001
+  void                         *rfp;   /* from rfp_start */
+};
+
+#define RFAPI_RIB_PREFIX_COUNT_INCR(rfd, rfapi) do {                   \
+    ++(rfd)->rib_prefix_count;                                         \
+    ++(rfapi)->rib_prefix_count_total;                                 \
+    if ((rfapi)->rib_prefix_count_total > (rfapi)->rib_prefix_count_total_max) \
+       ++(rfapi)->rib_prefix_count_total_max;                          \
+    } while (0)
+
+#define RFAPI_RIB_PREFIX_COUNT_DECR(rfd, rfapi) do {                   \
+    --(rfd)->rib_prefix_count;                                         \
+    --(rfapi)->rib_prefix_count_total;                                 \
+    } while (0)
+
+#define RFAPI_0_PREFIX(prefix) (                                       \
+    (((prefix)->family == AF_INET)? (prefix)->u.prefix4.s_addr == 0:   \
+    (((prefix)->family == AF_INET6)?                                   \
+        (IN6_IS_ADDR_UNSPECIFIED(&(prefix)->u.prefix6)) : 0))                  \
+)
+
+#define RFAPI_0_ETHERADDR(ea) (                                                \
+    ((ea)->octet[0] | (ea)->octet[1] | (ea)->octet[2] |                        \
+    (ea)->octet[3] | (ea)->octet[4] | (ea)->octet[5]) == 0)
+
+#define RFAPI_HOST_PREFIX(prefix) (                                    \
+    ((prefix)->family == AF_INET)? ((prefix)->prefixlen == 32):                \
+    (((prefix)->family == AF_INET6)? ((prefix)->prefixlen == 128): 0) )
+
+extern void
+rfapiQprefix2Rprefix (
+  struct prefix                        *qprefix,
+  struct rfapi_ip_prefix       *rprefix);
+
+extern int
+rfapi_find_rfd (
+  struct bgp                   *bgp,
+  struct rfapi_ip_addr         *vn_addr,
+  struct rfapi_ip_addr         *un_addr,
+  struct rfapi_descriptor      **rfd);
+
+extern void
+add_vnc_route (
+  struct rfapi_descriptor      *rfd,           /* cookie + UN addr for VPN */
+  struct bgp                   *bgp,
+  int                          safi,
+  struct prefix                        *p,
+  struct prefix_rd             *prd,
+  struct rfapi_ip_addr         *nexthop,
+  uint32_t                     *local_pref,    /* host byte order */
+  uint32_t                     *lifetime,      /* host byte order */
+  struct bgp_tea_options       *rfp_options,
+  struct rfapi_un_option       *options_un,
+  struct rfapi_vn_option       *options_vn,
+  struct ecommunity            *rt_export_list,
+  uint32_t                     *med,
+  uint32_t                     *label,
+  uint8_t                      type,
+  uint8_t                      sub_type,
+  int                          flags);
+#define RFAPI_AHR_NO_TUNNEL_SUBTLV     0x00000001
+#define RFAPI_AHR_RFPOPT_IS_VNCTLV     0x00000002      /* hack! */
+#if 0   /* unused? */
+#  define RFAPI_AHR_SET_PFX_TO_NEXTHOP 0x00000004
+#endif
+
+extern void
+del_vnc_route (
+  struct rfapi_descriptor      *rfd,
+  struct peer                  *peer,
+  struct bgp                   *bgp,
+  safi_t                       safi,
+  struct prefix                        *p,
+  struct prefix_rd             *prd,
+  uint8_t                      type,
+  uint8_t                      sub_type,
+  struct rfapi_nexthop         *lnh,
+  int                          kill);
+
+extern int
+rfapiCliGetPrefixAddr (struct vty *vty, const char *str, struct prefix *p);
+
+extern int
+rfapiGetVncLifetime (struct attr *attr, uint32_t * lifetime);
+
+extern int
+rfapiGetTunnelType (struct attr *attr, bgp_encap_types *type);
+
+extern int
+rfapiGetVncTunnelUnAddr (struct attr *attr, struct prefix *p);
+
+extern int
+rfapi_reopen (struct rfapi_descriptor *rfd, struct bgp *bgp);
+
+extern void
+vnc_import_bgp_add_rfp_host_route_mode_resolve_nve (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct prefix                        *prefix);
+
+extern void
+vnc_import_bgp_del_rfp_host_route_mode_resolve_nve (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct prefix                        *prefix);
+
+extern void
+rfapiFreeBgpTeaOptionChain (struct bgp_tea_options *p);
+
+extern struct rfapi_vn_option *
+rfapiVnOptionsDup (struct rfapi_vn_option *orig);
+
+extern struct rfapi_un_option *
+rfapiUnOptionsDup (struct rfapi_un_option *orig);
+
+extern struct bgp_tea_options *
+rfapiOptionsDup (struct bgp_tea_options *orig);
+
+extern int
+rfapi_ip_addr_cmp (struct rfapi_ip_addr *a1, struct rfapi_ip_addr *a2);
+
+extern uint32_t
+rfp_cost_to_localpref (uint8_t cost);
+
+extern int
+rfapi_set_autord_from_vn (struct prefix_rd *rd, struct rfapi_ip_addr *vn);
+
+extern void
+rfapiAdbFree (struct rfapi_adb *adb);
+
+extern struct rfapi_nexthop *
+rfapi_nexthop_new (struct rfapi_nexthop *copyme);
+
+extern void
+rfapi_nexthop_free (void *goner);
+
+extern struct rfapi_vn_option *
+rfapi_vn_options_dup (struct rfapi_vn_option *existing);
+
+extern void
+rfapi_un_options_free (struct rfapi_un_option *goner);
+
+extern void
+rfapi_vn_options_free (struct rfapi_vn_option *goner);
+
+/*------------------------------------------
+ * rfapi_extract_l2o
+ *
+ * Find Layer 2 options in an option chain 
+ *
+ * input: 
+ *     pHop            option chain
+ *
+ * output:
+ *     l2o             layer 2 options extracted
+ *
+ * return value:
+ *     0               OK
+ *     1               no options found
+ *
+ --------------------------------------------*/
+extern int
+rfapi_extract_l2o (
+  struct bgp_tea_options       *pHop,  /* chain of options */
+  struct rfapi_l2address_option        *l2o);  /* return extracted value */
+
+/* 
+ * compaitibility to old quagga_time call
+ * time_t value in terms of stabilised absolute time. 
+ * replacement for POSIX time()
+ */
+extern time_t rfapi_time (time_t *t);
+
+DECLARE_MGROUP(RFAPI)
+DECLARE_MTYPE(RFAPI_CFG)
+DECLARE_MTYPE(RFAPI_GROUP_CFG)
+DECLARE_MTYPE(RFAPI_L2_CFG)
+DECLARE_MTYPE(RFAPI_RFP_GROUP_CFG)
+DECLARE_MTYPE(RFAPI)
+DECLARE_MTYPE(RFAPI_DESC)
+DECLARE_MTYPE(RFAPI_IMPORTTABLE)
+DECLARE_MTYPE(RFAPI_MONITOR)
+DECLARE_MTYPE(RFAPI_MONITOR_ENCAP)
+DECLARE_MTYPE(RFAPI_NEXTHOP)
+DECLARE_MTYPE(RFAPI_VN_OPTION)
+DECLARE_MTYPE(RFAPI_UN_OPTION)
+DECLARE_MTYPE(RFAPI_WITHDRAW)
+DECLARE_MTYPE(RFAPI_RFG_NAME)
+DECLARE_MTYPE(RFAPI_ADB)
+DECLARE_MTYPE(RFAPI_ETI)
+DECLARE_MTYPE(RFAPI_NVE_ADDR)
+DECLARE_MTYPE(RFAPI_PREFIX_BAG)
+DECLARE_MTYPE(RFAPI_IT_EXTRA)
+DECLARE_MTYPE(RFAPI_INFO)
+DECLARE_MTYPE(RFAPI_ADDR)
+DECLARE_MTYPE(RFAPI_UPDATED_RESPONSE_QUEUE)
+DECLARE_MTYPE(RFAPI_RECENT_DELETE)
+DECLARE_MTYPE(RFAPI_L2ADDR_OPT)
+DECLARE_MTYPE(RFAPI_AP)
+DECLARE_MTYPE(RFAPI_MONITOR_ETH)
+
+#endif /* _QUAGGA_BGP_RFAPI_PRIVATE_H */
diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c
new file mode 100644 (file)
index 0000000..70acc14
--- /dev/null
@@ -0,0 +1,2535 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/*
+ * File:       rfapi_rib.c
+ * Purpose:    maintain per-nve ribs and generate change lists
+ */
+
+#include <errno.h>
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "memory.h"
+#include "log.h"
+#include "skiplist.h"
+#include "workqueue.h"
+
+#include "bgpd.h"
+#include "bgp_route.h"
+#include "bgp_ecommunity.h"
+#include "bgp_mplsvpn.h"
+#include "bgp_vnc_types.h"
+
+#include "rfapi.h"
+#include "bgp_rfapi_cfg.h"
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_vty.h"
+#include "vnc_import_bgp.h"
+#include "rfapi_rib.h"
+#include "rfapi_monitor.h"
+#include "rfapi_encap_tlv.h"
+
+#define DEBUG_PROCESS_PENDING_NODE     0
+#define DEBUG_PENDING_DELETE_ROUTE     0
+#define DEBUG_NHL                      0
+#define DEBUG_RIB_SL_RD                 0
+
+/* forward decl */
+#if DEBUG_NHL
+static void
+rfapiRibShowRibSl (void *stream, struct prefix *pfx, struct skiplist *sl);
+#endif
+
+/*
+ * RIB
+ * ---
+ * Model of the set of routes currently in the NVE's RIB.
+ *
+ * node->info          ptr to "struct skiplist".
+ *                     MUST be NULL if there are no routes.
+ *                     key = ptr to struct prefix {vn}
+ *                     val = ptr to struct rfapi_info
+ *                     skiplist.del = NULL
+ *                     skiplist.cmp = vnc_prefix_cmp
+ *
+ * node->aggregate     ptr to "struct skiplist".
+ *                     key = ptr to struct prefix {vn}
+ *                     val = ptr to struct rfapi_info
+ *                     skiplist.del = rfapi_info_free
+ *                     skiplist.cmp = vnc_prefix_cmp
+ *
+ *                     This skiplist at "aggregate"
+ *                     contains the routes recently
+ *                     deleted
+ *
+ *
+ * Pending RIB
+ * -----------
+ * Sparse list of prefixes that need to be updated. Each node
+ * will have the complete set of routes for the prefix.
+ *
+ * node->info          ptr to "struct list" (lib/linklist.h)
+ *                     "Cost List"
+ *                     List of routes sorted lowest cost first.
+ *                     This list is how the new complete set
+ *                     of routes should look.
+ *                     Set if there are updates to the prefix;
+ *                     MUST be NULL if there are no updates.
+ *
+ *                     .data = ptr to struct rfapi_info
+ *                     list.cmp = NULL (sorted manually)
+ *                     list.del = rfapi_info_free
+ *
+ *                     Special case: if node->info is 1, it means
+ *                     "delete all routes at this prefix".
+ *
+ * node->aggregate     ptr to struct skiplist
+ *                     key = ptr to struct prefix {vn} (part of ri)
+ *                     val =  struct rfapi_info
+ *                     skiplist.cmp = vnc_prefix_cmp
+ *                     skiplist.del = NULL
+ *
+ *                     ptlist is rewritten anew each time
+ *                     rfapiRibUpdatePendingNode() is called
+ *
+ *                     THE ptlist VALUES ARE REFERENCES TO THE
+ *                     rfapi_info STRUCTS IN THE node->info LIST.
+ */
+
+/*
+ * iterate over RIB to count responses, compare with running counters
+ */
+void
+rfapiRibCheckCounts (
+  int          checkstats,     /* validate rfd & global counts */
+  unsigned int offset)         /* number of ri's held separately */
+{
+  struct rfapi_descriptor *rfd;
+  struct listnode *node;
+
+  struct bgp *bgp = bgp_get_default ();
+
+  uint32_t t_pfx_active = 0;
+  uint32_t t_pfx_deleted = 0;
+
+  uint32_t t_ri_active = 0;
+  uint32_t t_ri_deleted = 0;
+  uint32_t t_ri_pend = 0;
+
+  unsigned int alloc_count;
+
+  /*
+   * loop over NVEs
+   */
+  for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, node, rfd))
+    {
+
+      afi_t afi;
+      uint32_t pfx_active = 0;
+      uint32_t pfx_deleted = 0;
+
+      for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+        {
+
+          struct route_node *rn;
+
+          for (rn = route_top (rfd->rib[afi]); rn; rn = route_next (rn))
+            {
+
+              struct skiplist *sl = rn->info;
+              struct skiplist *dsl = rn->aggregate;
+              uint32_t ri_active = 0;
+              uint32_t ri_deleted = 0;
+
+              if (sl)
+                {
+                  ri_active = skiplist_count (sl);
+                  assert (ri_active);
+                  t_ri_active += ri_active;
+                  ++pfx_active;
+                  ++t_pfx_active;
+                }
+
+              if (dsl)
+                {
+                  ri_deleted = skiplist_count (dsl);
+                  t_ri_deleted += ri_deleted;
+                  ++pfx_deleted;
+                  ++t_pfx_deleted;
+                }
+            }
+          for (rn = route_top (rfd->rib_pending[afi]); rn;
+               rn = route_next (rn))
+            {
+
+              struct list *l = rn->info;        /* sorted by cost */
+              struct skiplist *sl = rn->aggregate;
+              uint32_t ri_pend_cost = 0;
+              uint32_t ri_pend_uniq = 0;
+
+              if (sl)
+                {
+                  ri_pend_uniq = skiplist_count (sl);
+                }
+
+              if (l && (l != (void *) 1))
+                {
+                  ri_pend_cost = l->count;
+                  t_ri_pend += l->count;
+                }
+
+              assert (ri_pend_uniq == ri_pend_cost);
+            }
+        }
+
+      if (checkstats)
+        {
+          if (pfx_active != rfd->rib_prefix_count)
+            {
+              zlog_debug ("%s: rfd %p actual pfx count %u != running %u",
+                          __func__, rfd, pfx_active, rfd->rib_prefix_count);
+              assert (0);
+            }
+        }
+    }
+
+  if (checkstats && bgp && bgp->rfapi)
+    {
+      if (t_pfx_active != bgp->rfapi->rib_prefix_count_total)
+        {
+          zlog_debug ("%s: actual total pfx count %u != running %u",
+                      __func__, t_pfx_active,
+                      bgp->rfapi->rib_prefix_count_total);
+          assert (0);
+        }
+    }
+
+  /*
+   * Check against memory allocation count
+   */
+  alloc_count = mtype_stats_alloc (MTYPE_RFAPI_INFO);
+  assert (t_ri_active + t_ri_deleted + t_ri_pend + offset == alloc_count);
+}
+
+static struct rfapi_info *
+rfapi_info_new ()
+{
+  return XCALLOC (MTYPE_RFAPI_INFO, sizeof (struct rfapi_info));
+}
+
+void
+rfapiFreeRfapiUnOptionChain (struct rfapi_un_option *p)
+{
+  while (p)
+    {
+      struct rfapi_un_option *next;
+
+      next = p->next;
+      XFREE (MTYPE_RFAPI_UN_OPTION, p);
+      p = next;
+    }
+}
+
+void
+rfapiFreeRfapiVnOptionChain (struct rfapi_vn_option *p)
+{
+  while (p)
+    {
+      struct rfapi_vn_option *next;
+
+      next = p->next;
+      XFREE (MTYPE_RFAPI_VN_OPTION, p);
+      p = next;
+    }
+}
+
+
+static void
+rfapi_info_free (struct rfapi_info *goner)
+{
+  if (goner)
+    {
+      if (goner->tea_options)
+        {
+          rfapiFreeBgpTeaOptionChain (goner->tea_options);
+          goner->tea_options = NULL;
+        }
+      if (goner->un_options)
+        {
+          rfapiFreeRfapiUnOptionChain (goner->un_options);
+          goner->un_options = NULL;
+        }
+      if (goner->vn_options)
+        {
+          rfapiFreeRfapiVnOptionChain (goner->vn_options);
+          goner->vn_options = NULL;
+        }
+      if (goner->timer)
+        {
+          struct rfapi_rib_tcb *tcb;
+
+          tcb = ((struct thread *) goner->timer)->arg;
+          thread_cancel ((struct thread *) goner->timer);
+          XFREE (MTYPE_RFAPI_RECENT_DELETE, tcb);
+          goner->timer = NULL;
+        }
+      XFREE (MTYPE_RFAPI_INFO, goner);
+    }
+}
+
+/*
+ * Timer control block for recently-deleted and expired routes
+ */
+struct rfapi_rib_tcb
+{
+  struct rfapi_descriptor *rfd;
+  struct skiplist *sl;
+  struct rfapi_info *ri;
+  struct route_node *rn;
+  int flags;
+#define RFAPI_RIB_TCB_FLAG_DELETED     0x00000001
+};
+
+/*
+ * remove route from rib
+ */
+static int
+rfapiRibExpireTimer (struct thread *t)
+{
+  struct rfapi_rib_tcb *tcb = t->arg;
+
+  RFAPI_RIB_CHECK_COUNTS (1, 0);
+
+  /*
+   * Forget reference to thread. Otherwise rfapi_info_free() will
+   * attempt to free thread pointer as an option chain
+   */
+  tcb->ri->timer = NULL;
+
+  /* "deleted" skiplist frees ri, "active" doesn't */
+  assert (!skiplist_delete (tcb->sl, &tcb->ri->rk, NULL));
+  if (!tcb->sl->del)
+    {
+      /*
+       * XXX in this case, skiplist has no delete function: we must
+       * therefore delete rfapi_info explicitly.
+       */
+      rfapi_info_free (tcb->ri);
+    }
+
+  if (skiplist_empty (tcb->sl))
+    {
+      if (CHECK_FLAG (tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED))
+        tcb->rn->aggregate = NULL;
+      else
+        {
+          struct bgp *bgp = bgp_get_default ();
+          tcb->rn->info = NULL;
+          RFAPI_RIB_PREFIX_COUNT_DECR (tcb->rfd, bgp->rfapi);
+        }
+      skiplist_free (tcb->sl);
+      route_unlock_node (tcb->rn);
+    }
+
+  XFREE (MTYPE_RFAPI_RECENT_DELETE, tcb);
+
+  RFAPI_RIB_CHECK_COUNTS (1, 0);
+
+  return 0;
+}
+
+static void
+rfapiRibStartTimer (
+  struct rfapi_descriptor      *rfd,
+  struct rfapi_info            *ri,
+  struct route_node            *rn, /* route node attached to */
+  int                          deleted)
+{
+  struct thread *t = ri->timer;
+  struct rfapi_rib_tcb *tcb = NULL;
+  char buf_prefix[BUFSIZ];
+
+  if (t)
+    {
+      tcb = t->arg;
+      thread_cancel (t);
+      ri->timer = NULL;
+    }
+  else
+    {
+      tcb =
+        XCALLOC (MTYPE_RFAPI_RECENT_DELETE, sizeof (struct rfapi_rib_tcb));
+    }
+  tcb->rfd = rfd;
+  tcb->ri = ri;
+  tcb->rn = rn;
+  if (deleted)
+    {
+      tcb->sl = (struct skiplist *) rn->aggregate;
+      SET_FLAG (tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED);
+    }
+  else
+    {
+      tcb->sl = (struct skiplist *) rn->info;
+      UNSET_FLAG (tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED);
+    }
+
+  prefix2str (&rn->p, buf_prefix, BUFSIZ);
+  zlog_debug ("%s: rfd %p pfx %s life %u", __func__, rfd, buf_prefix,
+              ri->lifetime);
+  ri->timer = thread_add_timer (bm->master, rfapiRibExpireTimer,
+                                tcb, ri->lifetime);
+  assert (ri->timer);
+}
+
+/*
+ * Compares two <struct rfapi_rib_key>s
+ */
+static int
+rfapi_rib_key_cmp (void *k1, void *k2)
+{
+  struct rfapi_rib_key *a = (struct rfapi_rib_key *) k1;
+  struct rfapi_rib_key *b = (struct rfapi_rib_key *) k2;
+  int ret;
+
+  if (!a || !b)
+    return (a - b);
+
+  ret = vnc_prefix_cmp (&a->vn, &b->vn);
+  if (ret)
+    return ret;
+
+  ret = vnc_prefix_cmp(&a->rd, &b->rd);
+  if (ret)
+    return ret;
+
+  ret = vnc_prefix_cmp (&a->aux_prefix, &b->aux_prefix);
+
+  return ret;
+}
+
+
+/*
+ * Note: this function will claim that two option chains are
+ * different unless their option items are in identical order.
+ * The consequence is that RFP updated responses can be sent
+ * unnecessarily, or that they might contain nexthop items
+ * that are not strictly needed.
+ *
+ * This function could be modified to compare option chains more
+ * thoroughly, but it's not clear that the extra compuation would
+ * be worth it.
+ */
+static int
+bgp_tea_options_cmp (struct bgp_tea_options *a, struct bgp_tea_options *b)
+{
+  int rc;
+
+  if (!a || !b)
+    {
+      return (a - b);
+    }
+
+  if (a->type != b->type)
+    return (a->type - b->type);
+  if (a->length != b->length)
+    return (a->length = b->length);
+  if ((rc = memcmp (a->value, b->value, a->length)))
+    return rc;
+  if (!a->next != !b->next)
+    {                           /* logical xor */
+      return (a->next - b->next);
+    }
+  if (a->next)
+    return bgp_tea_options_cmp (a->next, b->next);
+  return 0;
+
+}
+
+static int
+rfapi_info_cmp (struct rfapi_info *a, struct rfapi_info *b)
+{
+  int rc;
+
+  if (!a || !b)
+    return (a - b);
+
+  if ((rc = rfapi_rib_key_cmp (&a->rk, &b->rk)))
+    return rc;
+
+  if ((rc = vnc_prefix_cmp (&a->un, &b->un)))
+    return rc;
+
+  if (a->cost != b->cost)
+    return (a->cost - b->cost);
+
+  if (a->lifetime != b->lifetime)
+    return (a->lifetime - b->lifetime);
+
+  if ((rc = bgp_tea_options_cmp (a->tea_options, b->tea_options)))
+    return rc;
+
+  return 0;
+}
+
+void
+rfapiRibClear (struct rfapi_descriptor *rfd)
+{
+  struct bgp *bgp = bgp_get_default ();
+  afi_t afi;
+
+#if DEBUG_L2_EXTRA
+  zlog_debug ("%s: rfd=%p", __func__, rfd);
+#endif
+
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+      struct route_node *pn;
+      struct route_node *rn;
+
+      if (rfd->rib_pending[afi])
+        {
+          for (pn = route_top (rfd->rib_pending[afi]); pn;
+               pn = route_next (pn))
+            {
+              if (pn->aggregate)
+                {
+                  /* 
+                   * free references into the rfapi_info structures before
+                   * freeing the structures themselves
+                   */
+                  skiplist_free ((struct skiplist *) (pn->aggregate));
+                  pn->aggregate = NULL;
+                  route_unlock_node (pn);       /* skiplist deleted */
+                }
+              /* 
+               * free the rfapi_info structures
+               */
+              if (pn->info)
+                {
+                  if (pn->info != (void *) 1)
+                    {
+                      list_delete ((struct list *) (pn->info));
+                    }
+                  pn->info = NULL;
+                  route_unlock_node (pn);       /* linklist or 1 deleted */
+                }
+            }
+        }
+      if (rfd->rib[afi])
+        {
+          for (rn = route_top (rfd->rib[afi]); rn; rn = route_next (rn))
+            {
+              if (rn->info)
+                {
+
+                  struct rfapi_info *ri;
+
+                  while (0 ==
+                         skiplist_first ((struct skiplist *) rn->info, NULL,
+                                         (void **) &ri))
+                    {
+
+                      rfapi_info_free (ri);
+                      skiplist_delete_first ((struct skiplist *) rn->info);
+                    }
+                  skiplist_free ((struct skiplist *) rn->info);
+                  rn->info = NULL;
+                  route_unlock_node (rn);
+                  RFAPI_RIB_PREFIX_COUNT_DECR (rfd, bgp->rfapi);
+                }
+              if (rn->aggregate)
+                {
+
+                  struct rfapi_info *ri_del;
+
+                  /* delete skiplist & contents */
+                  while (!skiplist_first ((struct skiplist *) (rn->aggregate),
+                                          NULL, (void **) &ri_del))
+                    {
+
+                      /* sl->del takes care of ri_del */
+                      skiplist_delete_first (
+                       (struct skiplist *) (rn->aggregate));
+                    }
+                  skiplist_free ((struct skiplist *) (rn->aggregate));
+
+                  rn->aggregate = NULL;
+                  route_unlock_node (rn);
+                }
+            }
+        }
+    }
+  if (rfd->updated_responses_queue)
+    {
+      work_queue_free (rfd->updated_responses_queue);
+      rfd->updated_responses_queue = NULL;
+    }
+}
+
+/*
+ * Release all dynamically-allocated memory that is part of an HD's RIB
+ */
+void
+rfapiRibFree (struct rfapi_descriptor *rfd)
+{
+  afi_t afi;
+
+
+  /*
+   * NB rfd is typically detached from master list, so is not included
+   * in the count performed by RFAPI_RIB_CHECK_COUNTS
+   */
+
+  /*
+   * Free routes attached to radix trees
+   */
+  rfapiRibClear (rfd);
+
+  /* Now the uncounted rfapi_info's are freed, so the check should succeed */
+  RFAPI_RIB_CHECK_COUNTS (1, 0);
+
+  /*
+   * Free radix trees
+   */
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+      route_table_finish (rfd->rib_pending[afi]);
+      rfd->rib_pending[afi] = NULL;
+
+      route_table_finish (rfd->rib[afi]);
+      rfd->rib[afi] = NULL;
+
+      /* NB route_table_finish frees only prefix nodes, not chained info */
+      route_table_finish (rfd->rsp_times[afi]);
+      rfd->rib[afi] = NULL;
+    }
+}
+
+/*
+ * Copies struct bgp_info to struct rfapi_info, except for rk fields and un
+ */
+static void
+rfapiRibBi2Ri(
+  struct bgp_info   *bi,
+  struct rfapi_info *ri,
+  uint32_t          lifetime)
+{
+  struct bgp_attr_encap_subtlv *pEncap;
+
+  ri->cost = rfapiRfpCost (bi->attr);
+  ri->lifetime = lifetime;
+
+  /* This loop based on rfapiRouteInfo2NextHopEntry() */
+  for (pEncap = bi->attr->extra->vnc_subtlvs; pEncap; pEncap = pEncap->next)
+    {
+      struct bgp_tea_options *hop;
+
+      switch (pEncap->type)
+        {
+        case BGP_VNC_SUBTLV_TYPE_LIFETIME:
+          /* use configured lifetime, not attr lifetime */
+          break;
+
+        case BGP_VNC_SUBTLV_TYPE_RFPOPTION:
+          hop = XCALLOC (MTYPE_BGP_TEA_OPTIONS,
+                         sizeof (struct bgp_tea_options));
+          assert (hop);
+          hop->type = pEncap->value[0];
+          hop->length = pEncap->value[1];
+          hop->value = XCALLOC (MTYPE_BGP_TEA_OPTIONS_VALUE,
+                                pEncap->length - 2);
+          assert (hop->value);
+          memcpy (hop->value, pEncap->value + 2, pEncap->length - 2);
+          if (hop->length > pEncap->length - 2)
+            {
+              zlog_warn ("%s: VNC subtlv length mismatch: "
+                         "RFP option says %d, attr says %d "
+                         "(shrinking)",
+                         __func__, hop->length, pEncap->length - 2);
+              hop->length = pEncap->length - 2;
+            }
+          hop->next = ri->tea_options;
+          ri->tea_options = hop;
+          break;
+
+        default:
+          break;
+        }
+    }
+
+  rfapi_un_options_free (ri->un_options);   /* maybe free old version */
+  ri->un_options = rfapi_encap_tlv_to_un_option (bi->attr);
+
+  /*
+   * VN options
+   */
+  if (bi->extra && 
+      decode_rd_type(bi->extra->vnc.import.rd.val) == RD_TYPE_VNC_ETH)
+    {
+      /* ethernet route */
+
+      struct rfapi_vn_option *vo;
+
+      vo = XCALLOC (MTYPE_RFAPI_VN_OPTION, sizeof (struct rfapi_vn_option));
+      assert (vo);
+
+      vo->type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+
+      /* copy from RD already stored in bi, so we don't need it_node */
+      memcpy (&vo->v.l2addr.macaddr, bi->extra->vnc.import.rd.val+2,
+       ETHER_ADDR_LEN);
+
+      if (bi->attr && bi->attr->extra)
+        {
+          (void) rfapiEcommunityGetLNI (bi->attr->extra->ecommunity,
+                                        &vo->v.l2addr.logical_net_id);
+        }
+
+      /* local_nve_id comes from RD */
+      vo->v.l2addr.local_nve_id = bi->extra->vnc.import.rd.val[1];
+
+      /* label comes from MP_REACH_NLRI label */
+      vo->v.l2addr.label = decode_label (bi->extra->tag);
+
+      rfapi_vn_options_free (ri->vn_options);   /* maybe free old version */
+      ri->vn_options = vo;
+    }
+
+  /*
+   * If there is an auxiliary IP address (L2 can have it), copy it
+   */
+  if (bi && bi->extra && bi->extra->vnc.import.aux_prefix.family)
+    {
+      ri->rk.aux_prefix = bi->extra->vnc.import.aux_prefix;
+    }
+}
+
+/*
+ * rfapiRibPreloadBi
+ *
+ *     Install route into NVE RIB model so as to be consistent with
+ *     caller's response to rfapi_query().
+ *
+ *     Also: return indication to caller whether this specific route
+ *     should be included in the response to the NVE according to
+ *     the following tests:
+ *
+ *     1. If there were prior duplicates of this route in this same
+ *        query response, don't include the route.
+ *
+ * RETURN VALUE:
+ *
+ *     0       OK to include route in response
+ *     !0      do not include route in response
+ */
+int
+rfapiRibPreloadBi(
+  struct route_node *rfd_rib_node,     /* NULL = don't preload or filter */
+  struct prefix     *pfx_vn,
+  struct prefix     *pfx_un,
+  uint32_t          lifetime,
+  struct bgp_info   *bi)
+{
+  struct rfapi_descriptor *rfd;
+  struct skiplist         *slRibPt = NULL;
+  struct rfapi_info       *ori = NULL;
+  struct rfapi_rib_key    rk;
+  struct route_node       *trn;
+  afi_t                   afi;
+
+  if (!rfd_rib_node)
+    return 0;
+
+  afi  = family2afi(rfd_rib_node->p.family);
+
+  rfd = (struct rfapi_descriptor *)(rfd_rib_node->table->info);
+
+  memset((void *)&rk, 0, sizeof(rk));
+  rk.vn = *pfx_vn;
+  rk.rd = bi->extra->vnc.import.rd;
+
+  /*
+   * If there is an auxiliary IP address (L2 can have it), copy it
+   */
+  if (bi->extra->vnc.import.aux_prefix.family)
+    {
+      rk.aux_prefix = bi->extra->vnc.import.aux_prefix;
+    }
+
+  /*
+   * is this route already in NVE's RIB?
+   */
+  slRibPt = (struct skiplist *) rfd_rib_node->info;
+
+  if (slRibPt && !skiplist_search (slRibPt, &rk, (void **) &ori))
+    {
+
+      if ((ori->rsp_counter == rfd->rsp_counter) &&
+        (ori->last_sent_time == rfd->rsp_time))
+       {
+         return -1;    /* duplicate in this response */
+       }
+
+      /* found: update contents of existing route in RIB */
+      ori->un = *pfx_un;
+      rfapiRibBi2Ri(bi, ori, lifetime);
+    }
+  else
+    {
+      /* not found: add new route to RIB */
+      ori = rfapi_info_new ();
+      ori->rk = rk;
+      ori->un = *pfx_un;
+      rfapiRibBi2Ri(bi, ori, lifetime);
+
+      if (!slRibPt)
+        {
+          slRibPt = skiplist_new (0, rfapi_rib_key_cmp, NULL);
+          rfd_rib_node->info = slRibPt;
+          route_lock_node (rfd_rib_node);
+          RFAPI_RIB_PREFIX_COUNT_INCR (rfd, rfd->bgp->rfapi);
+        }
+      skiplist_insert (slRibPt, &ori->rk, ori);
+    }
+
+  ori->last_sent_time = rfapi_time (NULL);
+
+  /*
+   * poke timer
+   */
+  RFAPI_RIB_CHECK_COUNTS (0, 0);
+  rfapiRibStartTimer (rfd, ori, rfd_rib_node, 0);
+  RFAPI_RIB_CHECK_COUNTS (0, 0);
+
+  /*
+   * Update last sent time for prefix
+   */
+  trn = route_node_get (rfd->rsp_times[afi], &rfd_rib_node->p); /* locks trn */
+  trn->info = (void *) (uintptr_t) bgp_clock ();
+  if (trn->lock > 1)
+    route_unlock_node (trn);
+
+  return 0;
+}
+
+/*
+ * Frees rfapi_info items at node
+ *
+ * Adjust 'rib' and 'rib_pending' as follows:
+ *
+ * If rib_pending node->info is 1 (magic value):
+ *     callback: NHL = RIB NHL with lifetime = withdraw_lifetime_value
+ *     RIB = remove all routes at the node
+ *     DONE
+ *
+ * For each item at rib node:
+ *  if not present in pending node, move RIB item to "delete list"
+ *
+ * For each item at pending rib node:
+ *  if present (same vn/un) in rib node with same lifetime & options, drop
+ *     matching item from pending node
+ *
+ * For each remaining item at pending rib node, add or replace item
+ * at rib node.
+ *
+ * Construct NHL as concatenation of pending list + delete list
+ *
+ * Clear pending node
+ */
+static void
+process_pending_node (
+  struct bgp *bgp,
+  struct rfapi_descriptor      *rfd,
+  afi_t                                afi,
+  struct route_node            *pn,                    /* pending node */
+  struct rfapi_next_hop_entry  **head,
+  struct rfapi_next_hop_entry  **tail)
+{
+  struct listnode              *node = NULL;
+  struct listnode              *nnode = NULL;
+  struct rfapi_info            *ri = NULL;             /* happy valgrind */
+  struct rfapi_ip_prefix       hp = { 0 };             /* pfx to put in NHE */
+  struct route_node            *rn = NULL;
+  struct skiplist              *slRibPt = NULL;        /* rib list */
+  struct skiplist              *slPendPt = NULL;
+  struct list                  *lPendCost = NULL;
+  struct list                  *delete_list = NULL;
+  int                          printedprefix = 0;
+  char                         buf_prefix[BUFSIZ];
+  int                          rib_node_started_nonempty = 0;
+  int                          sendingsomeroutes = 0;
+
+#if DEBUG_PROCESS_PENDING_NODE
+  unsigned int count_rib_initial = 0;
+  unsigned int count_pend_vn_initial = 0;
+  unsigned int count_pend_cost_initial = 0;
+#endif
+
+  assert (pn);
+  prefix2str (&pn->p, buf_prefix, BUFSIZ);
+  zlog_debug ("%s: afi=%d, %s pn->info=%p",
+              __func__, afi, buf_prefix, pn->info);
+
+  if (AFI_ETHER != afi)
+    {
+      rfapiQprefix2Rprefix (&pn->p, &hp);
+    }
+
+  RFAPI_RIB_CHECK_COUNTS (1, 0);
+
+  /*
+   * Find corresponding RIB node
+   */
+  rn = route_node_get (rfd->rib[afi], &pn->p);  /* locks rn */
+
+  /*
+   * RIB skiplist has key=rfapi_addr={vn,un}, val = rfapi_info,
+   * skiplist.del = NULL
+   */
+  slRibPt = (struct skiplist *) rn->info;
+  if (slRibPt)
+    rib_node_started_nonempty = 1;
+
+  slPendPt = (struct skiplist *) (pn->aggregate);
+  lPendCost = (struct list *) (pn->info);
+
+#if DEBUG_PROCESS_PENDING_NODE
+  /* debugging */
+  if (slRibPt)
+    count_rib_initial = skiplist_count (slRibPt);
+
+  if (slPendPt)
+    count_pend_vn_initial = skiplist_count (slPendPt);
+
+  if (lPendCost && lPendCost != (struct list *) 1)
+    count_pend_cost_initial = lPendCost->count;
+#endif
+
+
+  /*
+   * Handle special case: delete all routes at prefix
+   */
+  if (lPendCost == (struct list *) 1)
+    {
+      zlog_debug ("%s: lPendCost=1 => delete all", __func__);
+      if (slRibPt && !skiplist_empty (slRibPt))
+        {
+          delete_list = list_new ();
+          while (0 == skiplist_first (slRibPt, NULL, (void **) &ri))
+            {
+
+              char buf[BUFSIZ];
+              char buf2[BUFSIZ];
+
+              listnode_add (delete_list, ri);
+              zlog_debug ("%s: after listnode_add, delete_list->count=%d",
+                          __func__, delete_list->count);
+              rfapiFreeBgpTeaOptionChain (ri->tea_options);
+              ri->tea_options = NULL;
+
+              if (ri->timer)
+                {
+                  struct rfapi_rib_tcb *tcb;
+
+                  tcb = ((struct thread *) ri->timer)->arg;
+                  thread_cancel (ri->timer);
+                  XFREE (MTYPE_RFAPI_RECENT_DELETE, tcb);
+                  ri->timer = NULL;
+                }
+
+              prefix2str (&ri->rk.vn, buf, BUFSIZ);
+              prefix2str (&ri->un, buf2, BUFSIZ);
+              zlog_debug
+                ("%s:   put dl pfx=%s vn=%s un=%s cost=%d life=%d vn_options=%p",
+                 __func__, buf_prefix, buf, buf2, ri->cost, ri->lifetime,
+                 ri->vn_options);
+
+              skiplist_delete_first (slRibPt);
+            }
+
+          assert (skiplist_empty (slRibPt));
+
+          skiplist_free (slRibPt);
+          rn->info = slRibPt = NULL;
+          route_unlock_node (rn);
+
+          lPendCost = pn->info = NULL;
+          route_unlock_node (pn);
+
+          goto callback;
+        }
+      if (slRibPt)
+        {
+          skiplist_free (slRibPt);
+          rn->info = NULL;
+          route_unlock_node (rn);
+        }
+
+      assert (!slPendPt);
+      if (slPendPt)
+        {                       /* TBD I think we can toss this block */
+          skiplist_free (slPendPt);
+          pn->aggregate = NULL;
+          route_unlock_node (pn);
+        }
+
+      pn->info = NULL;
+      route_unlock_node (pn);
+
+      route_unlock_node (rn);   /* route_node_get() */
+
+      if (rib_node_started_nonempty)
+        {
+          RFAPI_RIB_PREFIX_COUNT_DECR (rfd, bgp->rfapi);
+        }
+
+      RFAPI_RIB_CHECK_COUNTS (1, 0);
+
+      return;
+    }
+
+  zlog_debug ("%s:   lPendCost->count=%d, slRibPt->count=%d",
+              __func__,
+              (lPendCost ? lPendCost->count : -1),
+              (slRibPt ? slRibPt->count : -1));
+
+  /*
+   * Iterate over routes at RIB Node.
+   * If not found at Pending Node, delete from RIB Node and add to deletelist
+   * If found at Pending Node
+   *      If identical rfapi_info, delete from Pending Node
+   */
+  if (slRibPt)
+    {
+      void *cursor = NULL;
+      struct rfapi_info *ori;
+
+      /*
+       * Iterate over RIB List
+       *
+       */
+      while (!skiplist_next (slRibPt, NULL, (void **) &ori, &cursor))
+        {
+
+          if (skiplist_search (slPendPt, &ori->rk, (void **) &ri))
+            {
+              /*
+               * Not in Pending list, so it should be deleted
+               */
+              if (!delete_list)
+                delete_list = list_new ();
+              listnode_add (delete_list, ori);
+              rfapiFreeBgpTeaOptionChain (ori->tea_options);
+              ori->tea_options = NULL;
+              if (ori->timer)
+                {
+                  struct rfapi_rib_tcb *tcb;
+
+                  tcb = ((struct thread *) ori->timer)->arg;
+                  thread_cancel (ori->timer);
+                  XFREE (MTYPE_RFAPI_RECENT_DELETE, tcb);
+                  ori->timer = NULL;
+                }
+
+#if DEBUG_PROCESS_PENDING_NODE
+              /* deleted from slRibPt below, after we're done iterating */
+              zlog_debug
+                ("%s:   slRibPt ri %p not matched in pending list, delete",
+                 __func__, ori);
+#endif
+
+            }
+          else
+            {
+              /*
+               * Found in pending list. If same lifetime, cost, options,
+               * then remove from pending list because the route
+               * hasn't changed.
+               */
+              int same = 0;
+              if (!rfapi_info_cmp (ori, ri))
+                {
+                  /* same: delete from pending list */
+                  same = 1;
+
+                  skiplist_delete (slPendPt, &ri->rk, NULL);
+                  assert (lPendCost);
+                  if (lPendCost)
+                    {
+                      /* linear walk: might need optimization */
+                      listnode_delete (lPendCost, ri);  /* XXX doesn't free data! bug? */
+                      rfapi_info_free (ri);     /* grr... */
+                    }
+                }
+#if DEBUG_PROCESS_PENDING_NODE
+              zlog_debug ("%s:   slRibPt ri %p matched in pending list, %s",
+                          __func__, ori,
+                          (same ? "same info" : "different info"));
+#endif
+            }
+        }
+      /*
+       * Go back and delete items from RIB
+       */
+      if (delete_list)
+        {
+          for (ALL_LIST_ELEMENTS_RO (delete_list, node, ri))
+            {
+              zlog_debug ("%s:   deleting ri %p from slRibPt", __func__, ri);
+              assert (!skiplist_delete (slRibPt, &ri->rk, NULL));
+            }
+          if (skiplist_empty (slRibPt))
+            {
+              skiplist_free (slRibPt);
+              slRibPt = rn->info = NULL;
+              route_unlock_node (rn);
+            }
+        }
+    }
+
+  RFAPI_RIB_CHECK_COUNTS (0, (delete_list ? delete_list->count : 0));
+
+  /*
+   * Iterate over routes at Pending Node
+   *
+   * If {vn} found at RIB Node, update RIB Node route contents to match PN
+   * If {vn} NOT found at RIB Node, add copy to RIB Node
+   */
+  if (lPendCost)
+    {
+      for (ALL_LIST_ELEMENTS_RO (lPendCost, node, ri))
+        {
+
+          struct rfapi_info *ori;
+
+          if (slRibPt && !skiplist_search (slRibPt, &ri->rk, (void **) &ori))
+            {
+
+              /* found: update contents of existing route in RIB */
+              ori->un = ri->un;
+              ori->cost = ri->cost;
+              ori->lifetime = ri->lifetime;
+              rfapiFreeBgpTeaOptionChain (ori->tea_options);
+              ori->tea_options = rfapiOptionsDup (ri->tea_options);
+              ori->last_sent_time = rfapi_time (NULL);
+
+              rfapiFreeRfapiVnOptionChain (ori->vn_options);
+              ori->vn_options = rfapiVnOptionsDup (ri->vn_options);
+
+              rfapiFreeRfapiUnOptionChain (ori->un_options);
+              ori->un_options = rfapiUnOptionsDup (ri->un_options);
+
+              zlog_debug
+                ("%s:   matched lPendCost item %p in slRibPt, rewrote",
+                 __func__, ri);
+
+            }
+          else
+            {
+
+             char      buf_rd[BUFSIZ];
+
+              /* not found: add new route to RIB */
+              ori = rfapi_info_new ();
+              ori->rk = ri->rk;
+              ori->un = ri->un;
+              ori->cost = ri->cost;
+              ori->lifetime = ri->lifetime;
+              ori->tea_options = rfapiOptionsDup (ri->tea_options);
+              ori->last_sent_time = rfapi_time (NULL);
+              ori->vn_options = rfapiVnOptionsDup (ri->vn_options);
+              ori->un_options = rfapiUnOptionsDup (ri->un_options);
+
+              if (!slRibPt)
+                {
+                  slRibPt = skiplist_new (0, rfapi_rib_key_cmp, NULL);
+                  rn->info = slRibPt;
+                  route_lock_node (rn);
+                }
+              skiplist_insert (slRibPt, &ori->rk, ori);
+
+#if DEBUG_RIB_SL_RD
+             prefix_rd2str(&ori->rk.rd, buf_rd, sizeof(buf_rd));
+#else
+             buf_rd[0] = 0;
+#endif
+
+              zlog_debug ("%s:   nomatch lPendCost item %p in slRibPt, added (rd=%s)",
+                          __func__, ri, buf_rd);
+            }
+
+          /*
+           * poke timer
+           */
+          RFAPI_RIB_CHECK_COUNTS (0, (delete_list ? delete_list->count : 0));
+          rfapiRibStartTimer (rfd, ori, rn, 0);
+          RFAPI_RIB_CHECK_COUNTS (0, (delete_list ? delete_list->count : 0));
+        }
+    }
+
+
+callback:
+  /*
+   * Construct NHL as concatenation of pending list + delete list
+   */
+
+
+  RFAPI_RIB_CHECK_COUNTS (0, (delete_list ? delete_list->count : 0));
+
+  if (lPendCost)
+    {
+
+      char buf[BUFSIZ];
+      char buf2[BUFSIZ];
+
+      zlog_debug ("%s: lPendCost->count now %d", __func__, lPendCost->count);
+      zlog_debug ("%s: For prefix %s (a)", __func__, buf_prefix);
+      printedprefix = 1;
+
+      for (ALL_LIST_ELEMENTS (lPendCost, node, nnode, ri))
+        {
+
+          struct rfapi_next_hop_entry *new;
+          struct route_node *trn;
+
+          new =
+            XCALLOC (MTYPE_RFAPI_NEXTHOP,
+                     sizeof (struct rfapi_next_hop_entry));
+          assert (new);
+
+          if (ri->rk.aux_prefix.family)
+            {
+              rfapiQprefix2Rprefix (&ri->rk.aux_prefix, &new->prefix);
+            }
+          else
+            {
+              new->prefix = hp;
+              if (AFI_ETHER == afi)
+                {
+                  /* hp is 0; need to set length to match AF of vn */
+                  new->prefix.length =
+                    (ri->rk.vn.family == AF_INET) ? 32 : 128;
+                }
+            }
+          new->prefix.cost = ri->cost;
+          new->lifetime = ri->lifetime;
+          rfapiQprefix2Raddr (&ri->rk.vn, &new->vn_address);
+          rfapiQprefix2Raddr (&ri->un, &new->un_address);
+          /* free option chain from ri */
+          rfapiFreeBgpTeaOptionChain (ri->tea_options);
+
+          ri->tea_options = NULL;      /* option chain was transferred to NHL */
+
+          new->vn_options = ri->vn_options;
+          ri->vn_options = NULL;        /* option chain was transferred to NHL */
+
+          new->un_options = ri->un_options;
+          ri->un_options = NULL;        /* option chain was transferred to NHL */
+
+          if (*tail)
+            (*tail)->next = new;
+          *tail = new;
+          if (!*head)
+            {
+              *head = new;
+            }
+          sendingsomeroutes = 1;
+
+          ++rfd->stat_count_nh_reachable;
+          ++bgp->rfapi->stat.count_updated_response_updates;
+
+          /*
+           * update this NVE's timestamp for this prefix
+           */
+          trn = route_node_get (rfd->rsp_times[afi], &pn->p);   /* locks trn */
+          trn->info = (void *) (uintptr_t) bgp_clock ();
+          if (trn->lock > 1)
+            route_unlock_node (trn);
+
+          rfapiRfapiIpAddr2Str (&new->vn_address, buf, BUFSIZ);
+          rfapiRfapiIpAddr2Str (&new->un_address, buf2, BUFSIZ);
+          zlog_debug ("%s:   add vn=%s un=%s cost=%d life=%d", __func__,
+                      buf, buf2, new->prefix.cost, new->lifetime);
+        }
+    }
+
+  RFAPI_RIB_CHECK_COUNTS (0, (delete_list ? delete_list->count : 0));
+
+  if (delete_list)
+    {
+
+      char buf[BUFSIZ];
+      char buf2[BUFSIZ];
+
+      if (!printedprefix)
+        {
+          zlog_debug ("%s: For prefix %s (d)", __func__, buf_prefix);
+          printedprefix = 1;
+        }
+      zlog_debug ("%s: delete_list has %d elements",
+                  __func__, delete_list->count);
+
+      RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+      if (!CHECK_FLAG (bgp->rfapi_cfg->flags,
+                       BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE))
+        {
+
+          for (ALL_LIST_ELEMENTS (delete_list, node, nnode, ri))
+            {
+
+              struct rfapi_next_hop_entry *new;
+              struct rfapi_info *ri_del;
+
+              RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+              new = XCALLOC (MTYPE_RFAPI_NEXTHOP,
+                             sizeof (struct rfapi_next_hop_entry));
+              assert (new);
+
+              if (ri->rk.aux_prefix.family)
+                {
+                  rfapiQprefix2Rprefix (&ri->rk.aux_prefix, &new->prefix);
+                }
+              else
+                {
+                  new->prefix = hp;
+                  if (AFI_ETHER == afi)
+                    {
+                      /* hp is 0; need to set length to match AF of vn */
+                      new->prefix.length =
+                        (ri->rk.vn.family == AF_INET) ? 32 : 128;
+                    }
+                }
+
+              new->prefix.cost = ri->cost;
+              new->lifetime = RFAPI_REMOVE_RESPONSE_LIFETIME;
+              rfapiQprefix2Raddr (&ri->rk.vn, &new->vn_address);
+              rfapiQprefix2Raddr (&ri->un, &new->un_address);
+
+              new->vn_options = ri->vn_options;
+              ri->vn_options = NULL;    /* option chain was transferred to NHL */
+
+              new->un_options = ri->un_options;
+              ri->un_options = NULL;    /* option chain was transferred to NHL */
+
+              if (*tail)
+                (*tail)->next = new;
+              *tail = new;
+              if (!*head)
+                {
+                  *head = new;
+                }
+              ++rfd->stat_count_nh_removal;
+              ++bgp->rfapi->stat.count_updated_response_deletes;
+
+              rfapiRfapiIpAddr2Str (&new->vn_address, buf, BUFSIZ);
+              rfapiRfapiIpAddr2Str (&new->un_address, buf2, BUFSIZ);
+              zlog_debug ("%s:   DEL vn=%s un=%s cost=%d life=%d", __func__,
+                          buf, buf2, new->prefix.cost, new->lifetime);
+
+              RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+              /*
+               * Update/add to list of recent deletions at this prefix
+               */
+              if (!rn->aggregate)
+                {
+                  rn->aggregate = skiplist_new (0, rfapi_rib_key_cmp,
+                                                (void (*)(void *))
+                                                rfapi_info_free);
+                  route_lock_node (rn);
+                }
+              RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+
+              /* sanity check lifetime */
+              if (ri->lifetime > RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY)
+                ri->lifetime = RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY;
+
+              RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+              /* cancel normal expire timer */
+              if (ri->timer)
+                {
+                  struct rfapi_rib_tcb *tcb;
+
+                  tcb = ((struct thread *) ri->timer)->arg;
+                  thread_cancel ((struct thread *) ri->timer);
+                  XFREE (MTYPE_RFAPI_RECENT_DELETE, tcb);
+                  ri->timer = NULL;
+                }
+              RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+
+              /*
+               * Look in "recently-deleted" list
+               */
+              if (skiplist_search ((struct skiplist *) (rn->aggregate),
+                                   &ri->rk, (void **) &ri_del))
+                {
+
+                  int rc;
+
+                  RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+                  /*
+                   * NOT in "recently-deleted" list
+                   */
+                  list_delete_node (delete_list, node); /* does not free ri */
+                  rc = skiplist_insert ((struct skiplist *) (rn->aggregate),
+                                        &ri->rk, ri);
+                  assert (!rc);
+
+                  RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+                  rfapiRibStartTimer (rfd, ri, rn, 1);
+                  RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+                  ri->last_sent_time = rfapi_time (NULL);
+#if DEBUG_RIB_SL_RD
+                 {
+                   char buf_rd[BUFSIZ];
+                   prefix_rd2str(&ri->rk.rd, buf_rd, sizeof(buf_rd));
+                   zlog_debug("%s: move route to recently deleted list, rd=%s",
+                       __func__, buf_rd);
+                 }
+#endif
+
+                }
+              else
+                {
+                  /*
+                   * IN "recently-deleted" list
+                   */
+                  RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+                  rfapiRibStartTimer (rfd, ri_del, rn, 1);
+                  RFAPI_RIB_CHECK_COUNTS (0, delete_list->count);
+                  ri->last_sent_time = rfapi_time (NULL);
+
+                }
+            }
+        }
+      else
+        {
+          zlog_debug ("%s: response removal disabled, omitting removals",
+                      __func__);
+        }
+
+      delete_list->del = (void (*)(void *)) rfapi_info_free;
+      list_delete (delete_list);
+    }
+
+  RFAPI_RIB_CHECK_COUNTS (0, 0);
+
+  /*
+   * Reset pending lists. The final route_unlock_node() will probably
+   * cause the pending node to be released.
+   */
+  if (slPendPt)
+    {
+      skiplist_free (slPendPt);
+      pn->aggregate = NULL;
+      route_unlock_node (pn);
+    }
+  if (lPendCost)
+    {
+      list_delete (lPendCost);
+      pn->info = NULL;
+      route_unlock_node (pn);
+    }
+  RFAPI_RIB_CHECK_COUNTS (0, 0);
+
+  if (rib_node_started_nonempty)
+    {
+      if (!rn->info)
+        {
+          RFAPI_RIB_PREFIX_COUNT_DECR (rfd, bgp->rfapi);
+        }
+    }
+  else
+    {
+      if (rn->info)
+        {
+          RFAPI_RIB_PREFIX_COUNT_INCR (rfd, bgp->rfapi);
+        }
+    }
+
+  if (sendingsomeroutes)
+    rfapiMonitorTimersRestart (rfd, &pn->p);
+
+  route_unlock_node (rn);       /* route_node_get() */
+
+  RFAPI_RIB_CHECK_COUNTS (1, 0);
+}
+
+/*
+ * regardless of targets, construct a single callback by doing
+ * only one traversal of the pending RIB
+ *
+ *
+ * Do callback
+ *
+ */
+static void
+rib_do_callback_onepass (struct rfapi_descriptor *rfd, afi_t afi)
+{
+  struct bgp *bgp = bgp_get_default ();
+  struct rfapi_next_hop_entry *head = NULL;
+  struct rfapi_next_hop_entry *tail = NULL;
+  struct route_node *rn;
+
+#if DEBUG_L2_EXTRA
+  zlog_debug ("%s: rfd=%p, afi=%d", __func__, rfd, afi);
+#endif
+
+  if (!rfd->rib_pending[afi])
+    return;
+
+  assert (bgp->rfapi);
+
+  for (rn = route_top (rfd->rib_pending[afi]); rn; rn = route_next (rn))
+    {
+      process_pending_node (bgp, rfd, afi, rn, &head, &tail);
+    }
+
+  if (head)
+    {
+      rfapi_response_cb_t *f;
+
+#if DEBUG_NHL
+      zlog_debug ("%s: response callback NHL follows:", __func__);
+      rfapiPrintNhl (NULL, head);
+#endif
+
+      if (rfd->response_cb)
+        f = rfd->response_cb;
+      else
+        f = bgp->rfapi->rfp_methods.response_cb;
+
+      bgp->rfapi->flags |= RFAPI_INCALLBACK;
+      zlog_debug ("%s: invoking updated response callback", __func__);
+      (*f) (head, rfd->cookie);
+      bgp->rfapi->flags &= ~RFAPI_INCALLBACK;
+      ++bgp->rfapi->response_updated_count;
+    }
+}
+
+static wq_item_status
+rfapiRibDoQueuedCallback (struct work_queue *wq, void *data)
+{
+  struct rfapi_descriptor *rfd;
+  afi_t afi;
+  uint32_t queued_flag;
+
+  RFAPI_RIB_CHECK_COUNTS (1, 0);
+
+  rfd = ((struct rfapi_updated_responses_queue *) data)->rfd;
+  afi = ((struct rfapi_updated_responses_queue *) data)->afi;
+
+  /* Make sure the HD wasn't closed after the work item was scheduled */
+  if (rfapi_check (rfd))
+    return WQ_SUCCESS;
+
+  rib_do_callback_onepass (rfd, afi);
+
+  queued_flag = RFAPI_QUEUED_FLAG (afi);
+
+  UNSET_FLAG (rfd->flags, queued_flag);
+
+  RFAPI_RIB_CHECK_COUNTS (1, 0);
+
+  return WQ_SUCCESS;
+}
+
+static void
+rfapiRibQueueItemDelete (struct work_queue *wq, void *data)
+{
+  XFREE (MTYPE_RFAPI_UPDATED_RESPONSE_QUEUE, data);
+}
+
+static void
+updated_responses_queue_init (struct rfapi_descriptor *rfd)
+{
+  if (rfd->updated_responses_queue)
+    return;
+
+  rfd->updated_responses_queue = work_queue_new (bm->master,
+                                                 "rfapi updated responses");
+  assert (rfd->updated_responses_queue);
+
+  rfd->updated_responses_queue->spec.workfunc = rfapiRibDoQueuedCallback;
+  rfd->updated_responses_queue->spec.del_item_data = rfapiRibQueueItemDelete;
+  rfd->updated_responses_queue->spec.max_retries = 0;
+  rfd->updated_responses_queue->spec.hold = 1;
+}
+
+/*
+ * Called when an import table node is modified. Construct a
+ * new complete nexthop list, sorted by cost (lowest first),
+ * based on the import table node.
+ *
+ * Filter out duplicate nexthops (vn address). There should be
+ * only one UN address per VN address from the point of view of
+ * a given import table, so we can probably ignore UN addresses
+ * while filtering.
+ *
+ * Based on rfapiNhlAddNodeRoutes()
+ */
+void
+rfapiRibUpdatePendingNode (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct rfapi_import_table    *it,        /* needed for L2 */
+  struct route_node            *it_node,
+  uint32_t                     lifetime)
+{
+  struct prefix                        *prefix;
+  struct bgp_info              *bi;
+  struct route_node            *pn;
+  afi_t                                afi;
+  uint32_t                     queued_flag;
+  int                          count = 0;
+  char                         buf[BUFSIZ];
+
+  zlog_debug ("%s: entry", __func__);
+
+  if (CHECK_FLAG (bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_CALLBACK_DISABLE))
+    return;
+
+  zlog_debug ("%s: callbacks are not disabled", __func__);
+
+  RFAPI_RIB_CHECK_COUNTS (1, 0);
+
+  prefix = &it_node->p;
+  afi = family2afi (prefix->family);
+  prefix2str (prefix, buf, BUFSIZ);
+  zlog_debug ("%s: prefix=%s", __func__, buf);
+
+  pn = route_node_get (rfd->rib_pending[afi], prefix);
+  assert (pn);
+
+  zlog_debug ("%s: pn->info=%p, pn->aggregate=%p", __func__, pn->info,
+              pn->aggregate);
+
+  if (pn->aggregate)
+    {
+      /* 
+       * free references into the rfapi_info structures before
+       * freeing the structures themselves
+       */
+      skiplist_free ((struct skiplist *) (pn->aggregate));
+      pn->aggregate = NULL;
+      route_unlock_node (pn);   /* skiplist deleted */
+    }
+
+
+  /* 
+   * free the rfapi_info structures
+   */
+  if (pn->info)
+    {
+      if (pn->info != (void *) 1)
+        {
+          list_delete ((struct list *) (pn->info));
+        }
+      pn->info = NULL;
+      route_unlock_node (pn);   /* linklist or 1 deleted */
+    }
+
+  /*
+   * The BIs in the import table are already sorted by cost
+   */
+  for (bi = it_node->info; bi; bi = bi->next)
+    {
+
+      struct rfapi_info *ri;
+      struct prefix pfx_nh;
+
+      if (!bi->attr)
+        {
+          /* shouldn't happen */
+          /* TBD increment error stats counter */
+          continue;
+        }
+      if (!bi->extra)
+        {
+          /* shouldn't happen */
+          /* TBD increment error stats counter */
+          continue;
+        }
+
+      rfapiNexthop2Prefix (bi->attr, &pfx_nh);
+
+      /*
+       * Omit route if nexthop is self
+       */
+      if (CHECK_FLAG
+          (bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP))
+        {
+
+          struct prefix pfx_vn;
+
+          rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx_vn);
+          if (prefix_same (&pfx_vn, &pfx_nh))
+            continue;
+        }
+
+      ri = rfapi_info_new ();
+      ri->rk.vn = pfx_nh;
+      ri->rk.rd = bi->extra->vnc.import.rd;
+      /*
+       * If there is an auxiliary IP address (L2 can have it), copy it
+       */
+      if (bi->extra->vnc.import.aux_prefix.family)
+        {
+          ri->rk.aux_prefix = bi->extra->vnc.import.aux_prefix;
+        }
+
+      if (rfapiGetUnAddrOfVpnBi (bi, &ri->un))
+        {
+          rfapi_info_free (ri);
+          continue;
+        }
+
+      if (!pn->aggregate)
+        {
+          pn->aggregate = skiplist_new (0, rfapi_rib_key_cmp, NULL);
+          route_lock_node (pn);
+        }
+
+      /*
+       * If we have already added this nexthop, the insert will fail.
+       * Note that the skiplist key is a pointer INTO the rfapi_info
+       * structure which will be added to the "info" list.
+       * The skiplist entry VALUE is not used for anything but
+       * might be useful during debugging.
+       */
+      if (skiplist_insert ((struct skiplist *) pn->aggregate, &ri->rk, ri))
+        {
+
+          /*
+           * duplicate
+           */
+          rfapi_info_free (ri);
+          continue;
+        }
+
+      rfapiRibBi2Ri(bi, ri, lifetime);
+
+      if (!pn->info)
+        {
+          pn->info = list_new ();
+          ((struct list *)(pn->info))->del = (void (*)(void *))rfapi_info_free;
+          route_lock_node (pn);
+        }
+
+      listnode_add ((struct list *) (pn->info), ri);
+    }
+
+  if (pn->info)
+    {
+      count = ((struct list *) (pn->info))->count;
+    }
+
+  if (!count)
+    {
+      assert (!pn->info);
+      assert (!pn->aggregate);
+      pn->info = (void *) 1;    /* magic value means this node has no routes */
+      route_lock_node (pn);
+    }
+
+  route_unlock_node (pn);       /* route_node_get */
+
+  queued_flag = RFAPI_QUEUED_FLAG (afi);
+
+  if (!CHECK_FLAG (rfd->flags, queued_flag))
+    {
+
+      struct rfapi_updated_responses_queue *urq;
+
+      urq = XCALLOC (MTYPE_RFAPI_UPDATED_RESPONSE_QUEUE,
+                     sizeof (struct rfapi_updated_responses_queue));
+      assert (urq);
+      if (!rfd->updated_responses_queue)
+        updated_responses_queue_init (rfd);
+
+      SET_FLAG (rfd->flags, queued_flag);
+      urq->rfd = rfd;
+      urq->afi = afi;
+      work_queue_add (rfd->updated_responses_queue, urq);
+    }
+  RFAPI_RIB_CHECK_COUNTS (1, 0);
+}
+
+void
+rfapiRibUpdatePendingNodeSubtree (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct rfapi_import_table    *it,
+  struct route_node            *it_node,
+  struct route_node            *omit_subtree,    /* may be NULL */
+  uint32_t                     lifetime)
+{
+  if (it_node->l_left && (it_node->l_left != omit_subtree))
+    {
+      if (it_node->l_left->info)
+        rfapiRibUpdatePendingNode (bgp, rfd, it, it_node->l_left, lifetime);
+      rfapiRibUpdatePendingNodeSubtree (bgp, rfd, it, it_node->l_left,
+                                        omit_subtree, lifetime);
+    }
+
+  if (it_node->l_right && (it_node->l_right != omit_subtree))
+    {
+      if (it_node->l_right->info)
+        rfapiRibUpdatePendingNode (bgp, rfd, it, it_node->l_right, lifetime);
+      rfapiRibUpdatePendingNodeSubtree (bgp, rfd, it, it_node->l_right,
+                                        omit_subtree, lifetime);
+    }
+}
+
+/*
+ * RETURN VALUE
+ *
+ *     0       allow prefix to be included in response
+ *     !0      don't allow prefix to be included in response
+ */
+int
+rfapiRibFTDFilterRecentPrefix(
+  struct rfapi_descriptor      *rfd,
+  struct route_node             *it_rn,                /* import table node */
+  struct prefix                 *pfx_target_original)  /* query target */
+{
+  struct bgp                   *bgp = rfd->bgp;
+  afi_t                         afi = family2afi(it_rn->p.family);
+  time_t                        prefix_time;
+  struct route_node             *trn;
+
+  /*
+   * Not in FTD mode, so allow prefix
+   */
+  if (bgp->rfapi_cfg->rfp_cfg.download_type != RFAPI_RFP_DOWNLOAD_FULL)
+    return 0;
+
+  /*
+   * TBD
+   * This matches behavior of now-obsolete rfapiRibFTDFilterRecent(),
+   * but we need to decide if that is correct.
+   */
+  if (it_rn->p.family == AF_ETHERNET)
+    return 0;
+
+#if DEBUG_FTD_FILTER_RECENT
+  {
+    char   buf_pfx[BUFSIZ];
+
+    prefix2str(&it_rn->p, buf_pfx, BUFSIZ);
+    zlog_debug("%s: prefix %s", __func__, buf_pfx);
+  }
+#endif
+
+  /*
+   * prefix covers target address, so allow prefix
+   */
+  if (prefix_match (&it_rn->p, pfx_target_original))
+    {
+#if DEBUG_FTD_FILTER_RECENT
+      zlog_debug("%s: prefix covers target, allowed", __func__);
+#endif
+      return 0;
+    }
+
+  /*
+   * check this NVE's timestamp for this prefix
+   */
+  trn = route_node_get (rfd->rsp_times[afi], &it_rn->p);  /* locks trn */
+  prefix_time = (time_t) trn->info;
+  if (trn->lock > 1)
+    route_unlock_node (trn);
+
+#if DEBUG_FTD_FILTER_RECENT
+  zlog_debug("%s: last sent time %lu, last allowed time %lu",
+    __func__, prefix_time, rfd->ftd_last_allowed_time);
+#endif
+
+  /*
+   * haven't sent this prefix, which doesn't cover target address,
+   * to NVE since ftd_advertisement_interval, so OK to send now.
+   */
+  if (prefix_time <= rfd->ftd_last_allowed_time)
+    return 0;
+
+  return 1;
+}
+
+/*
+ * Call when rfapi returns from rfapi_query() so the RIB reflects
+ * the routes sent to the NVE before the first updated response
+ *
+ * Also: remove duplicates from response. Caller should use returned
+ * value of nexthop chain.
+ */
+struct rfapi_next_hop_entry *
+rfapiRibPreload (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct rfapi_next_hop_entry  *response,
+  int                          use_eth_resolution)
+{
+  struct rfapi_next_hop_entry *nhp;
+  struct rfapi_next_hop_entry *nhp_next;
+  struct rfapi_next_hop_entry *head = NULL;
+  struct rfapi_next_hop_entry *tail = NULL;
+  time_t new_last_sent_time;
+
+  zlog_debug ("%s: loading response=%p, use_eth_resolution=%d",
+              __func__, response, use_eth_resolution);
+
+  new_last_sent_time = rfapi_time (NULL);
+
+  for (nhp = response; nhp; nhp = nhp_next)
+    {
+
+      struct prefix pfx;
+      struct rfapi_rib_key rk;
+      afi_t afi;
+      struct rfapi_info *ri;
+      int need_insert;
+      struct route_node *rn;
+      int rib_node_started_nonempty = 0;
+      struct route_node *trn;
+      int allowed = 0;
+
+      /* save in case we delete nhp */
+      nhp_next = nhp->next;
+
+      if (nhp->lifetime == RFAPI_REMOVE_RESPONSE_LIFETIME)
+        {
+          /*
+           * weird, shouldn't happen
+           */
+          zlog_debug
+            ("%s: got nhp->lifetime == RFAPI_REMOVE_RESPONSE_LIFETIME",
+             __func__);
+          continue;
+        }
+
+
+      if (use_eth_resolution)
+        {
+          /* get the prefix of the ethernet address in the L2 option */
+          struct rfapi_l2address_option *pL2o;
+          struct rfapi_vn_option *vo;
+
+          /*
+           * Look for VN option of type RFAPI_VN_OPTION_TYPE_L2ADDR
+           */
+          for (pL2o = NULL, vo = nhp->vn_options; vo; vo = vo->next)
+            {
+              if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type)
+                {
+                  pL2o = &vo->v.l2addr;
+                  break;
+                }
+            }
+
+          if (!pL2o)
+            {
+              /*
+               * not supposed to happen
+               */
+              zlog_debug ("%s: missing L2 info", __func__);
+              continue;
+            }
+
+          afi = AFI_ETHER;
+          rfapiL2o2Qprefix (pL2o, &pfx);
+        }
+      else
+        {
+          rfapiRprefix2Qprefix (&nhp->prefix, &pfx);
+          afi = family2afi (pfx.family);
+        }
+
+      /*
+       * TBD for ethernet, rib must know the right way to distinguish
+       * duplicate routes
+       *
+       * Current approach: prefix is key to radix tree; then
+       * each prefix has a set of routes with unique VN addrs
+       */
+
+      /*
+       * Look up prefix in RIB
+       */
+      rn = route_node_get (rfd->rib[afi], &pfx);        /* locks rn */
+
+      if (rn->info)
+        {
+          rib_node_started_nonempty = 1;
+        }
+      else
+        {
+          rn->info = skiplist_new (0, rfapi_rib_key_cmp, NULL);
+          route_lock_node (rn);
+        }
+
+      /*
+       * Look up route at prefix
+       */
+      need_insert = 0;
+      memset ((void *) &rk, 0, sizeof (rk));
+      assert (!rfapiRaddr2Qprefix (&nhp->vn_address, &rk.vn));
+
+      if (use_eth_resolution)
+        {
+          /* copy what came from aux_prefix to rk.aux_prefix */
+          rfapiRprefix2Qprefix (&nhp->prefix, &rk.aux_prefix);
+          if (RFAPI_0_PREFIX (&rk.aux_prefix)
+              && RFAPI_HOST_PREFIX (&rk.aux_prefix))
+            {
+              /* mark as "none" if nhp->prefix is 0/32 or 0/128 */
+              rk.aux_prefix.family = 0;
+            }
+        }
+
+#if DEBUG_NHL
+      {
+        char str_vn[BUFSIZ];
+        char str_aux_prefix[BUFSIZ];
+
+        str_vn[0] = 0;
+        str_aux_prefix[0] = 0;
+
+        prefix2str (&rk.vn, str_vn, BUFSIZ);
+        prefix2str (&rk.aux_prefix, str_aux_prefix, BUFSIZ);
+
+        if (!rk.aux_prefix.family)
+          {
+
+          }
+        zlog_debug ("%s:   rk.vn=%s rk.aux_prefix=%s",
+                    __func__, str_vn,
+                    (rk.aux_prefix.family ? str_aux_prefix : "-"));
+      }
+      zlog_debug ("%s: RIB skiplist for this prefix follows", __func__);
+      rfapiRibShowRibSl (NULL, &rn->p, (struct skiplist *) rn->info);
+#endif
+
+
+      if (!skiplist_search ((struct skiplist *) rn->info, &rk, (void **) &ri))
+        {
+          /*
+           * Already have this route; make values match
+           */
+          rfapiFreeRfapiUnOptionChain (ri->un_options);
+          ri->un_options = NULL;
+          rfapiFreeRfapiVnOptionChain (ri->vn_options);
+          ri->vn_options = NULL;
+
+#if DEBUG_NHL
+          zlog_debug ("%s: found in RIB", __func__);
+#endif
+
+          /*
+           * Filter duplicate routes from initial response.
+           * Check timestamps to avoid wraparound problems
+           */
+          if ((ri->rsp_counter != rfd->rsp_counter) ||
+              (ri->last_sent_time != new_last_sent_time))
+            {
+
+#if DEBUG_NHL
+              zlog_debug ("%s: allowed due to counter/timestamp diff",
+                          __func__);
+#endif
+              allowed = 1;
+            }
+
+        }
+      else
+        {
+
+#if DEBUG_NHL
+          zlog_debug ("%s: allowed due to not yet in RIB", __func__);
+#endif
+          /* not found: add new route to RIB */
+          ri = rfapi_info_new ();
+          need_insert = 1;
+          allowed = 1;
+        }
+
+      ri->rk = rk;
+      assert (!rfapiRaddr2Qprefix (&nhp->un_address, &ri->un));
+      ri->cost = nhp->prefix.cost;
+      ri->lifetime = nhp->lifetime;
+      ri->vn_options = rfapiVnOptionsDup (nhp->vn_options);
+      ri->rsp_counter = rfd->rsp_counter;
+      ri->last_sent_time = rfapi_time (NULL);
+
+      if (need_insert)
+        {
+          int rc;
+          rc = skiplist_insert ((struct skiplist *) rn->info, &ri->rk, ri);
+          assert (!rc);
+        }
+
+      if (!rib_node_started_nonempty)
+        {
+          RFAPI_RIB_PREFIX_COUNT_INCR (rfd, bgp->rfapi);
+        }
+
+      RFAPI_RIB_CHECK_COUNTS (0, 0);
+      rfapiRibStartTimer (rfd, ri, rn, 0);
+      RFAPI_RIB_CHECK_COUNTS (0, 0);
+
+      route_unlock_node (rn);
+
+      /*
+       * update this NVE's timestamp for this prefix
+       */
+      trn = route_node_get (rfd->rsp_times[afi], &pfx); /* locks trn */
+      trn->info = (void *) (uintptr_t) bgp_clock ();
+      if (trn->lock > 1)
+        route_unlock_node (trn);
+
+      {
+        char str_pfx[BUFSIZ];
+        char str_pfx_vn[BUFSIZ];
+
+        prefix2str (&pfx, str_pfx, BUFSIZ);
+        prefix2str (&rk.vn, str_pfx_vn, BUFSIZ);
+        zlog_debug
+          ("%s:   added pfx=%s nh[vn]=%s, cost=%u, lifetime=%u, allowed=%d",
+           __func__, str_pfx, str_pfx_vn, nhp->prefix.cost, nhp->lifetime,
+           allowed);
+      }
+
+      if (allowed)
+        {
+          if (tail)
+            (tail)->next = nhp;
+          tail = nhp;
+          if (!head)
+            {
+              head = nhp;
+            }
+        }
+      else
+        {
+          rfapi_un_options_free (nhp->un_options);
+          nhp->un_options = NULL;
+          rfapi_vn_options_free (nhp->vn_options);
+          nhp->vn_options = NULL;
+
+          XFREE (MTYPE_RFAPI_NEXTHOP, nhp);
+          nhp = NULL;
+        }
+    }
+
+  if (tail)
+    tail->next = NULL;
+  return head;
+}
+
+void
+rfapiRibPendingDeleteRoute (
+  struct bgp                   *bgp,
+  struct rfapi_import_table    *it,
+  afi_t                                afi,
+  struct route_node            *it_node)
+{
+  struct rfapi_descriptor *rfd;
+  struct listnode *node;
+  char buf[BUFSIZ];
+
+  prefix2str (&it_node->p, buf, BUFSIZ);
+  zlog_debug ("%s: entry, it=%p, afi=%d, it_node=%p, pfx=%s",
+              __func__, it, afi, it_node, buf);
+
+  if (AFI_ETHER == afi)
+    {
+      /*
+       * ethernet import tables are per-LNI and each ethernet monitor 
+       * identifies the rfd that owns it.
+       */
+      struct rfapi_monitor_eth *m;
+      struct route_node *rn;
+      struct skiplist *sl;
+      void *cursor;
+      int rc;
+
+      /*
+       * route-specific monitors
+       */
+      if ((sl = RFAPI_MONITOR_ETH (it_node)))
+        {
+
+          zlog_debug ("%s: route-specific skiplist: %p", __func__, sl);
+
+          for (cursor = NULL, rc =
+               skiplist_next (sl, NULL, (void **) &m, (void **) &cursor); !rc;
+               rc = skiplist_next (sl, NULL, (void **) &m, (void **) &cursor))
+            {
+
+#if DEBUG_PENDING_DELETE_ROUTE
+              zlog_debug ("%s: eth monitor rfd=%p", __func__, m->rfd);
+#endif
+              /*
+               * If we have already sent a route with this prefix to this
+               * NVE, it's OK to send an update with the delete
+               */
+              if ((rn = route_node_lookup (m->rfd->rib[afi], &it_node->p)))
+                {
+                  rfapiRibUpdatePendingNode (bgp, m->rfd, it, it_node,
+                                             m->rfd->response_lifetime);
+                  route_unlock_node (rn);
+                }
+            }
+        }
+
+      /*
+       * all-routes/FTD monitors
+       */
+      for (m = it->eth0_queries; m; m = m->next)
+        {
+#if DEBUG_PENDING_DELETE_ROUTE
+          zlog_debug ("%s: eth0 monitor rfd=%p", __func__, m->rfd);
+#endif
+          /*
+           * If we have already sent a route with this prefix to this
+           * NVE, it's OK to send an update with the delete
+           */
+          if ((rn = route_node_lookup (m->rfd->rib[afi], &it_node->p)))
+            {
+              rfapiRibUpdatePendingNode (bgp, m->rfd, it, it_node,
+                                         m->rfd->response_lifetime);
+            }
+        }
+
+    }
+  else
+    {
+      /*
+       * Find RFDs that reference this import table
+       */
+      for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, node, rfd))
+        {
+
+          struct route_node *rn;
+
+          zlog_debug ("%s: comparing rfd(%p)->import_table=%p to it=%p",
+                      __func__, rfd, rfd->import_table, it);
+
+          if (rfd->import_table != it)
+            continue;
+
+          zlog_debug ("%s: matched rfd %p", __func__, rfd);
+
+          /*
+           * If we have sent a response to this NVE with this prefix
+           * previously, we should send an updated response.
+           */
+          if ((rn = route_node_lookup (rfd->rib[afi], &it_node->p)))
+            {
+              rfapiRibUpdatePendingNode (bgp, rfd, it, it_node,
+                                         rfd->response_lifetime);
+              route_unlock_node (rn);
+            }
+        }
+    }
+}
+
+void
+rfapiRibShowResponsesSummary (void *stream)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+  struct bgp *bgp = bgp_get_default ();
+
+  int nves = 0;
+  int nves_with_nonempty_ribs = 0;
+  struct rfapi_descriptor *rfd;
+  struct listnode *node;
+
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  fp (out, "%-24s ", "Responses: (Prefixes)");
+  fp (out, "%-8s %-8u ", "Active:", bgp->rfapi->rib_prefix_count_total);
+  fp (out, "%-8s %-8u", "Maximum:", bgp->rfapi->rib_prefix_count_total_max);
+  fp (out, "%s", VTY_NEWLINE);
+
+  fp (out, "%-24s ", "           (Updated)");
+  fp (out, "%-8s %-8u ", "Update:",
+      bgp->rfapi->stat.count_updated_response_updates);
+  fp (out, "%-8s %-8u", "Remove:",
+      bgp->rfapi->stat.count_updated_response_deletes);
+  fp (out, "%-8s %-8u", "Total:",
+      bgp->rfapi->stat.count_updated_response_updates +
+      bgp->rfapi->stat.count_updated_response_deletes);
+  fp (out, "%s", VTY_NEWLINE);
+
+  fp (out, "%-24s ", "           (NVEs)");
+  for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, node, rfd))
+    {
+      ++nves;
+      if (rfd->rib_prefix_count)
+        ++nves_with_nonempty_ribs;
+    }
+  fp (out, "%-8s %-8u ", "Active:", nves_with_nonempty_ribs);
+  fp (out, "%-8s %-8u", "Total:", nves);
+  fp (out, "%s", VTY_NEWLINE);
+
+}
+
+void
+rfapiRibShowResponsesSummaryClear (void)
+{
+  struct bgp *bgp = bgp_get_default ();
+
+  bgp->rfapi->rib_prefix_count_total_max = bgp->rfapi->rib_prefix_count_total;
+}
+
+static int
+print_rib_sl (
+  int (*fp) (void *, const char *, ...),
+  struct vty           *vty,
+  void                 *out,
+  struct skiplist      *sl,
+  int                  deleted,
+  char                 *str_pfx,
+  int                  *printedprefix)
+{
+  struct rfapi_info *ri;
+  int rc;
+  void *cursor;
+  int routes_displayed = 0;
+
+  cursor = NULL;
+  for (rc = skiplist_next (sl, NULL, (void **) &ri, &cursor);
+       !rc; rc = skiplist_next (sl, NULL, (void **) &ri, &cursor))
+    {
+
+      char str_vn[BUFSIZ];
+      char str_un[BUFSIZ];
+      char str_lifetime[BUFSIZ];
+      char str_age[BUFSIZ];
+      char *p;
+      char str_rd[BUFSIZ];
+
+      ++routes_displayed;
+
+      prefix2str (&ri->rk.vn, str_vn, BUFSIZ);
+      p = index (str_vn, '/');
+      if (p)
+        *p = 0;
+
+      prefix2str (&ri->un, str_un, BUFSIZ);
+      p = index (str_un, '/');
+      if (p)
+        *p = 0;
+
+      rfapiFormatSeconds (ri->lifetime, str_lifetime, BUFSIZ);
+#if RFAPI_REGISTRATIONS_REPORT_AGE
+      rfapiFormatAge (ri->last_sent_time, str_age, BUFSIZ);
+#else
+      {
+        time_t now = rfapi_time (NULL);
+        time_t expire = ri->last_sent_time + (time_t) ri->lifetime;
+        /* allow for delayed/async removal */
+        rfapiFormatSeconds ((expire > now ? expire - now : 1),
+                            str_age, BUFSIZ);
+      }
+#endif
+
+    str_rd[0] = 0;     /* start empty */
+#if DEBUG_RIB_SL_RD
+    str_rd[0] = ' ';
+    prefix_rd2str(&ri->rk.rd, str_rd+1, BUFSIZ-1);
+#endif
+
+      fp (out, " %c %-20s %-15s %-15s %-4u %-8s %-8s%s%s",
+          deleted ? 'r' : ' ',
+          *printedprefix ? "" : str_pfx,
+          str_vn, str_un, ri->cost, str_lifetime, str_age, str_rd, VTY_NEWLINE);
+
+      if (!*printedprefix)
+        *printedprefix = 1;
+    }
+  return routes_displayed;
+}
+
+#if DEBUG_NHL
+/*
+ * This one is for debugging (set stream to NULL to send output to log)
+ */
+static void
+rfapiRibShowRibSl (void *stream, struct prefix *pfx, struct skiplist *sl)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  int nhs_displayed = 0;
+  char str_pfx[BUFSIZ];
+  int printedprefix = 0;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  prefix2str (pfx, str_pfx, BUFSIZ);
+
+  nhs_displayed += print_rib_sl (fp, vty, out, sl,
+                                 0, str_pfx, &printedprefix);
+}
+#endif
+
+void
+rfapiRibShowResponses (
+  void         *stream,
+  struct prefix        *pfx_match,
+  int          show_removed)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  struct rfapi_descriptor *rfd;
+  struct listnode *node;
+
+  struct bgp *bgp = bgp_get_default ();
+  int printedheader = 0;
+  int routes_total = 0;
+  int nhs_total = 0;
+  int prefixes_total = 0;
+  int prefixes_displayed = 0;
+  int nves_total = 0;
+  int nves_with_routes = 0;
+  int nves_displayed = 0;
+  int routes_displayed = 0;
+  int nhs_displayed = 0;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+  /*
+   * loop over NVEs
+   */
+  for (ALL_LIST_ELEMENTS_RO (&bgp->rfapi->descriptors, node, rfd))
+    {
+
+      int printednve = 0;
+      afi_t afi;
+
+      ++nves_total;
+      if (rfd->rib_prefix_count)
+        ++nves_with_routes;
+
+      for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+        {
+
+          struct route_node *rn;
+
+          if (!rfd->rib[afi])
+            continue;
+
+          for (rn = route_top (rfd->rib[afi]); rn; rn = route_next (rn))
+            {
+
+              struct skiplist *sl;
+              char str_pfx[BUFSIZ];
+              int printedprefix = 0;
+
+              if (!show_removed)
+                sl = rn->info;
+              else
+                sl = rn->aggregate;
+
+              if (!sl)
+                continue;
+
+              routes_total++;
+              nhs_total += skiplist_count (sl);
+              ++prefixes_total;
+
+              if (pfx_match && !prefix_match (pfx_match, &rn->p) &&
+                  !prefix_match (&rn->p, pfx_match))
+                continue;
+
+              ++prefixes_displayed;
+
+              if (!printedheader)
+                {
+                  ++printedheader;
+
+                  fp (out, "%s[%s]%s",
+                      VTY_NEWLINE,
+                      show_removed ? "Removed" : "Active", VTY_NEWLINE);
+                  fp (out, "%-15s %-15s%s", "Querying VN", "Querying UN",
+                      VTY_NEWLINE);
+                  fp (out, "   %-20s %-15s %-15s %4s %-8s %-8s%s",
+                      "Prefix", "Registered VN", "Registered UN", "Cost",
+                      "Lifetime",
+#if RFAPI_REGISTRATIONS_REPORT_AGE
+                      "Age",
+#else
+                      "Remaining",
+#endif
+                      VTY_NEWLINE);
+                }
+              if (!printednve)
+                {
+                  char str_vn[BUFSIZ];
+                  char str_un[BUFSIZ];
+
+                  ++printednve;
+                  ++nves_displayed;
+
+                  fp (out, "%-15s %-15s%s",
+                      rfapiRfapiIpAddr2Str (&rfd->vn_addr, str_vn, BUFSIZ),
+                      rfapiRfapiIpAddr2Str (&rfd->un_addr, str_un, BUFSIZ),
+                      VTY_NEWLINE);
+
+                }
+              prefix2str (&rn->p, str_pfx, BUFSIZ);
+              //fp(out, "  %s%s", buf, VTY_NEWLINE);  /* prefix */
+
+              routes_displayed++;
+              nhs_displayed += print_rib_sl (fp, vty, out, sl,
+                                             show_removed, str_pfx,
+                                             &printedprefix);
+            }
+        }
+    }
+
+  if (routes_total)
+    {
+      fp (out, "%s", VTY_NEWLINE);
+      fp (out, "Displayed %u NVEs, and %u out of %u %s prefixes",
+          nves_displayed, routes_displayed,
+          routes_total, show_removed ? "removed" : "active");
+      if (nhs_displayed != routes_displayed || nhs_total != routes_total)
+        fp (out, " with %u out of %u next hops", nhs_displayed, nhs_total);
+      fp (out, "%s", VTY_NEWLINE);
+    }
+}
diff --git a/bgpd/rfapi/rfapi_rib.h b/bgpd/rfapi/rfapi_rib.h
new file mode 100644 (file)
index 0000000..2a11194
--- /dev/null
@@ -0,0 +1,154 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/*
+ * File:       rfapi_rib.h
+ * Purpose:    per-nve rib
+ */
+
+#ifndef QUAGGA_HGP_RFAPI_RIB_H
+#define QUAGGA_HGP_RFAPI_RIB_H
+
+/*
+ * Key for indexing RIB and Pending RIB skiplists. For L3 RIBs,
+ * the VN address is sufficient because it represents the actual next hop.
+ *
+ * For L2 RIBs, it is possible to have multiple routes to a given L2
+ * prefix via a given VN address, but each route having a unique aux_prefix.
+ */
+struct rfapi_rib_key
+{
+  struct prefix                vn;
+  struct prefix_rd     rd;
+
+  /*
+   * for L2 routes: optional IP addr
+   * .family == 0 means "none"
+   */
+  struct prefix aux_prefix;
+};
+
+struct rfapi_info
+{
+  struct rfapi_rib_key         rk;             /* NVE VN addr + aux addr */
+  struct prefix                        un;
+  uint8_t                      cost;
+  uint32_t                     lifetime;
+  time_t                       last_sent_time;
+  uint32_t                     rsp_counter;    /* dedup initial responses */
+  struct bgp_tea_options       *tea_options;
+  struct rfapi_un_option       *un_options;
+  struct rfapi_vn_option       *vn_options;
+  void                         *timer;
+};
+
+/*
+ * Work item for updated responses queue
+ */
+struct rfapi_updated_responses_queue
+{
+  struct rfapi_descriptor      *rfd;
+  afi_t                                afi;
+};
+
+
+extern void
+rfapiRibClear (struct rfapi_descriptor *rfd);
+
+extern void
+rfapiRibFree (struct rfapi_descriptor *rfd);
+
+extern void
+rfapiRibUpdatePendingNode (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct rfapi_import_table    *it,
+  struct route_node            *it_node,
+  uint32_t                     lifetime);
+
+extern void
+rfapiRibUpdatePendingNodeSubtree (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct rfapi_import_table    *it,
+  struct route_node            *it_node,
+  struct route_node            *omit_subtree,
+  uint32_t                     lifetime);
+
+extern int
+rfapiRibPreloadBi(
+  struct route_node *rfd_rib_node,
+  struct prefix     *pfx_vn,
+  struct prefix     *pfx_un,
+  uint32_t          lifetime,
+  struct bgp_info   *bi);
+
+extern struct rfapi_next_hop_entry *
+rfapiRibPreload (
+  struct bgp                   *bgp,
+  struct rfapi_descriptor      *rfd,
+  struct rfapi_next_hop_entry  *response,
+  int                          use_eth_resolution);
+
+extern void
+rfapiRibPendingDeleteRoute (
+  struct bgp                   *bgp,
+  struct rfapi_import_table    *it,
+  afi_t                                afi,
+  struct route_node            *it_node);
+
+extern void
+rfapiRibShowResponsesSummary (void *stream);
+
+extern void
+rfapiRibShowResponsesSummaryClear (void);
+
+extern void
+rfapiRibShowResponses (
+  void         *stream,
+  struct prefix        *pfx_match,
+  int          show_removed);
+
+extern int
+rfapiRibFTDFilterRecentPrefix(
+  struct rfapi_descriptor      *rfd,
+  struct route_node             *it_rn,                /* import table node */
+  struct prefix                 *pfx_target_original); /* query target */
+
+extern void
+rfapiFreeRfapiUnOptionChain (struct rfapi_un_option *p);
+
+extern void
+rfapiFreeRfapiVnOptionChain (struct rfapi_vn_option *p);
+
+extern void
+rfapiRibCheckCounts (
+  int          checkstats,     /* validate rfd & global counts */
+  unsigned int offset);        /* number of ri's held separately */
+
+/* enable for debugging; disable for performance */
+#if 0
+#define RFAPI_RIB_CHECK_COUNTS(checkstats, offset)     rfapiRibCheckCounts(checkstats, offset)
+#else
+#define RFAPI_RIB_CHECK_COUNTS(checkstats, offset)
+#endif
+
+#endif /* QUAGGA_HGP_RFAPI_RIB_H */
diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c
new file mode 100644 (file)
index 0000000..315bac4
--- /dev/null
@@ -0,0 +1,5025 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+
+#include <errno.h>
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "memory.h"
+#include "routemap.h"
+#include "log.h"
+#include "linklist.h"
+#include "command.h"
+
+#include "bgpd.h"
+#include "bgp_ecommunity.h"
+#include "bgp_attr.h"
+#include "bgp_mplsvpn.h"
+
+#include "bgp_rfapi_cfg.h"
+#include "rfapi.h"
+#include "rfapi_backend.h"
+
+#include "bgp_route.h"
+#include "bgp_aspath.h"
+#include "bgp_community.h"
+#include "bgp_vnc_types.h"
+
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_monitor.h"
+#include "rfapi_rib.h"
+#include "rfapi_vty.h"
+#include "rfapi_ap.h"
+#include "rfapi_encap_tlv.h"
+#include "vnc_debug.h"
+
+#define DEBUG_L2_EXTRA 0
+
+#define VNC_SHOW_STR "VNC information\n"
+
+/* format related utilies */
+
+
+#define FMT_MIN      60         /* seconds */
+#define FMT_HOUR    (60  * FMT_MIN)
+#define FMT_DAY     (24  * FMT_HOUR)
+#define FMT_YEAR    (365 * FMT_DAY)
+
+char *
+rfapiFormatSeconds (uint32_t seconds, char *buf, size_t len)
+{
+  int year, day, hour, min;
+
+  if (seconds >= FMT_YEAR)
+    {
+      year = seconds / FMT_YEAR;
+      seconds -= year * FMT_YEAR;
+    }
+  else
+    year = 0;
+
+  if (seconds >= FMT_DAY)
+    {
+      day = seconds / FMT_DAY;
+      seconds -= day * FMT_DAY;
+    }
+  else
+    day = 0;
+
+  if (seconds >= FMT_HOUR)
+    {
+      hour = seconds / FMT_HOUR;
+      seconds -= hour * FMT_HOUR;
+    }
+  else
+    hour = 0;
+
+  if (seconds >= FMT_MIN)
+    {
+      min = seconds / FMT_MIN;
+      seconds -= min * FMT_MIN;
+    }
+  else
+    min = 0;
+
+  if (year > 0)
+    {
+      snprintf (buf, len, "%dy%dd%dh", year, day, hour);
+    }
+  else if (day > 0)
+    {
+      snprintf (buf, len, "%dd%dh%dm", day, hour, min);
+    }
+  else
+    {
+      snprintf (buf, len, "%02d:%02d:%02d", hour, min, seconds);
+    }
+
+  return buf;
+}
+
+char *
+rfapiFormatAge (time_t age, char *buf, size_t len)
+{
+  time_t now, age_adjusted;
+
+  now = rfapi_time (NULL);
+  age_adjusted = now - age;
+
+  return rfapiFormatSeconds (age_adjusted, buf, len);
+}
+
+
+/*
+ * Reimplementation of quagga/lib/prefix.c function, but
+ * for RFAPI-style prefixes
+ */
+void
+rfapiRprefixApplyMask (struct rfapi_ip_prefix *rprefix)
+{
+  uint8_t *pnt;
+  int index;
+  int offset;
+
+  static uint8_t maskbit[] =
+    { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+
+  switch (rprefix->prefix.addr_family)
+    {
+    case AF_INET:
+      index = rprefix->length / 8;
+      if (index < 4)
+        {
+          pnt = (uint8_t *) & rprefix->prefix.addr.v4;
+          offset = rprefix->length % 8;
+          pnt[index] &= maskbit[offset];
+          index++;
+          while (index < 4)
+            pnt[index++] = 0;
+        }
+      break;
+
+    case AF_INET6:
+      index = rprefix->length / 8;
+      if (index < 16)
+        {
+          pnt = (uint8_t *) & rprefix->prefix.addr.v6;
+          offset = rprefix->length % 8;
+          pnt[index] &= maskbit[offset];
+          index++;
+          while (index < 16)
+            pnt[index++] = 0;
+        }
+      break;
+
+    default:
+      assert (0);
+    }
+}
+
+/*
+ * translate a quagga prefix into a rfapi IP address. The
+ * prefix is REQUIRED to be 32 bits for IPv4 and 128 bits for IPv6
+ *
+ * RETURNS:
+ *
+ *     0       Success
+ *     <0      Error
+ */
+int
+rfapiQprefix2Raddr (struct prefix *qprefix, struct rfapi_ip_addr *raddr)
+{
+  memset (raddr, 0, sizeof (struct rfapi_ip_addr));
+  raddr->addr_family = qprefix->family;
+  switch (qprefix->family)
+    {
+    case AF_INET:
+      if (qprefix->prefixlen != 32)
+        return -1;
+      raddr->addr.v4 = qprefix->u.prefix4;
+      break;
+    case AF_INET6:
+      if (qprefix->prefixlen != 128)
+        return -1;
+      raddr->addr.v6 = qprefix->u.prefix6;
+      break;
+    default:
+      return -1;
+    }
+  return 0;
+}
+
+/* 
+ * Translate Quagga prefix to RFAPI prefix
+ */
+/* rprefix->cost set to 0 */
+void
+rfapiQprefix2Rprefix (struct prefix *qprefix, struct rfapi_ip_prefix *rprefix)
+{
+  memset (rprefix, 0, sizeof (struct rfapi_ip_prefix));
+  rprefix->length = qprefix->prefixlen;
+  rprefix->prefix.addr_family = qprefix->family;
+  switch (qprefix->family)
+    {
+    case AF_INET:
+      rprefix->prefix.addr.v4 = qprefix->u.prefix4;
+      break;
+    case AF_INET6:
+      rprefix->prefix.addr.v6 = qprefix->u.prefix6;
+      break;
+    default:
+      assert (0);
+    }
+}
+
+int
+rfapiRprefix2Qprefix (struct rfapi_ip_prefix *rprefix, struct prefix *qprefix)
+{
+  memset (qprefix, 0, sizeof (struct prefix));
+  qprefix->prefixlen = rprefix->length;
+  qprefix->family = rprefix->prefix.addr_family;
+
+  switch (rprefix->prefix.addr_family)
+    {
+    case AF_INET:
+      qprefix->u.prefix4 = rprefix->prefix.addr.v4;
+      break;
+    case AF_INET6:
+      qprefix->u.prefix6 = rprefix->prefix.addr.v6;
+      break;
+    default:
+      return EAFNOSUPPORT;
+    }
+  return 0;
+}
+
+/*
+ * returns 1 if prefixes have same addr family, prefix len, and address
+ * Note that host bits matter in this comparison!
+ *
+ * For paralellism with quagga/lib/prefix.c. if we need a comparison
+ * where host bits are ignored, call that function rfapiRprefixCmp.
+ */
+int
+rfapiRprefixSame (struct rfapi_ip_prefix *hp1, struct rfapi_ip_prefix *hp2)
+{
+  if (hp1->prefix.addr_family != hp2->prefix.addr_family)
+    return 0;
+  if (hp1->length != hp2->length)
+    return 0;
+  if (hp1->prefix.addr_family == AF_INET)
+    if (IPV4_ADDR_SAME (&hp1->prefix.addr.v4, &hp2->prefix.addr.v4))
+      return 1;
+  if (hp1->prefix.addr_family == AF_INET6)
+    if (IPV6_ADDR_SAME (&hp1->prefix.addr.v6, &hp2->prefix.addr.v6))
+      return 1;
+  return 0;
+}
+
+int
+rfapiRaddr2Qprefix (struct rfapi_ip_addr *hia, struct prefix *pfx)
+{
+  memset (pfx, 0, sizeof (struct prefix));
+  pfx->family = hia->addr_family;
+
+  switch (hia->addr_family)
+    {
+    case AF_INET:
+      pfx->prefixlen = 32;
+      pfx->u.prefix4 = hia->addr.v4;
+      break;
+    case AF_INET6:
+      pfx->prefixlen = 128;
+      pfx->u.prefix6 = hia->addr.v6;
+      break;
+    default:
+      return EAFNOSUPPORT;
+    }
+  return 0;
+}
+
+void
+rfapiL2o2Qprefix (struct rfapi_l2address_option *l2o, struct prefix *pfx)
+{
+  memset (pfx, 0, sizeof (struct prefix));
+  pfx->family = AF_ETHERNET;
+  pfx->prefixlen = 48;
+  pfx->u.prefix_eth = l2o->macaddr;
+}
+
+char *
+rfapiEthAddr2Str (const struct ethaddr *ea, char *buf, int bufsize)
+{
+  int i;
+  char *p = buf;
+
+  assert (bufsize > (3 * ETHER_ADDR_LEN));
+
+  for (i = 0; i <= ETHER_ADDR_LEN; ++i)
+    {
+      sprintf (p, "%02x", ea->octet[i]);
+      if (i < (ETHER_ADDR_LEN - 1))
+        *(p + 2) = ':';
+      p += 3;
+    }
+  return buf;
+}
+
+int
+rfapiStr2EthAddr (const char *str, struct ethaddr *ea)
+{
+  unsigned int a[6];
+  int i;
+
+  if (sscanf (str, "%2x:%2x:%2x:%2x:%2x:%2x",
+              a + 0, a + 1, a + 2, a + 3, a + 4, a + 5) != 6)
+    {
+
+      return EINVAL;
+    }
+
+  for (i = 0; i < 6; ++i)
+    ea->octet[i] = a[i] & 0xff;
+
+  return 0;
+}
+
+const char *
+rfapi_ntop (int af, const void *src, char *buf, socklen_t size)
+{
+  if (af == AF_ETHERNET)
+    {
+      return rfapiEthAddr2Str ((const struct ethaddr *) src, buf, size);
+    }
+
+  return inet_ntop (af, src, buf, size);
+}
+
+int
+rfapiDebugPrintf (void *dummy, const char *format, ...)
+{
+  va_list args;
+  va_start (args, format);
+  vzlog (NULL, LOG_DEBUG, format, args);
+  va_end (args);
+  return 0;
+}
+
+static int
+rfapiStdioPrintf (void *stream, const char *format, ...)
+{
+  FILE *file = NULL;
+
+  va_list args;
+  va_start (args, format);
+
+  switch ((uintptr_t) stream)
+    {
+    case 1:
+      file = stdout;
+      break;
+    case 2:
+      file = stderr;
+      break;
+    default:
+      assert (0);
+    }
+
+  vfprintf (file, format, args);
+  va_end (args);
+  return 0;
+}
+
+/* Fake out for debug logging */
+static struct vty vty_dummy_zlog;
+static struct vty vty_dummy_stdio;
+#define HVTY_NEWLINE ((vty == &vty_dummy_zlog)? "": VTY_NEWLINE)
+
+static const char *
+str_vty_newline (struct vty *vty)
+{
+  if (vty == &vty_dummy_zlog)
+    return "";
+  return VTY_NEWLINE;
+}
+
+int
+rfapiStream2Vty (
+  void *stream,                                        /* input */
+  int          (**fp) (void *, const char *, ...),     /* output */
+  struct vty   **vty,                                  /* output */
+  void **outstream,                                    /* output */
+  const char   **vty_newline)                          /* output */
+{
+
+  if (!stream)
+    {
+      vty_dummy_zlog.type = VTY_SHELL;  /* for VTY_NEWLINE */
+      *vty = &vty_dummy_zlog;
+      *fp = (int (*)(void *, const char *,...)) rfapiDebugPrintf;
+      *outstream = NULL;
+      *vty_newline = str_vty_newline (*vty);
+      return (vzlog_test (NULL, LOG_DEBUG));
+    }
+
+  if (((uintptr_t) stream == (uintptr_t) 1) ||
+      ((uintptr_t) stream == (uintptr_t) 2))
+    {
+
+      vty_dummy_stdio.type = VTY_SHELL; /* for VTY_NEWLINE */
+      *vty = &vty_dummy_stdio;
+      *fp = (int (*)(void *, const char *,...)) rfapiStdioPrintf;
+      *outstream = stream;
+      *vty_newline = str_vty_newline (*vty);
+      return 1;
+    }
+
+  if (stream)
+    {
+      *vty = stream;            /* VTY_NEWLINE requires vty to be legit */
+      *fp = (int (*)(void *, const char *,...)) vty_out;
+      *outstream = stream;
+      *vty_newline = str_vty_newline (*vty);
+      return 1;
+    }
+
+  return 0;
+}
+
+/* called from bgpd/bgp_vty.c'route_vty_out() */
+void
+rfapi_vty_out_vncinfo (
+  struct vty           *vty,
+  struct prefix                *p,
+  struct bgp_info      *bi,
+  safi_t               safi)
+{
+  char *s;
+  uint32_t lifetime;
+
+  /*
+   * Print, on an indented line:
+   *  UN address [if VPN route and VNC UN addr subtlv]
+   *  EC list
+   *  VNC lifetime
+   */
+  vty_out (vty, "    ");
+
+  if (safi == SAFI_MPLS_VPN)
+    {
+      struct prefix pfx_un;
+
+      if (!rfapiGetVncTunnelUnAddr (bi->attr, &pfx_un))
+        {
+          char buf[BUFSIZ];
+          vty_out (vty, "UN=%s", inet_ntop (pfx_un.family,
+                                            pfx_un.u.val, buf, BUFSIZ));
+        }
+    }
+
+  if (bi->attr && bi->attr->extra && bi->attr->extra->ecommunity)
+    {
+      s = ecommunity_ecom2str (bi->attr->extra->ecommunity,
+                               ECOMMUNITY_FORMAT_ROUTE_MAP);
+      vty_out (vty, " EC{%s}", s);
+      XFREE (MTYPE_ECOMMUNITY_STR, s);
+    }
+
+  if (bi->extra != NULL && bi->extra->tag != NULL)
+    vty_out (vty, " label=%u", decode_label (bi->extra->tag));
+
+  if (rfapiGetVncLifetime (bi->attr, &lifetime))
+    {
+      if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP)
+        {
+          vty_out (vty, " life=none");
+        }
+    }
+  else
+    {
+      vty_out (vty, " life=%d", lifetime);
+    }
+
+  vty_out (vty, " type=%s, subtype=%d",
+           zebra_route_string (bi->type), bi->sub_type);
+
+  vty_out (vty, "%s", HVTY_NEWLINE);
+}
+
+void
+rfapiPrintAttrPtrs (void *stream, struct attr *attr)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  struct attr_extra *ae;
+  char buf[BUFSIZ];
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  fp (out, "Attr[%p]:%s", attr, HVTY_NEWLINE);
+  if (!attr)
+    return;
+
+  /* IPv4 Nexthop */
+  inet_ntop (AF_INET, &attr->nexthop, buf, BUFSIZ);
+  fp (out, "  nexthop=%s%s", buf, HVTY_NEWLINE);
+
+  fp (out, "  aspath=%p, refcnt=%d%s", attr->aspath,
+      (attr->aspath ? attr->aspath->refcnt : 0), HVTY_NEWLINE);
+  fp (out, "  community=%p, refcnt=%d%s", attr->community,
+      (attr->community ? attr->community->refcnt : 0), HVTY_NEWLINE);
+
+  if ((ae = attr->extra))
+    {
+      fp (out, "  ecommunity=%p, refcnt=%d%s", ae->ecommunity,
+          (ae->ecommunity ? ae->ecommunity->refcnt : 0), HVTY_NEWLINE);
+      fp (out, "  cluster=%p, refcnt=%d%s", ae->cluster,
+          (ae->cluster ? ae->cluster->refcnt : 0), HVTY_NEWLINE);
+      fp (out, "  transit=%p, refcnt=%d%s", ae->transit,
+          (ae->transit ? ae->transit->refcnt : 0), HVTY_NEWLINE);
+    }
+}
+
+/*
+ * Print BI in an Import Table
+ */
+void
+rfapiPrintBi (void *stream, struct bgp_info *bi)
+{
+  char buf[BUFSIZ];
+  char *s;
+
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  char line[BUFSIZ];
+  char *p = line;
+  int r;
+  int has_macaddr = 0;
+  struct ethaddr macaddr;
+  struct rfapi_l2address_option l2o_buf;
+  uint8_t l2hid;                /* valid if has_macaddr */
+
+#define REMAIN (BUFSIZ - (p-line))
+#define INCP {p += (r > REMAIN)? REMAIN: r;}
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  if (!bi)
+    return;
+
+  if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) && bi->extra
+      && bi->extra->vnc.import.timer)
+    {
+      struct thread *t = (struct thread *) bi->extra->vnc.import.timer;
+      r = snprintf (p, REMAIN, " [%4lu] ", thread_timer_remain_second (t));
+      INCP;
+
+    }
+  else
+    {
+      r = snprintf (p, REMAIN, "        ");
+      INCP;
+    }
+
+  if (bi->extra)
+    {
+      /* TBD This valid only for SAFI_MPLS_VPN, but not for encap */
+      if (decode_rd_type(bi->extra->vnc.import.rd.val) == RD_TYPE_VNC_ETH)
+        {
+          has_macaddr = 1;
+          memcpy (macaddr.octet, bi->extra->vnc.import.rd.val + 2, 6);
+          l2hid = bi->extra->vnc.import.rd.val[1];
+        }
+    }
+
+  /*
+   * Print these items:
+   *          type/subtype
+   *          nexthop address
+   *          lifetime
+   *          RFP option sizes (they are opaque values)
+   *          extended communities (RTs)
+   */
+  if (bi->attr && bi->attr->extra)
+    {
+      uint32_t lifetime;
+      int printed_1st_gol = 0;
+      struct bgp_attr_encap_subtlv *pEncap;
+      struct prefix pfx_un;
+      int af = BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len);
+
+      /* Nexthop */
+      if (af == AF_INET)
+        {
+          r = snprintf (p, REMAIN, "%s", inet_ntop (AF_INET,
+                                                    &bi->attr->extra->mp_nexthop_global_in,
+                                                    buf, BUFSIZ));
+          INCP;
+        }
+      else if (af == AF_INET6)
+        {
+          r = snprintf (p, REMAIN, "%s", inet_ntop (AF_INET6,
+                                                    &bi->attr->extra->mp_nexthop_global,
+                                                    buf, BUFSIZ));
+          INCP;
+        }
+      else
+        {
+          r = snprintf (p, REMAIN, "?");
+          INCP;
+        }
+
+      /*
+       * VNC tunnel subtlv, if present, contains UN address
+       */
+      if (!rfapiGetVncTunnelUnAddr (bi->attr, &pfx_un))
+        {
+          r = snprintf (p, REMAIN, " un=%s", inet_ntop (pfx_un.family,
+                                                        pfx_un.u.val, buf,
+                                                        BUFSIZ));
+          INCP;
+
+        }
+
+      /* Lifetime */
+      if (rfapiGetVncLifetime (bi->attr, &lifetime))
+        {
+          r = snprintf (p, REMAIN, " nolife");
+          INCP;
+        }
+      else
+        {
+          if (lifetime == 0xffffffff)
+            r = snprintf (p, REMAIN, " %6s", "infini");
+          else
+            r = snprintf (p, REMAIN, " %6u", lifetime);
+          INCP;
+        }
+
+      /* RFP option lengths */
+      for (pEncap = bi->attr->extra->vnc_subtlvs; pEncap;
+           pEncap = pEncap->next)
+        {
+
+          if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION)
+            {
+              if (printed_1st_gol)
+                {
+                  r = snprintf (p, REMAIN, ",");
+                  INCP;
+                }
+              else
+                {
+                  r = snprintf (p, REMAIN, " ");        /* leading space */
+                  INCP;
+                }
+              r = snprintf (p, REMAIN, "%d", pEncap->length);
+              INCP;
+              printed_1st_gol = 1;
+            }
+        }
+
+      /* RT list */
+      if (bi->attr->extra->ecommunity)
+        {
+          s = ecommunity_ecom2str (bi->attr->extra->ecommunity,
+                                   ECOMMUNITY_FORMAT_ROUTE_MAP);
+          r = snprintf (p, REMAIN, " %s", s);
+          INCP;
+          XFREE (MTYPE_ECOMMUNITY_STR, s);
+        }
+
+    }
+
+  r = snprintf (p, REMAIN, " bi@%p", bi);
+  INCP;
+
+  r = snprintf (p, REMAIN, " p@%p", bi->peer);
+  INCP;
+
+  if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+    {
+      r = snprintf (p, REMAIN, " HD=yes");
+      INCP;
+    }
+  else
+    {
+      r = snprintf (p, REMAIN, " HD=no");
+      INCP;
+    }
+
+  if (bi->attr)
+    {
+
+      if (bi->attr->extra)
+        {
+          r = snprintf (p, REMAIN, " W=%d", bi->attr->extra->weight);
+          INCP;
+        }
+
+      if (bi->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+        {
+          r = snprintf (p, REMAIN, " LP=%d", bi->attr->local_pref);
+          INCP;
+        }
+      else
+        {
+          r = snprintf (p, REMAIN, " LP=unset");
+          INCP;
+        }
+    }
+
+  r =
+    snprintf (p, REMAIN, " %c:%u", zebra_route_char (bi->type), bi->sub_type);
+  INCP;
+
+  fp (out, "%s%s", line, HVTY_NEWLINE);
+
+  if (has_macaddr)
+    {
+      fp (out, "        RD HID=%d ETH=%02x:%02x:%02x:%02x:%02x:%02x%s",
+          l2hid,
+          macaddr.octet[0],
+          macaddr.octet[1],
+          macaddr.octet[2],
+          macaddr.octet[3], macaddr.octet[4], macaddr.octet[5], HVTY_NEWLINE);
+    }
+
+  if (!rfapiGetL2o (bi->attr, &l2o_buf))
+    {
+      fp (out,
+          "        L2O ETH=%02x:%02x:%02x:%02x:%02x:%02x LBL=%d LNI=%d LHI=%hhu%s",
+          l2o_buf.macaddr.octet[0], l2o_buf.macaddr.octet[1],
+          l2o_buf.macaddr.octet[2], l2o_buf.macaddr.octet[3],
+          l2o_buf.macaddr.octet[4], l2o_buf.macaddr.octet[5], l2o_buf.label,
+          l2o_buf.logical_net_id, l2o_buf.local_nve_id, HVTY_NEWLINE);
+    }
+  if (bi->extra && bi->extra->vnc.import.aux_prefix.family)
+    {
+      char buf[BUFSIZ];
+      const char *sp;
+
+      sp = rfapi_ntop (bi->extra->vnc.import.aux_prefix.family,
+                       &bi->extra->vnc.import.aux_prefix.u.prefix,
+                       buf, BUFSIZ);
+      buf[BUFSIZ - 1] = 0;
+      if (sp)
+        {
+          fp (out, "        IP: %s%s", sp, HVTY_NEWLINE);
+        }
+    }
+  {
+    struct rfapi_un_option *uo = rfapi_encap_tlv_to_un_option (bi->attr);
+    if (uo)
+      {
+        rfapi_print_tunneltype_option (stream, 8, &uo->v.tunnel);
+        rfapi_un_options_free (uo);
+      }
+  }
+}
+
+char *
+rfapiMonitorVpn2Str (struct rfapi_monitor_vpn *m, char *buf, int size)
+{
+  char buf_pfx[BUFSIZ];
+  char buf_vn[BUFSIZ];
+  char buf_un[BUFSIZ];
+  int rc;
+
+  rfapiRfapiIpAddr2Str (&m->rfd->un_addr, buf_vn, BUFSIZ);
+  rfapiRfapiIpAddr2Str (&m->rfd->vn_addr, buf_un, BUFSIZ);
+
+  rc = snprintf (buf, size,
+                 "m=%p, next=%p, rfd=%p(vn=%s un=%s), p=%s/%d, node=%p",
+                 m, m->next, m->rfd, buf_vn, buf_un,
+                 inet_ntop (m->p.family, &m->p.u.prefix, buf_pfx, BUFSIZ),
+                 m->p.prefixlen, m->node);
+  buf[size - 1] = 0;
+  if (rc >= size)
+    return NULL;
+  return buf;
+}
+
+static void
+rfapiDebugPrintMonitorVpn (void *stream, struct rfapi_monitor_vpn *m)
+{
+  char buf[BUFSIZ];
+
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  rfapiMonitorVpn2Str (m, buf, BUFSIZ);
+  fp (out, "    Mon %s%s", buf, HVTY_NEWLINE);
+}
+
+static void
+rfapiDebugPrintMonitorEncap (void *stream, struct rfapi_monitor_encap *m)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out = NULL;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  fp (out, "    Mon m=%p, next=%p, node=%p, bi=%p%s",
+      m, m->next, m->node, m->bi, HVTY_NEWLINE);
+}
+
+void
+rfapiShowItNode (void *stream, struct route_node *rn)
+{
+  struct bgp_info *bi;
+  char buf[BUFSIZ];
+
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  fp (out, "%s/%d @%p #%d%s",
+      rfapi_ntop (rn->p.family, &rn->p.u.prefix, buf, BUFSIZ),
+      rn->p.prefixlen, rn, rn->lock, HVTY_NEWLINE);
+
+  for (bi = rn->info; bi; bi = bi->next)
+    {
+      rfapiPrintBi (stream, bi);
+    }
+
+  /* doesn't show montors */
+}
+
+void
+rfapiShowImportTable (
+  void                 *stream,
+  const char           *label,
+  struct route_table   *rt,
+  int                  isvpn)
+{
+  struct route_node *rn;
+  char buf[BUFSIZ];
+
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  fp (out, "Import Table [%s]%s", label, HVTY_NEWLINE);
+
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    {
+      struct bgp_info *bi;
+
+      if (rn->p.family == AF_ETHERNET)
+        {
+          rfapiEthAddr2Str (&rn->p.u.prefix_eth, buf, BUFSIZ);
+        }
+      else
+        {
+          inet_ntop (rn->p.family, &rn->p.u.prefix, buf, BUFSIZ);
+        }
+
+      fp (out, "%s/%d @%p #%d%s", buf, rn->p.prefixlen, rn, rn->lock - 1,       /* account for loop iterator locking */
+          HVTY_NEWLINE);
+
+      for (bi = rn->info; bi; bi = bi->next)
+        {
+          rfapiPrintBi (stream, bi);
+        }
+
+      if (isvpn)
+        {
+          struct rfapi_monitor_vpn *m;
+          for (m = RFAPI_MONITOR_VPN (rn); m; m = m->next)
+            {
+              rfapiDebugPrintMonitorVpn (stream, m);
+            }
+        }
+      else
+        {
+          struct rfapi_monitor_encap *m;
+          for (m = RFAPI_MONITOR_ENCAP (rn); m; m = m->next)
+            {
+              rfapiDebugPrintMonitorEncap (stream, m);
+            }
+        }
+    }
+}
+
+int
+rfapiShowVncQueries (void *stream, struct prefix *pfx_match)
+{
+  struct bgp *bgp;
+  struct rfapi *h;
+  struct listnode *node;
+  struct rfapi_descriptor *rfd;
+
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  int printedheader = 0;
+
+  int nves_total = 0;
+  int nves_with_queries = 0;
+  int nves_displayed = 0;
+
+  int queries_total = 0;
+  int queries_displayed = 0;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return CMD_WARNING;
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+  if (!bgp)
+    {
+      vty_out (vty, "No BGP instance%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  h = bgp->rfapi;
+  if (!h)
+    {
+      vty_out (vty, "No RFAPI instance%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd))
+    {
+
+      struct route_node *rn;
+      int printedquerier = 0;
+
+
+      ++nves_total;
+
+      if (rfd->mon || (rfd->mon_eth && skiplist_count (rfd->mon_eth)))
+        {
+          ++nves_with_queries;
+        }
+      else
+        {
+          continue;
+        }
+
+      /*
+       * IP Queries
+       */
+      if (rfd->mon)
+        {
+          for (rn = route_top (rfd->mon); rn; rn = route_next (rn))
+            {
+              struct rfapi_monitor_vpn *m;
+              char buf_remain[BUFSIZ];
+              char buf_pfx[BUFSIZ];
+
+              if (!rn->info)
+                continue;
+
+              m = rn->info;
+
+              ++queries_total;
+
+              if (pfx_match && !prefix_match (pfx_match, &rn->p) &&
+                  !prefix_match (&rn->p, pfx_match))
+                continue;
+
+              ++queries_displayed;
+
+              if (!printedheader)
+                {
+                  ++printedheader;
+                  fp (out, "%s", VTY_NEWLINE);
+                  fp (out, "%-15s %-15s %-15s %-10s%s",
+                      "VN Address", "UN Address",
+                      "Target", "Remaining", VTY_NEWLINE);
+                }
+
+              if (!printedquerier)
+                {
+                  char buf_vn[BUFSIZ];
+                  char buf_un[BUFSIZ];
+
+                  rfapiRfapiIpAddr2Str (&rfd->un_addr, buf_un, BUFSIZ);
+                  rfapiRfapiIpAddr2Str (&rfd->vn_addr, buf_vn, BUFSIZ);
+
+                  fp (out, "%-15s %-15s", buf_vn, buf_un);
+                  printedquerier = 1;
+
+                  ++nves_displayed;
+                }
+              else
+                fp (out, "%-15s %-15s", "", "");
+              buf_remain[0] = 0;
+              if (m->timer)
+                {
+                  rfapiFormatSeconds (thread_timer_remain_second (m->timer),
+                                      buf_remain, BUFSIZ);
+                }
+              fp (out, " %-15s %-10s%s",
+                  inet_ntop (m->p.family, &m->p.u.prefix, buf_pfx, BUFSIZ),
+                  buf_remain, VTY_NEWLINE);
+            }
+        }
+
+      /*
+       * Ethernet Queries
+       */
+      if (rfd->mon_eth && skiplist_count (rfd->mon_eth))
+        {
+
+          int rc;
+          void *cursor;
+          struct rfapi_monitor_eth *mon_eth;
+
+          for (cursor = NULL,
+               rc =
+               skiplist_next (rfd->mon_eth, NULL, (void **) &mon_eth,
+                              &cursor); rc == 0;
+               rc =
+               skiplist_next (rfd->mon_eth, NULL, (void **) &mon_eth,
+                              &cursor))
+            {
+
+              char buf_remain[BUFSIZ];
+              char buf_pfx[BUFSIZ];
+              struct prefix pfx_mac;
+
+              ++queries_total;
+
+              zlog_debug ("%s: checking rfd=%p mon_eth=%p", __func__, rfd,
+                          mon_eth);
+
+              memset ((void *) &pfx_mac, 0, sizeof (struct prefix));
+              pfx_mac.family = AF_ETHERNET;
+              pfx_mac.prefixlen = 48;
+              pfx_mac.u.prefix_eth = mon_eth->macaddr;
+
+              if (pfx_match && !prefix_match (pfx_match, &pfx_mac) &&
+                  !prefix_match (&pfx_mac, pfx_match))
+                continue;
+
+              ++queries_displayed;
+
+              if (!printedheader)
+                {
+                  ++printedheader;
+                  fp (out, "%s", VTY_NEWLINE);
+                  fp (out, "%-15s %-15s %-17s %10s %-10s%s",
+                      "VN Address", "UN Address",
+                      "Target", "LNI", "Remaining", VTY_NEWLINE);
+                }
+
+              if (!printedquerier)
+                {
+                  char buf_vn[BUFSIZ];
+                  char buf_un[BUFSIZ];
+
+                  rfapiRfapiIpAddr2Str (&rfd->un_addr, buf_un, BUFSIZ);
+                  rfapiRfapiIpAddr2Str (&rfd->vn_addr, buf_vn, BUFSIZ);
+
+                  fp (out, "%-15s %-15s", buf_vn, buf_un);
+                  printedquerier = 1;
+
+                  ++nves_displayed;
+                }
+              else
+                fp (out, "%-15s %-15s", "", "");
+              buf_remain[0] = 0;
+              if (mon_eth->timer)
+                {
+                  rfapiFormatSeconds (thread_timer_remain_second
+                                      (mon_eth->timer), buf_remain, BUFSIZ);
+                }
+              fp (out, " %-17s %10d %-10s%s",
+                  rfapi_ntop (pfx_mac.family, &pfx_mac.u.prefix, buf_pfx,
+                              BUFSIZ), mon_eth->logical_net_id, buf_remain,
+                  VTY_NEWLINE);
+            }
+        }
+    }
+
+  if (queries_total)
+    {
+      fp (out, "%s", VTY_NEWLINE);
+      fp (out, "Displayed %d out of %d total queries%s",
+          queries_displayed, queries_total, VTY_NEWLINE);
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+rfapiPrintRemoteRegBi (
+  struct bgp           *bgp,
+  void                 *stream,
+  struct route_node    *rn,
+  struct bgp_info      *bi)
+{
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+  uint32_t factor;
+
+  struct prefix pfx_un;
+  struct prefix pfx_vn;
+  uint8_t cost;
+  uint32_t lifetime;
+  bgp_encap_types tun_type;
+
+  char buf_pfx[BUFSIZ];
+  char buf_ntop[BUFSIZ];
+  char buf_un[BUFSIZ];
+  char buf_vn[BUFSIZ];
+  char buf_lifetime[BUFSIZ];
+  int nlines = 0;
+
+  if (bgp && bgp->rfapi_cfg)
+    factor = bgp->rfapi_cfg->rfp_cfg.holddown_factor;
+  else
+    factor = RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR;
+
+
+  if (!stream)
+    return 0;                   /* for debug log, print into buf & call output once */
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return 0;
+
+  /*
+   * Prefix
+   */
+  buf_pfx[0] = 0;
+  snprintf (buf_pfx, BUFSIZ, "%s/%d",
+            rfapi_ntop (rn->p.family, &rn->p.u.prefix, buf_ntop, BUFSIZ),
+            rn->p.prefixlen);
+  buf_pfx[BUFSIZ - 1] = 0;
+  nlines++;
+
+  /*
+   * UN addr
+   */
+  buf_un[0] = 0;
+  if (!rfapiGetUnAddrOfVpnBi (bi, &pfx_un))
+    {
+      snprintf (buf_un, BUFSIZ, "%s",
+                inet_ntop (pfx_un.family, &pfx_un.u.prefix, buf_ntop,
+                           BUFSIZ));
+    }
+  buf_un[BUFSIZ - 1] = 0;
+
+  rfapiGetTunnelType(bi->attr,&tun_type);
+  /*
+   * VN addr
+   */
+  buf_vn[0] = 0;
+  if (tun_type == BGP_ENCAP_TYPE_MPLS)
+    {
+      /* MPLS carries un in nrli next hop (same as vn for IP tunnels) */
+      if (bi->extra)
+        {
+          u_int32_t l = decode_label (bi->extra->tag);
+          snprintf (buf_vn, BUFSIZ, "Label: %d", l);
+        }
+      else                      /* should never happen */
+        {
+          snprintf (buf_vn, BUFSIZ, "Label: N/A");
+        }
+    }
+  else
+    {
+      rfapiNexthop2Prefix (bi->attr, &pfx_vn);
+      snprintf (buf_vn, BUFSIZ, "%s",
+                inet_ntop (pfx_vn.family, &pfx_vn.u.prefix, buf_ntop, BUFSIZ));
+    }
+  buf_vn[BUFSIZ - 1] = 0;
+
+  
+  /*
+   * Cost is encoded in local_pref as (255-cost)
+   * See rfapi_import.c'rfapiRouteInfo2NextHopEntry() for conversion
+   * back to cost.
+   */
+  if (bi->attr)
+    {
+      uint32_t local_pref;
+      if (bi->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+        local_pref = bi->attr->local_pref;
+      else
+        local_pref = 0;
+      cost = (local_pref > 255) ? 0 : 255 - local_pref;
+    }
+  else
+    {
+      cost = 0;
+    }
+
+  fp (out, "%-20s ", buf_pfx);
+  fp (out, "%-15s ", buf_vn);
+  fp (out, "%-15s ", buf_un);
+  fp (out, "%-4d ", cost);
+
+  /* Lifetime */
+  /* NB rfapiGetVncLifetime sets infinite value when returning !0 */
+  if (rfapiGetVncLifetime (bi->attr, &lifetime) ||
+      (lifetime == RFAPI_INFINITE_LIFETIME))
+    {
+
+      fp (out, "%-10s ", "infinite");
+    }
+  else
+    {
+      time_t t_lifetime = lifetime;
+      rfapiFormatSeconds (t_lifetime, buf_lifetime, BUFSIZ);
+      fp (out, "%-10s ", buf_lifetime);
+    }
+
+  if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED) &&
+      bi->extra && bi->extra->vnc.import.timer)
+    {
+
+      uint32_t remaining;
+      time_t age;
+      char buf_age[BUFSIZ];
+
+      struct thread *t = (struct thread *) bi->extra->vnc.import.timer;
+      remaining = thread_timer_remain_second (t);
+
+#if RFAPI_REGISTRATIONS_REPORT_AGE
+      /*
+       * Calculate when the timer started. Doing so here saves
+       * us a timestamp field in "struct bgp_info".
+       *
+       * See rfapi_import.c'rfapiBiStartWithdrawTimer() for the
+       * original calculation.
+       */
+      age = rfapiGetHolddownFromLifetime (lifetime, factor) - remaining;
+#else /* report remaining time */
+      age = remaining;
+#endif
+      rfapiFormatSeconds (age, buf_age, BUFSIZ);
+
+      fp (out, "%-10s ", buf_age);
+
+    }
+  else if (RFAPI_LOCAL_BI (bi))
+    {
+
+      char buf_age[BUFSIZ];
+
+      if (bi && bi->extra && bi->extra->vnc.import.create_time)
+        {
+          rfapiFormatAge (bi->extra->vnc.import.create_time, buf_age, BUFSIZ);
+        }
+      else
+        {
+          buf_age[0] = '?';
+          buf_age[1] = 0;
+        }
+      fp (out, "%-10s ", buf_age);
+    }
+  fp (out, "%s", HVTY_NEWLINE);
+
+  if (rn->p.family == AF_ETHERNET)
+    {
+      /*
+       * If there is a corresponding IP address && != VN address, 
+       * print that on the next line
+       */
+
+      if (bi && bi->extra && bi->extra->vnc.import.aux_prefix.family)
+        {
+          const char *sp;
+
+          sp = rfapi_ntop (bi->extra->vnc.import.aux_prefix.family,
+                           &bi->extra->vnc.import.aux_prefix.u.prefix,
+                           buf_ntop, BUFSIZ);
+          buf_ntop[BUFSIZ - 1] = 0;
+
+          if (sp && strcmp (buf_vn, sp) != 0)
+            {
+              fp (out, "  IP: %s", sp);
+              if (nlines == 1)
+                nlines++;
+            }
+        }
+    }
+  if (tun_type != BGP_ENCAP_TYPE_MPLS && bi->extra)
+    {
+      u_int32_t l = decode_label (bi->extra->tag);
+      if (!MPLS_LABEL_IS_NULL (l))
+        {
+          fp (out, "  Label: %d", l);
+          if (nlines == 1)
+            nlines++;
+        }
+    }
+  if (nlines > 1)
+    fp (out, "%s", HVTY_NEWLINE);
+
+  return 1;
+}
+
+static int
+rfapiShowRemoteRegistrationsIt (
+  struct bgp                   *bgp,
+  void                         *stream,
+  struct rfapi_import_table    *it,
+  struct prefix                        *prefix_only,
+  int                          show_expiring,  /* either/or */
+  int                          show_local,
+  int                          show_remote,
+  int                          show_imported,  /* either/or */
+  uint32_t                     *pLni)          /* AFI_ETHER only */
+{
+  afi_t afi;
+  int printed_rtlist_hdr = 0;
+
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+  int total = 0;
+  int printed = 0;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return printed;
+
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+
+      struct route_node *rn;
+
+      if (!it->imported_vpn[afi])
+        continue;
+
+      for (rn = route_top (it->imported_vpn[afi]); rn; rn = route_next (rn))
+        {
+
+          struct bgp_info *bi;
+          int count_only;
+
+          /* allow for wider or more narrow mask from user */
+          if (prefix_only &&
+              !prefix_match (prefix_only, &rn->p) &&
+              !prefix_match (&rn->p, prefix_only))
+            count_only = 1;
+          else
+            count_only = 0;
+
+          for (bi = rn->info; bi; bi = bi->next)
+            {
+
+              if (!show_local && RFAPI_LOCAL_BI (bi))
+                {
+
+                  /* local route from RFP */
+                  continue;
+                }
+
+              if (!show_remote && !RFAPI_LOCAL_BI (bi))
+                {
+
+                  /* remote route */
+                  continue;
+                }
+
+              if (show_expiring && !CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+                continue;
+
+              if (!show_expiring && CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+                continue;
+
+              if (bi->type == ZEBRA_ROUTE_BGP_DIRECT ||
+                  bi->type == ZEBRA_ROUTE_BGP_DIRECT_EXT)
+                {
+                  if (!show_imported)
+                    continue;
+                }
+              else
+                {
+                  if (show_imported)
+                    continue;
+                }
+
+              total++;
+              if (count_only == 1)
+                continue;
+              if (!printed_rtlist_hdr)
+                {
+                  const char *agetype = "";
+                  char *s;
+                  const char *type = "";
+                  if (show_imported)
+                    {
+                      type = "Imported";
+                    }
+                  else
+                    {
+                      if (show_expiring)
+                        {
+                          type = "Holddown";
+                        }
+                      else
+                        {
+                          if (RFAPI_LOCAL_BI (bi))
+                            {
+                              type = "Local";
+                            }
+                          else
+                            {
+                              type = "Remote";
+                            }
+                        }
+                    }
+
+                  s = ecommunity_ecom2str (it->rt_import_list,
+                                           ECOMMUNITY_FORMAT_ROUTE_MAP);
+
+                  if (pLni)
+                    {
+                      fp (out, "%s[%s] L2VPN Network 0x%x (%u) RT={%s}%s",
+                          HVTY_NEWLINE, type, *pLni, (*pLni & 0xfff), s,
+                          HVTY_NEWLINE);
+                    }
+                  else
+                    {
+                      fp (out, "%s[%s] Prefix RT={%s}%s",
+                          HVTY_NEWLINE, type, s, HVTY_NEWLINE);
+                    }
+                  XFREE (MTYPE_ECOMMUNITY_STR, s);
+
+                  if (show_expiring)
+                    {
+#if RFAPI_REGISTRATIONS_REPORT_AGE
+                      agetype = "Age";
+#else
+                      agetype = "Remaining";
+#endif
+                    }
+                  else if (show_local)
+                    {
+                      agetype = "Age";
+                    }
+
+                  printed_rtlist_hdr = 1;
+
+                  fp (out, "%-20s %-15s %-15s %4s %-10s %-10s%s",
+                      (pLni ? "L2 Address/IP" : "Prefix"),
+                      "VN Address", "UN Address", "Cost",
+                      "Lifetime", agetype, HVTY_NEWLINE);
+                }
+              printed += rfapiPrintRemoteRegBi (bgp, stream, rn, bi);
+            }
+        }
+    }
+
+  if (printed > 0)
+    {
+
+      const char *type = "prefixes";
+
+      if (show_imported)
+        {
+          type = "imported prefixes";
+        }
+      else
+        {
+          if (show_expiring)
+            {
+              type = "prefixes in holddown";
+            }
+          else
+            {
+              if (show_local && !show_remote)
+                {
+                  type = "locally registered prefixes";
+                }
+              else if (!show_local && show_remote)
+                {
+                  type = "remotely registered prefixes";
+                }
+            }
+        }
+
+      fp (out, "Displayed %d out of %d %s%s",
+          printed, total, type, HVTY_NEWLINE);
+    }
+  return printed;
+}
+
+
+
+/*
+ * rfapiShowRemoteRegistrations
+ *
+ * Similar to rfapiShowImportTable() above. This function
+ * is mean to produce the "remote" portion of the output
+ * of "show vnc registrations".
+ */
+int
+rfapiShowRemoteRegistrations (
+  void         *stream,
+  struct prefix        *prefix_only,
+  int          show_expiring,
+  int          show_local,
+  int          show_remote,
+  int          show_imported)
+{
+  struct bgp *bgp;
+  struct rfapi *h;
+  struct rfapi_import_table *it;
+  int printed = 0;
+
+  bgp = bgp_get_default ();
+  if (!bgp)
+    {
+      return printed;
+    }
+
+  h = bgp->rfapi;
+  if (!h)
+    {
+      return printed;
+    }
+
+  for (it = h->imports; it; it = it->next)
+    {
+      printed +=
+        rfapiShowRemoteRegistrationsIt (bgp, stream, it, prefix_only,
+                                        show_expiring, show_local,
+                                        show_remote, show_imported, NULL);
+    }
+
+  if (h->import_mac)
+    {
+      void *cursor = NULL;
+      int rc;
+      uintptr_t lni_as_ptr;
+      uint32_t lni;
+      uint32_t *pLni;
+
+      for (rc =
+           skiplist_next (h->import_mac, (void **) &lni_as_ptr, (void **) &it,
+                          &cursor); !rc;
+           rc =
+           skiplist_next (h->import_mac, (void **) &lni_as_ptr, (void **) &it,
+                          &cursor))
+        {
+          pLni = NULL;
+          if ((lni_as_ptr & 0xffffffff) == lni_as_ptr)
+            {
+              lni = (uint32_t) (lni_as_ptr & 0xffffffff);
+              pLni = &lni;
+            }
+
+          printed +=
+            rfapiShowRemoteRegistrationsIt (bgp, stream, it, prefix_only,
+                                            show_expiring, show_local,
+                                            show_remote, show_imported, pLni);
+        }
+    }
+
+  return printed;
+}
+
+/*------------------------------------------
+ * rfapiRfapiIpAddr2Str
+ *
+ * UI helper: generate string from rfapi_ip_addr
+ *
+ * input: 
+ *     a                       IP v4/v6 address
+ *
+ * output
+ *     buf                     put string here
+ *     bufsize                 max space to write
+ *
+ * return value:
+ *     NULL                    conversion failed
+ *     non-NULL                pointer to buf
+ --------------------------------------------*/
+const char *
+rfapiRfapiIpAddr2Str (struct rfapi_ip_addr *a, char *buf, int bufsize)
+{
+  const char *rc = NULL;
+
+  switch (a->addr_family)
+    {
+    case AF_INET:
+      rc = inet_ntop (a->addr_family, &a->addr.v4, buf, bufsize);
+      break;
+    case AF_INET6:
+      rc = inet_ntop (a->addr_family, &a->addr.v6, buf, bufsize);
+      break;
+    }
+  return rc;
+}
+
+void
+rfapiPrintRfapiIpAddr (void *stream, struct rfapi_ip_addr *a)
+{
+  char buf[BUFSIZ];
+  const char *rc = NULL;
+
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out = NULL;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  rc = rfapiRfapiIpAddr2Str (a, buf, BUFSIZ);
+
+  if (rc)
+    fp (out, "%s", buf);
+}
+
+const char *
+rfapiRfapiIpPrefix2Str (struct rfapi_ip_prefix *p, char *buf, int bufsize)
+{
+  struct rfapi_ip_addr *a = &p->prefix;
+  const char *rc = NULL;
+
+  switch (a->addr_family)
+    {
+    case AF_INET:
+      rc = inet_ntop (a->addr_family, &a->addr.v4, buf, bufsize);
+      break;
+    case AF_INET6:
+      rc = inet_ntop (a->addr_family, &a->addr.v6, buf, bufsize);
+      break;
+    }
+
+  if (rc)
+    {
+      int alen = strlen (buf);
+      int remaining = bufsize - alen - 1;
+      int slen;
+
+      if (remaining > 0)
+        {
+          slen = snprintf (buf + alen, remaining, "/%u", p->length);
+          if (slen < remaining) /* see man page for snprintf(3) */
+            return rc;
+        }
+    }
+
+  return NULL;
+}
+
+void
+rfapiPrintRfapiIpPrefix (void *stream, struct rfapi_ip_prefix *p)
+{
+  char buf[BUFSIZ];
+  const char *rc;
+
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out = NULL;
+  const char *vty_newline;
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  rc = rfapiRfapiIpPrefix2Str (p, buf, BUFSIZ);
+
+  if (rc)
+    fp (out, "%s:%u", buf, p->cost);
+  else
+    fp (out, "?/?:?");
+}
+
+void
+rfapiPrintRd (struct vty *vty, struct prefix_rd *prd)
+{
+  char buf[BUFSIZ];
+
+  buf[0] = 0;
+  prefix_rd2str (prd, buf, BUFSIZ);
+  buf[BUFSIZ - 1] = 0;
+  vty_out (vty, "%s", buf);
+}
+
+void
+rfapiPrintAdvertisedInfo (
+  struct vty                   *vty,
+  struct rfapi_descriptor      *rfd,
+  safi_t                       safi,
+  struct prefix                        *p)
+{
+  afi_t afi;                    /* of the VN address */
+  struct bgp_node *bn;
+  struct bgp_info *bi;
+  uint8_t type = ZEBRA_ROUTE_BGP;
+  struct bgp *bgp;
+  int printed = 0;
+  struct prefix_rd prd0;
+  struct prefix_rd *prd;
+
+  /*
+   * Find the bgp_info in the RIB corresponding to this
+   * prefix and rfd
+   */
+
+  afi = family2afi (p->family);
+  assert (afi == AFI_IP || afi == AFI_IP6);
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+  assert (bgp);
+
+  if (safi == SAFI_ENCAP)
+    {
+      memset (&prd0, 0, sizeof (prd0));
+      prd0.family = AF_UNSPEC;
+      prd0.prefixlen = 64;
+      prd = &prd0;
+    }
+  else
+    {
+      prd = &rfd->rd;
+    }
+  bn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
+
+  vty_out (vty, "  bn=%p%s", bn, HVTY_NEWLINE);
+
+  for (bi = bn->info; bi; bi = bi->next)
+    {
+      if (bi->peer == rfd->peer &&
+          bi->type == type &&
+          bi->sub_type == BGP_ROUTE_RFP &&
+          bi->extra && bi->extra->vnc.export.rfapi_handle == (void *) rfd)
+        {
+
+          rfapiPrintBi (vty, bi);
+          printed = 1;
+        }
+    }
+
+  if (!printed)
+    {
+      vty_out (vty, "    --?--%s", HVTY_NEWLINE);
+      return;
+    }
+
+}
+
+void
+rfapiPrintDescriptor (struct vty *vty, struct rfapi_descriptor *rfd)
+{
+  /* pHD un-addr vn-addr pCB cookie rd lifetime */
+  /* RT export list */
+  /* RT import list */
+  /* list of advertised prefixes */
+  /* dump import table */
+
+  char *s;
+  void *cursor;
+  int rc;
+  afi_t afi;
+  struct rfapi_adb *adb;
+  char buf[BUFSIZ];
+
+  vty_out (vty, "%-10p ", rfd);
+  rfapiPrintRfapiIpAddr (vty, &rfd->un_addr);
+  vty_out (vty, " ");
+  rfapiPrintRfapiIpAddr (vty, &rfd->vn_addr);
+  vty_out (vty, " %p %p ", rfd->response_cb, rfd->cookie);
+  rfapiPrintRd (vty, &rfd->rd);
+  vty_out (vty, " %d", rfd->response_lifetime);
+  vty_out (vty, " %s", (rfd->rfg ? rfd->rfg->name : "<orphaned>"));
+  vty_out (vty, "%s", HVTY_NEWLINE);
+
+  vty_out (vty, " Peer %p #%d%s", rfd->peer, rfd->peer->lock, HVTY_NEWLINE);
+
+  /* export RT list */
+  if (rfd->rt_export_list)
+    {
+      s =
+        ecommunity_ecom2str (rfd->rt_export_list,
+                             ECOMMUNITY_FORMAT_ROUTE_MAP);
+      vty_out (vty, " Export %s%s", s, HVTY_NEWLINE);
+      XFREE (MTYPE_ECOMMUNITY_STR, s);
+    }
+  else
+    {
+      vty_out (vty, " Export (nil)%s", HVTY_NEWLINE);
+    }
+
+  /* import RT list */
+  if (rfd->import_table)
+    {
+      s = ecommunity_ecom2str (rfd->import_table->rt_import_list,
+                               ECOMMUNITY_FORMAT_ROUTE_MAP);
+      vty_out (vty, " Import %s%s", s, HVTY_NEWLINE);
+      XFREE (MTYPE_ECOMMUNITY_STR, s);
+    }
+  else
+    {
+      vty_out (vty, " Import (nil)%s", HVTY_NEWLINE);
+    }
+
+  for (afi = AFI_IP; afi < AFI_MAX; ++afi)
+    {
+      u_char family;
+
+      family = afi2family (afi);
+      if (!family)
+        continue;
+
+      cursor = NULL;
+      for (rc =
+           skiplist_next (rfd->advertised.ipN_by_prefix, NULL, (void **) &adb,
+                          &cursor); rc == 0;
+           rc =
+           skiplist_next (rfd->advertised.ipN_by_prefix, NULL, (void **) &adb,
+                          &cursor))
+        {
+
+          /* group like family prefixes together in output */
+          if (family != adb->prefix_ip.family)
+            continue;
+
+          prefix2str (&adb->prefix_ip, buf, BUFSIZ);
+          buf[BUFSIZ - 1] = 0;  /* guarantee NUL-terminated */
+
+          vty_out (vty, "  Adv Pfx: %s%s", buf, HVTY_NEWLINE);
+          rfapiPrintAdvertisedInfo (vty, rfd, SAFI_MPLS_VPN, &adb->prefix_ip);
+        }
+    }
+  for (rc =
+       skiplist_next (rfd->advertised.ip0_by_ether, NULL, (void **) &adb,
+                      &cursor); rc == 0;
+       rc =
+       skiplist_next (rfd->advertised.ip0_by_ether, NULL, (void **) &adb,
+                      &cursor))
+    {
+
+      prefix2str (&adb->prefix_eth, buf, BUFSIZ);
+      buf[BUFSIZ - 1] = 0;      /* guarantee NUL-terminated */
+
+      vty_out (vty, "  Adv Pfx: %s%s", buf, HVTY_NEWLINE);
+
+      /* TBD update the following function to print ethernet info */
+      /* Also need to pass/use rd */
+      rfapiPrintAdvertisedInfo (vty, rfd, SAFI_MPLS_VPN, &adb->prefix_ip);
+    }
+  vty_out (vty, "%s", HVTY_NEWLINE);
+}
+
+/*
+ * test scripts rely on first line for each nve starting in 1st column,
+ * leading whitespace for additional detail of that nve
+ */
+void
+rfapiPrintMatchingDescriptors (struct vty *vty,
+                               struct prefix *vn_prefix,
+                               struct prefix *un_prefix)
+{
+  struct bgp *bgp;
+  struct rfapi *h;
+  struct listnode *ln;
+  struct rfapi_descriptor *rfd;
+  int printed = 0;
+
+  bgp = bgp_get_default ();     /* assume 1 instance for now */
+  if (!bgp)
+    return;
+
+  h = bgp->rfapi;
+  assert (h);
+
+  for (ln = listhead (&h->descriptors); ln; ln = listnextnode (ln))
+    {
+      rfd = listgetdata (ln);
+
+      struct prefix pfx;
+
+      if (vn_prefix)
+        {
+          assert (!rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx));
+          if (!prefix_match (vn_prefix, &pfx))
+            continue;
+        }
+
+      if (un_prefix)
+        {
+          assert (!rfapiRaddr2Qprefix (&rfd->un_addr, &pfx));
+          if (!prefix_match (un_prefix, &pfx))
+            continue;
+        }
+
+      if (!printed)
+        {
+          /* print column header */
+          vty_out (vty,
+                   "%s %s %s %s %s %s %s %s%s",
+                   "descriptor", "un-addr", "vn-addr", "callback", "cookie",
+                   "RD", "lifetime", "group", HVTY_NEWLINE);
+        }
+      rfapiPrintDescriptor (vty, rfd);
+      printed = 1;
+    }
+}
+
+
+/*
+ * Parse an address and put into a struct prefix
+ */
+int
+rfapiCliGetPrefixAddr (struct vty *vty, const char *str, struct prefix *p)
+{
+  if (!str2prefix (str, p))
+    {
+      vty_out (vty, "Malformed address \"%s\"%s", str, HVTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  switch (p->family)
+    {
+    case AF_INET:
+      if (p->prefixlen != 32)
+        {
+          vty_out (vty, "Not a host address: \"%s\"%s", str, HVTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      break;
+    case AF_INET6:
+      if (p->prefixlen != 128)
+        {
+          vty_out (vty, "Not a host address: \"%s\"%s", str, HVTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      break;
+    default:
+      vty_out (vty, "Invalid address \"%s\"%s", str, HVTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return 0;
+}
+
+int
+rfapiCliGetRfapiIpAddr (
+  struct vty           *vty,
+  const char           *str,
+  struct rfapi_ip_addr *hai)
+{
+  struct prefix pfx;
+  int rc;
+
+  rc = rfapiCliGetPrefixAddr (vty, str, &pfx);
+  if (rc)
+    return rc;
+
+  hai->addr_family = pfx.family;
+  if (pfx.family == AF_INET)
+    hai->addr.v4 = pfx.u.prefix4;
+  else
+    hai->addr.v6 = pfx.u.prefix6;
+
+  return 0;
+}
+
+/*
+ * Note: this function does not flush vty output, so if it is called
+ * with a stream pointing to a vty, the user will have to type something
+ * before the callback output shows up
+ */
+void
+rfapiPrintNhl (void *stream, struct rfapi_next_hop_entry *next_hops)
+{
+  struct rfapi_next_hop_entry *nh;
+  int count;
+
+  int (*fp) (void *, const char *, ...);
+  struct vty *vty;
+  void *out;
+  const char *vty_newline;
+
+#define REMAIN (BUFSIZ - (p-line))
+#define INCP {p += (r > REMAIN)? REMAIN: r;}
+
+
+  if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
+    return;
+
+  for (nh = next_hops, count = 1; nh; nh = nh->next, ++count)
+    {
+
+      char line[BUFSIZ];
+      char *p = line;
+      int r;
+
+      r = snprintf (p, REMAIN, "%3d  pfx=", count);
+      INCP;
+
+      if (rfapiRfapiIpPrefix2Str (&nh->prefix, p, REMAIN))
+        {
+          /* it fit, so count length */
+          r = strlen (p);
+        }
+      else
+        {
+          /* didn't fit */
+          goto truncate;
+        }
+      INCP;
+
+      r = snprintf (p, REMAIN, ", un=");
+      INCP;
+
+      if (rfapiRfapiIpAddr2Str (&nh->un_address, p, REMAIN))
+        {
+          /* it fit, so count length */
+          r = strlen (p);
+        }
+      else
+        {
+          /* didn't fit */
+          goto truncate;
+        }
+      INCP;
+
+      r = snprintf (p, REMAIN, ", vn=");
+      INCP;
+
+      if (rfapiRfapiIpAddr2Str (&nh->vn_address, p, REMAIN))
+        {
+          /* it fit, so count length */
+          r = strlen (p);
+        }
+      else
+        {
+          /* didn't fit */
+          goto truncate;
+        }
+      INCP;
+
+    truncate:
+      line[BUFSIZ - 1] = 0;
+      fp (out, "%s%s", line, HVTY_NEWLINE);
+
+      /*
+       * options
+       */
+      if (nh->vn_options)
+        {
+          struct rfapi_vn_option *vo;
+          char offset[] = "     ";
+
+          for (vo = nh->vn_options; vo; vo = vo->next)
+            {
+              char pbuf[100];
+
+              switch (vo->type)
+                {
+                case RFAPI_VN_OPTION_TYPE_L2ADDR:
+                  rfapiEthAddr2Str (&vo->v.l2addr.macaddr, pbuf,
+                                    sizeof (pbuf));
+                  fp (out, "%sL2 %s LBL=0x%06x NETID=0x%06x NVEID=%d%s",
+                      offset, pbuf, (vo->v.l2addr.label & 0x00ffffff),
+                      (vo->v.l2addr.logical_net_id & 0x00ffffff),
+                      vo->v.l2addr.local_nve_id, HVTY_NEWLINE);
+                  break;
+
+                case RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP:
+                  prefix2str (&vo->v.local_nexthop.addr, pbuf, sizeof (pbuf));
+                  fp (out, "%sLNH %s cost=%d%s",
+                      offset, pbuf, vo->v.local_nexthop.cost, HVTY_NEWLINE);
+                  break;
+
+                default:
+                  fp (out, "%svn option type %d (unknown)%s",
+                      offset, vo->type, HVTY_NEWLINE);
+                  break;
+                }
+            }
+        }
+      if (nh->un_options)
+        {
+          struct rfapi_un_option *uo;
+          char offset[] = "     ";
+
+          for (uo = nh->un_options; uo; uo = uo->next)
+            {
+              switch (uo->type)
+                {
+                case RFAPI_UN_OPTION_TYPE_TUNNELTYPE:
+                  rfapi_print_tunneltype_option (stream, 8, &uo->v.tunnel);
+                  break;
+                default:
+                  fp (out, "%sUN Option type %d%s",
+                      offset, uo->type, vty_newline);
+                  break;
+                }
+
+            }
+        }
+    }
+}
+
+/***********************************************************************
+ *                     STATIC ROUTES
+ ***********************************************************************/
+
+/*
+ * Add another nexthop to the NHL
+ */
+static void
+rfapiAddDeleteLocalRfpPrefix (
+  struct rfapi_ip_addr         *un_addr,
+  struct rfapi_ip_addr         *vn_addr,
+  struct rfapi_ip_prefix       *rprefix,
+  int                          is_add,
+  uint32_t                     lifetime,     /* add only */
+  struct rfapi_vn_option       *vn_options,
+  struct rfapi_next_hop_entry  **head,
+  struct rfapi_next_hop_entry  **tail)
+{
+  struct rfapi_next_hop_entry *new;
+
+  /*
+   * construct NHL
+   */
+
+  new = XCALLOC (MTYPE_RFAPI_NEXTHOP, sizeof (struct rfapi_next_hop_entry));
+  new->prefix = *rprefix;
+  new->un_address = *un_addr;
+  new->vn_address = *vn_addr;
+
+  new->vn_options = vn_options;
+  if (is_add)
+    {
+      new->lifetime = lifetime;
+    }
+  else
+    {
+      new->lifetime = RFAPI_REMOVE_RESPONSE_LIFETIME;
+    }
+
+  if (*tail)
+    (*tail)->next = new;
+  *tail = new;
+  if (!*head)
+    {
+      *head = new;
+    }
+}
+
+
+static int
+register_add (
+  struct vty *vty,
+  const char *arg_prefix,
+  const char *arg_vn,
+  const char *arg_un,
+  const char *arg_cost,     /* optional */
+  const char *arg_lifetime, /* optional */
+  const char *arg_macaddr,  /* optional */
+  const char *arg_vni,      /* mac present=>mandatory Virtual Network ID */
+  int        argc,
+  const char **argv)
+{
+       struct rfapi_ip_addr vn_address;
+       struct rfapi_ip_addr un_address;
+       struct prefix pfx;
+       struct rfapi_ip_prefix rpfx;
+       uint32_t cost;
+       uint32_t lnh_cost;
+       uint32_t lifetime;
+       rfapi_handle rfd;
+       struct rfapi_vn_option optary[10];       /* XXX must be big enough */
+       struct rfapi_vn_option *opt = NULL;
+       int opt_next = 0;
+
+       int rc = CMD_WARNING;
+       char *endptr;
+       struct bgp *bgp;
+       struct rfapi *h;
+       struct rfapi_cfg *rfapi_cfg;
+
+       const char *arg_lnh = NULL;
+       const char *arg_lnh_cost = NULL;
+
+         bgp = bgp_get_default ();      /* assume 1 instance for now */
+       if (!bgp)
+         {
+           if (vty)
+             vty_out (vty, "BGP not configured%s", VTY_NEWLINE);
+           return CMD_WARNING;
+         }
+
+       h = bgp->rfapi;
+       rfapi_cfg = bgp->rfapi_cfg;
+       if (!h || !rfapi_cfg)
+         {
+           if (vty)
+             vty_out (vty, "RFAPI not configured%s", VTY_NEWLINE);
+           return CMD_WARNING;
+         }
+
+       for (; argc; --argc, ++argv)
+         {
+           if (!strcmp (*argv, "local-next-hop"))
+             {
+               if (arg_lnh)
+                 {
+                   vty_out (vty, "local-next-hop specified more than once%s",
+                            VTY_NEWLINE);
+                   return CMD_WARNING;
+                 }
+               if (argc <= 1)
+                 {
+                   vty_out (vty, "Missing parameter for local-next-hop%s",
+                            VTY_NEWLINE);
+                   return CMD_WARNING;
+                 }
+               ++argv, --argc;
+               arg_lnh = *argv;
+             }
+           if (!strcmp (*argv, "local-cost"))
+             {
+               if (arg_lnh_cost)
+                 {
+                   vty_out (vty, "local-cost specified more than once%s",
+                            VTY_NEWLINE);
+                   return CMD_WARNING;
+                 }
+               if (argc <= 1)
+                 {
+                   vty_out (vty, "Missing parameter for local-cost%s",
+                            VTY_NEWLINE);
+                   return CMD_WARNING;
+                 }
+               ++argv, --argc;
+               arg_lnh_cost = *argv;
+             }
+         }
+
+       if ((rc = rfapiCliGetRfapiIpAddr (vty, arg_vn, &vn_address)))
+         goto fail;
+       if ((rc = rfapiCliGetRfapiIpAddr (vty, arg_un, &un_address)))
+         goto fail;
+
+       /* arg_prefix is optional if mac address is given */
+       if (arg_macaddr && !arg_prefix)
+         {
+           /*
+            * fake up a 0/32 or 0/128 prefix
+            */
+           switch (vn_address.addr_family)
+             {
+             case AF_INET:
+               arg_prefix = "0.0.0.0/32";
+               break;
+             case AF_INET6:
+               arg_prefix = "0::0/128";
+               break;
+             default:
+               vty_out (vty, "Internal error, unknown VN address family%s",
+                        VTY_NEWLINE);
+               return CMD_WARNING;
+             }
+
+         }
+
+       if (!str2prefix (arg_prefix, &pfx))
+         {
+           vty_out (vty, "Malformed prefix \"%s\"%s", arg_prefix,
+                    VTY_NEWLINE);
+           goto fail;
+         }
+       if (pfx.family != AF_INET
+           && pfx.family != AF_INET6)
+         {
+           vty_out (vty, "prefix \"%s\" has invalid address family%s",
+                    arg_prefix, VTY_NEWLINE);
+           goto fail;
+         }
+
+
+       memset (optary, 0, sizeof (optary));
+
+       if (arg_cost)
+         {
+           endptr = NULL;
+           cost = strtoul (arg_cost, &endptr, 10);
+           if (*endptr != '\0' || cost > 255)
+             {
+               vty_out (vty, "%% Invalid %s value%s", "cost", VTY_NEWLINE);
+               goto fail;
+             }
+         }
+       else
+         {
+           cost = 255;
+         }
+
+       if (arg_lifetime)
+         {
+           if (!strcmp (arg_lifetime, "infinite"))
+             {
+               lifetime = RFAPI_INFINITE_LIFETIME;
+             }
+           else
+             {
+               endptr = NULL;
+               lifetime = strtoul (arg_lifetime, &endptr, 10);
+               if (*endptr != '\0')
+                 {
+                   vty_out (vty, "%% Invalid %s value%s", "lifetime",
+                            VTY_NEWLINE);
+                   goto fail;
+                 }
+             }
+         }
+       else
+         {
+           lifetime = RFAPI_INFINITE_LIFETIME;  /* default infinite */
+         }
+
+       if (arg_lnh_cost)
+         {
+           if (!arg_lnh)
+             {
+               vty_out (vty,
+                        "%% %s may only be specified with local-next-hop%s",
+                        "local-cost", VTY_NEWLINE);
+               goto fail;
+             }
+           endptr = NULL;
+           lnh_cost = strtoul (arg_lnh_cost, &endptr, 10);
+           if (*endptr != '\0' || lnh_cost > 255)
+             {
+               vty_out (vty, "%% Invalid %s value%s", "local-cost",
+                        VTY_NEWLINE);
+               goto fail;
+             }
+         }
+       else
+         {
+           lnh_cost = 255;
+         }
+
+       if (arg_lnh)
+         {
+           if (!arg_prefix)
+             {
+               vty_out (vty, "%% %s may only be specified with prefix%s",
+                        "local-next-hop", VTY_NEWLINE);
+               goto fail;
+             }
+           if ((rc = rfapiCliGetPrefixAddr (vty, arg_lnh,
+                                            &optary[opt_next].v.
+                                            local_nexthop.addr)))
+             {
+
+               goto fail;
+             }
+
+           optary[opt_next].v.local_nexthop.cost = lnh_cost;
+           optary[opt_next].type = RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP;
+
+           if (opt_next)
+             {
+               optary[opt_next - 1].next = optary + opt_next;
+             }
+           else
+             {
+               opt = optary;
+             }
+           ++opt_next;
+         }
+
+       if (arg_vni && !arg_macaddr)
+         {
+           vty_out (vty, "%% %s may only be specified with mac address%s",
+                    "virtual-network-identifier", VTY_NEWLINE);
+           goto fail;
+         }
+
+       if (arg_macaddr)
+         {
+           if (!arg_vni)
+             {
+               vty_out (vty,
+                        "Missing \"vni\" parameter (mandatory with mac)%s",
+                        VTY_NEWLINE);
+               return CMD_WARNING;
+             }
+           VTY_GET_INTEGER ("Logical Network ID",
+                            optary[opt_next].v.l2addr.logical_net_id,
+                            arg_vni);
+
+           if ((rc = rfapiStr2EthAddr (arg_macaddr,
+                                       &optary[opt_next].v.l2addr.macaddr)))
+             {
+               vty_out (vty, "Invalid %s value%s", "mac address",
+                        VTY_NEWLINE);
+               goto fail;
+             }
+           /* TBD label, NVE ID */
+
+           optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+
+           if (opt_next)
+             {
+               optary[opt_next - 1].next = optary + opt_next;
+             }
+           else
+             {
+               opt = optary;
+             }
+           ++opt_next;
+         }
+
+       zlog_debug
+         ("%s: vn=%s, un=%s, prefix=%s, cost=%s, lifetime=%s, lnh=%s",
+          __func__, arg_vn, arg_un, arg_prefix,
+          (arg_cost ? arg_cost : "NULL"),
+          (arg_lifetime ? arg_lifetime : "NULL"),
+          (arg_lnh ? arg_lnh : "NULL"));
+
+       rfapiQprefix2Rprefix (&pfx, &rpfx);
+
+       rpfx.cost = cost & 255;
+
+       /* look up rf descriptor, call open if it doesn't exist  */
+       rc =
+         rfapi_find_rfd (bgp, &vn_address, &un_address,
+                         (struct rfapi_descriptor **) &rfd);
+       if (rc)
+         {
+           if (ENOENT == rc)
+             {
+               struct rfapi_un_option uo;
+
+               /*
+                * flag descriptor as provisionally opened for static route
+                * registration so that we can fix up the other parameters
+                * when the real open comes along
+                */
+               memset (&uo, 0, sizeof (uo));
+               uo.type = RFAPI_UN_OPTION_TYPE_PROVISIONAL;
+
+               rc = rfapi_open (rfapi_get_rfp_start_val_by_bgp (bgp), &vn_address, &un_address, &uo,    /* flags */
+                                NULL, NULL,     /* no userdata */
+                                &rfd);
+               if (rc)
+                 {
+                   vty_out (vty, "Can't open session for this NVE: %s%s",
+                            rfapi_error_str (rc), VTY_NEWLINE);
+                   rc = CMD_WARNING;
+                   goto fail;
+                 }
+             }
+           else
+             {
+               vty_out (vty, "Can't find session for this NVE: %s%s",
+                        rfapi_error_str (rc), VTY_NEWLINE);
+               goto fail;
+             }
+         }
+
+       rc =
+         rfapi_register (rfd, &rpfx, lifetime, NULL, opt, RFAPI_REGISTER_ADD);
+       if (!rc)
+         {
+           struct rfapi_next_hop_entry *head = NULL;
+           struct rfapi_next_hop_entry *tail = NULL;
+           struct rfapi_vn_option *vn_opt_new;
+
+           zlog_debug ("%s: rfapi_register succeeded, returning 0", __func__);
+
+           if (h->rfp_methods.local_cb)
+             {
+               struct rfapi_descriptor *r = (struct rfapi_descriptor *) rfd;
+               vn_opt_new = rfapi_vn_options_dup (opt);
+
+               rfapiAddDeleteLocalRfpPrefix (&r->un_addr, &r->vn_addr, &rpfx,
+                                             1, lifetime, vn_opt_new, &head,
+                                             &tail);
+               if (head)
+                 {
+                   h->flags |= RFAPI_INCALLBACK;
+                   (*h->rfp_methods.local_cb) (head, r->cookie);
+                   h->flags &= ~RFAPI_INCALLBACK;
+                 }
+               head = tail = NULL;
+             }
+           return 0;
+         }
+
+       zlog_debug ("%s: rfapi_register failed", __func__);
+       vty_out (vty, "%s", VTY_NEWLINE);
+       vty_out (vty, "Registration failed.%s", VTY_NEWLINE);
+       vty_out (vty,
+                "Confirm that either the VN or UN address matches a configured NVE group.%s",
+                VTY_NEWLINE);
+       return CMD_WARNING;
+
+     fail:
+       zlog_debug ("%s: fail, rc=%d", __func__, rc);
+       return rc;
+}
+
+/************************************************************************
+ *             Add prefix With .LNH_OPTIONS
+ ************************************************************************/
+DEFUN (add_vnc_prefix_cost_life_lnh,
+       add_vnc_prefix_cost_life_lnh_cmd,
+       "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255> lifetime <1-4294967295> .LNH_OPTIONS",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Administrative cost   [default: 255]\n"
+       "Administrative cost\n"
+       "Registration lifetime [default: infinite]\n"
+       "Lifetime value in seconds\n"
+       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[0], argv[1], argv[2], argv[3], argv[4],
+                       /* mac vni */
+                       NULL, NULL, argc, argv);
+}
+
+DEFUN (add_vnc_prefix_life_cost_lnh,
+       add_vnc_prefix_life_cost_lnh_cmd,
+       "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lifetime <1-4294967295> cost <0-255> .LNH_OPTIONS",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Registration lifetime [default: infinite]\n"
+       "Lifetime value in seconds\n"
+       "Administrative cost   [default: 255]\n"
+       "Administrative cost\n"
+       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[0], argv[1], argv[2], argv[4], argv[3],
+                       /* mac vni */
+                       NULL, NULL, argc, argv);
+}
+
+DEFUN (add_vnc_prefix_cost_lnh,
+       add_vnc_prefix_cost_lnh_cmd,
+       "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255> .LNH_OPTIONS",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Administrative cost   [default: 255]\n"
+       "Administrative cost\n"
+       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[0], argv[1], argv[2], argv[3], NULL,
+                       /* mac vni */
+                       NULL, NULL, argc, argv);
+}
+
+DEFUN (add_vnc_prefix_life_lnh,
+       add_vnc_prefix_life_lnh_cmd,
+       "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lifetime <1-4294967295> .LNH_OPTIONS",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Registration lifetime [default: infinite]\n"
+       "Lifetime value in seconds\n"
+       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[0], argv[1], argv[2], NULL, argv[3],
+                       /* mac vni */
+                       NULL, NULL, argc, argv);
+}
+
+DEFUN (add_vnc_prefix_lnh,
+       add_vnc_prefix_lnh_cmd,
+       "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) .LNH_OPTIONS",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[0], argv[1], argv[2], NULL, NULL,
+                       /* mac vni */
+                       NULL, NULL, argc, argv);
+}
+
+/************************************************************************
+ *             Add prefix Without .LNH_OPTIONS
+ ************************************************************************/
+DEFUN (add_vnc_prefix_cost_life,
+       add_vnc_prefix_cost_life_cmd,
+       "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255> lifetime <1-4294967295>",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Administrative cost   [default: 255]\n"
+       "Administrative cost\n"
+       "Registration lifetime [default: infinite]\n"
+       "Lifetime value in seconds\n"
+       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[0], argv[1], argv[2], argv[3], argv[4],
+                       /* mac vni */
+                       NULL, NULL, 0, NULL);
+}
+
+DEFUN (add_vnc_prefix_life_cost,
+       add_vnc_prefix_life_cost_cmd,
+       "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lifetime <1-4294967295> cost <0-255>",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Registration lifetime [default: infinite]\n"
+       "Lifetime value in seconds\n"
+       "Administrative cost   [default: 255]\n"
+       "Administrative cost\n"
+       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[0], argv[1], argv[2], argv[4], argv[3],
+                       /* mac vni */
+                       NULL, NULL, 0, NULL);
+}
+
+DEFUN (add_vnc_prefix_cost,
+       add_vnc_prefix_cost_cmd,
+       "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255>",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Administrative cost   [default: 255]\n"
+       "Administrative cost\n"
+       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[0], argv[1], argv[2], argv[3], NULL,
+                       /* mac vni */
+                       NULL, NULL, 0, NULL);
+}
+
+DEFUN (add_vnc_prefix_life,
+       add_vnc_prefix_life_cmd,
+       "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lifetime <1-4294967295>",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Registration lifetime [default: infinite]\n"
+       "Lifetime value in seconds\n"
+       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[0], argv[1], argv[2], NULL, argv[3],
+                       /* mac vni */
+                       NULL, NULL, 0, NULL);
+}
+
+DEFUN (add_vnc_prefix,
+       add_vnc_prefix_cmd,
+       "add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X)",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[0], argv[1], argv[2], NULL, NULL,
+                       /* mac vni */
+                       NULL, NULL, 0, NULL);
+}
+
+/************************************************************************
+ *                     Mac address registrations
+ ************************************************************************/
+DEFUN (add_vnc_mac_vni_prefix_cost_life,
+       add_vnc_mac_vni_prefix_cost_life_cmd,
+       "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M) cost <0-255> lifetime <1-4294967295>",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify mac address infomation\n"
+       "MAC address\n"
+       "Virtual Network Identifier follows\n"
+       "Virtual Network Identifier\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "Administrative cost   [default: 255]\n"
+       "Administrative cost\n"
+       "Registration lifetime [default: infinite]\n"
+       "Lifetime value in seconds\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[4], argv[2], argv[3], argv[5], argv[6],
+                       /* mac vni */
+                       argv[0], argv[1], 0, NULL);
+}
+
+
+DEFUN (add_vnc_mac_vni_prefix_life,
+       add_vnc_mac_vni_prefix_life_cmd,
+       "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M) lifetime <1-4294967295>",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify mac address infomation\n"
+       "MAC address\n"
+       "Virtual Network Identifier follows\n"
+       "Virtual Network Identifier\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "Registration lifetime [default: infinite]\n"
+       "Lifetime value in seconds\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[4], argv[2], argv[3], NULL, argv[5],
+                       /* mac vni */
+                       argv[0], argv[1], 0, NULL);
+}
+
+DEFUN (add_vnc_mac_vni_prefix_cost,
+       add_vnc_mac_vni_prefix_cost_cmd,
+       "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M) cost <0-255>",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify mac address infomation\n"
+       "MAC address\n"
+       "Virtual Network Identifier follows\n"
+       "Virtual Network Identifier\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "Administrative cost   [default: 255]\n" "Administrative cost\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[4], argv[2], argv[3], argv[5], NULL,
+                       /* mac vni */
+                       argv[0], argv[1], 0, NULL);
+}
+
+DEFUN (add_vnc_mac_vni_prefix,
+       add_vnc_mac_vni_prefix_cmd,
+       "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) prefix (A.B.C.D/M|X:X::X:X/M)",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify mac address infomation\n"
+       "MAC address\n"
+       "Virtual Network Identifier follows\n"
+       "Virtual Network Identifier\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Add/modify prefix related infomation\n"
+       "IPv4 prefix\n" "IPv6 prefix\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, argv[4], argv[2], argv[3], NULL, NULL,
+                       /* mac vni */
+                       argv[0], argv[1], 0, NULL);
+}
+
+DEFUN (add_vnc_mac_vni_cost_life,
+       add_vnc_mac_vni_cost_life_cmd,
+       "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255> lifetime <1-4294967295>",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify mac address infomation\n"
+       "MAC address\n"
+       "Virtual Network Identifier follows\n"
+       "Virtual Network Identifier\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Administrative cost   [default: 255]\n"
+       "Administrative cost\n"
+       "Registration lifetime [default: infinite]\n"
+       "Lifetime value in seconds\n")
+{
+  /*                       pfx      vn       un       cost     life */
+  return register_add (vty, NULL, argv[2], argv[3], argv[4], argv[5],
+                       /* mac vni */
+                       argv[0], argv[1], 0, NULL);
+}
+
+
+DEFUN (add_vnc_mac_vni_cost,
+       add_vnc_mac_vni_cost_cmd,
+       "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) cost <0-255>",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify mac address infomation\n"
+       "MAC address\n"
+       "Virtual Network Identifier follows\n"
+       "Virtual Network Identifier\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Administrative cost   [default: 255]\n" "Administrative cost\n")
+{
+  /*                       pfx      vn       un    cost     life */
+  return register_add (vty, NULL, argv[2], argv[3], argv[4], NULL,
+                       /* mac vni */
+                       argv[0], argv[1], 0, NULL);
+}
+
+
+DEFUN (add_vnc_mac_vni_life,
+       add_vnc_mac_vni_life_cmd,
+       "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) lifetime <1-4294967295>",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify mac address infomation\n"
+       "MAC address\n"
+       "Virtual Network Identifier follows\n"
+       "Virtual Network Identifier\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Registration lifetime [default: infinite]\n"
+       "Lifetime value in seconds\n")
+{
+  /*                       pfx      vn       un    cost  life */
+  return register_add (vty, NULL, argv[2], argv[3], NULL, argv[4],
+                       /* mac vni */
+                       argv[0], argv[1], 0, NULL);
+}
+
+
+DEFUN (add_vnc_mac_vni,
+       add_vnc_mac_vni_cmd,
+       "add vnc mac YY:YY:YY:YY:YY:YY virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X)",
+       "Add registration\n"
+       "VNC Information\n"
+       "Add/modify mac address infomation\n"
+       "MAC address\n"
+       "Virtual Network Identifier follows\n"
+       "Virtual Network Identifier\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n" "UN IPv6 interface address\n")
+{
+  /*                       pfx      vn       un    cost  life */
+  return register_add (vty, NULL, argv[2], argv[3], NULL, NULL,
+                       /* mac vni */
+                       argv[0], argv[1], 0, NULL);
+}
+
+/************************************************************************
+ *                     Delete prefix
+ ************************************************************************/
+
+struct rfapi_local_reg_delete_arg
+{
+  /*
+   * match parameters
+   */
+  struct rfapi_ip_addr un_address;     /* AF==0: wildcard */
+  struct rfapi_ip_addr vn_address;     /* AF==0: wildcard */
+  struct prefix                prefix;         /* AF==0: wildcard */
+
+  struct rfapi_l2address_option_match l2o;
+
+  /*
+   * result parameters
+   */
+  struct vty           *vty;
+  uint32_t             reg_count;
+  uint32_t             pfx_count;
+  uint32_t             query_count;
+
+  uint32_t             failed_pfx_count;
+
+  uint32_t             nve_count;
+  struct skiplist      *nves;
+
+  uint32_t             remote_active_nve_count;
+  uint32_t             remote_active_pfx_count;
+  uint32_t             remote_holddown_nve_count;
+  uint32_t             remote_holddown_pfx_count;
+};
+
+struct nve_addr
+{
+  struct rfapi_ip_addr                 vn;
+  struct rfapi_ip_addr                 un;
+  struct rfapi_descriptor              *rfd;
+  struct rfapi_local_reg_delete_arg    *cda;
+};
+
+static void
+nve_addr_free (void *hap)
+{
+  ((struct nve_addr *) hap)->cda->nve_count += 1;
+  XFREE (MTYPE_RFAPI_NVE_ADDR, hap);
+}
+
+static int
+nve_addr_cmp (void *k1, void *k2)
+{
+  struct nve_addr *a = (struct nve_addr *) k1;
+  struct nve_addr *b = (struct nve_addr *) k2;
+  int ret = 0;
+
+  if (!a || !b)
+    {
+      return (a - b);
+    }
+  if (a->un.addr_family != b->un.addr_family)
+    {
+      return (a->un.addr_family - b->un.addr_family);
+    }
+  if (a->vn.addr_family != b->vn.addr_family)
+    {
+      return (a->vn.addr_family - b->vn.addr_family);
+    }
+  if (a->un.addr_family == AF_INET)
+    {
+      ret = IPV4_ADDR_CMP (&a->un.addr.v4, &b->un.addr.v4);
+      if (ret != 0)
+        {
+          return ret;
+        }
+    }
+  else if (a->un.addr_family == AF_INET6)
+    {
+      ret = IPV6_ADDR_CMP (&a->un.addr.v6, &b->un.addr.v6);
+      if (ret != 0)
+        {
+          return ret;
+        }
+    }
+  else
+    {
+      assert (0);
+    }
+  if (a->vn.addr_family == AF_INET)
+    {
+      ret = IPV4_ADDR_CMP (&a->vn.addr.v4, &b->vn.addr.v4);
+      if (ret != 0)
+        return ret;
+    }
+  else if (a->vn.addr_family == AF_INET6)
+    {
+      ret = IPV6_ADDR_CMP (&a->vn.addr.v6, &b->vn.addr.v6);
+      if (ret == 0)
+        {
+          return ret;
+        }
+    }
+  else
+    {
+      assert (0);
+    }
+  return 0;
+}
+
+static int
+parse_deleter_args (
+  struct vty                           *vty,
+  const char                           *arg_prefix,
+  const char                           *arg_vn,
+  const char                           *arg_un,
+  const char                           *arg_l2addr,
+  const char                           *arg_vni,
+  struct rfapi_local_reg_delete_arg    *rcdarg)
+{
+  int rc = CMD_WARNING;
+
+    memset (rcdarg, 0, sizeof (struct rfapi_local_reg_delete_arg));
+
+  if (arg_vn && strcmp (arg_vn, "*"))
+    {
+      if ((rc = rfapiCliGetRfapiIpAddr (vty, arg_vn, &rcdarg->vn_address)))
+        return rc;
+    }
+  if (arg_un && strcmp (arg_un, "*"))
+    {
+      if ((rc = rfapiCliGetRfapiIpAddr (vty, arg_un, &rcdarg->un_address)))
+        return rc;
+    }
+  if (arg_prefix && strcmp (arg_prefix, "*"))
+    {
+
+      if (!str2prefix (arg_prefix, &rcdarg->prefix))
+        {
+          vty_out (vty, "Malformed prefix \"%s\"%s", arg_prefix, VTY_NEWLINE);
+          return rc;
+        }
+    }
+
+  if (arg_l2addr)
+    {
+      if (!arg_vni)
+        {
+          vty_out (vty, "Missing VNI%s", VTY_NEWLINE);
+          return rc;
+        }
+      if (strcmp (arg_l2addr, "*"))
+        {
+          if ((rc = rfapiStr2EthAddr (arg_l2addr, &rcdarg->l2o.o.macaddr)))
+            {
+              vty_out (vty, "Malformed L2 Address \"%s\"%s",
+                       arg_l2addr, VTY_NEWLINE);
+              return rc;
+            }
+          rcdarg->l2o.flags |= RFAPI_L2O_MACADDR;
+        }
+      if (strcmp (arg_vni, "*"))
+        {
+          VTY_GET_INTEGER ("Logical Network ID",
+                           rcdarg->l2o.o.logical_net_id, arg_vni);
+          rcdarg->l2o.flags |= RFAPI_L2O_LNI;
+        }
+    }
+  return 0;
+}
+
+static void
+record_nve_in_cda_list (
+  struct rfapi_local_reg_delete_arg    *cda,
+  struct rfapi_ip_addr                 *un_address,
+  struct rfapi_ip_addr                 *vn_address,
+  struct rfapi_descriptor              *rfd)
+{
+  struct nve_addr ha;
+  struct nve_addr *hap;
+
+    memset (&ha, 0, sizeof (ha));
+    ha.un = *un_address;
+    ha.vn = *vn_address;
+    ha.rfd = rfd;
+
+  if (!cda->nves)
+      cda->nves = skiplist_new (0, nve_addr_cmp, nve_addr_free);
+
+  if (skiplist_search (cda->nves, &ha, (void *) &hap))
+    {
+      hap = XCALLOC (MTYPE_RFAPI_NVE_ADDR, sizeof (struct nve_addr));
+        assert (hap);
+        ha.cda = cda;
+      * hap = ha;
+        skiplist_insert (cda->nves, hap, hap);
+    }
+}
+
+static void
+clear_vnc_responses (struct rfapi_local_reg_delete_arg *cda)
+{
+  struct rfapi *h;
+  struct rfapi_descriptor *rfd;
+  int query_count = 0;
+  struct listnode *node;
+  struct bgp *bgp_default = bgp_get_default ();
+
+  if (cda->vn_address.addr_family && cda->un_address.addr_family)
+    {
+      /*
+       * Single nve case
+       */
+      if (rfapi_find_rfd
+          (bgp_default, &cda->vn_address, &cda->un_address, &rfd))
+        return;
+
+      rfapiRibClear (rfd);
+      rfapi_query_done_all (rfd, &query_count);
+      cda->query_count += query_count;
+
+      /*
+       * Track unique nves seen
+       */
+      record_nve_in_cda_list (cda, &rfd->un_addr, &rfd->vn_addr, rfd);
+      return;
+    }
+
+  /*
+   * wildcard case
+   */
+
+  if (!bgp_default)
+      return;                   /* ENXIO */
+
+  h = bgp_default->rfapi;
+
+  if (!h)
+    return;                     /* ENXIO */
+
+  for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd))
+    {
+      /*
+       * match un, vn addresses of NVEs
+       */
+      if (cda->un_address.addr_family &&
+          rfapi_ip_addr_cmp (&cda->un_address, &rfd->un_addr))
+        {
+          continue;
+        }
+      if (cda->vn_address.addr_family &&
+          rfapi_ip_addr_cmp (&cda->vn_address, &rfd->vn_addr))
+        {
+          continue;
+        }
+
+      rfapiRibClear (rfd);
+
+      rfapi_query_done_all (rfd, &query_count);
+      cda->query_count += query_count;
+
+      /*
+       * Track unique nves seen
+       */
+      record_nve_in_cda_list (cda, &rfd->un_addr, &rfd->vn_addr, rfd);
+    }
+}
+
+/*
+ * TBD need to count deleted prefixes and nves?
+ *
+ * ENXIO       BGP or VNC not configured
+ */
+static int
+rfapiDeleteLocalPrefixes (struct rfapi_local_reg_delete_arg *cda)
+{
+  struct rfapi_ip_addr *pUn;    /* NULL = wildcard */
+  struct rfapi_ip_addr *pVn;    /* NULL = wildcard */
+  struct prefix *pPrefix;       /* NULL = wildcard */
+
+  struct rfapi *h;
+  struct listnode *node;
+  struct rfapi_descriptor *rfd;
+  struct rfapi_ip_prefix rprefix;
+  struct bgp *bgp_default = bgp_get_default ();
+  struct rfapi_next_hop_entry *head = NULL;
+  struct rfapi_next_hop_entry *tail = NULL;
+  struct rfapi_cfg *rfapi_cfg;
+
+#if DEBUG_L2_EXTRA
+    zlog_debug ("%s: entry", __func__);
+#endif
+
+  if (!bgp_default)
+      return ENXIO;
+
+    pUn = (cda->un_address.addr_family ? &cda->un_address : NULL);
+    pVn = (cda->vn_address.addr_family ? &cda->vn_address : NULL);
+    pPrefix = (cda->prefix.family ? &cda->prefix : NULL);
+
+    h = bgp_default->rfapi;
+    rfapi_cfg = bgp_default->rfapi_cfg;
+
+  if (!h || !rfapi_cfg)
+      return ENXIO;
+
+  if (pPrefix)
+    {
+      rfapiQprefix2Rprefix (pPrefix, &rprefix);
+    }
+
+#if DEBUG_L2_EXTRA
+  zlog_debug ("%s: starting descriptor loop", __func__);
+#endif
+
+  for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd))
+    {
+      struct rfapi_adb *adb;
+      int rc;
+      int deleted_from_this_nve;
+      struct nve_addr ha;
+      struct nve_addr *hap;
+
+#if DEBUG_L2_EXTRA
+      zlog_debug ("%s: rfd=%p", __func__, rfd);
+#endif
+
+      /*
+       * match un, vn addresses of NVEs
+       */
+      if (pUn && (rfapi_ip_addr_cmp (pUn, &rfd->un_addr)))
+        continue;
+      if (pVn && (rfapi_ip_addr_cmp (pVn, &rfd->vn_addr)))
+        continue;
+
+#if DEBUG_L2_EXTRA
+      zlog_debug ("%s: un, vn match", __func__);
+#endif
+
+      /*
+       * match prefix
+       */
+
+      deleted_from_this_nve = 0;
+
+      {
+        struct skiplist *sl;
+        struct rfapi_ip_prefix rp;
+        void *cursor;
+        struct list *adb_delete_list;
+
+        /*
+         * The advertisements are stored in a skiplist. Withdrawing
+         * the registration deletes the advertisement from the
+         * skiplist, which we can't do while iterating over that
+         * same skiplist using the current skiplist API.
+         *
+         * Strategy: iterate over the skiplist and build another
+         * list containing only the matching ADBs. Then delete
+         * _everything_ in that second list (which can be done
+         * using either skiplists or quagga linklists).
+         */
+        adb_delete_list = list_new ();
+
+        /*
+         * Advertised IP prefixes (not 0/32 or 0/128)
+         */
+        sl = rfd->advertised.ipN_by_prefix;
+
+        for (cursor = NULL,
+             rc = skiplist_next (sl, NULL, (void **) &adb, &cursor);
+             !rc; rc = skiplist_next (sl, NULL, (void **) &adb, &cursor))
+          {
+
+            if (pPrefix)
+              {
+                if (!prefix_same (pPrefix, &adb->prefix_ip))
+                  {
+#if DEBUG_L2_EXTRA
+                    zlog_debug ("%s: adb=%p, prefix doesn't match, skipping",
+                                __func__, adb);
+#endif
+                    continue;
+                  }
+              }
+            if (CHECK_FLAG (cda->l2o.flags, RFAPI_L2O_MACADDR))
+              {
+                if (memcmp
+                    (cda->l2o.o.macaddr.octet,
+                     adb->prefix_eth.u.prefix_eth.octet, ETHER_ADDR_LEN))
+                  {
+#if DEBUG_L2_EXTRA
+                    zlog_debug ("%s: adb=%p, macaddr doesn't match, skipping",
+                                __func__, adb);
+#endif
+                    continue;
+                  }
+              }
+
+            if (CHECK_FLAG (cda->l2o.flags, RFAPI_L2O_LNI))
+              {
+                if (cda->l2o.o.logical_net_id != adb->l2o.logical_net_id)
+                  {
+#if DEBUG_L2_EXTRA
+                    zlog_debug ("%s: adb=%p, LNI doesn't match, skipping",
+                                __func__, adb);
+#endif
+                    continue;
+                  }
+              }
+
+#if DEBUG_L2_EXTRA
+            zlog_debug ("%s: ipN adding adb %p to delete list", __func__,
+                        adb);
+#endif
+
+            listnode_add (adb_delete_list, adb);
+          }
+
+        struct listnode *node;
+
+        for (ALL_LIST_ELEMENTS_RO (adb_delete_list, node, adb))
+          {
+
+            struct rfapi_vn_option vn1;
+            struct rfapi_vn_option vn2;
+            struct rfapi_vn_option *pVn;
+            int this_advertisement_prefix_count;
+
+            this_advertisement_prefix_count = 1;
+
+            rfapiQprefix2Rprefix (&adb->prefix_ip, &rp);
+
+            /* if mac addr present in advert,  make l2o vn option */
+            if (adb->prefix_eth.family == AF_ETHERNET)
+              {
+
+                memset (&vn1, 0, sizeof (vn1));
+                memset (&vn2, 0, sizeof (vn2));
+
+                vn1.type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+                vn1.v.l2addr.macaddr = adb->prefix_eth.u.prefix_eth;
+
+                /*
+                 * use saved RD value instead of trying to invert
+                 * complex L2-style RD computation in rfapi_register()
+                 */
+                vn2.type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD;
+                vn2.v.internal_rd = adb->prd;
+
+                vn1.next = &vn2;
+
+                pVn = &vn1;
+                ++this_advertisement_prefix_count;
+              }
+            else
+              {
+                pVn = NULL;
+              }
+
+#if DEBUG_L2_EXTRA
+            zlog_debug ("%s: ipN killing reg from adb %p ", __func__, adb);
+#endif
+
+            rc = rfapi_register (rfd, &rp, 0, NULL, pVn, RFAPI_REGISTER_KILL);
+            if (!rc)
+              {
+                cda->pfx_count += this_advertisement_prefix_count;
+                cda->reg_count += 1;
+                deleted_from_this_nve = 1;
+              }
+            if (h->rfp_methods.local_cb)
+              {
+                rfapiAddDeleteLocalRfpPrefix (&rfd->un_addr, &rfd->vn_addr,
+                                              &rp, 0, 0, NULL, &head, &tail);
+              }
+          }
+        list_delete_all_node (adb_delete_list);
+
+        if (!(pPrefix && !RFAPI_0_PREFIX (pPrefix)))
+          {
+            void *cursor;
+
+            /*
+             * Caller didn't specify a prefix, or specified (0/32 or 0/128)
+             */
+
+            /*
+             * Advertised 0/32 and 0/128 (indexed by ethernet address)
+             */
+            sl = rfd->advertised.ip0_by_ether;
+
+            for (cursor = NULL,
+                 rc = skiplist_next (sl, NULL, (void **) &adb, &cursor);
+                 !rc; rc = skiplist_next (sl, NULL, (void **) &adb, &cursor))
+              {
+
+                if (CHECK_FLAG (cda->l2o.flags, RFAPI_L2O_MACADDR))
+                  {
+                    if (memcmp (cda->l2o.o.macaddr.octet,
+                                adb->prefix_eth.u.prefix_eth.octet,
+                                ETHER_ADDR_LEN))
+                      {
+
+                        continue;
+                      }
+                  }
+                if (CHECK_FLAG (cda->l2o.flags, RFAPI_L2O_LNI))
+                  {
+                    if (cda->l2o.o.logical_net_id != adb->l2o.logical_net_id)
+                      {
+                        continue;
+                      }
+                  }
+#if DEBUG_L2_EXTRA
+                zlog_debug ("%s: ip0 adding adb %p to delete list",
+                            __func__, adb);
+#endif
+                listnode_add (adb_delete_list, adb);
+              }
+
+
+            for (ALL_LIST_ELEMENTS_RO (adb_delete_list, node, adb))
+              {
+
+                struct rfapi_vn_option vn;
+
+                rfapiQprefix2Rprefix (&adb->prefix_ip, &rp);
+
+                memset (&vn, 0, sizeof (vn));
+                vn.type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+                vn.v.l2addr = adb->l2o;
+
+#if DEBUG_L2_EXTRA
+                zlog_debug ("%s: ip0 killing reg from adb %p ",
+                            __func__, adb);
+#endif
+
+                rc = rfapi_register (rfd, &rp, 0, NULL, &vn,
+                                     RFAPI_REGISTER_KILL);
+                if (!rc)
+                  {
+                    cda->pfx_count += 1;
+                    cda->reg_count += 1;
+                    deleted_from_this_nve = 1;
+                  }
+                if (h->rfp_methods.local_cb)
+                  {
+                    struct rfapi_vn_option *vn_opt_new;
+
+                    vn_opt_new = rfapi_vn_options_dup (&vn);
+                    rfapiAddDeleteLocalRfpPrefix (&rfd->un_addr,
+                                                  &rfd->vn_addr, &rp, 0, 0,
+                                                  vn_opt_new, &head, &tail);
+                  }
+              }
+            list_delete_all_node (adb_delete_list);
+          }
+        list_delete (adb_delete_list);
+      }
+
+
+      if (head)
+        {                       /* should not be set if (NULL == rfapi_cfg->local_cb) */
+          h->flags |= RFAPI_INCALLBACK;
+          (*h->rfp_methods.local_cb) (head, rfd->cookie);
+          h->flags &= ~RFAPI_INCALLBACK;
+          head = tail = NULL;
+        }
+
+      if (deleted_from_this_nve)
+        {
+          /*
+           * track unique NVEs seen
+           */
+          memset (&ha, 0, sizeof (ha));
+          ha.un = rfd->un_addr;
+          ha.vn = rfd->vn_addr;
+
+          if (!cda->nves)
+            cda->nves = skiplist_new (0, nve_addr_cmp, nve_addr_free);
+          if (skiplist_search (cda->nves, &ha, (void **) &hap))
+            {
+              hap = XCALLOC (MTYPE_RFAPI_NVE_ADDR, sizeof (struct nve_addr));
+              assert (hap);
+              ha.cda = cda;
+              *hap = ha;
+              skiplist_insert (cda->nves, hap, hap);
+            }
+        }
+    }
+
+  return 0;
+}
+
+/*
+ * clear_vnc_prefix
+ *
+ * Deletes local and remote prefixes that match
+ */
+static void
+clear_vnc_prefix (struct rfapi_local_reg_delete_arg *cda)
+{
+  struct prefix pfx_un;
+  struct prefix pfx_vn;
+
+  struct prefix *pUN = NULL;
+  struct prefix *pVN = NULL;
+  struct prefix *pPrefix = NULL;
+
+  /*
+   * Delete matching remote prefixes in holddown
+   */
+  if (cda->vn_address.addr_family)
+    {
+      if (!rfapiRaddr2Qprefix (&cda->vn_address, &pfx_vn))
+        pVN = &pfx_vn;
+    }
+  if (cda->un_address.addr_family)
+    {
+      if (!rfapiRaddr2Qprefix (&cda->un_address, &pfx_un))
+        pUN = &pfx_un;
+    }
+  if (cda->prefix.family)
+    {
+      pPrefix = &cda->prefix;
+    }
+  rfapiDeleteRemotePrefixes (pUN, pVN, pPrefix,
+                             0, 1, &cda->remote_active_pfx_count,
+                             &cda->remote_active_nve_count,
+                             &cda->remote_holddown_pfx_count,
+                             &cda->remote_holddown_nve_count);
+
+  /*
+   * Now do local prefixes
+   */
+  rfapiDeleteLocalPrefixes (cda);
+}
+
+static void
+print_cleared_stats (struct rfapi_local_reg_delete_arg *cda)
+{
+  struct vty *vty = cda->vty;   /* for benefit of VTY_NEWLINE */
+
+  /* Our special element-deleting function counts nves */
+  if (cda->nves)
+    {
+      skiplist_free (cda->nves);
+      cda->nves = NULL;
+    }
+  if (cda->failed_pfx_count)
+      vty_out (vty, "Failed to delete %d prefixes%s",
+               cda->failed_pfx_count, VTY_NEWLINE);
+
+  /* left as "prefixes" even in single case for ease of machine parsing */
+  vty_out (vty,
+           "[Local] Cleared %u registrations, %u prefixes, %u responses from %d NVEs%s",
+           cda->reg_count, cda->pfx_count, cda->query_count, cda->nve_count,
+           VTY_NEWLINE);
+
+/*
+ * We don't currently allow deletion of active remote prefixes from
+ * the command line
+ */
+
+  vty_out (vty, "[Holddown] Cleared %u prefixes from %u NVEs%s",
+           cda->remote_holddown_pfx_count, cda->remote_holddown_nve_count,
+           VTY_NEWLINE);
+}
+
+/* 
+ * Caller has already deleted registrations and queries for this/these
+ * NVEs. Now we just have to close their descriptors.
+ */
+static void
+clear_vnc_nve_closer (struct rfapi_local_reg_delete_arg *cda)
+{
+  struct skiplist *sl = cda->nves;      /* contains affected NVEs */
+  struct nve_addr *pKey;
+  struct nve_addr *pValue;
+  void *cursor = NULL;
+  int rc;
+
+  if (!sl)
+      return;
+
+  for (rc = skiplist_next (sl, (void **) &pKey, (void **) &pValue, &cursor);
+       !rc;
+       rc = skiplist_next (sl, (void **) &pKey, (void **) &pValue, &cursor))
+    {
+
+      if (pValue->rfd)
+        {
+          ((struct rfapi_descriptor *) pValue->rfd)->flags |=
+            RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY;
+          rfapi_close (pValue->rfd);
+        }
+    }
+}
+
+DEFUN (clear_vnc_nve_all,
+       clear_vnc_nve_all_cmd,
+       "clear vnc nve *",
+       "clear\n"
+       "VNC Information\n" "Clear per NVE information\n" "For all NVEs\n")
+{
+
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  if ((rc = parse_deleter_args (vty, NULL, NULL, NULL, NULL, NULL, &cda)))
+    return rc;
+
+  cda.vty = vty;
+
+  clear_vnc_responses (&cda);
+  clear_vnc_prefix (&cda);
+  clear_vnc_nve_closer (&cda);
+
+  print_cleared_stats (&cda);
+
+  return 0;
+}
+
+DEFUN (clear_vnc_nve_vn_un,
+       clear_vnc_nve_vn_un_cmd,
+       "clear vnc nve vn (*|A.B.C.D|X:X::X:X) un (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear prefix registration infomation\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n" "UN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  if ((rc =
+       parse_deleter_args (vty, NULL, argv[0], argv[1], NULL, NULL, &cda)))
+    return rc;
+
+  cda.vty = vty;
+
+  clear_vnc_responses (&cda);
+  clear_vnc_prefix (&cda);
+  clear_vnc_nve_closer (&cda);
+
+  print_cleared_stats (&cda);
+
+  return 0;
+}
+
+DEFUN (clear_vnc_nve_un_vn,
+       clear_vnc_nve_un_vn_cmd,
+       "clear vnc nve un (*|A.B.C.D|X:X::X:X) vn (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear prefix registration infomation\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n" "VN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  if ((rc =
+       parse_deleter_args (vty, NULL, argv[1], argv[0], NULL, NULL, &cda)))
+    return rc;
+
+  cda.vty = vty;
+
+  clear_vnc_responses (&cda);
+  clear_vnc_prefix (&cda);
+  clear_vnc_nve_closer (&cda);
+
+  print_cleared_stats (&cda);
+
+  return 0;
+}
+
+DEFUN (clear_vnc_nve_vn,
+       clear_vnc_nve_vn_cmd,
+       "clear vnc nve vn (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear prefix registration infomation\n"
+       "VN address of NVE\n"
+       "VN IPv4 interface address\n" "VN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  if ((rc = parse_deleter_args (vty, NULL, argv[0], NULL, NULL, NULL, &cda)))
+    return rc;
+
+  cda.vty = vty;
+
+  clear_vnc_responses (&cda);
+  clear_vnc_prefix (&cda);
+  clear_vnc_nve_closer (&cda);
+
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_nve_un,
+       clear_vnc_nve_un_cmd,
+       "clear vnc nve un (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear prefix registration infomation\n"
+       "UN address of NVE\n"
+       "UN IPv4 interface address\n" "UN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  if ((rc = parse_deleter_args (vty, NULL, NULL, argv[0], NULL, NULL, &cda)))
+    return rc;
+
+  cda.vty = vty;
+
+  clear_vnc_responses (&cda);
+  clear_vnc_prefix (&cda);
+  clear_vnc_nve_closer (&cda);
+
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+/*-------------------------------------------------
+ *             Clear VNC Prefix
+ *-------------------------------------------------*/
+
+/*
+ * This function is defined in this file (rather than in rfp_registration.c)
+ * because here we have access to all the task handles.
+ */
+DEFUN (clear_vnc_prefix_vn_un,
+       clear_vnc_prefix_vn_un_cmd,
+       "clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) vn (*|A.B.C.D|X:X::X:X) un (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear prefix registration infomation\n"
+       "All prefixes\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "VN address of NVE\n"
+       "All VN addresses\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "All UN addresses\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  if ((rc =
+       parse_deleter_args (vty, argv[0], argv[1], argv[2], NULL, NULL, &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_prefix_un_vn,
+       clear_vnc_prefix_un_vn_cmd,
+       "clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) un (*|A.B.C.D|X:X::X:X) vn (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear prefix registration infomation\n"
+       "All prefixes\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "UN address of NVE\n"
+       "All UN addresses\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "VN address of NVE\n"
+       "All VN addresses\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  if ((rc =
+       parse_deleter_args (vty, argv[0], argv[2], argv[1], NULL, NULL, &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_prefix_un,
+       clear_vnc_prefix_un_cmd,
+       "clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) un (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear prefix registration infomation\n"
+       "All prefixes\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "UN address of NVE\n"
+       "All UN addresses\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  if ((rc =
+       parse_deleter_args (vty, argv[0], NULL, argv[1], NULL, NULL, &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_prefix_vn,
+       clear_vnc_prefix_vn_cmd,
+       "clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) vn (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear prefix registration infomation\n"
+       "All prefixes\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "UN address of NVE\n"
+       "All VN addresses\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  if ((rc =
+       parse_deleter_args (vty, argv[0], argv[1], NULL, NULL, NULL, &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_prefix_all,
+       clear_vnc_prefix_all_cmd,
+       "clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) *",
+       "clear\n"
+       "VNC Information\n"
+       "Clear prefix registration infomation\n"
+       "All prefixes\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n"
+       "From any NVE\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  if ((rc = parse_deleter_args (vty, argv[0], NULL, NULL, NULL, NULL, &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+/*-------------------------------------------------
+ *             Clear VNC MAC
+ *-------------------------------------------------*/
+
+/*
+ * This function is defined in this file (rather than in rfp_registration.c)
+ * because here we have access to all the task handles.
+ */
+DEFUN (clear_vnc_mac_vn_un,
+       clear_vnc_mac_vn_un_cmd,
+       "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) vn (*|A.B.C.D|X:X::X:X) un (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear mac registration infomation\n"
+       "All macs\n"
+       "MAC address\n"
+       "VNI keyword\n"
+       "Any virtual network identifier\n"
+       "Virtual network identifier\n"
+       "Virtual network identifier\n"
+       "VN address of NVE\n"
+       "All VN addresses\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "All UN addresses\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  /* pfx vn un L2 VNI */
+  if ((rc =
+       parse_deleter_args (vty, NULL, argv[2], argv[3], argv[0], argv[1],
+                           &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_mac_un_vn,
+       clear_vnc_mac_un_vn_cmd,
+       "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) un (*|A.B.C.D|X:X::X:X) vn (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear mac registration infomation\n"
+       "All macs\n"
+       "MAC address\n"
+       "VNI keyword\n"
+       "Any virtual network identifier\n"
+       "Virtual network identifier\n"
+       "UN address of NVE\n"
+       "All UN addresses\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "VN address of NVE\n"
+       "All VN addresses\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  /* pfx vn un L2 VNI */
+  if ((rc =
+       parse_deleter_args (vty, NULL, argv[3], argv[2], argv[0], argv[1],
+                           &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_mac_un,
+       clear_vnc_mac_un_cmd,
+       "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) un (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear mac registration infomation\n"
+       "All macs\n"
+       "MAC address\n"
+       "VNI keyword\n"
+       "Any virtual network identifier\n"
+       "Virtual network identifier\n"
+       "UN address of NVE\n"
+       "All UN addresses\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  /* pfx vn un L2 VNI */
+  if ((rc =
+       parse_deleter_args (vty, NULL, NULL, argv[2], argv[0], argv[1], &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_mac_vn,
+       clear_vnc_mac_vn_cmd,
+       "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) vn (*|A.B.C.D|X:X::X:X)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear mac registration infomation\n"
+       "All macs\n"
+       "MAC address\n"
+       "VNI keyword\n"
+       "Any virtual network identifier\n"
+       "Virtual network identifier\n"
+       "UN address of NVE\n"
+       "All VN addresses\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  /* pfx vn un L2 VNI */
+  if ((rc =
+       parse_deleter_args (vty, NULL, argv[2], NULL, argv[0], argv[1], &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_mac_all,
+       clear_vnc_mac_all_cmd,
+       "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) *",
+       "clear\n"
+       "VNC Information\n"
+       "Clear mac registration infomation\n"
+       "All macs\n"
+       "MAC address\n"
+       "VNI keyword\n"
+       "Any virtual network identifier\n"
+       "Virtual network identifier\n"
+       "From any NVE\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  /* pfx vn un L2 VNI */
+  if ((rc =
+       parse_deleter_args (vty, NULL, NULL, NULL, argv[0], argv[1], &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+/*-------------------------------------------------
+ *             Clear VNC MAC PREFIX
+ *-------------------------------------------------*/
+
+DEFUN (clear_vnc_mac_vn_un_prefix,
+       clear_vnc_mac_vn_un_prefix_cmd,
+       "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) vn (*|A.B.C.D|X:X::X:X) un (*|A.B.C.D|X:X::X:X) prefix (*|A.B.C.D/M|X:X::X:X/M)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear mac registration infomation\n"
+       "All macs\n"
+       "MAC address\n"
+       "VNI keyword\n"
+       "Any virtual network identifier\n"
+       "Virtual network identifier\n"
+       "Virtual network identifier\n"
+       "VN address of NVE\n"
+       "All VN addresses\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n"
+       "UN address of NVE\n"
+       "All UN addresses\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "Clear prefix registration infomation\n"
+       "All prefixes\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  /* pfx vn un L2 VNI */
+  if ((rc =
+       parse_deleter_args (vty, argv[4], argv[2], argv[3], argv[0], argv[1],
+                           &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_mac_un_vn_prefix,
+       clear_vnc_mac_un_vn_prefix_cmd,
+       "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) un (*|A.B.C.D|X:X::X:X) vn (*|A.B.C.D|X:X::X:X) prefix (*|A.B.C.D/M|X:X::X:X/M) prefix (*|A.B.C.D/M|X:X::X:X/M)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear mac registration infomation\n"
+       "All macs\n"
+       "MAC address\n"
+       "VNI keyword\n"
+       "Any virtual network identifier\n"
+       "Virtual network identifier\n"
+       "UN address of NVE\n"
+       "All UN addresses\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n"
+       "VN address of NVE\n"
+       "All VN addresses\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  /* pfx vn un L2 VNI */
+  if ((rc =
+       parse_deleter_args (vty, argv[4], argv[3], argv[2], argv[0], argv[1],
+                           &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_mac_un_prefix,
+       clear_vnc_mac_un_prefix_cmd,
+       "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) un (*|A.B.C.D|X:X::X:X) prefix (*|A.B.C.D/M|X:X::X:X/M)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear mac registration infomation\n"
+       "All macs\n"
+       "MAC address\n"
+       "VNI keyword\n"
+       "Any virtual network identifier\n"
+       "Virtual network identifier\n"
+       "UN address of NVE\n"
+       "All UN addresses\n"
+       "UN IPv4 interface address\n"
+       "UN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  /* pfx vn un L2 VNI */
+  if ((rc =
+       parse_deleter_args (vty, argv[3], NULL, argv[2], argv[0], argv[1],
+                           &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_mac_vn_prefix,
+       clear_vnc_mac_vn_prefix_cmd,
+       "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) vn (*|A.B.C.D|X:X::X:X) prefix (*|A.B.C.D/M|X:X::X:X/M)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear mac registration infomation\n"
+       "All macs\n"
+       "MAC address\n"
+       "VNI keyword\n"
+       "Any virtual network identifier\n"
+       "Virtual network identifier\n"
+       "UN address of NVE\n"
+       "All VN addresses\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  /* pfx vn un L2 VNI */
+  if ((rc =
+       parse_deleter_args (vty, argv[3], argv[2], NULL, argv[0], argv[1],
+                           &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+DEFUN (clear_vnc_mac_all_prefix,
+       clear_vnc_mac_all_prefix_cmd,
+       "clear vnc mac (*|YY:YY:YY:YY:YY:YY) virtual-network-identifier (*|<1-4294967295>) prefix (*|A.B.C.D/M|X:X::X:X/M)",
+       "clear\n"
+       "VNC Information\n"
+       "Clear mac registration infomation\n"
+       "All macs\n"
+       "MAC address\n"
+       "VNI keyword\n"
+       "Any virtual network identifier\n"
+       "Virtual network identifier\n"
+       "UN address of NVE\n"
+       "All VN addresses\n"
+       "VN IPv4 interface address\n"
+       "VN IPv6 interface address\n")
+{
+  struct rfapi_local_reg_delete_arg cda;
+  int rc;
+
+  /* pfx vn un L2 VNI */
+  if ((rc =
+       parse_deleter_args (vty, argv[2], NULL, NULL, argv[0], argv[1], &cda)))
+    return rc;
+  cda.vty = vty;
+  clear_vnc_prefix (&cda);
+  print_cleared_stats (&cda);
+  return 0;
+}
+
+/************************************************************************
+ *                     Show commands
+ ************************************************************************/
+
+
+/* copied from rfp_vty.c */
+static int
+check_and_display_is_vnc_running (struct vty *vty)
+{
+  if (!bgp_rfapi_is_vnc_configured (NULL))
+    return 1;                   /* is running */
+
+  if (vty)
+    {
+      vty_out (vty,
+               "VNC is not configured. (There are no configured BGP VPN SAFI peers.)%s",
+               VTY_NEWLINE);
+    }
+  return 0;                     /* not running */
+}
+
+static int
+rfapi_vty_show_nve_summary (struct vty *vty, show_nve_summary_t show_type)
+{
+  struct bgp *bgp_default = bgp_get_default ();
+  struct rfapi *h;
+  int is_vnc_running = !bgp_rfapi_is_vnc_configured (bgp_default);
+
+  int active_local_routes;
+  int active_remote_routes;
+  int holddown_remote_routes;
+  int imported_remote_routes;
+
+  if (!bgp_default)
+      goto notcfg;
+
+    h = bgp_default->rfapi;
+
+  if (!h)
+      goto notcfg;
+
+  /* don't show local info if not running RFP */
+  if (is_vnc_running || show_type == SHOW_NVE_SUMMARY_REGISTERED)
+    {
+
+      switch (show_type)
+        {
+
+        case SHOW_NVE_SUMMARY_ACTIVE_NVES:
+          vty_out (vty, "%-24s ", "NVEs:");
+          vty_out (vty, "%-8s %-8u ", "Active:", h->descriptors.count);
+          vty_out (vty, "%-8s %-8u ", "Maximum:", h->stat.max_descriptors);
+          vty_out (vty, "%-8s %-8u", "Unknown:", h->stat.count_unknown_nves);
+          break;
+
+          case SHOW_NVE_SUMMARY_REGISTERED:
+            /*
+             * NB: With the introduction of L2 route support, we no
+             * longer have a one-to-one correspondence between
+             * locally-originated route advertisements and routes in
+             * the import tables that have local origin. This
+             * discrepancy arises because a single advertisement
+             * may contain both an IP prefix and a MAC address.
+             * Such an advertisement results in two import table
+             * entries: one indexed by IP prefix, the other indexed
+             * by MAC address.
+             *
+             * TBD: update computation and display of registration
+             * statistics to reflect the underlying semantics.
+             */
+          if (is_vnc_running)
+            {
+              vty_out (vty, "%-24s ", "Registrations:");
+              vty_out (vty, "%-8s %-8u ", "Active:",
+                       rfapiApCountAll (bgp_default));
+              vty_out (vty, "%-8s %-8u ", "Failed:",
+                       h->stat.count_registrations_failed);
+              vty_out (vty, "%-8s %-8u", "Total:",
+                       h->stat.count_registrations);
+              vty_out (vty, "%s", VTY_NEWLINE);
+            }
+          vty_out (vty, "%-24s ", "Prefixes registered:");
+          vty_out (vty, "%s", VTY_NEWLINE);
+
+          rfapiCountAllItRoutes (&active_local_routes,
+                                 &active_remote_routes,
+                                 &holddown_remote_routes,
+                                 &imported_remote_routes);
+
+          /* local */
+          if (is_vnc_running)
+            {
+              vty_out (vty, "    %-20s ", "Locally:");
+              vty_out (vty, "%-8s %-8u ", "Active:", active_local_routes);
+              vty_out (vty, "%s", VTY_NEWLINE);
+            }
+
+
+          vty_out (vty, "    %-20s ", "Remotely:");
+          vty_out (vty, "%-8s %-8u", "Active:", active_remote_routes);
+          vty_out (vty, "%s", VTY_NEWLINE);
+          vty_out (vty, "    %-20s ", "In Holddown:");
+          vty_out (vty, "%-8s %-8u", "Active:", holddown_remote_routes);
+          vty_out (vty, "%s", VTY_NEWLINE);
+          vty_out (vty, "    %-20s ", "Imported:");
+          vty_out (vty, "%-8s %-8u", "Active:", imported_remote_routes);
+          break;
+
+        case SHOW_NVE_SUMMARY_QUERIES:
+          vty_out (vty, "%-24s ", "Queries:");
+          vty_out (vty, "%-8s %-8u ", "Active:", rfapi_monitor_count (NULL));
+          vty_out (vty, "%-8s %-8u ", "Failed:",
+                   h->stat.count_queries_failed);
+          vty_out (vty, "%-8s %-8u", "Total:", h->stat.count_queries);
+          break;
+
+        case SHOW_NVE_SUMMARY_RESPONSES:
+          rfapiRibShowResponsesSummary (vty);
+
+        default:
+          break;
+        }
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+  return 0;
+
+notcfg:
+  vty_out (vty, "VNC is not configured.%s", VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+static int
+rfapi_show_nves (
+  struct vty   *vty,
+  struct prefix        *vn_prefix,
+  struct prefix        *un_prefix)
+{
+  //struct hash                      *rfds;
+  //struct rfp_rfapi_descriptor_param param;
+
+  struct bgp *bgp_default = bgp_get_default ();
+  struct rfapi *h;
+  struct listnode *node;
+  struct rfapi_descriptor *rfd;
+
+  int total = 0;
+  int printed = 0;
+  int rc;
+
+  if (!bgp_default)
+      goto notcfg;
+
+    h = bgp_default->rfapi;
+
+  if (!h)
+      goto notcfg;
+
+    rc = rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_ACTIVE_NVES);
+  if (rc)
+      return rc;
+
+  for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd))
+    {
+      struct prefix pfx;
+      char vn_addr_buf[INET6_ADDRSTRLEN] =
+      {
+      0,};
+      char un_addr_buf[INET6_ADDRSTRLEN] =
+      {
+      0,};
+      char age[10];
+
+      ++total;
+
+      if (vn_prefix)
+        {
+          assert (!rfapiRaddr2Qprefix (&rfd->vn_addr, &pfx));
+          if (!prefix_match (vn_prefix, &pfx))
+            continue;
+        }
+
+      if (un_prefix)
+        {
+          assert (!rfapiRaddr2Qprefix (&rfd->un_addr, &pfx));
+          if (!prefix_match (un_prefix, &pfx))
+            continue;
+        }
+
+      rfapiRfapiIpAddr2Str (&rfd->vn_addr, vn_addr_buf, INET6_ADDRSTRLEN);
+      rfapiRfapiIpAddr2Str (&rfd->un_addr, un_addr_buf, INET6_ADDRSTRLEN);
+
+      if (!printed)
+        {
+          /* print out a header */
+          vty_out (vty, "                                "
+                   "Active      Next Hops%s", VTY_NEWLINE);
+          vty_out (vty, "%-15s %-15s %-5s %-5s %-6s %-6s %s%s",
+                   "VN Address",
+                   "UN Address",
+                   "Regis", "Resps", "Reach", "Remove", "Age", VTY_NEWLINE);
+        }
+
+      ++printed;
+
+      vty_out (vty, "%-15s %-15s %-5u %-5u %-6u %-6u %s%s",
+               vn_addr_buf,
+               un_addr_buf,
+               rfapiApCount (rfd),
+               rfapi_monitor_count (rfd),
+               rfd->stat_count_nh_reachable,
+               rfd->stat_count_nh_removal,
+               rfapiFormatAge (rfd->open_time, age, 10), VTY_NEWLINE);
+    }
+
+  if (printed > 0 || vn_prefix || un_prefix)
+    vty_out (vty, "Displayed %d out of %d active NVEs%s",
+             printed, total, VTY_NEWLINE);
+
+  return 0;
+
+notcfg:
+  vty_out (vty, "VNC is not configured.%s", VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+
+DEFUN (vnc_show_summary,
+       vnc_show_summary_cmd,
+       "show vnc summary",
+       SHOW_STR
+       VNC_SHOW_STR
+       "Display VNC status summary\n")
+{
+  if (!check_and_display_is_vnc_running (vty))
+    return CMD_SUCCESS;
+  bgp_rfapi_show_summary (bgp_get_default (), vty);
+  vty_out (vty, "%s", VTY_NEWLINE);
+  rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_ACTIVE_NVES);
+  rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_QUERIES);
+  rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_RESPONSES);
+  rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_REGISTERED);
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_show_nves,
+       vnc_show_nves_cmd,
+       "show vnc nves",
+       SHOW_STR
+       VNC_SHOW_STR
+       "List known NVEs\n")
+{
+  rfapi_show_nves (vty, NULL, NULL);
+  return CMD_SUCCESS;
+}
+
+DEFUN (vnc_show_nves_ptct,
+       vnc_show_nves_ptct_cmd,
+       "show vnc nves (vn|un) (A.B.C.D|X:X::X:X)",
+       SHOW_STR
+       VNC_SHOW_STR
+       "List known NVEs\n"
+       "VN address of NVE\n"
+       "UN address of NVE\n"
+       "IPv4 interface address\n"
+       "IPv6 interface address\n")
+{
+  struct prefix pfx;
+
+  if (!check_and_display_is_vnc_running (vty))
+    return CMD_SUCCESS;
+
+  if (!str2prefix (argv[1], &pfx))
+    {
+      vty_out (vty, "Malformed address \"%s\"%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (pfx.family != AF_INET && pfx.family != AF_INET6)
+    {
+      vty_out (vty, "Invalid address \"%s\"%s", argv[1], VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (*(argv[0]) == 'u')
+    {
+      rfapi_show_nves (vty, NULL, &pfx);
+    }
+  else
+    {
+      rfapi_show_nves (vty, &pfx, NULL);
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* adapted from rfp_registration_cache_log() */
+static void
+rfapi_show_registrations (
+  struct vty   *vty,
+  struct prefix        *restrict_to,
+  int          show_local,
+  int          show_remote,
+  int          show_holddown,
+  int          show_imported)
+{
+  int printed = 0;
+
+  if (!vty)
+      return;
+
+    rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_REGISTERED);
+
+  if (show_local)
+    {
+      /* non-expiring, local */
+      printed += rfapiShowRemoteRegistrations (vty, restrict_to, 0, 1, 0, 0);
+    }
+  if (show_remote)
+    {
+      /* non-expiring, non-local */
+      printed += rfapiShowRemoteRegistrations (vty, restrict_to, 0, 0, 1, 0);
+    }
+  if (show_holddown)
+    {
+      /* expiring, including local */
+      printed += rfapiShowRemoteRegistrations (vty, restrict_to, 1, 1, 1, 0);
+    }
+  if (show_imported)
+    {
+      /* non-expiring, non-local */
+      printed += rfapiShowRemoteRegistrations (vty, restrict_to, 0, 0, 1, 1);
+    }
+  if (!printed)
+    {
+      vty_out (vty, "%s", VTY_NEWLINE);
+    }
+}
+
+DEFUN (vnc_show_registrations_pfx,
+       vnc_show_registrations_pfx_cmd,
+       "show vnc registrations ([A.B.C.D/M]|[X:X::X:X/M]|[YY:YY:YY:YY:YY:YY])",
+       SHOW_STR
+       VNC_SHOW_STR
+       "List active prefix registrations\n"
+       "Limit output to a particular prefix or address\n"
+       "Limit output to a particular prefix or address\n")
+{
+  struct prefix p;
+  struct prefix *p_addr = NULL;
+
+  if (argc == 1)
+    {
+      if (!str2prefix (argv[0], &p))
+        {
+          vty_out (vty, "Invalid prefix: %s%s", argv[0], VTY_NEWLINE);
+          return CMD_SUCCESS;
+        }
+      else
+        {
+          p_addr = &p;
+        }
+    }
+
+  rfapi_show_registrations (vty, p_addr, 1, 1, 1, 1);
+  return CMD_SUCCESS;
+}
+
+ALIAS (vnc_show_registrations_pfx,
+       vnc_show_registrations_cmd,
+       "show vnc registrations",
+       SHOW_STR
+       VNC_SHOW_STR
+       "List active prefix registrations\n")
+  DEFUN (vnc_show_registrations_some_pfx,
+         vnc_show_registrations_some_pfx_cmd,
+         "show vnc registrations (all|holddown|imported|local|remote) ([A.B.C.D/M]|[X:X::X:X/M]|[YY:YY:YY:YY:YY:YY])",
+         SHOW_STR
+         VNC_SHOW_STR
+         "List active prefix registrations\n"
+         "show all registrations\n"
+         "show only registrations in holddown\n"
+         "show only imported prefixes\n"
+         "show only local registrations\n"
+         "show only remote registrations\n"
+         "Limit output to a particular prefix or address\n"
+         "Limit output to a particular prefix or address\n")
+{
+  struct prefix p;
+  struct prefix *p_addr = NULL;
+
+  int show_local = 0;
+  int show_remote = 0;
+  int show_holddown = 0;
+  int show_imported = 0;
+
+  if (argc == 2)
+    {
+      if (!str2prefix (argv[1], &p))
+        {
+          vty_out (vty, "Invalid prefix: %s%s", argv[1], VTY_NEWLINE);
+          return CMD_SUCCESS;
+        }
+      else
+        {
+          p_addr = &p;
+        }
+    }
+  switch (*argv[0])
+    {
+    case 'a':
+      show_local = 1;
+      show_remote = 1;
+      show_holddown = 1;
+      show_imported = 1;
+      break;
+
+    case 'h':
+      show_holddown = 1;
+      break;
+
+    case 'i':
+      show_imported = 1;
+      break;
+
+    case 'l':
+      show_local = 1;
+      break;
+
+    case 'r':
+      show_remote = 1;
+      break;
+    }
+
+  rfapi_show_registrations (vty, p_addr,
+                            show_local, show_remote, show_holddown,
+                            show_imported);
+  return CMD_SUCCESS;
+}
+
+ALIAS (vnc_show_registrations_some_pfx,
+       vnc_show_registrations_some_cmd,
+       "show vnc registrations (all|holddown|imported|local|remote)",
+       SHOW_STR
+       VNC_SHOW_STR
+       "List active prefix registrations\n"
+       "show all registrations\n"
+       "show only registrations in holddown\n"
+       "show only imported prefixes\n"
+       "show only local registrations\n"
+       "show only remote registrations\n")
+
+DEFUN (vnc_show_responses_pfx,
+         vnc_show_responses_pfx_cmd,
+         "show vnc responses ([A.B.C.D/M]|[X:X::X:X/M]|[YY:YY:YY:YY:YY:YY])",
+         SHOW_STR
+         VNC_SHOW_STR
+         "List recent query responses\n"
+         "Limit output to a particular prefix or address\n"
+         "Limit output to a particular prefix or address\n")
+{
+  struct prefix p;
+  struct prefix *p_addr = NULL;
+
+  if (argc == 1)
+    {
+      if (!str2prefix (argv[0], &p))
+        {
+          vty_out (vty, "Invalid prefix: %s%s", argv[0], VTY_NEWLINE);
+          return CMD_SUCCESS;
+        }
+      else
+        {
+          p_addr = &p;
+        }
+    }
+  rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_QUERIES);
+
+  rfapiRibShowResponsesSummary (vty);
+
+  rfapiRibShowResponses (vty, p_addr, 0);
+  rfapiRibShowResponses (vty, p_addr, 1);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (vnc_show_responses_pfx,
+       vnc_show_responses_cmd,
+       "show vnc responses",
+       SHOW_STR
+       VNC_SHOW_STR
+       "List recent query responses\n")
+
+DEFUN (vnc_show_responses_some_pfx,
+         vnc_show_responses_some_pfx_cmd,
+         "show vnc responses (active|removed) ([A.B.C.D/M]|[X:X::X:X/M]|[YY:YY:YY:YY:YY:YY])",
+         SHOW_STR
+         VNC_SHOW_STR
+         "List recent query responses\n"
+         "show only active query responses\n"
+         "show only removed query responses\n"
+         "Limit output to a particular prefix or address\n"
+         "Limit output to a particular prefix or address\n")
+{
+  struct prefix p;
+  struct prefix *p_addr = NULL;
+
+  int show_active = 0;
+  int show_removed = 0;
+
+  if (!check_and_display_is_vnc_running (vty))
+    return CMD_SUCCESS;
+
+  if (argc == 2)
+    {
+      if (!str2prefix (argv[1], &p))
+        {
+          vty_out (vty, "Invalid prefix: %s%s", argv[1], VTY_NEWLINE);
+          return CMD_SUCCESS;
+        }
+      else
+        {
+          p_addr = &p;
+        }
+    }
+
+  switch (*argv[0])
+    {
+    case 'a':
+      show_active = 1;
+      break;
+
+    case 'r':
+      show_removed = 1;
+      break;
+    }
+
+  rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_QUERIES);
+
+  rfapiRibShowResponsesSummary (vty);
+
+  if (show_active)
+    rfapiRibShowResponses (vty, p_addr, 0);
+  if (show_removed)
+    rfapiRibShowResponses (vty, p_addr, 1);
+
+  return CMD_SUCCESS;
+}
+
+ALIAS (vnc_show_responses_some_pfx,
+       vnc_show_responses_some_cmd,
+       "show vnc responses (active|removed)",
+       SHOW_STR
+       VNC_SHOW_STR
+       "List recent query responses\n"
+       "show only active query responses\n"
+       "show only removed query responses\n")
+
+DEFUN (show_vnc_queries_pfx,
+       show_vnc_queries_pfx_cmd,
+       "show vnc queries ([A.B.C.D/M]|[X:X::X:X/M]|[YY:YY:YY:YY:YY:YY])",
+       SHOW_STR
+       VNC_SHOW_STR
+       "List active queries\n"
+       "Limit output to a particular IPv4 prefix or address\n"
+       "Limit output to a particular IPv6 prefix or address\n")
+{
+  struct prefix pfx;
+  struct prefix *p = NULL;
+
+  if (argc == 1)
+    {
+      if (!str2prefix (argv[0], &pfx))
+        {
+          vty_out (vty, "Invalid prefix: %s%s", argv[0], VTY_NEWLINE);
+          return CMD_WARNING;
+        }
+      p = &pfx;
+    }
+
+  rfapi_vty_show_nve_summary (vty, SHOW_NVE_SUMMARY_QUERIES);
+
+  return rfapiShowVncQueries (vty, p);
+}
+
+ALIAS (show_vnc_queries_pfx,
+       show_vnc_queries_cmd,
+       "show vnc queries",
+       SHOW_STR
+       VNC_SHOW_STR
+       "List active queries\n")
+
+DEFUN (vnc_clear_counters,
+       vnc_clear_counters_cmd,
+       "clear vnc counters",
+       CLEAR_STR
+       VNC_SHOW_STR
+       "Reset VNC counters\n")
+{
+  struct bgp *bgp_default = bgp_get_default ();
+  struct rfapi *h;
+  struct listnode *node;
+  struct rfapi_descriptor *rfd;
+
+  if (!bgp_default)
+    goto notcfg;
+
+  h = bgp_default->rfapi;
+
+  if (!h)
+    goto notcfg;
+
+  /* per-rfd */
+  for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd))
+    {
+      rfd->stat_count_nh_reachable = 0;
+      rfd->stat_count_nh_removal = 0;
+    }
+
+  /* global */
+  memset (&h->stat, 0, sizeof (h->stat));
+
+  /*
+   * 151122 per bug 103, set count_registrations = number active.
+   * Do same for queries
+   */
+  h->stat.count_registrations = rfapiApCountAll (bgp_default);
+  h->stat.count_queries = rfapi_monitor_count (NULL);
+
+  rfapiRibShowResponsesSummaryClear ();
+
+  return CMD_SUCCESS;
+
+notcfg:
+  vty_out (vty, "VNC is not configured.%s", VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+void rfapi_vty_init ()
+{
+  install_element (ENABLE_NODE, &add_vnc_prefix_cost_life_lnh_cmd);
+  install_element (ENABLE_NODE, &add_vnc_prefix_life_cost_lnh_cmd);
+  install_element (ENABLE_NODE, &add_vnc_prefix_cost_lnh_cmd);
+  install_element (ENABLE_NODE, &add_vnc_prefix_life_lnh_cmd);
+  install_element (ENABLE_NODE, &add_vnc_prefix_lnh_cmd);
+
+  install_element (ENABLE_NODE, &add_vnc_prefix_cost_life_cmd);
+  install_element (ENABLE_NODE, &add_vnc_prefix_life_cost_cmd);
+  install_element (ENABLE_NODE, &add_vnc_prefix_cost_cmd);
+  install_element (ENABLE_NODE, &add_vnc_prefix_life_cmd);
+  install_element (ENABLE_NODE, &add_vnc_prefix_cmd);
+
+  install_element (ENABLE_NODE, &add_vnc_mac_vni_prefix_cost_life_cmd);
+  install_element (ENABLE_NODE, &add_vnc_mac_vni_prefix_life_cmd);
+  install_element (ENABLE_NODE, &add_vnc_mac_vni_prefix_cost_cmd);
+  install_element (ENABLE_NODE, &add_vnc_mac_vni_prefix_cmd);
+  install_element (ENABLE_NODE, &add_vnc_mac_vni_cost_life_cmd);
+  install_element (ENABLE_NODE, &add_vnc_mac_vni_cost_cmd);
+  install_element (ENABLE_NODE, &add_vnc_mac_vni_life_cmd);
+  install_element (ENABLE_NODE, &add_vnc_mac_vni_cmd);
+
+  install_element (ENABLE_NODE, &clear_vnc_nve_all_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_nve_vn_un_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_nve_un_vn_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_nve_vn_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_nve_un_cmd);
+
+  install_element (ENABLE_NODE, &clear_vnc_prefix_vn_un_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_prefix_un_vn_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_prefix_un_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_prefix_vn_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_prefix_all_cmd);
+
+  install_element (ENABLE_NODE, &clear_vnc_mac_vn_un_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_mac_un_vn_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_mac_un_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_mac_vn_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_mac_all_cmd);
+
+  install_element (ENABLE_NODE, &clear_vnc_mac_vn_un_prefix_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_mac_un_vn_prefix_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_mac_un_prefix_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_mac_vn_prefix_cmd);
+  install_element (ENABLE_NODE, &clear_vnc_mac_all_prefix_cmd);
+
+  install_element (ENABLE_NODE, &vnc_clear_counters_cmd);
+
+  install_element (VIEW_NODE, &vnc_show_summary_cmd);
+  install_element (ENABLE_NODE, &vnc_show_summary_cmd);
+  install_element (VIEW_NODE, &vnc_show_nves_cmd);
+  install_element (ENABLE_NODE, &vnc_show_nves_cmd);
+  install_element (VIEW_NODE, &vnc_show_nves_ptct_cmd);
+  install_element (ENABLE_NODE, &vnc_show_nves_ptct_cmd);
+
+  install_element (VIEW_NODE, &vnc_show_registrations_cmd);
+  install_element (ENABLE_NODE, &vnc_show_registrations_cmd);
+  install_element (VIEW_NODE, &vnc_show_registrations_pfx_cmd);
+  install_element (ENABLE_NODE, &vnc_show_registrations_pfx_cmd);
+
+  install_element (VIEW_NODE, &vnc_show_registrations_some_cmd);
+  install_element (ENABLE_NODE, &vnc_show_registrations_some_cmd);
+  install_element (VIEW_NODE, &vnc_show_registrations_some_pfx_cmd);
+  install_element (ENABLE_NODE, &vnc_show_registrations_some_pfx_cmd);
+
+  install_element (VIEW_NODE, &vnc_show_responses_cmd);
+  install_element (ENABLE_NODE, &vnc_show_responses_cmd);
+  install_element (VIEW_NODE, &vnc_show_responses_pfx_cmd);
+  install_element (ENABLE_NODE, &vnc_show_responses_pfx_cmd);
+
+  install_element (VIEW_NODE, &vnc_show_responses_some_cmd);
+  install_element (ENABLE_NODE, &vnc_show_responses_some_cmd);
+  install_element (VIEW_NODE, &vnc_show_responses_some_pfx_cmd);
+  install_element (ENABLE_NODE, &vnc_show_responses_some_pfx_cmd);
+
+  install_element (ENABLE_NODE, &show_vnc_queries_cmd);
+  install_element (VIEW_NODE, &show_vnc_queries_cmd);
+  install_element (ENABLE_NODE, &show_vnc_queries_pfx_cmd);
+  install_element (VIEW_NODE, &show_vnc_queries_pfx_cmd);
+}
diff --git a/bgpd/rfapi/rfapi_vty.h b/bgpd/rfapi/rfapi_vty.h
new file mode 100644 (file)
index 0000000..08c8e1c
--- /dev/null
@@ -0,0 +1,223 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef RFAPI_VTY_H
+#define RFAPI_VTY_H
+
+#include "vty.h"
+
+typedef enum
+{
+  SHOW_NVE_SUMMARY_ACTIVE_NVES,
+  SHOW_NVE_SUMMARY_UNKNOWN_NVES,        /* legacy */
+  SHOW_NVE_SUMMARY_REGISTERED,
+  SHOW_NVE_SUMMARY_QUERIES,
+  SHOW_NVE_SUMMARY_RESPONSES,
+  SHOW_NVE_SUMMARY_MAX
+} show_nve_summary_t;
+
+#define VNC_SHOW_STR "VNC information\n"
+
+extern char *
+rfapiFormatSeconds (uint32_t seconds, char *buf, size_t len);
+
+extern char *
+rfapiFormatAge (time_t age, char *buf, size_t len);
+
+extern void
+rfapiRprefixApplyMask (struct rfapi_ip_prefix *rprefix);
+
+extern int
+rfapiQprefix2Raddr (struct prefix *qprefix, struct rfapi_ip_addr *raddr);
+
+extern void
+rfapiQprefix2Rprefix (struct prefix *qprefix,
+                      struct rfapi_ip_prefix *rprefix);
+
+extern int
+rfapiRprefix2Qprefix (struct rfapi_ip_prefix *rprefix,
+                      struct prefix *qprefix);
+
+extern int
+rfapiRaddr2Qprefix (struct rfapi_ip_addr *hia, struct prefix *pfx);
+
+extern int
+rfapiRprefixSame (struct rfapi_ip_prefix *hp1, struct rfapi_ip_prefix *hp2);
+
+extern void
+rfapiL2o2Qprefix (struct rfapi_l2address_option *l2o, struct prefix *pfx);
+
+extern int
+rfapiStr2EthAddr (const char *str, struct ethaddr *ea);
+
+extern const char *
+rfapi_ntop (
+    int                af,
+    const void *src,
+    char       *buf,
+    socklen_t  size);
+
+extern int
+rfapiDebugPrintf (void *dummy, const char *format, ...);
+
+extern int
+rfapiStream2Vty (
+    void       *stream,                                /* input */
+    int                (**fp) (void *, const char *, ...),     /* output */
+    struct vty **vty,                                  /* output */
+    void       **outstream,                            /* output */
+    const char **vty_newline);                         /* output */
+
+/*------------------------------------------
+ * rfapiRfapiIpAddr2Str
+ *
+ * UI helper: generate string from rfapi_ip_addr
+ *
+ * input: 
+ *     a                       IP v4/v6 address
+ *
+ * output
+ *     buf                     put string here
+ *     bufsize                 max space to write
+ *
+ * return value:
+ *     NULL                    conversion failed
+ *     non-NULL                pointer to buf
+ --------------------------------------------*/
+extern const char *
+rfapiRfapiIpAddr2Str (struct rfapi_ip_addr *a, char *buf, int bufsize);
+
+extern void
+rfapiPrintRfapiIpAddr (void *stream, struct rfapi_ip_addr *a);
+
+extern void
+rfapiPrintRfapiIpPrefix (void *stream, struct rfapi_ip_prefix *p);
+
+void
+rfapiPrintRd (struct vty *vty, struct prefix_rd *prd);
+
+extern void
+rfapiPrintAdvertisedInfo (
+    struct vty                 *vty,
+    struct rfapi_descriptor    *rfd,
+    safi_t                     safi,
+    struct prefix              *p);
+
+extern void
+rfapiPrintDescriptor (struct vty *vty, struct rfapi_descriptor *rfd);
+
+extern void
+rfapiPrintMatchingDescriptors (struct vty *vty,
+                               struct prefix *vn_prefix,
+                               struct prefix *un_prefix);
+
+extern void
+rfapiPrintAttrPtrs (void *stream, struct attr *attr);
+
+/*
+ * Parse an address and put into a struct prefix
+ */
+extern int
+rfapiCliGetPrefixAddr (struct vty *vty, const char *str, struct prefix *p);
+
+extern int
+rfapiCliGetRfapiIpAddr (
+    struct vty                 *vty,
+    const char                 *str,
+    struct rfapi_ip_addr       *hai);
+
+extern void
+rfapiPrintNhl (void *stream, struct rfapi_next_hop_entry *next_hops);
+
+extern char *
+rfapiMonitorVpn2Str (
+    struct rfapi_monitor_vpn   *m,
+    char                       *buf,
+    int                                size);
+
+extern const char *
+rfapiRfapiIpPrefix2Str (
+    struct rfapi_ip_prefix     *p,
+    char                       *buf,
+    int                                bufsize);
+
+extern void
+rfapiShowItNode (void *stream, struct route_node *rn);
+
+extern char *
+rfapiEthAddr2Str (
+    const struct ethaddr       *ea,
+    char                       *buf,
+   int                         bufsize);
+
+/* install vty commands */
+extern void
+rfapi_vty_init (void);
+
+/*------------------------------------------
+ * rfapiShowRemoteRegistrations
+ *
+ * UI helper: produces the "remote" portion of the output
+ * of "show vnc registrations".
+ *
+ * input: 
+ *     stream          pointer to output stream
+ *     prefix_only     pointer to prefix. If non-NULL, print only registrations
+ *                     matching the specified prefix
+ *     show_expiring   if non-zero, show expiring registrations
+ *     show_local      if non-zero, show local registrations
+ *     show_imported   if non-zero, show imported registrations
+ *
+ * return value:
+ *     0               nothing printed
+ *     >0              something printed
+ --------------------------------------------*/
+extern int
+rfapiShowRemoteRegistrations (
+    void               *stream,
+    struct prefix      *prefix_only,
+    int                        show_expiring,
+    int                        show_local,
+    int                        show_remote,
+    int                        show_imported);
+
+/*------------------------------------------
+ * rfapi_monitor_count
+ *
+ * UI helper: count number of active monitors
+ *
+ * input: 
+ *     handle                  rfapi handle (NULL to count across
+ *                             all open handles)
+ *
+ * output
+ *
+ * return value:
+ *     count of monitors
+ --------------------------------------------*/
+extern uint32_t
+rfapi_monitor_count (rfapi_handle);
+
+extern int
+rfapiShowVncQueries (void *stream, struct prefix *pfx_match);
+
+
+#endif
diff --git a/bgpd/rfapi/vnc_debug.c b/bgpd/rfapi/vnc_debug.c
new file mode 100644 (file)
index 0000000..8f45781
--- /dev/null
@@ -0,0 +1,230 @@
+/* 
+ *
+ * Copyright 2016, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "prefix.h"
+#include "linklist.h"
+#include "stream.h"
+#include "command.h"
+#include "str.h"
+#include "log.h"
+#include "vnc_debug.h"
+
+/*
+ * debug state storage
+ */
+unsigned long conf_vnc_debug;
+unsigned long term_vnc_debug;
+
+struct vnc_debug {
+    unsigned long      bit;
+    const char         *name;
+};
+
+struct vnc_debug       vncdebug[] =
+{
+    {VNC_DEBUG_RFAPI_QUERY,            "rfapi-query"},
+    {VNC_DEBUG_IMPORT_BI_ATTACH,       "import-bi-attach"},
+    {VNC_DEBUG_IMPORT_DEL_REMOTE,      "import-del-remote"},
+    {VNC_DEBUG_EXPORT_BGP_GETCE,       "export-bgp-getce"},
+    {VNC_DEBUG_EXPORT_BGP_DIRECT_ADD,  "export-bgp-direct-add"},
+    {VNC_DEBUG_IMPORT_BGP_ADD_ROUTE,   "import-bgp-add-route"},
+};
+
+#define VNC_STR "VNC information\n"
+
+/***********************************************************************
+ *     debug bgp vnc <foo>
+ ***********************************************************************/
+DEFUN (debug_bgp_vnc,
+       debug_bgp_vnc_cmd,
+       "debug bgp vnc (rfapi-query|import-bi-attach|import-del-remote)",
+       DEBUG_STR
+       BGP_STR
+       VNC_STR
+       "rfapi query handling\n"
+       "import BI atachment\n"
+       "import delete remote routes\n")
+{
+  size_t       i;
+
+  for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i)
+    {
+      if (!strcmp(argv[0], vncdebug[i].name))
+       {
+         if (vty->node == CONFIG_NODE)
+           {
+             conf_vnc_debug |= vncdebug[i].bit;
+             term_vnc_debug |= vncdebug[i].bit;
+           }
+         else
+           {
+             term_vnc_debug |= vncdebug[i].bit;
+             vty_out (vty, "BGP vnc %s debugging is on%s",
+               vncdebug[i].name, VTY_NEWLINE);
+           }
+         return CMD_SUCCESS;
+       }
+    }
+  vty_out (vty, "Unknown debug flag: %s%s", argv[0], VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+DEFUN (no_debug_bgp_vnc,
+       no_debug_bgp_vnc_cmd,
+       "no debug bgp vnc (rfapi-query|import-bi-attach|import-del-remote)",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       VNC_STR
+       "rfapi query handling\n"
+       "import BI atachment\n"
+       "import delete remote routes\n")
+{
+  size_t       i;
+
+  for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i)
+    {
+      if (!strcmp(argv[0], vncdebug[i].name))
+       {
+         if (vty->node == CONFIG_NODE)
+           {
+             conf_vnc_debug &= ~vncdebug[i].bit;
+             term_vnc_debug &= ~vncdebug[i].bit;
+           }
+         else
+           {
+             term_vnc_debug &= ~vncdebug[i].bit;
+             vty_out (vty, "BGP vnc %s debugging is off%s",
+               vncdebug[i].name, VTY_NEWLINE);
+           }
+         return CMD_SUCCESS;
+       }
+    }
+  vty_out (vty, "Unknown debug flag: %s%s", argv[0], VTY_NEWLINE);
+  return CMD_WARNING;
+}
+
+ALIAS (no_debug_bgp_vnc,
+       undebug_bgp_vnc_cmd,
+       "undebug bgp vnc (rfapi-query|import-bi-attach|import-del-remote)",
+       UNDEBUG_STR
+       BGP_STR
+       VNC_STR
+       "rfapi query handling\n"
+       "import BI atachment\n"
+       "import delete remote routes\n")
+
+
+/***********************************************************************
+ *     no debug bgp vnc all
+ ***********************************************************************/
+
+DEFUN (no_debug_bgp_vnc_all,
+       no_debug_bgp_vnc_all_cmd,
+       "no debug all bgp vnc",
+       NO_STR
+       DEBUG_STR
+       "Disable all VNC debugging\n"
+       BGP_STR
+       VNC_STR)
+{
+  term_vnc_debug = 0;
+  vty_out (vty, "All possible VNC debugging has been turned off%s", VTY_NEWLINE);
+      
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_bgp_vnc_all,
+       undebug_bgp_vnc_all_cmd,
+       "undebug all bgp vnc",
+       UNDEBUG_STR
+       "Disable all VNC debugging\n"
+       BGP_STR
+       VNC_STR)
+
+/***********************************************************************
+ *     show/save
+ ***********************************************************************/
+
+DEFUN (show_debugging_bgp_vnc,
+       show_debugging_bgp_vnc_cmd,
+       "show debugging bgp vnc",
+       SHOW_STR
+       DEBUG_STR
+       BGP_STR
+       VNC_STR)
+{
+  size_t       i;
+
+  vty_out (vty, "BGP VNC debugging status:%s", VTY_NEWLINE);
+
+  for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i)
+    {
+      if (term_vnc_debug & vncdebug[i].bit)
+       {
+         vty_out (vty, "  BGP VNC %s debugging is on%s",
+           vncdebug[i].name, VTY_NEWLINE);
+       }
+    }
+  vty_out (vty, "%s", VTY_NEWLINE);
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_vnc_config_write_debug (struct vty *vty)
+{
+  int write = 0;
+  size_t       i;
+
+  for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i)
+    {
+      if (conf_vnc_debug & vncdebug[i].bit)
+       {
+         vty_out (vty, "debug bgp vnc %s%s", vncdebug[i].name, VTY_NEWLINE);
+         write++;
+       }
+    }
+  return write;
+}
+
+static struct cmd_node debug_node =
+{
+  DEBUG_VNC_NODE,
+  "",
+  1
+};
+
+void
+vnc_debug_init (void)
+{
+  install_node (&debug_node, bgp_vnc_config_write_debug);
+  install_element (ENABLE_NODE, &show_debugging_bgp_vnc_cmd);
+
+  install_element (ENABLE_NODE, &debug_bgp_vnc_cmd);
+  install_element (CONFIG_NODE, &debug_bgp_vnc_cmd);
+  install_element (ENABLE_NODE, &no_debug_bgp_vnc_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_vnc_cmd);
+
+  install_element (ENABLE_NODE, &no_debug_bgp_vnc_all_cmd);
+  install_element (ENABLE_NODE, &undebug_bgp_vnc_all_cmd);
+}
diff --git a/bgpd/rfapi/vnc_debug.h b/bgpd/rfapi/vnc_debug.h
new file mode 100644 (file)
index 0000000..9d42706
--- /dev/null
@@ -0,0 +1,49 @@
+/* 
+ *
+ * Copyright 2016, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_BGP_VNC_DEBUG_H
+#define _QUAGGA_BGP_VNC_DEBUG_H
+
+#if ENABLE_BGP_VNC
+
+/*
+ * debug state storage
+ */
+extern unsigned long conf_vnc_debug;
+extern unsigned long term_vnc_debug;
+
+/*
+ * debug flag bits
+ */
+#define VNC_DEBUG_RFAPI_QUERY          0x00000001
+#define VNC_DEBUG_IMPORT_BI_ATTACH     0x00000002
+#define VNC_DEBUG_IMPORT_DEL_REMOTE    0x00000004
+#define VNC_DEBUG_EXPORT_BGP_GETCE     0x00000008
+#define VNC_DEBUG_EXPORT_BGP_DIRECT_ADD        0x00000010
+#define VNC_DEBUG_IMPORT_BGP_ADD_ROUTE 0x00000020
+
+#define VNC_DEBUG(bit) (term_vnc_debug & (VNC_DEBUG_ ## bit))
+
+extern void
+vnc_debug_init (void);
+
+#endif /* ENABLE_BGP_VNC */
+
+#endif /* _QUAGGA_BGP_VNC_DEBUG_H */
diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c
new file mode 100644 (file)
index 0000000..f0a6289
--- /dev/null
@@ -0,0 +1,2177 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/*
+ * File:       vnc_export_bgp.c
+ * Purpose:    Export routes to BGP directly (not via zebra)
+ */
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "log.h"
+#include "stream.h"
+#include "memory.h"
+#include "linklist.h"
+#include "plist.h"
+#include "routemap.h"
+
+#include "bgpd.h"
+#include "bgp_ecommunity.h"
+#include "bgp_attr.h"
+#include "bgp_aspath.h"
+
+#include "vnc_export_bgp.h"
+#include "vnc_export_bgp_p.h"
+#include "vnc_export_table.h"
+#include "bgp_rfapi_cfg.h"
+#include "rfapi.h"
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_backend.h"
+#include "rfapi_vty.h"
+#include "vnc_debug.h"
+
+/***********************************************************************
+ * Export methods that set nexthop to CE (from 5226 roo EC) BEGIN
+ ***********************************************************************/
+
+/*
+ * Memory allocation approach: make a ghost attr that
+ * has non-interned parts for the modifications. ghost attr
+ * memory is allocated by caller.
+ *
+ *     - extract ce (=5226) EC and use as new nexthop
+ *     - strip Tunnel Encap attr
+ *     - copy all ECs
+ */
+static void
+encap_attr_export_ce (
+    struct attr                *new,
+    struct attr                *orig,
+    struct prefix      *use_nexthop)
+{
+  /*
+   * Make "new" a ghost attr copy of "orig"
+   */
+  memset (new, 0, sizeof (struct attr));
+  bgp_attr_dup (new, orig);
+  bgp_attr_extra_get (new);
+  bgp_attr_flush_encap (new);
+
+  /*
+   * Set nexthop
+   */
+  switch (use_nexthop->family)
+    {
+    case AF_INET:
+      new->nexthop = use_nexthop->u.prefix4;
+      new->extra->mp_nexthop_len = 4;   /* bytes */
+      new->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+      break;
+
+    case AF_INET6:
+      if (!new->extra)
+        {
+          new->extra = XCALLOC (MTYPE_ATTR_EXTRA, sizeof (struct attr_extra));
+        }
+      new->extra->mp_nexthop_global = use_nexthop->u.prefix6;
+      new->extra->mp_nexthop_len = 16;  /* bytes */
+      break;
+
+    default:
+      assert (0);
+      break;
+    }
+
+  /*
+   * Set MED
+   *
+   * Note that it will be deleted when BGP sends to any eBGP
+   * peer unless PEER_FLAG_MED_UNCHANGED is set:
+   *
+   *          neighbor NEIGHBOR attribute-unchanged med
+   */
+  if (!CHECK_FLAG (new->flag, BGP_ATTR_MULTI_EXIT_DISC))
+    {
+      if (CHECK_FLAG (new->flag, BGP_ATTR_LOCAL_PREF))
+        {
+          if (new->local_pref > 255)
+            new->med = 0;
+          else
+            new->med = 255 - new->local_pref;
+        }
+      else
+        {
+          new->med = 255;       /* shouldn't happen */
+        }
+      new->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+    }
+
+  /*
+   * "new" is now a ghost attr:
+   *  - it owns an "extra" struct
+   *  - it owns any non-interned parts
+   *  - any references to interned parts are not counted
+   *
+   * Caller should, after using the attr, call:
+   *  - bgp_attr_flush() to free non-interned parts
+   *  - call bgp_attr_extra_free() to free extra
+   */
+}
+
+static int
+getce (struct bgp *bgp, struct attr *attr, struct prefix *pfx_ce)
+{
+  uint8_t *ecp;
+  int i;
+  uint16_t localadmin = bgp->rfapi_cfg->resolve_nve_roo_local_admin;
+
+  for (ecp = attr->extra->ecommunity->val, i = 0;
+       i < attr->extra->ecommunity->size; ++i, ecp += ECOMMUNITY_SIZE)
+    {
+
+      if (VNC_DEBUG(EXPORT_BGP_GETCE))
+       {
+         zlog_debug ("%s: %02x %02x %02x %02x %02x %02x %02x %02x",
+                  __func__,
+                  ecp[0], ecp[1], ecp[2], ecp[3], ecp[4], ecp[5], ecp[6],
+                  ecp[7]);
+       }
+
+      /*
+       * is it ROO?
+       */
+      if (ecp[0] != 1 || ecp[1] != 3)
+        {
+          continue;
+        }
+
+      /*
+       * Match local admin value?
+       */
+      if (ecp[6] != ((localadmin & 0xff00) >> 8) ||
+          ecp[7] != (localadmin & 0xff))
+        continue;
+
+      memset ((uint8_t *) pfx_ce, 0, sizeof (*pfx_ce));
+      memcpy (&pfx_ce->u.prefix4, ecp + 2, 4);
+      pfx_ce->family = AF_INET;
+      pfx_ce->prefixlen = 32;
+
+      return 0;
+    }
+  return -1;
+}
+
+
+void
+vnc_direct_bgp_add_route_ce (
+    struct bgp         *bgp,
+    struct route_node  *rn,
+    struct bgp_info    *bi)
+{
+  struct attr *attr = bi->attr;
+  struct peer *peer = bi->peer;
+  struct prefix *prefix = &rn->p;
+  afi_t afi = family2afi (prefix->family);
+  struct bgp_node *urn;
+  struct bgp_info *ubi;
+  struct attr hattr;
+  struct attr *iattr;
+  struct prefix ce_nexthop;
+  struct prefix post_routemap_nexthop;
+
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi of route node", __func__);
+      return;
+    }
+
+  if ((bi->type != ZEBRA_ROUTE_BGP) ||
+      (bi->sub_type != BGP_ROUTE_NORMAL &&
+       bi->sub_type != BGP_ROUTE_RFP && bi->sub_type != BGP_ROUTE_STATIC))
+    {
+
+      zlog_debug ("%s: wrong route type/sub_type for export, skipping",
+                  __func__);
+      return;
+    }
+
+  /* check bgp redist flag for vnc direct ("vpn") routes */
+  if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT])
+    {
+      zlog_debug ("%s: bgp redistribution of VNC direct routes is off",
+                  __func__);
+      return;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  if (!VNC_EXPORT_BGP_CE_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export-to-bgp ce mode not enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  /*
+   * prefix list check
+   */
+  if (bgp->rfapi_cfg->plist_export_bgp[afi])
+    {
+      if (prefix_list_apply (bgp->rfapi_cfg->plist_export_bgp[afi], prefix) ==
+          PREFIX_DENY)
+        {
+          zlog_debug ("%s: prefix list denied, skipping", __func__);
+          return;
+        }
+    }
+
+
+  /*
+   * Extract CE
+   * This works only for IPv4 because IPv6 addresses are too big
+   * to fit in an extended community
+   */
+  if (getce (bgp, attr, &ce_nexthop))
+    {
+      zlog_debug ("%s: EC has no encoded CE, skipping", __func__);
+      return;
+    }
+
+  /*
+   * Is this route already represented in the unicast RIB?
+   * (look up prefix; compare route type, sub_type, peer, nexthop)
+   */
+  urn =
+    bgp_afi_node_get (bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST, prefix,
+                      NULL);
+  for (ubi = urn->info; ubi; ubi = ubi->next)
+    {
+      struct prefix unicast_nexthop;
+
+      if (CHECK_FLAG (ubi->flags, BGP_INFO_REMOVED))
+        continue;
+
+      rfapiUnicastNexthop2Prefix (afi, ubi->attr, &unicast_nexthop);
+
+      if (ubi->type == ZEBRA_ROUTE_VNC_DIRECT &&
+          ubi->sub_type == BGP_ROUTE_REDISTRIBUTE &&
+          ubi->peer == peer && prefix_same (&unicast_nexthop, &ce_nexthop))
+        {
+
+          zlog_debug
+            ("%s: already have matching exported unicast route, skipping",
+             __func__);
+          return;
+        }
+    }
+
+  /*
+   * Construct new attribute set with CE addr as
+   * nexthop and without Tunnel Encap attr
+   */
+  encap_attr_export_ce (&hattr, attr, &ce_nexthop);
+  if (bgp->rfapi_cfg->routemap_export_bgp)
+    {
+      struct bgp_info info;
+      route_map_result_t ret;
+
+      memset (&info, 0, sizeof (info));
+      info.peer = peer;
+      info.attr = &hattr;
+      ret =
+        route_map_apply (bgp->rfapi_cfg->routemap_export_bgp, prefix,
+                         RMAP_BGP, &info);
+      if (ret == RMAP_DENYMATCH)
+        {
+          bgp_attr_flush (&hattr);
+          bgp_attr_extra_free (&hattr);
+          return;
+        }
+    }
+
+  iattr = bgp_attr_intern (&hattr);
+  bgp_attr_flush (&hattr);
+  bgp_attr_extra_free (&hattr);
+
+  /*
+   * Rule: disallow route-map alteration of next-hop, because it
+   * would make it too difficult to keep track of the correspondence
+   * between VPN routes and unicast routes.
+   */
+  rfapiUnicastNexthop2Prefix (afi, iattr, &post_routemap_nexthop);
+
+  if (!prefix_same (&ce_nexthop, &post_routemap_nexthop))
+    {
+      zlog_debug
+        ("%s: route-map modification of nexthop not allowed, skipping",
+         __func__);
+      bgp_attr_unintern (&iattr);
+      return;
+    }
+
+  bgp_update (peer, prefix,  
+              0,                /* addpath_id */
+              iattr,      /* bgp_update copies this attr */
+              afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL,  /* RD not used for unicast */
+              NULL,             /* tag not used for unicast */
+              0);
+  bgp_attr_unintern (&iattr);
+}
+
+
+/*
+ * "Withdrawing a Route" export process
+ */
+void
+vnc_direct_bgp_del_route_ce (
+    struct bgp         *bgp,
+    struct route_node  *rn,
+    struct bgp_info    *bi)
+{
+  afi_t afi = family2afi (rn->p.family);
+  struct bgp_info *vbi;
+  struct prefix ce_nexthop;
+
+  if (!afi)
+    {
+      zlog_err ("%s: bad afi", __func__);
+      return;
+    }
+
+  /* check bgp redist flag for vnc direct ("vpn") routes */
+  if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT])
+    {
+      zlog_debug ("%s: bgp redistribution of VNC direct routes is off",
+                  __func__);
+      return;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+  if (!VNC_EXPORT_BGP_CE_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export-to-bgp ce mode not enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  /*
+   * Extract CE
+   * This works only for IPv4 because IPv6 addresses are too big
+   * to fit in an extended community
+   */
+  if (getce (bgp, bi->attr, &ce_nexthop))
+    {
+      zlog_debug ("%s: EC has no encoded CE, skipping", __func__);
+      return;
+    }
+
+  /*
+   * Look for other VPN routes with same prefix, same 5226 CE,
+   * same peer. If at least one is present, don't remove the
+   * route from the unicast RIB
+   */
+
+  for (vbi = rn->info; vbi; vbi = vbi->next)
+    {
+      struct prefix ce;
+      if (bi == vbi)
+        continue;
+      if (bi->peer != vbi->peer)
+        continue;
+      if (getce (bgp, vbi->attr, &ce))
+        continue;
+      if (prefix_same (&ce, &ce_nexthop))
+        {
+          zlog_debug ("%s: still have a route via CE, not deleting unicast",
+                      __func__);
+          return;
+        }
+    }
+
+  /*
+   * withdraw the route
+   */
+  bgp_withdraw (bi->peer, &rn->p, 
+                0,                /* addpath_id */
+                NULL, /* attr, ignored */
+                afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL,        /* RD not used for unicast */
+                NULL);          /* tag not used for unicast */
+
+}
+
+static void
+vnc_direct_bgp_vpn_enable_ce (struct bgp *bgp, afi_t afi)
+{
+  struct rfapi_cfg *hc;
+  struct route_node *rn;
+  struct bgp_info *ri;
+
+  zlog_debug ("%s: entry, afi=%d", __func__, afi);
+
+  if (!bgp)
+    return;
+
+  if (!(hc = bgp->rfapi_cfg))
+    return;
+
+  if (!VNC_EXPORT_BGP_CE_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export of CE routes not enabled, skipping", __func__);
+      return;
+    }
+
+  if (afi != AFI_IP
+      && afi != AFI_IP6)
+    {
+      zlog_debug ("%s: bad afi: %d", __func__, afi);
+      return;
+    }
+
+  /*
+   * Go through entire ce import table and export to BGP unicast.
+   */
+  for (rn = route_top (bgp->rfapi->it_ce->imported_vpn[afi]); rn;
+       rn = route_next (rn))
+    {
+
+      if (!rn->info)
+        continue;
+
+      {
+        char prefixstr[BUFSIZ];
+
+        prefixstr[0] = 0;
+        inet_ntop (rn->p.family, &rn->p.u.prefix, prefixstr, BUFSIZ);
+        zlog_debug ("%s: checking prefix %s/%d", __func__, prefixstr,
+                    rn->p.prefixlen);
+      }
+
+      for (ri = rn->info; ri; ri = ri->next)
+        {
+
+          zlog_debug ("%s: ri->sub_type: %d", __func__, ri->sub_type);
+
+          if (ri->sub_type == BGP_ROUTE_NORMAL ||
+              ri->sub_type == BGP_ROUTE_RFP ||
+              ri->sub_type == BGP_ROUTE_STATIC)
+            {
+
+              vnc_direct_bgp_add_route_ce (bgp, rn, ri);
+            }
+
+        }
+    }
+}
+
+static void
+vnc_direct_bgp_vpn_disable_ce (struct bgp *bgp, afi_t afi)
+{
+  struct bgp_node *rn;
+
+  zlog_debug ("%s: entry, afi=%d", __func__, afi);
+
+  if (!bgp)
+    return;
+
+  if (afi != AFI_IP
+      && afi != AFI_IP6)
+    {
+      zlog_debug ("%s: bad afi: %d", __func__, afi);
+      return;
+    }
+
+  /*
+   * Go through the entire BGP unicast table and remove routes that
+   * originated from us
+   */
+  for (rn = bgp_table_top (bgp->rib[afi][SAFI_UNICAST]); rn;
+       rn = bgp_route_next (rn))
+    {
+
+      struct bgp_info *ri;
+      struct bgp_info *next;
+
+      for (ri = rn->info, next = NULL; ri; ri = next)
+        {
+
+          next = ri->next;
+
+          if (ri->type == ZEBRA_ROUTE_VNC_DIRECT &&
+              ri->sub_type == BGP_ROUTE_REDISTRIBUTE)
+            {
+
+              bgp_withdraw (ri->peer, &rn->p,   /* prefix */
+                            0,                /* addpath_id */
+                            NULL,       /* ignored */
+                            AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */
+                            NULL);      /* tag not used for unicast */
+            }
+        }
+    }
+}
+
+/***********************************************************************
+ * Export methods that set nexthop to CE (from 5226 roo EC) END
+ ***********************************************************************/
+
+/***********************************************************************
+ * Export methods that proxy nexthop BEGIN
+ ***********************************************************************/
+
+static struct ecommunity *
+vnc_route_origin_ecom (struct route_node *rn)
+{
+  struct ecommunity *new;
+  struct bgp_info *bi;
+
+  if (!rn->info)
+    return NULL;
+
+  new = ecommunity_new ();
+
+  for (bi = rn->info; bi; bi = bi->next)
+    {
+
+      struct ecommunity_val roec;
+
+      switch (BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len))
+        {
+        case AF_INET:
+          memset (&roec, 0, sizeof (roec));
+          roec.val[0] = 0x01;
+          roec.val[1] = 0x03;
+          memcpy (roec.val + 2,
+                  &bi->attr->extra->mp_nexthop_global_in.s_addr, 4);
+          roec.val[6] = 0;
+          roec.val[7] = 0;
+          ecommunity_add_val (new, &roec);
+          break;
+        case AF_INET6:
+          /* No support for IPv6 addresses in extended communities */
+          break;
+        }
+    }
+
+  if (!new->size)
+    {
+      ecommunity_free (&new);
+      new = NULL;
+    }
+
+  return new;
+}
+
+static struct ecommunity *
+vnc_route_origin_ecom_single (struct in_addr *origin)
+{
+  struct ecommunity *new;
+  struct ecommunity_val roec;
+
+  memset (&roec, 0, sizeof (roec));
+  roec.val[0] = 0x01;
+  roec.val[1] = 0x03;
+  memcpy (roec.val + 2, &origin->s_addr, 4);
+  roec.val[6] = 0;
+  roec.val[7] = 0;
+
+  new = ecommunity_new ();
+  assert (new);
+  ecommunity_add_val (new, &roec);
+
+  if (!new->size)
+    {
+      ecommunity_free (&new);
+      new = NULL;
+    }
+
+  return new;
+}
+
+
+/*
+ * New memory allocation approach: make a ghost attr that
+ * has non-interned parts for the modifications. ghost attr
+ * memory is allocated by caller.
+ */
+static int
+encap_attr_export (
+    struct attr                *new,
+    struct attr                *orig,
+    struct prefix      *new_nexthop,
+    struct route_node  *rn)            /* for VN addrs for ecom list */
+                                       /* if rn is 0, use route's nexthop */
+{
+  struct prefix orig_nexthop;
+  struct prefix *use_nexthop;
+  static struct ecommunity *ecom_ro;
+
+  if (new_nexthop)
+    {
+      use_nexthop = new_nexthop;
+    }
+  else
+    {
+      use_nexthop = &orig_nexthop;
+      orig_nexthop.family =
+        BGP_MP_NEXTHOP_FAMILY (orig->extra->mp_nexthop_len);
+      if (orig_nexthop.family == AF_INET)
+        {
+          orig_nexthop.prefixlen = 32;
+          orig_nexthop.u.prefix4 = orig->extra->mp_nexthop_global_in;
+        }
+      else if (orig_nexthop.family == AF_INET6)
+        {
+          orig_nexthop.prefixlen = 128;
+          orig_nexthop.u.prefix6 = orig->extra->mp_nexthop_global;
+        }
+      else
+        {
+          return -1;            /* FAIL - can't compute nexthop */
+        }
+    }
+
+
+  /*
+   * Make "new" a ghost attr copy of "orig"
+   */
+  memset (new, 0, sizeof (struct attr));
+  bgp_attr_dup (new, orig);
+
+  /*
+   * Set nexthop
+   */
+  switch (use_nexthop->family)
+    {
+    case AF_INET:
+      new->nexthop = use_nexthop->u.prefix4;
+      new->extra->mp_nexthop_len = 4;   /* bytes */
+      new->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+      break;
+
+    case AF_INET6:
+      if (!new->extra)
+        {
+          new->extra = XCALLOC (MTYPE_ATTR_EXTRA, sizeof (struct attr_extra));
+        }
+      new->extra->mp_nexthop_global = use_nexthop->u.prefix6;
+      new->extra->mp_nexthop_len = 16;  /* bytes */
+      break;
+
+    default:
+      assert (0);
+      break;
+    }
+
+  bgp_attr_extra_get (new);
+  if (rn)
+    {
+      ecom_ro = vnc_route_origin_ecom (rn);
+    }
+  else
+    {
+      /* TBD test/assert for IPv6 */
+      ecom_ro = vnc_route_origin_ecom_single (&use_nexthop->u.prefix4);
+    }
+  if (new->extra->ecommunity)
+    {
+      if (ecom_ro)
+        {
+          new->extra->ecommunity =
+            ecommunity_merge (ecom_ro, new->extra->ecommunity);
+        }
+    }
+  else
+    {
+      new->extra->ecommunity = ecom_ro;
+    }
+  if (ecom_ro)
+    {
+      new->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
+    }
+
+  /*
+   * Set MED
+   *
+   * Note that it will be deleted when BGP sends to any eBGP
+   * peer unless PEER_FLAG_MED_UNCHANGED is set:
+   *
+   *          neighbor NEIGHBOR attribute-unchanged med
+   */
+  if (!CHECK_FLAG (new->flag, BGP_ATTR_MULTI_EXIT_DISC))
+    {
+      if (CHECK_FLAG (new->flag, BGP_ATTR_LOCAL_PREF))
+        {
+          if (new->local_pref > 255)
+            new->med = 0;
+          else
+            new->med = 255 - new->local_pref;
+        }
+      else
+        {
+          new->med = 255;       /* shouldn't happen */
+        }
+      new->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+    }
+
+  /*
+   * "new" is now a ghost attr:
+   *  - it owns an "extra" struct
+   *  - it owns any non-interned parts
+   *  - any references to interned parts are not counted
+   *
+   * Caller should, after using the attr, call:
+   *  - bgp_attr_flush() to free non-interned parts
+   *  - call bgp_attr_extra_free() to free extra
+   */
+
+  return 0;
+}
+
+/*
+ * "Adding a Route" export process
+ */
+void
+vnc_direct_bgp_add_prefix (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *import_table,
+    struct route_node          *rn)
+{
+  struct attr attr = { 0 };
+  struct listnode *node, *nnode;
+  struct rfapi_rfg_name *rfgn;
+  afi_t afi = family2afi (rn->p.family);
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi of route node", __func__);
+      return;
+    }
+
+  /* check bgp redist flag for vnc direct ("vpn") routes */
+  if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT])
+    {
+      zlog_debug ("%s: bgp redistribution of VNC direct routes is off",
+                  __func__);
+      return;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  if (!VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export-to-bgp group mode not enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  if (!listcount (bgp->rfapi_cfg->rfg_export_direct_bgp_l))
+    {
+      zlog_debug ("%s: no bgp-direct export nve group, skipping", __func__);
+      return;
+    }
+
+  bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE);
+  /* TBD set some configured med, see add_vnc_route() */
+
+  zlog_debug ("%s: looping over nve-groups in direct-bgp export list",
+              __func__);
+
+  for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                          node, nnode, rfgn))
+    {
+
+      struct listnode *ln;
+
+      /*
+       * If nve group is not defined yet, skip it
+       */
+      if (!rfgn->rfg)
+        continue;
+
+      /*
+       * If the nve group uses a different import table, skip it
+       */
+      if (import_table != rfgn->rfg->rfapi_import_table)
+        continue;
+
+      /*
+       * if no NVEs currently associated with this group, skip it
+       */
+      if (!rfgn->rfg->nves)
+        continue;
+
+      /*
+       * per-nve-group prefix list check
+       */
+      if (rfgn->rfg->plist_export_bgp[afi])
+        {
+          if (prefix_list_apply (rfgn->rfg->plist_export_bgp[afi], &rn->p) ==
+              PREFIX_DENY)
+
+            continue;
+        }
+
+      /*
+       * For each NVE that is assigned to the export nve group, generate
+       * a route with that NVE as its next hop
+       */
+      for (ln = listhead (rfgn->rfg->nves); ln; ln = listnextnode (ln))
+        {
+
+          struct prefix nhp;
+          struct rfapi_descriptor *irfd;
+          struct bgp_info info;
+          struct attr hattr;
+          struct attr *iattr;
+
+          irfd = listgetdata (ln);
+
+          if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp))
+            continue;
+
+          /*
+           * Construct new attribute set with NVE's VN addr as
+           * nexthop and without Tunnel Encap attr
+           */
+          if (encap_attr_export (&hattr, &attr, &nhp, rn))
+            continue;
+
+         if (VNC_DEBUG(EXPORT_BGP_DIRECT_ADD))
+           {
+             zlog_debug ("%s: attr follows", __func__);
+             rfapiPrintAttrPtrs (NULL, &attr);
+             zlog_debug ("%s: hattr follows", __func__);
+             rfapiPrintAttrPtrs (NULL, &hattr);
+           }
+
+          if (rfgn->rfg->routemap_export_bgp)
+            {
+              route_map_result_t ret;
+              info.peer = irfd->peer;
+              info.attr = &hattr;
+              ret = route_map_apply (rfgn->rfg->routemap_export_bgp, &rn->p,
+                                     RMAP_BGP, &info);
+              if (ret == RMAP_DENYMATCH)
+                {
+                  bgp_attr_flush (&hattr);
+                  bgp_attr_extra_free (&hattr);
+                  zlog_debug
+                    ("%s: route map says DENY, so not calling bgp_update",
+                     __func__);
+                  continue;
+                }
+            }
+
+         if (VNC_DEBUG(EXPORT_BGP_DIRECT_ADD))
+           {
+          zlog_debug ("%s: hattr after route_map_apply:", __func__);
+          rfapiPrintAttrPtrs (NULL, &hattr);
+           }
+
+          iattr = bgp_attr_intern (&hattr);
+          bgp_attr_flush (&hattr);
+          bgp_attr_extra_free (&hattr);
+
+          bgp_update (irfd->peer, &rn->p,       /* prefix */ 
+                      0,                        /* addpath_id */
+                      iattr,    /* bgp_update copies it */
+                      afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL,  /* RD not used for unicast */
+                      NULL,     /* tag not used for unicast */
+                      0);
+
+          bgp_attr_unintern (&iattr);
+        }
+    }
+
+  aspath_unintern (&attr.aspath);
+  bgp_attr_extra_free (&attr);
+}
+
+/*
+ * "Withdrawing a Route" export process
+ */
+void
+vnc_direct_bgp_del_prefix (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *import_table,
+    struct route_node          *rn)
+{
+  struct listnode *node, *nnode;
+  struct rfapi_rfg_name *rfgn;
+  afi_t afi = family2afi (rn->p.family);
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi route node", __func__);
+      return;
+    }
+
+  /* check bgp redist flag for vnc direct ("vpn") routes */
+  if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT])
+    {
+      zlog_debug ("%s: bgp redistribution of VNC direct routes is off",
+                  __func__);
+      return;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  if (!VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export-to-bgp group mode not enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  if (!listcount (bgp->rfapi_cfg->rfg_export_direct_bgp_l))
+    {
+      zlog_debug ("%s: no bgp-direct export nve group, skipping", __func__);
+      return;
+    }
+
+  for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                          node, nnode, rfgn))
+    {
+
+      struct listnode *ln;
+
+      /*
+       * If nve group is not defined yet, skip it
+       */
+      if (!rfgn->rfg)
+        continue;
+
+      /*
+       * if no NVEs currently associated with this group, skip it
+       */
+      if (!rfgn->rfg->nves)
+        continue;
+
+      /*
+       * If the nve group uses a different import table,
+       * skip it
+       */
+      if (import_table != rfgn->rfg->rfapi_import_table)
+        continue;
+
+      /*
+       * For each NVE that is assigned to the export nve group, generate
+       * a route with that NVE as its next hop
+       */
+      for (ln = listhead (rfgn->rfg->nves); ln; ln = listnextnode (ln))
+        {
+
+          struct prefix nhp;
+          struct rfapi_descriptor *irfd;
+
+          irfd = listgetdata (ln);
+
+          if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp))
+            continue;
+
+          bgp_withdraw (irfd->peer, &rn->p,     /* prefix */
+                        0,                /* addpath_id */
+                        NULL,   /* attr, ignored */
+                        afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL,        /* RD not used for unicast */
+                        NULL);  /* tag not used for unicast */
+        }
+    }
+}
+
+void
+vnc_direct_bgp_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+  struct listnode *node, *nnode;
+  struct rfapi_rfg_name *rfgn;
+  struct rfapi_nve_group_cfg *rfg = rfd->rfg;
+  afi_t afi = family2afi (rfd->vn_addr.addr_family);
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi of nve vn addr", __func__);
+      return;
+    }
+
+  if (!bgp)
+    return;
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+  if (!VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export-to-bgp group mode not enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT])
+    {
+      zlog_debug ("%s: bgp redistribution of VNC direct routes is off",
+                  __func__);
+      return;
+    }
+
+  /*
+   * Loop over the list of NVE-Groups configured for
+   * exporting to direct-bgp and see if this new NVE's
+   * group is among them.
+   */
+  for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                          node, nnode, rfgn))
+    {
+
+      /*
+       * Yes, this NVE's group is configured for export to direct-bgp
+       */
+      if (rfgn->rfg == rfg)
+        {
+
+          struct route_table *rt = NULL;
+          struct route_node *rn;
+          struct attr attr = { 0 };
+          struct rfapi_import_table *import_table;
+
+
+          import_table = rfg->rfapi_import_table;
+
+          bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE);
+          /* TBD set some configured med, see add_vnc_route() */
+
+          if (afi == AFI_IP
+              || afi == AFI_IP6)
+            {
+              rt = import_table->imported_vpn[afi];
+            }
+          else
+            {
+              zlog_err ("%s: bad afi %d", __func__, afi);
+              return;
+            }
+
+          /* 
+           * Walk the NVE-Group's VNC Import table
+           */
+          for (rn = route_top (rt); rn; rn = route_next (rn))
+            {
+
+              if (rn->info)
+                {
+
+                  struct prefix nhp;
+                  struct rfapi_descriptor *irfd = rfd;
+                  struct attr hattr;
+                  struct attr *iattr;
+                  struct bgp_info info;
+
+                  if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp))
+                    continue;
+
+                  /*
+                   * per-nve-group prefix list check
+                   */
+                  if (rfgn->rfg->plist_export_bgp[afi])
+                    {
+                      if (prefix_list_apply (rfgn->rfg->plist_export_bgp[afi],
+                                             &rn->p) == PREFIX_DENY)
+
+                        continue;
+                    }
+
+
+                  /*
+                   * Construct new attribute set with NVE's VN addr as
+                   * nexthop and without Tunnel Encap attr
+                   */
+                  if (encap_attr_export (&hattr, &attr, &nhp, rn))
+                    continue;
+
+                  if (rfgn->rfg->routemap_export_bgp)
+                    {
+                      route_map_result_t ret;
+                      info.peer = irfd->peer;
+                      info.attr = &hattr;
+                      ret = route_map_apply (rfgn->rfg->routemap_export_bgp,
+                                             &rn->p, RMAP_BGP, &info);
+                      if (ret == RMAP_DENYMATCH)
+                        {
+                          bgp_attr_flush (&hattr);
+                          bgp_attr_extra_free (&hattr);
+                          continue;
+                        }
+
+                    }
+
+                  iattr = bgp_attr_intern (&hattr);
+                  bgp_attr_flush (&hattr);
+                  bgp_attr_extra_free (&hattr);
+
+                  bgp_update (irfd->peer, &rn->p,       /* prefix */ 
+                              0,                        /* addpath_id */
+                              iattr,    /* bgp_update copies it */
+                              afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL,  /* RD not used for unicast */
+                              NULL,     /* tag not used for unicast */
+                              0);
+
+                  bgp_attr_unintern (&iattr);
+
+                }
+            }
+
+          aspath_unintern (&attr.aspath);
+          bgp_attr_extra_free (&attr);
+        }
+    }
+}
+
+
+void
+vnc_direct_bgp_del_nve (struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+  struct listnode *node, *nnode;
+  struct rfapi_rfg_name *rfgn;
+  struct rfapi_nve_group_cfg *rfg = rfd->rfg;
+  afi_t afi = family2afi (rfd->vn_addr.addr_family);
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi of nve vn addr", __func__);
+      return;
+    }
+
+  if (!bgp)
+    return;
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+  if (!VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export-to-bgp group mode not enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT])
+    {
+      zlog_debug ("%s: bgp redistribution of VNC direct routes is off",
+                  __func__);
+      return;
+    }
+
+  /*
+   * Loop over the list of NVE-Groups configured for
+   * exporting to direct-bgp and see if this new NVE's
+   * group is among them.
+   */
+  for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                          node, nnode, rfgn))
+    {
+
+      /*
+       * Yes, this NVE's group is configured for export to direct-bgp
+       */
+      if (rfg && rfgn->rfg == rfg)
+        {
+
+          struct route_table *rt = NULL;
+          struct route_node *rn;
+          struct rfapi_import_table *import_table;
+
+          import_table = rfg->rfapi_import_table;
+
+          if (afi == AFI_IP
+              || afi == AFI_IP6)
+            {
+              rt = import_table->imported_vpn[afi];
+            }
+          else
+            {
+              zlog_err ("%s: bad afi %d", __func__, afi);
+              return;
+            }
+
+          /* 
+           * Walk the NVE-Group's VNC Import table
+           */
+          for (rn = route_top (rt); rn; rn = route_next (rn))
+            {
+
+              if (rn->info)
+                {
+
+                  struct prefix nhp;
+                  struct rfapi_descriptor *irfd = rfd;
+
+                  if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp))
+                    continue;
+
+                  bgp_withdraw (irfd->peer, &rn->p,     /* prefix */
+                                0,                      /* addpath_id */
+                                NULL,   /* attr, ignored */
+                                afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL,        /* RD not used for unicast */
+                                NULL);  /* tag not used for unicast */
+
+                }
+            }
+        }
+    }
+}
+
+
+
+/*
+ * Caller is responsible for ensuring that the specified nve-group
+ * is actually part of the list of exported nve groups.
+ */
+static void
+vnc_direct_bgp_add_group_afi (
+    struct bgp                 *bgp,
+    struct rfapi_nve_group_cfg *rfg,
+    afi_t                      afi)
+{
+  struct route_table *rt = NULL;
+  struct route_node *rn;
+  struct attr attr = { 0 };
+  struct rfapi_import_table *import_table;
+
+  zlog_debug ("%s: entry", __func__);
+
+  import_table = rfg->rfapi_import_table;
+  if (!import_table)
+    {
+      zlog_debug ("%s: import table not defined, returning", __func__);
+      return;
+    }
+
+  if (afi == AFI_IP
+      || afi == AFI_IP6)
+    {
+      rt = import_table->imported_vpn[afi];
+    }
+  else
+    {
+      zlog_err ("%s: bad afi %d", __func__, afi);
+      return;
+    }
+
+  if (!rfg->nves)
+    {
+      /* avoid segfault below if list doesn't exist */
+      zlog_debug ("%s: no NVEs in this group", __func__);
+      return;
+    }
+
+  bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE);
+  /* TBD set some configured med, see add_vnc_route() */
+
+  /* 
+   * Walk the NVE-Group's VNC Import table
+   */
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    {
+
+      if (rn->info)
+        {
+
+          struct listnode *ln;
+
+          /*
+           * per-nve-group prefix list check
+           */
+          if (rfg->plist_export_bgp[afi])
+            {
+              if (prefix_list_apply (rfg->plist_export_bgp[afi], &rn->p) ==
+                  PREFIX_DENY)
+
+                continue;
+            }
+
+          /*
+           * For each NVE that is assigned to the export nve group, generate
+           * a route with that NVE as its next hop
+           */
+          for (ln = listhead (rfg->nves); ln; ln = listnextnode (ln))
+            {
+
+              struct prefix nhp;
+              struct rfapi_descriptor *irfd;
+              struct bgp_info info;
+              struct attr hattr;
+              struct attr *iattr;
+
+              irfd = listgetdata (ln);
+
+              if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp))
+                continue;
+
+              /*
+               * Construct new attribute set with NVE's VN addr as
+               * nexthop and without Tunnel Encap attr
+               */
+              if (encap_attr_export (&hattr, &attr, &nhp, rn))
+                continue;
+
+              if (rfg->routemap_export_bgp)
+                {
+                  route_map_result_t ret;
+                  info.peer = irfd->peer;
+                  info.attr = &hattr;
+                  ret = route_map_apply (rfg->routemap_export_bgp,
+                                         &rn->p, RMAP_BGP, &info);
+                  if (ret == RMAP_DENYMATCH)
+                    {
+                      bgp_attr_flush (&hattr);
+                      bgp_attr_extra_free (&hattr);
+                      continue;
+                    }
+
+                }
+
+              iattr = bgp_attr_intern (&hattr);
+              bgp_attr_flush (&hattr);
+              bgp_attr_extra_free (&hattr);
+
+              bgp_update (irfd->peer, &rn->p,   /* prefix */ 
+                          0,                    /* addpath_id */
+                          iattr,        /* bgp_update copies it */
+                          afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL,      /* RD not used for unicast */
+                          NULL, /* tag not used for unicast */
+                          0);
+
+              bgp_attr_unintern (&iattr);
+            }
+        }
+    }
+
+  aspath_unintern (&attr.aspath);
+  bgp_attr_extra_free (&attr);
+}
+
+
+/*
+ * Caller is responsible for ensuring that the specified nve-group
+ * is actually part of the list of exported nve groups.
+ */
+void
+vnc_direct_bgp_add_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg)
+{
+  vnc_direct_bgp_add_group_afi (bgp, rfg, AFI_IP);
+  vnc_direct_bgp_add_group_afi (bgp, rfg, AFI_IP6);
+}
+
+
+
+/*
+ * Caller is responsible for ensuring that the specified nve-group
+ * was actually part of the list of exported nve groups.
+ */
+static void
+vnc_direct_bgp_del_group_afi (
+    struct bgp                 *bgp,
+    struct rfapi_nve_group_cfg *rfg,
+    afi_t                      afi)
+{
+  struct route_table *rt = NULL;
+  struct route_node *rn;
+  struct rfapi_import_table *import_table;
+
+  zlog_debug ("%s: entry", __func__);
+
+  import_table = rfg->rfapi_import_table;
+  if (!import_table)
+    {
+      zlog_debug ("%s: import table not defined, returning", __func__);
+      return;
+    }
+
+  assert (afi == AFI_IP
+          || afi == AFI_IP6);
+  rt = import_table->imported_vpn[afi];
+
+  if (!rfg->nves)
+    {
+      /* avoid segfault below if list does not exist */
+      zlog_debug ("%s: no NVEs in this group", __func__);
+      return;
+    }
+
+  /* 
+   * Walk the NVE-Group's VNC Import table
+   */
+  for (rn = route_top (rt); rn; rn = route_next (rn))
+    {
+
+      if (rn->info)
+        {
+
+          struct listnode *ln;
+
+          /*
+           * For each NVE that is assigned to the export nve group, generate
+           * a route with that NVE as its next hop
+           */
+          for (ln = listhead (rfg->nves); ln; ln = listnextnode (ln))
+            {
+
+              struct rfapi_descriptor *irfd;
+
+              irfd = listgetdata (ln);
+
+              bgp_withdraw (irfd->peer, &rn->p, /* prefix */
+                            0,                  /* addpath_id */
+                            NULL,       /* attr, ignored */
+                            afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL,    /* RD not used for unicast */
+                            NULL);      /* tag not used for unicast */
+
+            }
+        }
+    }
+}
+
+
+/*
+ * Caller is responsible for ensuring that the specified nve-group
+ * was actually part of the list of exported nve groups.
+ */
+void
+vnc_direct_bgp_del_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg)
+{
+  vnc_direct_bgp_del_group_afi (bgp, rfg, AFI_IP);
+  vnc_direct_bgp_del_group_afi (bgp, rfg, AFI_IP6);
+}
+
+void
+vnc_direct_bgp_reexport_group_afi (
+    struct bgp                 *bgp,
+    struct rfapi_nve_group_cfg *rfg,
+    afi_t                      afi)
+{
+  struct listnode *node;
+  struct rfapi_rfg_name *rfgn;
+
+  if (VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg))
+    {
+      /*
+       * look in the list of currently-exported groups
+       */
+      for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                                 node, rfgn))
+        {
+
+          if (rfgn->rfg == rfg)
+            {
+              /*
+               * If it matches, reexport it
+               */
+              vnc_direct_bgp_del_group_afi (bgp, rfg, afi);
+              vnc_direct_bgp_add_group_afi (bgp, rfg, afi);
+              break;
+            }
+        }
+    }
+}
+
+
+static void
+vnc_direct_bgp_unexport_table (
+    afi_t              afi,
+    struct route_table *rt,
+    struct list                *nve_list)
+{
+  if (nve_list)
+    {
+
+      struct route_node *rn;
+
+      for (rn = route_top (rt); rn; rn = route_next (rn))
+        {
+
+          if (rn->info)
+            {
+
+              struct listnode *hln;
+              struct rfapi_descriptor *irfd;
+
+              for (ALL_LIST_ELEMENTS_RO (nve_list, hln, irfd))
+                {
+
+                  bgp_withdraw (irfd->peer, &rn->p,     /* prefix */
+                                0,                      /* addpath_id */
+                                NULL,   /* attr, ignored */
+                                afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL,        /* RD not used for unicast */
+                                NULL);  /* tag not used for unicast */
+
+                }
+            }
+        }
+    }
+}
+
+static void
+import_table_to_nve_list_direct_bgp (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *it,
+    struct list                        **nves,
+    uint8_t                    family)
+{
+  struct listnode *node;
+  struct rfapi_rfg_name *rfgn;
+
+  /*
+   * Loop over the list of NVE-Groups configured for
+   * exporting to direct-bgp.
+   *
+   * Build a list of NVEs that use this import table
+   */
+  *nves = NULL;
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+                             node, rfgn))
+    {
+
+      /*
+       * If this NVE-Group's import table matches the current one
+       */
+      if (rfgn->rfg && rfgn->rfg->nves && rfgn->rfg->rfapi_import_table == it)
+        {
+
+          nve_group_to_nve_list (rfgn->rfg, nves, family);
+        }
+    }
+}
+
+void
+vnc_direct_bgp_vpn_enable (struct bgp *bgp, afi_t afi)
+{
+  struct listnode *rfgn;
+  struct rfapi_nve_group_cfg *rfg;
+
+  if (!bgp)
+    return;
+
+  if (!VNC_EXPORT_BGP_GRP_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export-to-bgp group mode not enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  if (afi != AFI_IP
+      && afi != AFI_IP6)
+    {
+      zlog_debug ("%s: bad afi: %d", __func__, afi);
+      return;
+    }
+
+  /*
+   * Policy is applied per-nve-group, so we need to iterate
+   * over the groups to add everything.
+   */
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->nve_groups_sequential,
+                             rfgn, rfg))
+    {
+
+      /*
+       * contains policy management
+       */
+      vnc_direct_bgp_add_group_afi (bgp, rfg, afi);
+    }
+}
+
+
+void
+vnc_direct_bgp_vpn_disable (struct bgp *bgp, afi_t afi)
+{
+  struct rfapi_import_table *it;
+  uint8_t family = afi2family (afi);
+
+  zlog_debug ("%s: entry, afi=%d", __func__, afi);
+
+  if (!bgp)
+    return;
+
+  if (!bgp->rfapi)
+    {
+      zlog_debug ("%s: rfapi not initialized", __func__);
+      return;
+    }
+
+  if (!family || (afi != AFI_IP
+                  && afi != AFI_IP6))
+    {
+      zlog_debug ("%s: bad afi: %d", __func__, afi);
+      return;
+    }
+
+  for (it = bgp->rfapi->imports; it; it = it->next)
+    {
+
+      struct list *nve_list = NULL;
+
+      import_table_to_nve_list_direct_bgp (bgp, it, &nve_list, family);
+
+      if (nve_list)
+        {
+          vnc_direct_bgp_unexport_table (afi, it->imported_vpn[afi],
+                                         nve_list);
+          list_free (nve_list);
+        }
+    }
+}
+
+
+/***********************************************************************
+ * Export methods that proxy nexthop END
+ ***********************************************************************/
+
+
+/***********************************************************************
+ * Export methods that preserve original nexthop BEGIN
+ * rh = "registering nve"
+ ***********************************************************************/
+
+
+/*
+ * "Adding a Route" export process
+ * TBD do we need to check bi->type and bi->sub_type here, or does
+ * caller do it?
+ */
+void
+vnc_direct_bgp_rh_add_route (
+    struct bgp         *bgp,
+    afi_t              afi,
+    struct prefix      *prefix,
+    struct peer                *peer,
+    struct attr                *attr)
+{
+  struct vnc_export_info *eti;
+  struct attr hattr;
+  struct rfapi_cfg *hc;
+  struct attr *iattr;
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi of route node", __func__);
+      return;
+    }
+
+  /* check bgp redist flag for vnc direct ("vpn") routes */
+  if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT])
+    {
+      zlog_debug ("%s: bgp redistribution of VNC direct routes is off",
+                  __func__);
+      return;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  if (!VNC_EXPORT_BGP_RH_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export-to-bgp RH mode not enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  /*
+   * prefix list check
+   */
+  if (hc->plist_export_bgp[afi])
+    {
+      if (prefix_list_apply (hc->plist_export_bgp[afi], prefix) ==
+          PREFIX_DENY)
+        return;
+    }
+
+  /*
+   * Construct new attribute set with NVE's VN addr as
+   * nexthop and without Tunnel Encap attr
+   */
+  if (encap_attr_export (&hattr, attr, NULL, NULL))
+    return;
+  if (hc->routemap_export_bgp)
+    {
+      struct bgp_info info;
+      route_map_result_t ret;
+
+      memset (&info, 0, sizeof (info));
+      info.peer = peer;
+      info.attr = &hattr;
+      ret =
+        route_map_apply (hc->routemap_export_bgp, prefix, RMAP_BGP, &info);
+      if (ret == RMAP_DENYMATCH)
+        {
+          bgp_attr_flush (&hattr);
+          bgp_attr_extra_free (&hattr);
+          return;
+        }
+    }
+
+  iattr = bgp_attr_intern (&hattr);
+  bgp_attr_flush (&hattr);
+  bgp_attr_extra_free (&hattr);
+
+  /*
+   * record route information that we will need to expire
+   * this route
+   */
+  eti = vnc_eti_get (bgp, EXPORT_TYPE_BGP, prefix, peer,
+                     ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE);
+  rfapiGetVncLifetime (attr, &eti->lifetime);
+  eti->lifetime = rfapiGetHolddownFromLifetime (eti->lifetime);
+
+  if (eti->timer)
+    {
+      /*
+       * export expiration timer is already running on
+       * this route: cancel it
+       */
+      thread_cancel (eti->timer);
+      eti->timer = NULL;
+    }
+
+  bgp_update (peer, prefix,     /* prefix */ 
+              0,                /* addpath_id */
+              iattr,            /* bgp_update copies this attr */
+              afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE, NULL,       /* RD not used for unicast */
+              NULL,             /* tag not used for unicast */
+              0);
+  bgp_attr_unintern (&iattr);
+
+}
+
+static int
+vncExportWithdrawTimer (struct thread *t)
+{
+  struct vnc_export_info *eti = t->arg;
+
+  /*
+   * withdraw the route
+   */
+  bgp_withdraw (
+    eti->peer,
+    &eti->node->p,
+    0,                                  /* addpath_id */
+    NULL,                              /* attr, ignored */
+    family2afi (eti->node->p.family),
+    SAFI_UNICAST,
+    eti->type,
+    eti->subtype,
+    NULL,                              /* RD not used for unicast */
+    NULL);                             /* tag not used for unicast */
+
+  /*
+   * Free the eti
+   */
+  vnc_eti_delete (eti);
+
+  return 0;
+}
+
+/*
+ * "Withdrawing a Route" export process
+ * TBD do we need to check bi->type and bi->sub_type here, or does
+ * caller do it?
+ */
+void
+vnc_direct_bgp_rh_del_route (
+    struct bgp         *bgp,
+    afi_t              afi,
+    struct prefix      *prefix,
+    struct peer                *peer)
+{
+  struct vnc_export_info *eti;
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi route node", __func__);
+      return;
+    }
+
+  /* check bgp redist flag for vnc direct ("vpn") routes */
+  if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT])
+    {
+      zlog_debug ("%s: bgp redistribution of VNC direct routes is off",
+                  __func__);
+      return;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+  if (!VNC_EXPORT_BGP_RH_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export-to-bgp group mode not enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  eti = vnc_eti_get (bgp, EXPORT_TYPE_BGP, prefix, peer,
+                     ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE);
+
+  if (!eti->timer && eti->lifetime <= INT32_MAX)
+    {
+      eti->timer = thread_add_timer (bm->master,
+                                     vncExportWithdrawTimer,
+                                     eti, eti->lifetime);
+      zlog_debug ("%s: set expiration timer for %u seconds",
+                  __func__, eti->lifetime);
+    }
+}
+
+
+void
+vnc_direct_bgp_rh_vpn_enable (struct bgp *bgp, afi_t afi)
+{
+  struct prefix_rd prd;
+  struct bgp_node *prn;
+  struct rfapi_cfg *hc;
+
+  zlog_debug ("%s: entry, afi=%d", __func__, afi);
+
+  if (!bgp)
+    return;
+
+  if (!(hc = bgp->rfapi_cfg))
+    return;
+
+  if (!VNC_EXPORT_BGP_RH_ENABLED (bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: export of RH routes not enabled, skipping", __func__);
+      return;
+    }
+
+  if (afi != AFI_IP
+      && afi != AFI_IP6)
+    {
+      zlog_debug ("%s: bad afi: %d", __func__, afi);
+      return;
+    }
+
+  /*
+   * Go through the entire BGP VPN table and export to BGP unicast.
+   */
+
+  zlog_debug ("%s: starting RD loop", __func__);
+
+  /* Loop over all the RDs */
+  for (prn = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); prn;
+       prn = bgp_route_next (prn))
+    {
+
+      struct bgp_table *table;
+      struct bgp_node *rn;
+      struct bgp_info *ri;
+
+      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;
+
+      for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+        {
+
+          /*
+           * skip prefix list check if no routes here
+           */
+          if (!rn->info)
+            continue;
+
+          {
+            char prefixstr[BUFSIZ];
+
+            prefixstr[0] = 0;
+            inet_ntop (rn->p.family, &rn->p.u.prefix, prefixstr, BUFSIZ);
+            zlog_debug ("%s: checking prefix %s/%d", __func__, prefixstr,
+                        rn->p.prefixlen);
+          }
+
+          /*
+           * prefix list check
+           */
+          if (hc->plist_export_bgp[afi])
+            {
+              if (prefix_list_apply (hc->plist_export_bgp[afi], &rn->p) ==
+                  PREFIX_DENY)
+                {
+
+                  zlog_debug ("%s:   prefix list says DENY", __func__);
+                  continue;
+                }
+            }
+
+          for (ri = rn->info; ri; ri = ri->next)
+            {
+
+              zlog_debug ("%s: ri->sub_type: %d", __func__, ri->sub_type);
+
+              if (ri->sub_type == BGP_ROUTE_NORMAL ||
+                  ri->sub_type == BGP_ROUTE_RFP)
+                {
+
+                  struct vnc_export_info *eti;
+                  struct attr hattr;
+                  struct attr *iattr;
+
+                  /*
+                   * Construct new attribute set with NVE's VN addr as
+                   * nexthop and without Tunnel Encap attr
+                   */
+                  if (encap_attr_export (&hattr, ri->attr, NULL, NULL))
+                    {
+                      zlog_debug ("%s:   encap_attr_export failed", __func__);
+                      continue;
+                    }
+
+                  if (hc->routemap_export_bgp)
+                    {
+                      struct bgp_info info;
+                      route_map_result_t ret;
+
+                      memset (&info, 0, sizeof (info));
+                      info.peer = ri->peer;
+                      info.attr = &hattr;
+                      ret = route_map_apply (hc->routemap_export_bgp,
+                                             &rn->p, RMAP_BGP, &info);
+                      if (ret == RMAP_DENYMATCH)
+                        {
+                          bgp_attr_flush (&hattr);
+                          bgp_attr_extra_free (&hattr);
+                          zlog_debug ("%s:   route map says DENY", __func__);
+                          continue;
+                        }
+                    }
+
+                  iattr = bgp_attr_intern (&hattr);
+                  bgp_attr_flush (&hattr);
+                  bgp_attr_extra_free (&hattr);
+
+                  /*
+                   * record route information that we will need to expire
+                   * this route
+                   */
+                  eti = vnc_eti_get (bgp, EXPORT_TYPE_BGP, &rn->p, ri->peer,
+                                     ZEBRA_ROUTE_VNC_DIRECT_RH,
+                                     BGP_ROUTE_REDISTRIBUTE);
+                  rfapiGetVncLifetime (ri->attr, &eti->lifetime);
+
+                  if (eti->timer)
+                    {
+                      /*
+                       * export expiration timer is already running on
+                       * this route: cancel it
+                       */
+                      thread_cancel (eti->timer);
+                      eti->timer = NULL;
+                    }
+
+                  zlog_debug ("%s: calling bgp_update", __func__);
+
+                  bgp_update (ri->peer, &rn->p, /* prefix */ 
+                              0,                /* addpath_id */
+                              iattr,    /* bgp_update copies it */
+                              AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE, NULL,    /* RD not used for unicast */
+                              NULL,     /* tag not used for unicast */
+                              0);
+                  bgp_attr_unintern (&iattr);
+                }
+            }
+        }
+    }
+}
+
+void
+vnc_direct_bgp_rh_vpn_disable (struct bgp *bgp, afi_t afi)
+{
+  struct bgp_node *rn;
+
+  zlog_debug ("%s: entry, afi=%d", __func__, afi);
+
+  if (!bgp)
+    return;
+
+  if (afi != AFI_IP
+      && afi != AFI_IP6)
+    {
+      zlog_debug ("%s: bad afi: %d", __func__, afi);
+      return;
+    }
+
+  /*
+   * Go through the entire BGP unicast table and remove routes that
+   * originated from us
+   */
+  for (rn = bgp_table_top (bgp->rib[afi][SAFI_UNICAST]); rn;
+       rn = bgp_route_next (rn))
+    {
+
+      struct bgp_info *ri;
+      struct bgp_info *next;
+
+      for (ri = rn->info, next = NULL; ri; ri = next)
+        {
+
+          next = ri->next;
+
+          if (ri->type == ZEBRA_ROUTE_VNC_DIRECT_RH &&
+              ri->sub_type == BGP_ROUTE_REDISTRIBUTE)
+            {
+
+              struct vnc_export_info *eti;
+
+              /*
+               * Delete routes immediately (no timer)
+               */
+              eti =
+                vnc_eti_checktimer (bgp, EXPORT_TYPE_BGP, &rn->p, ri->peer,
+                                    ZEBRA_ROUTE_VNC_DIRECT_RH,
+                                    BGP_ROUTE_REDISTRIBUTE);
+              if (eti)
+                {
+                  if (eti->timer)
+                    thread_cancel (eti->timer);
+                  vnc_eti_delete (eti);
+                }
+
+              bgp_withdraw (ri->peer, &rn->p,   /* prefix */
+                            0,                  /* addpath_id */
+                            NULL,       /* ignored */
+                            AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE, NULL,      /* RD not used for unicast */
+                            NULL);      /* tag not used for unicast */
+            }
+        }
+    }
+}
+
+void
+vnc_direct_bgp_rh_reexport (struct bgp *bgp, afi_t afi)
+{
+  if (VNC_EXPORT_BGP_RH_ENABLED (bgp->rfapi_cfg))
+    {
+      vnc_direct_bgp_rh_vpn_disable (bgp, afi);
+      vnc_direct_bgp_rh_vpn_enable (bgp, afi);
+    }
+}
+
+/***********************************************************************
+ * Generic Export methods
+ ***********************************************************************/
+
+/*
+ * Assumes the correct mode bits are already turned on. Thus it
+ * is OK to call this function from, e.g., bgp_redistribute_set()
+ * without caring if export is enabled or not
+ */
+void
+vnc_export_bgp_enable (struct bgp *bgp, afi_t afi)
+{
+  switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS)
+    {
+    case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE:
+      break;
+
+    case BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP:
+      vnc_direct_bgp_vpn_enable (bgp, afi);
+      break;
+
+    case BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH:
+      vnc_direct_bgp_rh_vpn_enable (bgp, afi);
+      break;
+
+    case BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE:
+      vnc_direct_bgp_vpn_enable_ce (bgp, afi);
+      break;
+    }
+}
+
+void
+vnc_export_bgp_disable (struct bgp *bgp, afi_t afi)
+{
+  switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS)
+    {
+    case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE:
+      break;
+
+    case BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP:
+      vnc_direct_bgp_vpn_disable (bgp, afi);
+      break;
+
+    case BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH:
+      vnc_direct_bgp_rh_vpn_disable (bgp, afi);
+      break;
+
+    case BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE:
+      vnc_direct_bgp_vpn_disable_ce (bgp, afi);
+      break;
+    }
+}
+
+void
+vnc_export_bgp_prechange (struct bgp *bgp)
+{
+  vnc_export_bgp_disable (bgp, AFI_IP);
+  vnc_export_bgp_disable (bgp, AFI_IP6);
+}
+
+void
+vnc_export_bgp_postchange (struct bgp *bgp)
+{
+  vnc_export_bgp_enable (bgp, AFI_IP);
+  vnc_export_bgp_enable (bgp, AFI_IP6);
+}
+
+void
+vnc_direct_bgp_reexport (struct bgp *bgp, afi_t afi)
+{
+  vnc_export_bgp_disable (bgp, afi);
+  vnc_export_bgp_enable (bgp, afi);
+}
diff --git a/bgpd/rfapi/vnc_export_bgp.h b/bgpd/rfapi/vnc_export_bgp.h
new file mode 100644 (file)
index 0000000..ab2197d
--- /dev/null
@@ -0,0 +1,42 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_
+#define _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_
+
+#include "zebra.h"
+#include "prefix.h"
+
+#include "bgpd.h"
+#include "bgp_route.h"
+
+
+extern void vnc_direct_bgp_rh_reexport (struct bgp *bgp, afi_t afi);
+
+extern void vnc_export_bgp_prechange (struct bgp *bgp);
+
+extern void vnc_export_bgp_postchange (struct bgp *bgp);
+
+extern void vnc_export_bgp_enable (struct bgp *bgp, afi_t afi);
+
+extern void vnc_export_bgp_disable (struct bgp *bgp, afi_t afi);
+
+#endif /* _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_ */
diff --git a/bgpd/rfapi/vnc_export_bgp_p.h b/bgpd/rfapi/vnc_export_bgp_p.h
new file mode 100644 (file)
index 0000000..628778a
--- /dev/null
@@ -0,0 +1,95 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_
+#define _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_
+
+#include "zebra.h"
+#include "prefix.h"
+
+#include "bgpd.h"
+#include "bgp_route.h"
+
+#include "rfapi_private.h"
+
+extern void
+vnc_direct_bgp_add_route_ce (
+    struct bgp         *bgp,
+    struct route_node  *rn,
+    struct bgp_info    *bi);
+
+extern void
+vnc_direct_bgp_del_route_ce (
+    struct bgp         *bgp,
+    struct route_node  *rn,
+    struct bgp_info    *bi);
+
+extern void
+vnc_direct_bgp_add_prefix (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *import_table,
+    struct route_node          *rn);
+
+extern void
+vnc_direct_bgp_del_prefix (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *import_table,
+    struct route_node          *rn);
+
+extern void
+vnc_direct_bgp_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd);
+
+extern void
+vnc_direct_bgp_del_nve (struct bgp *bgp, struct rfapi_descriptor *rfd);
+
+extern void
+vnc_direct_bgp_add_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg);
+
+extern void
+vnc_direct_bgp_del_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg);
+
+extern void
+vnc_direct_bgp_reexport_group_afi (
+    struct bgp                 *bgp,
+    struct rfapi_nve_group_cfg *rfg,
+    afi_t                      afi);
+
+
+extern void
+vnc_direct_bgp_rh_add_route (
+    struct bgp         *bgp,
+    afi_t              afi,
+    struct prefix      *prefix,
+    struct peer                *peer,
+    struct attr                *attr);
+
+
+extern void
+vnc_direct_bgp_rh_del_route (
+    struct bgp         *bgp,
+    afi_t              afi,
+    struct prefix      *prefix,
+    struct peer                *peer);
+
+extern void
+vnc_direct_bgp_reexport (struct bgp *bgp, afi_t afi);
+
+#endif /* _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_ */
diff --git a/bgpd/rfapi/vnc_export_table.c b/bgpd/rfapi/vnc_export_table.c
new file mode 100644 (file)
index 0000000..5a96d8a
--- /dev/null
@@ -0,0 +1,214 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; 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 "table.h"
+#include "memory.h"
+#include "vty.h"
+
+#include "bgpd.h"
+#include "bgp_route.h"
+
+#include "vnc_export_table.h"
+#include "rfapi_private.h"
+#include "rfapi_import.h"
+
+struct route_node *
+vnc_etn_get (struct bgp *bgp, vnc_export_type_t type, struct prefix *p)
+{
+  struct route_table *t = NULL;
+  struct route_node *rn = NULL;
+  afi_t afi;
+
+  if (!bgp || !bgp->rfapi)
+    return NULL;
+
+  afi = family2afi (p->family);
+  assert (afi == AFI_IP || afi == AFI_IP6);
+
+  switch (type)
+    {
+    case EXPORT_TYPE_BGP:
+      if (!bgp->rfapi->rt_export_bgp[afi])
+        bgp->rfapi->rt_export_bgp[afi] = route_table_init ();
+      t = bgp->rfapi->rt_export_bgp[afi];
+      break;
+
+    case EXPORT_TYPE_ZEBRA:
+      if (!bgp->rfapi->rt_export_zebra[afi])
+        bgp->rfapi->rt_export_zebra[afi] = route_table_init ();
+      t = bgp->rfapi->rt_export_zebra[afi];
+      break;
+    }
+
+  if (t)
+    rn = route_node_get (t, p);
+  return rn;
+}
+
+struct route_node *
+vnc_etn_lookup (struct bgp *bgp, vnc_export_type_t type, struct prefix *p)
+{
+  struct route_table *t = NULL;
+  struct route_node *rn = NULL;
+  afi_t afi;
+
+  if (!bgp || !bgp->rfapi)
+    return NULL;
+
+  afi = family2afi (p->family);
+  assert (afi == AFI_IP || afi == AFI_IP6);
+
+  switch (type)
+    {
+    case EXPORT_TYPE_BGP:
+      if (!bgp->rfapi->rt_export_bgp[afi])
+        bgp->rfapi->rt_export_bgp[afi] = route_table_init ();
+      t = bgp->rfapi->rt_export_bgp[afi];
+      break;
+
+    case EXPORT_TYPE_ZEBRA:
+      if (!bgp->rfapi->rt_export_zebra[afi])
+        bgp->rfapi->rt_export_zebra[afi] = route_table_init ();
+      t = bgp->rfapi->rt_export_zebra[afi];
+      break;
+    }
+
+  if (t)
+    rn = route_node_lookup (t, p);
+  return rn;
+}
+
+struct vnc_export_info *
+vnc_eti_get (
+    struct bgp         *bgp,
+    vnc_export_type_t  etype,
+    struct prefix      *p,
+    struct peer                *peer,
+    uint8_t            type,
+    uint8_t            subtype)
+{
+  struct route_node *etn;
+  struct vnc_export_info *eti;
+
+  etn = vnc_etn_get (bgp, etype, p);
+  assert (etn);
+
+  for (eti = etn->info; eti; eti = eti->next)
+    {
+      if (peer == eti->peer && type == eti->type && subtype == eti->subtype)
+        {
+
+          break;
+        }
+    }
+
+  if (eti)
+    {
+      route_unlock_node (etn);
+    }
+  else
+    {
+      eti = XCALLOC (MTYPE_RFAPI_ETI, sizeof (struct vnc_export_info));
+      assert (eti);
+      eti->node = etn;
+      eti->peer = peer;
+      peer_lock (peer);
+      eti->type = type;
+      eti->subtype = subtype;
+      eti->next = etn->info;
+      etn->info = eti;
+    }
+
+  return eti;
+}
+
+void
+vnc_eti_delete (struct vnc_export_info *goner)
+{
+  struct route_node *etn;
+  struct vnc_export_info *eti;
+  struct vnc_export_info *eti_prev = NULL;
+
+  etn = goner->node;
+
+  for (eti = etn->info; eti; eti_prev = eti, eti = eti->next)
+    {
+      if (eti == goner)
+        break;
+    }
+
+  if (!eti)
+    {
+      zlog_debug ("%s: COULDN'T FIND ETI", __func__);
+      return;
+    }
+
+  if (eti_prev)
+    {
+      eti_prev->next = goner->next;
+    }
+  else
+    {
+      etn->info = goner->next;
+    }
+
+  peer_unlock (eti->peer);
+  goner->node = NULL;
+  XFREE (MTYPE_RFAPI_ETI, goner);
+
+  route_unlock_node (etn);
+}
+
+struct vnc_export_info *
+vnc_eti_checktimer (
+    struct bgp         *bgp,
+    vnc_export_type_t  etype,
+    struct prefix      *p,
+    struct peer                *peer,
+    uint8_t            type,
+    uint8_t            subtype)
+{
+  struct route_node *etn;
+  struct vnc_export_info *eti;
+
+  etn = vnc_etn_lookup (bgp, etype, p);
+  if (!etn)
+    return NULL;
+
+  for (eti = etn->info; eti; eti = eti->next)
+    {
+      if (peer == eti->peer && type == eti->type && subtype == eti->subtype)
+        {
+
+          break;
+        }
+    }
+
+  route_unlock_node (etn);
+
+  if (eti && eti->timer)
+    return eti;
+
+  return NULL;
+}
diff --git a/bgpd/rfapi/vnc_export_table.h b/bgpd/rfapi/vnc_export_table.h
new file mode 100644 (file)
index 0000000..231861a
--- /dev/null
@@ -0,0 +1,85 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_VNC_VNC_EXPORT_TABLE_H_
+#define _QUAGGA_VNC_VNC_EXPORT_TABLE_H_
+
+#include "table.h"
+#include "thread.h"
+#include "vty.h"
+
+#include "bgpd.h"
+
+#define VNC_EXPORT_TYPE_BGP    1
+#define VNC_EXPORT_TYPE_ZEBRA  2
+
+typedef enum vnc_export_type
+{
+  EXPORT_TYPE_BGP,
+  EXPORT_TYPE_ZEBRA
+} vnc_export_type_t;
+
+struct vnc_export_info
+{
+  struct vnc_export_info       *next;
+  struct route_node            *node;
+  struct peer                  *peer;
+  u_char                       type;
+  u_char                       subtype;
+  uint32_t                     lifetime;
+  struct thread                        *timer;
+};
+
+extern struct route_node *
+vnc_etn_get (
+    struct bgp         *bgp,
+    vnc_export_type_t  type,
+    struct prefix      *p);
+
+extern struct route_node *
+vnc_etn_lookup (
+    struct bgp         *bgp,
+    vnc_export_type_t  type,
+    struct prefix      *p);
+
+extern struct vnc_export_info *
+vnc_eti_get (
+    struct bgp         *bgp,
+    vnc_export_type_t  etype,
+    struct prefix      *p,
+    struct peer                *peer,
+    uint8_t            type,
+    uint8_t            subtype);
+
+extern void
+vnc_eti_delete (struct vnc_export_info *goner);
+
+extern struct vnc_export_info *
+vnc_eti_checktimer (
+    struct bgp         *bgp,
+    vnc_export_type_t  etype,
+    struct prefix      *p,
+    struct peer                *peer,
+    uint8_t            type,
+    uint8_t            subtype);
+
+
+#endif /* _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ */
diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c
new file mode 100644 (file)
index 0000000..020cf18
--- /dev/null
@@ -0,0 +1,3165 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/*
+ * File:       vnc_import_bgp.c
+ * Purpose:    Import routes from BGP unicast directly (not via zebra)
+ */
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "log.h"
+#include "memory.h"
+#include "linklist.h"
+#include "plist.h"
+#include "routemap.h"
+
+#include "bgpd.h"
+#include "bgp_ecommunity.h"
+#include "bgp_attr.h"
+#include "bgp_mplsvpn.h"        /* for RD_TYPE_IP */
+
+#include "vnc_export_bgp.h"
+#include "bgp_rfapi_cfg.h"
+#include "rfapi.h"
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_monitor.h"
+#include "rfapi_vty.h"
+#include "vnc_import_bgp.h"
+#include "vnc_import_bgp_p.h"
+#include "vnc_debug.h"
+
+#define ENABLE_VNC_RHNCK
+
+#define DEBUG_RHN_LIST 0
+
+static struct rfapi_descriptor vncHDBgpDirect;  /* dummy nve descriptor */
+static struct rfapi_descriptor vncHDResolveNve; /* dummy nve descriptor */
+
+/*
+ * For routes from another AS:
+ *
+ * If MED is set,
+ *     LOCAL_PREF = 255 - MIN(255, MED)
+ * else
+ *     LOCAL_PREF = default_local_pref
+ *
+ * For routes from the same AS:
+ *
+ *     LOCAL_PREF unchanged
+ */
+uint32_t
+calc_local_pref (struct attr *attr, struct peer *peer)
+{
+  uint32_t local_pref = 0;
+
+  if (!attr)
+    {
+      if (peer)
+        {
+          return peer->bgp->default_local_pref;
+        }
+      return bgp_get_default ()->default_local_pref;
+    }
+
+  if (peer && (peer->as != peer->bgp->as))
+    {
+      if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+        {
+          if (attr->med > 255)
+            {
+              local_pref = 0;
+            }
+          else
+            {
+              local_pref = 255 - attr->med;
+            }
+        }
+      else
+        {
+          local_pref = peer->bgp->default_local_pref;
+        }
+    }
+  else
+    {
+      if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+        {
+          local_pref = attr->local_pref;
+        }
+      else
+        {
+          if (peer && peer->bgp)
+            {
+              local_pref = peer->bgp->default_local_pref;
+            }
+        }
+    }
+
+  return local_pref;
+}
+
+static int
+is_host_prefix (struct prefix *p)
+{
+  switch (p->family)
+    {
+    case AF_INET:
+      return (p->prefixlen == 32);
+    case AF_INET6:
+      return (p->prefixlen == 128);
+    }
+  return 0;
+}
+
+/***********************************************************************
+ *                             RHN list
+ ***********************************************************************/
+
+struct prefix_bag
+{
+  struct prefix hpfx;           /* ce address = unicast nexthop */
+  struct prefix upfx;           /* unicast prefix */
+  struct bgp_info *ubi;         /* unicast route */
+};
+
+static const u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0,
+  0xf8, 0xfc, 0xfe, 0xff
+};
+
+int
+vnc_prefix_cmp (void *pfx1, void *pfx2)
+{
+  int offset;
+  int shift;
+  u_char mask;
+
+  struct prefix *p1 = pfx1;
+  struct prefix *p2 = pfx2;
+
+  if (p1->family < p2->family)
+    return -1;
+  if (p1->family > p2->family)
+    return 1;
+
+  if (p1->prefixlen < p2->prefixlen)
+    return -1;
+  if (p1->prefixlen > p2->prefixlen)
+    return 1;
+
+  offset = p1->prefixlen / 8;
+  shift = p1->prefixlen % 8;
+  if (shift == 0 && offset)
+    {                           /* catch aligned case */
+      offset--;
+      shift = 8;
+    }
+
+  /* Set both prefix's head pointer. */
+  const u_char *pp1 = (const u_char *) &p1->u.prefix;
+  const u_char *pp2 = (const u_char *) &p2->u.prefix;
+
+  while (offset--)
+    {
+      if (*pp1 < *pp2)
+        return -1;
+      if (*pp1 > *pp2)
+        return 1;
+      ++pp1;
+      ++pp2;
+    }
+
+  mask = maskbit[shift];
+  if ((*pp1 & mask) < (*pp2 & mask))
+    return -1;
+  if ((*pp1 & mask) > (*pp2 & mask))
+    return 1;
+
+  return 0;
+}
+
+static void
+prefix_bag_free (void *pb)
+{
+  XFREE (MTYPE_RFAPI_PREFIX_BAG, pb);
+}
+
+#if DEBUG_RHN_LIST
+static void
+print_rhn_list (const char *tag1, const char *tag2)
+{
+  struct bgp *bgp = bgp_get_default ();
+  struct skiplist *sl = bgp->rfapi->resolve_nve_nexthop;
+  struct skiplistnode *p;
+  struct prefix_bag *pb;
+  int count = 0;
+
+  if (!sl)
+    {
+      zlog_debug ("%s: %s: RHN List is empty", (tag1 ? tag1 : ""),
+                  (tag2 ? tag2 : ""));
+      return;
+    }
+
+  zlog_debug ("%s: %s: RHN list:", (tag1 ? tag1 : ""), (tag2 ? tag2 : ""));
+
+  /* XXX uses secret knowledge of skiplist structure */
+  for (p = sl->header->forward[0]; p; p = p->forward[0])
+    {
+      char kbuf[BUFSIZ];
+      char hbuf[BUFSIZ];
+      char ubuf[BUFSIZ];
+
+      pb = p->value;
+
+      prefix2str (p->key, kbuf, BUFSIZ);
+      prefix2str (&pb->hpfx, hbuf, BUFSIZ);
+      prefix2str (&pb->upfx, ubuf, BUFSIZ);
+
+      zlog_debug ("RHN Entry %d (q=%p): kpfx=%s, upfx=%s, hpfx=%s, ubi=%p",
+                  ++count, p, kbuf, ubuf, hbuf, pb->ubi);
+    }
+}
+#endif
+
+#ifdef ENABLE_VNC_RHNCK
+static void
+vnc_rhnck (char *tag)
+{
+  struct bgp *bgp;
+  struct skiplist *sl;
+  struct skiplistnode *p;
+
+  bgp = bgp_get_default ();
+  sl = bgp->rfapi->resolve_nve_nexthop;
+
+  if (!sl)
+    return;
+
+  /* XXX uses secret knowledge of skiplist structure */
+  for (p = sl->header->forward[0]; p; p = p->forward[0])
+    {
+      struct prefix_bag *pb;
+      struct prefix *pkey;
+      afi_t afi;
+      struct prefix pfx_orig_nexthop;
+
+      memset (&pfx_orig_nexthop, 0, sizeof (struct prefix));    /* keep valgrind happy */
+
+      pkey = p->key;
+      pb = p->value;
+
+      afi = family2afi (pb->upfx.family);
+
+      rfapiUnicastNexthop2Prefix (afi, pb->ubi->attr, &pfx_orig_nexthop);
+
+      /* pb->hpfx, pb->ubi nexthop, pkey should all reflect the same pfx */
+      assert (!vnc_prefix_cmp (&pb->hpfx, pkey));
+      if (vnc_prefix_cmp (&pb->hpfx, &pfx_orig_nexthop))
+        {
+          char str_onh[BUFSIZ];
+          char str_nve_pfx[BUFSIZ];
+
+          prefix2str (&pfx_orig_nexthop, str_onh, BUFSIZ);
+          str_onh[BUFSIZ - 1] = 0;
+
+          prefix2str (&pb->hpfx, str_nve_pfx, BUFSIZ);
+          str_nve_pfx[BUFSIZ - 1] = 0;
+
+          zlog_debug
+            ("%s: %s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s",
+             __func__, tag, str_onh, str_nve_pfx);
+          assert (0);
+        }
+    }
+  zlog_debug ("%s: vnc_rhnck OK", tag);
+}
+
+#define VNC_RHNCK(n)   do {char buf[BUFSIZ];sprintf(buf,"%s: %s", __func__, #n);vnc_rhnck(buf);} while (0)
+
+#else
+
+#define VNC_RHNCK(n)
+
+#endif
+
+/***********************************************************************
+ *                     Add/Delete Unicast Route
+ ***********************************************************************/
+
+/*
+ * "Adding a Route" import process
+ */
+
+/*
+ * extract and package information from the BGP unicast route.
+ * Return code 0 means OK, non-0 means drop.
+ *
+ * If return code is 0, caller MUST release ecom
+ */
+static int
+process_unicast_route (
+    struct bgp         *bgp,                   /* in */
+    afi_t              afi,                    /* in */
+    struct prefix      *prefix,                /* in */
+    struct bgp_info    *info,                  /* in */
+    struct ecommunity  **ecom,                 /* OUT */
+    struct prefix      *unicast_nexthop)       /* OUT */
+{
+  struct rfapi_cfg *hc = bgp->rfapi_cfg;
+  struct peer *peer = info->peer;
+  struct attr *attr = info->attr;
+  struct attr hattr;
+  struct route_map *rmap = NULL;
+  struct prefix pfx_orig_nexthop;
+
+  memset (&pfx_orig_nexthop, 0, sizeof (struct prefix));        /* keep valgrind happy */
+
+  /*
+   * prefix list check
+   */
+  if (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi])
+    {
+      zlog_debug ("%s: HC prefix list is set, checking", __func__);
+      if (prefix_list_apply
+          (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi],
+           prefix) == PREFIX_DENY)
+        {
+          zlog_debug ("%s: prefix list returns DENY, blocking route",
+                      __func__);
+          return -1;
+        }
+      zlog_debug ("%s: prefix list returns PASS, allowing route", __func__);
+    }
+
+  /* apply routemap, if any, later */
+  rmap = hc->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT];
+
+  /*
+   * Extract original nexthop, which we expect to be a NVE connected router
+   * Note that this is the nexthop before any possible application of policy
+   */
+  /*
+   * Incoming prefix is unicast. If v6, it is in multiprotocol area,
+   * but if v4 it is in attr->nexthop
+   */
+  rfapiUnicastNexthop2Prefix (afi, attr, &pfx_orig_nexthop);
+
+  /*
+   * route map handling
+   * This code is here because it allocates an interned attr which
+   * must be freed before we return. It's easier to put it after
+   * all of the possible returns above.
+   */
+  memset (&hattr, 0, sizeof (struct attr));
+  bgp_attr_dup (&hattr, attr);  /* hattr becomes a ghost attr */
+
+  if (rmap)
+    {
+      struct bgp_info info;
+      route_map_result_t ret;
+
+      memset (&info, 0, sizeof (info));
+      info.peer = peer;
+      info.attr = &hattr;
+      ret = route_map_apply (rmap, prefix, RMAP_BGP, &info);
+      if (ret == RMAP_DENYMATCH)
+        {
+          bgp_attr_flush (&hattr);
+          bgp_attr_extra_free (&hattr);
+          zlog_debug ("%s: route map \"%s\" says DENY, returning", __func__,
+                      rmap->name);
+          return -1;
+        }
+    }
+
+  /*
+   * Get the (possibly altered by policy) unicast nexthop
+   * for later lookup in the Import Table by caller
+   */
+  rfapiUnicastNexthop2Prefix (afi, &hattr, unicast_nexthop);
+
+  if (hattr.extra && hattr.extra->ecommunity)
+    *ecom = ecommunity_dup (hattr.extra->ecommunity);
+  else
+    *ecom = ecommunity_new ();
+
+  /*
+   * Done with hattr, clean up
+   */
+  bgp_attr_flush (&hattr);
+  bgp_attr_extra_free (&hattr);
+
+  /*
+   * Add EC that carries original NH of iBGP route (2 bytes = magic
+   * value indicating it came from an VNC gateway; default 5226, but
+   * must be user configurable). Note that this is the nexthop before
+   * any application of policy.
+   */
+  {
+    struct ecommunity_val vnc_gateway_magic;
+    uint16_t localadmin;
+
+    /* Using route origin extended community type */
+    memset (&vnc_gateway_magic, 0, sizeof (vnc_gateway_magic));
+    vnc_gateway_magic.val[0] = 0x01;
+    vnc_gateway_magic.val[1] = 0x03;
+
+    /* Only works for IPv4 nexthops */
+    if (prefix->family == AF_INET)
+      {
+        memcpy (vnc_gateway_magic.val + 2, &unicast_nexthop->u.prefix4, 4);
+      }
+    localadmin = htons (hc->resolve_nve_roo_local_admin);
+    memcpy (vnc_gateway_magic.val + 6, (char *) &localadmin, 2);
+
+    ecommunity_add_val (*ecom, &vnc_gateway_magic);
+  }
+
+  return 0;
+}
+
+
+static void
+vnc_import_bgp_add_route_mode_resolve_nve_one_bi (
+  struct bgp           *bgp,
+  afi_t                        afi,
+  struct bgp_info      *bi,        /* VPN bi */
+  struct prefix_rd     *prd,       /* RD */
+  struct prefix                *prefix,    /* unicast route prefix */
+  uint32_t             *local_pref,/* NULL = no local_pref */
+  uint32_t             *med,       /* NULL = no med */
+  struct ecommunity    *ecom)      /* generated ecoms */
+{
+  struct prefix un;
+  struct prefix nexthop;
+  struct rfapi_ip_addr nexthop_h;
+  uint32_t lifetime;
+  uint32_t *plifetime;
+  struct bgp_attr_encap_subtlv *encaptlvs;
+
+  zlog_debug ("%s: entry", __func__);
+
+  if (bi->type != ZEBRA_ROUTE_BGP && bi->type != ZEBRA_ROUTE_BGP_DIRECT)
+    {
+
+      return;
+    }
+  if (bi->sub_type != BGP_ROUTE_NORMAL &&
+      bi->sub_type != BGP_ROUTE_STATIC && bi->sub_type != BGP_ROUTE_RFP)
+    {
+
+      return;
+    }
+  if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+    return;
+
+  vncHDResolveNve.peer = bi->peer;
+  if (!rfapiGetVncTunnelUnAddr (bi->attr, &un))
+    {
+      if (rfapiQprefix2Raddr (&un, &vncHDResolveNve.un_addr))
+        return;
+    }
+  else
+    {
+      memset (&vncHDResolveNve.un_addr, 0, sizeof (vncHDResolveNve.un_addr));
+    }
+
+  /* Use nexthop of VPN route as nexthop of constructed route */
+  rfapiNexthop2Prefix (bi->attr, &nexthop);
+  rfapiQprefix2Raddr (&nexthop, &nexthop_h);
+
+  if (rfapiGetVncLifetime (bi->attr, &lifetime))
+    {
+      plifetime = NULL;
+    }
+  else
+    {
+      plifetime = &lifetime;
+    }
+
+  if (bi->attr && bi->attr->extra)
+    {
+      encaptlvs = bi->attr->extra->vnc_subtlvs;
+    }
+  else
+    {
+      encaptlvs = NULL;
+    }
+
+  struct ecommunity *new_ecom = ecommunity_dup (ecom);
+
+  if (bi->attr && bi->attr->extra && bi->attr->extra->ecommunity)
+    ecommunity_merge (new_ecom, bi->attr->extra->ecommunity);
+
+  add_vnc_route (
+    &vncHDResolveNve,
+    bgp,
+    SAFI_MPLS_VPN,
+    prefix,                                    /* unicast route prefix */
+    prd,
+    &nexthop_h,                                        /* new nexthop */
+    local_pref,
+    plifetime,
+    (struct bgp_tea_options *) encaptlvs,      /* RFP options */
+    NULL,
+    NULL,
+    new_ecom,
+    med,                                       /* NULL => don't set med */
+    NULL,                                      /* label: default */
+    ZEBRA_ROUTE_BGP_DIRECT,
+    BGP_ROUTE_REDISTRIBUTE,
+    RFAPI_AHR_RFPOPT_IS_VNCTLV);               /* flags */
+
+  ecommunity_free (&new_ecom);
+
+}
+
+static void
+vnc_import_bgp_add_route_mode_resolve_nve_one_rd (
+    struct prefix_rd   *prd,           /* RD */
+    struct bgp_table   *table_rd,      /* per-rd VPN route table */
+    afi_t              afi,
+    struct bgp         *bgp,
+    struct prefix      *prefix,        /* unicast prefix */
+    struct ecommunity  *ecom,          /* generated ecoms */
+    uint32_t           *local_pref,    /* NULL = no local_pref */
+    uint32_t           *med,           /* NULL = no med */
+    struct prefix      *ubi_nexthop)   /* unicast nexthop */
+{
+  struct bgp_node *bn;
+  struct bgp_info *bi;
+
+  if (!table_rd)
+    return;
+
+  {
+    char str_nh[BUFSIZ];
+
+    prefix2str (ubi_nexthop, str_nh, BUFSIZ);
+    str_nh[BUFSIZ - 1] = 0;
+
+    zlog_debug ("%s: ubi_nexthop=%s", __func__, str_nh);
+  }
+
+  /* exact match */
+  bn = bgp_node_lookup (table_rd, ubi_nexthop);
+  if (!bn)
+    {
+      zlog_debug ("%s: no match in RD's table for ubi_nexthop", __func__);
+      return;
+    }
+
+  /* Iterate over bgp_info items at this node */
+  for (bi = bn->info; bi; bi = bi->next)
+    {
+
+      vnc_import_bgp_add_route_mode_resolve_nve_one_bi (bgp, afi, bi,   /* VPN bi */
+                                                        prd,
+                                                        prefix,
+                                                        local_pref,
+                                                        med, ecom);
+    }
+
+  bgp_unlock_node (bn);
+}
+
+static void
+vnc_import_bgp_add_route_mode_resolve_nve (
+    struct bgp         *bgp,
+    struct prefix      *prefix,/* unicast prefix */
+    struct bgp_info    *info)  /* unicast info */
+{
+  afi_t afi = family2afi (prefix->family);
+  struct rfapi_cfg *hc = NULL;
+
+  struct prefix pfx_unicast_nexthop = { 0 };    /* happy valgrind */
+
+  struct ecommunity *ecom = NULL;
+  uint32_t local_pref;
+  uint32_t *med = NULL;
+
+  struct prefix_bag *pb;
+  struct bgp_node *bnp;         /* prd table node */
+
+  /*debugging */
+  {
+    char str_pfx[BUFSIZ];
+    char str_nh[BUFSIZ];
+    struct prefix nh;
+
+    prefix2str (prefix, str_pfx, BUFSIZ);
+    str_pfx[BUFSIZ - 1] = 0;
+
+    nh.prefixlen = 0;
+    rfapiUnicastNexthop2Prefix (afi, info->attr, &nh);
+    if (nh.prefixlen)
+      {
+        prefix2str (&nh, str_nh, BUFSIZ);
+        str_nh[BUFSIZ - 1] = 0;
+      }
+    else
+      {
+        str_nh[0] = '?';
+        str_nh[1] = 0;
+      }
+
+    zlog_debug ("%s(bgp=%p, unicast prefix=%s, unicast nh=%s)",
+                __func__, bgp, str_pfx, str_nh);
+  }
+
+  if (info->type != ZEBRA_ROUTE_BGP)
+    {
+      zlog_debug ("%s: unicast type %d=\"%s\" is not %d=%s, skipping",
+                  __func__, info->type, zebra_route_string (info->type),
+                  ZEBRA_ROUTE_BGP, "ZEBRA_ROUTE_BGP");
+      return;
+    }
+
+  /*
+   * Preliminary checks
+   */
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi of prefix", __func__);
+      return;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  /* check vnc redist flag for bgp direct routes */
+  if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT])
+    {
+      zlog_debug
+        ("%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+         __func__, afi);
+      return;
+    }
+
+
+  if (process_unicast_route (bgp, afi, prefix, info,
+                             &ecom, &pfx_unicast_nexthop))
+    {
+
+      zlog_debug ("%s: process_unicast_route error, skipping", __func__);
+      return;
+    }
+
+  local_pref = calc_local_pref (info->attr, info->peer);
+  if (info->attr &&
+      (info->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)))
+    {
+
+      med = &info->attr->med;
+    }
+
+
+  /*
+   * At this point, we have allocated:
+   *
+   *  ecom    ecommunity ptr, union of unicast and ROO parts (no NVE part)
+   *
+   * And we have set:
+   *
+   *  pfx_unicast_nexthop     nexthop of uncast route
+   */
+
+  if (!bgp->rfapi->resolve_nve_nexthop)
+    {
+      bgp->rfapi->resolve_nve_nexthop =
+        skiplist_new (SKIPLIST_FLAG_ALLOW_DUPLICATES, vnc_prefix_cmp,
+                      prefix_bag_free);
+    }
+
+  pb = XCALLOC (MTYPE_RFAPI_PREFIX_BAG, sizeof (struct prefix_bag));
+  pb->hpfx = pfx_unicast_nexthop;
+  pb->ubi = info;
+  pb->upfx = *prefix;
+
+  bgp_info_lock (info);         /* skiplist refers to it */
+  skiplist_insert (bgp->rfapi->resolve_nve_nexthop, &pb->hpfx, pb);
+
+  /*
+   * Iterate over RDs in VPN RIB. For each RD, look up unicast nexthop
+   * (exact match, /32). If an exact match is found, call add_vnc_route.
+   */
+
+  for (bnp = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); bnp;
+       bnp = bgp_route_next (bnp))
+    {
+
+      struct bgp_table *table;
+
+      table = (struct bgp_table *) (bnp->info);
+
+      if (!table)
+        continue;
+
+      vnc_import_bgp_add_route_mode_resolve_nve_one_rd ((struct prefix_rd *)
+                                                        &bnp->p, table, afi,
+                                                        bgp, prefix, ecom,
+                                                        &local_pref, med,
+                                                        &pfx_unicast_nexthop);
+
+    }
+
+
+  if (ecom)
+    ecommunity_free (&ecom);
+
+  zlog_debug ("%s: done", __func__);
+}
+
+
+static void
+vnc_import_bgp_add_route_mode_plain (struct bgp *bgp,
+                                     struct prefix *prefix,
+                                     struct bgp_info *info)
+{
+  afi_t afi = family2afi (prefix->family);
+  struct peer *peer = info->peer;
+  struct attr *attr = info->attr;
+  struct attr hattr;
+  struct rfapi_cfg *hc = NULL;
+  struct attr *iattr = NULL;
+
+  struct rfapi_ip_addr vnaddr;
+  struct prefix vn_pfx_space;
+  struct prefix *vn_pfx = NULL;
+  int ahr_flags = 0;
+  struct ecommunity *ecom = NULL;
+  struct prefix_rd prd;
+  struct route_map *rmap = NULL;
+  uint32_t local_pref;
+  uint32_t *med = NULL;
+
+  {
+    char buf[BUFSIZ];
+
+    buf[0] = 0;
+    prefix2str (prefix, buf, BUFSIZ);
+    buf[BUFSIZ - 1] = 0;
+    zlog_debug ("%s(prefix=%s) entry", __func__, buf);
+  }
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi of prefix", __func__);
+      return;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  /* check vnc redist flag for bgp direct routes */
+  if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT])
+    {
+      zlog_debug
+        ("%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+         __func__, afi);
+      return;
+    }
+
+  /*
+   * mode "plain" specific code
+   */
+  {
+    zlog_debug ("%s: NOT using redist RFG", __func__);
+
+    /*
+     * prefix list check
+     */
+    if (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi])
+      {
+        zlog_debug ("%s: HC prefix list is set, checking", __func__);
+        if (prefix_list_apply
+            (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi],
+             prefix) == PREFIX_DENY)
+          {
+            zlog_debug ("%s: prefix list returns DENY, blocking route",
+                        __func__);
+            return;
+          }
+        zlog_debug ("%s: prefix list returns PASS, allowing route", __func__);
+      }
+
+    /* apply routemap, if any, later */
+    rmap = hc->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT];
+
+    /*
+     * Incoming prefix is unicast. If v6, it is in multiprotocol area,
+     * but if v4 it is in attr->nexthop
+     */
+    rfapiUnicastNexthop2Prefix (afi, attr, &vn_pfx_space);
+    vn_pfx = &vn_pfx_space;
+
+    /* UN address */
+    ahr_flags |= RFAPI_AHR_NO_TUNNEL_SUBTLV;
+  }
+
+  if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE))
+    {
+      char buf[BUFSIZ];
+
+      buf[0] = 0;
+      prefix2str (vn_pfx, buf, BUFSIZ);
+      buf[BUFSIZ - 1] = 0;
+      zlog_debug ("%s vn_pfx=%s", __func__, buf);
+    }
+
+  /*
+   * Compute VN address
+   */
+  if (rfapiQprefix2Raddr (vn_pfx, &vnaddr))
+    {
+      zlog_debug ("%s: redist VN invalid, skipping", __func__);
+      return;
+    }
+
+  /*
+   * route map handling
+   * This code is here because it allocates an interned attr which
+   * must be freed before we return. It's easier to put it after
+   * all of the possible returns above.
+   */
+  memset (&hattr, 0, sizeof (struct attr));
+  bgp_attr_dup (&hattr, attr);  /* hattr becomes a ghost attr */
+
+  if (rmap)
+    {
+      struct bgp_info info;
+      route_map_result_t ret;
+
+      memset (&info, 0, sizeof (info));
+      info.peer = peer;
+      info.attr = &hattr;
+      ret = route_map_apply (rmap, prefix, RMAP_BGP, &info);
+      if (ret == RMAP_DENYMATCH)
+        {
+          bgp_attr_flush (&hattr);
+          bgp_attr_extra_free (&hattr);
+          zlog_debug ("%s: route map \"%s\" says DENY, returning", __func__,
+                      rmap->name);
+          return;
+        }
+    }
+
+  iattr = bgp_attr_intern (&hattr);
+  bgp_attr_flush (&hattr);
+  bgp_attr_extra_free (&hattr);
+
+  /* Now iattr is an allocated interned attr */
+
+  /*
+   * Mode "plain" specific code
+   *
+   * Sets RD in dummy HD
+   * Allocates ecom
+   */
+  {
+    if (vnaddr.addr_family != AF_INET)
+      {
+        zlog_debug
+          ("%s: can't auto-assign RD, VN AF (%d) is not IPv4, skipping",
+           __func__, vnaddr.addr_family);
+        if (iattr)
+          {
+            bgp_attr_unintern (&iattr);
+          }
+        return;
+      }
+    memset (&prd, 0, sizeof (prd));
+    rfapi_set_autord_from_vn (&prd, &vnaddr);
+
+    if (iattr && iattr->extra && iattr->extra->ecommunity)
+      ecom = ecommunity_dup (iattr->extra->ecommunity);
+
+  }
+
+  local_pref = calc_local_pref (iattr, peer);
+
+  if (iattr && (iattr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)))
+    {
+      med = &iattr->med;
+    }
+
+  if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE))
+    {
+      char buf[BUFSIZ];
+
+      buf[0] = 0;
+      rfapiRfapiIpAddr2Str (&vnaddr, buf, BUFSIZ);
+      buf[BUFSIZ - 1] = 0;
+      zlog_debug ("%s: setting vnaddr to %s", __func__, buf);
+    }
+
+  vncHDBgpDirect.peer = peer;
+  add_vnc_route (&vncHDBgpDirect, bgp, SAFI_MPLS_VPN, prefix, &prd, &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime), NULL,     /* RFP options */
+                 NULL, NULL, ecom, med, /* med */
+                 NULL,          /* label: default */
+                 ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, ahr_flags);
+  vncHDBgpDirect.peer = NULL;
+
+  if (ecom)
+    ecommunity_free (&ecom);
+}
+
+static void
+vnc_import_bgp_add_route_mode_nvegroup (struct bgp *bgp,
+                                        struct prefix *prefix,
+                                        struct bgp_info *info,
+                                        struct rfapi_nve_group_cfg *rfg)
+{
+  afi_t afi = family2afi (prefix->family);
+  struct peer *peer = info->peer;
+  struct attr *attr = info->attr;
+  struct attr hattr;
+  struct rfapi_cfg *hc = NULL;
+  struct attr *iattr = NULL;
+
+  struct rfapi_ip_addr vnaddr;
+  struct prefix *vn_pfx = NULL;
+  int ahr_flags = 0;
+  struct ecommunity *ecom = NULL;
+  struct prefix_rd prd;
+  struct route_map *rmap = NULL;
+  uint32_t local_pref;
+  uint32_t *med = NULL;
+
+  {
+    char buf[BUFSIZ];
+
+    buf[0] = 0;
+    prefix2str (prefix, buf, BUFSIZ);
+    buf[BUFSIZ - 1] = 0;
+    zlog_debug ("%s(prefix=%s) entry", __func__, buf);
+  }
+
+  assert (rfg);
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi of prefix", __func__);
+      return;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  /* check vnc redist flag for bgp direct routes */
+  if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT])
+    {
+      zlog_debug
+        ("%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+         __func__, afi);
+      return;
+    }
+
+
+  /*
+   * RFG-specific code
+   */
+  {
+
+    struct rfapi_ip_prefix pfx_un;
+
+    zlog_debug ("%s: using redist RFG", __func__);
+
+    /*
+     * RFG prefix list check
+     */
+    if (rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi])
+      {
+        zlog_debug ("%s: RFG prefix list is set, checking", __func__);
+        if (prefix_list_apply
+            (rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi],
+             prefix) == PREFIX_DENY)
+          {
+            zlog_debug ("%s: prefix list returns DENY, blocking route",
+                        __func__);
+            return;
+          }
+        zlog_debug ("%s: prefix list returns PASS, allowing route", __func__);
+      }
+
+    /* apply routemap, if any, later */
+    rmap = rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT];
+
+    /*
+     * export nve group's VN addr prefix must be a /32 which
+     * will yield the VN addr to use
+     */
+    vn_pfx = &rfg->vn_prefix;
+
+    /*
+     * UN Address
+     */
+    if (!is_host_prefix (&rfg->un_prefix))
+      {
+        /* NB prefixlen==0 means it has not been configured */
+        zlog_debug ("%s: redist RFG UN pfx not host pfx (plen=%d), skipping",
+                    __func__, rfg->un_prefix.prefixlen);
+        return;
+      }
+
+    rfapiQprefix2Rprefix (&rfg->un_prefix, &pfx_un);
+
+    vncHDBgpDirect.un_addr = pfx_un.prefix;
+  }
+
+  if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE))
+    {
+      char buf[BUFSIZ];
+
+      buf[0] = 0;
+      prefix2str (vn_pfx, buf, BUFSIZ);
+      buf[BUFSIZ - 1] = 0;
+      zlog_debug ("%s vn_pfx=%s", __func__, buf);
+    }
+
+  /*
+   * Compute VN address
+   */
+  if (rfapiQprefix2Raddr (vn_pfx, &vnaddr))
+    {
+      zlog_debug ("%s: redist VN invalid, skipping", __func__);
+      return;
+    }
+
+  /*
+   * route map handling
+   * This code is here because it allocates an interned attr which
+   * must be freed before we return. It's easier to put it after
+   * all of the possible returns above.
+   */
+  memset (&hattr, 0, sizeof (struct attr));
+  bgp_attr_dup (&hattr, attr);  /* hattr becomes a ghost attr */
+
+  if (rmap)
+    {
+      struct bgp_info info;
+      route_map_result_t ret;
+
+      memset (&info, 0, sizeof (info));
+      info.peer = peer;
+      info.attr = &hattr;
+      ret = route_map_apply (rmap, prefix, RMAP_BGP, &info);
+      if (ret == RMAP_DENYMATCH)
+        {
+          bgp_attr_flush (&hattr);
+          bgp_attr_extra_free (&hattr);
+          zlog_debug ("%s: route map \"%s\" says DENY, returning", __func__,
+                      rmap->name);
+          return;
+        }
+    }
+
+  iattr = bgp_attr_intern (&hattr);
+  bgp_attr_flush (&hattr);
+  bgp_attr_extra_free (&hattr);
+
+  /* Now iattr is an allocated interned attr */
+
+  /*
+   * RFG-specific code
+   *
+   * Sets RD in dummy HD
+   * Allocates ecom
+   */
+  {
+
+    memset (&prd, 0, sizeof (prd));
+    prd = rfg->rd;
+    prd.family = AF_UNSPEC;
+    prd.prefixlen = 64;
+
+    if (rfg->rd.family == AF_UNIX)
+      {
+        rfapi_set_autord_from_vn (&prd, &vnaddr);
+      }
+
+    if (rfg->rt_export_list)
+      ecom = ecommunity_dup (bgp->rfapi_cfg->rfg_redist->rt_export_list);
+    else
+      ecom = ecommunity_new ();
+
+    if (iattr && iattr->extra && iattr->extra->ecommunity)
+      ecom = ecommunity_merge (ecom, iattr->extra->ecommunity);
+  }
+
+  local_pref = calc_local_pref (iattr, peer);
+
+  if (iattr && (iattr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)))
+    {
+
+      med = &iattr->med;
+    }
+
+  if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE))
+    {
+      char buf[BUFSIZ];
+
+      buf[0] = 0;
+      rfapiRfapiIpAddr2Str (&vnaddr, buf, BUFSIZ);
+      buf[BUFSIZ - 1] = 0;
+      zlog_debug ("%s: setting vnaddr to %s", __func__, buf);
+    }
+
+  vncHDBgpDirect.peer = peer;
+  add_vnc_route (
+    &vncHDBgpDirect,
+    bgp,
+    SAFI_MPLS_VPN,
+    prefix,
+    &prd,
+    &vnaddr,
+    &local_pref,
+    &(bgp->rfapi_cfg->redist_lifetime),
+    NULL,          /* RFP options */
+    NULL,
+    NULL,
+    ecom,
+    NULL,          /* med */
+    NULL,          /* label: default */
+    ZEBRA_ROUTE_BGP_DIRECT,
+    BGP_ROUTE_REDISTRIBUTE,
+    ahr_flags);
+  vncHDBgpDirect.peer = NULL;
+
+  if (ecom)
+    ecommunity_free (&ecom);
+}
+
+static void
+vnc_import_bgp_del_route_mode_plain (struct bgp *bgp,
+                                     struct prefix *prefix,
+                                     struct bgp_info *info)
+{
+  struct prefix_rd prd;
+  afi_t afi = family2afi (prefix->family);
+  struct prefix *vn_pfx = NULL;
+  struct rfapi_ip_addr vnaddr;
+  struct prefix vn_pfx_space;
+
+
+  assert (afi);
+
+  /*
+   * Compute VN address
+   */
+
+  if (info && info->attr)
+    {
+      rfapiUnicastNexthop2Prefix (afi, info->attr, &vn_pfx_space);
+    }
+  else
+    {
+      zlog_debug ("%s: no attr, can't delete route", __func__);
+      return;
+    }
+  vn_pfx = &vn_pfx_space;
+
+  vnaddr.addr_family = vn_pfx->family;
+  switch (vn_pfx->family)
+    {
+    case AF_INET:
+      if (vn_pfx->prefixlen != 32)
+        {
+          zlog_debug ("%s: redist VN plen (%d) != 32, skipping",
+                      __func__, vn_pfx->prefixlen);
+          return;
+        }
+      vnaddr.addr.v4 = vn_pfx->u.prefix4;
+      break;
+
+    case AF_INET6:
+      if (vn_pfx->prefixlen != 128)
+        {
+          zlog_debug ("%s: redist VN plen (%d) != 128, skipping",
+                      __func__, vn_pfx->prefixlen);
+          return;
+        }
+      vnaddr.addr.v6 = vn_pfx->u.prefix6;
+      break;
+
+    default:
+      zlog_debug ("%s: no redist RFG VN host pfx configured, skipping",
+                  __func__);
+      return;
+    }
+
+
+  memset (&prd, 0, sizeof (prd));
+  if (rfapi_set_autord_from_vn (&prd, &vnaddr))
+    {
+      zlog_debug ("%s: can't auto-assign RD, skipping", __func__);
+      return;
+    }
+
+  vncHDBgpDirect.peer = info->peer;
+  zlog_debug ("%s: setting peer to %p", __func__, vncHDBgpDirect.peer);
+  del_vnc_route (&vncHDBgpDirect,
+                 info->peer,
+                 bgp,
+                 SAFI_MPLS_VPN,
+                 prefix,
+                 &prd,
+                 ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, 1);
+
+  vncHDBgpDirect.peer = NULL;
+}
+
+static void
+vnc_import_bgp_del_route_mode_nvegroup (struct bgp *bgp,
+                                        struct prefix *prefix,
+                                        struct bgp_info *info)
+{
+  struct prefix_rd prd;
+  afi_t afi = family2afi (prefix->family);
+  struct rfapi_nve_group_cfg *rfg = NULL;
+  struct prefix *vn_pfx = NULL;
+  struct rfapi_ip_addr vnaddr;
+
+
+  assert (afi);
+
+  assert ((rfg = bgp->rfapi_cfg->rfg_redist));
+
+  /*
+   * Compute VN address
+   */
+
+  /*
+   * export nve group's VN addr prefix must be a /32 which
+   * will yield the VN addr to use
+   */
+  vn_pfx = &rfg->vn_prefix;
+
+
+  vnaddr.addr_family = vn_pfx->family;
+  switch (vn_pfx->family)
+    {
+    case AF_INET:
+      if (vn_pfx->prefixlen != 32)
+        {
+          zlog_debug ("%s: redist VN plen (%d) != 32, skipping",
+                      __func__, vn_pfx->prefixlen);
+          return;
+        }
+      vnaddr.addr.v4 = vn_pfx->u.prefix4;
+      break;
+
+    case AF_INET6:
+      if (vn_pfx->prefixlen != 128)
+        {
+          zlog_debug ("%s: redist VN plen (%d) != 128, skipping",
+                      __func__, vn_pfx->prefixlen);
+          return;
+        }
+      vnaddr.addr.v6 = vn_pfx->u.prefix6;
+      break;
+
+    default:
+      zlog_debug ("%s: no redist RFG VN host pfx configured, skipping",
+                  __func__);
+      return;
+    }
+
+  memset (&prd, 0, sizeof (prd));
+  prd = rfg->rd;
+  prd.family = AF_UNSPEC;
+  prd.prefixlen = 64;
+
+  if (rfg->rd.family == AF_UNIX)
+    {
+      /* means "auto" with VN addr */
+      if (rfapi_set_autord_from_vn (&prd, &vnaddr))
+        {
+          zlog_debug ("%s: can't auto-assign RD, skipping", __func__);
+          return;
+        }
+    }
+
+
+  vncHDBgpDirect.peer = info->peer;
+  zlog_debug ("%s: setting peer to %p", __func__, vncHDBgpDirect.peer);
+  del_vnc_route (&vncHDBgpDirect,
+                 info->peer,
+                 bgp,
+                 SAFI_MPLS_VPN,
+                 prefix,
+                 &prd,
+                 ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, 1);
+
+  vncHDBgpDirect.peer = NULL;
+}
+
+static void
+vnc_import_bgp_del_route_mode_resolve_nve_one_bi (
+    struct bgp         *bgp,
+    afi_t              afi,
+    struct bgp_info    *bi,    /* VPN bi */
+    struct prefix_rd   *prd,   /* RD */
+    struct prefix      *prefix)/* unicast route prefix */
+{
+  struct prefix un;
+  uint32_t lifetime;
+  uint32_t *plifetime;
+  struct bgp_attr_encap_subtlv *encaptlvs;
+
+  if (bi->type != ZEBRA_ROUTE_BGP && bi->type != ZEBRA_ROUTE_BGP_DIRECT)
+    {
+
+      return;
+    }
+  if (bi->sub_type != BGP_ROUTE_NORMAL &&
+      bi->sub_type != BGP_ROUTE_STATIC && bi->sub_type != BGP_ROUTE_RFP)
+    {
+
+      return;
+    }
+  if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+    return;
+
+  vncHDResolveNve.peer = bi->peer;
+  if (!rfapiGetVncTunnelUnAddr (bi->attr, &un))
+    {
+      if (rfapiQprefix2Raddr (&un, &vncHDResolveNve.un_addr))
+        return;
+    }
+  else
+    {
+      memset (&vncHDResolveNve.un_addr, 0, sizeof (vncHDResolveNve.un_addr));
+    }
+
+  if (rfapiGetVncLifetime (bi->attr, &lifetime))
+    {
+      plifetime = NULL;
+    }
+  else
+    {
+      plifetime = &lifetime;
+    }
+
+  if (bi->attr && bi->attr->extra)
+    {
+      encaptlvs = bi->attr->extra->vnc_subtlvs;
+    }
+  else
+    {
+      encaptlvs = NULL;
+    }
+
+  del_vnc_route (&vncHDResolveNve, vncHDResolveNve.peer, bgp, SAFI_MPLS_VPN, prefix,    /* unicast route prefix */
+                 prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, 0); /* flags */
+
+}
+
+static void
+vnc_import_bgp_del_route_mode_resolve_nve_one_rd (
+    struct prefix_rd   *prd,
+    struct bgp_table   *table_rd,    /* per-rd VPN route table */
+    afi_t              afi,
+    struct bgp         *bgp,
+    struct prefix      *prefix,    /* unicast prefix */
+    struct prefix      *ubi_nexthop)   /* unicast bi's nexthop */
+{
+  struct bgp_node *bn;
+  struct bgp_info *bi;
+
+  if (!table_rd)
+    return;
+
+  {
+    char str_nh[BUFSIZ];
+
+    prefix2str (ubi_nexthop, str_nh, BUFSIZ);
+    str_nh[BUFSIZ - 1] = 0;
+
+    zlog_debug ("%s: ubi_nexthop=%s", __func__, str_nh);
+  }
+
+
+  /* exact match */
+  bn = bgp_node_lookup (table_rd, ubi_nexthop);
+  if (!bn)
+    {
+      zlog_debug ("%s: no match in RD's table for ubi_nexthop", __func__);
+      return;
+    }
+
+  /* Iterate over bgp_info items at this node */
+  for (bi = bn->info; bi; bi = bi->next)
+    {
+
+      vnc_import_bgp_del_route_mode_resolve_nve_one_bi (bgp, afi, bi,   /* VPN bi */
+                                                        prd,    /* VPN RD */
+                                                        prefix);        /* unicast route prefix */
+    }
+
+  bgp_unlock_node (bn);
+}
+
+static void
+vnc_import_bgp_del_route_mode_resolve_nve (struct bgp *bgp,
+                                           afi_t afi,
+                                           struct prefix *prefix,
+                                           struct bgp_info *info)
+{
+  struct ecommunity *ecom = NULL;
+  struct prefix pfx_unicast_nexthop = { 0 };    /* happy valgrind */
+
+  //struct listnode           *hnode;
+  //struct rfapi_descriptor   *rfd;
+  struct prefix_bag *pb;
+  void *cursor;
+  struct skiplist *sl = bgp->rfapi->resolve_nve_nexthop;
+  int rc;
+  struct bgp_node *bnp;         /* prd table node */
+
+  if (!sl)
+    {
+      zlog_debug ("%s: no RHN entries, skipping", __func__);
+      return;
+    }
+
+  if (info->type != ZEBRA_ROUTE_BGP)
+    {
+      zlog_debug ("%s: unicast type %d=\"%s\" is not %d=%s, skipping",
+                  __func__, info->type, zebra_route_string (info->type),
+                  ZEBRA_ROUTE_BGP, "ZEBRA_ROUTE_BGP");
+      return;
+    }
+
+  if (process_unicast_route (bgp, afi, prefix, info,
+                             &ecom, &pfx_unicast_nexthop))
+    {
+
+      zlog_debug ("%s: process_unicast_route error, skipping", __func__);
+      return;
+    }
+
+  rc = skiplist_first_value (sl, &pfx_unicast_nexthop, (void *) &pb, &cursor);
+  while (!rc)
+    {
+      if (pb->ubi == info)
+        {
+          skiplist_delete (sl, &pfx_unicast_nexthop, pb);
+          bgp_info_unlock (info);
+          break;
+        }
+      rc =
+        skiplist_next_value (sl, &pfx_unicast_nexthop, (void *) &pb, &cursor);
+    }
+
+  /*
+   * Iterate over RDs in VPN RIB. For each RD, look up unicast nexthop
+   * (exact match, /32). If an exact match is found, call add_vnc_route.
+   */
+
+  for (bnp = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); bnp;
+       bnp = bgp_route_next (bnp))
+    {
+
+      struct bgp_table *table;
+
+      table = (struct bgp_table *) (bnp->info);
+
+      if (!table)
+        continue;
+
+      vnc_import_bgp_del_route_mode_resolve_nve_one_rd ((struct prefix_rd *) &bnp->p, table, afi, bgp, prefix, &pfx_unicast_nexthop);   /* TBD how is this set? */
+    }
+
+  if (ecom)
+    ecommunity_free (&ecom);
+}
+
+
+
+
+/***********************************************************************
+ *                     Add/Delete CE->NVE routes
+ ***********************************************************************/
+
+/*
+ * Should be called whan a bi is added to VPN RIB. This function
+ * will check if it is a host route and return immediately if not.
+ */
+void
+vnc_import_bgp_add_vnc_host_route_mode_resolve_nve (
+    struct bgp         *bgp,
+    struct prefix_rd   *prd,           /* RD */
+    struct bgp_table   *table_rd,      /* per-rd VPN route table */
+    struct prefix      *prefix,        /* VPN prefix */
+    struct bgp_info    *bi)            /* new VPN host route */
+{
+  afi_t afi = family2afi (prefix->family);
+  struct skiplist *sl = NULL;
+  int rc;
+  struct prefix_bag *pb;
+  void *cursor;
+  struct rfapi_cfg *hc = NULL;
+
+  zlog_debug ("%s: entry", __func__);
+
+  if (afi != AFI_IP && afi != AFI_IP6)
+    {
+      zlog_debug ("%s: bad afi %d, skipping", __func__, afi);
+      return;
+    }
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  /* check vnc redist flag for bgp direct routes */
+  if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT])
+    {
+      zlog_debug
+        ("%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+         __func__, afi);
+      return;
+    }
+
+  if (hc->redist_mode != VNC_REDIST_MODE_RESOLVE_NVE)
+    {
+      zlog_debug ("%s: not in resolve-nve mode, skipping", __func__);
+      return;
+    }
+
+  if (bgp && bgp->rfapi)
+    sl = bgp->rfapi->resolve_nve_nexthop;
+
+  if (!sl)
+    {
+      zlog_debug ("%s: no resolve_nve_nexthop skiplist, skipping", __func__);
+      return;
+    }
+
+  if (!is_host_prefix (prefix))
+    {
+      zlog_debug ("%s: not host prefix, skipping", __func__);
+      return;
+    }
+
+  rc = skiplist_first_value (sl, prefix, (void *) &pb, &cursor);
+  while (!rc)
+    {
+      struct ecommunity *ecom;
+      struct prefix pfx_unicast_nexthop;
+      uint32_t *med = NULL;
+      uint32_t local_pref;
+
+      memset (&pfx_unicast_nexthop, 0, sizeof (struct prefix)); /* keep valgrind happy */
+
+      if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE))
+       {
+         char hbuf[BUFSIZ];
+         char ubuf[BUFSIZ];
+
+         prefix2str (&pb->hpfx, hbuf, BUFSIZ);
+         prefix2str (&pb->upfx, ubuf, BUFSIZ);
+
+         zlog_debug
+           ("%s: examining RHN Entry (q=%p): upfx=%s, hpfx=%s, ubi=%p",
+            __func__, cursor, ubuf, hbuf, pb->ubi);
+       }
+
+      if (process_unicast_route (bgp, afi, &pb->upfx, pb->ubi,
+                                 &ecom, &pfx_unicast_nexthop))
+        {
+
+          zlog_debug ("%s: process_unicast_route error, skipping", __func__);
+          continue;
+        }
+      local_pref = calc_local_pref (pb->ubi->attr, pb->ubi->peer);
+
+      if (pb->ubi->attr &&
+          (pb->ubi->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)))
+        {
+
+          med = &pb->ubi->attr->med;
+        }
+
+      /*
+       * Sanity check
+       */
+      if (vnc_prefix_cmp (&pfx_unicast_nexthop, prefix))
+        {
+          char str_unh[BUFSIZ];
+          char str_nve_pfx[BUFSIZ];
+
+          prefix2str (&pfx_unicast_nexthop, str_unh, BUFSIZ);
+          str_unh[BUFSIZ - 1] = 0;
+
+          prefix2str (prefix, str_nve_pfx, BUFSIZ);
+          str_nve_pfx[BUFSIZ - 1] = 0;
+
+          zlog_debug
+            ("%s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s",
+             __func__, str_unh, str_nve_pfx);
+          assert (0);
+        }
+
+      vnc_import_bgp_add_route_mode_resolve_nve_one_bi (bgp, afi, bi,   /* VPN bi */
+                                                        prd, &pb->upfx, /* unicast prefix */
+                                                        &local_pref,
+                                                        med, ecom);
+
+      if (ecom)
+        ecommunity_free (&ecom);
+
+#if DEBUG_RHN_LIST
+      /* debug */
+      {
+        char pbuf[BUFSIZ];
+
+        prefix2str (prefix, pbuf, BUFSIZ);
+
+        zlog_debug ("%s: advancing past RHN Entry (q=%p): with prefix %s",
+                    __func__, cursor, pbuf);
+        print_rhn_list (__func__, NULL);        /* debug */
+      }
+#endif
+      rc = skiplist_next_value (sl, prefix, (void *) &pb, &cursor);
+    }
+  zlog_debug ("%s: done", __func__);
+}
+
+
+void
+vnc_import_bgp_del_vnc_host_route_mode_resolve_nve (
+  struct bgp           *bgp,
+  struct prefix_rd     *prd,           /* RD */
+  struct bgp_table     *table_rd,      /* per-rd VPN route table */
+  struct prefix                *prefix,        /* VPN prefix */
+  struct bgp_info      *bi)            /* old VPN host route */
+{
+  afi_t afi = family2afi (prefix->family);
+  struct skiplist *sl = NULL;
+  struct prefix_bag *pb;
+  void *cursor;
+  struct rfapi_cfg *hc = NULL;
+  int rc;
+
+  {
+    char str_pfx[BUFSIZ];
+
+    prefix2str (prefix, str_pfx, BUFSIZ);
+    str_pfx[BUFSIZ - 1] = 0;
+
+    zlog_debug ("%s(bgp=%p, nve prefix=%s)", __func__, bgp, str_pfx);
+  }
+
+  if (afi != AFI_IP && afi != AFI_IP6)
+    return;
+
+  if (!(hc = bgp->rfapi_cfg))
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  /* check vnc redist flag for bgp direct routes */
+  if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT])
+    {
+      zlog_debug
+        ("%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+         __func__, afi);
+      return;
+    }
+
+  if (hc->redist_mode != VNC_REDIST_MODE_RESOLVE_NVE)
+    {
+      zlog_debug ("%s: not in resolve-nve mode, skipping", __func__);
+      return;
+    }
+
+  if (bgp && bgp->rfapi)
+    sl = bgp->rfapi->resolve_nve_nexthop;
+
+  if (!sl)
+    {
+      zlog_debug ("%s: no RHN entries, skipping", __func__);
+      return;
+    }
+
+  if (!is_host_prefix (prefix))
+    {
+      zlog_debug ("%s: not host route, skip", __func__);
+      return;
+    }
+
+  /*
+   * Find all entries with key == CE in the RHN list
+   */
+  rc = skiplist_first_value (sl, prefix, (void *) &pb, &cursor);
+  while (!rc)
+    {
+
+      struct ecommunity *ecom;
+      struct prefix pfx_unicast_nexthop;
+
+      memset (&pfx_unicast_nexthop, 0, sizeof (struct prefix)); /* keep valgrind happy */
+
+      if (process_unicast_route (bgp, afi, &pb->upfx, pb->ubi,
+                                 &ecom, &pfx_unicast_nexthop))
+        {
+
+          zlog_debug ("%s: process_unicast_route error, skipping", __func__);
+          continue;
+        }
+
+      /*
+       * Sanity check
+       */
+      if (vnc_prefix_cmp (&pfx_unicast_nexthop, prefix))
+        {
+          char str_unh[BUFSIZ];
+          char str_nve_pfx[BUFSIZ];
+
+          prefix2str (&pfx_unicast_nexthop, str_unh, BUFSIZ);
+          str_unh[BUFSIZ - 1] = 0;
+
+          prefix2str (prefix, str_nve_pfx, BUFSIZ);
+          str_nve_pfx[BUFSIZ - 1] = 0;
+
+          zlog_debug
+            ("%s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s",
+             __func__, str_unh, str_nve_pfx);
+          assert (0);
+        }
+
+      vnc_import_bgp_del_route_mode_resolve_nve_one_bi (bgp,
+                                                        afi,
+                                                        bi, prd, &pb->upfx);
+
+      if (ecom)
+        ecommunity_free (&ecom);
+
+      rc = skiplist_next_value (sl, prefix, (void *) &pb, &cursor);
+    }
+}
+
+
+/***********************************************************************
+ *                     Exterior Routes
+ ***********************************************************************/
+
+#define DEBUG_IS_USABLE_INTERIOR 1
+
+static int
+is_usable_interior_route (struct bgp_info *bi_interior)
+{
+  if (!VALID_INTERIOR_TYPE (bi_interior->type))
+    {
+#if DEBUG_IS_USABLE_INTERIOR
+      zlog_debug ("%s: NO: type %d is not valid interior type",
+                  __func__, bi_interior->type);
+#endif
+      return 0;
+    }
+  if (!CHECK_FLAG (bi_interior->flags, BGP_INFO_VALID))
+    {
+#if DEBUG_IS_USABLE_INTERIOR
+      zlog_debug ("%s: NO: BGP_INFO_VALID not set", __func__);
+#endif
+      return 0;
+    }
+  return 1;
+}
+
+/*
+ * There should be only one of these per prefix at a time.
+ * This should be called as a result of selection operation
+ *
+ * NB should be called espacially for bgp instances that are named,
+ * because the exterior routes will always come from one of those.
+ * We filter here on the instance name to make sure we get only the
+ * right routes.
+ */
+static void
+vnc_import_bgp_exterior_add_route_it (
+    struct bgp                 *bgp,   /* exterior instance, we hope */
+    struct prefix              *prefix,/* unicast prefix */
+    struct bgp_info            *info,  /* unicast info */
+    struct rfapi_import_table  *it_only)/* NULL, or limit to this IT */
+{
+  struct rfapi *h;
+  struct rfapi_cfg *hc;
+  struct prefix pfx_orig_nexthop;
+  struct rfapi_import_table *it;
+  struct bgp *bgp_default = bgp_get_default ();
+  afi_t afi = family2afi (prefix->family);
+
+  h = bgp_default->rfapi;
+  hc = bgp_default->rfapi_cfg;
+
+  zlog_debug ("%s: entry with it=%p", __func__, it_only);
+
+  if (!h || !hc)
+    {
+      zlog_debug ("%s: rfapi or rfapi_cfg not instantiated, skipping",
+                  __func__);
+      return;
+    }
+  if (!hc->redist_bgp_exterior_view)
+    {
+      zlog_debug ("%s: exterior view not set, skipping", __func__);
+      return;
+    }
+  if (bgp != hc->redist_bgp_exterior_view)
+    {
+      zlog_debug ("%s: bgp %p != hc->redist_bgp_exterior_view %p, skipping",
+                  __func__, bgp, hc->redist_bgp_exterior_view);
+      return;
+    }
+
+  if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT])
+    {
+      zlog_debug ("%s: redist of exterior routes not enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  if (!info->attr)
+    {
+      zlog_debug ("%s: no info, skipping", __func__);
+      return;
+    }
+
+  /*
+   * Extract nexthop from exterior route
+   *
+   * Incoming prefix is unicast. If v6, it is in multiprotocol area,
+   * but if v4 it is in attr->nexthop
+   */
+  rfapiUnicastNexthop2Prefix (afi, info->attr, &pfx_orig_nexthop);
+
+  for (it = h->imports; it; it = it->next)
+    {
+      struct route_table *table;
+      struct route_node *rn;
+      struct route_node *par;
+      struct bgp_info *bi_interior;
+      int have_usable_route;
+
+      zlog_debug ("%s: doing it %p", __func__, it);
+
+      if (it_only && (it_only != it))
+        {
+          zlog_debug ("%s: doesn't match it_only %p", __func__, it_only);
+          continue;
+        }
+
+      table = it->imported_vpn[afi];
+
+      for (rn = route_node_match (table, &pfx_orig_nexthop),
+           have_usable_route = 0; (!have_usable_route) && rn;)
+        {
+
+          zlog_debug ("%s: it %p trying rn %p", __func__, it, rn);
+
+          for (bi_interior = rn->info; bi_interior;
+               bi_interior = bi_interior->next)
+            {
+              struct prefix_rd *prd;
+              struct attr new_attr;
+              u_int32_t label = 0;
+
+              if (!is_usable_interior_route (bi_interior))
+                continue;
+
+              zlog_debug ("%s: usable: bi_interior %p", __func__,
+                          bi_interior);
+
+              /*
+               * have a legitimate route to exterior's nexthop
+               * via NVE.
+               *
+               * Import unicast route to the import table
+               */
+              have_usable_route = 1;
+
+              if (bi_interior->extra)
+                {
+                  prd = &bi_interior->extra->vnc.import.rd;
+                  label = decode_label (bi_interior->extra->tag);
+                }
+              else
+                prd = NULL;
+
+              /* use local_pref from unicast route */
+              memset (&new_attr, 0, sizeof (struct attr));
+              bgp_attr_dup (&new_attr, bi_interior->attr);
+              if (info->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+                {
+                  new_attr.local_pref = info->attr->local_pref;
+                  new_attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
+                }
+
+              rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_UPDATE, bi_interior->peer, NULL,    /* rfd */
+                                             prefix,
+                                             NULL,
+                                             afi,
+                                             prd,
+                                             &new_attr,
+                                             ZEBRA_ROUTE_BGP_DIRECT_EXT,
+                                             BGP_ROUTE_REDISTRIBUTE, &label);
+
+              bgp_attr_extra_free (&new_attr);
+            }
+
+          if (have_usable_route)
+            {
+              /*
+               * Make monitor
+               *
+               * TBD factor this out into its own function
+               */
+              struct prefix *pfx_mon = prefix_new ();
+              if (!RFAPI_MONITOR_EXTERIOR (rn)->source)
+                {
+                  RFAPI_MONITOR_EXTERIOR (rn)->source =
+                    skiplist_new (0, NULL, (void (*)(void *)) prefix_free);
+                  route_lock_node (rn); /* for skiplist */
+                }
+              route_lock_node (rn);     /* for skiplist entry */
+              prefix_copy (pfx_mon, prefix);
+              if (!skiplist_insert (RFAPI_MONITOR_EXTERIOR (rn)->source,
+                                    info, pfx_mon))
+                {
+
+                  bgp_info_lock (info);
+                }
+            }
+          par = rn->parent;
+          if (par)
+            route_lock_node (par);
+          route_unlock_node (rn);
+          rn = par;
+        }
+      if (rn)
+        route_unlock_node (rn);
+
+      if (!have_usable_route)
+        {
+          struct prefix *pfx_mon = prefix_new ();
+          prefix_copy (pfx_mon, prefix);
+          if (!skiplist_insert (it->monitor_exterior_orphans, info, pfx_mon))
+            {
+
+              bgp_info_lock (info);
+            }
+        }
+    }
+}
+
+void
+vnc_import_bgp_exterior_add_route (
+    struct bgp         *bgp,   /* exterior instance, we hope */
+    struct prefix      *prefix,/* unicast prefix */
+    struct bgp_info    *info)  /* unicast info */
+{
+  vnc_import_bgp_exterior_add_route_it (bgp, prefix, info, NULL);
+}
+
+/*
+ * There should be only one of these per prefix at a time.
+ * This should probably be called as a result of selection operation.
+ *
+ * NB should be called espacially for bgp instances that are named,
+ * because the exterior routes will always come from one of those.
+ * We filter here on the instance name to make sure we get only the
+ * right routes.
+ */
+void
+vnc_import_bgp_exterior_del_route (
+    struct bgp         *bgp,
+    struct prefix      *prefix,        /* unicast prefix */
+    struct bgp_info    *info)          /* unicast info */
+{
+  struct rfapi *h;
+  struct rfapi_cfg *hc;
+  struct rfapi_import_table *it;
+  struct prefix pfx_orig_nexthop;
+  afi_t afi = family2afi (prefix->family);
+  struct bgp *bgp_default = bgp_get_default ();
+
+  memset (&pfx_orig_nexthop, 0, sizeof (struct prefix));        /* keep valgrind happy */
+
+  h = bgp_default->rfapi;
+  hc = bgp_default->rfapi_cfg;
+
+  if (!h || !hc)
+    {
+      zlog_debug ("%s: rfapi or rfapi_cfg not instantiated, skipping",
+                  __func__);
+      return;
+    }
+  if (!hc->redist_bgp_exterior_view)
+    {
+      zlog_debug ("%s: exterior view not set, skipping", __func__);
+      return;
+    }
+  if (bgp != hc->redist_bgp_exterior_view)
+    {
+      zlog_debug ("%s: bgp %p != hc->redist_bgp_exterior_view %p, skipping",
+                  __func__, bgp, hc->redist_bgp_exterior_view);
+      return;
+    }
+  if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT])
+    {
+      zlog_debug ("%s: redist of exterior routes no enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  if (!info->attr)
+    {
+      zlog_debug ("%s: no info, skipping", __func__);
+      return;
+    }
+
+  /*
+   * Extract nexthop from exterior route
+   *
+   * Incoming prefix is unicast. If v6, it is in multiprotocol area,
+   * but if v4 it is in attr->nexthop
+   */
+  rfapiUnicastNexthop2Prefix (afi, info->attr, &pfx_orig_nexthop);
+
+  for (it = h->imports; it; it = it->next)
+    {
+      struct route_table *table;
+      struct route_node *rn;
+      struct route_node *par;
+      struct bgp_info *bi_interior;
+      int have_usable_route;
+
+      table = it->imported_vpn[afi];
+
+      for (rn = route_node_match (table, &pfx_orig_nexthop),
+           have_usable_route = 0; (!have_usable_route) && rn;)
+        {
+
+          for (bi_interior = rn->info; bi_interior;
+               bi_interior = bi_interior->next)
+            {
+              struct prefix_rd *prd;
+              u_int32_t label = 0;
+
+              if (!is_usable_interior_route (bi_interior))
+                continue;
+
+              /*
+               * have a legitimate route to exterior's nexthop
+               * via NVE.
+               *
+               * Import unicast route to the import table
+               */
+              have_usable_route = 1;
+
+              if (bi_interior->extra)
+                {
+                  prd = &bi_interior->extra->vnc.import.rd;
+                  label = decode_label (bi_interior->extra->tag);
+                }
+              else
+                prd = NULL;
+
+              rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_KILL, bi_interior->peer, NULL,      /* rfd */
+                                             prefix,
+                                             NULL,
+                                             afi,
+                                             prd,
+                                             bi_interior->attr,
+                                             ZEBRA_ROUTE_BGP_DIRECT_EXT,
+                                             BGP_ROUTE_REDISTRIBUTE, &label);
+
+              /*
+               * Delete monitor
+               *
+               * TBD factor this out into its own function
+               */
+              {
+                if (RFAPI_MONITOR_EXTERIOR (rn)->source)
+                  {
+                    if (!skiplist_delete (RFAPI_MONITOR_EXTERIOR (rn)->source,
+                                          info, NULL))
+                      {
+
+                        bgp_info_unlock (info);
+                        route_unlock_node (rn); /* sl entry */
+                      }
+                    if (skiplist_empty (RFAPI_MONITOR_EXTERIOR (rn)->source))
+                      {
+                        skiplist_free (RFAPI_MONITOR_EXTERIOR (rn)->source);
+                        RFAPI_MONITOR_EXTERIOR (rn)->source = NULL;
+                        route_unlock_node (rn); /* skiplist itself */
+                      }
+                  }
+              }
+            }
+          par = rn->parent;
+          if (par)
+            route_lock_node (par);
+          route_unlock_node (rn);
+          rn = par;
+        }
+      if (rn)
+        route_unlock_node (rn);
+
+      if (!have_usable_route)
+        {
+          if (!skiplist_delete (it->monitor_exterior_orphans, info, NULL))
+            {
+
+              bgp_info_unlock (info);
+            }
+        }
+    }
+}
+
+/*
+ * This function should be called after a new interior VPN route
+ * has been added to an import_table.
+ *
+ * NB should also be called whenever an existing vpn interior route
+ * becomes valid (e.g., valid_interior_count is inremented)
+ */
+void
+vnc_import_bgp_exterior_add_route_interior (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *it,
+    struct route_node          *rn_interior,   /* VPN IT node */
+    struct bgp_info            *bi_interior)   /* VPN IT route */
+{
+  afi_t afi = family2afi (rn_interior->p.family);
+  struct route_node *par;
+  struct bgp_info *bi_exterior;
+  struct prefix *pfx_exterior;  /* exterior pfx */
+  void *cursor;
+  int rc;
+  struct list *list_adopted;
+
+  zlog_debug ("%s: entry", __func__);
+
+  if (!is_usable_interior_route (bi_interior))
+    {
+      zlog_debug ("%s: not usable interior route, skipping", __func__);
+      return;
+    }
+
+  if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT])
+    {
+      zlog_debug ("%s: redist of exterior routes no enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  if (it == bgp->rfapi->it_ce)
+    {
+      zlog_debug ("%s: import table is it_ce, skipping", __func__);
+      return;
+    }
+
+  /*debugging */
+  {
+    char str_pfx[BUFSIZ];
+
+    prefix2str (&rn_interior->p, str_pfx, BUFSIZ);
+    str_pfx[BUFSIZ - 1] = 0;
+
+    zlog_debug ("%s: interior prefix=%s, bi type=%d",
+                __func__, str_pfx, bi_interior->type);
+  }
+
+  if (RFAPI_HAS_MONITOR_EXTERIOR (rn_interior))
+    {
+
+      int count = 0;            /* debugging */
+
+      zlog_debug ("%s: has exterior monitor; ext src: %p", __func__,
+                  RFAPI_MONITOR_EXTERIOR (rn_interior)->source);
+
+      /*
+       * There is a monitor here already. Therefore, we do not need
+       * to do any pulldown. Just construct exterior routes based
+       * on the new interior route.
+       */
+      cursor = NULL;
+      for (rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source,
+                               (void **) &bi_exterior,
+                               (void **) &pfx_exterior, &cursor); !rc;
+           rc =
+           skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source,
+                          (void **) &bi_exterior, (void **) &pfx_exterior,
+                          &cursor))
+        {
+
+          struct prefix_rd *prd;
+          struct attr new_attr;
+          u_int32_t label = 0;
+
+
+          ++count;              /* debugging */
+
+          assert (bi_exterior);
+          assert (pfx_exterior);
+
+          if (bi_interior->extra)
+            {
+              prd = &bi_interior->extra->vnc.import.rd;
+              label = decode_label (bi_interior->extra->tag);
+            }
+          else
+            prd = NULL;
+
+          /* use local_pref from unicast route */
+          memset (&new_attr, 0, sizeof (struct attr));
+          bgp_attr_dup (&new_attr, bi_interior->attr);
+          if (bi_exterior &&
+              (bi_exterior->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))
+            {
+              new_attr.local_pref = bi_exterior->attr->local_pref;
+              new_attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
+            }
+
+          rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_UPDATE, bi_interior->peer, NULL,        /* rfd */
+                                         pfx_exterior,
+                                         NULL,
+                                         afi,
+                                         prd,
+                                         &new_attr,
+                                         ZEBRA_ROUTE_BGP_DIRECT_EXT,
+                                         BGP_ROUTE_REDISTRIBUTE, &label);
+
+          bgp_attr_extra_free (&new_attr);
+        }
+      zlog_debug
+        ("%s: finished constructing exteriors based on existing monitors",
+         __func__);
+      return;
+    }
+
+  zlog_debug ("%s: no exterior monitor", __func__);
+
+  /*
+   * No monitor at this node. Is this the first valid interior
+   * route at this node?
+   */
+  if (RFAPI_MONITOR_EXTERIOR (rn_interior)->valid_interior_count > 1)
+    {
+      zlog_debug
+        ("%s: new interior route not first valid one, skipping pulldown",
+         __func__);
+      return;
+    }
+
+  /*
+   * Look up the tree for possible pulldown candidates.
+   * Find nearest parent with an exterior route monitor
+   */
+  for (par = rn_interior->parent; par; par = par->parent)
+    {
+      if (RFAPI_HAS_MONITOR_EXTERIOR (par))
+        break;
+    }
+
+  if (par)
+    {
+
+      zlog_debug ("%s: checking parent %p for possible pulldowns",
+                  __func__, par);
+
+      /* check monitors at par for possible pulldown */
+      cursor = NULL;
+      for (rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (par)->source,
+                               (void **) &bi_exterior,
+                               (void **) &pfx_exterior, &cursor); !rc;
+           rc =
+           skiplist_next (RFAPI_MONITOR_EXTERIOR (par)->source,
+                          (void **) &bi_exterior, (void **) &pfx_exterior,
+                          &cursor))
+        {
+
+          struct prefix pfx_nexthop;
+
+          memset (&pfx_nexthop, 0, sizeof (struct prefix));     /* keep valgrind happy */
+
+          /* check original nexthop for prefix match */
+          rfapiUnicastNexthop2Prefix (afi, bi_exterior->attr, &pfx_nexthop);
+
+          if (prefix_match (&rn_interior->p, &pfx_nexthop))
+            {
+
+              struct bgp_info *bi;
+              struct prefix_rd *prd;
+              struct attr new_attr;
+              u_int32_t label = 0;
+
+              /* do pull-down */
+
+              /*
+               * add monitor to longer prefix
+               */
+              struct prefix *pfx_mon = prefix_new ();
+              prefix_copy (pfx_mon, pfx_exterior);
+              if (!RFAPI_MONITOR_EXTERIOR (rn_interior)->source)
+                {
+                  RFAPI_MONITOR_EXTERIOR (rn_interior)->source =
+                    skiplist_new (0, NULL, (void (*)(void *)) prefix_free);
+                  route_lock_node (rn_interior);
+                }
+              skiplist_insert (RFAPI_MONITOR_EXTERIOR (rn_interior)->source,
+                               bi_exterior, pfx_mon);
+              route_lock_node (rn_interior);
+
+              /*
+               * Delete constructed exterior routes based on
+               * parent routes.
+               */
+              for (bi = par->info; bi; bi = bi->next)
+                {
+
+                  if (bi->extra)
+                    {
+                      prd = &bi->extra->vnc.import.rd;
+                      label = decode_label (bi->extra->tag);
+                    }
+                  else
+                    prd = NULL;
+
+                  rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_KILL, bi->peer, NULL,   /* rfd */
+                                                 pfx_exterior,
+                                                 NULL,
+                                                 afi,
+                                                 prd,
+                                                 bi->attr,
+                                                 ZEBRA_ROUTE_BGP_DIRECT_EXT,
+                                                 BGP_ROUTE_REDISTRIBUTE,
+                                                 &label);
+                }
+
+
+              /*
+               * Add constructed exterior routes based on
+               * the new interior route at longer prefix.
+               */
+              if (bi_interior->extra)
+                {
+                  prd = &bi_interior->extra->vnc.import.rd;
+                  label = decode_label (bi_interior->extra->tag);
+                }
+              else
+                prd = NULL;
+
+              /* use local_pref from unicast route */
+              memset (&new_attr, 0, sizeof (struct attr));
+              bgp_attr_dup (&new_attr, bi_interior->attr);
+              if (bi_exterior &&
+                  (bi_exterior->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))
+                {
+                  new_attr.local_pref = bi_exterior->attr->local_pref;
+                  new_attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
+                }
+
+              rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_UPDATE, bi_interior->peer, NULL,    /* rfd */
+                                             pfx_exterior,
+                                             NULL,
+                                             afi,
+                                             prd,
+                                             &new_attr,
+                                             ZEBRA_ROUTE_BGP_DIRECT_EXT,
+                                             BGP_ROUTE_REDISTRIBUTE, &label);
+
+              bgp_attr_extra_free (&new_attr);
+            }
+        }
+
+      /*
+       * The only monitors at rn_interior are the ones we added just
+       * above, so we can use the rn_interior list to identify which
+       * monitors to delete from the parent.
+       */
+      cursor = NULL;
+      for (rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source,
+                               (void **) &bi_exterior, NULL, &cursor);
+           !rc;
+           rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source,
+                               (void **) &bi_exterior, NULL, &cursor))
+        {
+
+
+          skiplist_delete (RFAPI_MONITOR_EXTERIOR (par)->source,
+                           bi_exterior, NULL);
+          route_unlock_node (par);      /* sl entry */
+        }
+      if (skiplist_empty (RFAPI_MONITOR_EXTERIOR (par)->source))
+        {
+          skiplist_free (RFAPI_MONITOR_EXTERIOR (par)->source);
+          RFAPI_MONITOR_EXTERIOR (par)->source = NULL;
+          route_unlock_node (par);      /* sl itself */
+        }
+    }
+
+  zlog_debug ("%s: checking orphans", __func__);
+
+  /*
+   * See if any orphans can be pulled down to the current node
+   */
+  cursor = NULL;
+  list_adopted = NULL;
+  for (rc = skiplist_next (it->monitor_exterior_orphans,
+                           (void **) &bi_exterior, (void **) &pfx_exterior,
+                           &cursor); !rc;
+       rc =
+       skiplist_next (it->monitor_exterior_orphans, (void **) &bi_exterior,
+                      (void **) &pfx_exterior, &cursor))
+    {
+
+      struct prefix pfx_nexthop;
+      char buf[BUFSIZ];
+      afi_t afi_exterior = family2afi (pfx_exterior->family);
+
+      prefix2str (pfx_exterior, buf, sizeof (buf));
+      buf[sizeof (buf) - 1] = 0;
+      zlog_debug ("%s: checking exterior orphan at prefix %s", __func__, buf);
+
+      if (afi_exterior != afi)
+        {
+          zlog_debug ("%s: exterior orphan afi %d != interior afi %d, skip",
+                      __func__, afi_exterior, afi);
+          continue;
+        }
+
+      /* check original nexthop for prefix match */
+      rfapiUnicastNexthop2Prefix (afi, bi_exterior->attr, &pfx_nexthop);
+
+      if (prefix_match (&rn_interior->p, &pfx_nexthop))
+        {
+
+          struct prefix_rd *prd;
+          struct attr new_attr;
+          u_int32_t label = 0;
+
+          /* do pull-down */
+
+          /*
+           * add monitor to longer prefix
+           */
+
+          struct prefix *pfx_mon = prefix_new ();
+          prefix_copy (pfx_mon, pfx_exterior);
+          if (!RFAPI_MONITOR_EXTERIOR (rn_interior)->source)
+            {
+              RFAPI_MONITOR_EXTERIOR (rn_interior)->source =
+                skiplist_new (0, NULL, (void (*)(void *)) prefix_free);
+              route_lock_node (rn_interior);    /* sl */
+            }
+          skiplist_insert (RFAPI_MONITOR_EXTERIOR (rn_interior)->source,
+                           bi_exterior, pfx_mon);
+          route_lock_node (rn_interior);        /* sl entry */
+          if (!list_adopted)
+            {
+              list_adopted = list_new ();
+            }
+          listnode_add (list_adopted, bi_exterior);
+
+          /*
+           * Add constructed exterior routes based on the
+           * new interior route at the longer prefix.
+           */
+          if (bi_interior->extra)
+            {
+              prd = &bi_interior->extra->vnc.import.rd;
+              label = decode_label (bi_interior->extra->tag);
+            }
+          else
+            prd = NULL;
+
+          /* use local_pref from unicast route */
+          memset (&new_attr, 0, sizeof (struct attr));
+          bgp_attr_dup (&new_attr, bi_interior->attr);
+          if (bi_exterior &&
+              (bi_exterior->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))
+            {
+              new_attr.local_pref = bi_exterior->attr->local_pref;
+              new_attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
+            }
+
+          rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_UPDATE, bi_interior->peer, NULL,        /* rfd */
+                                         pfx_exterior,
+                                         NULL,
+                                         afi,
+                                         prd,
+                                         &new_attr,
+                                         ZEBRA_ROUTE_BGP_DIRECT_EXT,
+                                         BGP_ROUTE_REDISTRIBUTE, &label);
+
+          bgp_attr_extra_free (&new_attr);
+        }
+    }
+  if (list_adopted)
+    {
+      struct listnode *node;
+      struct route_node *bi_exterior;
+
+      for (ALL_LIST_ELEMENTS_RO (list_adopted, node, bi_exterior))
+        {
+          skiplist_delete (it->monitor_exterior_orphans, bi_exterior, NULL);
+        }
+      list_delete (list_adopted);
+    }
+}
+
+/*
+ * This function should be called after an interior VPN route
+ * has been deleted from an import_table.
+ * bi_interior must still be valid, but it must already be detached
+ * from its route node and the route node's valid_interior_count
+ * must already be decremented.
+ *
+ * NB should also be called whenever an existing vpn interior route
+ * becomes invalid (e.g., valid_interior_count is decremented)
+ */
+void
+vnc_import_bgp_exterior_del_route_interior (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *it,
+    struct route_node          *rn_interior,   /* VPN IT node */
+    struct bgp_info            *bi_interior)   /* VPN IT route */
+{
+  afi_t afi = family2afi (rn_interior->p.family);
+  struct route_node *par;
+  struct bgp_info *bi_exterior;
+  struct prefix *pfx_exterior;  /* exterior pfx */
+  void *cursor;
+  int rc;
+
+  if (!VALID_INTERIOR_TYPE (bi_interior->type))
+    {
+      zlog_debug ("%s: type %d not valid interior type, skipping",
+                  __func__, bi_interior->type);
+      return;
+    }
+
+  if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT])
+    {
+      zlog_debug ("%s: redist of exterior routes no enabled, skipping",
+                  __func__);
+      return;
+    }
+
+  if (it == bgp->rfapi->it_ce)
+    {
+      zlog_debug ("%s: it is it_ce, skipping", __func__);
+      return;
+    }
+
+  /* If no exterior routes depend on this prefix, nothing to do */
+  if (!RFAPI_HAS_MONITOR_EXTERIOR (rn_interior))
+    {
+      zlog_debug ("%s: no exterior monitor, skipping", __func__);
+      return;
+    }
+
+  /*debugging */
+  {
+    char str_pfx[BUFSIZ];
+
+    prefix2str (&rn_interior->p, str_pfx, BUFSIZ);
+    str_pfx[BUFSIZ - 1] = 0;
+
+    zlog_debug ("%s: interior prefix=%s, bi type=%d",
+                __func__, str_pfx, bi_interior->type);
+  }
+
+  /*
+   * Remove constructed routes based on the deleted interior route
+   */
+  cursor = NULL;
+  for (rc = skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source,
+                           (void **) &bi_exterior, (void **) &pfx_exterior,
+                           &cursor); !rc;
+       rc =
+       skiplist_next (RFAPI_MONITOR_EXTERIOR (rn_interior)->source,
+                      (void **) &bi_exterior, (void **) &pfx_exterior,
+                      &cursor))
+    {
+
+      struct prefix_rd *prd;
+      u_int32_t label = 0;
+
+      if (bi_interior->extra)
+        {
+          prd = &bi_interior->extra->vnc.import.rd;
+          label = decode_label (bi_interior->extra->tag);
+        }
+      else
+        prd = NULL;
+
+      rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_KILL, bi_interior->peer, NULL,      /* rfd */
+                                     pfx_exterior,
+                                     NULL,
+                                     afi,
+                                     prd,
+                                     bi_interior->attr,
+                                     ZEBRA_ROUTE_BGP_DIRECT_EXT,
+                                     BGP_ROUTE_REDISTRIBUTE, &label);
+    }
+
+  /*
+   * If there are no remaining valid interior routes at this prefix,
+   * we need to look up the tree for a possible node to move monitors to
+   */
+  if (RFAPI_MONITOR_EXTERIOR (rn_interior)->valid_interior_count)
+    {
+      zlog_debug ("%s: interior routes still present, skipping", __func__);
+      return;
+    }
+
+  /*
+   * Find nearest parent with at least one valid interior route
+   * If none is found, par will end up NULL, and we will move
+   * the monitors to the orphan list for this import table
+   */
+  for (par = rn_interior->parent; par; par = par->parent)
+    {
+      if (RFAPI_MONITOR_EXTERIOR (par)->valid_interior_count)
+        break;
+    }
+
+  zlog_debug ("%s: par=%p, ext src: %p", __func__,
+              par, RFAPI_MONITOR_EXTERIOR (rn_interior)->source);
+
+  /* move all monitors */
+  /*
+   * We will use and delete every element of the source skiplist
+   */
+  while (!skiplist_first (RFAPI_MONITOR_EXTERIOR (rn_interior)->source,
+                          (void **) &bi_exterior, (void **) &pfx_exterior))
+    {
+
+      struct prefix *pfx_mon = prefix_new ();
+
+      prefix_copy (pfx_mon, pfx_exterior);
+
+      if (par)
+        {
+
+          struct bgp_info *bi;
+
+          /* 
+           * Add monitor to parent node
+           */
+          if (!RFAPI_MONITOR_EXTERIOR (par)->source)
+            {
+              RFAPI_MONITOR_EXTERIOR (par)->source =
+                skiplist_new (0, NULL, (void (*)(void *)) prefix_free);
+              route_lock_node (par);    /* sl */
+            }
+          skiplist_insert (RFAPI_MONITOR_EXTERIOR (par)->source,
+                           bi_exterior, pfx_mon);
+          route_lock_node (par);        /* sl entry */
+
+          /* Add constructed exterior routes based on parent */
+          for (bi = par->info; bi; bi = bi->next)
+            {
+
+              struct prefix_rd *prd;
+              struct attr new_attr;
+              u_int32_t label = 0;
+
+              if (bi->type == ZEBRA_ROUTE_BGP_DIRECT_EXT)
+                continue;
+
+              if (bi->extra)
+                {
+                  prd = &bi->extra->vnc.import.rd;
+                  label = decode_label (bi->extra->tag);
+                }
+              else
+                prd = NULL;
+
+              /* use local_pref from unicast route */
+              memset (&new_attr, 0, sizeof (struct attr));
+              bgp_attr_dup (&new_attr, bi->attr);
+              if (bi_exterior &&
+                  (bi_exterior->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))
+                {
+                  new_attr.local_pref = bi_exterior->attr->local_pref;
+                  new_attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
+                }
+
+              rfapiBgpInfoFilteredImportVPN (it, FIF_ACTION_UPDATE, bi->peer, NULL,     /* rfd */
+                                             pfx_exterior,
+                                             NULL,
+                                             afi,
+                                             prd,
+                                             &new_attr,
+                                             ZEBRA_ROUTE_BGP_DIRECT_EXT,
+                                             BGP_ROUTE_REDISTRIBUTE, &label);
+
+              bgp_attr_extra_free (&new_attr);
+            }
+
+        }
+      else
+        {
+
+          /*
+           * No interior route for exterior's nexthop. Save monitor
+           * in orphan list to await future route.
+           */
+          skiplist_insert (it->monitor_exterior_orphans,
+                           bi_exterior, pfx_mon);
+        }
+
+      skiplist_delete_first (RFAPI_MONITOR_EXTERIOR (rn_interior)->source);
+      route_unlock_node (rn_interior);  /* sl entry */
+    }
+  if (skiplist_empty (RFAPI_MONITOR_EXTERIOR (rn_interior)->source))
+    {
+      skiplist_free (RFAPI_MONITOR_EXTERIOR (rn_interior)->source);
+      RFAPI_MONITOR_EXTERIOR (rn_interior)->source = NULL;
+      route_unlock_node (rn_interior);  /* sl itself */
+    }
+
+}
+
+/***********************************************************************
+ *                     Generic add/delete unicast routes
+ ***********************************************************************/
+
+void
+vnc_import_bgp_add_route (
+    struct bgp         *bgp,
+    struct prefix      *prefix,
+    struct bgp_info    *info)
+{
+  afi_t afi = family2afi (prefix->family);
+
+  {
+    struct prefix pfx_nexthop;
+    char buf[BUFSIZ];
+    char buf_nh[BUFSIZ];
+
+    prefix2str (prefix, buf, BUFSIZ);
+    rfapiUnicastNexthop2Prefix (afi, info->attr, &pfx_nexthop);
+    prefix2str (&pfx_nexthop, buf_nh, BUFSIZ);
+
+    zlog_debug ("%s: pfx %s, nh %s", __func__, buf, buf_nh);
+  }
+#if DEBUG_RHN_LIST
+  print_rhn_list(__func__, "ENTER ");
+#endif
+  VNC_RHNCK (enter);
+
+  if (!afi)
+    {
+      zlog_err ("%s: can't get afi of prefix", __func__);
+      return;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  /* check vnc redist flag for bgp direct routes */
+  if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT])
+    {
+      zlog_debug
+        ("%s: bgp->rfapi_cfg->redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+         __func__, afi, ZEBRA_ROUTE_BGP_DIRECT);
+      return;
+    }
+
+  switch (bgp->rfapi_cfg->redist_mode)
+    {
+    case VNC_REDIST_MODE_PLAIN:
+      vnc_import_bgp_add_route_mode_plain (bgp, prefix, info);
+      break;
+
+    case VNC_REDIST_MODE_RFG:
+      if (bgp->rfapi_cfg->rfg_redist)
+        vnc_import_bgp_add_route_mode_nvegroup (bgp, prefix, info,
+                                                bgp->rfapi_cfg->rfg_redist);
+      else
+        zlog_debug ("%s: mode RFG but no redist RFG", __func__);
+      break;
+
+    case VNC_REDIST_MODE_RESOLVE_NVE:
+      vnc_import_bgp_add_route_mode_resolve_nve (bgp, prefix, info);
+      break;
+    }
+#if DEBUG_RHN_LIST
+  print_rhn_list(__func__, "LEAVE ");
+#endif
+  VNC_RHNCK (leave);
+}
+
+/*
+ * "Withdrawing a Route" import process
+ */
+void
+vnc_import_bgp_del_route (
+    struct bgp         *bgp,
+    struct prefix      *prefix,
+    struct bgp_info    *info)  /* unicast info */
+{
+  afi_t afi = family2afi (prefix->family);
+
+  assert (afi);
+
+  {
+    struct prefix pfx_nexthop;
+    char buf[BUFSIZ];
+    char buf_nh[BUFSIZ];
+
+    prefix2str (prefix, buf, BUFSIZ);
+    rfapiUnicastNexthop2Prefix (afi, info->attr, &pfx_nexthop);
+    prefix2str (&pfx_nexthop, buf_nh, BUFSIZ);
+
+    zlog_debug ("%s: pfx %s, nh %s", __func__, buf, buf_nh);
+  }
+#if DEBUG_RHN_LIST
+  print_rhn_list(__func__, "ENTER ");
+#endif
+  VNC_RHNCK (enter);
+
+  /* check bgp redist flag for vnc direct ("vpn") routes */
+
+  if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT])
+    {
+      zlog_debug ("%s: bgp redistribution of afi=%d VNC direct routes is off",
+                  __func__, afi);
+      return;
+    }
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  switch (bgp->rfapi_cfg->redist_mode)
+    {
+    case VNC_REDIST_MODE_PLAIN:
+      vnc_import_bgp_del_route_mode_plain (bgp, prefix, info);
+      break;
+
+    case VNC_REDIST_MODE_RFG:
+      if (bgp->rfapi_cfg->rfg_redist)
+        vnc_import_bgp_del_route_mode_nvegroup (bgp, prefix, info);
+      else
+        zlog_debug ("%s: mode RFG but no redist RFG", __func__);
+      break;
+
+    case VNC_REDIST_MODE_RESOLVE_NVE:
+      vnc_import_bgp_del_route_mode_resolve_nve (bgp, afi, prefix, info);
+      break;
+
+    }
+#if DEBUG_RHN_LIST
+  print_rhn_list(__func__, "LEAVE ");
+#endif
+  VNC_RHNCK (leave);
+}
+
+
+/***********************************************************************
+ *                     Enable/Disable
+ ***********************************************************************/
+
+void
+vnc_import_bgp_redist_enable (struct bgp *bgp, afi_t afi)
+{
+  /* iterate over bgp unicast v4 and v6 routes, call vnc_import_bgp_add_route */
+
+  struct bgp_node *rn;
+
+  zlog_debug ("%s: entry, afi=%d", __func__, afi);
+
+  if (bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT])
+    {
+      zlog_debug ("%s: already enabled for afi %d, skipping", __func__, afi);
+      return;
+    }
+  bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 1;
+
+  for (rn = bgp_table_top (bgp->rib[afi][SAFI_UNICAST]);
+       rn; rn = bgp_route_next (rn))
+    {
+
+      struct bgp_info *bi;
+
+      for (bi = rn->info; bi; bi = bi->next)
+        {
+
+          if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+            continue;
+
+          vnc_import_bgp_add_route (bgp, &rn->p, bi);
+        }
+    }
+  zlog_debug ("%s: set redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] return",
+              __func__, afi, ZEBRA_ROUTE_BGP_DIRECT);
+}
+
+void
+vnc_import_bgp_exterior_redist_enable (struct bgp *bgp, afi_t afi)
+{
+  struct bgp *bgp_exterior;
+  struct bgp_node *rn;
+
+  bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view;
+
+  if (bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT])
+    {
+      zlog_debug ("%s: already enabled for afi %d, skipping", __func__, afi);
+      return;
+    }
+  bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT] = 1;
+
+  if (!bgp_exterior)
+    {
+      zlog_debug ("%s: no exterior view set yet, no routes to import yet",
+                  __func__);
+      return;
+    }
+
+  for (rn = bgp_table_top (bgp_exterior->rib[afi][SAFI_UNICAST]);
+       rn; rn = bgp_route_next (rn))
+    {
+
+      struct bgp_info *bi;
+
+      for (bi = rn->info; bi; bi = bi->next)
+        {
+
+          if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+            continue;
+
+          vnc_import_bgp_exterior_add_route (bgp_exterior, &rn->p, bi);
+        }
+    }
+  zlog_debug ("%s: set redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] return",
+              __func__, afi, ZEBRA_ROUTE_BGP_DIRECT);
+
+}
+
+/*
+ * This function is for populating a newly-created Import Table
+ */
+void
+vnc_import_bgp_exterior_redist_enable_it (
+    struct bgp                 *bgp,
+    afi_t                      afi,
+    struct rfapi_import_table  *it_only)
+{
+  struct bgp *bgp_exterior;
+  struct bgp_node *rn;
+
+  zlog_debug ("%s: entry", __func__);
+
+  bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view;
+
+  if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT])
+    {
+      zlog_debug ("%s: not enabled for afi %d, skipping", __func__, afi);
+      return;
+    }
+
+  if (!bgp_exterior)
+    {
+      zlog_debug ("%s: no exterior view set yet, no routes to import yet",
+                  __func__);
+      return;
+    }
+
+  for (rn = bgp_table_top (bgp_exterior->rib[afi][SAFI_UNICAST]);
+       rn; rn = bgp_route_next (rn))
+    {
+
+      struct bgp_info *bi;
+
+      for (bi = rn->info; bi; bi = bi->next)
+        {
+
+          if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+            continue;
+
+          vnc_import_bgp_exterior_add_route_it (bgp_exterior, &rn->p, bi,
+                                                it_only);
+        }
+    }
+
+}
+
+
+void
+vnc_import_bgp_redist_disable (struct bgp *bgp, afi_t afi)
+{
+  /*
+   * iterate over vpn routes, find routes of type ZEBRA_ROUTE_BGP_DIRECT,
+   * delete (call timer expire immediately)
+   */
+  struct bgp_node *rn1;
+  struct bgp_node *rn2;
+
+  zlog_debug ("%s: entry", __func__);
+
+  if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT])
+    {
+      zlog_debug ("%s: already disabled for afi %d, skipping", __func__, afi);
+      return;
+    }
+
+  /*
+   * Two-level table for SAFI_MPLS_VPN
+   * Be careful when changing the things we iterate over
+   */
+  for (rn1 = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]);
+       rn1; rn1 = bgp_route_next (rn1))
+    {
+
+      if (rn1->info)
+        {
+          for (rn2 = bgp_table_top (rn1->info);
+               rn2; rn2 = bgp_route_next (rn2))
+            {
+
+              struct bgp_info *bi;
+              struct bgp_info *nextbi;
+
+              for (bi = rn2->info; bi; bi = nextbi)
+                {
+
+                  nextbi = bi->next;
+
+                  if (bi->type == ZEBRA_ROUTE_BGP_DIRECT)
+                    {
+
+                      struct rfapi_descriptor *rfd;
+                      vncHDBgpDirect.peer = bi->peer;
+
+                      rfd = bi->extra->vnc.export.rfapi_handle;
+
+                      zlog_debug
+                        ("%s: deleting bi=%p, bi->peer=%p, bi->type=%d, bi->sub_type=%d, bi->extra->vnc.export.rfapi_handle=%p [passing rfd=%p]",
+                         __func__, bi, bi->peer, bi->type, bi->sub_type,
+                         (bi->extra ? bi->extra->vnc.
+                          export.rfapi_handle : NULL), rfd);
+
+
+                      del_vnc_route (rfd, bi->peer, bgp, SAFI_MPLS_VPN, &rn2->p, (struct prefix_rd *) &rn1->p, bi->type, bi->sub_type, NULL, 1);        /* kill */
+
+                      vncHDBgpDirect.peer = NULL;
+                    }
+                }
+            }
+        }
+    }
+  /* Clear RHN list */
+  if (bgp->rfapi->resolve_nve_nexthop)
+    {
+      struct prefix_bag *pb;
+      struct bgp_info *info;
+      while (!skiplist_first
+             (bgp->rfapi->resolve_nve_nexthop, NULL, (void *) &pb))
+        {
+          info = pb->ubi;
+          skiplist_delete_first (bgp->rfapi->resolve_nve_nexthop);
+          bgp_info_unlock (info);
+        }
+    }
+
+  bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 0;
+  zlog_debug ("%s: return", __func__);
+}
+
+
+void
+vnc_import_bgp_exterior_redist_disable (struct bgp *bgp, afi_t afi)
+{
+  struct rfapi_cfg *hc = bgp->rfapi_cfg;
+  struct bgp *bgp_exterior = hc->redist_bgp_exterior_view;
+
+  zlog_debug ("%s: entry", __func__);
+
+  if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT])
+    {
+      zlog_debug ("%s: already disabled for afi %d, skipping", __func__, afi);
+      return;
+    }
+
+  if (!bgp_exterior)
+    {
+      zlog_debug ("%s: bgp exterior view not defined, skipping", __func__);
+      return;
+    }
+
+
+  {
+    struct bgp_node *rn;
+    for (rn = bgp_table_top (bgp_exterior->rib[afi][SAFI_UNICAST]);
+         rn; rn = bgp_route_next (rn))
+      {
+
+        struct bgp_info *bi;
+
+        for (bi = rn->info; bi; bi = bi->next)
+          {
+
+            if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED))
+              continue;
+
+            vnc_import_bgp_exterior_del_route (bgp_exterior, &rn->p, bi);
+          }
+      }
+#if DEBUG_RHN_LIST
+    print_rhn_list (__func__, NULL);
+#endif
+  }
+
+  bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT] = 0;
+  zlog_debug ("%s: return", __func__);
+}
diff --git a/bgpd/rfapi/vnc_import_bgp.h b/bgpd/rfapi/vnc_import_bgp.h
new file mode 100644 (file)
index 0000000..acab0c6
--- /dev/null
@@ -0,0 +1,93 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_
+#define _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_
+
+#include "zebra.h"
+#include "prefix.h"
+
+#include "bgpd.h"
+#include "bgp_route.h"
+
+#define VALID_INTERIOR_TYPE(type)      \
+    (((type) == ZEBRA_ROUTE_BGP) || ((type) == ZEBRA_ROUTE_BGP_DIRECT))
+
+extern uint32_t
+calc_local_pref (struct attr *attr, struct peer *peer);
+
+extern int
+vnc_prefix_cmp (void *pfx1, void *pfx2);
+
+extern void
+vnc_import_bgp_add_route (
+    struct bgp         *bgp,
+    struct prefix      *prefix,
+    struct bgp_info    *info);
+
+extern void
+vnc_import_bgp_del_route (
+    struct bgp         *bgp,
+    struct prefix      *prefix,
+    struct bgp_info    *info);
+
+extern void
+vnc_import_bgp_redist_enable (struct bgp *bgp, afi_t afi);
+
+extern void
+vnc_import_bgp_redist_disable (struct bgp *bgp, afi_t afi);
+
+extern void
+vnc_import_bgp_exterior_redist_enable (struct bgp *bgp, afi_t afi);
+
+extern void
+vnc_import_bgp_exterior_redist_disable (struct bgp *bgp, afi_t afi);
+
+
+extern void
+vnc_import_bgp_exterior_add_route (
+    struct bgp         *bgp,   /* exterior instance, we hope */
+    struct prefix      *prefix,/* unicast prefix */
+    struct bgp_info    *info); /* unicast info */
+
+extern void
+vnc_import_bgp_exterior_del_route (
+    struct bgp         *bgp,
+    struct prefix      *prefix,/* unicast prefix */
+    struct bgp_info    *info); /* unicast info */
+
+extern void
+vnc_import_bgp_add_vnc_host_route_mode_resolve_nve (
+    struct bgp         *bgp,
+    struct prefix_rd   *prd,           /* RD */
+    struct bgp_table   *table_rd,      /* per-rd VPN route table */
+    struct prefix      *prefix,        /* VPN prefix */
+    struct bgp_info    *bi);           /* new VPN host route */
+
+extern void
+vnc_import_bgp_del_vnc_host_route_mode_resolve_nve (
+    struct bgp         *bgp,
+    struct prefix_rd   *prd,           /* RD */
+    struct bgp_table   *table_rd,      /* per-rd VPN route table */
+    struct prefix      *prefix,        /* VPN prefix */
+    struct bgp_info    *bi);           /* old VPN host route */
+
+#endif /* _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ */
diff --git a/bgpd/rfapi/vnc_import_bgp_p.h b/bgpd/rfapi/vnc_import_bgp_p.h
new file mode 100644 (file)
index 0000000..4d37ce9
--- /dev/null
@@ -0,0 +1,51 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_
+#define _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_
+
+#include "zebra.h"
+#include "prefix.h"
+
+#include "bgpd.h"
+#include "bgp_route.h"
+
+extern void
+vnc_import_bgp_exterior_add_route_interior (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *it,
+    struct route_node          *rn_interior,   /* VPN IT node */
+    struct bgp_info            *bi_interior);  /* VPN IT route */
+
+extern void
+vnc_import_bgp_exterior_del_route_interior (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *it,
+    struct route_node          *rn_interior, /* VPN IT node */
+    struct bgp_info            *bi_interior);  /* VPN IT route */
+
+extern void
+vnc_import_bgp_exterior_redist_enable_it (
+    struct bgp                 *bgp,
+    afi_t                      afi,
+    struct rfapi_import_table  *it_only);
+
+#endif /* _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_ */
diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c
new file mode 100644 (file)
index 0000000..54e8a2a
--- /dev/null
@@ -0,0 +1,1121 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/*
+ * File:       vnc_zebra.c
+ * Purpose:    Handle exchange of routes between VNC and Zebra
+ */
+
+#include "zebra.h"
+#include "prefix.h"
+#include "table.h"
+#include "log.h"
+#include "command.h"
+#include "zclient.h"
+#include "stream.h"
+#include "memory.h"
+
+#include "bgpd.h"
+#include "bgp_ecommunity.h"
+#include "bgp_route.h"
+#include "bgp_debug.h"
+#include "bgp_advertise.h"
+
+#include "bgp_rfapi_cfg.h"
+#include "rfapi.h"
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "vnc_zebra.h"
+#include "rfapi_vty.h"
+#include "rfapi_backend.h"
+
+static struct rfapi_descriptor vncHD1VR;        /* Single-VR export dummy nve descr */
+static struct zclient *zclient_vnc = NULL;
+
+/***********************************************************************
+ *     REDISTRIBUTE: Zebra sends updates/withdraws to BGPD
+ ***********************************************************************/
+
+/*
+ * Routes coming from zebra get added to VNC here
+ */
+static void
+vnc_redistribute_add (
+    struct prefix      *p,
+    struct in_addr     *nexthop,
+    u_int32_t          metric,
+    uint8_t            type)
+{
+  struct bgp *bgp = bgp_get_default ();
+  struct prefix_rd prd;
+  struct rfapi_ip_addr vnaddr;
+  afi_t afi;
+  uint32_t local_pref = rfp_cost_to_localpref (metric > 255 ? 255 : metric);
+
+  if (!bgp)
+    return;
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  afi = family2afi (p->family);
+  if (!afi)
+    {
+      zlog_debug ("%s: unknown prefix address family %d", __func__,
+                  p->family);
+      return;
+    }
+
+  if (!bgp->rfapi_cfg->redist[afi][type])
+    {
+      zlog_debug
+        ("%s: bgp->rfapi_cfg->redist[afi=%d][type=%d] is 0, skipping",
+         __func__, afi, type);
+      return;
+    }
+  if (!bgp->rfapi_cfg->rfg_redist)
+    {
+      zlog_debug ("%s: no redist nve group, skipping", __func__);
+      return;
+    }
+
+  /*
+   * Assume nve group's configured VN address prefix is a host
+   * route which also happens to give the NVE VN address to use
+   * for redistributing into VNC.
+   */
+  vnaddr.addr_family = bgp->rfapi_cfg->rfg_redist->vn_prefix.family;
+  switch (bgp->rfapi_cfg->rfg_redist->vn_prefix.family)
+    {
+    case AF_INET:
+      if (bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen != 32)
+        {
+          zlog_debug
+            ("%s: redist nve group VN prefix len (%d) != 32, skipping",
+             __func__, bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen);
+          return;
+        }
+      vnaddr.addr.v4 = bgp->rfapi_cfg->rfg_redist->vn_prefix.u.prefix4;
+      break;
+    case AF_INET6:
+      if (bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen != 128)
+        {
+          zlog_debug
+            ("%s: redist nve group VN prefix len (%d) != 128, skipping",
+             __func__, bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen);
+          return;
+        }
+      vnaddr.addr.v6 = bgp->rfapi_cfg->rfg_redist->vn_prefix.u.prefix6;
+      break;
+    default:
+      zlog_debug
+        ("%s: no redist nve group VN host prefix configured, skipping",
+         __func__);
+      return;
+    }
+
+  /*
+   * Assume nve group's configured UN address prefix is a host
+   * route which also happens to give the NVE UN address to use
+   * for redistributing into VNC.
+   */
+
+  /*
+   * Set UN address in dummy nve descriptor so add_vnc_route
+   * can use it in VNC tunnel SubTLV
+   */
+  {
+    struct rfapi_ip_prefix pfx_un;
+
+    rfapiQprefix2Rprefix (&bgp->rfapi_cfg->rfg_redist->un_prefix, &pfx_un);
+
+    switch (pfx_un.prefix.addr_family)
+      {
+      case AF_INET:
+        if (pfx_un.length != 32)
+          {
+            zlog_debug
+              ("%s: redist nve group UN prefix len (%d) != 32, skipping",
+               __func__, pfx_un.length);
+            return;
+          }
+        break;
+      case AF_INET6:
+        if (pfx_un.length != 128)
+          {
+            zlog_debug
+              ("%s: redist nve group UN prefix len (%d) != 128, skipping",
+               __func__, pfx_un.length);
+            return;
+          }
+        break;
+      default:
+        zlog_debug
+          ("%s: no redist nve group UN host prefix configured, skipping",
+           __func__);
+        return;
+      }
+
+    vncHD1VR.un_addr = pfx_un.prefix;
+
+    if (!vncHD1VR.peer)
+      {
+        /*
+         * Same setup as in rfapi_open()
+         */
+        vncHD1VR.peer = peer_new (bgp);
+        vncHD1VR.peer->status = Established;    /* keep bgp core happy */
+        bgp_sync_delete (vncHD1VR.peer);        /* don't need these */
+        if (vncHD1VR.peer->ibuf)
+          {
+            stream_free (vncHD1VR.peer->ibuf);  /* don't need it */
+            vncHD1VR.peer->ibuf = NULL;
+          }
+        if (vncHD1VR.peer->obuf)
+          {
+            stream_fifo_free (vncHD1VR.peer->obuf);     /* don't need it */
+            vncHD1VR.peer->obuf = NULL;
+          }
+        if (vncHD1VR.peer->work)
+          {
+            stream_free (vncHD1VR.peer->work);  /* don't need it */
+            vncHD1VR.peer->work = NULL;
+          }
+        /* base code assumes have valid host pointer */
+        vncHD1VR.peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, ".zebra.");
+
+        /* Mark peer as belonging to HD */
+        SET_FLAG (vncHD1VR.peer->flags, PEER_FLAG_IS_RFAPI_HD);
+      }
+  }
+
+  memset (&prd, 0, sizeof (prd));
+  prd = bgp->rfapi_cfg->rfg_redist->rd;
+  prd.family = AF_UNSPEC;
+  prd.prefixlen = 64;
+
+  add_vnc_route (&vncHD1VR,     /* cookie + UN addr */
+                 bgp, SAFI_MPLS_VPN, p, &prd, &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime), NULL,   /* RFP options */
+                 NULL,          /* struct rfapi_un_option */
+                 NULL,          /* struct rfapi_vn_option */
+                 bgp->rfapi_cfg->rfg_redist->rt_export_list, NULL, NULL,        /* label: default */
+                 type, BGP_ROUTE_REDISTRIBUTE, 0);      /* flags */
+}
+
+/*
+ * Route deletions from zebra propagate to VNC here
+ */
+static void
+vnc_redistribute_delete (struct prefix *p, uint8_t type)
+{
+  struct bgp *bgp = bgp_get_default ();
+  struct prefix_rd prd;
+  afi_t afi;
+
+  if (!bgp)
+    return;
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+  afi = family2afi (p->family);
+  if (!afi)
+    {
+      zlog_debug ("%s: unknown prefix address family %d", __func__,
+                  p->family);
+      return;
+    }
+  if (!bgp->rfapi_cfg->redist[afi][type])
+    {
+      zlog_debug
+        ("%s: bgp->rfapi_cfg->redist[afi=%d][type=%d] is 0, skipping",
+         __func__, afi, type);
+      return;
+    }
+  if (!bgp->rfapi_cfg->rfg_redist)
+    {
+      zlog_debug ("%s: no redist nve group, skipping", __func__);
+      return;
+    }
+
+  memset (&prd, 0, sizeof (prd));
+  prd = bgp->rfapi_cfg->rfg_redist->rd;
+  prd.family = AF_UNSPEC;
+  prd.prefixlen = 64;
+
+  del_vnc_route (&vncHD1VR,     /* use dummy ptr as cookie */
+                 vncHD1VR.peer,
+                 bgp,
+                 SAFI_MPLS_VPN,
+                 p, &prd, type, BGP_ROUTE_REDISTRIBUTE, NULL, 0);
+}
+
+/*
+ * Flush all redistributed routes of type <type>
+ */
+static void
+vnc_redistribute_withdraw (struct bgp *bgp, afi_t afi, uint8_t type)
+{
+  struct prefix_rd prd;
+  struct bgp_table *table;
+  struct bgp_node *prn;
+  struct bgp_node *rn;
+
+  zlog_debug ("%s: entry", __func__);
+
+  if (!bgp)
+    return;
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  /*
+   * Loop over all the RDs
+   */
+  for (prn = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); prn;
+       prn = bgp_route_next (prn))
+    {
+      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;
+
+      for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+        {
+
+          struct bgp_info *ri;
+
+          for (ri = rn->info; ri; ri = ri->next)
+            {
+              if (ri->type == type)
+                {               /* has matching redist type */
+                  break;
+                }
+            }
+          if (ri)
+            {
+              del_vnc_route (&vncHD1VR, /* use dummy ptr as cookie */
+                             vncHD1VR.peer,
+                             bgp,
+                             SAFI_MPLS_VPN,
+                             &(rn->p),
+                             &prd, type, BGP_ROUTE_REDISTRIBUTE, NULL, 0);
+            }
+        }
+    }
+  zlog_debug ("%s: return", __func__);
+}
+
+/*
+ * Zebra route add and delete treatment.
+ *
+ * Assumes 1 nexthop
+ */
+static int
+vnc_zebra_read_ipv4 (
+    int                        command,
+    struct zclient     *zclient,
+    zebra_size_t       length,
+    vrf_id_t           vrf_id)
+{
+  struct stream *s;
+  struct zapi_ipv4 api;
+  unsigned long ifindex;
+  struct in_addr nexthop;
+  struct prefix_ipv4 p;
+
+  s = zclient->ibuf;
+  ifindex = 0;
+  nexthop.s_addr = 0;
+
+  /* Type, flags, message. */
+  api.type = stream_getc (s);
+  api.flags = stream_getc (s);
+  api.message = stream_getc (s);
+
+  /* IPv4 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv4));
+  p.family = AF_INET;
+  p.prefixlen = stream_getc (s);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      api.nexthop_num = stream_getc (s);
+      nexthop.s_addr = stream_get_ipv4 (s);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX))
+    {
+      api.ifindex_num = stream_getc (s);
+      ifindex = stream_getl (s);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (s);
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (s);
+  else
+    api.metric = 0;
+
+  if (command == ZEBRA_IPV4_ROUTE_ADD)
+    {
+      if (BGP_DEBUG (zebra, ZEBRA))
+        {
+          char buf[2][INET_ADDRSTRLEN];
+          zlog_debug
+            ("%s: Zebra rcvd: IPv4 route add %s %s/%d nexthop %s metric %u",
+             __func__, zebra_route_string (api.type), inet_ntop (AF_INET,
+                                                                 &p.prefix,
+                                                                 buf[0],
+                                                                 sizeof (buf
+                                                                         [0])),
+             p.prefixlen, inet_ntop (AF_INET, &nexthop, buf[1],
+                                     sizeof (buf[1])), api.metric);
+        }
+      vnc_redistribute_add ((struct prefix *) &p, &nexthop, api.metric,
+                            api.type);
+    }
+  else
+    {
+      if (BGP_DEBUG (zebra, ZEBRA))
+        {
+          char buf[2][INET_ADDRSTRLEN];
+          zlog_debug ("%s: Zebra rcvd: IPv4 route delete %s %s/%d "
+                      "nexthop %s metric %u",
+                      __func__,
+                      zebra_route_string (api.type),
+                      inet_ntop (AF_INET, &p.prefix, buf[0], sizeof (buf[0])),
+                      p.prefixlen,
+                      inet_ntop (AF_INET, &nexthop, buf[1], sizeof (buf[1])),
+                      api.metric);
+        }
+      vnc_redistribute_delete ((struct prefix *) &p, api.type);
+    }
+
+  return 0;
+}
+
+/* Zebra route add and delete treatment. */
+static int
+vnc_zebra_read_ipv6 (
+    int                        command,
+    struct zclient     *zclient,
+    zebra_size_t       length,
+    vrf_id_t           vrf_id)
+{
+  struct stream *s;
+  struct zapi_ipv6 api;
+  unsigned long ifindex;
+  struct in6_addr nexthop;
+  struct prefix_ipv6 p;
+
+  s = zclient->ibuf;
+  ifindex = 0;
+  memset (&nexthop, 0, sizeof (struct in6_addr));
+
+  /* Type, flags, message. */
+  api.type = stream_getc (s);
+  api.flags = stream_getc (s);
+  api.message = stream_getc (s);
+
+  /* IPv6 prefix. */
+  memset (&p, 0, sizeof (struct prefix_ipv6));
+  p.family = AF_INET6;
+  p.prefixlen = stream_getc (s);
+  stream_get (&p.prefix, s, PSIZE (p.prefixlen));
+
+  /* Nexthop, ifindex, distance, metric. */
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP))
+    {
+      api.nexthop_num = stream_getc (s);
+      stream_get (&nexthop, s, 16);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX))
+    {
+      api.ifindex_num = stream_getc (s);
+      ifindex = stream_getl (s);
+    }
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE))
+    api.distance = stream_getc (s);
+  else
+    api.distance = 0;
+  if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC))
+    api.metric = stream_getl (s);
+  else
+    api.metric = 0;
+
+  /* Simply ignore link-local address. */
+  if (IN6_IS_ADDR_LINKLOCAL (&p.prefix))
+    return 0;
+
+  if (command == ZEBRA_IPV6_ROUTE_ADD)
+    {
+      if (BGP_DEBUG (zebra, ZEBRA))
+        {
+          char buf[INET6_ADDRSTRLEN];
+          zlog_debug ("Zebra rcvd: IPv6 route add %s %s/%d metric %u",
+                      zebra_route_string (api.type),
+                      inet_ntop (AF_INET6, &p.prefix, buf, sizeof (buf)),
+                      p.prefixlen, api.metric);
+        }
+      vnc_redistribute_add ((struct prefix *) &p, NULL, api.metric, api.type);
+    }
+  else
+    {
+      if (BGP_DEBUG (zebra, ZEBRA))
+        {
+          char buf[INET6_ADDRSTRLEN];
+          zlog_debug ("Zebra rcvd: IPv6 route delete %s %s/%d metric %u",
+                      zebra_route_string (api.type),
+                      inet_ntop (AF_INET6, &p.prefix, buf, sizeof (buf)),
+                      p.prefixlen, api.metric);
+        }
+      vnc_redistribute_delete ((struct prefix *) &p, api.type);
+    }
+
+  return 0;
+}
+
+/***********************************************************************
+ *     vnc_bgp_zebra_*: VNC sends updates/withdraws to Zebra
+ ***********************************************************************/
+
+/*
+ * low-level message builder
+ */
+static void
+vnc_zebra_route_msg (
+    struct prefix      *p,
+    int                        nhp_count,
+    void               *nhp_ary,
+    int                        add)   /* 1 = add, 0 = del */
+{
+  if (!nhp_count)
+    {
+      zlog_debug ("%s: empty nexthop list, skipping", __func__);
+      return;
+    }
+
+  if (p->family == AF_INET)
+    {
+
+      struct zapi_ipv4 api;
+
+      api.flags = 0;
+      api.vrf_id = VRF_DEFAULT;
+      api.type = ZEBRA_ROUTE_VNC;
+      api.message = 0;
+      SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);     /* TBD what's it mean? */
+      api.nexthop_num = nhp_count;
+      api.nexthop = nhp_ary;
+      api.ifindex_num = 0;
+
+      if (BGP_DEBUG (zebra, ZEBRA))
+        {
+
+          char buf[INET_ADDRSTRLEN];
+          zlog_debug ("%s: Zebra send: IPv4 route %s %s/%d, nhp_count=%d",
+                      __func__,
+                      (add ? "add" : "del"),
+                      inet_ntop (AF_INET, &p->u.prefix4, buf, sizeof (buf)),
+                      p->prefixlen, nhp_count);
+        }
+
+      zapi_ipv4_route ((add ? ZEBRA_IPV4_NEXTHOP_ADD :
+                        ZEBRA_IPV4_NEXTHOP_DELETE), zclient_vnc,
+                       (struct prefix_ipv4 *) p, &api);
+
+    }
+  else if (p->family == AF_INET6)
+    {
+
+      struct zapi_ipv6 api;
+      ifindex_t ifindex = 0;
+
+      /* Make Zebra API structure. */
+      api.flags = 0;
+      api.vrf_id = VRF_DEFAULT;
+      api.type = ZEBRA_ROUTE_VNC;
+      api.message = 0;
+      SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP);     /* TBD means? */
+      api.nexthop_num = nhp_count;
+      api.nexthop = nhp_ary;
+      SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX);
+      api.ifindex_num = 1;
+      api.ifindex = &ifindex;
+
+      if (BGP_DEBUG (zebra, ZEBRA))
+        {
+
+          char buf[INET6_ADDRSTRLEN];
+          zlog_debug ("%s: Zebra send: IPv6 route %s %s/%d nhp_count=%d",
+                      __func__,
+                      (add ? "add" : "del"),
+                      inet_ntop (AF_INET6, &p->u.prefix6, buf, sizeof (buf)),
+                      p->prefixlen, nhp_count);
+        }
+
+      zapi_ipv6_route ((add ? ZEBRA_IPV6_NEXTHOP_ADD :
+                        ZEBRA_IPV6_NEXTHOP_DELETE), zclient_vnc,
+                       (struct prefix_ipv6 *) p, &api);
+    }
+  else
+    {
+      zlog_debug ("%s: unknown prefix address family, skipping", __func__);
+      return;
+    }
+}
+
+
+static void
+nve_list_to_nh_array (
+    u_char     family,
+    struct list        *nve_list,
+    int                *nh_count_ret,
+    void       **nh_ary_ret,   /* returned address array */
+    void       **nhp_ary_ret)  /* returned pointer array */
+{
+  int nve_count = listcount (nve_list);
+
+  *nh_count_ret = 0;
+  *nh_ary_ret = NULL;
+  *nhp_ary_ret = NULL;
+
+  if (!nve_count)
+    {
+      zlog_debug ("%s: empty nve_list, skipping", __func__);
+      return;
+    }
+
+  if (family == AF_INET)
+    {
+      struct listnode *ln;
+      struct in_addr *iap;
+      struct in_addr **v;
+
+      /* 
+       * Array of nexthop addresses
+       */
+      *nh_ary_ret = XCALLOC (MTYPE_TMP, nve_count * sizeof (struct in_addr));
+
+      /*
+       * Array of pointers to nexthop addresses
+       */
+      *nhp_ary_ret =
+        XCALLOC (MTYPE_TMP, nve_count * sizeof (struct in_addr *));
+      iap = *nh_ary_ret;
+      v = *nhp_ary_ret;
+
+      for (ln = listhead (nve_list); ln; ln = listnextnode (ln))
+        {
+
+          struct rfapi_descriptor *irfd;
+          struct prefix nhp;
+
+          irfd = listgetdata (ln);
+
+          if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp))
+            continue;
+
+          *iap = nhp.u.prefix4;
+          *v = iap;
+          zlog_debug ("%s: ipadr: (%p)<-0x%x, ptr: (%p)<-%p",
+                      __func__, iap, nhp.u.prefix4.s_addr, v, iap);
+
+          ++iap;
+          ++v;
+          ++*nh_count_ret;
+        }
+
+    }
+  else if (family == AF_INET6)
+    {
+
+      struct listnode *ln;
+
+      *nh_ary_ret = XCALLOC (MTYPE_TMP, nve_count * sizeof (struct in6_addr));
+
+      *nhp_ary_ret = XCALLOC (MTYPE_TMP,
+                              nve_count * sizeof (struct in6_addr *));
+
+      for (ln = listhead (nve_list); ln; ln = listnextnode (ln))
+        {
+
+          struct rfapi_descriptor *irfd;
+          struct in6_addr *iap = *nh_ary_ret;
+          struct in6_addr **v = *nhp_ary_ret;
+          struct prefix nhp;
+
+          irfd = listgetdata (ln);
+
+          if (rfapiRaddr2Qprefix (&irfd->vn_addr, &nhp))
+            continue;
+
+          *iap = nhp.u.prefix6;
+          *v = iap;
+
+          ++iap;
+          ++v;
+          ++*nh_count_ret;
+        }
+    }
+}
+
+static void
+import_table_to_nve_list_zebra (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *it,
+    struct list                        **nves,
+    uint8_t                    family)
+{
+  struct listnode *node;
+  struct rfapi_rfg_name *rfgn;
+
+  /*
+   * Loop over the list of NVE-Groups configured for
+   * exporting to direct-bgp.
+   *
+   * Build a list of NVEs that use this import table
+   */
+  *nves = NULL;
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn))
+    {
+
+      /*
+       * If this NVE-Group's import table matches the current one
+       */
+      if (rfgn->rfg && rfgn->rfg->nves && rfgn->rfg->rfapi_import_table == it)
+        {
+
+          nve_group_to_nve_list (rfgn->rfg, nves, family);
+        }
+    }
+}
+
+static void
+vnc_zebra_add_del_prefix (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *import_table,
+    struct route_node          *rn,
+    int                                add)    /* !0 = add, 0 = del */
+{
+  struct list *nves;
+
+  int nexthop_count = 0;
+  void *nh_ary = NULL;
+  void *nhp_ary = NULL;
+
+  zlog_debug ("%s: entry, add=%d", __func__, add);
+
+  if (zclient_vnc->sock < 0)
+    return;
+
+  if (rn->p.family != AF_INET
+      && rn->p.family != AF_INET6)
+    {
+      zlog_err ("%s: invalid route node addr family", __func__);
+      return;
+    }
+
+  if (!zclient_vnc->redist[family2afi(rn->p.family)][ZEBRA_ROUTE_VNC])
+    return;
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+  if (!listcount (bgp->rfapi_cfg->rfg_export_zebra_l))
+    {
+      zlog_debug ("%s: no zebra export nve group, skipping", __func__);
+      return;
+    }
+
+  import_table_to_nve_list_zebra (bgp, import_table, &nves, rn->p.family);
+
+  if (nves)
+    {
+      nve_list_to_nh_array (rn->p.family,
+                            nves, &nexthop_count, &nh_ary, &nhp_ary);
+
+      list_delete (nves);
+
+      if (nexthop_count)
+        vnc_zebra_route_msg (&rn->p, nexthop_count, nhp_ary, add);
+    }
+
+  if (nhp_ary)
+    XFREE (MTYPE_TMP, nhp_ary);
+  if (nh_ary)
+    XFREE (MTYPE_TMP, nh_ary);
+}
+
+void
+vnc_zebra_add_prefix (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *import_table,
+    struct route_node          *rn)
+{
+  vnc_zebra_add_del_prefix (bgp, import_table, rn, 1);
+}
+
+void
+vnc_zebra_del_prefix (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *import_table,
+    struct route_node          *rn)
+{
+  vnc_zebra_add_del_prefix (bgp, import_table, rn, 0);
+}
+
+
+
+static void
+vnc_zebra_add_del_nve (
+    struct bgp                 *bgp,
+    struct rfapi_descriptor    *rfd,
+    int                                add)  /* 0 = del, !0 = add */
+{
+  struct listnode *node;
+  struct rfapi_rfg_name *rfgn;
+  struct rfapi_nve_group_cfg *rfg = rfd->rfg;
+  afi_t afi = family2afi (rfd->vn_addr.addr_family);
+  struct prefix nhp;
+//    struct prefix             *nhpp;
+  void *pAddr;
+
+  zlog_debug ("%s: entry, add=%d", __func__, add);
+
+  if (zclient_vnc->sock < 0)
+    return;
+
+  if (!zclient_vnc->redist[afi][ZEBRA_ROUTE_VNC])
+    return;
+
+  if (afi != AFI_IP && afi != AFI_IP6)
+    {
+      zlog_err ("%s: invalid vn addr family", __func__);
+      return;
+    }
+
+  if (!bgp)
+    return;
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: bgp->rfapi_cfg is NULL, skipping", __func__);
+      return;
+    }
+
+  if (rfapiRaddr2Qprefix (&rfd->vn_addr, &nhp))
+    {
+      zlog_debug ("%s: can't convert vn address, skipping", __func__);
+      return;
+    }
+
+  pAddr = &nhp.u.prefix4;
+
+  /*
+   * Loop over the list of NVE-Groups configured for
+   * exporting to zebra and see if this new NVE's
+   * group is among them.
+   */
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn))
+    {
+
+      /*
+       * Yes, this NVE's group is configured for export to zebra
+       */
+      if (rfgn->rfg == rfg)
+        {
+
+          struct route_table *rt = NULL;
+          struct route_node *rn;
+          struct rfapi_import_table *import_table;
+          import_table = rfg->rfapi_import_table;
+
+          zlog_debug ("%s: this nve's group is in zebra export list",
+                      __func__);
+
+          rt = import_table->imported_vpn[afi];
+
+          /* 
+           * Walk the NVE-Group's VNC Import table
+           */
+          for (rn = route_top (rt); rn; rn = route_next (rn))
+            {
+
+              if (rn->info)
+                {
+
+                  zlog_debug ("%s: sending %s", __func__,
+                              (add ? "add" : "del"));
+                  vnc_zebra_route_msg (&rn->p, 1, &pAddr, add);
+                }
+            }
+        }
+    }
+}
+
+void
+vnc_zebra_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+  vnc_zebra_add_del_nve (bgp, rfd, 1);
+}
+
+void
+vnc_zebra_del_nve (struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+  vnc_zebra_add_del_nve (bgp, rfd, 0);
+}
+
+static void
+vnc_zebra_add_del_group_afi (
+    struct bgp                 *bgp,
+    struct rfapi_nve_group_cfg *rfg,
+    afi_t                      afi,
+    int                                add)
+{
+  struct route_table *rt = NULL;
+  struct route_node *rn;
+  struct rfapi_import_table *import_table;
+  uint8_t family = afi2family (afi);
+
+  struct list *nves = NULL;
+  int nexthop_count = 0;
+  void *nh_ary = NULL;
+  void *nhp_ary = NULL;
+
+  zlog_debug ("%s: entry", __func__);
+  import_table = rfg->rfapi_import_table;
+  if (!import_table)
+    {
+      zlog_debug ("%s: import table not defined, returning", __func__);
+      return;
+    }
+
+  if (afi == AFI_IP
+      || afi == AFI_IP6)
+    {
+      rt = import_table->imported_vpn[afi];
+    }
+  else
+    {
+      zlog_err ("%s: bad afi %d", __func__, afi);
+      return;
+    }
+
+  if (!family)
+    {
+      zlog_err ("%s: computed bad family: %d", __func__, family);
+      return;
+    }
+
+  if (!rfg->nves)
+    {
+      /* avoid segfault below if list doesn't exist */
+      zlog_debug ("%s: no NVEs in this group", __func__);
+      return;
+    }
+
+  nve_group_to_nve_list (rfg, &nves, family);
+  if (nves)
+    {
+      zlog_debug ("%s: have nves", __func__);
+      nve_list_to_nh_array (family, nves, &nexthop_count, &nh_ary, &nhp_ary);
+
+      zlog_debug ("%s: family: %d, nve count: %d", __func__, family,
+                  nexthop_count);
+
+      list_delete (nves);
+
+      if (nexthop_count)
+        {
+          /* 
+           * Walk the NVE-Group's VNC Import table
+           */
+          for (rn = route_top (rt); rn; rn = route_next (rn))
+            {
+              if (rn->info)
+                {
+                  vnc_zebra_route_msg (&rn->p, nexthop_count, nhp_ary, add);
+                }
+            }
+        }
+      if (nhp_ary)
+        XFREE (MTYPE_TMP, nhp_ary);
+      if (nh_ary)
+        XFREE (MTYPE_TMP, nh_ary);
+    }
+}
+
+void
+vnc_zebra_add_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg)
+{
+  vnc_zebra_add_del_group_afi (bgp, rfg, AFI_IP, 1);
+  vnc_zebra_add_del_group_afi (bgp, rfg, AFI_IP6, 1);
+}
+
+void
+vnc_zebra_del_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg)
+{
+  zlog_debug ("%s: entry", __func__);
+  vnc_zebra_add_del_group_afi (bgp, rfg, AFI_IP, 0);
+  vnc_zebra_add_del_group_afi (bgp, rfg, AFI_IP6, 0);
+}
+
+void
+vnc_zebra_reexport_group_afi (
+    struct bgp                 *bgp,
+    struct rfapi_nve_group_cfg *rfg,
+    afi_t                      afi)
+{
+  struct listnode *node;
+  struct rfapi_rfg_name *rfgn;
+
+  for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn))
+    {
+
+      if (rfgn->rfg == rfg)
+        {
+          vnc_zebra_add_del_group_afi (bgp, rfg, afi, 0);
+          vnc_zebra_add_del_group_afi (bgp, rfg, afi, 1);
+          break;
+        }
+    }
+}
+
+
+/***********************************************************************
+ *                     CONTROL INTERFACE
+ ***********************************************************************/
+
+
+/* Other routes redistribution into BGP. */
+int
+vnc_redistribute_set (struct bgp *bgp, afi_t afi, int type)
+{
+  if (!bgp->rfapi_cfg)
+    {
+      return CMD_WARNING;
+    }
+
+  /* Set flag to BGP instance. */
+  bgp->rfapi_cfg->redist[afi][type] = 1;
+
+//  bgp->redist[afi][type] = 1;
+
+  /* Return if already redistribute flag is set. */
+  if (zclient_vnc->redist[afi][type])
+    return CMD_WARNING;
+
+  vrf_bitmap_set (zclient_vnc->redist[afi][type], VRF_DEFAULT);
+
+  //zclient_vnc->redist[afi][type] = 1; 
+
+  /* Return if zebra connection is not established. */
+  if (zclient_vnc->sock < 0)
+    return CMD_WARNING;
+
+  if (BGP_DEBUG (zebra, ZEBRA))
+    zlog_debug ("Zebra send: redistribute add %s", zebra_route_string (type));
+
+  /* Send distribute add message to zebra. */
+  zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient_vnc, afi, type, 0, VRF_DEFAULT);
+
+  return CMD_SUCCESS;
+}
+
+/* Unset redistribution.  */
+int
+vnc_redistribute_unset (struct bgp *bgp, afi_t afi, int type)
+{
+  zlog_debug ("%s: type=%d entry", __func__, type);
+
+  if (!bgp->rfapi_cfg)
+    {
+      zlog_debug ("%s: return (no rfapi_cfg)", __func__);
+      return CMD_WARNING;
+    }
+
+  /* Unset flag from BGP instance. */
+  bgp->rfapi_cfg->redist[afi][type] = 0;
+
+  /* Return if zebra connection is disabled. */
+  if (!zclient_vnc->redist[afi][type])
+    return CMD_WARNING;
+  zclient_vnc->redist[afi][type] = 0;
+
+  if (bgp->rfapi_cfg->redist[AFI_IP][type] == 0
+      && bgp->rfapi_cfg->redist[AFI_IP6][type] == 0 && zclient_vnc->sock >= 0)
+    {
+      /* Send distribute delete message to zebra. */
+      if (BGP_DEBUG (zebra, ZEBRA))
+        zlog_debug ("Zebra send: redistribute delete %s",
+                    zebra_route_string (type));
+      zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient_vnc, afi, type,
+                               0, VRF_DEFAULT);
+    }
+
+  /* Withdraw redistributed routes from current BGP's routing table. */
+  vnc_redistribute_withdraw (bgp, afi, type);
+
+  zlog_debug ("%s: return", __func__);
+
+  return CMD_SUCCESS;
+}
+
+
+/*
+ * Modeled after bgp_zebra.c'bgp_zebra_init()
+ * Charriere asks, "Is it possible to carry two?"
+ */
+void
+vnc_zebra_init (struct thread_master *master)
+{
+  /* Set default values. */
+  zclient_vnc = zclient_new (master);
+  zclient_init (zclient_vnc, ZEBRA_ROUTE_VNC, 0);
+
+  zclient_vnc->redistribute_route_ipv4_add = vnc_zebra_read_ipv4;
+  zclient_vnc->redistribute_route_ipv4_del = vnc_zebra_read_ipv4;
+  zclient_vnc->redistribute_route_ipv6_add = vnc_zebra_read_ipv6;
+  zclient_vnc->redistribute_route_ipv6_del = vnc_zebra_read_ipv6;
+}
+
+void
+vnc_zebra_destroy (void)
+{
+  if (zclient_vnc == NULL)
+    return;
+  zclient_stop (zclient_vnc);
+  zclient_free (zclient_vnc);
+  zclient_vnc = NULL;
+}
diff --git a/bgpd/rfapi/vnc_zebra.h b/bgpd/rfapi/vnc_zebra.h
new file mode 100644 (file)
index 0000000..226136e
--- /dev/null
@@ -0,0 +1,67 @@
+/* 
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/*
+ * File:       vnc_zebra.h
+ */
+
+#ifndef _QUAGGA_BGP_VNC_ZEBRA_H
+#define _QUAGGA_BGP_VNC_ZEBRA_H
+
+#include "zebra.h"
+
+extern void
+vnc_zebra_add_prefix (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *import_table,
+    struct route_node          *rn);
+
+extern void
+vnc_zebra_del_prefix (
+    struct bgp                 *bgp,
+    struct rfapi_import_table  *import_table,
+    struct route_node          *rn);
+
+extern void
+vnc_zebra_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd);
+
+extern void
+vnc_zebra_del_nve (struct bgp *bgp, struct rfapi_descriptor *rfd);
+
+extern void
+vnc_zebra_add_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg);
+
+extern void
+vnc_zebra_del_group (struct bgp *bgp, struct rfapi_nve_group_cfg *rfg);
+
+extern void
+vnc_zebra_reexport_group_afi (
+    struct bgp                 *bgp,
+    struct rfapi_nve_group_cfg *rfg,
+    afi_t                      afi);
+
+extern int
+vnc_redistribute_set (struct bgp *bgp, afi_t afi, int type);
+
+extern int
+vnc_redistribute_unset (struct bgp *bgp, afi_t afi, int type);
+
+#endif /* _QUAGGA_BGP_VNC_ZEBRA_H */
diff --git a/bgpd/rfp-example/librfp/Makefile.am b/bgpd/rfp-example/librfp/Makefile.am
new file mode 100644 (file)
index 0000000..fc66a40
--- /dev/null
@@ -0,0 +1,40 @@
+#
+# This file has been modified by LabN Consulting, L.L.C.
+#
+#
+## Process this file with automake to produce Makefile.in.
+
+if ENABLE_BGP_VNC
+BGP_VNC_RFAPI_INC=-I$(top_srcdir)/bgpd/rfapi
+BGP_VNC_RFP_LIBDIR=.
+BGP_VNC_RFP_INCDIR=$(BGP_VNC_RFP_LIBDIR)
+BGP_VNC_RFP_LIB=librfp.a
+BGP_VNC_RFP_INC=-I$(BGP_VNC_RFP_INCDIR)
+
+librfp_a_SOURCES = \
+       rfp_example.c 
+
+librfp_a_INCLUDES = \
+       rfp.h \
+       rfp_internal.h 
+
+else
+BGP_VNC_RFAPI_INC=
+BGP_VNC_RFAPI_SRC=
+BGP_VNC_RFP_LIB=
+BGP_VNC_RFP_INC=
+endif
+
+AM_CPPFLAGS =  -I$(top_srcdir) -I$(top_srcdir)/lib \
+       -I$(top_builddir) -I$(top_builddir)/lib \
+       $(BGP_VNC_RFAPI_INC) $(BGP_VNC_RFP_INC)
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+
+AM_CFLAGS = $(PICFLAGS)
+AM_LDFLAGS = $(PILDFLAGS)
+
+noinst_LIBRARIES = $(BGP_VNC_RFP_LIB)
+
+noinst_HEADERS = \
+       $(librfp_a_INCLUDES)
diff --git a/bgpd/rfp-example/librfp/rfp.h b/bgpd/rfp-example/librfp/rfp.h
new file mode 100644 (file)
index 0000000..4aa3078
--- /dev/null
@@ -0,0 +1,31 @@
+/* 
+ *
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/* Sample header file */
+#ifndef _RFP_H
+#define _RFP_H
+
+#include "rfapi.h"
+extern int bgp_rfp_cfg_write (void *vty, void *bgp);
+/* TO BE REMOVED */
+void rfp_clear_vnc_nve_all (void);
+
+#endif /* _RFP_H */
diff --git a/bgpd/rfp-example/librfp/rfp_example.c b/bgpd/rfp-example/librfp/rfp_example.c
new file mode 100644 (file)
index 0000000..b533550
--- /dev/null
@@ -0,0 +1,286 @@
+/* 
+ *
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/* stub rfp */
+#include "rfp_internal.h"
+#include "rfapi.h"
+#include "command.h"
+
+struct rfp_instance_t
+{
+  struct rfapi_rfp_cfg rfapi_config;
+  struct rfapi_rfp_cb_methods rfapi_callbacks;
+  struct thread_master *master;
+  uint32_t config_var;
+};
+
+struct rfp_instance_t global_rfi;       /* dynamically allocate in full implementation */
+
+/***********************************************************************
+ * Sample VTY / internal function
+ **********************************************************************/
+#define RFP_SHOW_STR "RFP information\n"
+DEFUN (rfp_example_config_value,
+       rfp_example_config_value_cmd,
+       "rfp example-config-value VALUE",
+       RFP_SHOW_STR "Example value to be configured\n")
+{
+  uint32_t value = 0;
+  struct rfp_instance_t *rfi = NULL;
+  rfi = rfapi_get_rfp_start_val (vty->index);   /* index=bgp for BGP_NODE */
+  assert (rfi != NULL);
+
+  VTY_GET_INTEGER ("Example value", value, argv[0]);
+  if (rfi)
+    rfi->config_var = value;
+  return CMD_SUCCESS;
+}
+
+static void
+rfp_vty_install ()
+{
+  static int installed = 0;
+  if (installed)                /* do this only once */
+    return;
+  installed = 1;
+  /* example of new cli command */
+  install_element (BGP_NODE, &rfp_example_config_value_cmd);
+}
+
+/***********************************************************************
+ * RFAPI Callbacks
+ **********************************************************************/
+
+/*------------------------------------------
+ * rfp_response_cb
+ *
+ * Callbacks of this type are used to provide asynchronous 
+ * route updates from RFAPI to the RFP client.
+ *
+ * response_cb
+ *     called to notify the rfp client that a next hop list
+ *     that has previously been provided in response to an
+ *     rfapi_query call has been updated. Deleted routes are indicated
+ *     with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME.
+ *
+ *     By default, the routes an NVE receives via this callback include
+ *     its own routes (that it has registered). However, these may be
+ *     filtered out if the global BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP
+ *     flag is set.
+ *
+ * input: 
+ *     next_hops       a list of possible next hops.
+ *                     This is a linked list allocated within the
+ *                     rfapi. The response_cb callback function is responsible
+ *                     for freeing this memory via rfapi_free_next_hop_list()
+ *                     in order to avoid memory leaks.
+ *
+ *     userdata        value (cookie) originally specified in call to
+ *                     rfapi_open()
+ *
+ *------------------------------------------*/
+static void
+rfp_response_cb (struct rfapi_next_hop_entry *next_hops, void *userdata)
+{
+  /* 
+   * Identify NVE based on userdata, which is a value passed 
+   * to RFAPI in the rfapi_open call 
+   */
+
+  /* process list of next_hops */
+
+  /* free next hops */
+  rfapi_free_next_hop_list (next_hops);
+  return;
+}
+
+/*------------------------------------------
+ * rfp_local_cb
+ *
+ * Callbacks of this type are used to provide asynchronous 
+ * route updates from RFAPI to the RFP client.
+ *
+ * local_cb
+ *     called to notify the rfp client that a local route
+ *     has been added or deleted. Deleted routes are indicated
+ *     with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME.
+ *
+ * input: 
+ *     next_hops       a list of possible next hops.
+ *                     This is a linked list allocated within the
+ *                     rfapi. The local_cb callback function is responsible
+ *                     for freeing this memory via rfapi_free_next_hop_list()
+ *                     in order to avoid memory leaks.
+ *
+ *     userdata        value (cookie) originally specified in call to
+ *                     rfapi_open()
+ *
+ *------------------------------------------*/
+static void
+rfp_local_cb (struct rfapi_next_hop_entry *next_hops, void *userdata)
+{
+  /* 
+   * Identify NVE based on userdata, which is a value passed 
+   * to RFAPI in the rfapi_open call 
+   */
+
+  /* process list of local next_hops */
+
+  /* free next hops */
+  rfapi_free_next_hop_list (next_hops);
+  return;
+}
+
+/*------------------------------------------
+ * rfp_close_cb 
+ *
+ * Callbacks used to provide asynchronous 
+ * notification that an rfapi_handle was invalidated
+ *
+ * input: 
+ *     pHandle         Firmerly valid rfapi_handle returned to
+ *                     client via rfapi_open().
+ *
+ *     reason          EIDRM   handle administratively closed (clear nve ...)
+ *                     ESTALE  handle invalidated by configuration change
+ *
+ *------------------------------------------*/
+static void
+rfp_close_cb (rfapi_handle pHandle, int reason)
+{
+  /* close / invalidate NVE with the pHandle returned by the rfapi_open call */
+  return;
+}
+
+/*------------------------------------------
+ * rfp_cfg_write_cb
+ *
+ * This callback is used to generate output for any config parameters
+ * that may supported by RFP  via RFP defined vty commands at the bgp 
+ * level.  See loglevel as an example.
+ *
+ * input: 
+ *    vty           -- quagga vty context
+ *    rfp_start_val -- value returned by rfp_start
+ *
+ * output:
+ *    to vty, rfp related configuration
+ *
+ * return value: 
+ *    lines written
+--------------------------------------------*/
+static int
+rfp_cfg_write_cb (struct vty *vty, void *rfp_start_val)
+{
+  struct rfp_instance_t *rfi = rfp_start_val;
+  int write = 0;
+  assert (rfp_start_val != NULL);
+  if (rfi->config_var != 0)
+    {
+      vty_out (vty, " rfp example-config-value %u", rfi->config_var);
+      vty_out (vty, "%s", VTY_NEWLINE);
+      write++;
+    }
+
+  return write;
+}
+
+/***********************************************************************
+ * RFAPI required functions
+ **********************************************************************/
+
+/*------------------------------------------
+ * rfp_start
+ *
+ * This function will start the RFP code
+ *
+ * input: 
+ *    master    quagga thread_master to tie into bgpd threads
+ * 
+ * output:
+ *    cfgp      Pointer to rfapi_rfp_cfg (null = use defaults), 
+ *              copied by caller, updated via rfp_set_configuration
+ *    cbmp      Pointer to rfapi_rfp_cb_methods, may be null
+ *              copied by caller, updated via rfapi_rfp_set_cb_methods
+ *
+ * return value: 
+ *    rfp_start_val rfp returned value passed on rfp_stop and rfp_cfg_write
+ * 
+--------------------------------------------*/
+void *
+rfp_start (struct thread_master *master,
+           struct rfapi_rfp_cfg **cfgp, struct rfapi_rfp_cb_methods **cbmp)
+{
+  memset (&global_rfi, 0, sizeof (struct rfp_instance_t));
+  global_rfi.master = master;   /* for BGPD threads */
+
+  /* initilize struct rfapi_rfp_cfg, see rfapi.h */
+  global_rfi.rfapi_config.download_type = RFAPI_RFP_DOWNLOAD_FULL;      /* default=partial */
+  global_rfi.rfapi_config.ftd_advertisement_interval =
+    RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL;
+  global_rfi.rfapi_config.holddown_factor = 0;  /* default: RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR */
+  global_rfi.rfapi_config.use_updated_response = 1;     /* 0=no */
+  global_rfi.rfapi_config.use_removes = 1;      /* 0=no */
+
+
+  /* initilize structrfapi_rfp_cb_methods , see rfapi.h */
+  global_rfi.rfapi_callbacks.cfg_cb = rfp_cfg_write_cb;
+  /* no group config */
+  global_rfi.rfapi_callbacks.response_cb = rfp_response_cb;
+  global_rfi.rfapi_callbacks.local_cb = rfp_local_cb;
+  global_rfi.rfapi_callbacks.close_cb = rfp_close_cb;
+
+  if (cfgp != NULL)
+    *cfgp = &global_rfi.rfapi_config;
+  if (cbmp != NULL)
+    *cbmp = &global_rfi.rfapi_callbacks;
+
+  rfp_vty_install ();
+
+  return &global_rfi;
+}
+
+/*------------------------------------------
+ * rfp_stop
+ *
+ * This function is called on shutdown to trigger RFP cleanup
+ *
+ * input: 
+ *    none
+ *
+ * output:
+ *    none
+ *
+ * return value: 
+ *    rfp_start_val
+--------------------------------------------*/
+void
+rfp_stop (void *rfp_start_val)
+{
+  assert (rfp_start_val != NULL);
+}
+
+/* TO BE REMOVED */
+void
+rfp_clear_vnc_nve_all (void)
+{
+  return;
+}
diff --git a/bgpd/rfp-example/librfp/rfp_internal.h b/bgpd/rfp-example/librfp/rfp_internal.h
new file mode 100644 (file)
index 0000000..9419253
--- /dev/null
@@ -0,0 +1,29 @@
+/* 
+ *
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/* Sample header file */
+#ifndef _RFP_INTERNAL_H
+#define _RFP_INTERNAL_H
+#include <zebra.h>
+#include "rfp.h"
+#include "rfapi.h"
+
+#endif /* _RFP_INTERNAL_H */
diff --git a/bgpd/rfp-example/rfptest/Makefile.am b/bgpd/rfp-example/rfptest/Makefile.am
new file mode 100644 (file)
index 0000000..a1001e4
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# This file has been modified by LabN Consulting, L.L.C.
+#
+#
+## Process this file with automake to produce Makefile.in.
+
+if ENABLE_BGP_VNC
+BGP_VNC_RFAPI_INC=-I$(top_srcdir)/bgpd/rfapi
+BGP_VNC_RFP_LIBDIR=../librfp
+BGP_VNC_RFP_INCDIR=$(BGP_VNC_RFP_LIBDIR)
+BGP_VNC_RFP_LIB=$(BGP_VNC_RFP_LIBDIR)/librfp.a
+BGP_VNC_RFP_INC=-I$(BGP_VNC_RFP_INCDIR)
+
+rfptest_SOURCES =  \
+       rfptest.c 
+
+rfptest_INCLUDES = \
+       rfptest.h 
+
+
+RFPTEST_BIN = rfptest
+
+else
+BGP_VNC_RFAPI_INC=
+BGP_VNC_RFAPI_SRC=
+BGP_VNC_RFP_LIB=
+BGP_VNC_RFP_INC=
+RFPTEST_BIN=
+endif
+
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib \
+       -I$(top_builddir) -I$(top_builddir)/lib \
+       $(BGP_VNC_RFAPI_INC) $(BGP_VNC_RFP_INC)
+
+DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
+INSTALL_SDATA=@INSTALL@ -m 600
+
+
+AM_CFLAGS = $(PICFLAGS)
+AM_LDFLAGS = $(PILDFLAGS)
+
+
+noinst_HEADERS = \
+       $(rfptest_INCLUDES)
+
+noinst_LIBRARIES = 
+sbin_PROGRAMS = $(RFPTEST_BIN)
+
+examplesdir = $(exampledir)
+rfptest_LDADD = $(top_builddir)/lib/libzebra.la $(BGP_VNC_RFP_LIB)
+dist_examples_DATA = 
diff --git a/bgpd/rfp-example/rfptest/rfptest.c b/bgpd/rfp-example/rfptest/rfptest.c
new file mode 100644 (file)
index 0000000..39b798e
--- /dev/null
@@ -0,0 +1,32 @@
+/* 
+ *
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+
+/* dummy test program */
+#include <stdio.h>
+#include <stdlib.h>
+#include "rfptest.h"
+int
+main ()
+{
+  printf ("Your test code goes here.\n");
+  exit (1);
+}
diff --git a/bgpd/rfp-example/rfptest/rfptest.h b/bgpd/rfp-example/rfptest/rfptest.h
new file mode 100644 (file)
index 0000000..00effb8
--- /dev/null
@@ -0,0 +1,26 @@
+/* 
+ *
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/* Sample header file */
+#ifndef _RFPTEST_H
+#define _RFPTEST_H
+
+#endif /* _RFPTEST_H */
index d2a7a9da2862affd214dbfa544b9d7984683556b..e6e1e5348687d912d4997679a663703d0464cfa9 100755 (executable)
@@ -258,6 +258,10 @@ AC_ARG_ENABLE(pimd,
   AS_HELP_STRING([--disable-pimd], [do not build pimd]))
 AC_ARG_ENABLE(bgp-announce,
   AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement]))
+AC_ARG_ENABLE(bgp-vnc,
+  AS_HELP_STRING([--enable-bgp-vnc],[turn on BGP VNC support]))
+AC_ARG_WITH(rfp-path,
+  AS_HELP_STRING([--with-rfp-path[=DIR]],[path to replaced stub RFP used with BGP VNC]))
 AC_ARG_ENABLE(snmp,
   AS_HELP_STRING([--enable-snmp=ARG], [enable SNMP support (smux or agentx)]))
 AC_ARG_WITH(libpam,
@@ -1362,8 +1366,32 @@ else
   AC_DEFINE(DISABLE_BGP_ANNOUNCE,0,Disable BGP installation to zebra)
 fi
 
+if test "${with_rfp_path}" = "yes" || test x"${with_rfp_path}" = x""; then
+  with_rfp_path="bgpd/rfp-example"
+fi
+if test "${with_rfp_path}" != "no"; then
+  VNC_RFP_PATH="${with_rfp_path}"
+  AC_SUBST(VNC_RFP_PATH)
+fi
+
+if test "${enable_bgp_vnc}" = "yes";then
+  AC_DEFINE(ENABLE_BGP_VNC,1,Enable BGP VNC support)
+  RFPTEST="${with_rfp_path}/rfptest"
+  LIBRFP="${with_rfp_path}/librfp"
+  RFPINC="${with_rfp_path}/librfp"
+else
+  RFPTEST=
+  LIBRFP=
+  RFPINC="bgpd/rfp-example/librfp"
+fi
+# set 
+AM_CONDITIONAL([ENABLE_BGP_VNC], [test x${enable_bgp_vnc} = xyes])
+
 AC_SUBST(DOC)
 AC_SUBST(ZEBRA)
+AC_SUBST(RFPTEST)
+AC_SUBST(LIBRFP)
+AC_SUBST(RFPINC)
 AC_SUBST(BGPD)
 AC_SUBST(RIPD)
 AC_SUBST(RIPNGD)
@@ -1744,9 +1772,19 @@ AC_CONFIG_FILES([Makefile lib/Makefile qpb/Makefile zebra/Makefile ripd/Makefile
          isisd/topology/Makefile
          pkgsrc/bgpd.sh pkgsrc/ospf6d.sh pkgsrc/ospfd.sh
          pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh])
+
+if test "${enable_bgp_vnc}" = "yes"; then
+   if test "${with_rfp_path}" = "bgpd/rfp-example" ; then 
+     AC_CONFIG_FILES([bgpd/rfp-example/rfptest/Makefile bgpd/rfp-example/librfp/Makefile])
+   else
+     AC_CONFIG_FILES([${with_rfp_path}/rfptest/Makefile ${with_rfp_path}/librfp/Makefile])
+   fi
+fi
+
 AC_CONFIG_FILES([solaris/Makefile])
 
 AC_CONFIG_FILES([vtysh/extract.pl],[chmod +x vtysh/extract.pl])
+
 ## Hack, but working solution to avoid rebuilding of quagga.info.
 ## It's already in CVS until texinfo 4.7 is more common.
 AC_OUTPUT
index cc4c7abc94a2705bbc0ce971054dfd28970ec10a..d5db6cf4977031f4bf38f6ecb0bc5eed5e9617b7 100644 (file)
@@ -19,13 +19,24 @@ PNGTOEPS = convert -antialias -contrast -despeckle
 PNGTOPDF = $(PNGTOEPS)
 EPSTOPDF = epstopdf
 
+VNCFIGURES_PNG = 
+VNCFIGURES_DIA = -vnc-mesh -vnc-quagga-route-reflector         \
+-vnc-commercial-route-reflector -vnc-redundant-route-reflectors        \
+-vnc-gw -vnc-gw-rr
+
+# TODO: A target that creates an empty text file for each member of
+# VNCFIGURES_TXT
+VNCFIGURES_TXT = $(VNCFIGURES:%.png=%.txt)
+
 # The figure sources
 figures_names_parts = -normal-processing -rs-processing \
-       _topologies_full _topologies_rs
+       _topologies_full _topologies_rs \
+       $(VNCFIGURES_DIA)
+
 figures_sources = $(figures_names_parts:%=fig%.dia)
-figures_png = $(figures_names_parts:%=fig%.png)
-figures_pdf = $(figures_names_parts:%=fig%.pdf)
-figures_eps = $(figures_names_parts:%=fig%.eps)
+figures_png = $(figures_names_parts:%=fig%.png) $(VNCFIGURES_PNG)
+figures_pdf = $(figures_names_parts:%=fig%.pdf) $(VNCFIGURES_PNG:%.png=%.pdf)
+figures_eps = $(figures_names_parts:%=fig%.eps) $(VNCFIGURES_PNG:%.png=%.eps)
 figures_txt = $(figures_names_parts:%=fig%.txt)
 
 # rather twisted logic because we have to build PDFs of the EPS figures for
@@ -47,6 +58,7 @@ quagga.pdf: $(info_TEXINFOS) $(figures_pdf) $(quagga_TEXINFOS)
        $(TEXI2PDF) -o "$@" $< || true
 
 quagga_TEXINFOS = appendix.texi basic.texi bgpd.texi isisd.texi filter.texi \
+       vnc.texi \
        install.texi ipv6.texi kernel.texi main.texi ospf6d.texi ospfd.texi \
        overview.texi protocol.texi ripd.texi ripngd.texi routemap.texi \
        snmp.texi vtysh.texi routeserver.texi defines.texi $(figures_png) \
@@ -120,3 +132,11 @@ EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \
 
 draft-zebra-00.txt: draft-zebra-00.ms
        groff -T ascii -ms $< > $@
+
+# Ensure that all of the figures are copied into the html directory
+html-local: $(HTMLS)
+       if test -d $(HTMLS) ; then \
+               cp -p $(figures_png) $(HTMLS) ; \
+       else \
+               echo "$(HTMLS) is not a directory. Make it so, the rerun make."; \
+       fi
index 3ef7c8f72f8e47ddc34d3b0fbca9c903ab90c6ef..54bed102f3f75ee3f363f9ff6ba09bc01ffd63ac 100644 (file)
@@ -581,6 +581,10 @@ Redistribute RIP route to BGP process.
 Redistribute OSPF route to BGP process.
 @end deffn
 
+@deffn {BGP} {redistribute vpn} {}
+Redistribute VNC routes to BGP process.
+@end deffn
+
 @deffn {BGP} {update-delay @var{max-delay}} {}
 @deffnx {BGP} {update-delay @var{max-delay} @var{establish-wait}} {}
 This feature is used to enable read-only mode on BGP process restart or when
diff --git a/doc/fig-vnc-commercial-route-reflector.dia b/doc/fig-vnc-commercial-route-reflector.dia
new file mode 100644 (file)
index 0000000..0da5bd1
--- /dev/null
@@ -0,0 +1,794 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true" active="true">
+    <dia:object type="Standard - Box" version="0" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,39.145"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,39.095;64.0901,42.445"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,39.145"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,34.2475"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,34.1975;64.0901,37.5475"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,34.2475"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,29.35"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,29.3;64.0901,32.65"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,29.35"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,39.295"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,39.245;8.9726,42.595"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,39.295"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O4">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,34.3975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,34.3475;8.9726,37.6975"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,34.3975"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O5">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,29.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,29.45;8.9726,32.8"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,29.5"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O6">
+      <dia:attribute name="obj_pos">
+        <dia:point val="22.5347,32.178"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="22.4642,23.359;30.091,32.2485"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="22.5347,32.178"/>
+        <dia:point val="30.0205,23.4295"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O7">
+      <dia:attribute name="obj_pos">
+        <dia:point val="43.1205,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="36.8099,23.3599;43.1901,32.5401"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="43.1205,32.4705"/>
+        <dia:point val="36.8795,23.4295"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O8">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.5501,5.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.5501,4.905;16.5501,5.6525"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="16.5501,5.5"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O9">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,31.125"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3.29385,30.5113;7.76004,32.1149"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 4
+VN 172.16.4.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,31.125"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O5" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O10">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,36.0225"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.88385,35.4088;8.17004,37.0124"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 5
+VN 172.16.130.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,36.0225"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O4" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O11">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,40.92"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.8826,40.3063;8.17129,41.9099"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 6
+VN 172.16.132.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,40.92"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O3" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O12">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,30.975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.4101,30.3613;62.8788,31.9649"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 7
+VN 172.16.6.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,30.975"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O2" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O13">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,35.8725"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.4101,35.2588;62.8788,36.8624"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 8
+VN 172.16.8.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,35.8725"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O1" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O14">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,40.77"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.0026,40.1563;63.2863,41.7599"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 9
+VN 172.16.134.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,40.77"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O0" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O15">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,31.125"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.86406,31.0665;15.979,32.529"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,31.125"/>
+        <dia:point val="15.9205,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O5" connection="4"/>
+        <dia:connection handle="1" to="O24" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O16">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,36.0225"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.87151,35.8489;14.5511,36.0736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,36.0225"/>
+        <dia:point val="14.5,35.9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O4" connection="4"/>
+        <dia:connection handle="1" to="O24" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O17">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,40.92"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.86276,39.2697;15.9803,40.9798"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,40.92"/>
+        <dia:point val="15.9205,39.3295"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O3" connection="4"/>
+        <dia:connection handle="1" to="O24" connection="5"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O18">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,30.975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="49.9204,30.9159;57.2892,32.5296"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,30.975"/>
+        <dia:point val="49.9795,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O2" connection="3"/>
+        <dia:connection handle="1" to="O22" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O19">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,35.8725"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="51.3498,35.8223;57.2803,35.9502"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,35.8725"/>
+        <dia:point val="51.4,35.9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O1" connection="3"/>
+        <dia:connection handle="1" to="O22" connection="4"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O20">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,40.77"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="50.9399,37.8657;57.2963,40.8362"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,40.77"/>
+        <dia:point val="51.0061,37.9319"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O0" connection="3"/>
+        <dia:connection handle="1" to="O22" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O21">
+      <dia:attribute name="obj_pos">
+        <dia:point val="34.8,20.45"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="34.8,19.855;34.8,20.6025"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="34.8,20.45"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O22">
+        <dia:attribute name="obj_pos">
+          <dia:point val="41.7,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="41.65,31;51.45,40.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="41.7,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O23">
+        <dia:attribute name="obj_pos">
+          <dia:point val="46.55,35.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="44.2012,35.305;48.8987,36.8525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 3
+192.168.1.102#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="46.55,35.9"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O22" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O24">
+        <dia:attribute name="obj_pos">
+          <dia:point val="14.5,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="14.45,31;24.25,40.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="14.5,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O25">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.35,35.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.0013,35.305;21.6988,36.8525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 2
+192.168.1.101#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19.35,35.9"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O24" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O26">
+        <dia:attribute name="obj_pos">
+          <dia:point val="28.6,15.15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="28.55,15.1;38.35,24.9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="28.6,15.15"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O27">
+        <dia:attribute name="obj_pos">
+          <dia:point val="33.45,19.7"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="30.9863,19.105;35.9138,21.4525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#Commercial Router
+Route Reflector
+192.168.1.104#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="33.45,19.7"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>
diff --git a/doc/fig-vnc-commercial-route-reflector.png b/doc/fig-vnc-commercial-route-reflector.png
new file mode 100644 (file)
index 0000000..ca8a248
Binary files /dev/null and b/doc/fig-vnc-commercial-route-reflector.png differ
diff --git a/doc/fig-vnc-commercial-route-reflector.txt b/doc/fig-vnc-commercial-route-reflector.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/fig-vnc-gw-rr.dia b/doc/fig-vnc-gw-rr.dia
new file mode 100644 (file)
index 0000000..dab27f7
--- /dev/null
@@ -0,0 +1,1155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true" active="true">
+    <dia:object type="Standard - Text" version="1" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.5501,5.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.5501,4.905;16.5501,5.6525"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="16.5501,5.5"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="29.5,14.85"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="29.45,14.8;47.05,14.9"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="29.5,14.85"/>
+        <dia:point val="47,14.85"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O31" connection="4"/>
+        <dia:connection handle="1" to="O37" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="29.5,28.85"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="29.45,28.8;47.05,28.9"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="29.5,28.85"/>
+        <dia:point val="47,28.85"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O33" connection="4"/>
+        <dia:connection handle="1" to="O35" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24.65,24"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="24.6,19.65;24.7,24.05"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="24.65,24"/>
+        <dia:point val="24.65,19.7"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O33" connection="1"/>
+        <dia:connection handle="1" to="O31" connection="6"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O4">
+      <dia:attribute name="obj_pos">
+        <dia:point val="51.85,24"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="51.8,19.65;51.9,24.05"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="51.85,24"/>
+        <dia:point val="51.85,19.7"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O35" connection="1"/>
+        <dia:connection handle="1" to="O37" connection="6"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O5">
+      <dia:attribute name="obj_pos">
+        <dia:point val="47.4933,26.6076"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="28.9394,17.0251;47.5606,26.6749"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="47.4933,26.6076"/>
+        <dia:point val="29.0067,17.0924"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O6">
+      <dia:attribute name="obj_pos">
+        <dia:point val="29.0067,26.6076"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="28.9394,17.0251;47.5606,26.6749"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="29.0067,26.6076"/>
+        <dia:point val="47.4933,17.0924"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O7">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.8594,28.6941"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.8084,28.6431;19.8026,28.803"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16.8594,28.6941"/>
+        <dia:point val="19.7516,28.752"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O11" connection="8"/>
+        <dia:connection handle="1" to="O33" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O8">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.81,32.625"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.7433,30.909;20.302,32.6917"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16.81,32.625"/>
+        <dia:point val="20.2352,30.9757"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O13" connection="4"/>
+        <dia:connection handle="1" to="O33" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O9">
+      <dia:attribute name="obj_pos">
+        <dia:point val="59.9506,31.4965"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="56.4448,30.3087;60.0137,31.5595"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="59.9506,31.4965"/>
+        <dia:point val="56.5079,30.3717"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O17" connection="8"/>
+        <dia:connection handle="1" to="O35" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O10">
+      <dia:attribute name="obj_pos">
+        <dia:point val="59.9506,28.6923"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="56.6981,28.6413;60.0016,28.8056"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="59.9506,28.6923"/>
+        <dia:point val="56.7491,28.7546"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O15" connection="8"/>
+        <dia:connection handle="1" to="O35" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O11">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,27"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.95,26.95;16.86,30.3"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="10,27"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O12">
+      <dia:attribute name="obj_pos">
+        <dia:point val="13.405,28.625"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="11.1862,28.03;15.6237,29.5775"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 1
+VN 172.16.1.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="13.405,28.625"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O11" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O13">
+        <dia:attribute name="obj_pos">
+          <dia:point val="10,31"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9.95,30.95;16.86,34.3"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="10,31"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.8100000000000023"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3.2500000000000018"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O14">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13.405,32.625"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.1862,32.03;15.6237,33.5775"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVE 2
+VN 172.16.2.1#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="13.405,32.625"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O13" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O15">
+        <dia:attribute name="obj_pos">
+          <dia:point val="60,27"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="59.95,26.95;66.86,30.3"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="60,27"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.8100000000000023"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3.2500000000000018"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O16">
+        <dia:attribute name="obj_pos">
+          <dia:point val="63.405,28.625"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="61.1863,28.03;65.6238,29.5775"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVE 3
+VN 172.16.3.1#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="63.405,28.625"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O15" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O17">
+        <dia:attribute name="obj_pos">
+          <dia:point val="60,31"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="59.95,30.95;66.86,34.3"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="60,31"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.8100000000000023"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3.2500000000000018"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O18">
+        <dia:attribute name="obj_pos">
+          <dia:point val="63.405,32.625"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="61.1863,32.03;65.6238,33.5775"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVE 4
+VN 172.16.4.1#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="63.405,32.625"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O17" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Standard - Line" version="0" id="O19">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.1073,12.7602"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.0469,12.6997;19.9513,13.7462"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16.1073,12.7602"/>
+        <dia:point val="19.8909,13.6858"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O23" connection="8"/>
+        <dia:connection handle="1" to="O31" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O20">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.1475,18.0292"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.0831,16.5016;20.1253,18.0935"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16.1475,18.0292"/>
+        <dia:point val="20.0609,16.5659"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O25" connection="8"/>
+        <dia:connection handle="1" to="O31" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O21">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.0374,12.8937"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="56.5559,12.8335;60.0976,13.7714"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="60.0374,12.8937"/>
+        <dia:point val="56.6162,13.7112"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O27" connection="8"/>
+        <dia:connection handle="1" to="O37" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O22">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.3187,18.1413"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="56.3502,16.5594;60.3834,18.206"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="60.3187,18.1413"/>
+        <dia:point val="56.4149,16.6241"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O29" connection="8"/>
+        <dia:connection handle="1" to="O37" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O23">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9.85,8.85"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9.8,8.8;16.2,15.2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="9.85,8.85"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O24">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.27,11.405;14.73,12.9525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#CE 1
+172.16.1.2#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="13,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O23" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O25">
+        <dia:attribute name="obj_pos">
+          <dia:point val="10,16"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9.95,15.95;16.35,22.35"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="10,16"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O26">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13,19"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.27,18.405;14.73,19.9525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#CE 2
+172.16.2.2#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="13,19"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O27">
+        <dia:attribute name="obj_pos">
+          <dia:point val="60,9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="59.95,8.95;66.35,15.35"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="60,9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O28">
+        <dia:attribute name="obj_pos">
+          <dia:point val="63,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="61.27,11.405;64.73,12.9525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#CE 3
+172.16.3.2#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="63,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O29">
+        <dia:attribute name="obj_pos">
+          <dia:point val="60.15,16.15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="60.1,16.1;66.5,22.5"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="60.15,16.15"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O30">
+        <dia:attribute name="obj_pos">
+          <dia:point val="63.3,19.3"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="61.57,18.705;65.03,20.2525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#CE 4
+172.16.4.2#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="63.3,19.3"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O29" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O31">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.8,10"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="19.75,9.95;29.55,19.75"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="19.8,10"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O32">
+        <dia:attribute name="obj_pos">
+          <dia:point val="24.65,14.85"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="22.15,14.255;27.15,15.8025"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#VNC Gateway 1
+192.168.1.101#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="24.65,14.85"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O31" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O33">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.8,24"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="19.75,23.95;29.55,33.75"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="19.8,24"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O34">
+        <dia:attribute name="obj_pos">
+          <dia:point val="24.65,28.85"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="21.4637,28.255;27.8362,29.8025"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 1 (NVA)
+192.168.1.103#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="24.65,28.85"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O33" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O35">
+        <dia:attribute name="obj_pos">
+          <dia:point val="47,24"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="46.95,23.95;56.75,33.75"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="47,24"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O36">
+        <dia:attribute name="obj_pos">
+          <dia:point val="51.85,28.85"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="48.6638,28.255;55.0363,29.8025"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 2 (NVA)
+192.168.1.104#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="51.85,28.85"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O35" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O37">
+        <dia:attribute name="obj_pos">
+          <dia:point val="47,10"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="46.95,9.95;56.75,19.75"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="47,10"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O38">
+        <dia:attribute name="obj_pos">
+          <dia:point val="51.85,14.85"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="49.35,14.255;54.35,15.8025"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#VNC Gateway 2
+192.168.1.102#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="51.85,14.85"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O37" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O39">
+        <dia:attribute name="obj_pos">
+          <dia:point val="35,36"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="34.95,35.95;41.35,42.35"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="35,36"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O40">
+        <dia:attribute name="obj_pos">
+          <dia:point val="38.15,39.15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="35.8087,38.555;40.4912,40.1025"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#RR
+192.168.1.105#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="38.15,39.15"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O39" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Standard - Line" version="0" id="O41">
+      <dia:attribute name="obj_pos">
+        <dia:point val="40.7075,37.2272"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="40.6374,31.7234;48.005,37.2972"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="40.7075,37.2272"/>
+        <dia:point val="47.935,31.7934"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O39" connection="8"/>
+        <dia:connection handle="1" to="O35" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O42">
+      <dia:attribute name="obj_pos">
+        <dia:point val="35.6122,37.2137"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="28.4724,31.7497;35.6822,37.2838"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="35.6122,37.2137"/>
+        <dia:point val="28.5425,31.8198"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O39" connection="8"/>
+        <dia:connection handle="1" to="O33" connection="8"/>
+      </dia:connections>
+    </dia:object>
+  </dia:layer>
+</dia:diagram>
diff --git a/doc/fig-vnc-gw-rr.png b/doc/fig-vnc-gw-rr.png
new file mode 100644 (file)
index 0000000..7ae0630
Binary files /dev/null and b/doc/fig-vnc-gw-rr.png differ
diff --git a/doc/fig-vnc-gw-rr.txt b/doc/fig-vnc-gw-rr.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/fig-vnc-gw.dia b/doc/fig-vnc-gw.dia
new file mode 100644 (file)
index 0000000..8270e20
--- /dev/null
@@ -0,0 +1,1058 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true" active="true">
+    <dia:object type="Standard - Text" version="1" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.5501,5.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.5501,4.905;16.5501,5.6525"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="16.5501,5.5"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="29.5,14.85"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="29.45,14.8;47.05,14.9"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="29.5,14.85"/>
+        <dia:point val="47,14.85"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O31" connection="4"/>
+        <dia:connection handle="1" to="O37" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="29.5,28.85"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="29.45,28.8;47.05,28.9"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="29.5,28.85"/>
+        <dia:point val="47,28.85"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O33" connection="4"/>
+        <dia:connection handle="1" to="O35" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24.65,24"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="24.6,19.65;24.7,24.05"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="24.65,24"/>
+        <dia:point val="24.65,19.7"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O33" connection="1"/>
+        <dia:connection handle="1" to="O31" connection="6"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O4">
+      <dia:attribute name="obj_pos">
+        <dia:point val="51.85,24"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="51.8,19.65;51.9,24.05"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="51.85,24"/>
+        <dia:point val="51.85,19.7"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O35" connection="1"/>
+        <dia:connection handle="1" to="O37" connection="6"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O5">
+      <dia:attribute name="obj_pos">
+        <dia:point val="47.4933,26.6076"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="28.9394,17.0251;47.5606,26.6749"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="47.4933,26.6076"/>
+        <dia:point val="29.0067,17.0924"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O6">
+      <dia:attribute name="obj_pos">
+        <dia:point val="29.0067,26.6076"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="28.9394,17.0251;47.5606,26.6749"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="29.0067,26.6076"/>
+        <dia:point val="47.4933,17.0924"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O7">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.8594,28.6941"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.8084,28.6431;19.8026,28.803"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16.8594,28.6941"/>
+        <dia:point val="19.7516,28.752"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O11" connection="8"/>
+        <dia:connection handle="1" to="O33" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O8">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.81,32.625"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.7433,30.909;20.302,32.6917"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16.81,32.625"/>
+        <dia:point val="20.2352,30.9757"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O13" connection="4"/>
+        <dia:connection handle="1" to="O33" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O9">
+      <dia:attribute name="obj_pos">
+        <dia:point val="59.9506,31.4965"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="56.4448,30.3087;60.0137,31.5595"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="59.9506,31.4965"/>
+        <dia:point val="56.5079,30.3717"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O17" connection="8"/>
+        <dia:connection handle="1" to="O35" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O10">
+      <dia:attribute name="obj_pos">
+        <dia:point val="59.9506,28.6923"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="56.6981,28.6413;60.0016,28.8056"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="59.9506,28.6923"/>
+        <dia:point val="56.7491,28.7546"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O15" connection="8"/>
+        <dia:connection handle="1" to="O35" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O11">
+      <dia:attribute name="obj_pos">
+        <dia:point val="10,27"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.95,26.95;16.86,30.3"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="10,27"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O12">
+      <dia:attribute name="obj_pos">
+        <dia:point val="13.405,28.625"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="11.1862,28.03;15.6237,29.5775"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 1
+VN 172.16.1.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="13.405,28.625"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O11" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O13">
+        <dia:attribute name="obj_pos">
+          <dia:point val="10,31"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9.95,30.95;16.86,34.3"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="10,31"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.8100000000000023"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3.2500000000000018"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O14">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13.405,32.625"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.1862,32.03;15.6237,33.5775"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVE 2
+VN 172.16.2.1#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="13.405,32.625"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O13" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O15">
+        <dia:attribute name="obj_pos">
+          <dia:point val="60,27"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="59.95,26.95;66.86,30.3"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="60,27"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.8100000000000023"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3.2500000000000018"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O16">
+        <dia:attribute name="obj_pos">
+          <dia:point val="63.405,28.625"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="61.1863,28.03;65.6238,29.5775"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVE 3
+VN 172.16.3.1#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="63.405,28.625"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O15" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Box" version="0" id="O17">
+        <dia:attribute name="obj_pos">
+          <dia:point val="60,31"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="59.95,30.95;66.86,34.3"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="60,31"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.8100000000000023"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="3.2500000000000018"/>
+        </dia:attribute>
+        <dia:attribute name="show_background">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O18">
+        <dia:attribute name="obj_pos">
+          <dia:point val="63.405,32.625"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="61.1863,32.03;65.6238,33.5775"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVE 4
+VN 172.16.4.1#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="63.405,32.625"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O17" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Standard - Line" version="0" id="O19">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.1073,12.7602"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.0469,12.6997;19.9513,13.7462"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16.1073,12.7602"/>
+        <dia:point val="19.8909,13.6858"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O23" connection="8"/>
+        <dia:connection handle="1" to="O31" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O20">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.1475,18.0292"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.0831,16.5016;20.1253,18.0935"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="16.1475,18.0292"/>
+        <dia:point val="20.0609,16.5659"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O25" connection="8"/>
+        <dia:connection handle="1" to="O31" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O21">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.0374,12.8937"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="56.5559,12.8335;60.0976,13.7714"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="60.0374,12.8937"/>
+        <dia:point val="56.6162,13.7112"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O27" connection="8"/>
+        <dia:connection handle="1" to="O37" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O22">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.3187,18.1413"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="56.3502,16.5594;60.3834,18.206"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="60.3187,18.1413"/>
+        <dia:point val="56.4149,16.6241"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O29" connection="8"/>
+        <dia:connection handle="1" to="O37" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O23">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9.85,8.85"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9.8,8.8;16.2,15.2"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="9.85,8.85"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O24">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.27,11.405;14.73,12.9525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#CE 1
+172.16.1.2#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="13,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O23" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O25">
+        <dia:attribute name="obj_pos">
+          <dia:point val="10,16"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9.95,15.95;16.35,22.35"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="10,16"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O26">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13,19"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11.27,18.405;14.73,19.9525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#CE 2
+172.16.2.2#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="13,19"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O27">
+        <dia:attribute name="obj_pos">
+          <dia:point val="60,9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="59.95,8.95;66.35,15.35"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="60,9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O28">
+        <dia:attribute name="obj_pos">
+          <dia:point val="63,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="61.27,11.405;64.73,12.9525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#CE 3
+172.16.3.2#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="63,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O29">
+        <dia:attribute name="obj_pos">
+          <dia:point val="60.15,16.15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="60.1,16.1;66.5,22.5"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="60.15,16.15"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="6.2999992370605469"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O30">
+        <dia:attribute name="obj_pos">
+          <dia:point val="63.3,19.3"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="61.57,18.705;65.03,20.2525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#CE 4
+172.16.4.2#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="63.3,19.3"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O29" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O31">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.8,10"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="19.75,9.95;29.55,19.75"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="19.8,10"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O32">
+        <dia:attribute name="obj_pos">
+          <dia:point val="24.65,14.85"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="22.15,14.2363;27.1687,15.8399"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#VNC Gateway 1
+192.168.1.101#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="24.65,14.85"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O31" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O33">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.8,24"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="19.75,23.95;29.55,33.75"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="19.8,24"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O34">
+        <dia:attribute name="obj_pos">
+          <dia:point val="24.65,28.85"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="21.4637,28.2363;27.8549,29.8399"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 1 (NVA)
+192.168.1.103#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="24.65,28.85"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O33" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O35">
+        <dia:attribute name="obj_pos">
+          <dia:point val="47,24"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="46.95,23.95;56.75,33.75"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="47,24"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O36">
+        <dia:attribute name="obj_pos">
+          <dia:point val="51.85,28.85"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="48.6638,28.2363;55.0549,29.8399"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 2 (NVA)
+192.168.1.104#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="51.85,28.85"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O35" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O37">
+        <dia:attribute name="obj_pos">
+          <dia:point val="47,10"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="46.95,9.95;56.75,19.75"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="47,10"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O38">
+        <dia:attribute name="obj_pos">
+          <dia:point val="51.85,14.85"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="49.35,14.2363;54.3687,15.8399"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#VNC Gateway 2
+192.168.1.102#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="51.85,14.85"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O37" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>
diff --git a/doc/fig-vnc-gw.png b/doc/fig-vnc-gw.png
new file mode 100644 (file)
index 0000000..df8f23f
Binary files /dev/null and b/doc/fig-vnc-gw.png differ
diff --git a/doc/fig-vnc-gw.txt b/doc/fig-vnc-gw.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/fig-vnc-mesh.dia b/doc/fig-vnc-mesh.dia
new file mode 100644 (file)
index 0000000..a8f702f
--- /dev/null
@@ -0,0 +1,1071 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true" active="true">
+    <dia:object type="Standard - Line" version="0" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24.2,35.9"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="24.15,35.85;41.75,35.95"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="24.2,35.9"/>
+        <dia:point val="41.7,35.9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="22.5347,32.178"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="22.4642,23.359;30.091,32.2485"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="22.5347,32.178"/>
+        <dia:point val="30.0205,23.4295"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="43.1205,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="36.8099,23.3599;43.1901,32.5401"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="43.1205,32.4705"/>
+        <dia:point val="36.8795,23.4295"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.5501,5.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.5501,4.905;16.5501,5.6525"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="16.5501,5.5"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O4">
+      <dia:attribute name="obj_pos">
+        <dia:point val="23.2957,9.72508"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="23.225,9.65437;30.0912,16.6412"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="23.2957,9.72508"/>
+        <dia:point val="30.0205,16.5705"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O33" connection="8"/>
+        <dia:connection handle="1" to="O15" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O5">
+      <dia:attribute name="obj_pos">
+        <dia:point val="33.0601,9.675"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="33.0098,9.62471;33.1421,15.1003"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="33.0601,9.675"/>
+        <dia:point val="33.0918,15.05"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O35" connection="6"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O6">
+      <dia:attribute name="obj_pos">
+        <dia:point val="44.4726,9.675"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="36.8089,9.60437;44.5432,16.6411"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="44.4726,9.675"/>
+        <dia:point val="36.8795,16.5705"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O37" connection="6"/>
+        <dia:connection handle="1" to="O15" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O7">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,31.125"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.86406,31.0665;15.979,32.529"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,31.125"/>
+        <dia:point val="15.9205,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O27" connection="4"/>
+        <dia:connection handle="1" to="O17" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O8">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,36.0225"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.87151,35.8489;14.5511,36.0736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,36.0225"/>
+        <dia:point val="14.5,35.9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O29" connection="4"/>
+        <dia:connection handle="1" to="O17" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O9">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,40.92"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.86276,39.2697;15.9803,40.9798"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,40.92"/>
+        <dia:point val="15.9205,39.3295"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O31" connection="4"/>
+        <dia:connection handle="1" to="O17" connection="5"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O10">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,30.975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="49.9204,30.9159;57.2892,32.5296"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,30.975"/>
+        <dia:point val="49.9795,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O21" connection="3"/>
+        <dia:connection handle="1" to="O19" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O11">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,35.8725"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="51.3498,35.8223;57.2803,35.9502"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,35.8725"/>
+        <dia:point val="51.4,35.9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O23" connection="3"/>
+        <dia:connection handle="1" to="O19" connection="4"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O12">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,40.77"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="50.9399,37.8657;57.2963,40.8362"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,40.77"/>
+        <dia:point val="51.0061,37.9319"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O25" connection="3"/>
+        <dia:connection handle="1" to="O19" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O13">
+      <dia:attribute name="obj_pos">
+        <dia:point val="62.55,31.6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="62.55,31.005;62.55,31.7525"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="62.55,31.6"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O14">
+      <dia:attribute name="obj_pos">
+        <dia:point val="59.65,31.65"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="59.65,31.055;59.65,31.8025"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="59.65,31.65"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O15">
+        <dia:attribute name="obj_pos">
+          <dia:point val="28.6,15.15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="28.55,15.1;38.35,24.9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="28.6,15.15"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O16">
+        <dia:attribute name="obj_pos">
+          <dia:point val="33.45,20"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="31.1025,19.405;35.7975,20.9525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 1
+192.168.1.100#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="33.45,20"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O15" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O17">
+        <dia:attribute name="obj_pos">
+          <dia:point val="14.5,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="14.45,31;24.25,40.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="14.5,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O18">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.35,35.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.0013,35.305;21.6988,36.8525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 2
+192.168.1.101#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19.35,35.9"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O17" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O19">
+        <dia:attribute name="obj_pos">
+          <dia:point val="41.7,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="41.65,31;51.45,40.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="41.7,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O20">
+        <dia:attribute name="obj_pos">
+          <dia:point val="46.55,35.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="44.2012,35.305;48.8987,36.8525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 3
+192.168.1.102#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="46.55,35.9"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O19" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:object type="Standard - Box" version="0" id="O21">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,29.35"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,29.3;64.0901,32.65"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,29.35"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O22">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,30.975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.4101,30.3613;62.8788,31.9649"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 7
+VN 172.16.6.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,30.975"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O21" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O23">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,34.2475"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,34.1975;64.0901,37.5475"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,34.2475"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O24">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,35.8725"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.4101,35.2588;62.8788,36.8624"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 8
+VN 172.16.8.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,35.8725"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O23" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O25">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,39.145"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,39.095;64.0901,42.445"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,39.145"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O26">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,40.77"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.0026,40.1563;63.2863,41.7599"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 9
+VN 172.16.134.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,40.77"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O25" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O27">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,29.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,29.45;8.9726,32.8"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,29.5"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O28">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,31.125"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3.29385,30.5113;7.76004,32.1149"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 4
+VN 172.16.4.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,31.125"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O27" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O29">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,34.3975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,34.3475;8.9726,37.6975"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,34.3975"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O30">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,36.0225"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.88385,35.4088;8.17004,37.0124"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 5
+VN 172.16.130.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,36.0225"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O29" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O31">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,39.295"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,39.245;8.9726,42.595"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,39.295"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O32">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,40.92"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.8826,40.3063;8.17129,41.9099"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 6
+VN 172.16.132.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,40.92"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O31" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O33">
+      <dia:attribute name="obj_pos">
+        <dia:point val="18.2451,6.425"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="18.1951,6.375;25.1051,9.725"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="18.2451,6.425"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O34">
+      <dia:attribute name="obj_pos">
+        <dia:point val="21.6501,8.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="19.4251,7.43631;23.8938,9.03988"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 1
+VN 172.16.0.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="21.6501,8.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O33" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O35">
+      <dia:attribute name="obj_pos">
+        <dia:point val="29.6551,6.425"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="29.6051,6.375;36.5151,9.725"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="29.6551,6.425"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O36">
+      <dia:attribute name="obj_pos">
+        <dia:point val="33.0601,8.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="30.8338,7.43631;35.305,9.03988"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 2
+VN 172.16.2.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="33.0601,8.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O35" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O37">
+      <dia:attribute name="obj_pos">
+        <dia:point val="41.0676,6.425"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="41.0176,6.375;47.9276,9.725"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="41.0676,6.425"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O38">
+      <dia:attribute name="obj_pos">
+        <dia:point val="44.4726,8.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="41.8376,7.43631;47.1263,9.03988"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 3
+VN 172.16.128.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="44.4726,8.05"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O37" connection="8"/>
+      </dia:connections>
+    </dia:object>
+  </dia:layer>
+</dia:diagram>
diff --git a/doc/fig-vnc-mesh.png b/doc/fig-vnc-mesh.png
new file mode 100644 (file)
index 0000000..fa0762d
Binary files /dev/null and b/doc/fig-vnc-mesh.png differ
diff --git a/doc/fig-vnc-mesh.txt b/doc/fig-vnc-mesh.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/fig-vnc-quagga-route-reflector.dia b/doc/fig-vnc-quagga-route-reflector.dia
new file mode 100644 (file)
index 0000000..634f0b1
--- /dev/null
@@ -0,0 +1,763 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true" active="true">
+    <dia:object type="Standard - Box" version="0" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,39.145"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,39.095;64.0901,42.445"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,39.145"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,34.2475"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,34.1975;64.0901,37.5475"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,34.2475"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,29.35"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,29.3;64.0901,32.65"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,29.35"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,39.295"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,39.245;8.9726,42.595"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,39.295"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O4">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,34.3975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,34.3475;8.9726,37.6975"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,34.3975"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O5">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,29.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,29.45;8.9726,32.8"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,29.5"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O6">
+      <dia:attribute name="obj_pos">
+        <dia:point val="22.5347,32.178"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="22.4642,23.359;30.091,32.2485"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="22.5347,32.178"/>
+        <dia:point val="30.0205,23.4295"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O7">
+      <dia:attribute name="obj_pos">
+        <dia:point val="43.1205,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="36.8099,23.3599;43.1901,32.5401"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="43.1205,32.4705"/>
+        <dia:point val="36.8795,23.4295"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O8">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.5501,5.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.5501,4.905;16.5501,5.6525"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="16.5501,5.5"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O9">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,31.125"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3.29385,30.5113;7.76004,32.1149"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 4
+VN 172.16.4.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,31.125"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O5" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O10">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,36.0225"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.88385,35.4088;8.17004,37.0124"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 5
+VN 172.16.130.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,36.0225"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O4" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O11">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,40.92"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.8826,40.3063;8.17129,41.9099"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 6
+VN 172.16.132.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,40.92"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O3" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O12">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,30.975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.4101,30.3613;62.8788,31.9649"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 7
+VN 172.16.6.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,30.975"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O2" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O13">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,35.8725"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.4101,35.2588;62.8788,36.8624"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 8
+VN 172.16.8.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,35.8725"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O1" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O14">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,40.77"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.0026,40.1563;63.2863,41.7599"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 9
+VN 172.16.134.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,40.77"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O0" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O15">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,31.125"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.86406,31.0665;15.979,32.529"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,31.125"/>
+        <dia:point val="15.9205,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O5" connection="4"/>
+        <dia:connection handle="1" to="O23" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O16">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,36.0225"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.87151,35.8489;14.5511,36.0736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,36.0225"/>
+        <dia:point val="14.5,35.9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O4" connection="4"/>
+        <dia:connection handle="1" to="O23" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O17">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,40.92"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.86276,39.2697;15.9803,40.9798"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,40.92"/>
+        <dia:point val="15.9205,39.3295"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O3" connection="4"/>
+        <dia:connection handle="1" to="O23" connection="5"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O18">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,30.975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="49.9204,30.9159;57.2892,32.5296"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,30.975"/>
+        <dia:point val="49.9795,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O2" connection="3"/>
+        <dia:connection handle="1" to="O25" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O19">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,35.8725"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="51.3498,35.8223;57.2803,35.9502"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,35.8725"/>
+        <dia:point val="51.4,35.9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O1" connection="3"/>
+        <dia:connection handle="1" to="O25" connection="4"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O20">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,40.77"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="50.9399,37.8657;57.2963,40.8362"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,40.77"/>
+        <dia:point val="51.0061,37.9319"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O0" connection="3"/>
+        <dia:connection handle="1" to="O25" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O21">
+        <dia:attribute name="obj_pos">
+          <dia:point val="28.6,15.15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="28.55,15.1;38.35,24.9"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="28.6,15.15"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O22">
+        <dia:attribute name="obj_pos">
+          <dia:point val="33.45,20"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="29.9225,19.405;36.9775,20.9525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#BGP Route Reflector 1
+192.168.1.100#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="33.45,20"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O21" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O23">
+        <dia:attribute name="obj_pos">
+          <dia:point val="14.5,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="14.45,31;24.25,40.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="14.5,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O24">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.35,35.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.0013,35.305;21.6988,36.8525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 2
+192.168.1.101#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19.35,35.9"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O23" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O25">
+        <dia:attribute name="obj_pos">
+          <dia:point val="41.7,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="41.65,31;51.45,40.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="41.7,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O26">
+        <dia:attribute name="obj_pos">
+          <dia:point val="46.55,35.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="44.2012,35.305;48.8987,36.8525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 3
+192.168.1.102#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="46.55,35.9"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O25" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>
diff --git a/doc/fig-vnc-quagga-route-reflector.png b/doc/fig-vnc-quagga-route-reflector.png
new file mode 100644 (file)
index 0000000..4770521
Binary files /dev/null and b/doc/fig-vnc-quagga-route-reflector.png differ
diff --git a/doc/fig-vnc-quagga-route-reflector.txt b/doc/fig-vnc-quagga-route-reflector.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/fig-vnc-redundant-route-reflectors.dia b/doc/fig-vnc-redundant-route-reflectors.dia
new file mode 100644 (file)
index 0000000..4065b8b
--- /dev/null
@@ -0,0 +1,871 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true" active="true">
+    <dia:object type="Standard - Box" version="0" id="O0">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,39.145"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,39.095;64.0901,42.445"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,39.145"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O1">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,34.2475"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,34.1975;64.0901,37.5475"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,34.2475"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O2">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,29.35"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="57.1801,29.3;64.0901,32.65"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="57.2301,29.35"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O3">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,39.295"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,39.245;8.9726,42.595"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,39.295"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O4">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,34.3975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,34.3475;8.9726,37.6975"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,34.3975"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Box" version="0" id="O5">
+      <dia:attribute name="obj_pos">
+        <dia:point val="2.1126,29.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.0626,29.45;8.9726,32.8"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="2.1126,29.5"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="6.8100000000000023"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="3.2500000000000018"/>
+      </dia:attribute>
+      <dia:attribute name="show_background">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O6">
+      <dia:attribute name="obj_pos">
+        <dia:point val="22.5347,32.178"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="22.4648,17.4572;42.6316,32.2479"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="22.5347,32.178"/>
+        <dia:point val="42.5617,17.5271"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O26" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O7">
+      <dia:attribute name="obj_pos">
+        <dia:point val="43.1205,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="22.6348,17.9948;43.1902,32.5402"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="43.1205,32.4705"/>
+        <dia:point val="22.7045,18.0645"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="1" to="O24" connection="7"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O8">
+      <dia:attribute name="obj_pos">
+        <dia:point val="16.5501,5.5"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="16.5501,4.905;16.5501,5.6525"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="16.5501,5.5"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="0"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O9">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,31.125"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="3.29385,30.5113;7.76004,32.1149"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 4
+VN 172.16.4.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,31.125"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O5" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O10">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,36.0225"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.88385,35.4088;8.17004,37.0124"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 5
+VN 172.16.130.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,36.0225"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O4" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O11">
+      <dia:attribute name="obj_pos">
+        <dia:point val="5.5176,40.92"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="2.8826,40.3063;8.17129,41.9099"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 6
+VN 172.16.132.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="5.5176,40.92"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O3" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O12">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,30.975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.4101,30.3613;62.8788,31.9649"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 7
+VN 172.16.6.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,30.975"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O2" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O13">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,35.8725"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.4101,35.2588;62.8788,36.8624"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 8
+VN 172.16.8.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,35.8725"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O1" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Text" version="1" id="O14">
+      <dia:attribute name="obj_pos">
+        <dia:point val="60.6351,40.77"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="58.0026,40.1563;63.2863,41.7599"/>
+      </dia:attribute>
+      <dia:attribute name="text">
+        <dia:composite type="text">
+          <dia:attribute name="string">
+            <dia:string>#NVE 9
+VN 172.16.134.1#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="font">
+            <dia:font family="sans" style="0" name="Helvetica"/>
+          </dia:attribute>
+          <dia:attribute name="height">
+            <dia:real val="0.80000000000000004"/>
+          </dia:attribute>
+          <dia:attribute name="pos">
+            <dia:point val="60.6351,40.77"/>
+          </dia:attribute>
+          <dia:attribute name="color">
+            <dia:color val="#000000"/>
+          </dia:attribute>
+          <dia:attribute name="alignment">
+            <dia:enum val="1"/>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="valign">
+        <dia:enum val="3"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O0" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O15">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,31.125"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.86398,31.0664;15.9041,32.5291"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,31.125"/>
+        <dia:point val="15.8455,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O5" connection="4"/>
+        <dia:connection handle="1" to="O28" connection="0"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O16">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,36.0225"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.8715,35.8489;14.4761,36.0736"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,36.0225"/>
+        <dia:point val="14.425,35.9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O4" connection="4"/>
+        <dia:connection handle="1" to="O28" connection="3"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O17">
+      <dia:attribute name="obj_pos">
+        <dia:point val="8.9226,40.92"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="8.86267,39.2696;15.9054,40.9799"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="8.9226,40.92"/>
+        <dia:point val="15.8455,39.3295"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O3" connection="4"/>
+        <dia:connection handle="1" to="O28" connection="5"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O18">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,30.975"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="49.8855,30.916;57.2891,32.5295"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,30.975"/>
+        <dia:point val="49.9445,32.4705"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O2" connection="3"/>
+        <dia:connection handle="1" to="O30" connection="2"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O19">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,35.8725"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="51.3148,35.8223;57.2803,35.9502"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,35.8725"/>
+        <dia:point val="51.365,35.9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O1" connection="3"/>
+        <dia:connection handle="1" to="O30" connection="4"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O20">
+      <dia:attribute name="obj_pos">
+        <dia:point val="57.2301,40.77"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="50.9097,37.8613;57.2963,40.8362"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="57.2301,40.77"/>
+        <dia:point val="50.9759,37.9275"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O0" connection="3"/>
+        <dia:connection handle="1" to="O30" connection="8"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O21">
+      <dia:attribute name="obj_pos">
+        <dia:point val="24.25,15.05"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="24.1999,14.9999;41.6501,15.1501"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="24.25,15.05"/>
+        <dia:point val="41.6,15.1"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O22">
+      <dia:attribute name="obj_pos">
+        <dia:point val="19.1,31"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="18.9998,19.5998;19.1502,31.0502"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="19.1,31"/>
+        <dia:point val="19.05,19.65"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Standard - Line" version="0" id="O23">
+      <dia:attribute name="obj_pos">
+        <dia:point val="46.4,19.6"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="46.35,19.55;46.45,30.95"/>
+      </dia:attribute>
+      <dia:attribute name="conn_endpoints">
+        <dia:point val="46.4,19.6"/>
+        <dia:point val="46.4,30.9"/>
+      </dia:attribute>
+      <dia:attribute name="numcp">
+        <dia:int val="1"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O24">
+        <dia:attribute name="obj_pos">
+          <dia:point val="14.425,9.785"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="14.375,9.735;24.175,19.535"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="14.425,9.785"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O25">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.275,14.635"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="15.7475,14.04;22.8025,15.5875"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#BGP Route Reflector 1
+192.168.1.100#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19.275,14.635"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O24" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O26">
+        <dia:attribute name="obj_pos">
+          <dia:point val="41.665,9.785"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="41.615,9.735;51.415,19.535"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="41.665,9.785"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O27">
+        <dia:attribute name="obj_pos">
+          <dia:point val="46.565,14.285"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="44.1013,13.69;49.0288,16.0375"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#Commercial Router
+Route Reflector
+192.168.1.104#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="46.565,14.285"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O28">
+        <dia:attribute name="obj_pos">
+          <dia:point val="14.425,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="14.375,31;24.175,40.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="14.425,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000007629394531"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O29">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19.275,35.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="16.9262,35.305;21.6238,36.8525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 2
+192.168.1.101#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19.275,35.9"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O28" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+    <dia:group>
+      <dia:object type="Standard - Ellipse" version="0" id="O30">
+        <dia:attribute name="obj_pos">
+          <dia:point val="41.665,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="41.615,31;51.415,40.8"/>
+        </dia:attribute>
+        <dia:attribute name="elem_corner">
+          <dia:point val="41.665,31.05"/>
+        </dia:attribute>
+        <dia:attribute name="elem_width">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="elem_height">
+          <dia:real val="9.7000026702880859"/>
+        </dia:attribute>
+        <dia:attribute name="aspect">
+          <dia:enum val="2"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O31">
+        <dia:attribute name="obj_pos">
+          <dia:point val="46.515,35.9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="44.1662,35.305;48.8637,36.8525"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#NVA 3
+192.168.1.102#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="46.515,35.9"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="1"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+        <dia:connections>
+          <dia:connection handle="0" to="O30" connection="8"/>
+        </dia:connections>
+      </dia:object>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>
diff --git a/doc/fig-vnc-redundant-route-reflectors.png b/doc/fig-vnc-redundant-route-reflectors.png
new file mode 100644 (file)
index 0000000..06a27b6
Binary files /dev/null and b/doc/fig-vnc-redundant-route-reflectors.png differ
diff --git a/doc/fig-vnc-redundant-route-reflectors.txt b/doc/fig-vnc-redundant-route-reflectors.txt
new file mode 100644 (file)
index 0000000..e69de29
index 96dffe00003cfe156863623ed3de38409934e010..7b9df8e78e96dd65854376acae98d52d6b43a8fa 100644 (file)
@@ -580,6 +580,10 @@ redistributed into OSPF (@pxref{OSPF redistribute}).
 @deffnx {OSPF Command} {no distance ospf} {}
 @end deffn
 
+@deffn {Command} {router zebra} {}
+@deffnx {Command} {no router zebra} {}
+@end deffn
+
 @node Showing OSPF information
 @section Showing OSPF information
 
index 6831b30cdf433faf2c9ed0c8ffc9fa07e24ecbb8..13b885b69c38c0e8b0439aa66915a8b6e57465ec 100644 (file)
@@ -1,5 +1,8 @@
 \input texinfo @c -*- texinfo -*-
 
+@c Set variables - sourced from defines.texi
+@include defines.texi
+
 @c %**start of header
 @setfilename quagga.info
 @c Set variables - sourced from defines.texi
@@ -28,6 +31,7 @@ Permission is granted to copy and distribute translations of this manual
 into another language, under the above conditions for modified versions,
 except that this permission notice may be stated in a translation
 approved by Kunihiro Ishiguro.
+
 @end quotation
 @end copying
 
@@ -66,7 +70,7 @@ Version @value{VERSION}.
 
 @ifnottex
 @node Top
-@top Quagga
+@top Quagga -- With Virtual Network Control
                     
 @uref{http://www.quagga.net,,Quagga} is an advanced routing software package
 that provides a suite of TCP/IP based routing protocols.  This is the Manual
@@ -88,6 +92,7 @@ for @value{PACKAGE_STRING}. @uref{http://www.quagga.net,,Quagga} is a fork of
 * ISIS::
 * BGP::
 * Configuring Quagga as a Route Server::
+* VNC and VNC-GW::
 * VTY shell::
 * Filtering::
 * Route Map::
@@ -113,6 +118,7 @@ for @value{PACKAGE_STRING}. @uref{http://www.quagga.net,,Quagga} is a fork of
 @include isisd.texi
 @include bgpd.texi
 @include routeserver.texi
+@include vnc.texi
 @include vtysh.texi
 @include filter.texi
 @include routemap.texi
index b3ef7ca76f66af09c28611e9d348ff491de04194..33062a7f61cd5cf90c4d3eae94a9c304f50599d7 100644 (file)
@@ -167,7 +167,7 @@ Set the BGP nexthop address.
 @end deffn
 
 @deffn {Route-map Command} {set local-preference @var{local_pref}} {}
-Set the BGP local preference.
+Set the BGP local preference to @var{local_pref}. 
 @end deffn
 
 @deffn {Route-map Command} {set weight @var{weight}} {}
diff --git a/doc/vnc.texi b/doc/vnc.texi
new file mode 100644 (file)
index 0000000..341cbfc
--- /dev/null
@@ -0,0 +1,1584 @@
+@c -*-texinfo-*-
+@c This is part of the Quagga Manual.
+@c @value{COPYRIGHT_STR}
+@c See file quagga.texi for copying conditions.
+
+@node VNC and VNC-GW
+@chapter VNC and VNC-GW
+This chapter describes how to use
+Virtual Network Control (@acronym{VNC}) services,
+including Network Virtualization Authority (@acronym{NVA}) and 
+VNC Gateway (@acronym{VNC-GW}) functions.
+Background information on NVAs, 
+Network Virtualization Edges (@acronym{NVE}s), underlay networks (@acronym{UN}s),
+and virtual networks (@acronym{VN}s) is available from the  
+@url{https://datatracker.ietf.org/wg/nvo3,IETF Network Virtualization Overlays (@acronym{NVO3}) Working Group}.
+VNC Gateways (@acronym{VNC-GW}s) support the import/export of routing
+information between VNC and customer edge routers (@acronym{CE}s)
+operating within a VN.  Both IP/Layer 3 (L3) VNs, and IP with
+Ethernet/Layer 2 (L2) VNs are supported.
+
+BGP, with IP VPNs and Tunnel Encapsulation, is used to distribute VN
+information between NVAs. BGP based IP VPN support is defined in
+@cite{RFC4364, BGP/MPLS IP Virtual Private Networks (VPNs)}, and
+@cite{RFC4659, BGP-MPLS IP Virtual Private Network (VPN) Extension for
+IPv6 VPN }.  Both the Encapsulation Subsequent Address Family Identifier
+(SAFI) and the Tunnel Encapsulation Attribute, @cite{RFC5512, The BGP
+Encapsulation Subsequent Address Family Identifier (SAFI) and the BGP
+Tunnel Encapsulation Attribute}, are supported.
+
+The protocol that is used to communicate routing and Ethernet / Layer 2
+(L2) forwarding information between NVAs and NVEs is referred to as the
+Remote Forwarder Protocol (RFP). @code{OpenFlow} is an example
+RFP.  Specific RFP implementations may choose to implement either a
+@code{hard-state} or @code{soft-state} prefix and address registration
+model.  To support a @code{soft-state} refresh model, a @var{lifetime}
+in seconds is associated with all registrations and responses.
+
+The chapter also provides sample configurations for basic example scenarios.  
+
+@menu
+* Configuring VNC Services::
+* Manual Address Control::
+* Other VNC-Related Commands::
+* Example VNC and VNC-GW Configurations::
+* Release Notes::
+@end menu
+
+@node Configuring VNC Services
+@section Configuring VNC
+
+Virtual Network Control (@acronym{VNC}) service configuration commands
+appear in the @code{router bgp} section of the BGPD configuration file
+(@pxref{BGP Configuration Examples}). The commands are broken down into
+the following areas:
+
+@menu
+* General VNC Configuration::
+* RFP Related Configuration::
+* VNC Defaults Configuration::
+* VNC NVE Group Configuration::
+* VNC L2 Group Configuration::
+* Configuring Redistribution of Routes from Other Routing Protocols::
+* Configuring Export of Routes to Other Routing Protocols::
+@end menu
+
+@code{General VNC} configuration applies to general VNC operation and is
+primarily used to control the method used to advertise tunnel
+information.  
+
+@code{Remote Forwarder Protocol (RFP)} configuration relates to the
+protocol used between NVAs and NVEs.  
+
+@code{VNC Defaults} provides default parameters for registered NVEs.
+
+@code{VNC NVE Group} provides for configuration of a specific set of 
+registered NVEs and overrides default parameters.
+
+@code{Redistribution} and @code{Export} control VNC-GW operation, i.e.,
+the  import/export of routing
+information between VNC and customer edge routers (@acronym{CE}s)
+operating within a VN.
+
+@node General VNC Configuration
+@subsection General VNC Configuration
+
+@deffn {VNC} {vnc advertise-un-method encap-safi|encap-attr} {}
+Advertise NVE underlay-network IP addresses using the encapsulation SAFI
+(@code{encap-safi}) or the UN address sub-TLV of the Tunnel Encapsulation attribute
+(@code{encap-attr}). When @code{encap-safi} is used, neighbors under 
+@code{address-family encap} and/or @code{address-family encapv6} must be
+configured.  The default is @code{encap-attr}. 
+@end deffn
+
+@node RFP Related Configuration
+@subsection RFP Related Configuration 
+
+The protocol that is used to communicate routing and Ethernet / L2
+forwarding information between NVAs and NVEs is referred to as the
+Remote Forwarder Protocol (RFP).  Currently, only a simple example RFP
+is included in Quagga.  Developers may use this example as a starting
+point to integrate Quagga with an RFP of their choosing, e.g.,
+@code{OpenFlow}.  The example code includes the following sample
+configuration: 
+
+@deffn {RFP} {rfp example-config-value @var{VALUE}} 
+This is a simple example configuration parameter included as part of the
+RFP example code.  @code{VALUE} must be in the range of 0 to 4294967295.
+@end deffn
+
+@node VNC Defaults Configuration
+@subsection VNC Defaults Configuration
+
+The VNC Defaults section allows the user to specify default values for
+configuration parameters for all registered NVEs.
+Default values are overridden by @ref{VNC NVE Group Configuration}. 
+
+@deffn {VNC} {vnc defaults} {}
+Enter VNC configuration mode for specifying VNC default behaviors.  Use
+@code{exit-vnc} to leave VNC configuration mode.  @code{vnc
+defaults} is optional.
+
+@example
+vnc defaults
+  ... various VNC defaults
+exit-vnc
+@end example
+@end deffn
+
+These are the statements that can appear between @code{vnc defaults}
+and @code{exit-vnc}.
+
+@deffn {VNC} {rt import @var{rt-list}} {}
+@deffnx {VNC} {rt export @var{rt-list}} {}
+@deffnx {VNC} {rt both @var{rt-list}} {}
+
+Specify default route target import and export lists.  @var{rt-list} is a
+space-separated list of route targets, each element of which is
+in one of the following forms:
+@itemize
+@item @var{IPv4-address}:@var{two-byte-integer}
+@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer}
+@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer}
+@end itemize
+
+If no default import RT list is specified, then the default import RT
+list is empty.
+If no default export RT list is specified, then the default export RT
+list is empty.
+
+A complete definition of these parameters is
+given below (@pxref{VNC NVE Group Configuration}).
+
+@end deffn
+
+@deffn {VNC} {rd @var{route-distinguisher}}
+
+Specify the default route distinguisher (RD) for routes advertised via BGP
+VPNs.  The route distinguisher must be in one of four forms:
+@itemize
+@item @var{IPv4-address}:@var{two-byte-integer}
+@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer}
+@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer}
+@item auto:vn:@var{two-byte-integer}
+@end itemize
+
+If RD is specified in the defaults section, the default RD
+value is @var{two-byte-autonomous-system-number=0}:@var{four-byte-integer=0}.
+
+A complete definition of this parameter is
+given below (@pxref{VNC NVE Group Configuration}).
+@end deffn
+
+@deffn {VNC} {l2rd @var{nve-id-value}}
+Set the value used to distinguish NVEs connected to the same logical
+Ethernet segment (i.e., L2VPN).
+
+A complete definition of this parameter is
+given below (@pxref{VNC NVE Group Configuration}).
+@end deffn
+
+@deffn {VNC} {response-lifetime @var{lifetime}|infinite} {}
+Specify the default lifetime to be included in RFP
+response messages sent to NVEs.
+
+A complete definition of this parameter is
+given below (@pxref{VNC NVE Group Configuration}).
+
+@end deffn
+
+@deffn {VNC} {export bgp|zebra route-map MAP-NAME}
+Specify that the named route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no route-map}
+Specify that no route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME}
+Specify that the named prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+Prefix-lists for ipv4 and ipv6 are independent of each other.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no ipv4|ipv6 prefix-list}
+Specify that no prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} {exit-vnc} {}
+Exit VNC configuration mode.
+@end deffn
+
+@c The following example @code{vnc defaults} defines a route target import-export
+@c list for the route targets 1000:1 and 1000:2; a default route
+@c distinguisher, 4444:10; and a default response lifetime of 500
+@c seconds.
+@c 
+@c @example
+@c vnc defaults
+@c     rt both 1000:1 1000:2
+@c     rd 4444:10
+@c     response-lifetime 500
+@c exit-vnc
+@c @end example
+
+@node VNC NVE Group Configuration
+@subsection VNC NVE Group Configuration
+
+A NVE Group corresponds to a specific set of NVEs.  A Client NVE is
+assigned to an NVE Group based on whether there is a match for either
+its virtual or underlay network address against the VN and/or UN address
+prefixes specified in the NVE Group definition.  When an NVE Group
+definition specifies both VN and UN address prefixes, then an NVE must
+match both prefixes in order to be assigned to the NVE Group.  In the
+event that multiple NVE Groups match based on VN and/or UN addresses,
+the NVE is assigned to the first NVE Group listed in the configuration.  
+If an NVE is not assigned to an NVE Group, its messages will be ignored.
+
+Configuration values specified for an NVE group apply to all
+member NVEs and override configuration values specified in the VNC
+Defaults section.
+
+@strong{At least one @code{nve-group} is mandatory for useful VNC
+operation.}
+
+@deffn {VNC} {vnc nve-group @var{name}} {}
+Enter VNC configuration mode for defining the NVE group @var{name}.  
+Use @code{exit} or @code{exit-vnc} to exit group configuration mode.
+
+@example
+vnc nve-group group1
+  ... configuration commands
+exit-vnc
+@end example
+@end deffn
+
+@deffn {VNC} {no vnc nve-group @var{name}} {}
+Delete the NVE group named @var{name}.
+@end deffn
+
+The following statements are valid in an NVE group definition:
+
+@deffn {VNC} {l2rd @var{nve-id-value}}
+Set the value used to distinguish NVEs connected to the same physical
+Ethernet segment (i.e., at the same location)@footnote{The nve-id is
+carried in the route
+distinguisher.  It is the second octet of the eight-octet route
+distinguisher generated for Ethernet / L2 advertisements.
+The first octet is a constant 0xFF, and the third through eighth
+octets are set to the L2 ethernet address being advertised.}
+
+The nve-id subfield may be specified as either a literal value
+in the range 1-255, or it may be specified as @code{auto:vn}, which
+means to use the least-significant octet of the originating
+NVE's VN address.
+@end deffn
+
+@deffn {VNC} {prefix vn|un A.B.C.D/M|X:X::X:X/M} {}
+@anchor{prefix}
+Specify the matching prefix for this NVE group by either virtual-network address
+(@code{vn}) or underlay-network address (@code{un}). Either or both virtual-network
+and underlay-network prefixes may be specified.  Subsequent virtual-network or
+underlay-network values within a @code{vnc nve-group} @code{exit-vnc}
+block override their respective previous values.
+
+These prefixes are used only for determining assignments of NVEs
+to NVE Groups.
+@end deffn
+
+@deffn {VNC} {rd @var{route-distinguisher}}
+Specify the route distinguisher for routes advertised via BGP
+VPNs.  The route distinguisher must be in one of these forms:
+@itemize
+@item @var{IPv4-address}:@var{two-byte-integer}
+@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer}
+@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer}
+@item auto:vn:@var{two-byte-integer}
+@end itemize
+
+Routes originated by NVEs in the NVE group will use
+the group's specified @var{route-distinguisher} when they are
+advertised via BGP. 
+If the @code{auto} form is specified, it means that a matching NVE has
+its RD set to
+@var{rd_type=IP=1}:@var{IPv4-address=VN-address}:@var{two-byte-integer},
+for IPv4 VN addresses and
+@var{rd_type=IP=1}:@var{IPv4-address=Last-four-bytes-of-VN-address}:@var{two-byte-integer},
+for IPv6 VN addresses.
+
+If the NVE group definition does not specify a @var{route-distinguisher},
+then the default @var{route-distinguisher} is used.
+If neither a group nor a default @var{route-distinguisher} is
+configured, then the advertised RD is set to
+@var{two-byte-autonomous-system-number=0}:@var{four-byte-integer=0}.
+@end deffn
+
+@deffn {VNC} {response-lifetime @var{lifetime}|infinite} {}
+Specify the response lifetime, in seconds, to be included in RFP
+response messages sent to NVEs.  If the value
+``infinite'' is given, an infinite lifetime will be used.
+
+Note that this parameter is not the same as the lifetime supplied by
+NVEs in RFP registration messages. This parameter does not affect
+the lifetime value attached to routes sent by this server via BGP.
+
+If the NVE group definition does not specify a @var{response-lifetime},
+the default @var{response-lifetime} will be used.
+If neither a group nor a default @var{response-lifetime} is configured,
+the value 3600 will be used.  The maximum response lifetime is 2147483647.
+@end deffn
+
+@deffn {VNC} {rt export @var{rt-list}} {}
+@deffnx {VNC} {rt import @var{rt-list}} {}
+@deffnx {VNC} {rt both @var{rt-list}} {}
+Specify route target import and export lists.  @var{rt-list} is a
+space-separated list of route targets, each element of which is
+in one of the following forms:
+@itemize
+@item @var{IPv4-address}:@var{two-byte-integer}
+@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer}
+@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer}
+@end itemize
+
+The first form, @code{rt export}, specifies an @var{export rt-list}.
+The @var{export rt-list} will be attached to routes originated by
+NVEs in the NVE group when they are advertised via BGP. 
+If the NVE group definition does not specify an @var{export rt-list},
+then the default @var{export rt-list} is used.
+If neither a group nor a default @var{export rt-list} is configured,
+then no RT list will be sent; in turn, these routes will probably
+not be processed
+by receiving NVAs.
+
+The second form, @code{rt import} specifies an @var{import rt-list},
+which is a filter for incoming routes.
+In order to be made available to NVEs in the group,
+incoming BGP VPN and @w{ENCAP} @w{SAFI} (when @code{vnc
+advertise-un-method encap-safi} is set) routes must have
+RT lists that have at least one route target in common with the
+group's @var{import rt-list}.
+
+If the NVE group definition does not specify an import filter,
+then the default @var{import rt-list} is used.
+If neither a group nor a default @var{import rt-list} is configured,
+there can be no RT intersections when receiving BGP routes and
+therefore no incoming BGP routes will be processed for the group.
+
+The third, @code{rt both}, is a shorthand way of specifying both
+lists simultaneously, and is equivalent to @code{rt export @var{rt-list}}
+followed by @code{rt import @var{rt-list}}.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra route-map MAP-NAME}
+Specify that the named route-map should be applied to routes
+being exported to bgp or zebra. 
+This paramter is used in conjunction with 
+@ref{Configuring Export of Routes to Other Routing Protocols}.
+This item is optional.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no route-map}
+Specify that no route-map should be applied to routes
+being exported to bgp or zebra. 
+This paramter is used in conjunction with 
+@ref{Configuring Export of Routes to Other Routing Protocols}.
+This item is optional.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME}
+Specify that the named prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+Prefix-lists for ipv4 and ipv6 are independent of each other. 
+This paramter is used in conjunction with 
+@ref{Configuring Export of Routes to Other Routing Protocols}.
+This item is optional.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no ipv4|ipv6 prefix-list}
+Specify that no prefix-list filter should be applied to
+routes being exported to bgp or zebra. 
+This paramter is used in conjunction with 
+@ref{Configuring Export of Routes to Other Routing Protocols}.
+This item is optional.
+@end deffn
+
+@c The following example shows two @code{vnc nve-group} definitions.  The first one,
+@c ``group1'', applies to the IPV4 virtual-network route prefix 172.16/16.  It
+@c sets the response lifetime to 200 seconds.  It defines a route target
+@c import-export filter for the route targets 1000:1 and 1000:2
+@c 
+@c The second @code{vnc nve-group} definition, ``group2'', applies to the IPV6
+@c underlay-network route prefix 10.0.2/24.  It defines the same response
+@c lifetime and import-export filter as ``group1''.
+@c 
+@c @example
+@c vnc nve-group group1
+@c     prefix vn 172.16/16
+@c     response-lifetime 200
+@c     rt both 1000:1 1000:2
+@c exit-vnc
+@c 
+@c vnc nve-group group2
+@c     prefix un 10.0.2/24
+@c     response-lifetime 200
+@c     rt both 1000:1 1000:2
+@c exit-vnc
+@c @end example
+
+@node VNC L2 Group Configuration
+@subsection VNC L2 Group Configuration
+
+The route targets advertised with prefixes and addresses registered by
+an NVE are determined based on the NVE's associated VNC NVE Group
+Configuration, @pxref{VNC NVE Group Configuration}.  Layer 2 (L2) Groups
+are used to override the route targets for an NVE's Ethernet
+registrations based on the Logical Network Identifier and label value.
+A Logical Network Identifier is used to uniquely identify a logical
+Ethernet segment and is conceptually similar to the Ethernet Segment
+Identifier defined in @cite{RFC7432, BGP MPLS-Based Ethernet VPN}.  Both
+the Logical Network Identifier and Label are passed to VNC via RFP
+prefix and address registration.
+
+Note that a corresponding NVE group configuration must be present, and
+that other NVE associated configuration information, notably RD, is
+not impacted by L2 Group Configuration.
+
+@deffn {VNC} {vnc l2-group @var{name}} {}
+Enter VNC configuration mode for defining the L2 group @var{name}.  
+Use @code{exit} or @code{exit-vnc} to exit group configuration mode.
+
+@example
+vnc l2-group group1
+  ... configuration commands
+exit-vnc
+@end example
+@end deffn
+
+@deffn {VNC} {no vnc l2-group @var{name}} {}
+Delete the L2 group named @var{name}.
+@end deffn
+
+The following statements are valid in a L2 group definition:
+
+@deffn {VNC} {logical-network-id @var{VALUE}}
+Define the Logical Network Identifier with a value in the range of
+0-4294967295 that identifies the logical Ethernet segment. 
+@end deffn
+
+@deffn {VNC} {labels @var{label-list}}
+@deffnx {VNC} {no labels @var{label-list}}
+Add or remove labels associated with the group.  @var{label-list} is a
+space separated list of label values in the range of 0-1048575.
+@end deffn
+
+@deffn {VNC} {rt import @var{rt-target}} {}
+@deffnx {VNC} {rt export @var{rt-target}} {}
+@deffnx {VNC} {rt both @var{rt-target}} {}
+Specify the route target import and export value associated with the
+group. A complete definition of these parameters is given above,
+@pxref{VNC NVE Group Configuration}.
+@end deffn
+
+
+@node Configuring Redistribution of Routes from Other Routing Protocols
+@subsection Configuring Redistribution of Routes from Other Routing Protocols
+
+Routes from other protocols (including BGP) can be provided to VNC (both
+for RFP and for redistribution via BGP)
+from three sources: the zebra kernel routing process;
+directly from the main (default) unicast BGP RIB; or directly
+from a designated BGP unicast exterior routing RIB instance.
+
+The protocol named in the @code{vnc redistribute} command indicates
+the route source:
+@code{bgp-direct} routes come directly from the main (default)
+unicast BGP RIB and are available for RFP and are redistributed via BGP;
+@code{bgp-direct-to-nve-groups} routes come directly from a designated
+BGP unicast routing RIB and are made available only to RFP;
+and routes from other protocols come from the zebra kernel
+routing process.
+Note that the zebra process does not need to be active if
+only @code{bgp-direct} or @code{bgp-direct-to-nve-groups} routes are used.
+
+@subsubsection @code{zebra} routes
+
+Routes originating from protocols other than BGP must be obtained
+via the zebra routing process.
+Redistribution of these routes into VNC does not support policy mechanisms
+such as prefix-lists or route-maps.
+
+@subsubsection @code{bgp-direct} routes
+
+@code{bgp-direct} redistribution supports policy via
+prefix lists and route-maps. This policy is applied to incoming
+original unicast routes before the redistribution translations
+(described below) are performed.
+
+Redistribution of @code{bgp-direct} routes is performed in one of three
+possible modes: @code{plain}, @code{nve-group}, or @code{resolve-nve}.
+The default mode is @code{plain}.
+These modes indicate the kind of translations applied to routes before
+they are added to the VNC RIB.
+
+In @code{plain} mode, the route's next hop is unchanged and the RD is set
+based on the next hop.
+For @code{bgp-direct} redistribution, the following translations are performed:
+@itemize @bullet
+@item
+The VN address is set to the original unicast route's next hop address.
+@item
+The UN address is NOT set. (VN->UN mapping will occur via
+ENCAP route or attribute, based on @code{vnc advertise-un-method}
+setting, generated by the RFP registration of the actual NVE) 
+@item
+The RD is set to as if auto:vn:0 were specified (i.e.,
+@var{rd_type=IP=1}:@var{IPv4-address=VN-address}:@var{two-byte-integer=0})
+@item
+The RT list is included in the extended community list copied from the
+original unicast route (i.e., it must be set in the original unicast route).
+@end itemize
+
+
+
+In @code{nve-group} mode, routes are registered with VNC as
+if they came from an NVE in the nve-group designated in the
+@code{vnc redistribute nve-group} command. The following
+translations are performed:
+
+@itemize @bullet
+@item
+The next hop/VN address is set to the VN prefix configured for the
+redistribute nve-group.
+@item
+The UN address is set to the UN prefix configured for the
+redistribute nve-group.
+@item
+The RD is set to the RD configured for the redistribute nve-group.
+@item
+The RT list is set to the RT list configured for the redistribute nve-group.
+If @code{bgp-direct} routes are being redistributed, 
+any extended communities present in the original unicast route
+will also be included.
+@end itemize
+
+
+In @code{resolve-nve} mode, the next hop of the original BGP route is
+typically the address of an NVE connected router (CE) connected by one or
+more NVEs.
+Each of the connected NVEs will register, via RFP, a VNC host route
+to the CE.
+This mode may be though of as a mechanism to proxy RFP registrations
+of BGP unicast routes on behalf of registering NVEs.
+
+Multiple copies of the BGP route, one per matching NVE host route, will be
+added to VNC.
+In other words, for a given BGP unicast route, each instance of a
+RFP-registered host route to the unicast route's next hop will result
+in an instance of an imported VNC route.
+Each such imported VNC route will have a prefix equal to the original
+BGP unicast route's prefix, and a next hop equal to the next hop of the
+matching RFP-registered host route.
+If there is no RFP-registered host route to the next hop of the BGP unicast
+route, no corresponding VNC route will be imported.
+
+The following translations are applied:
+
+@itemize @bullet
+@item
+The Next Hop is set to the next hop of the NVE route (i.e., the
+VN address of the NVE).
+
+@item
+The extended community list in the new route is set to the 
+union of:
+@itemize @minus
+@item
+Any extended communities in the original BGP route
+@item
+Any extended communities in the NVE route
+@item
+An added route-origin extended community with the next hop of the
+original BGP route
+is added to the new route.
+The value of the local administrator field defaults 5226 but may
+be configured by the user via the @code{roo-ec-local-admin} parameter.
+@end itemize
+
+@item
+The Tunnel Encapsulation attribute is set to the value of the Tunnel
+Encapsulation attribute of the NVE route, if any.
+
+@end itemize
+
+@subsubsection @code{bgp-direct-to-nve-groups} routes
+
+Unicast routes from the main or a designated instance of BGP
+may be redistributed to VNC as bgp-direct-to-nve-groups routes. These
+routes are NOT announced via BGP,
+but they are made available for local RFP lookup in response to
+queries from NVEs.
+
+A non-main/default BGP instance is configured using the
+@code{bgp multiple-instance} and @code{router bgp AS view NAME}
+commands as described elsewhere in this document.
+
+In order for a route in the unicast BGP RIB to be made
+available to a querying NVE, there must already be, available to
+that NVE, an (interior) VNC route matching the next hop address
+of the unicast route.
+When the unicast route is provided to the NVE, its next hop 
+is replaced by the next hop of the corresponding
+NVE. If there are multiple longest-prefix-match VNC routes,
+the unicast route will be replicated for each.
+
+There is currently no policy (prefix-list or route-map) support
+for @code{bgp-direct-to-nve-groups} routes.
+
+@subsubsection Redistribution Command Syntax
+
+@deffn {VNC} {vnc redistribute ipv4|ipv6 bgp|bgp-direct|ipv6 bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static} {}
+@deffnx {VNC} {vnc redistribute ipv4|ipv6 bgp-direct-to-nve-groups view @var{VIEWNAME}} {}
+@deffnx {VNC} {no vnc redistribute ipv4|ipv6 bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static} {}
+Import (or do not import) prefixes from another routing
+protocols. Specify both the address family to import (@code{ipv4} or
+@code{ipv6}) and the protocol (@code{bgp}, @code{bgp-direct},
+@code{bgp-direct-to-nve-groups}, @code{connected},
+@code{kernel}, @code{ospf}, @code{rip}, or @code{static}).  Repeat
+this statement as needed for each combination of address family and
+routing protocol.
+Prefixes from protocol @code{bgp-direct} are imported from unicast BGP
+in the same bgpd process.
+Prefixes from all other protocols (including @code{bgp}) are imported
+via the @code{zebra} kernel routing process.
+@end deffn
+
+@deffn {VNC} {vnc redistribute mode plain|nve-group|resolve-nve}
+Redistribute routes from other protocols into VNC using the
+specified mode.
+Not all combinations of modes and protocols are supported.
+@end deffn
+
+@deffn {VNC} {vnc redistribute nve-group @var{group-name}} {}
+@deffnx {VNC} {no vnc redistribute nve-group @var{group-name}} {}
+When using @code{nve-group} mode,
+assign (or do not assign) the NVE group @var{group-name} to routes
+redistributed from another routing protocol.  @var{group-name}
+must be configured using @code{vnc nve-group}.
+
+The VN and UN prefixes of the nve-group must both be configured,
+and each prefix must be specified as a full-length (/32 for IPv4,
+/128 for IPv6) prefix.
+@end deffn
+
+@deffn {VNC} {vnc redistribute lifetime @var{lifetime}|infinite} {}
+Assign a registration lifetime, either @var{lifetime} seconds or
+@code{infinite}, to prefixes redistributed from other routing
+protocols as if they had been received via RFP registration messages
+from an NVE.  @var{lifetime} can be any integer between 1 and
+4294967295, inclusive. 
+@end deffn
+
+@deffn {VNC} {vnc redistribute resolve-nve roo-ec-local-admin @var{0-65536}}
+Assign a value to the local-administrator subfield used in the
+Route Origin extended community that is assigned to routes exported 
+under the @code{resolve-nve} mode. The default value is @var{5226}.
+@end deffn
+
+The following four @code{prefix-list} and @code{route-map} commands
+may be specified in the context of an nve-group or not.
+If they are specified in the context of an nve-group, they
+apply only if the redistribution mode is @code{nve-group},
+and then only for routes being redistributed from
+@code{bgp-direct}.
+If they are specified outside the context of an nve-group, then
+they apply only for redistribution modes @code{plain} and @code{resolve-nve},
+and then only for routes being redistributed from @code{bgp-direct}.
+
+@deffn {VNC} {vnc redistribute bgp-direct (ipv4|ipv6) prefix-list @var{LIST-NAME}}
+When redistributing @code{bgp-direct} routes,
+specifies that the named prefix-list should be applied.
+@end deffn
+
+@deffn {VNC} {vnc redistribute bgp-direct no (ipv4|ipv6) prefix-list}
+When redistributing @code{bgp-direct} routes,
+specifies that no prefix-list should be applied.
+@end deffn
+
+@deffn {VNC} {vnc redistribute bgp-direct route-map  @var{MAP-NAME}}
+When redistributing @code{bgp-direct} routes,
+specifies that the named route-map should be applied.
+@end deffn
+
+@deffn {VNC} {vnc redistribute bgp-direct no route-map}
+When redistributing @code{bgp-direct} routes,
+specifies that no route-map should be applied.
+@end deffn
+
+@node Configuring Export of Routes to Other Routing Protocols
+@subsection Configuring Export of Routes to Other Routing Protocols
+
+Routes from VNC (both for RFP and for redistribution via BGP) can be
+provided to other protocols, either via zebra or directly to BGP.
+
+It is important to note that when exporting routes to other protocols,
+the downstream protocol must also be configured to import the routes.
+For example, when VNC routes are exported to unicast BGP, the BGP
+configuration must include a corresponding @code{redistribute vpn}
+statement.
+
+@deffn {VNC} {export bgp|zebra mode none|group-nve|registering-nve|ce}
+Specify how routes should be exported to bgp or zebra.
+If the mode is @code{none}, routes are not exported.
+If the mode is @code{group-nve}, routes are exported according
+to nve-group configuration (@pxref{VNC NVE Group Configuration}): if a group is configured to
+allow export, then each prefix visible to the group is exported
+with next hops set to the currently-registered NVEs.
+If the mode is @code{registering-nve}, then all VNC routes are
+exported with their original next hops.
+If the mode is @code{ce}, only VNC routes that have an NVE connected CE Router
+encoded in a Route Origin Extended Community are exported.
+This extended community must have an administrative value that
+matches the configured @code{roo-ec-local-admin} value.
+The next hop of the exported route is set to the encoded
+NVE connected CE Router.
+
+The default for both bgp and zebra is mode @code{none}.
+@end deffn
+
+@deffn {VNC} {vnc export bgp|zebra group-nve group @var{group-name}}
+@deffnx {VNC} {vnc export bgp|zebra group-nve no group @var{group-name}}
+When export mode is @code{group-nve},
+export (or do not export) prefixes from the specified nve-group
+to unicast BGP or to zebra.
+Repeat this statement as needed for each nve-group to be exported.
+Each VNC prefix that is exported will result in N exported routes to the
+prefix, each with a next hop corresponding to one of the N NVEs currently
+associated with the nve-group.
+@end deffn
+
+@deffn {VNC} export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME
+When export mode is @code{ce} or @code{registering-nve},
+specifies that the named prefix-list should be applied to routes
+being exported to bgp or zebra.
+Prefix-lists for ipv4 and ipv6 are independent of each other.
+@end deffn
+
+@deffn {VNC} export bgp|zebra no ipv4|ipv6 prefix-list
+When export mode is @code{ce} or @code{registering-nve},
+specifies that no prefix-list should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} export bgp|zebra route-map MAP-NAME
+When export mode is @code{ce} or @code{registering-nve},
+specifies that the named route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} export bgp|zebra no route-map
+When export mode is @code{ce} or @code{registering-nve},
+specifies that no route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+When the export mode is @code{group-nve}, policy for exported
+routes is specified per-NVE-group inside a @code{nve-group} @var{RFG-NAME} block
+via the following commands(@pxref{VNC NVE Group Configuration}):
+
+@deffn {VNC} {export bgp|zebra route-map MAP-NAME}
+This command is valid inside a @code{nve-group} @var{RFG-NAME} block.
+It specifies that the named route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no route-map}
+This command is valid inside a @code{nve-group} @var{RFG-NAME} block.
+It specifies that no route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME}
+This command is valid inside a @code{nve-group} @var{RFG-NAME} block.
+It specifies that the named prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+Prefix-lists for ipv4 and ipv6 are independent of each other.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no ipv4|ipv6 prefix-list}
+This command is valid inside a @code{nve-group} @var{RFG-NAME} block.
+It specifies that no prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+@end deffn
+
+@node Manual Address Control
+@section Manual Address Control
+
+The commands in this section can be used to augment normal dynamic VNC.
+The @code{add vnc} commands can be used to manually add IP prefix or
+Ethernet MAC address forwarding information.  The @code{clear vnc}
+commands can be used to remove manually and dynamically added
+information.
+
+@deffn {Command} {add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) [cost <0-255>] [lifetime (infinite|<1-4294967295>)] [local-next-hop (A.B.C.D|X:X::X:X) [local-cost <0-255>]]} {}
+Register an IP prefix on behalf of the NVE identified by the VN and UN
+addresses.  The @code{cost} parameter provides the administrative
+preference of the forwarding information for remote advertisement.  If
+omitted, it defaults to 255 (lowest preference).  The @code{lifetime}
+parameter identifies the period, in seconds, that the information
+remains valid.  If omitted, it defaults to @var{infinite}.  The optional
+@code{local-next-hop} parameter is used to configure a nexthop to be
+used by an NVE to reach the prefix via a locally connected CE router.
+This information remains local to the NVA, i.e., not passed to other
+NVAs, and is only passed to registered NVEs. When specified, it is also
+possible to provide a @code{local-cost} parameter to provide a
+forwarding preference.  If omitted, it defaults to 255 (lowest
+preference).
+@end deffn
+
+
+@deffn {Command} {add vnc mac xx:xx:xx:xx:xx:xx virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) [prefix (A.B.C.D/M|X:X::X:X/M)] [cost <0-255>] [lifetime (infinite|<1-4294967295>)]} {}
+Register a MAC address for a logical Ethernet (L2VPN) on behalf of the
+NVE identified by the VN and UN addresses.
+The optional @code{prefix} parameter is to support enable IP address
+mediation for the given prefix.   The @code{cost} parameter provides the administrative
+preference of the forwarding information.  If omitted, it defaults to
+255.  The @code{lifetime} parameter identifies the period, in seconds,
+that the information remains valid.  If omitted, it defaults to
+@var{infinite}. 
+@end deffn
+
+@deffn {Command} {clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) (*|[(vn|un) (A.B.C.D|X:X::X:X|*) [(un|vn) (A.B.C.D|X:X::X:X|*)] [mac xx:xx:xx:xx:xx:xx] [local-next-hop (A.B.C.D|X:X::X:X)])} {}
+Delete the information identified by prefix, VN address, and UN address.
+Any or all of these parameters may be wilcarded to (potentially) match
+more than one registration.
+The optional @code{mac} parameter specifies a layer-2 MAC address
+that must match the registration(s) to be deleted.
+The optional @code{local-next-hop} parameter is used to
+delete specific local nexthop information.
+@end deffn
+
+@deffn {Command} {clear vnc mac (*|xx:xx:xx:xx:xx:xx) virtual-network-identifier (*|<1-4294967295>) (*|[(vn|un) (A.B.C.D|X:X::X:X|*) [(un|vn) (A.B.C.D|X:X::X:X|*)] [prefix (*|A.B.C.D/M|X:X::X:X/M)])} {}
+Delete mac forwarding information.
+Any or all of these parameters may be wilcarded to (potentially) match
+more than one registration.
+The default value for the @code{prefix} parameter is the wildcard value @var{*}.
+@end deffn
+
+@deffn {Command} {clear vnc nve (*|((vn|un) (A.B.C.D|X:X::X:X) [(un|vn) (A.B.C.D|X:X::X:X)])) } {}
+Delete prefixes associated with the NVE specified by the given VN and UN
+addresses.
+It is permissible to specify only one of VN or UN, in which case
+any matching registration will be deleted.
+It is also permissible to specify @code{*} in lieu of any VN or UN
+address, in which case all registrations will match.
+@end deffn
+
+@node Other VNC-Related Commands
+@section Other VNC-Related Commands
+
+Note: VNC-Related configuration can be obtained via the @code{show
+running-configuration} command when in @code{enable} mode.
+
+The following commands are used to clear and display 
+Virtual Network Control related information:
+
+@deffn {COMMAND} {clear vnc counters} {}
+Reset the counter values stored by the NVA. Counter
+values can be seen using the @code{show vnc} commands listed above. This
+command is only available in @code{enable} mode.
+@end deffn
+
+@deffn {Command} {show vnc summary} {}
+Print counter values and other general information 
+about the NVA. Counter values can be reset 
+using the @code{clear vnc counters} command listed below.
+@end deffn
+
+@deffn {Command} {show vnc nves} {}
+@deffnx {Command} {show vnc nves vn|un @var{address}} {}
+Display the NVA's current clients. Specifying @var{address}
+limits the output to the NVEs whose addresses match @var{address}.
+The time since the NVA last communicated with the NVE, per-NVE
+summary counters and each NVE's addresses will be displayed.
+@end deffn
+
+@deffn {Command} {show vnc queries} {}
+@deffnx {Command} {show vnc queries @var{prefix}} {}
+Display active Query information.  Queries remain valid for the default
+Response Lifetime (@pxref{VNC Defaults Configuration}) or NVE-group
+Response Lifetime (@pxref{VNC NVE Group Configuration}).  Specifying
+@var{prefix} limits the output to Query Targets that fall within
+@var{prefix}.
+
+Query information is provided for each querying NVE, and includes the
+Query Target and the time remaining before the information is removed.
+@end deffn
+
+@deffn {Command} {show vnc registrations [all|local|remote|holddown|imported]} {}
+@deffnx {Command} {show vnc registrations [all|local|remote|holddown|imported] @var{prefix}} {}
+Display local, remote, holddown, and/or imported registration information.
+Local registrations are routes received via RFP, which are present in the
+NVA Registrations Cache.
+Remote registrations are routes received via BGP (VPN SAFIs), which
+are present in the NVE-group import tables.
+Holddown registrations are local and remote routes that have been
+withdrawn but whose holddown timeouts have not yet elapsed.
+Imported information represents routes that are imported into NVA and
+are made available to querying NVEs.  Depending on configuration,
+imported routes may also be advertised via BGP.
+Specifying @var{prefix} limits the output to the registered prefixes that
+fall within @var{prefix}.
+
+Registration information includes the registered prefix, the registering
+NVE addresses, the registered administrative cost, the registration
+lifetime and the time since the information was registered or, in the
+case of Holddown registrations, the amount of time remaining before the
+information is removed.
+@end deffn
+
+@deffn {Command} {show vnc responses [active|removed]} {}
+@deffnx {Command} {show vnc responses [active|removed] @var{prefix}} {}
+Display all, active and/or removed response information which are
+present in the NVA Responses Cache. Responses remain valid for the
+default Response Lifetime (@pxref{VNC Defaults Configuration}) or
+NVE-group Response Lifetime (@pxref{VNC NVE Group Configuration}.)
+When Removal Responses are enabled (@pxref{General VNC Configuration}),
+such responses are listed for the Response Lifetime.  Specifying
+@var{prefix} limits the output to the addresses that fall within
+@var{prefix}.
+
+Response information is provided for each querying NVE, and includes
+the response prefix, the prefix-associated registering NVE addresses,
+the administrative cost, the provided response lifetime and the time
+remaining before the information is to be removed or will become inactive.
+@end deffn
+
+@deffn {Command} {show memory vnc} {}
+Print the number of memory items allocated by the NVA.
+@end deffn
+
+@node Example VNC and VNC-GW Configurations
+@section Example VNC and VNC-GW Configurations
+
+@menu
+* Mesh NVA Configuration::
+* Mesh NVA and VNC-GW Configuration::
+* VNC with Quagga Route Reflector Configuration::
+* VNC with Commercial Route Reflector Configuration::
+* VNC with Redundant Route Reflectors Configuration::
+@c * Interfacing VNC to an IGP::
+@end menu
+
+@node  Mesh NVA Configuration
+@subsection  Mesh NVA Configuration
+
+This example includes three NVAs, nine NVEs, and two NVE groups. Note
+that while not shown, a single physical device may support multiple
+logical NVEs.  @ref{fig:fig-vnc-mesh} shows @code{NVA 1}
+(192.168.1.100), @code{NVA 2} (192.168.1.101), and @code{NVA 3}
+(192.168.1.102), which are connected in a full mesh.  Each is a
+member of the autonomous system 64512.  Each NVA provides VNC
+services to three NVE clients in the 172.16.0.0/16 virtual-network
+address range.  The 172.16.0.0/16 address range is partitioned into
+two NVE groups, @code{group1} (172.16.0.0/17) and @code{group2}
+(172.16.128.0/17).
+
+Each NVE belongs to either NVE group @code{group1} or NVE group
+@code{group2}.  The NVEs @code{NVE 1}, @code{NVE 2}, @code{NVE
+4}, @code{NVE 7}, and @code{NVE 8} are members of the NVE group
+@code{group1}.  The NVEs @code{NVE 3}, @code{NVE 5}, @code{NVE
+6}, and @code{NVE 9} are members of the NVE group @code{group2}.
+
+Each NVA advertises NVE underlay-network IP addresses using the
+Tunnel Encapsulation Attribute.
+
+@float Figure,fig:fig-vnc-mesh
+@center @image{fig-vnc-mesh,400pt,,Three-way Mesh}
+@caption{A three-way full mesh with three NVEs per NVA}
+@end float
+
+@file{bgpd.conf} for @code{NVA 1} (192.168.1.100)
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.100
+
+    neighbor 192.168.1.101  remote-as 64512
+    neighbor 192.168.1.102  remote-as 64512
+
+    address-family vpnv4
+        neighbor 192.168.1.101 activate
+        neighbor 192.168.1.102 activate
+    exit-address-family
+
+    vnc defaults
+        rd 64512:1
+        response-lifetime 200
+        rt both 1000:1 1000:2
+    exit-vnc
+
+    vnc nve-group group1
+        prefix vn 172.16.0.0/17
+        rt both 1000:1 
+    exit-vnc
+
+    vnc nve-group group2
+        prefix vn 172.16.128.0/17
+        rt both 1000:2
+    exit-vnc
+
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 2} (192.168.1.101):
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.101
+
+    neighbor 192.168.1.100  remote-as 64512
+    neighbor 192.168.1.102  remote-as 64512
+
+    address-family vpnv4
+        neighbor 192.168.1.100 activate
+        neighbor 192.168.1.102 activate
+    exit-address-family
+
+    vnc nve-group group1
+        prefix vn 172.16.0.0/17
+        rd 64512:1
+        response-lifetime 200
+        rt both 1000:1 1000:2
+    exit-vnc
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 3} (192.168.1.102):
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.102
+
+    neighbor 192.168.1.101  remote-as 64512
+    neighbor 192.168.1.102  remote-as 64512
+
+    address-family vpnv4
+        neighbor 192.168.1.100 activate
+        neighbor 192.168.1.101 activate
+    exit-address-family
+
+    vnc defaults
+        rd 64512:1
+        response-lifetime 200
+        rt both 1000:1 1000:2
+    exit-vnc
+
+    vnc nve-group group1
+        prefix vn 172.16.128.0/17
+    exit-vnc
+exit
+@end verbatim
+
+@node Mesh NVA and VNC-GW Configuration
+@subsection Mesh NVA and VNC-GW Configuration
+
+This example includes two NVAs, each with two associated NVEs, and two
+VNC-GWs, each supporting two CE routers physically attached to the four
+NVEs.  Note that this example is showing a more complex configuration
+where VNC-GW is separated from normal NVA functions; it is equally
+possible to simplify the configuration and combine NVA and VNC-GW
+functions in a single quagga instance.
+
+@float Figure,fig:fig-vnc-gw
+@center @image{fig-vnc-gw,400pt,,Quagga VNC Gateway}
+@caption{Meshed NVEs and VNC-GWs}
+@end float
+
+As shown in @ref{fig:fig-vnc-gw}, NVAs and VNC-GWs are connected in a
+full iBGP mesh.  The VNC-GWs each have two CEs configured as
+route-reflector clients.  Each client provides BGP updates with unicast
+routes that the VNC-GW reflects to the other client.  The VNC-GW also
+imports these unicast routes into VPN routes to be shared with the other
+VNC-GW and the two NVAs.  This route importation is controlled with the
+@code{vnc redistribute} statements shown in the configuration.
+Similarly, registrations sent by NVEs via RFP to the NVAs are exported
+by the VNC-GWs to the route-reflector clients as unicast routes.  RFP
+registrations exported this way have a next-hop address of the CE behind
+the connected (registering) NVE.  Exporting VNC routes as IPv4 unicast
+is enabled with the @code{vnc export} command below.
+
+The configuration for @code{VNC-GW 1} is shown below.
+@verbatim
+router bgp 64512
+ bgp router-id 192.168.1.101
+ bgp cluster-id 1.2.3.4
+ redistribute vpn
+ neighbor 192.168.1.102 remote-as 64512
+ no neighbor 192.168.1.102 activate
+ neighbor 192.168.1.103 remote-as 64512
+ no neighbor 192.168.1.103 activate
+ neighbor 192.168.1.104 remote-as 64512
+ no neighbor 192.168.1.104 activate
+ neighbor 172.16.1.2 remote-as 64512
+ neighbor 172.16.1.2 route-reflector-client
+ neighbor 172.16.2.2 remote-as 64512
+ neighbor 172.16.2.2 route-reflector-client
+!
+ address-family vpnv4 unicast
+   neighbor 192.168.1.102 activate
+   neighbor 192.168.1.103 activate
+   neighbor 192.168.1.104 activate
+ exit-address-family
+ vnc export bgp mode ce
+ vnc redistribute mode resolve-nve
+ vnc redistribute ipv4 bgp-direct
+ exit
+@end verbatim
+
+Note that in the VNC-GW configuration, the neighboring VNC-GW and
+NVAs each have a statement disabling the IPv4 unicast address family.
+IPv4 unicast is on by default and this prevents the other VNC-GW and
+NVAs from learning unicast routes advertised by the route-reflector clients.
+
+Configuration for @code{NVA 2}:
+@verbatim
+router bgp 64512
+ bgp router-id 192.168.1.104
+ neighbor 192.168.1.101 remote-as 64512
+ no neighbor 192.168.1.101 activate
+ neighbor 192.168.1.102 remote-as 64512
+ no neighbor 192.168.1.102 activate
+ neighbor 192.168.1.103 remote-as 64512
+ no neighbor 192.168.1.103 activate
+ address-family vpnv4 unicast
+   neighbor 192.168.1.101 activate
+   neighbor 192.168.1.102 activate
+   neighbor 192.168.1.103 activate
+ exit-address-family
+ vnc defaults
+  response-lifetime 3600
+  exit-vnc
+ vnc nve-group nve1
+  prefix vn 172.16.1.1/32
+  response-lifetime 3600
+  rt both 1000:1 1000:2
+  exit-vnc
+ vnc nve-group nve2
+  prefix vn 172.16.2.1/32
+  response-lifetime 3600
+  rt both 1000:1 1000:2
+  exit-vnc
+ exit
+@end verbatim
+
+@c TBD make this its own example:
+@c 
+@c @float Figure,fig:fig-vnc-gw-rr
+@c @center @image{fig-vnc-gw-rr,400pt,,Quagga VNC Gateway with RR}
+@c @end float
+@c An NVA can also import unicast routes from BGP without advertising the
+@c imported routes as VPN routes.  Such imported routes, while not
+@c distributed to other NVAs or VNC-GWs, are are available to NVEs via
+@c RFP query messages sent to the NVA. @ref{fig:fig-vnc-gw-rr}
+@c shows an example topology where unicast routes are imported into NVAs
+@c from a Route Reflector.  (@pxref{Route Reflector} for route reflector
+@c configuration details.)  The following three lines can be added to the
+@c @code{NVA 1} and @code{NVA 2} configurations to import routes into VNC
+@c for local VNC use:
+@c 
+@c @verbatim
+@c  neighbor 192.168.1.105 remote-as 64512
+@c  vnc redistribute mode plain
+@c  vnc redistribute ipv4 bgp-direct-to-nve-groups
+@c @end verbatim
+
+@node VNC with Quagga Route Reflector Configuration
+@subsection VNC with Quagga Route Reflector Configuration
+A route reflector eliminates the need for a fully meshed NVA
+network by acting as the hub between NVAs.
+@ref{fig:fig-vnc-quagga-route-reflector} shows BGP route reflector
+@code{BGP Route Reflector 1} (192.168.1.100) as a route reflector for
+NVAs @code{NVA 2}(192.168.1.101) and @code{NVA 3}
+(192.168.1.102).
+
+@float Figure,fig:fig-vnc-quagga-route-reflector
+@center @image{fig-vnc-quagga-route-reflector,400pt,,Quagga Route Reflector}
+@caption{Two NVAs and a BGP Route Reflector} 
+@end float
+
+@code{NVA 2} and @code{NVA 3} 
+advertise NVE underlay-network IP addresses using the Tunnel Encapsulation Attribute.
+@code{BGP Route Reflector 1} ``reflects'' advertisements from
+@code{NVA 2} to @code{NVA 3} and vice versa.
+
+As in the example of @ref{Mesh NVA Configuration}, there are two NVE groups.
+The 172.16.0.0/16 address range is partitioned into two NVE groups,
+@code{group1} (172.16.0.0/17) and @code{group2} (172.16.128.0/17).
+The NVE @code{NVE 4}, @code{NVE 7}, and @code{NVE 8} are
+members of the NVE group @code{group1}.  The NVEs @code{NVE 5},
+@code{NVE 6}, and @code{NVE 9} are members of the NVE group
+@code{group2}.
+
+@file{bgpd.conf} for @code{BGP Route Reflector 1} on 192.168.1.100:
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.100
+
+    neighbor 192.168.1.101 remote-as 64512
+    neighbor 192.168.1.101 port 7179
+    neighbor 192.168.1.101 description iBGP-client-192-168-1-101
+    neighbor 192.168.1.101 route-reflector-client
+
+    neighbor 192.168.1.102 remote-as 64512
+    neighbor 192.168.1.102 port 7179
+    neighbor 192.168.1.102 description iBGP-client-192-168-1-102
+    neighbor 192.168.1.102 route-reflector-client
+
+    address-family vpnv4
+        neighbor 192.168.1.101 activate
+        neighbor 192.168.1.102 activate
+
+        neighbor 192.168.1.101 route-reflector-client
+        neighbor 192.168.1.102 route-reflector-client
+    exit-address-family
+
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 2} on 192.168.1.101:
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.101
+
+    neighbor 192.168.1.100  remote-as 64512
+
+    address-family vpnv4
+        neighbor 192.168.1.100 activate
+    exit-address-family
+
+    vnc nve-group group1
+        prefix vn 172.16.0.0/17
+        rd 64512:1
+        response-lifetime 200
+        rt both 1000:1 1000:2
+    exit-vnc
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 2} on 192.168.1.102:
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.102
+
+    neighbor 192.168.1.100  remote-as 64512
+
+    address-family vpnv4
+        neighbor 192.168.1.100 activate
+    exit-address-family
+
+    vnc defaults
+        rd 64512:1
+        response-lifetime 200
+        rt both 1000:1 1000:2
+    exit-vnc
+
+    vnc nve-group group1
+        prefix vn 172.16.128.0/17
+    exit-vnc
+exit
+@end verbatim
+
+While not shown, an NVA can also be configured as a route reflector.
+
+@node VNC with Commercial Route Reflector Configuration
+@subsection VNC with Commercial Route Reflector Configuration
+This example is identical to @ref{VNC with Quagga Route Reflector
+Configuration} with the exception that the route reflector is a
+commercial router.  Only the
+VNC-relevant configuration is provided. 
+
+@float Figure,fig:fig-vnc-commercial-route-reflector
+@center @image{fig-vnc-commercial-route-reflector,400pt,,Commercial Route Reflector}
+@caption{Two NVAs with a commercial route reflector}
+@end float
+
+@file{bgpd.conf} for BGP route reflector @code{Commercial Router} on 192.168.1.104:
+@verbatim
+version 8.5R1.13;
+routing-options {
+    rib inet.0 {
+        static {
+            route 172.16.0.0/16 next-hop 192.168.1.104;
+        }
+    }
+    autonomous-system 64512;
+    resolution {
+        rib inet.3 {
+            resolution-ribs inet.0;
+        }
+        rib bgp.l3vpn.0 {
+            resolution-ribs inet.0;
+        }
+    }
+}
+protocols {
+    bgp {
+        advertise-inactive;
+        family inet {
+            labeled-unicast;
+        }
+       group 1 {
+            type internal;
+            advertise-inactive;
+            advertise-peer-as;
+            import h;
+            family inet {
+                unicast;
+            }
+            family inet-vpn {
+                unicast;
+            }
+            cluster 192.168.1.104;
+            neighbor 192.168.1.101;
+            neighbor 192.168.1.102;
+        }
+    }
+}
+policy-options {
+    policy-statement h {
+        from protocol bgp;
+        then {
+            as-path-prepend 64512;
+            accept;
+        }
+    }
+}
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 2} on 192.168.1.101:
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.101
+
+    neighbor 192.168.1.100  remote-as 64512
+
+    address-family vpnv4
+        neighbor 192.168.1.100 activate
+    exit-address-family
+
+    vnc nve-group group1
+        prefix vn 172.16.0.0/17
+        rd 64512:1
+        response-lifetime 200
+        rt both 1000:1 1000:2
+    exit-vnc
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 3} on 192.168.1.102:
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.102
+
+    neighbor 192.168.1.100  remote-as 64512
+
+    address-family vpnv4
+        neighbor 192.168.1.100 activate
+    exit-address-family
+
+    vnc defaults
+        rd 64512:1
+        response-lifetime 200
+        rt both 1000:1 1000:2
+    exit-vnc
+
+    vnc nve-group group1
+        prefix vn 172.16.128.0/17
+    exit-vnc
+exit
+@end verbatim
+
+@node VNC with Redundant Route Reflectors Configuration
+@subsection VNC with Redundant Route Reflectors Configuration
+This example combines the previous two (@ref{VNC with Quagga Route
+Reflector Configuration} and @ref{VNC with Commercial Route Reflector
+Configuration}) into a redundant route reflector configuration.  BGP
+route reflectors @code{BGP Route Reflector 1} and @code{Commercial Router}
+are the route reflectors for NVAs @code{NVA 2} and
+@code{NVA 3}.  The two NVAs have connections to both
+route reflectors.
+
+@float Figure,fig:fig-vnc-redundant-route-reflectors
+@center @image{fig-vnc-redundant-route-reflectors,400pt,,Redundant Route Reflectors}
+@caption{Quagga-based NVA with redundant route reflectors}
+@end float
+
+@file{bgpd.conf} for @code{Bgpd Route Reflector 1} on 192.168.1.100:
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.100
+    bgp cluster-id 192.168.1.100
+
+    neighbor 192.168.1.104 remote-as 64512
+
+    neighbor 192.168.1.101 remote-as 64512
+    neighbor 192.168.1.101 description iBGP-client-192-168-1-101
+    neighbor 192.168.1.101 route-reflector-client
+
+    neighbor 192.168.1.102 remote-as 64512
+    neighbor 192.168.1.102 description iBGP-client-192-168-1-102
+    neighbor 192.168.1.102 route-reflector-client
+
+    address-family vpnv4
+       neighbor 192.168.1.101 activate
+       neighbor 192.168.1.102 activate
+       neighbor 192.168.1.104 activate
+
+       neighbor 192.168.1.101 route-reflector-client
+       neighbor 192.168.1.102 route-reflector-client
+    exit-address-family
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 2} on 192.168.1.101:
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.101
+
+    neighbor 192.168.1.100  remote-as 64512
+    neighbor 192.168.1.104  remote-as 64512
+
+    address-family vpnv4
+        neighbor 192.168.1.100 activate
+        neighbor 192.168.1.104 activate
+    exit-address-family
+
+    vnc nve-group group1
+        prefix vn 172.16.0.0/17
+        rd 64512:1
+        response-lifetime 200
+        rt both 1000:1 1000:2
+    exit-vnc
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 3} on 192.168.1.102:
+@verbatim
+router bgp 64512
+
+    bgp router-id 192.168.1.102
+
+    neighbor 192.168.1.100  remote-as 64512
+    neighbor 192.168.1.104  remote-as 64512
+
+    address-family vpnv4
+        neighbor 192.168.1.100 activate
+        neighbor 192.168.1.104 activate
+    exit-address-family
+
+    vnc defaults
+        rd 64512:1
+        response-lifetime 200
+        rt both 1000:1 1000:2
+    exit-vnc
+
+    vnc nve-group group1
+        prefix vn 172.16.128.0/17
+    exit-vnc
+exit
+@end verbatim
+
+@file{bgpd.conf} for the Commercial Router route reflector on
+192.168.1.104:
+@verbatim
+routing-options {
+    rib inet.0 {
+        static {
+            route 172.16.0.0/16 next-hop 192.168.1.104;
+        }
+    }
+    autonomous-system 64512;
+    resolution {
+        rib inet.3 {
+            resolution-ribs inet.0;
+        }
+        rib bgp.l3vpn.0 {
+            resolution-ribs inet.0;
+        }
+    }
+}
+protocols {
+    bgp {
+        advertise-inactive;
+        family inet {
+            labeled-unicast;
+        }
+       group 1 {
+            type internal;
+            advertise-inactive;
+            advertise-peer-as;
+            import h;
+            family inet {
+                unicast;
+            }
+            family inet-vpn {
+                unicast;
+            }
+            cluster 192.168.1.104;
+            neighbor 192.168.1.101;
+            neighbor 192.168.1.102;
+        }
+
+       group 2 {
+            type internal;
+            advertise-inactive;
+            advertise-peer-as;
+            import h;
+            family inet {
+                unicast;
+            }
+            family inet-vpn {
+                unicast;
+            }
+            neighbor 192.168.1.100;
+        }
+
+    }
+}
+policy-options {
+    policy-statement h {
+        from protocol bgp;
+        then {
+            as-path-prepend 64512;
+            accept;
+        }
+    }
+}
+@end verbatim
+
+@node Release Notes
+@section Release Notes
+
+@c A paragraph that introduces our release notes.
+
+@c outer list, one item per VNC release, items preceded by bullet
+@itemize @bullet 
+@item
+
+@c @item
+@end itemize
+
+@evenheading @thispage@|@|@thistitle
+@oddheading @thischapter@|@|@thispage
+@everyfooting 
+
index bf8b1b1d3385abd7f51620cc82f2bdee9a980e2e..fa3a782a91f111bfd63f22520f04fc663c674619 100644 (file)
@@ -2538,6 +2538,9 @@ node_parent ( enum node_type node )
     case BGP_VPNV6_NODE:
     case BGP_ENCAP_NODE:
     case BGP_ENCAPV6_NODE:
+    case BGP_VNC_DEFAULTS_NODE:
+    case BGP_VNC_NVE_GROUP_NODE: 
+    case BGP_VNC_L2_GROUP_NODE: 
     case BGP_IPV4_NODE:
     case BGP_IPV4M_NODE:
     case BGP_IPV6_NODE:
@@ -2949,6 +2952,9 @@ DEFUN (config_exit,
     case BGP_VPNV6_NODE:
     case BGP_ENCAP_NODE:
     case BGP_ENCAPV6_NODE:
+    case BGP_VNC_DEFAULTS_NODE:
+    case BGP_VNC_NVE_GROUP_NODE:
+    case BGP_VNC_L2_GROUP_NODE:
     case BGP_IPV6_NODE:
     case BGP_IPV6M_NODE:
       vty->node = BGP_NODE;
@@ -3007,6 +3013,9 @@ DEFUN (config_end,
     case BGP_NODE:
     case BGP_ENCAP_NODE:
     case BGP_ENCAPV6_NODE:
+    case BGP_VNC_DEFAULTS_NODE:
+    case BGP_VNC_NVE_GROUP_NODE:
+    case BGP_VNC_L2_GROUP_NODE:
     case BGP_VPNV4_NODE:
     case BGP_VPNV6_NODE:
     case BGP_IPV4_NODE:
@@ -3630,6 +3639,7 @@ DEFUN (config_logmsg,
   zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
   if (message)
     XFREE(MTYPE_TMP, message);
+
   return CMD_SUCCESS;
 }
 
index 7a4c53a616cfd99ed32c7d990ff78c4dd382cddd..5932743115101baec15064ee65a03b3c2827b042 100644 (file)
@@ -77,6 +77,7 @@ enum node_type
   SERVICE_NODE,                /* Service node. */
   DEBUG_NODE,                  /* Debug node. */
   VRF_DEBUG_NODE,               /* Vrf Debug node. */
+  DEBUG_VNC_NODE,              /* Debug VNC node. */
   AAA_NODE,                    /* AAA node. */
   KEYCHAIN_NODE,               /* Key-chain node. */
   KEYCHAIN_KEY_NODE,           /* Key-chain key node. */
@@ -96,6 +97,10 @@ enum node_type
   BGP_IPV6M_NODE,              /* BGP IPv6 multicast address family. */
   BGP_ENCAP_NODE,              /* BGP ENCAP SAFI */
   BGP_ENCAPV6_NODE,            /* BGP ENCAP SAFI */
+  BGP_VNC_DEFAULTS_NODE,       /* BGP VNC nve defaults */
+  BGP_VNC_NVE_GROUP_NODE,      /* BGP VNC nve group */
+  BGP_VNC_L2_GROUP_NODE,       /* BGP VNC L2 group */
+  RFP_DEFAULTS_NODE,   /* RFP defaults node */
   OSPF_NODE,                   /* OSPF protocol mode */
   OSPF6_NODE,                  /* OSPF protocol for IPv6 mode */
   LDP_NODE,                    /* LDP protocol mode */
index fb7b33dcf9dc1c3e88d876c65ebc48869b2057b8..49c69efc8a9738850b216227286d10cba1255f4a 100644 (file)
--- a/lib/log.c
+++ b/lib/log.c
@@ -55,6 +55,7 @@ const char *zlog_proto_names[] =
   "ISIS",
   "PIM",
   "MASC",
+  "RFP", 
   NULL,
 };
 
@@ -258,6 +259,44 @@ vzlog (struct zlog *zl, int priority, const char *format, va_list args)
   errno = original_errno;
 }
 
+int 
+vzlog_test (struct zlog *zl, int priority)
+{
+  /* If zlog is not specified, use default one. */
+  if (zl == NULL)
+    zl = zlog_default;
+
+  /* When zlog_default is also NULL, use stderr for logging. */
+  if (zl == NULL)
+    {
+      return 1;
+    }
+
+  /* Syslog output */
+  if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
+    {
+      return 1;
+    }
+
+  /* File output. */
+  if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
+    {
+      return 1;
+    }
+
+  /* stdout output. */
+  if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
+    {
+      return 1;
+    }
+
+  /* Terminal monitor. */
+  if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
+    return 1;
+    
+  return 0;
+}
+
 static char *
 str_append(char *dst, int len, const char *src)
 {
@@ -680,6 +719,7 @@ _zlog_assert_failed (const char *assertion, const char *file,
        assertion,file,line,(function ? function : "?"));
   zlog_backtrace(LOG_CRIT);
   zlog_thread_info(LOG_CRIT);
+  log_memstats_stderr ("log");
   abort();
 }
 
@@ -941,6 +981,10 @@ static const struct zebra_desc_table command_types[] = {
   DESC_ENTRY    (ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB),
   DESC_ENTRY   (ZEBRA_MPLS_LABELS_ADD),
   DESC_ENTRY   (ZEBRA_MPLS_LABELS_DELETE),
+  DESC_ENTRY   (ZEBRA_IPV4_NEXTHOP_ADD),
+  DESC_ENTRY   (ZEBRA_IPV4_NEXTHOP_DELETE),
+  DESC_ENTRY   (ZEBRA_IPV6_NEXTHOP_ADD),
+  DESC_ENTRY   (ZEBRA_IPV6_NEXTHOP_DELETE),
 };
 #undef DESC_ENTRY
 
@@ -1029,6 +1073,10 @@ proto_redistnum(int afi, const char *s)
        return ZEBRA_ROUTE_BGP;
       else if (strncmp (s, "ta", 2) == 0)
        return ZEBRA_ROUTE_TABLE;
+      else if (strncmp (s, "v", 1) == 0)
+       return ZEBRA_ROUTE_VNC;
+      else if (strncmp (s, "vd", 1) == 0)
+       return ZEBRA_ROUTE_VNC_DIRECT;
     }
   if (afi == AFI_IP6)
     {
@@ -1048,6 +1096,10 @@ proto_redistnum(int afi, const char *s)
        return ZEBRA_ROUTE_BGP;
       else if (strncmp (s, "ta", 2) == 0)
        return ZEBRA_ROUTE_TABLE;
+      else if (strncmp (s, "v", 1) == 0)
+       return ZEBRA_ROUTE_VNC;
+      else if (strncmp (s, "vd", 1) == 0)
+       return ZEBRA_ROUTE_VNC_DIRECT;
     }
   return -1;
 }
index cb8fac78c39bdb2e155c9c71a3269c54c882ab53..91cab3f96a0cdfb9b8ec36703d5766bf9264e8ff 100644 (file)
--- a/lib/log.h
+++ b/lib/log.h
@@ -53,6 +53,7 @@ typedef enum
   ZLOG_OSPF6,
   ZLOG_LDP,
   ZLOG_ISIS,
+  ZLOG_RFP,
   ZLOG_PIM,
   ZLOG_MASC
 } zlog_proto_t;
@@ -184,6 +185,10 @@ extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */,
 
 extern void zlog_hexdump(const void *mem, unsigned int len);
 
+
+extern int 
+vzlog_test (struct zlog *zl, int priority);
+
 /* structure useful for avoiding repeated rendering of the same timestamp */
 struct timestamp_control {
    size_t len;         /* length of rendered timestamp */
index 34bb1a493a8f2865a6a3d7de759b8f10f58f01b0..112dae5822d5a17da4a804c7be8367ea936f1d04 100644 (file)
@@ -240,6 +240,8 @@ afi2str(afi_t afi)
        return "IPv6";
     case AFI_ETHER:
        return "ethernet";
+    case AFI_MAX:
+        return "bad-value";
     default:
        break;
   }
index 0ac442d85d1164ffec69f525c3b3248ed1dd84a7..698e8b9e5c20d4b47faec73b572d8a9307cb3476 100644 (file)
@@ -61,6 +61,16 @@ ZEBRA_ROUTE_HSLS,       hsls,      hslsd,  'H', 0, 0, "HSLS"
 ZEBRA_ROUTE_OLSR,       olsr,      olsrd,  'o', 0, 0, "OLSR"
 ZEBRA_ROUTE_TABLE,      table,     zebra,  'T', 1, 1, "Table"
 ZEBRA_ROUTE_LDP,        ldp,       ldpd,   'L', 0, 0, "LDP"
+#vnc when sent to zebra 
+ZEBRA_ROUTE_VNC,        vnc,       NULL,   'v', 1, 1, "VNC"
+# vnc when sent to bgp
+ZEBRA_ROUTE_VNC_DIRECT, vpn,       NULL,   'V', 1, 1, "VPN"
+# vnc when sent to bgp (remote next hop?)
+ZEBRA_ROUTE_VNC_DIRECT_RH, vpn-rh, NULL,   'V', 0, 0, "VPN"
+#  bgp unicast -> vnc 
+ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL,  'b', 0, 0, "BGP-Direct"
+#  bgp unicast -> vnc 
+ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC"
 
 ## help strings
 ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only"
@@ -75,6 +85,8 @@ ZEBRA_ROUTE_ISIS,   "Intermediate System to Intermediate System (IS-IS)"
 ZEBRA_ROUTE_BGP,    "Border Gateway Protocol (BGP)"
 ZEBRA_ROUTE_PIM,    "Protocol Independent Multicast (PIM)"
 ZEBRA_ROUTE_HSLS,   "Hazy-Sighted Link State Protocol (HSLS)"
+ZEBRA_ROUTE_VNC,    "Virtual Network Control (VNC)"
 ZEBRA_ROUTE_OLSR,   "Optimised Link State Routing (OLSR)"
 ZEBRA_ROUTE_TABLE,  "Non-main Kernel Routing Table"
 ZEBRA_ROUTE_LDP,    "Label Distribution Protocol (LDP)"
+ZEBRA_ROUTE_VNC_DIRECT,    "VPN routes(VPN)"
index 3f135004494ff73806435b6abf0a33dce9135720..14ef7e6e4aaabe6e38add6c67c59828e0f379a0a 100644 (file)
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -747,6 +747,9 @@ vty_end_config (struct vty *vty)
     case BGP_VPNV6_NODE:
     case BGP_ENCAP_NODE:
     case BGP_ENCAPV6_NODE:
+    case BGP_VNC_DEFAULTS_NODE:
+    case BGP_VNC_NVE_GROUP_NODE:
+    case BGP_VNC_L2_GROUP_NODE:
     case BGP_IPV4_NODE:
     case BGP_IPV4M_NODE:
     case BGP_IPV6_NODE:
index 599daa3509054d60bd7ec4c2e805cd88bf8753e3..e625b4051bd85e7c8e3e8ad389c6f47b6d7366e8 100644 (file)
@@ -427,6 +427,10 @@ typedef enum {
   ZEBRA_INTERFACE_LINK_PARAMS,
   ZEBRA_MPLS_LABELS_ADD,
   ZEBRA_MPLS_LABELS_DELETE,
+  ZEBRA_IPV4_NEXTHOP_ADD,
+  ZEBRA_IPV4_NEXTHOP_DELETE,
+  ZEBRA_IPV6_NEXTHOP_ADD,
+  ZEBRA_IPV6_NEXTHOP_DELETE,
 } zebra_message_types_t;
 
 /* Marker value used in new Zserv, in the byte location corresponding
index 40c30e40ae271efbfb5152cffab455de365ddb74..4c35a8bf68898deebba4e4376eb42524be8f1467 100644 (file)
@@ -24,6 +24,7 @@
 %{!?vty_group:                 %global vty_group               quaggavty }
 %{!?with_fpm:                  %global with_fpm                0 }
 %{!?with_watchquagga:          %global with_watchquagga        1 }
+%{!?with_bgp_vnc:              %global with_bgp_vnc            0 }
 
 # path defines
 %define                _sysconfdir     /etc/quagga
@@ -235,6 +236,11 @@ developing OSPF-API and quagga applications.
        --enable-watchquagga \
 %else
        --disable-watchquagga \
+%endif
+%if %{with_bgp_vnc}
+       --enable-bgp-vnc \
+%else
+       --disable-bgp-vnc \
 %endif
        --enable-gcc-rdynamic \
        --enable-isisd=yes \
index ad39f75d688feca7c2829263f246f6487a5f4aae..23f2adf82c8266d9f3e01635c5eb2c192f0b2d16 100644 (file)
@@ -248,6 +248,7 @@ static struct {
   {ZEBRA_ROUTE_STATIC,  1, "static"},
   {ZEBRA_ROUTE_OSPF,    1, "ospf"},
   {ZEBRA_ROUTE_BGP,     2, "bgp"},
+  {ZEBRA_ROUTE_VNC,     1, "vnc"},
   {0, 0, NULL}
 };
 
index 637e8f5a27317797fcb083f47c137524cbad8cf1..c4ed0c52c62889eed70ec25c54a7895b04b524ff 100644 (file)
@@ -255,6 +255,7 @@ static struct {
   {ZEBRA_ROUTE_STATIC,  1, "static"},
   {ZEBRA_ROUTE_OSPF6,   1, "ospf6"},
   {ZEBRA_ROUTE_BGP,     2, "bgp"},
+  {ZEBRA_ROUTE_VNC,     1, "vnc"},
   {0, 0, NULL}
 };
 
index 16c9e4c3db5db6f47b1e4eb0736adf9957ee181b..0014fa34d1ed87e55b95f6facb348108399b604a 100644 (file)
@@ -25,6 +25,12 @@ else
 TESTS_BGPD =
 endif
 
+if ENABLE_BGP_VNC
+BGP_VNC_RFP_LIB=@top_srcdir@/$(LIBRFP)/librfp.a 
+else
+BGP_VNC_RFP_LIB =
+endif
+
 check_PROGRAMS = testsig testsegv testbuffer testmemory heavy heavywq heavythread \
                testprivs teststream testchecksum tabletest testnexthopiter \
                testcommands test-timer-correctness test-timer-performance \
@@ -77,12 +83,12 @@ teststream_LDADD = ../lib/libzebra.la @LIBCAP@
 heavy_LDADD = ../lib/libzebra.la @LIBCAP@ -lm
 heavywq_LDADD = ../lib/libzebra.la @LIBCAP@ -lm
 heavythread_LDADD = ../lib/libzebra.la @LIBCAP@ -lm
-aspathtest_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm
-testbgpcap_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm
-ecommtest_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm
-testbgpmpattr_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm
+aspathtest_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ -lm
+testbgpcap_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ -lm
+ecommtest_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ -lm
+testbgpmpattr_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ -lm
 testchecksum_LDADD = ../lib/libzebra.la @LIBCAP@ 
-testbgpmpath_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm
+testbgpmpath_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ -lm
 tabletest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm
 testnexthopiter_LDADD = ../lib/libzebra.la @LIBCAP@
 testcommands_LDADD = ../lib/libzebra.la @LIBCAP@
index 71fb0ae53e6a391d868869bb64e80548cfb9f69d..58ffdfca26d5d0e99fcbdfc572b44923578c8d28 100644 (file)
@@ -1,6 +1,23 @@
 ## Process this file with Automake to create Makefile.in
 
-AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
+
+if ENABLE_BGP_VNC
+BGP_VNC_RFP_SRCDIR   = @top_srcdir@/@LIBRFP@
+BGP_VNC_RFP_INCDIR   = -I$(BGP_VNC_RFP_SRCDIR)
+BGP_VNC_RFP_SRC      = $(BGP_VNC_RFP_SRCDIR)/*.c
+BGP_VNC_RFAPI_SRCDIR = @top_srcdir@/bgpd/rfapi
+BGP_VNC_RFAPI_INCDIR = -I$(BGP_VNC_RFAPI_SRCDIR) -I$(top_srcdir)/bgpd
+BGP_VNC_RFAPI_SRC    = $(BGP_VNC_RFAPI_SRCDIR)/*.c
+else
+BGP_VNC_RFP_INCDIR   =
+BGP_VNC_RFP_SRCDIR   =
+BGP_VNC_RFP_SRC      =
+BGP_VNC_RFAPI_INCDIR =
+BGP_VNC_RFAPI_SRCDIR =
+BGP_VNC_RFAPI_SRC    =
+endif
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \
+       $(BGP_VNC_RFAPI_INCDIR) $(BGP_VNC_RFP_INCDIR)
 DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
 
 LIBS = @LIBS@ @CURSES@ @LIBPAM@
@@ -68,7 +85,8 @@ vtysh_cmd_FILES = $(vtysh_scan) \
                  $(top_srcdir)/zebra/zebra_routemap.c \
                  $(top_srcdir)/zebra/zebra_fpm.c \
                  $(top_srcdir)/zebra/zebra_ptm.c \
-                 $(top_srcdir)/zebra/zebra_mpls_vty.c
+                 $(top_srcdir)/zebra/zebra_mpls_vty.c \
+                 $(BGP_VNC_RFAPI_SRC) $(BGP_VNC_RFP_SRC)
 
 vtysh_cmd.c: $(vtysh_cmd_FILES) extract.pl
        ./extract.pl $(vtysh_cmd_FILES) > vtysh_cmd.c
index b4884b9687a13e1bd95c2f292c32fd39b3e9984c..7c5dec9a5474c4e629f4d39d20e2fe9e338d1834 100755 (executable)
@@ -65,6 +65,9 @@ $ignore{'"address-family encapv6"'} = "ignore";
 $ignore{'"address-family vpnv6"'} = "ignore";
 $ignore{'"address-family vpnv6 unicast"'} = "ignore";
 $ignore{'"exit-address-family"'} = "ignore";
+$ignore{'"vnc defaults"'} = "ignore";
+$ignore{'"vnc nve-group NAME"'} = "ignore";
+$ignore{'"exit-vnc"'} = "ignore";
 $ignore{'"key chain WORD"'} = "ignore";
 $ignore{'"key <0-2147483647>"'} = "ignore";
 $ignore{'"route-map WORD (deny|permit) <1-65535>"'} = "ignore";
@@ -80,7 +83,7 @@ my $cli_stomp = 0;
 foreach (@ARGV) {
     $file = $_;
 
-    open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -DHAVE_IPV6 -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_builddir@/lib -I@top_srcdir@/isisd/topology @CPPFLAGS@ $file |");
+    open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -DHAVE_IPV6 -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_builddir@/lib -I@top_srcdir@/isisd/topology -I@top_srcdir@/bgpd -I@top_srcdir@/@LIBRFP@ -I@top_srcdir@/bgpd/rfapi @CPPFLAGS@ $file |");
     local $/; undef $/;
     $line = <FH>;
     close (FH);
@@ -159,7 +162,10 @@ foreach (@ARGV) {
         elsif ($file =~ /lib\/vty\.c$/) {
            $protocol = "VTYSH_ALL";
         }
-        else {
+        elsif ($file =~ /librfp\/.*\.c$/ || $file =~ /rfapi\/.*\.c$/) {
+           $protocol = "VTYSH_BGPD";
+        }
+       else {
            ($protocol) = ($file =~ /^.*\/([a-z0-9]+)\/[a-zA-Z0-9_\-]+\.c$/);
            $protocol = "VTYSH_" . uc $protocol;
         }
index 9dc2eca0b7b09a4d88163934d65186d41080ca07..4f64283a0e27758836317ae09d0a693274e23d25 100644 (file)
@@ -375,6 +375,12 @@ vtysh_execute_func (const char *line, int pager)
        {
          vtysh_execute("exit-address-family");
        }
+      else if ((saved_node == BGP_VNC_DEFAULTS_NODE
+           || saved_node == BGP_VNC_NVE_GROUP_NODE
+           || saved_node == BGP_VNC_L2_GROUP_NODE) && (tried == 1))
+       {
+         vtysh_execute("exit-vnc");
+       }
       else if ((saved_node == KEYCHAIN_KEY_NODE) && (tried == 1))
        {
          vtysh_execute("exit");
@@ -1015,6 +1021,24 @@ static struct cmd_node bgp_ipv6m_node =
   "%s(config-router-af)# "
 };
 
+static struct cmd_node bgp_vnc_defaults_node =
+{
+  BGP_VNC_DEFAULTS_NODE,
+  "%s(config-router-vnc-defaults)# "
+};
+
+static struct cmd_node bgp_vnc_nve_group_node =
+{
+  BGP_VNC_NVE_GROUP_NODE,
+  "%s(config-router-vnc-nve-group)# "
+};
+
+static struct cmd_node bgp_vnc_l2_group_node =
+{
+  BGP_VNC_L2_GROUP_NODE,
+  "%s(config-router-vnc-l2-group)# "
+};
+
 static struct cmd_node ospf_node =
 {
   OSPF_NODE,
@@ -1289,6 +1313,41 @@ DEFUNSH (VTYSH_BGPD,
   return CMD_SUCCESS;
 }
 
+DEFUNSH (VTYSH_BGPD,
+         vnc_defaults,
+         vnc_defaults_cmd,
+         "vnc defaults",
+         "VNC/RFP related configuration\n"
+         "Configure default NVE group\n")
+{
+  vty->node = BGP_VNC_DEFAULTS_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+         vnc_nve_group,
+         vnc_nve_group_cmd,
+         "vnc nve-group NAME",
+         "VNC/RFP related configuration\n"
+         "Configure a NVE group\n"
+         "Group name\n")
+{
+  vty->node = BGP_VNC_NVE_GROUP_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
+         vnc_l2_group,
+         vnc_l2_group_cmd,
+         "vnc l2-group NAME",
+         "VNC/RFP related configuration\n"
+         "Configure a L2 group\n"
+         "Group name\n")
+{
+  vty->node = BGP_VNC_L2_GROUP_NODE;
+  return CMD_SUCCESS;
+}
+
 DEFUNSH (VTYSH_RIPD,
         key_chain,
         key_chain_cmd,
@@ -1553,6 +1612,9 @@ vtysh_exit (struct vty *vty)
     case BGP_IPV4M_NODE:
     case BGP_IPV6_NODE:
     case BGP_IPV6M_NODE:
+    case BGP_VNC_DEFAULTS_NODE:
+    case BGP_VNC_NVE_GROUP_NODE:
+    case BGP_VNC_L2_GROUP_NODE:
       vty->node = BGP_NODE;
       break;
     case LDP_IPV4_NODE:
@@ -1612,6 +1674,19 @@ DEFUNSH (VTYSH_BGPD,
   return CMD_SUCCESS;
 }
 
+DEFUNSH (VTYSH_BGPD,
+        exit_vnc_config,
+        exit_vnc_config_cmd,
+        "exit-vnc",
+        "Exit from VNC configuration mode\n")
+{
+  if (vty->node == BGP_VNC_DEFAULTS_NODE
+      || vty->node == BGP_VNC_NVE_GROUP_NODE
+      || vty->node == BGP_VNC_L2_GROUP_NODE)
+    vty->node = BGP_NODE;
+  return CMD_SUCCESS;
+}
+
 DEFUNSH (VTYSH_ZEBRA,
         vtysh_exit_zebra,
         vtysh_exit_zebra_cmd,
@@ -3071,6 +3146,11 @@ vtysh_init_vty (void)
 /* #ifdef HAVE_IPV6 */
   install_node (&bgp_ipv6_node, NULL);
   install_node (&bgp_ipv6m_node, NULL);
+/* #endif */
+/*#if ENABLE_BGP_VNC                */
+  install_node (&bgp_vnc_defaults_node, NULL);
+  install_node (&bgp_vnc_nve_group_node, NULL);
+  install_node (&bgp_vnc_l2_group_node, NULL);
 /* #endif */
   install_node (&ospf_node, NULL);
 /* #ifdef HAVE_IPV6 */
@@ -3108,6 +3188,11 @@ vtysh_init_vty (void)
   vtysh_install_default (BGP_IPV4M_NODE);
   vtysh_install_default (BGP_IPV6_NODE);
   vtysh_install_default (BGP_IPV6M_NODE);
+  /* #if ENABLE_BGP_VNC */
+  vtysh_install_default (BGP_VNC_DEFAULTS_NODE);
+  vtysh_install_default (BGP_VNC_NVE_GROUP_NODE);
+  vtysh_install_default (BGP_VNC_L2_GROUP_NODE);
+  /* #endif */
   vtysh_install_default (OSPF_NODE);
   vtysh_install_default (RIPNG_NODE);
   vtysh_install_default (OSPF6_NODE);
@@ -3174,6 +3259,12 @@ vtysh_init_vty (void)
   install_element (BGP_IPV6_NODE, &vtysh_quit_bgpd_cmd);
   install_element (BGP_IPV6M_NODE, &vtysh_exit_bgpd_cmd);
   install_element (BGP_IPV6M_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &vtysh_quit_bgpd_cmd);
+  install_element (BGP_VNC_L2_GROUP_NODE, &vtysh_exit_bgpd_cmd);
+  install_element (BGP_VNC_L2_GROUP_NODE, &vtysh_quit_bgpd_cmd);
   install_element (ISIS_NODE, &vtysh_exit_isisd_cmd);
   install_element (ISIS_NODE, &vtysh_quit_isisd_cmd);
   install_element (KEYCHAIN_NODE, &vtysh_exit_ripd_cmd);
@@ -3208,6 +3299,9 @@ vtysh_init_vty (void)
   install_element (BGP_ENCAPV6_NODE, &vtysh_end_all_cmd);
   install_element (BGP_IPV6_NODE, &vtysh_end_all_cmd);
   install_element (BGP_IPV6M_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &vtysh_end_all_cmd);
+  install_element (BGP_VNC_L2_GROUP_NODE, &vtysh_end_all_cmd);
   install_element (ISIS_NODE, &vtysh_end_all_cmd);
   install_element (KEYCHAIN_NODE, &vtysh_end_all_cmd);
   install_element (KEYCHAIN_KEY_NODE, &vtysh_end_all_cmd);
@@ -3256,6 +3350,8 @@ vtysh_init_vty (void)
   install_element (BGP_NODE, &address_family_vpnv6_unicast_cmd);
   install_element (BGP_NODE, &address_family_encap_cmd);
   install_element (BGP_NODE, &address_family_encapv6_cmd);
+  install_element (BGP_NODE, &vnc_defaults_cmd);
+  install_element (BGP_NODE, &vnc_nve_group_cmd);
   install_element (BGP_NODE, &address_family_ipv4_unicast_cmd);
   install_element (BGP_NODE, &address_family_ipv4_multicast_cmd);
 #ifdef HAVE_IPV6
@@ -3271,6 +3367,11 @@ vtysh_init_vty (void)
   install_element (BGP_IPV4M_NODE, &exit_address_family_cmd);
   install_element (BGP_IPV6_NODE, &exit_address_family_cmd);
   install_element (BGP_IPV6M_NODE, &exit_address_family_cmd);
+
+  install_element (BGP_VNC_DEFAULTS_NODE, &exit_vnc_config_cmd);
+  install_element (BGP_VNC_NVE_GROUP_NODE, &exit_vnc_config_cmd);
+  install_element (BGP_VNC_L2_GROUP_NODE, &exit_vnc_config_cmd);
+
   install_element (CONFIG_NODE, &key_chain_cmd);
   install_element (CONFIG_NODE, &route_map_cmd);
   install_element (CONFIG_NODE, &vtysh_line_vty_cmd);
index bf6215c401e074b35c11a4ccae875b6d099f7830..eb58497310b639b29634ccb003350eca371f6a20 100644 (file)
@@ -173,10 +173,36 @@ vtysh_config_parse_line (const char *line)
       /* Store line to current configuration. */
       if (config)
        {
-         if (config->index == RMAP_NODE ||
+          if (strncmp (line, " address-family vpnv4",
+             strlen (" address-family vpnv4")) == 0)
+           config = config_get (BGP_VPNV4_NODE, line);
+         else if (strncmp (line, " address-family vpn6",
+             strlen (" address-family vpn6")) == 0)
+           config = config_get (BGP_VPNV6_NODE, line);
+         else if (strncmp (line, " address-family encapv6",
+             strlen (" address-family encapv6")) == 0)
+           config = config_get (BGP_ENCAPV6_NODE, line);
+         else if (strncmp (line, " address-family encap",
+             strlen (" address-family encap")) == 0)
+           config = config_get (BGP_ENCAP_NODE, line);
+         else if (strncmp (line, " address-family ipv4 multicast",
+                  strlen (" address-family ipv4 multicast")) == 0)
+           config = config_get (BGP_IPV4M_NODE, line);
+         else if (strncmp (line, " address-family ipv6",
+                  strlen (" address-family ipv6")) == 0)
+           config = config_get (BGP_IPV6_NODE, line);
+         else if (strncmp (line, " vnc defaults",
+                  strlen (" vnc defaults")) == 0)
+           config = config_get (BGP_VNC_DEFAULTS_NODE, line);
+         else if (strncmp (line, " vnc nve-group",
+                  strlen (" vnc nve-group")) == 0)
+           config = config_get (BGP_VNC_NVE_GROUP_NODE, line);
+         else if (strncmp (line, " vnc l2-group",
+                  strlen (" vnc l2-group")) == 0)
+           config = config_get (BGP_VNC_L2_GROUP_NODE, line);
+         else if (config->index == RMAP_NODE ||
                   config->index == INTERFACE_NODE ||
                   config->index == NS_NODE ||
-                  config->index == VRF_NODE ||
                   config->index == VTY_NODE)
            config_add_line_uniq (config->line, line);
          else
index 25f028af6966e95e5b8ca511603500865fad1df7..135cd88a10a73ad20a0b9d9abfe43fc3ec1f0051 100644 (file)
@@ -1942,6 +1942,12 @@ zebra_client_read (struct thread *thread)
     case ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD:
       zread_ipv4_route_ipv6_nexthop_add (client, length, zvrf);
       break;
+    case ZEBRA_IPV4_NEXTHOP_ADD:
+      zread_ipv4_add(client, length, zvrf); /* LB: r1.0 merge - id was 1 */
+      break;
+    case ZEBRA_IPV4_NEXTHOP_DELETE:
+      zread_ipv4_delete(client, length, zvrf); /* LB: r1.0 merge - id was 1 */
+      break;
     case ZEBRA_IPV6_ROUTE_ADD:
       zread_ipv6_add (client, length, zvrf);
       break;