diff options
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, <, 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 Binary files differnew file mode 100644 index 000000000..ca8a24850 --- /dev/null +++ b/doc/fig-vnc-commercial-route-reflector.png 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 Binary files differnew file mode 100644 index 000000000..7ae0630f6 --- /dev/null +++ b/doc/fig-vnc-gw-rr.png 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 Binary files differnew file mode 100644 index 000000000..df8f23f43 --- /dev/null +++ b/doc/fig-vnc-gw.png 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 Binary files differnew file mode 100644 index 000000000..fa0762d16 --- /dev/null +++ b/doc/fig-vnc-mesh.png 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 Binary files differnew file mode 100644 index 000000000..477052184 --- /dev/null +++ b/doc/fig-vnc-quagga-route-reflector.png 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 Binary files differnew file mode 100644 index 000000000..06a27b657 --- /dev/null +++ b/doc/fig-vnc-redundant-route-reflectors.png 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 */ @@ -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; } @@ -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)" @@ -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; |