#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_updgrp.h"
#include "bgpd/bgp_encap_types.h"
+#if ENABLE_BGP_VNC
+# include "bgpd/rfapi/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 [] =
{ 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);
static void
cluster_finish (void)
{
+ hash_clean (cluster_hash, (void (*)(void *))cluster_free);
hash_free (cluster_hash);
cluster_hash = NULL;
}
+static struct hash *encap_hash = NULL;
+#if ENABLE_BGP_VNC
+static struct hash *vnc_hash = NULL;
+#endif
+
struct bgp_attr_encap_subtlv *
encap_tlv_dup(struct bgp_attr_encap_subtlv *orig)
{
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
}
/*
struct bgp_attr_encap_subtlv *p;
struct bgp_attr_encap_subtlv *q;
- if (!h1 && !h2)
- return 1;
- if (h1 && !h2)
- return 0;
- if (!h1 && h2)
- return 0;
if (h1 == h2)
return 1;
+ if (h1 == NULL || h2 == NULL)
+ return 0;
for (p = h1; p; p = p->next) {
for (q = h2; q; q = q->next) {
return 1;
}
+static void *
+encap_hash_alloc (void *p)
+{
+ /* Encap structure is already allocated. */
+ return p;
+}
+
+typedef enum
+{
+ ENCAP_SUBTLV_TYPE,
+#if ENABLE_BGP_VNC
+ VNC_SUBTLV_TYPE
+#endif
+} encap_subtlv_type;
+
+static struct bgp_attr_encap_subtlv *
+encap_intern (struct bgp_attr_encap_subtlv *encap, encap_subtlv_type type)
+{
+ struct bgp_attr_encap_subtlv *find;
+ struct hash *hash = encap_hash;
+#if ENABLE_BGP_VNC
+ if (type == VNC_SUBTLV_TYPE)
+ hash = vnc_hash;
+#endif
+
+ find = hash_get (hash, encap, encap_hash_alloc);
+ if (find != encap)
+ encap_free (encap);
+ find->refcnt++;
+
+ return find;
+}
+
+static void
+encap_unintern (struct bgp_attr_encap_subtlv **encapp, encap_subtlv_type type)
+{
+ struct bgp_attr_encap_subtlv *encap = *encapp;
+ if (encap->refcnt)
+ encap->refcnt--;
+
+ if (encap->refcnt == 0)
+ {
+ struct hash *hash = encap_hash;
+#if ENABLE_BGP_VNC
+ if (type == VNC_SUBTLV_TYPE)
+ hash = vnc_hash;
+#endif
+ hash_release (hash, encap);
+ encap_free (encap);
+ *encapp = NULL;
+ }
+}
+
+static unsigned int
+encap_hash_key_make (void *p)
+{
+ const struct bgp_attr_encap_subtlv * encap = p;
+
+ return jhash(encap->value, encap->length, 0);
+}
+
+static int
+encap_hash_cmp (const void *p1, const void *p2)
+{
+ return encap_same((struct bgp_attr_encap_subtlv *)p1,
+ (struct bgp_attr_encap_subtlv *)p2);
+}
+
+static void
+encap_init (void)
+{
+ encap_hash = hash_create (encap_hash_key_make, encap_hash_cmp);
+#if ENABLE_BGP_VNC
+ vnc_hash = hash_create (encap_hash_key_make, encap_hash_cmp);
+#endif
+}
+
+static void
+encap_finish (void)
+{
+ hash_clean (encap_hash, (void (*)(void *))encap_free);
+ hash_free (encap_hash);
+ encap_hash = NULL;
+#if ENABLE_BGP_VNC
+ hash_clean (vnc_hash, (void (*)(void *))encap_free);
+ hash_free (vnc_hash);
+ vnc_hash = NULL;
+#endif
+}
+
/* Unknown transit attribute. */
static struct hash *transit_hash;
static void
transit_finish (void)
{
+ hash_clean (transit_hash, (void (*)(void *))transit_free);
hash_free (transit_hash);
transit_hash = NULL;
}
{
if (attr->extra)
{
- if (attr->extra->encap_subtlvs) {
- encap_free(attr->extra->encap_subtlvs);
- attr->extra->encap_subtlvs = NULL;
- }
XFREE (MTYPE_ATTR_EXTRA, attr->extra);
attr->extra = NULL;
}
memset(new->extra, 0, sizeof(struct attr_extra));
if (orig->extra) {
*new->extra = *orig->extra;
- if (orig->extra->encap_subtlvs) {
- new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs);
- }
}
}
else if (orig->extra)
{
new->extra = bgp_attr_extra_new();
*new->extra = *orig->extra;
- if (orig->extra->encap_subtlvs) {
- new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs);
- }
}
}
new->extra->cluster = cluster_dup(orig->extra->cluster);
if (orig->extra->transit)
new->extra->transit = transit_dup(orig->extra->transit);
+ 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
}
}
cluster_free(attr->extra->cluster);
if (attr->extra->transit)
transit_free(attr->extra->transit);
+ if (attr->extra->encap_subtlvs)
+ encap_free(attr->extra->encap_subtlvs);
+#if ENABLE_BGP_VNC
+ if (attr->extra->vnc_subtlvs)
+ encap_free(attr->extra->vnc_subtlvs);
+#endif
}
}
MIX(cluster_hash_key_make (extra->cluster));
if (extra->transit)
MIX(transit_hash_key_make (extra->transit));
-
+ if (extra->encap_subtlvs)
+ MIX(encap_hash_key_make (extra->encap_subtlvs));
+#if ENABLE_BGP_VNC
+ if (extra->vnc_subtlvs)
+ MIX(encap_hash_key_make (extra->vnc_subtlvs));
+#endif
#ifdef HAVE_IPV6
MIX(extra->mp_nexthop_len);
key = jhash(extra->mp_nexthop_global.s6_addr, IPV6_MAX_BYTELEN, key);
&& 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)
attrhash = hash_create (attrhash_key_make, attrhash_cmp);
}
+/*
+ * special for hash_clean below
+ */
+static void
+attr_vfree (void *attr)
+{
+ bgp_attr_extra_free ((struct attr *)attr);
+ XFREE (MTYPE_ATTR, attr);
+}
+
static void
attrhash_finish (void)
{
+ hash_clean(attrhash, attr_vfree);
hash_free (attrhash);
attrhash = NULL;
}
{
attr->extra = bgp_attr_extra_new ();
*attr->extra = *val->extra;
-
- if (attr->extra->encap_subtlvs) {
- attr->extra->encap_subtlvs = encap_tlv_dup(attr->extra->encap_subtlvs);
+ if (val->extra->encap_subtlvs) {
+ val->extra->encap_subtlvs = NULL;
}
+#if ENABLE_BGP_VNC
+ if (val->extra->vnc_subtlvs) {
+ val->extra->vnc_subtlvs = NULL;
+ }
+#endif
}
attr->refcnt = 0;
return attr;
else
attre->transit->refcnt++;
}
+ if (attre->encap_subtlvs)
+ {
+ if (! attre->encap_subtlvs->refcnt)
+ attre->encap_subtlvs = encap_intern (attre->encap_subtlvs, ENCAP_SUBTLV_TYPE);
+ else
+ attre->encap_subtlvs->refcnt++;
+ }
+#if ENABLE_BGP_VNC
+ if (attre->vnc_subtlvs)
+ {
+ if (! attre->vnc_subtlvs->refcnt)
+ attre->vnc_subtlvs = encap_intern (attre->vnc_subtlvs, VNC_SUBTLV_TYPE);
+ else
+ attre->vnc_subtlvs->refcnt++;
+ }
+#endif
}
find = (struct attr *) hash_get (attrhash, attr, bgp_attr_hash_alloc);
find->refcnt++;
-
+
return find;
}
if (attre->transit)
attre->transit->refcnt++;
+
+ if (attre->encap_subtlvs)
+ attre->encap_subtlvs->refcnt++;
+
+#if ENABLE_BGP_VNC
+ if (attre->vnc_subtlvs)
+ attre->vnc_subtlvs->refcnt++;
+#endif
}
attr->refcnt++;
return attr;
if (attr->extra->transit)
transit_unintern (attr->extra->transit);
+
+ if (attr->extra->encap_subtlvs)
+ encap_unintern (&attr->extra->encap_subtlvs, ENCAP_SUBTLV_TYPE);
+
+#if ENABLE_BGP_VNC
+ if (attr->extra->vnc_subtlvs)
+ encap_unintern (&attr->extra->vnc_subtlvs, VNC_SUBTLV_TYPE);
+#endif
}
}
bgp_attr_flush (struct attr *attr)
{
if (attr->aspath && ! attr->aspath->refcnt)
- aspath_free (attr->aspath);
+ {
+ aspath_free (attr->aspath);
+ attr->aspath = NULL;
+ }
if (attr->community && ! attr->community->refcnt)
- community_free (attr->community);
+ {
+ community_free (attr->community);
+ attr->community = NULL;
+ }
if (attr->extra)
{
struct attr_extra *attre = attr->extra;
if (attre->ecommunity && ! attre->ecommunity->refcnt)
ecommunity_free (&attre->ecommunity);
if (attre->cluster && ! attre->cluster->refcnt)
- cluster_free (attre->cluster);
+ {
+ cluster_free (attre->cluster);
+ attre->cluster = NULL;
+ }
if (attre->transit && ! attre->transit->refcnt)
- transit_free (attre->transit);
- encap_free(attre->encap_subtlvs);
- attre->encap_subtlvs = NULL;
+ {
+ transit_free (attre->transit);
+ attre->transit = NULL;
+ }
+ if (attre->encap_subtlvs && ! attre->encap_subtlvs->refcnt)
+ {
+ encap_free(attre->encap_subtlvs);
+ attre->encap_subtlvs = NULL;
+ }
+#if ENABLE_BGP_VNC
+ if (attre->vnc_subtlvs && ! attre->vnc_subtlvs->refcnt)
+ {
+ encap_free(attre->vnc_subtlvs);
+ attre->vnc_subtlvs = NULL;
+ }
+#endif
}
}
[BGP_ATTR_AS4_PATH] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_AS4_AGGREGATOR] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
};
-static const size_t attr_flags_values_max =
- sizeof (attr_flags_values) / sizeof (attr_flags_values[0]);
+static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
static int
bgp_attr_flag_invalid (struct bgp_attr_parser_args *args)
safi_t safi;
bgp_size_t nlri_len;
size_t start;
- int ret;
- int num_mp_pfx = 0;
struct stream *s;
struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
__func__, peer->host);
return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
}
-
- if (safi != SAFI_MPLS_LABELED_VPN)
- {
- ret = bgp_nlri_sanity_check (peer, afi, safi, stream_pnt (s),
- nlri_len, &num_mp_pfx);
- if (ret < 0)
- {
- zlog_info ("%s: (%s) NLRI doesn't pass sanity check",
- __func__, peer->host);
- return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
- }
- }
mp_update->afi = afi;
mp_update->safi = safi;
afi_t afi;
safi_t safi;
u_int16_t withdraw_len;
- int ret;
- int num_mp_pfx = 0;
struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
const bgp_size_t length = args->length;
withdraw_len = length - BGP_MP_UNREACH_MIN_SIZE;
- if (safi != SAFI_MPLS_LABELED_VPN)
- {
- ret = bgp_nlri_sanity_check (peer, afi, safi, stream_pnt (s),
- withdraw_len, &num_mp_pfx);
- if (ret < 0)
- return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
- }
-
mp_withdraw->afi = afi;
mp_withdraw->safi = safi;
mp_withdraw->nlri = stream_pnt (s);
bgp_size_t total;
struct attr_extra *attre = NULL;
struct bgp_attr_encap_subtlv *stlv_last = NULL;
- uint16_t tunneltype;
+ uint16_t tunneltype = 0;
total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
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) {
} 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;
stlv_last = tlv;
}
- if (attre && (BGP_ATTR_ENCAP == type)) {
- attre->encap_tunneltype = tunneltype;
+ if (BGP_ATTR_ENCAP == type) {
+ if (!attre)
+ attre = bgp_attr_extra_get(attr);
+ attre->encap_tunneltype = tunneltype;
}
if (length) {
attr_endp = BGP_INPUT_PNT (peer) + length;
if (attr_endp > endp)
- {
- zlog_warn ("%s: BGP type %d length %d is too large, attribute total length is %d. attr_endp is %p. endp is %p", peer->host, type, length, size, attr_endp, endp);
- bgp_notify_send_with_data (peer,
- BGP_NOTIFY_UPDATE_ERR,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, attr_endp - startp);
- return BGP_ATTR_PARSE_ERROR;
- }
+ {
+ zlog_warn("%s: BGP type %d length %d is too large, attribute total length is %d. attr_endp is %p. endp is %p",
+ peer->host, type, length, size, attr_endp, endp);
+ /*
+ * RFC 4271 6.3
+ * If any recognized attribute has an Attribute Length that conflicts
+ * with the expected length (based on the attribute type code), then
+ * the Error Subcode MUST be set to Attribute Length Error. The Data
+ * field MUST contain the erroneous attribute (type, length, and
+ * value).
+ * ----------
+ * We do not currently have a good way to determine the length of the
+ * attribute independent of the length received in the message.
+ * Instead we send the minimum between the amount of data we have and
+ * the amount specified by the attribute length field.
+ *
+ * Instead of directly passing in the packet buffer and offset we use
+ * the stream_get* functions to read into a stack buffer, since they
+ * perform bounds checking and we are working with untrusted data.
+ */
+ unsigned char ndata[BGP_MAX_PACKET_SIZE];
+ memset(ndata, 0x00, sizeof(ndata));
+ size_t lfl = CHECK_FLAG(flag, BGP_ATTR_FLAG_EXTLEN) ? 2 : 1;
+ /* Rewind to end of flag field */
+ stream_forward_getp(BGP_INPUT(peer), -(1 + lfl));
+ /* Type */
+ stream_get(&ndata[0], BGP_INPUT(peer), 1);
+ /* Length */
+ stream_get(&ndata[1], BGP_INPUT(peer), lfl);
+ /* Value */
+ size_t atl = attr_endp - startp;
+ size_t ndl = MIN(atl, STREAM_READABLE(BGP_INPUT(peer)));
+ stream_get(&ndata[lfl + 1], BGP_INPUT(peer), ndl);
+
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, ndata, ndl + lfl + 1);
+
+ return BGP_ATTR_PARSE_ERROR;
+ }
struct bgp_attr_parser_args attr_args = {
.peer = peer,
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;
if (ret != BGP_ATTR_PARSE_PROCEED)
return ret;
}
-
- /* Finally intern unknown attribute. */
- if (attr->extra && attr->extra->transit)
- attr->extra->transit = transit_intern (attr->extra->transit);
+ if (attr->extra)
+ {
+ /* Finally intern unknown attribute. */
+ if (attr->extra->transit)
+ attr->extra->transit = transit_intern (attr->extra->transit);
+ if (attr->extra->encap_subtlvs)
+ attr->extra->encap_subtlvs = encap_intern (attr->extra->encap_subtlvs, ENCAP_SUBTLV_TYPE);
+#if ENABLE_BGP_VNC
+ if (attr->extra->vnc_subtlvs)
+ attr->extra->vnc_subtlvs = encap_intern (attr->extra->vnc_subtlvs, VNC_SUBTLV_TYPE);
+#endif
+ }
return BGP_ATTR_PARSE_PROCEED;
}
stream_putw (s, afi);
stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi);
+ if (nh_afi == AFI_MAX)
+ nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->extra->mp_nexthop_len);
/* Nexthop */
switch (nh_afi)
{
}
/*
- * 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(
struct bgp_attr_encap_subtlv *st;
const char *attrname;
- if (!attr || !attr->extra)
+ if (!attr || !attr->extra ||
+ (attrtype == BGP_ATTR_ENCAP &&
+ (!attr->extra->encap_tunneltype ||
+ attr->extra->encap_tunneltype == BGP_ENCAP_TYPE_MPLS)))
return;
switch (attrtype) {
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);
}
-
- /* if no tlvs, don't make attr */
- if (subtlvs == NULL)
- return;
-
/* compute attr length */
for (st = subtlvs; st; st = st->next) {
attrlenfield += (attrhdrlen + st->length);
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);
}
size_t mpattrlen_pos = 0;
mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi,
- (peer_cap_enhe(peer) ? AFI_IP6 : afi),
+ (peer_cap_enhe(peer) ? AFI_IP6 :
+ AFI_MAX), /* get from NH */
vecarr, attr);
bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag,
addpath_encode, addpath_tx_id);
{
/* 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. */
ecommunity_init ();
cluster_init ();
transit_init ();
+ encap_init ();
}
void
ecommunity_finish ();
cluster_finish ();
transit_finish ();
+ encap_finish ();
}
/* Make attribute packet. */