]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_attr.c
bgpd: fix mishandled attribute length
[mirror_frr.git] / bgpd / bgp_attr.c
index 0d82aba04edc7164e14e9cf89d9fbca85db01582..a7f2d9084a9adea15823f169fb5e418da0d73727 100644 (file)
@@ -29,6 +29,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "log.h"
 #include "hash.h"
 #include "jhash.h"
+#include "queue.h"
+#include "table.h"
+#include "filter.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_attr.h"
@@ -38,7 +41,14 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_debug.h"
 #include "bgpd/bgp_packet.h"
 #include "bgpd/bgp_ecommunity.h"
-\f
+#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 [] = 
 {
@@ -61,8 +71,12 @@ static const struct message attr_str [] =
   { BGP_ATTR_AS4_PATH,         "AS4_PATH" }, 
   { 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 = sizeof(attr_str)/sizeof(attr_str[0]);
+static const int attr_str_max = array_size(attr_str);
 
 static const struct message attr_flag_str[] =
 {
@@ -72,15 +86,13 @@ static const struct message attr_flag_str[] =
   /* bgp_attr_flags_diagnose() relies on this bit being last in this list */
   { BGP_ATTR_FLAG_EXTLEN,   "Extended Length" },
 };
-static const size_t attr_flag_str_max =
-  sizeof (attr_flag_str) / sizeof (attr_flag_str[0]);
-\f
+
 static struct hash *cluster_hash;
 
 static void *
 cluster_hash_alloc (void *p)
 {
-  struct cluster_list * val = (struct cluster_list *) p;
+  const struct cluster_list *val = (const struct cluster_list *) p;
   struct cluster_list *cluster;
 
   cluster = XMALLOC (MTYPE_CLUSTER, sizeof (struct cluster_list));
@@ -151,7 +163,6 @@ cluster_free (struct cluster_list *cluster)
   XFREE (MTYPE_CLUSTER, cluster);
 }
 
-#if 0
 static struct cluster_list *
 cluster_dup (struct cluster_list *cluster)
 {
@@ -170,7 +181,6 @@ cluster_dup (struct cluster_list *cluster)
   
   return new;
 }
-#endif
 
 static struct cluster_list *
 cluster_intern (struct cluster_list *cluster)
@@ -205,10 +215,207 @@ cluster_init (void)
 static void
 cluster_finish (void)
 {
+  hash_clean (cluster_hash, (void (*)(void *))cluster_free);
   hash_free (cluster_hash);
   cluster_hash = NULL;
 }
-\f
+
+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)
+{
+    struct bgp_attr_encap_subtlv *new;
+    struct bgp_attr_encap_subtlv *tail;
+    struct bgp_attr_encap_subtlv *p;
+
+    for (p = orig, tail = new = NULL; p; p = p->next) {
+       int size = sizeof(struct bgp_attr_encap_subtlv) - 1 + p->length;
+       if (tail) {
+           tail->next = XCALLOC(MTYPE_ENCAP_TLV, size);
+           tail = tail->next;
+       } else {
+           tail = new = XCALLOC(MTYPE_ENCAP_TLV, size);
+       }
+       assert(tail);
+       memcpy(tail, p, size);
+       tail->next = NULL;
+    }
+
+    return new;
+}
+
+static void
+encap_free(struct bgp_attr_encap_subtlv *p)
+{
+    struct bgp_attr_encap_subtlv *next;
+    while (p) {
+        next    = p->next;
+        p->next = NULL;
+        XFREE(MTYPE_ENCAP_TLV, p);
+        p       = next;
+    }
+}
+
+void
+bgp_attr_flush_encap(struct attr *attr)
+{
+    if (!attr || !attr->extra)
+       return;
+
+    if (attr->extra->encap_subtlvs) {
+       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
+}
+
+/*
+ * Compare encap sub-tlv chains
+ *
+ *     1 = equivalent
+ *     0 = not equivalent
+ *
+ * This algorithm could be made faster if needed
+ */
+static int
+encap_same(struct bgp_attr_encap_subtlv *h1, struct bgp_attr_encap_subtlv *h2)
+{
+    struct bgp_attr_encap_subtlv *p;
+    struct bgp_attr_encap_subtlv *q;
+
+    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) {
+           if ((p->type == q->type) &&
+               (p->length == q->length) &&
+               !memcmp(p->value, q->value, p->length)) {
+
+               break;
+           }
+       }
+       if (!q)
+           return 0;
+    }
+
+    for (p = h2; p; p = p->next) {
+       for (q = h1; q; q = q->next) {
+           if ((p->type == q->type) &&
+               (p->length == q->length) &&
+               !memcmp(p->value, q->value, p->length)) {
+
+               break;
+           }
+       }
+       if (!q)
+           return 0;
+    }
+
+    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;
 
@@ -220,6 +427,23 @@ transit_free (struct transit *transit)
   XFREE (MTYPE_TRANSIT, transit);
 }
 
+static struct transit *
+transit_dup (struct transit *transit)
+{
+  struct transit *new;
+
+  new = XCALLOC (MTYPE_TRANSIT, sizeof (struct transit));
+  new->length = transit->length;
+  if (new->length)
+    {
+      new->val = XMALLOC (MTYPE_TRANSIT_VAL, transit->length);
+      memcpy (new->val, transit->val, transit->length);
+    }
+  else
+    new->val = NULL;
+
+  return new;
+}
 
 static void *
 transit_hash_alloc (void *p)
@@ -281,10 +505,11 @@ transit_init (void)
 static void
 transit_finish (void)
 {
+  hash_clean (transit_hash, (void (*)(void *))transit_free);
   hash_free (transit_hash);
   transit_hash = NULL;
 }
-\f
+
 /* Attribute hash routines. */
 static struct hash *attrhash;
 
@@ -319,14 +544,84 @@ bgp_attr_extra_get (struct attr *attr)
 void
 bgp_attr_dup (struct attr *new, struct attr *orig)
 {
+  struct attr_extra *extra = new->extra;
+
   *new = *orig;
-  if (orig->extra)
+  /* if caller provided attr_extra space, use it in any case.
+   *
+   * This is neccesary even if orig->extra equals NULL, because otherwise
+   * memory may be later allocated on the heap by bgp_attr_extra_get.
+   *
+   * That memory would eventually be leaked, because the caller must not
+   * call bgp_attr_extra_free if he provided attr_extra on the stack.
+   */
+  if (extra)
+    {
+      new->extra = extra;
+      memset(new->extra, 0, sizeof(struct attr_extra));
+      if (orig->extra) {
+        *new->extra = *orig->extra;
+      }
+    }
+  else if (orig->extra)
     {
       new->extra = bgp_attr_extra_new();
       *new->extra = *orig->extra;
     }
 }
 
+void
+bgp_attr_deep_dup (struct attr *new, struct attr *orig)
+{
+  if (orig->aspath)
+    new->aspath = aspath_dup(orig->aspath);
+
+  if (orig->community)
+    new->community = community_dup(orig->community);
+
+  if (orig->extra)
+    {
+      if (orig->extra->ecommunity)
+        new->extra->ecommunity = ecommunity_dup(orig->extra->ecommunity);
+      if (orig->extra->cluster)
+        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
+    }
+}
+
+void
+bgp_attr_deep_free (struct attr *attr)
+{
+  if (attr->aspath)
+    aspath_free(attr->aspath);
+
+  if (attr->community)
+    community_free(attr->community);
+
+  if (attr->extra)
+    {
+      if (attr->extra->ecommunity)
+        ecommunity_free(&attr->extra->ecommunity);
+      if (attr->extra->cluster)
+        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
+    }
+}
+
 unsigned long int
 attr_count (void)
 {
@@ -342,7 +637,8 @@ attr_unknown_count (void)
 unsigned int
 attrhash_key_make (void *p)
 {
-  const struct attr * attr = (struct attr *) p;
+  const struct attr *attr = (struct attr *) p;
+  const struct attr_extra *extra = attr->extra;
   uint32_t key = 0;
 #define MIX(val)       key = jhash_1word(val, key)
 
@@ -356,12 +652,14 @@ attrhash_key_make (void *p)
   key += attr->med;
   key += attr->local_pref;
   
-  if (attr->extra)
+  if (extra)
     {
-      MIX(attr->extra->aggregator_as);
-      MIX(attr->extra->aggregator_addr.s_addr);
-      MIX(attr->extra->weight);
-      MIX(attr->extra->mp_nexthop_global_in.s_addr);
+      MIX(extra->aggregator_as);
+      MIX(extra->aggregator_addr.s_addr);
+      MIX(extra->weight);
+      MIX(extra->mp_nexthop_global_in.s_addr);
+      MIX(extra->originator_id.s_addr);
+      MIX(extra->tag);
     }
   
   if (attr->aspath)
@@ -369,19 +667,24 @@ attrhash_key_make (void *p)
   if (attr->community)
     MIX(community_hash_make (attr->community));
   
-  if (attr->extra)
-    {
-      if (attr->extra->ecommunity)
-        MIX(ecommunity_hash_make (attr->extra->ecommunity));
-      if (attr->extra->cluster)
-        MIX(cluster_hash_key_make (attr->extra->cluster));
-      if (attr->extra->transit)
-        MIX(transit_hash_key_make (attr->extra->transit));
-
+  if (extra)
+    {
+      if (extra->ecommunity)
+        MIX(ecommunity_hash_make (extra->ecommunity));
+      if (extra->cluster)
+        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(attr->extra->mp_nexthop_len);
-      key = jhash(attr->extra->mp_nexthop_global.s6_addr, 16, key);
-      key = jhash(attr->extra->mp_nexthop_local.s6_addr, 16, key);
+      MIX(extra->mp_nexthop_len);
+      key = jhash(extra->mp_nexthop_global.s6_addr, IPV6_MAX_BYTELEN, key);
+      key = jhash(extra->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key);
 #endif /* HAVE_IPV6 */
     }
 
@@ -400,7 +703,8 @@ attrhash_cmp (const void *p1, const void *p2)
       && attr1->aspath == attr2->aspath
       && attr1->community == attr2->community
       && attr1->med == attr2->med
-      && attr1->local_pref == attr2->local_pref)
+      && attr1->local_pref == attr2->local_pref
+      && attr1->rmap_change_flags == attr2->rmap_change_flags)
     {
       const struct attr_extra *ae1 = attr1->extra;
       const struct attr_extra *ae2 = attr2->extra;
@@ -409,6 +713,7 @@ attrhash_cmp (const void *p1, const void *p2)
           && ae1->aggregator_as == ae2->aggregator_as
           && ae1->aggregator_addr.s_addr == ae2->aggregator_addr.s_addr
           && ae1->weight == ae2->weight
+          && ae1->tag == ae2->tag
 #ifdef HAVE_IPV6
           && ae1->mp_nexthop_len == ae2->mp_nexthop_len
           && IPV6_ADDR_SAME (&ae1->mp_nexthop_global, &ae2->mp_nexthop_global)
@@ -417,7 +722,13 @@ attrhash_cmp (const void *p1, const void *p2)
           && IPV4_ADDR_SAME (&ae1->mp_nexthop_global_in, &ae2->mp_nexthop_global_in)
           && ae1->ecommunity == ae2->ecommunity
           && ae1->cluster == ae2->cluster
-          && ae1->transit == ae2->transit)
+          && 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)
         return 0;
