summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am5
-rw-r--r--bgpd/Makefile.am74
-rw-r--r--bgpd/bgp_attr.c84
-rw-r--r--bgpd/bgp_attr.h19
-rw-r--r--bgpd/bgp_ecommunity.c4
-rw-r--r--bgpd/bgp_ecommunity.h3
-rw-r--r--bgpd/bgp_encap.c12
-rw-r--r--bgpd/bgp_encap_tlv.c4
-rw-r--r--bgpd/bgp_encap_types.h2
-rw-r--r--bgpd/bgp_main.c8
-rw-r--r--bgpd/bgp_memory.c3
-rw-r--r--bgpd/bgp_memory.h3
-rw-r--r--bgpd/bgp_mplsvpn.c127
-rw-r--r--bgpd/bgp_mplsvpn.h41
-rw-r--r--bgpd/bgp_nexthop.h2
-rw-r--r--bgpd/bgp_route.c294
-rw-r--r--bgpd/bgp_route.h37
-rw-r--r--bgpd/bgp_routemap.c11
-rw-r--r--bgpd/bgp_vnc_types.h41
-rw-r--r--bgpd/bgp_zebra.c18
-rw-r--r--bgpd/bgpd.c33
-rw-r--r--bgpd/bgpd.conf.vnc.sample89
-rw-r--r--bgpd/bgpd.h18
-rw-r--r--bgpd/rfapi/bgp_rfapi_cfg.c4706
-rw-r--r--bgpd/rfapi/bgp_rfapi_cfg.h312
-rw-r--r--bgpd/rfapi/rfapi.c4412
-rw-r--r--bgpd/rfapi/rfapi.h980
-rw-r--r--bgpd/rfapi/rfapi_ap.c629
-rw-r--r--bgpd/rfapi/rfapi_ap.h99
-rw-r--r--bgpd/rfapi/rfapi_backend.h92
-rw-r--r--bgpd/rfapi/rfapi_descriptor_rfp_utils.c131
-rw-r--r--bgpd/rfapi/rfapi_descriptor_rfp_utils.h39
-rw-r--r--bgpd/rfapi/rfapi_encap_tlv.c812
-rw-r--r--bgpd/rfapi/rfapi_encap_tlv.h43
-rw-r--r--bgpd/rfapi/rfapi_import.c5154
-rw-r--r--bgpd/rfapi/rfapi_import.h283
-rw-r--r--bgpd/rfapi/rfapi_monitor.c1701
-rw-r--r--bgpd/rfapi/rfapi_monitor.h217
-rw-r--r--bgpd/rfapi/rfapi_nve_addr.c175
-rw-r--r--bgpd/rfapi/rfapi_nve_addr.h43
-rw-r--r--bgpd/rfapi/rfapi_private.h455
-rw-r--r--bgpd/rfapi/rfapi_rib.c2535
-rw-r--r--bgpd/rfapi/rfapi_rib.h154
-rw-r--r--bgpd/rfapi/rfapi_vty.c5025
-rw-r--r--bgpd/rfapi/rfapi_vty.h223
-rw-r--r--bgpd/rfapi/vnc_debug.c230
-rw-r--r--bgpd/rfapi/vnc_debug.h49
-rw-r--r--bgpd/rfapi/vnc_export_bgp.c2177
-rw-r--r--bgpd/rfapi/vnc_export_bgp.h42
-rw-r--r--bgpd/rfapi/vnc_export_bgp_p.h95
-rw-r--r--bgpd/rfapi/vnc_export_table.c214
-rw-r--r--bgpd/rfapi/vnc_export_table.h85
-rw-r--r--bgpd/rfapi/vnc_import_bgp.c3165
-rw-r--r--bgpd/rfapi/vnc_import_bgp.h93
-rw-r--r--bgpd/rfapi/vnc_import_bgp_p.h51
-rw-r--r--bgpd/rfapi/vnc_zebra.c1121
-rw-r--r--bgpd/rfapi/vnc_zebra.h67
-rw-r--r--bgpd/rfp-example/librfp/Makefile.am40
-rw-r--r--bgpd/rfp-example/librfp/rfp.h31
-rw-r--r--bgpd/rfp-example/librfp/rfp_example.c286
-rw-r--r--bgpd/rfp-example/librfp/rfp_internal.h29
-rw-r--r--bgpd/rfp-example/rfptest/Makefile.am52
-rw-r--r--bgpd/rfp-example/rfptest/rfptest.c32
-rw-r--r--bgpd/rfp-example/rfptest/rfptest.h26
-rwxr-xr-xconfigure.ac38
-rw-r--r--doc/Makefile.am28
-rw-r--r--doc/bgpd.texi4
-rw-r--r--doc/fig-vnc-commercial-route-reflector.dia794
-rw-r--r--doc/fig-vnc-commercial-route-reflector.pngbin0 -> 50984 bytes
-rw-r--r--doc/fig-vnc-commercial-route-reflector.txt0
-rw-r--r--doc/fig-vnc-gw-rr.dia1155
-rw-r--r--doc/fig-vnc-gw-rr.pngbin0 -> 80004 bytes
-rw-r--r--doc/fig-vnc-gw-rr.txt0
-rw-r--r--doc/fig-vnc-gw.dia1058
-rw-r--r--doc/fig-vnc-gw.pngbin0 -> 67376 bytes
-rw-r--r--doc/fig-vnc-gw.txt0
-rw-r--r--doc/fig-vnc-mesh.dia1071
-rw-r--r--doc/fig-vnc-mesh.pngbin0 -> 61028 bytes
-rw-r--r--doc/fig-vnc-mesh.txt0
-rw-r--r--doc/fig-vnc-quagga-route-reflector.dia763
-rw-r--r--doc/fig-vnc-quagga-route-reflector.pngbin0 -> 49978 bytes
-rw-r--r--doc/fig-vnc-quagga-route-reflector.txt0
-rw-r--r--doc/fig-vnc-redundant-route-reflectors.dia871
-rw-r--r--doc/fig-vnc-redundant-route-reflectors.pngbin0 -> 75353 bytes
-rw-r--r--doc/fig-vnc-redundant-route-reflectors.txt0
-rw-r--r--doc/ospfd.texi4
-rw-r--r--doc/quagga.texi8
-rw-r--r--doc/routemap.texi2
-rw-r--r--doc/vnc.texi1584
-rw-r--r--lib/command.c10
-rw-r--r--lib/command.h5
-rw-r--r--lib/log.c52
-rw-r--r--lib/log.h5
-rw-r--r--lib/prefix.c2
-rw-r--r--lib/route_types.txt12
-rw-r--r--lib/vty.c3
-rw-r--r--lib/zebra.h4
-rw-r--r--redhat/quagga.spec.in6
-rw-r--r--ripd/rip_zebra.c1
-rw-r--r--ripngd/ripng_zebra.c1
-rw-r--r--tests/Makefile.am16
-rw-r--r--vtysh/Makefile.am22
-rwxr-xr-xvtysh/extract.pl.in10
-rw-r--r--vtysh/vtysh.c101
-rw-r--r--vtysh/vtysh_config.c30
-rw-r--r--zebra/zserv.c6
106 files changed, 44654 insertions, 49 deletions
diff --git a/Makefile.am b/Makefile.am
index 031afac9d..d19df6f3b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am
index fb5b2375d..ebd7932bd 100644
--- a/bgpd/Makefile.am
+++ b/bgpd/Makefile.am
@@ -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
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 3cb52ef91..cd92aec23 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -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. */
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 0bf8c897d..002bdfa08 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -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. */
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 926e2650a..6c72aa36d 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -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;
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index 993fd5acf..c5c58e426 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -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 */
diff --git a/bgpd/bgp_encap.c b/bgpd/bgp_encap.c
index 3f7712b24..2cfb51029 100644
--- a/bgpd/bgp_encap.c
+++ b/bgpd/bgp_encap.c
@@ -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);
}
diff --git a/bgpd/bgp_encap_tlv.c b/bgpd/bgp_encap_tlv.c
index 347b4b3ce..c554ade27 100644
--- a/bgpd/bgp_encap_tlv.c
+++ b/bgpd/bgp_encap_tlv.c
@@ -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
diff --git a/bgpd/bgp_encap_types.h b/bgpd/bgp_encap_types.h
index 603ff9d2d..0985446ff 100644
--- a/bgpd/bgp_encap_types.h
+++ b/bgpd/bgp_encap_types.h
@@ -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,
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 404fe7d73..9ee8838fd 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -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);
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index 166400b74..72c0311c1 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -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")
diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h
index b2956f07e..a4ce8b891 100644
--- a/bgpd/bgp_memory.h
+++ b/bgpd/bgp_memory.h
@@ -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 */
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 36ba65af1..991a92b85 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -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;
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index 3fbbd3354..f75b98905 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -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);
diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h
index 861da5740..652a6813e 100644
--- a/bgpd/bgp_nexthop.h
+++ b/bgpd/bgp_nexthop.h
@@ -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
{
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index f604b7658..6c97dc333 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -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 */
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 60c406775..3d65b4b0a 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -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 */
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index fea9ae6b1..efed71ebe 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -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
index 000000000..8bc9cb640
--- /dev/null
+++ b/bgpd/bgp_vnc_types.h
@@ -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 */
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 7089d140b..cb2415406 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -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. */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index f4a16d6ba..291483a86 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -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
index 000000000..863abde3a
--- /dev/null
+++ b/bgpd/bgpd.conf.vnc.sample
@@ -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
+
+
+
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index a6d3b61e5..2aa90e487 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -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
index 000000000..57fb04d23
--- /dev/null
+++ b/bgpd/rfapi/bgp_rfapi_cfg.c
@@ -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
index 000000000..cd3a28f60
--- /dev/null
+++ b/bgpd/rfapi/bgp_rfapi_cfg.h
@@ -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
index 000000000..3360ad89c
--- /dev/null
+++ b/bgpd/rfapi/rfapi.c
@@ -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
index 000000000..6e1baa60c
--- /dev/null
+++ b/bgpd/rfapi/rfapi.h
@@ -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
index 000000000..94b6dea8d
--- /dev/null
+++ b/bgpd/rfapi/rfapi_ap.c
@@ -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
index 000000000..3bb08a4b1
--- /dev/null
+++ b/bgpd/rfapi/rfapi_ap.h
@@ -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
index 000000000..451f5c20c
--- /dev/null
+++ b/bgpd/rfapi/rfapi_backend.h
@@ -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
index 000000000..c2d11820e
--- /dev/null
+++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c
@@ -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
index 000000000..9067cdf54
--- /dev/null
+++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.h
@@ -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
index 000000000..17fee2cc0
--- /dev/null
+++ b/bgpd/rfapi/rfapi_encap_tlv.c
@@ -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
index 000000000..9678655a6
--- /dev/null
+++ b/bgpd/rfapi/rfapi_encap_tlv.h
@@ -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
index 000000000..63c1079c4
--- /dev/null
+++ b/bgpd/rfapi/rfapi_import.c
@@ -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
index 000000000..9e88b52f5
--- /dev/null
+++ b/bgpd/rfapi/rfapi_import.h
@@ -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
index 000000000..4677d1fa6
--- /dev/null
+++ b/bgpd/rfapi/rfapi_monitor.c
@@ -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
index 000000000..b08a6e60c
--- /dev/null
+++ b/bgpd/rfapi/rfapi_monitor.h
@@ -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
index 000000000..835c2d2fa
--- /dev/null
+++ b/bgpd/rfapi/rfapi_nve_addr.c
@@ -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
index 000000000..2b2d2b50d
--- /dev/null
+++ b/bgpd/rfapi/rfapi_nve_addr.h
@@ -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
index 000000000..aca034b57
--- /dev/null
+++ b/bgpd/rfapi/rfapi_private.h
@@ -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
index 000000000..70acc14d3
--- /dev/null
+++ b/bgpd/rfapi/rfapi_rib.c
@@ -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
index 000000000..2a111946f
--- /dev/null
+++ b/bgpd/rfapi/rfapi_rib.h
@@ -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
index 000000000..315bac4fe
--- /dev/null
+++ b/bgpd/rfapi/rfapi_vty.c
@@ -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
index 000000000..08c8e1cf4
--- /dev/null
+++ b/bgpd/rfapi/rfapi_vty.h
@@ -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
index 000000000..8f4578159
--- /dev/null
+++ b/bgpd/rfapi/vnc_debug.c
@@ -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
index 000000000..9d4270651
--- /dev/null
+++ b/bgpd/rfapi/vnc_debug.h
@@ -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
index 000000000..f0a628922
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_bgp.c
@@ -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
index 000000000..ab2197d12
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_bgp.h
@@ -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
index 000000000..628778af3
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_bgp_p.h
@@ -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
index 000000000..5a96d8a52
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_table.c
@@ -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
index 000000000..231861af4
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_table.h
@@ -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
index 000000000..020cf181b
--- /dev/null
+++ b/bgpd/rfapi/vnc_import_bgp.c
@@ -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
index 000000000..acab0c62f
--- /dev/null
+++ b/bgpd/rfapi/vnc_import_bgp.h
@@ -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
index 000000000..4d37ce9cd
--- /dev/null
+++ b/bgpd/rfapi/vnc_import_bgp_p.h
@@ -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
index 000000000..54e8a2a3e
--- /dev/null
+++ b/bgpd/rfapi/vnc_zebra.c
@@ -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
index 000000000..226136ea6
--- /dev/null
+++ b/bgpd/rfapi/vnc_zebra.h
@@ -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
index 000000000..fc66a40f0
--- /dev/null
+++ b/bgpd/rfp-example/librfp/Makefile.am
@@ -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
index 000000000..4aa307821
--- /dev/null
+++ b/bgpd/rfp-example/librfp/rfp.h
@@ -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
index 000000000..b53355014
--- /dev/null
+++ b/bgpd/rfp-example/librfp/rfp_example.c
@@ -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
index 000000000..94192534f
--- /dev/null
+++ b/bgpd/rfp-example/librfp/rfp_internal.h
@@ -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
index 000000000..a1001e4ef
--- /dev/null
+++ b/bgpd/rfp-example/rfptest/Makefile.am
@@ -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
index 000000000..39b798e51
--- /dev/null
+++ b/bgpd/rfp-example/rfptest/rfptest.c
@@ -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
index 000000000..00effb867
--- /dev/null
+++ b/bgpd/rfp-example/rfptest/rfptest.h
@@ -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 */
diff --git a/configure.ac b/configure.ac
index d2a7a9da2..e6e1e5348 100755
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/doc/Makefile.am b/doc/Makefile.am
index cc4c7abc9..d5db6cf49 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -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
diff --git a/doc/bgpd.texi b/doc/bgpd.texi
index 3ef7c8f72..54bed102f 100644
--- a/doc/bgpd.texi
+++ b/doc/bgpd.texi
@@ -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
index 000000000..0da5bd1c8
--- /dev/null
+++ b/doc/fig-vnc-commercial-route-reflector.dia
@@ -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
index 000000000..ca8a24850
--- /dev/null
+++ b/doc/fig-vnc-commercial-route-reflector.png
Binary files differ
diff --git a/doc/fig-vnc-commercial-route-reflector.txt b/doc/fig-vnc-commercial-route-reflector.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/doc/fig-vnc-commercial-route-reflector.txt
diff --git a/doc/fig-vnc-gw-rr.dia b/doc/fig-vnc-gw-rr.dia
new file mode 100644
index 000000000..dab27f700
--- /dev/null
+++ b/doc/fig-vnc-gw-rr.dia
@@ -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
index 000000000..7ae0630f6
--- /dev/null
+++ b/doc/fig-vnc-gw-rr.png
Binary files differ
diff --git a/doc/fig-vnc-gw-rr.txt b/doc/fig-vnc-gw-rr.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/doc/fig-vnc-gw-rr.txt
diff --git a/doc/fig-vnc-gw.dia b/doc/fig-vnc-gw.dia
new file mode 100644
index 000000000..8270e208b
--- /dev/null
+++ b/doc/fig-vnc-gw.dia
@@ -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
index 000000000..df8f23f43
--- /dev/null
+++ b/doc/fig-vnc-gw.png
Binary files differ
diff --git a/doc/fig-vnc-gw.txt b/doc/fig-vnc-gw.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/doc/fig-vnc-gw.txt
diff --git a/doc/fig-vnc-mesh.dia b/doc/fig-vnc-mesh.dia
new file mode 100644
index 000000000..a8f702f76
--- /dev/null
+++ b/doc/fig-vnc-mesh.dia
@@ -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
index 000000000..fa0762d16
--- /dev/null
+++ b/doc/fig-vnc-mesh.png
Binary files differ
diff --git a/doc/fig-vnc-mesh.txt b/doc/fig-vnc-mesh.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/doc/fig-vnc-mesh.txt
diff --git a/doc/fig-vnc-quagga-route-reflector.dia b/doc/fig-vnc-quagga-route-reflector.dia
new file mode 100644
index 000000000..634f0b17f
--- /dev/null
+++ b/doc/fig-vnc-quagga-route-reflector.dia
@@ -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
index 000000000..477052184
--- /dev/null
+++ b/doc/fig-vnc-quagga-route-reflector.png
Binary files differ
diff --git a/doc/fig-vnc-quagga-route-reflector.txt b/doc/fig-vnc-quagga-route-reflector.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/doc/fig-vnc-quagga-route-reflector.txt
diff --git a/doc/fig-vnc-redundant-route-reflectors.dia b/doc/fig-vnc-redundant-route-reflectors.dia
new file mode 100644
index 000000000..4065b8ba1
--- /dev/null
+++ b/doc/fig-vnc-redundant-route-reflectors.dia
@@ -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
index 000000000..06a27b657
--- /dev/null
+++ b/doc/fig-vnc-redundant-route-reflectors.png
Binary files differ
diff --git a/doc/fig-vnc-redundant-route-reflectors.txt b/doc/fig-vnc-redundant-route-reflectors.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/doc/fig-vnc-redundant-route-reflectors.txt
diff --git a/doc/ospfd.texi b/doc/ospfd.texi
index 96dffe000..7b9df8e78 100644
--- a/doc/ospfd.texi
+++ b/doc/ospfd.texi
@@ -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
diff --git a/doc/quagga.texi b/doc/quagga.texi
index 6831b30cd..13b885b69 100644
--- a/doc/quagga.texi
+++ b/doc/quagga.texi
@@ -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
diff --git a/doc/routemap.texi b/doc/routemap.texi
index b3ef7ca76..33062a7f6 100644
--- a/doc/routemap.texi
+++ b/doc/routemap.texi
@@ -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
index 000000000..341cbfcce
--- /dev/null
+++ b/doc/vnc.texi
@@ -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
+
diff --git a/lib/command.c b/lib/command.c
index bf8b1b1d3..fa3a782a9 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -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;
}
diff --git a/lib/command.h b/lib/command.h
index 7a4c53a61..593274311 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -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 */
diff --git a/lib/log.c b/lib/log.c
index fb7b33dcf..49c69efc8 100644
--- 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;
}
diff --git a/lib/log.h b/lib/log.h
index cb8fac78c..91cab3f96 100644
--- 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 */
diff --git a/lib/prefix.c b/lib/prefix.c
index 34bb1a493..112dae582 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -240,6 +240,8 @@ afi2str(afi_t afi)
return "IPv6";
case AFI_ETHER:
return "ethernet";
+ case AFI_MAX:
+ return "bad-value";
default:
break;
}
diff --git a/lib/route_types.txt b/lib/route_types.txt
index 0ac442d85..698e8b9e5 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -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)"
diff --git a/lib/vty.c b/lib/vty.c
index 3f1350044..14ef7e6e4 100644
--- 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:
diff --git a/lib/zebra.h b/lib/zebra.h
index 599daa350..e625b4051 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -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
diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in
index 40c30e40a..4c35a8bf6 100644
--- a/redhat/quagga.spec.in
+++ b/redhat/quagga.spec.in
@@ -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
@@ -236,6 +237,11 @@ developing OSPF-API and quagga applications.
%else
--disable-watchquagga \
%endif
+%if %{with_bgp_vnc}
+ --enable-bgp-vnc \
+%else
+ --disable-bgp-vnc \
+%endif
--enable-gcc-rdynamic \
--enable-isisd=yes \
--enable-systemd=yes \
diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c
index ad39f75d6..23f2adf82 100644
--- a/ripd/rip_zebra.c
+++ b/ripd/rip_zebra.c
@@ -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}
};
diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c
index 637e8f5a2..c4ed0c52c 100644
--- a/ripngd/ripng_zebra.c
+++ b/ripngd/ripng_zebra.c
@@ -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}
};
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 16c9e4c3d..0014fa34d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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@
diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
index 71fb0ae53..58ffdfca2 100644
--- a/vtysh/Makefile.am
+++ b/vtysh/Makefile.am
@@ -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
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index b4884b968..7c5dec9a5 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -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;
}
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 9dc2eca0b..4f64283a0 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -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,
@@ -3072,6 +3147,11 @@ vtysh_init_vty (void)
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 */
install_node (&ripng_node, NULL);
@@ -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);
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index bf6215c40..eb5849731 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -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
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 25f028af6..135cd88a1 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -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;