@@ -434,9 +745,20 @@ attrhash_init (void)
   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;
 }
@@ -462,7 +784,7 @@ attr_show_all (struct vty *vty)
 static void *
 bgp_attr_hash_alloc (void *p)
 {
-  struct attr * val = (struct attr *) p;
+  const struct attr * val = (const struct attr *) p;
   struct attr *attr;
 
   attr = XMALLOC (MTYPE_ATTR, sizeof (struct attr));
@@ -471,6 +793,14 @@ bgp_attr_hash_alloc (void *p)
     {
       attr->extra = bgp_attr_extra_new ();
       *attr->extra = *val->extra;
+      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;
@@ -523,14 +853,72 @@ bgp_attr_intern (struct attr *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;
 }
 
+/**
+ * Increment the refcount on various structures that attr holds.
+ * Note on usage: call _only_ when the 'attr' object has already
+ * been 'intern'ed and exists in 'attrhash' table. The function
+ * serves to hold a reference to that (real) object.
+ * Note also that the caller can safely call bgp_attr_unintern()
+ * after calling bgp_attr_refcount(). That would release the
+ * reference and could result in a free() of the attr object.
+ */
+struct attr *
+bgp_attr_refcount (struct attr *attr)
+{
+  /* Intern referenced strucutre. */
+  if (attr->aspath)
+    attr->aspath->refcnt++;
+
+  if (attr->community)
+    attr->community->refcnt++;
+
+  if (attr->extra)
+    {
+      struct attr_extra *attre = attr->extra;
+      if (attre->ecommunity)
+       attre->ecommunity->refcnt++;
+
+      if (attre->cluster)
+       attre->cluster->refcnt++;
+
+      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;
+}
 
 /* Make network statement's attribute. */
 struct attr *
@@ -544,6 +932,7 @@ bgp_attr_default_set (struct attr *attr, u_char origin)
   attr->aspath = aspath_empty ();
   attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
   attr->extra->weight = BGP_ATTR_DEFAULT_WEIGHT;
+  attr->extra->tag = 0;
   attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
 #ifdef HAVE_IPV6
   attr->extra->mp_nexthop_len = IPV6_MAX_BYTELEN;
@@ -559,10 +948,10 @@ bgp_attr_default_intern (u_char origin)
 {
   struct attr attr;
   struct attr *new;
-  
+
   memset (&attr, 0, sizeof (struct attr));
   bgp_attr_extra_get (&attr);
-  
+
   bgp_attr_default_set(&attr, origin);
 
   new = bgp_attr_intern (&attr);
@@ -572,18 +961,21 @@ bgp_attr_default_intern (u_char origin)
   return new;
 }
 
+/* Create the attributes for an aggregate */
 struct attr *
 bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin,
                           struct aspath *aspath,
-                          struct community *community, int as_set)
+                          struct community *community, int as_set,
+                          u_char atomic_aggregate)
 {
   struct attr attr;
   struct attr *new;
-  struct attr_extra *attre;
+  struct attr_extra attre;
 
   memset (&attr, 0, sizeof (struct attr));
-  attre = bgp_attr_extra_get (&attr);
-  
+  memset (&attre, 0, sizeof (struct attr_extra));
+  attr.extra = &attre;
+
   /* Origin attribute. */
   attr.origin = origin;
   attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
@@ -604,22 +996,21 @@ bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin,
       attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
     }
 
-  attre->weight = BGP_ATTR_DEFAULT_WEIGHT;
+  attre.weight = BGP_ATTR_DEFAULT_WEIGHT;
 #ifdef HAVE_IPV6
-  attre->mp_nexthop_len = IPV6_MAX_BYTELEN;
+  attre.mp_nexthop_len = IPV6_MAX_BYTELEN;
 #endif
-  if (! as_set)
+  if (! as_set || atomic_aggregate)
     attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
   attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
   if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION))
-    attre->aggregator_as = bgp->confed_id;
+    attre.aggregator_as = bgp->confed_id;
   else
-    attre->aggregator_as = bgp->as;
-  attre->aggregator_addr = bgp->router_id;
+    attre.aggregator_as = bgp->as;
+  attre.aggregator_addr = bgp->router_id;
 
   new = bgp_attr_intern (&attr);
-  bgp_attr_extra_free (&attr);
-  
+
   aspath_unintern (&new->aspath);
   return new;
 }
@@ -631,66 +1022,81 @@ bgp_attr_unintern_sub (struct attr *attr)
   /* aspath refcount shoud be decrement. */
   if (attr->aspath)
     aspath_unintern (&attr->aspath);
-  UNSET_FLAG(attr->flag, BGP_ATTR_AS_PATH);
+  UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH));
   
   if (attr->community)
     community_unintern (&attr->community);
-  UNSET_FLAG(attr->flag, BGP_ATTR_COMMUNITIES);
+  UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES));
   
   if (attr->extra)
     {
       if (attr->extra->ecommunity)
         ecommunity_unintern (&attr->extra->ecommunity);
-      UNSET_FLAG(attr->flag, BGP_ATTR_EXT_COMMUNITIES);
+      UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES));
       
       if (attr->extra->cluster)
         cluster_unintern (attr->extra->cluster);
-      UNSET_FLAG(attr->flag, BGP_ATTR_CLUSTER_LIST);
+      UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST));
       
       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
     }
 }
 
 /* Free bgp attribute and aspath. */
 void
-bgp_attr_unintern (struct attr **attr)
+bgp_attr_unintern (struct attr **pattr)
 {
+  struct attr *attr = *pattr;
   struct attr *ret;
   struct attr tmp;
+  struct attr_extra tmp_extra;
   
   /* Decrement attribute reference. */
-  (*attr)->refcnt--;
+  attr->refcnt--;
   
-  tmp = *(*attr);
+  tmp = *attr;
   
-  if ((*attr)->extra)
+  if (attr->extra)
     {
-      tmp.extra = bgp_attr_extra_new ();
-      memcpy (tmp.extra, (*attr)->extra, sizeof (struct attr_extra));
+      tmp.extra = &tmp_extra;
+      memcpy (tmp.extra, attr->extra, sizeof (struct attr_extra));
     }
   
   /* If reference becomes zero then free attribute object. */
-  if ((*attr)->refcnt == 0)
-    {    
-      ret = hash_release (attrhash, *attr);
+  if (attr->refcnt == 0)
+    {
+      ret = hash_release (attrhash, attr);
       assert (ret != NULL);
-      bgp_attr_extra_free (*attr);
-      XFREE (MTYPE_ATTR, *attr);
-      *attr = NULL;
+      bgp_attr_extra_free (attr);
+      XFREE (MTYPE_ATTR, attr);
+      *pattr = NULL;
     }
 
   bgp_attr_unintern_sub (&tmp);
-  bgp_attr_extra_free (&tmp);
 }
 
 void
 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;
@@ -698,9 +1104,27 @@ bgp_attr_flush (struct attr *attr)
       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);
+        {
+          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
     }
 }
 
@@ -722,7 +1146,7 @@ bgp_attr_malformed (struct bgp_attr_parser_args *args, u_char subcode,
   u_char *notify_datap = (length > 0 ? args->startp : NULL);
   
   /* Only relax error handling for eBGP peers */
-  if (peer_sort (peer) != BGP_PEER_EBGP)
+  if (peer->sort != BGP_PEER_EBGP)
     {
       bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, subcode,
                                  notify_datap, length);
@@ -777,7 +1201,7 @@ bgp_attr_malformed (struct bgp_attr_parser_args *args, u_char subcode,
     return BGP_ATTR_PARSE_WITHDRAW;
   
   /* default to reset */
-  return BGP_ATTR_PARSE_ERROR;
+  return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
 }
 
 /* Find out what is wrong with the path attribute flag bits and log the error.
@@ -803,19 +1227,18 @@ bgp_attr_flags_diagnose (struct bgp_attr_parser_args *args,
       CHECK_FLAG (real_flags,    attr_flag_str[i].key)
     )
     {
-      zlog (args->peer->log, LOG_ERR, "%s attribute must%s be flagged as \"%s\"",
-            LOOKUP (attr_str, attr_code),
-            CHECK_FLAG (desired_flags, attr_flag_str[i].key) ? "" : " not",
-            attr_flag_str[i].str);
+      zlog_err ("%s attribute must%s be flagged as \"%s\"",
+                LOOKUP (attr_str, attr_code),
+                CHECK_FLAG (desired_flags, attr_flag_str[i].key) ? "" : " not",
+                attr_flag_str[i].str);
       seen = 1;
     }
   if (!seen)
     {
-      zlog (args->peer->log, LOG_DEBUG,
-            "Strange, %s called for attr %s, but no problem found with flags"
-            " (real flags 0x%x, desired 0x%x)",
-            __func__, LOOKUP (attr_str, attr_code),
-            real_flags, desired_flags);
+      zlog_debug ("Strange, %s called for attr %s, but no problem found with flags"
+                  " (real flags 0x%x, desired 0x%x)",
+                  __func__, LOOKUP (attr_str, attr_code),
+                  real_flags, desired_flags);
     }
 }
 
@@ -839,8 +1262,7 @@ const u_int8_t attr_flags_values [] = {
   [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)
@@ -848,7 +1270,6 @@ bgp_attr_flag_invalid (struct bgp_attr_parser_args *args)
   u_int8_t mask = BGP_ATTR_FLAG_EXTLEN;
   const u_int8_t flags = args->flags;
   const u_int8_t attr_code = args->type;
-  struct peer *const peer = args->peer; 
   
   /* there may be attributes we don't know about */
   if (attr_code > attr_flags_values_max)
@@ -862,9 +1283,8 @@ bgp_attr_flag_invalid (struct bgp_attr_parser_args *args)
   if (!CHECK_FLAG (BGP_ATTR_FLAG_OPTIONAL, flags)
       && !CHECK_FLAG (BGP_ATTR_FLAG_TRANS, flags))
     {
-      zlog (peer->log, LOG_ERR,
-            "%s well-known attributes must have transitive flag set (%x)",
-            LOOKUP (attr_str, attr_code), flags);
+      zlog_err ("%s well-known attributes must have transitive flag set (%x)",
+                LOOKUP (attr_str, attr_code), flags);
       return 1;
     }
   
@@ -875,19 +1295,17 @@ bgp_attr_flag_invalid (struct bgp_attr_parser_args *args)
     {
       if (!CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL))
         {
-          zlog (peer->log, LOG_ERR,
-                "%s well-known attribute "
-                "must NOT have the partial flag set (%x)",
-                 LOOKUP (attr_str, attr_code), flags);
+          zlog_err ("%s well-known attribute "
+                    "must NOT have the partial flag set (%x)",
+                    LOOKUP (attr_str, attr_code), flags);
           return 1;
         }
       if (CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL)
           && !CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS))
         {
-          zlog (peer->log, LOG_ERR,
-                "%s optional + transitive attribute "
-                "must NOT have the partial flag set (%x)",
-                 LOOKUP (attr_str, attr_code), flags);
+          zlog_err ("%s optional + transitive attribute "
+                    "must NOT have the partial flag set (%x)",
+                    LOOKUP (attr_str, attr_code), flags);
           return 1;
         }
     }
@@ -922,8 +1340,7 @@ bgp_attr_origin (struct bgp_attr_parser_args *args)
      value). */
   if (length != 1)
     {
-      zlog (peer->log, LOG_ERR, "Origin attribute length is not one %d",
-           length);
+      zlog_err ("Origin attribute length is not one %d", length);
       return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
                                  args->total);
@@ -939,8 +1356,7 @@ bgp_attr_origin (struct bgp_attr_parser_args *args)
       && (attr->origin != BGP_ORIGIN_EGP)
       && (attr->origin != BGP_ORIGIN_INCOMPLETE))
     {
-      zlog (peer->log, LOG_ERR, "Origin attribute value is invalid %d",
-             attr->origin);
+      zlog_err ("Origin attribute value is invalid %d", attr->origin);
       return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_INVAL_ORIGIN,
                                  args->total);
@@ -971,9 +1387,7 @@ bgp_attr_aspath (struct bgp_attr_parser_args *args)
   /* In case of IBGP, length will be zero. */
   if (! attr->aspath)
     {
-      zlog (peer->log, LOG_ERR,
-            "Malformed AS path from %s, length is %d",
-            peer->host, length);
+      zlog_err ("Malformed AS path from %s, length is %d", peer->host, length);
       return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, 0);
     }
 
@@ -996,13 +1410,11 @@ bgp_attr_aspath_check (struct peer *const peer, struct attr *const attr)
   struct bgp *bgp = peer->bgp;
   struct aspath *aspath;
 
-  bgp = peer->bgp;
-    
   /* Confederation sanity check. */
-  if ((peer_sort (peer) == BGP_PEER_CONFED && ! aspath_left_confed_check (attr->aspath)) ||
-     (peer_sort (peer) == BGP_PEER_EBGP && aspath_confed_check (attr->aspath)))
+  if ((peer->sort == BGP_PEER_CONFED && ! aspath_left_confed_check (attr->aspath)) ||
+     (peer->sort == BGP_PEER_EBGP && aspath_confed_check (attr->aspath)))
     {
-      zlog (peer->log, LOG_ERR, "Malformed AS path from %s", peer->host);
+      zlog_err ("Malformed AS path from %s", peer->host);
       bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
                        BGP_NOTIFY_UPDATE_MAL_AS_PATH);
       return BGP_ATTR_PARSE_ERROR;
@@ -1011,11 +1423,10 @@ bgp_attr_aspath_check (struct peer *const peer, struct attr *const attr)
   /* First AS check for EBGP. */
   if (bgp != NULL && bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS))
     {
-      if (peer_sort (peer) == BGP_PEER_EBGP 
+      if (peer->sort == BGP_PEER_EBGP
          && ! aspath_firstas_check (attr->aspath, peer->as))
        {
-         zlog (peer->log, LOG_ERR,
-               "%s incorrect first AS (must be %u)", peer->host, peer->as);
+          zlog_err ("%s incorrect first AS (must be %u)", peer->host, peer->as);
           bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
                            BGP_NOTIFY_UPDATE_MAL_AS_PATH);
           return BGP_ATTR_PARSE_ERROR;
@@ -1049,9 +1460,7 @@ bgp_attr_as4_path (struct bgp_attr_parser_args *args, struct aspath **as4_path)
   /* In case of IBGP, length will be zero. */
   if (!*as4_path)
     {
-      zlog (peer->log, LOG_ERR,
-            "Malformed AS4 path from %s, length is %d",
-            peer->host, length);
+      zlog_err ("Malformed AS4 path from %s, length is %d", peer->host, length);
       return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_MAL_AS_PATH,
                                  0);
@@ -1077,8 +1486,7 @@ bgp_attr_nexthop (struct bgp_attr_parser_args *args)
   /* Check nexthop attribute length. */
   if (length != 4)
     {
-      zlog (peer->log, LOG_ERR, "Nexthop attribute length isn't four [%d]",
-             length);
+      zlog_err ("Nexthop attribute length isn't four [%d]", length);
 
       return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -1092,11 +1500,12 @@ bgp_attr_nexthop (struct bgp_attr_parser_args *args)
      gets ignored in any of these cases. */
   nexthop_n = stream_get_ipv4 (peer->ibuf);
   nexthop_h = ntohl (nexthop_n);
-  if (IPV4_NET0 (nexthop_h) || IPV4_NET127 (nexthop_h) || IPV4_CLASS_DE (nexthop_h))
+  if ((IPV4_NET0 (nexthop_h) || IPV4_NET127 (nexthop_h) || IPV4_CLASS_DE (nexthop_h))
+      && !BGP_DEBUG (allow_martians, ALLOW_MARTIANS)) /* loopbacks may be used in testing */
     {
       char buf[INET_ADDRSTRLEN];
-      inet_ntop (AF_INET, &nexthop_h, buf, INET_ADDRSTRLEN);
-      zlog (peer->log, LOG_ERR, "Martian nexthop %s", buf);
+      inet_ntop (AF_INET, &nexthop_n, buf, INET_ADDRSTRLEN);
+      zlog_err ("Martian nexthop %s", buf);
       return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP,
                                  args->total);
@@ -1119,8 +1528,7 @@ bgp_attr_med (struct bgp_attr_parser_args *args)
   /* Length check. */
   if (length != 4)
     {
-      zlog (peer->log, LOG_ERR, 
-           "MED attribute length isn't four [%d]", length);
+      zlog_err ("MED attribute length isn't four [%d]", length);
 
       return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -1145,8 +1553,7 @@ bgp_attr_local_pref (struct bgp_attr_parser_args *args)
   /* Length check. */
   if (length != 4)
   {
-    zlog (peer->log, LOG_ERR, "LOCAL_PREF attribute length isn't 4 [%u]",
-          length);
+    zlog_err ("LOCAL_PREF attribute length isn't 4 [%u]", length);
     return bgp_attr_malformed (args,
                                BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
                                args->total);
@@ -1155,7 +1562,7 @@ bgp_attr_local_pref (struct bgp_attr_parser_args *args)
   /* If it is contained in an UPDATE message that is received from an
      external peer, then this attribute MUST be ignored by the
      receiving speaker. */
-  if (peer_sort (peer) == BGP_PEER_EBGP)
+  if (peer->sort == BGP_PEER_EBGP)
     {
       stream_forward_getp (peer->ibuf, length);
       return BGP_ATTR_PARSE_PROCEED;
@@ -1173,15 +1580,13 @@ bgp_attr_local_pref (struct bgp_attr_parser_args *args)
 static int
 bgp_attr_atomic (struct bgp_attr_parser_args *args)
 {
-  struct peer *const peer = args->peer; 
   struct attr *const attr = args->attr;
   const bgp_size_t length = args->length;
   
   /* Length check. */
   if (length != 0)
     {
-      zlog (peer->log, LOG_ERR, "ATOMIC_AGGREGATE attribute length isn't 0 [%u]",
-            length);
+      zlog_err ("ATOMIC_AGGREGATE attribute length isn't 0 [%u]", length);
       return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
                                  args->total);
@@ -1210,8 +1615,7 @@ bgp_attr_aggregator (struct bgp_attr_parser_args *args)
   
   if (length != wantedlen)
     {
-      zlog (peer->log, LOG_ERR, "AGGREGATOR attribute length isn't %u [%u]",
-            wantedlen, length);
+      zlog_err ("AGGREGATOR attribute length isn't %u [%u]", wantedlen, length);
       return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
                                  args->total);
@@ -1241,8 +1645,7 @@ bgp_attr_as4_aggregator (struct bgp_attr_parser_args *args,
       
   if (length != 8)
     {
-      zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]",
-            length);
+      zlog_err ("New Aggregator length is not 8 [%d]", length);
       return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
                                  0);
@@ -1267,7 +1670,17 @@ bgp_attr_munge_as4_attrs (struct peer *const peer,
   int ignore_as4_path = 0;
   struct aspath *newpath;
   struct attr_extra *attre = attr->extra;
-    
+
+  if (!attr->aspath)
+    {
+      /* NULL aspath shouldn't be possible as bgp_attr_parse should have
+       * checked that all well-known, mandatory attributes were present.
+       *
+       * Can only be a problem with peer itself - hard error
+       */
+      return BGP_ATTR_PARSE_ERROR;
+    }
+
   if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV))
     {
       /* peer can do AS4, so we ignore AS4_PATH and AS4_AGGREGATOR
@@ -1347,9 +1760,9 @@ bgp_attr_munge_as4_attrs (struct peer *const peer,
   /* need to reconcile NEW_AS_PATH and AS_PATH */
   if (!ignore_as4_path && (attr->flag & (ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH))))
     {
-       newpath = aspath_reconcile_as4 (attr->aspath, as4_path);
-       aspath_unintern (&attr->aspath);
-       attr->aspath = aspath_intern (newpath);
+      newpath = aspath_reconcile_as4 (attr->aspath, as4_path);
+      aspath_unintern (&attr->aspath);
+      attr->aspath = aspath_intern (newpath);
     }
   return BGP_ATTR_PARSE_PROCEED;
 }
@@ -1395,7 +1808,7 @@ bgp_attr_originator_id (struct bgp_attr_parser_args *args)
   /* Length check. */
   if (length != 4)
     {
-      zlog (peer->log, LOG_ERR, "Bad originator ID length %d", length);
+      zlog_err ("Bad originator ID length %d", length);
 
       return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -1421,7 +1834,7 @@ bgp_attr_cluster_list (struct bgp_attr_parser_args *args)
   /* Check length. */
   if (length % 4)
     {
-      zlog (peer->log, LOG_ERR, "Bad cluster list length %d", length);
+      zlog_err ("Bad cluster list length %d", length);
 
       return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
                                  args->total);
@@ -1447,7 +1860,6 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
   safi_t safi;
   bgp_size_t nlri_len;
   size_t start;
-  int ret;
   struct stream *s;
   struct peer *const peer = args->peer;  
   struct attr *const attr = args->attr;
@@ -1465,7 +1877,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
     {
       zlog_info ("%s: %s sent invalid length, %lu", 
                 __func__, peer->host, (unsigned long)length);
-      return BGP_ATTR_PARSE_ERROR;
+      return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
     }
   
   /* Load AFI, SAFI. */
@@ -1479,57 +1891,75 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
     {
       zlog_info ("%s: %s, MP nexthop length, %u, goes past end of attribute", 
                 __func__, peer->host, attre->mp_nexthop_len);
-      return BGP_ATTR_PARSE_ERROR;
+      return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
     }
   
   /* Nexthop length check. */
   switch (attre->mp_nexthop_len)
     {
-    case 4:
-      stream_get (&attre->mp_nexthop_global_in, s, 4);
+    case BGP_ATTR_NHLEN_IPV4:
+      stream_get (&attre->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN);
       /* Probably needed for RFC 2283 */
       if (attr->nexthop.s_addr == 0)
-        memcpy(&attr->nexthop.s_addr, &attre->mp_nexthop_global_in, 4);
+        memcpy(&attr->nexthop.s_addr, &attre->mp_nexthop_global_in, IPV4_MAX_BYTELEN);
       break;
-    case 12:
+    case BGP_ATTR_NHLEN_VPNV4:
       stream_getl (s); /* RD high */
       stream_getl (s); /* RD low */
-      stream_get (&attre->mp_nexthop_global_in, s, 4);
+      stream_get (&attre->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN);
       break;
 #ifdef HAVE_IPV6
-    case 16:
-      stream_get (&attre->mp_nexthop_global, s, 16);
+    case BGP_ATTR_NHLEN_IPV6_GLOBAL:
+    case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
+      if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL)
+        {
+          stream_getl (s); /* RD high */
+          stream_getl (s); /* RD low */
+        }
+      stream_get (&attre->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
       break;
-    case 32:
-      stream_get (&attre->mp_nexthop_global, s, 16);
-      stream_get (&attre->mp_nexthop_local, s, 16);
+    case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
+    case BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL:
+      if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
+        {
+          stream_getl (s); /* RD high */
+          stream_getl (s); /* RD low */
+        }
+      stream_get (&attre->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
+      if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
+        {
+          stream_getl (s); /* RD high */
+          stream_getl (s); /* RD low */
+        }
+      stream_get (&attre->mp_nexthop_local, s, IPV6_MAX_BYTELEN);
       if (! IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local))
        {
          char buf1[INET6_ADDRSTRLEN];
          char buf2[INET6_ADDRSTRLEN];
 
-         if (BGP_DEBUG (update, UPDATE_IN))
-           zlog_debug ("%s got two nexthop %s %s but second one is not a link-local nexthop", peer->host,
+         if (bgp_debug_update(peer, NULL, NULL, 1))
+            zlog_debug ("%s rcvd nexthops %s, %s -- ignoring non-LL value",
+                        peer->host,
                       inet_ntop (AF_INET6, &attre->mp_nexthop_global,
                                  buf1, INET6_ADDRSTRLEN),
                       inet_ntop (AF_INET6, &attre->mp_nexthop_local,
                                  buf2, INET6_ADDRSTRLEN));
 
-         attre->mp_nexthop_len = 16;
+         attre->mp_nexthop_len = IPV6_MAX_BYTELEN;
        }
       break;
 #endif /* HAVE_IPV6 */
     default:
       zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d", 
                 __func__, peer->host, attre->mp_nexthop_len);
-      return BGP_ATTR_PARSE_ERROR;
+      return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
     }
 
   if (!LEN_LEFT)
     {
       zlog_info ("%s: (%s) Failed to read SNPA and NLRI(s)",
                  __func__, peer->host);
-      return BGP_ATTR_PARSE_ERROR;
+      return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
     }
   
   {
@@ -1545,18 +1975,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
     {
       zlog_info ("%s: (%s) Failed to read NLRI",
                  __func__, peer->host);
-      return BGP_ATTR_PARSE_ERROR;
-    }
-  if (safi != SAFI_MPLS_LABELED_VPN)
-    {
-      ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), nlri_len);
-      if (ret < 0) 
-        {
-          zlog_info ("%s: (%s) NLRI doesn't pass sanity check",
-                     __func__, peer->host);
-         return BGP_ATTR_PARSE_ERROR;
-       }
+      return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
     }
 
   mp_update->afi = afi;
@@ -1566,6 +1985,8 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
 
   stream_forward_getp (s, nlri_len);
 
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI);
+
   return BGP_ATTR_PARSE_PROCEED;
 #undef LEN_LEFT
 }
@@ -1579,28 +2000,21 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
   afi_t afi;
   safi_t safi;
   u_int16_t withdraw_len;
-  int ret;
   struct peer *const peer = args->peer;  
+  struct attr *const attr = args->attr;
   const bgp_size_t length = args->length;
 
   s = peer->ibuf;
   
 #define BGP_MP_UNREACH_MIN_SIZE 3
   if ((length > STREAM_READABLE(s)) || (length <  BGP_MP_UNREACH_MIN_SIZE))
-    return BGP_ATTR_PARSE_ERROR;
+    return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
   
   afi = stream_getw (s);
   safi = stream_getc (s);
   
   withdraw_len = length - BGP_MP_UNREACH_MIN_SIZE;
 
-  if (safi != SAFI_MPLS_LABELED_VPN)
-    {
-      ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), withdraw_len);
-      if (ret < 0)
-       return BGP_ATTR_PARSE_ERROR;
-    }
-
   mp_withdraw->afi = afi;
   mp_withdraw->safi = safi;
   mp_withdraw->nlri = stream_pnt (s);
@@ -1608,6 +2022,8 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
 
   stream_forward_getp (s, withdraw_len);
 
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI);
+
   return BGP_ATTR_PARSE_PROCEED;
 }
 
@@ -1642,11 +2058,143 @@ bgp_attr_ext_communities (struct bgp_attr_parser_args *args)
   return BGP_ATTR_PARSE_PROCEED;
 }
 
+/* Parse Tunnel Encap attribute in an UPDATE */
+static int
+bgp_attr_encap(
+  uint8_t      type,
+  struct peer  *peer,  /* IN */
+  bgp_size_t   length, /* IN: attr's length field */
+  struct attr  *attr,  /* IN: caller already allocated */
+  u_char       flag,   /* IN: attr's flags field */
+  u_char       *startp)
+{
+  bgp_size_t                   total;
+  struct attr_extra            *attre = NULL;
+  struct bgp_attr_encap_subtlv *stlv_last = NULL;
+  uint16_t                     tunneltype = 0;
+
+  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+
+  if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS)
+       || !CHECK_FLAG(flag, BGP_ATTR_FLAG_OPTIONAL))
+    {
+      zlog_info ("Tunnel Encap attribute flag isn't optional and transitive %d", flag);
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+                                startp, total);
+      return -1;
+    }
+
+  if (BGP_ATTR_ENCAP == type) {
+    /* read outer TLV type and length */
+    uint16_t   tlv_length;
+
+    if (length < 4) {
+       zlog_info ("Tunnel Encap attribute not long enough to contain outer T,L");
+       bgp_notify_send_with_data(peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                                startp, total);
+       return -1;
+    }
+    tunneltype = stream_getw (BGP_INPUT (peer));
+    tlv_length = stream_getw (BGP_INPUT (peer));
+    length -= 4;
+
+    if (tlv_length != length) {
+       zlog_info ("%s: tlv_length(%d) != length(%d)",
+           __func__, tlv_length, length);
+    }
+  }
+
+  while (length >= 4) {
+    uint16_t   subtype   = 0;
+    uint16_t   sublength = 0;
+    struct bgp_attr_encap_subtlv *tlv;
+
+    if (BGP_ATTR_ENCAP == type) {
+        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) {
+      zlog_info ("Tunnel Encap attribute sub-tlv length %d exceeds remaining length %d",
+           sublength, length);
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                                startp, total);
+      return -1;
+    }
+
+    /* alloc and copy sub-tlv */
+    /* TBD make sure these are freed when attributes are released */
+    tlv = XCALLOC (MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv)-1+sublength);
+    tlv->type = subtype;
+    tlv->length = sublength;
+    stream_get(tlv->value, peer->ibuf, sublength);
+    length -= sublength;
+
+    /* attach tlv to encap chain */
+    if (!attre) {
+       attre = bgp_attr_extra_get(attr);
+       if (BGP_ATTR_ENCAP == type) {
+           for (stlv_last = attre->encap_subtlvs; stlv_last && stlv_last->next;
+               stlv_last = stlv_last->next);
+           if (stlv_last) {
+               stlv_last->next = tlv;
+           } 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 (BGP_ATTR_ENCAP == type) {
+    if (!attre)
+      attre = bgp_attr_extra_get(attr);
+    attre->encap_tunneltype = tunneltype;
+  }
+
+  if (length) {
+    /* spurious leftover data */
+      zlog_info ("Tunnel Encap attribute length is bad: %d leftover octets", length);
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                                startp, total);
+      return -1;
+  }
+
+  return 0;
+}
+
 /* BGP unknown attribute treatment. */
 static bgp_attr_parse_ret_t
 bgp_attr_unknown (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total;
+  bgp_size_t total = args->total;
   struct transit *transit;
   struct attr_extra *attre;
   struct peer *const peer = args->peer; 
@@ -1656,15 +2204,10 @@ bgp_attr_unknown (struct bgp_attr_parser_args *args)
   const u_char flag = args->flags;  
   const bgp_size_t length = args->length;
   
-
-  if (BGP_DEBUG (normal, NORMAL))
-  zlog_debug ("%s Unknown attribute is received (type %d, length %d)",
-             peer->host, type, length);
+  if (bgp_debug_update(peer, NULL, NULL, 1))
+    zlog_debug ("%s Unknown attribute is received (type %d, length %d)",
+                peer->host, type, length);
   
-  if (BGP_DEBUG (events, EVENTS))
-    zlog (peer->log, LOG_DEBUG, 
-         "Unknown attribute type %d length %d is received", type, length);
-
   /* Forward read pointer of input stream. */
   stream_forward_getp (peer->ibuf, length);
 
@@ -1708,8 +2251,57 @@ bgp_attr_unknown (struct bgp_attr_parser_args *args)
   return BGP_ATTR_PARSE_PROCEED;
 }
 
+/* Well-known attribute check. */
+static int
+bgp_attr_check (struct peer *peer, struct attr *attr)
+{
+  u_char type = 0;
+
+  /* BGP Graceful-Restart End-of-RIB for IPv4 unicast is signaled as an
+   * empty UPDATE.  */
+  if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV) && !attr->flag)
+    return BGP_ATTR_PARSE_PROCEED;
+
+  /* "An UPDATE message that contains the MP_UNREACH_NLRI is not required
+     to carry any other path attributes.", though if MP_REACH_NLRI or NLRI
+     are present, it should.  Check for any other attribute being present
+     instead.
+   */
+  if (attr->flag == ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI))
+    return BGP_ATTR_PARSE_PROCEED;
+
+  if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN)))
+    type = BGP_ATTR_ORIGIN;
+
+  if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)))
+    type = BGP_ATTR_AS_PATH;
+
+  /* RFC 2858 makes Next-Hop optional/ignored, if MP_REACH_NLRI is present and
+   * NLRI is empty. We can't easily check NLRI empty here though.
+   */
+  if (!CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))
+      && !CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI)))
+    type = BGP_ATTR_NEXT_HOP;
+
+  if (peer->sort == BGP_PEER_IBGP
+      && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))
+    type = BGP_ATTR_LOCAL_PREF;
+
+  if (type)
+    {
+      zlog_warn ("%s Missing well-known attribute %s.", peer->host,
+                 LOOKUP (attr_str, type));
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_MISS_ATTR,
+                                &type, 1);
+      return BGP_ATTR_PARSE_ERROR;
+    }
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
 /* Read attribute of update packet.  This function is called from
-   bgp_update() in bgpd.c.  */
+   bgp_update_receive() in bgp_packet.c.  */
 bgp_attr_parse_ret_t
 bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
                struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw)
@@ -1725,7 +2317,7 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
   /* same goes for as4_aggregator */
   struct aspath *as4_path = NULL;
   as_t as4_aggregator = 0;
-  struct in_addr as4_aggregator_addr = { 0 };
+  struct in_addr as4_aggregator_addr = { .s_addr = 0 };
 
   /* Initialize bitmap. */
   memset (seen, 0, BGP_ATTR_BITMAP_SIZE);
@@ -1740,10 +2332,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
       if (endp - BGP_INPUT_PNT (peer) < BGP_ATTR_MIN_LEN)
        {
          /* XXX warning: long int format, int arg (arg 5) */
-         zlog (peer->log, LOG_WARNING, 
-               "%s: error BGP attribute length %lu is smaller than min len",
-               peer->host,
-               (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer))));
+         zlog_warn ("%s: error BGP attribute length %lu is smaller than min len",
+                     peer->host,
+                    (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer))));
 
          bgp_notify_send (peer, 
                           BGP_NOTIFY_UPDATE_ERR, 
@@ -1763,10 +2354,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
       if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN)
           && ((endp - startp) < (BGP_ATTR_MIN_LEN + 1)))
        {
-         zlog (peer->log, LOG_WARNING, 
-               "%s: Extended length set, but just %lu bytes of attr header",
-               peer->host,
-               (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer))));
+         zlog_warn ("%s: Extended length set, but just %lu bytes of attr header",
+                    peer->host,
+                    (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer))));
 
          bgp_notify_send (peer, 
                           BGP_NOTIFY_UPDATE_ERR, 
@@ -1786,9 +2376,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
 
       if (CHECK_BITMAP (seen, type))
        {
-         zlog (peer->log, LOG_WARNING,
-               "%s: error BGP attribute type %d appears twice in a message",
-               peer->host, type);
+         zlog_warn ("%s: error BGP attribute type %d appears twice in a message",
+                    peer->host, type);
 
          bgp_notify_send (peer, 
                           BGP_NOTIFY_UPDATE_ERR, 
@@ -1805,14 +2394,45 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
       attr_endp =  BGP_INPUT_PNT (peer) + length;
 
       if (attr_endp > endp)
-       {
-         zlog (peer->log, LOG_WARNING, 
-               "%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 (peer, 
-                          BGP_NOTIFY_UPDATE_ERR, 
-                          BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
-         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,
@@ -1890,21 +2510,31 @@ 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;
        default:
          ret = bgp_attr_unknown (&attr_args);
          break;
        }
       
+      if (ret == BGP_ATTR_PARSE_ERROR_NOTIFYPLS)
+       {
+         bgp_notify_send (peer, 
+                          BGP_NOTIFY_UPDATE_ERR,
+                          BGP_NOTIFY_UPDATE_MAL_ATTR);
+         ret = BGP_ATTR_PARSE_ERROR;
+       }
+
       /* If hard error occured immediately return to the caller. */
       if (ret == BGP_ATTR_PARSE_ERROR)
         {
-          zlog (peer->log, LOG_WARNING,
-                "%s: Attribute %s, parse error", 
-                peer->host, 
-                LOOKUP (attr_str, type));
-          bgp_notify_send (peer, 
-                           BGP_NOTIFY_UPDATE_ERR,
-                           BGP_NOTIFY_UPDATE_MAL_ATTR);
+          zlog_warn ("%s: Attribute %s, parse error", 
+                     peer->host, 
+                     LOOKUP (attr_str, type));
           if (as4_path)
             aspath_unintern (&as4_path);
           return ret;
@@ -1912,10 +2542,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
       if (ret == BGP_ATTR_PARSE_WITHDRAW)
         {
           
-          zlog (peer->log, LOG_WARNING,
-                "%s: Attribute %s, parse error - treating as withdrawal",
-                peer->host,
-                LOOKUP (attr_str, type));
+          zlog_warn ("%s: Attribute %s, parse error - treating as withdrawal",
+                     peer->host,
+                     LOOKUP (attr_str, type));
           if (as4_path)
             aspath_unintern (&as4_path);
           return ret;
@@ -1924,9 +2553,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
       /* Check the fetched length. */
       if (BGP_INPUT_PNT (peer) != attr_endp)
        {
-         zlog (peer->log, LOG_WARNING, 
-               "%s: BGP attribute %s, fetch error", 
-                peer->host, LOOKUP (attr_str, type));
+         zlog_warn ("%s: BGP attribute %s, fetch error",
+                     peer->host, LOOKUP (attr_str, type));
          bgp_notify_send (peer, 
                           BGP_NOTIFY_UPDATE_ERR, 
                           BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
@@ -1939,9 +2567,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
   /* Check final read pointer is same as end pointer. */
   if (BGP_INPUT_PNT (peer) != endp)
     {
-      zlog (peer->log, LOG_WARNING, 
-           "%s: BGP attribute %s, length mismatch",
-           peer->host, LOOKUP (attr_str, type));
+      zlog_warn ("%s: BGP attribute %s, length mismatch",
+                peer->host, LOOKUP (attr_str, type));
       bgp_notify_send (peer, 
                       BGP_NOTIFY_UPDATE_ERR, 
                       BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
@@ -1950,6 +2577,17 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
       return BGP_ATTR_PARSE_ERROR;
     }
 
+  /* Check all mandatory well-known attributes are present */
+  {
+    bgp_attr_parse_ret_t ret;
+    if ((ret = bgp_attr_check (peer, attr)) < 0)
+      {
+        if (as4_path)
+          aspath_unintern (&as4_path);
+        return ret;
+      }
+  }
+
   /* 
    * At this place we can see whether we got AS4_PATH and/or
    * AS4_AGGREGATOR from a 16Bit peer and act accordingly.
@@ -1960,10 +2598,19 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
    * So, to be defensive, we are not relying on any order and read
    * all attributes first, including these 32bit ones, and now,
    * afterwards, we look what and if something is to be done for as4.
+   *
+   * It is possible to not have AS_PATH, e.g. GR EoR and sole
+   * MP_UNREACH_NLRI.
    */
-  if (bgp_attr_munge_as4_attrs (peer, attr, as4_path,
+  /* actually... this doesn't ever return failure currently, but
+   * better safe than sorry */
+  if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))
+      && bgp_attr_munge_as4_attrs (peer, attr, as4_path,
                                 as4_aggregator, &as4_aggregator_addr))
     {
+      bgp_notify_send (peer, 
+                      BGP_NOTIFY_UPDATE_ERR,
+                      BGP_NOTIFY_UPDATE_MAL_ATTR);
       if (as4_path)
         aspath_unintern (&as4_path);
       return BGP_ATTR_PARSE_ERROR;
@@ -1997,55 +2644,269 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
       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;
 }
 
-/* Well-known attribute check. */
-int
-bgp_attr_check (struct peer *peer, struct attr *attr)
+size_t
+bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, afi_t nh_afi,
+                        struct bpacket_attr_vec_arr *vecarr,
+                        struct attr *attr)
 {
-  u_char type = 0;
-  
-  if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN)))
-    type = BGP_ATTR_ORIGIN;
+  size_t sizep;
 
-  if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)))
-    type = BGP_ATTR_AS_PATH;
+  /* Set extended bit always to encode the attribute length as 2 bytes */
+  stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
+  stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
+  sizep = stream_get_endp (s);
+  stream_putw (s, 0);  /* Marker: Attribute length. */
 
-  if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP)))
-    type = BGP_ATTR_NEXT_HOP;
+  stream_putw (s, afi);
+  stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi);
 
-  if (peer_sort (peer) == BGP_PEER_IBGP
-      && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))
-    type = BGP_ATTR_LOCAL_PREF;
+  if (nh_afi == AFI_MAX)
+    nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->extra->mp_nexthop_len);
+  /* Nexthop */
+  switch (nh_afi)
+    {
+    case AFI_IP:
+      switch (safi)
+       {
+       case SAFI_UNICAST:
+       case SAFI_MULTICAST:
+         bpacket_attr_vec_arr_set_vec (vecarr, BGP_ATTR_VEC_NH, s, attr);
+         stream_putc (s, 4);
+         stream_put_ipv4 (s, attr->nexthop.s_addr);
+         break;
+       case SAFI_MPLS_VPN:
+         bpacket_attr_vec_arr_set_vec (vecarr, BGP_ATTR_VEC_NH, s, attr);
+         stream_putc (s, 12);
+         stream_putl (s, 0);   /* RD = 0, per RFC */
+         stream_putl (s, 0);
+         stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
+         break;
+       case SAFI_ENCAP:
+         stream_putc (s, 4);
+         stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
+         break;
+       default:
+         break;
+       }
+      break;
+#ifdef HAVE_IPV6
+    case AFI_IP6:
+      switch (safi)
+      {
+      case SAFI_UNICAST:
+      case SAFI_MULTICAST:
+       {
+         struct attr_extra *attre = attr->extra;
+
+         assert (attr->extra);
+         bpacket_attr_vec_arr_set_vec (vecarr, BGP_ATTR_VEC_NH, s, attr);
+         stream_putc (s, attre->mp_nexthop_len);
+         stream_put (s, &attre->mp_nexthop_global, IPV6_MAX_BYTELEN);
+         if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+           stream_put (s, &attre->mp_nexthop_local, IPV6_MAX_BYTELEN);
+       }
+       break;
+      case SAFI_MPLS_VPN:
+       {
+         struct attr_extra *attre = attr->extra;
+
+         assert (attr->extra);
+          if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL) {
+            stream_putc (s, 24);
+            stream_putl (s, 0);   /* RD = 0, per RFC */
+            stream_putl (s, 0);
+            stream_put (s, &attre->mp_nexthop_global, IPV6_MAX_BYTELEN);
+          } else if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+            stream_putc (s, 48);
+            stream_putl (s, 0);   /* RD = 0, per RFC */
+            stream_putl (s, 0);
+            stream_put (s, &attre->mp_nexthop_global, IPV6_MAX_BYTELEN);
+            stream_putl (s, 0);   /* RD = 0, per RFC */
+            stream_putl (s, 0);
+            stream_put (s, &attre->mp_nexthop_local, IPV6_MAX_BYTELEN);
+          }
+        }
+       break;
+       case SAFI_ENCAP:
+          assert (attr->extra);
+          stream_putc (s, IPV6_MAX_BYTELEN);
+         stream_put (s, &attr->extra->mp_nexthop_global, IPV6_MAX_BYTELEN);
+         break;
+      default:
+       break;
+      }
+      break;
+#endif /*HAVE_IPV6*/
+    default:
+      break;
+    }
 
-  if (type)
+  /* SNPA */
+  stream_putc (s, 0);
+  return sizep;
+}
+
+void
+bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi,
+                         struct prefix *p, struct prefix_rd *prd,
+                          u_char *tag, int addpath_encode,
+                          u_int32_t addpath_tx_id)
+{
+  if (safi == SAFI_MPLS_VPN)
     {
-      zlog (peer->log, LOG_WARNING, 
-           "%s Missing well-known attribute %d.",
-           peer->host, type);
-      bgp_notify_send_with_data (peer, 
-                                BGP_NOTIFY_UPDATE_ERR, 
-                                BGP_NOTIFY_UPDATE_MISS_ATTR,
-                                &type, 1);
-      return BGP_ATTR_PARSE_ERROR;
+      if (addpath_encode)
+        stream_putl(s, addpath_tx_id);
+      /* Tag, RD, Prefix write. */
+      stream_putc (s, p->prefixlen + 88);
+      stream_put (s, tag, 3);
+      stream_put (s, prd->val, 8);
+      stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
     }
-  return BGP_ATTR_PARSE_PROCEED;
+  else
+    stream_put_prefix_addpath (s, p, addpath_encode, addpath_tx_id);
+}
+
+size_t
+bgp_packet_mpattr_prefix_size (afi_t afi, safi_t safi, struct prefix *p)
+{
+  int size = PSIZE (p->prefixlen);
+  if (safi == SAFI_MPLS_VPN)
+      size += 88;
+  return size;
+}
+
+/*
+ * 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         *bgp,
+    struct peer                *peer,
+    struct stream      *s,
+    struct attr                *attr,
+    uint8_t            attrtype)
+{
+    unsigned int                       attrlenfield = 0;
+    unsigned int                       attrhdrlen   = 0;
+    struct bgp_attr_encap_subtlv       *subtlvs;
+    struct bgp_attr_encap_subtlv       *st;
+    const char                         *attrname;
+
+    if (!attr || !attr->extra || 
+        (attrtype == BGP_ATTR_ENCAP && 
+         (!attr->extra->encap_tunneltype ||
+          attr->extra->encap_tunneltype == BGP_ENCAP_TYPE_MPLS)))
+       return;
+
+    switch (attrtype) {
+       case BGP_ATTR_ENCAP:
+           attrname = "Tunnel Encap";
+           subtlvs = attr->extra->encap_subtlvs;
+
+           /*
+            * The tunnel encap attr has an "outer" tlv.
+            * T = tunneltype,
+            * L = total length of subtlvs,
+            * V = concatenated subtlvs.
+            */
+           attrlenfield = 2 + 2;       /* T + L */
+            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);
+    }
+
+    /* compute attr length */
+    for (st = subtlvs; st; st = st->next) {
+       attrlenfield += (attrhdrlen + st->length);
+    }
+
+    if (attrlenfield > 0xffff) {
+       zlog_info ("%s attribute is too long (length=%d), can't send it",
+           attrname,
+           attrlenfield);
+       return;
+    }
+
+    if (attrlenfield > 0xff) {
+       /* 2-octet length field */
+       stream_putc (s,
+           BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
+       stream_putc (s, attrtype);
+       stream_putw (s, attrlenfield & 0xffff);
+    } else {
+       /* 1-octet length field */
+       stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL);
+       stream_putc (s, attrtype);
+       stream_putc (s, attrlenfield & 0xff);
+    }
+
+    if (attrtype == BGP_ATTR_ENCAP) {
+       /* write outer T+L */
+       stream_putw(s, attr->extra->encap_tunneltype);
+       stream_putw(s, attrlenfield - 4);
+    }
+
+    /* write each sub-tlv */
+    for (st = subtlvs; st; st = st->next) {
+        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);
+    }
+}
+
+void
+bgp_packet_mpattr_end (struct stream *s, size_t sizep)
+{
+  /* Set MP attribute length. Don't count the (2) bytes used to encode
+     the attr length */
+  stream_putw_at (s, sizep, (stream_get_endp (s) - sizep) - 2);
 }
-\f
-int stream_put_prefix (struct stream *, struct prefix *);
 
 /* Make attribute packet. */
 bgp_size_t
 bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
-                     struct stream *s, struct attr *attr, struct prefix *p,
-                     afi_t afi, safi_t safi, struct peer *from,
-                     struct prefix_rd *prd, u_char *tag)
+                     struct stream *s, struct attr *attr,
+                     struct bpacket_attr_vec_arr *vecarr,
+                     struct prefix *p, afi_t afi, safi_t safi,
+                     struct peer *from, struct prefix_rd *prd, u_char *tag,
+                      int addpath_encode,
+                      u_int32_t addpath_tx_id)
 {
   size_t cp;
   size_t aspath_sizep;
@@ -2055,11 +2916,25 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
   int use32bit = (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) ? 1 : 0;
 
   if (! bgp)
-    bgp = bgp_get_default ();
+    bgp = peer->bgp;
 
   /* Remember current pointer. */
   cp = stream_get_endp (s);
 
+  if (p && !((afi == AFI_IP && safi == SAFI_UNICAST) &&
+              !peer_cap_enhe(peer)))
+    {
+      size_t mpattrlen_pos = 0;
+
+      mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi,
+                                    (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);
+      bgp_packet_mpattr_end(s, mpattrlen_pos);
+    }
+
   /* Origin attribute. */
   stream_putc (s, BGP_ATTR_FLAG_TRANS);
   stream_putc (s, BGP_ATTR_ORIGIN);
@@ -2069,28 +2944,37 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
   /* AS path attribute. */
 
   /* If remote-peer is EBGP */
-  if (peer_sort (peer) == BGP_PEER_EBGP
+  if (peer->sort == BGP_PEER_EBGP
       && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
          || attr->aspath->segments == NULL)
       && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)))
     {    
       aspath = aspath_dup (attr->aspath);
 
+      /* Even though we may not be configured for confederations we may have
+       * RXed an AS_PATH with AS_CONFED_SEQUENCE or AS_CONFED_SET */
+      aspath = aspath_delete_confed_seq (aspath);
+
       if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
        {
-         /* Strip the confed info, and then stuff our path CONFED_ID
-            on the front */
-         aspath = aspath_delete_confed_seq (aspath);
+         /* Stuff our path CONFED_ID on the front */
          aspath = aspath_add_seq (aspath, bgp->confed_id);
        }
       else
        {
-         aspath = aspath_add_seq (aspath, peer->local_as);
-         if (peer->change_local_as)
+         if (peer->change_local_as) {
+            /* If replace-as is specified, we only use the change_local_as when
+               advertising routes. */
+            if( ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ) {
+              aspath = aspath_add_seq (aspath, peer->local_as);
+            }
            aspath = aspath_add_seq (aspath, peer->change_local_as);
+          } else {
+            aspath = aspath_add_seq (aspath, peer->local_as);
+          }
        }
     }
-  else if (peer_sort (peer) == BGP_PEER_CONFED)
+  else if (peer->sort == BGP_PEER_CONFED)
     {
       /* A confed member, so we need to do the AS_CONFED_SEQUENCE thing */
       aspath = aspath_dup (attr->aspath);
@@ -2121,34 +3005,46 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
       send_as4_path = 1; /* we'll do this later, at the correct place */
   
   /* Nexthop attribute. */
-  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP) && afi == AFI_IP)
+  if (afi == AFI_IP && safi == SAFI_UNICAST && !peer_cap_enhe(peer))
     {
-      stream_putc (s, BGP_ATTR_FLAG_TRANS);
-      stream_putc (s, BGP_ATTR_NEXT_HOP);
-      stream_putc (s, 4);
-      if (safi == SAFI_MPLS_VPN)
-       {
-         if (attr->nexthop.s_addr == 0)
-           stream_put_ipv4 (s, peer->nexthop.v4.s_addr);
-         else
-           stream_put_ipv4 (s, attr->nexthop.s_addr);
-       }
-      else
-       stream_put_ipv4 (s, attr->nexthop.s_addr);
+      if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))
+        {
+          stream_putc (s, BGP_ATTR_FLAG_TRANS);
+          stream_putc (s, BGP_ATTR_NEXT_HOP);
+          bpacket_attr_vec_arr_set_vec (vecarr, BGP_ATTR_VEC_NH, s, attr);
+          stream_putc (s, 4);
+          stream_put_ipv4 (s, attr->nexthop.s_addr);
+        }
+      else if (safi == SAFI_UNICAST && peer_cap_enhe(from))
+        {
+          /*
+           * Likely this is the case when an IPv4 prefix was received with
+           * Extended Next-hop capability and now being advertised to
+           * non-ENHE peers.
+           * Setting the mandatory (ipv4) next-hop attribute here to enable
+           * implicit next-hop self with correct (ipv4 address family).
+           */
+          stream_putc (s, BGP_ATTR_FLAG_TRANS);
+          stream_putc (s, BGP_ATTR_NEXT_HOP);
+          bpacket_attr_vec_arr_set_vec (vecarr, BGP_ATTR_VEC_NH, s, NULL);
+          stream_putc (s, 4);
+          stream_put_ipv4 (s, 0);
+        }
     }
 
   /* MED attribute. */
-  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC) ||
+      bgp->maxmed_active)
     {
       stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
       stream_putc (s, BGP_ATTR_MULTI_EXIT_DISC);
       stream_putc (s, 4);
-      stream_putl (s, attr->med);
+      stream_putl (s, (bgp->maxmed_active ? bgp->maxmed_value : attr->med));
     }
 
   /* Local preference. */
-  if (peer_sort (peer) == BGP_PEER_IBGP ||
-      peer_sort (peer) == BGP_PEER_CONFED)
+  if (peer->sort == BGP_PEER_IBGP ||
+      peer->sort == BGP_PEER_CONFED)
     {
       stream_putc (s, BGP_ATTR_FLAG_TRANS);
       stream_putc (s, BGP_ATTR_LOCAL_PREF);
@@ -2221,9 +3117,9 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
     }
 
   /* Route Reflector. */
-  if (peer_sort (peer) == BGP_PEER_IBGP
+  if (peer->sort == BGP_PEER_IBGP
       && from
-      && peer_sort (from) == BGP_PEER_IBGP)
+      && from->sort == BGP_PEER_IBGP)
     {
       /* Originator ID. */
       stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
@@ -2261,96 +3157,6 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
        }
     }
 
-#ifdef HAVE_IPV6
-  /* If p is IPv6 address put it into attribute. */
-  if (p->family == AF_INET6)
-    {
-      unsigned long sizep;
-      struct attr_extra *attre = attr->extra;
-      
-      assert (attr->extra);
-      
-      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
-      stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
-      sizep = stream_get_endp (s);
-      stream_putc (s, 0);      /* Marker: Attribute length. */
-      stream_putw (s, AFI_IP6);        /* AFI */
-      stream_putc (s, safi);   /* SAFI */
-
-      stream_putc (s, attre->mp_nexthop_len);
-
-      if (attre->mp_nexthop_len == 16)
-       stream_put (s, &attre->mp_nexthop_global, 16);
-      else if (attre->mp_nexthop_len == 32)
-       {
-         stream_put (s, &attre->mp_nexthop_global, 16);
-         stream_put (s, &attre->mp_nexthop_local, 16);
-       }
-      
-      /* SNPA */
-      stream_putc (s, 0);
-
-      /* Prefix write. */
-      stream_put_prefix (s, p);
-
-      /* Set MP attribute length. */
-      stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1);
-    }
-#endif /* HAVE_IPV6 */
-
-  if (p->family == AF_INET && safi == SAFI_MULTICAST)
-    {
-      unsigned long sizep;
-
-      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
-      stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
-      sizep = stream_get_endp (s);
-      stream_putc (s, 0);      /* Marker: Attribute Length. */
-      stream_putw (s, AFI_IP); /* AFI */
-      stream_putc (s, SAFI_MULTICAST); /* SAFI */
-
-      stream_putc (s, 4);
-      stream_put_ipv4 (s, attr->nexthop.s_addr);
-
-      /* SNPA */
-      stream_putc (s, 0);
-
-      /* Prefix write. */
-      stream_put_prefix (s, p);
-
-      /* Set MP attribute length. */
-      stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1);
-    }
-
-  if (p->family == AF_INET && safi == SAFI_MPLS_VPN)
-    {
-      unsigned long sizep;
-
-      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
-      stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
-      sizep = stream_get_endp (s);
-      stream_putc (s, 0);      /* Length of this attribute. */
-      stream_putw (s, AFI_IP); /* AFI */
-      stream_putc (s, SAFI_MPLS_LABELED_VPN);  /* SAFI */
-
-      stream_putc (s, 12);
-      stream_putl (s, 0);
-      stream_putl (s, 0);
-      stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
-
-      /* SNPA */
-      stream_putc (s, 0);
-
-      /* Tag, RD, Prefix write. */
-      stream_putc (s, p->prefixlen + 88);
-      stream_put (s, tag, 3);
-      stream_put (s, prd->val, 8);
-      stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
-
-      /* Set MP attribute length. */
-      stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1);
-    }
-
   /* Extended Communities attribute. */
   if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) 
       && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
@@ -2359,8 +3165,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
       
       assert (attre);
       
-      if (peer_sort (peer) == BGP_PEER_IBGP 
-          || peer_sort (peer) == BGP_PEER_CONFED)
+      if (peer->sort == BGP_PEER_IBGP
+          || peer->sort == BGP_PEER_CONFED)
        {
          if (attre->ecommunity->size * 8 > 255)
            {
@@ -2463,7 +3269,19 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
       stream_putl (s, attr->extra->aggregator_as);
       stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
     }
-  
+
+  if ((afi == AFI_IP || afi == AFI_IP6) &&
+      (safi == SAFI_ENCAP || safi == SAFI_MPLS_VPN))
+    {
+       /* 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. */
   if (attr->extra && attr->extra->transit)
     stream_put (s, attr->extra->transit->val, attr->extra->transit->length);
@@ -2472,50 +3290,48 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
   return stream_get_endp (s) - cp;
 }
 
-bgp_size_t
-bgp_packet_withdraw (struct peer *peer, struct stream *s, struct prefix *p,
-                    afi_t afi, safi_t safi, struct prefix_rd *prd,
-                    u_char *tag)
+size_t
+bgp_packet_mpunreach_start (struct stream *s, afi_t afi, safi_t safi)
 {
-  unsigned long cp;
   unsigned long attrlen_pnt;
-  bgp_size_t size;
-
-  cp = stream_get_endp (s);
 
-  stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+  /* Set extended bit always to encode the attribute length as 2 bytes */
+  stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
   stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI);
 
   attrlen_pnt = stream_get_endp (s);
-  stream_putc (s, 0);          /* Length of this attribute. */
+  stream_putw (s, 0);          /* Length of this attribute. */
 
-  stream_putw (s, family2afi (p->family));
+  stream_putw (s, afi);
+  stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi);
+  return attrlen_pnt;
+}
 
+void
+bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p,
+                            afi_t afi, safi_t safi, struct prefix_rd *prd,
+                            u_char *tag, int addpath_encode,
+                             u_int32_t addpath_tx_id)
+{
   if (safi == SAFI_MPLS_VPN)
     {
-      /* SAFI */
-      stream_putc (s, SAFI_MPLS_LABELED_VPN);
+      /* addpath TX ID */
+      if (addpath_encode)
+        stream_putl(s, addpath_tx_id);
 
-      /* prefix. */
       stream_putc (s, p->prefixlen + 88);
       stream_put (s, tag, 3);
       stream_put (s, prd->val, 8);
       stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
     }
   else
-    {
-      /* SAFI */
-      stream_putc (s, safi);
-
-      /* prefix */
-      stream_put_prefix (s, p);
-    }
-
-  /* Set MP attribute length. */
-  size = stream_get_endp (s) - attrlen_pnt - 1;
-  stream_putc_at (s, attrlen_pnt, size);
+    stream_put_prefix_addpath (s, p, addpath_encode, addpath_tx_id);
+}
 
-  return stream_get_endp (s) - cp;
+void
+bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt)
+{
+  bgp_packet_mpattr_end (s, attrlen_pnt);
 }
 
 /* Initialization of attribute. */
@@ -2528,6 +3344,7 @@ bgp_attr_init (void)
   ecommunity_init ();
   cluster_init ();
   transit_init ();
+  encap_init ();
 }
 
 void
@@ -2539,6 +3356,7 @@ bgp_attr_finish (void)
   ecommunity_finish ();
   cluster_finish ();
   transit_finish ();
+  encap_finish ();
 }
 
 /* Make attribute packet. */
@@ -2550,6 +3368,8 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
   unsigned long len;
   size_t aspath_lenp;
   struct aspath *aspath;
+  int addpath_encode = 0;
+  u_int32_t addpath_tx_id = 0;
 
   /* Remember current pointer. */
   cp = stream_get_endp (s);
@@ -2644,7 +3464,8 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
 #ifdef HAVE_IPV6
   /* Add a MP_NLRI attribute to dump the IPv6 next hop */
   if (prefix != NULL && prefix->family == AF_INET6 && attr->extra &&
-     (attr->extra->mp_nexthop_len == 16 || attr->extra->mp_nexthop_len == 32) )
+     (attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL ||
+      attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) )
     {
       int sizep;
       struct attr_extra *attre = attr->extra;
@@ -2660,15 +3481,15 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
 
       /* Next hop */
       stream_putc(s, attre->mp_nexthop_len);
-      stream_put(s, &attre->mp_nexthop_global, 16);
-      if (attre->mp_nexthop_len == 32)
-        stream_put(s, &attre->mp_nexthop_local, 16);
+      stream_put(s, &attre->mp_nexthop_global, IPV6_MAX_BYTELEN);
+      if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+        stream_put(s, &attre->mp_nexthop_local, IPV6_MAX_BYTELEN);
 
       /* SNPA */
       stream_putc(s, 0);
 
       /* Prefix */
-      stream_put_prefix(s, prefix);
+      stream_put_prefix_addpath (s, prefix, addpath_encode, addpath_tx_id);
 
       /* Set MP attribute length. */
       stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1);