]> 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 1c13d11c52e781fcaf21194abeb2ac331570dca2..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
     }
 }
 
@@ -710,23 +1134,38 @@ bgp_attr_flush (struct attr *attr)
  * introduced by the sending neighbour.
  */
 static bgp_attr_parse_ret_t
-bgp_attr_malformed (struct peer *peer, u_char type, u_char flag,
-                    u_char subcode, u_char *startp, bgp_size_t length)
-{
+bgp_attr_malformed (struct bgp_attr_parser_args *args, u_char subcode,
+                    bgp_size_t length)
+{
+  struct peer *const peer = args->peer; 
+  const u_int8_t flags = args->flags;
+  /* startp and length must be special-cased, as whether or not to
+   * send the attribute data with the NOTIFY depends on the error,
+   * the caller therefore signals this with the seperate length argument
+   */
+  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,
-                                 startp, length);
+                                 notify_datap, length);
       return BGP_ATTR_PARSE_ERROR;
 
     }
   
-  switch (type) {
-    /* where an optional attribute is inconsequential, e.g. it does not affect
-     * route selection, and can be safely ignored then any such attributes
-     * which are malformed should just be ignored and the route processed as
-     * normal.
+  /* Adjust the stream getp to the end of the attribute, in case we can
+   * still proceed but the caller hasn't read all the attribute.
+   */
+  stream_set_getp (BGP_INPUT (peer),
+                   (args->startp - STREAM_DATA (BGP_INPUT (peer)))
+                    + args->total);
+  
+  switch (args->type) {
+    /* where an attribute is relatively inconsequential, e.g. it does not
+     * affect route selection, and can be safely ignored, then any such
+     * attributes which are malformed should just be ignored and the route
+     * processed as normal.
      */
     case BGP_ATTR_AS4_AGGREGATOR:
     case BGP_ATTR_AGGREGATOR:
@@ -734,7 +1173,7 @@ bgp_attr_malformed (struct peer *peer, u_char type, u_char flag,
       return BGP_ATTR_PARSE_PROCEED;
     
     /* Core attributes, particularly ones which may influence route
-     * selection should always cause session resets
+     * selection, should always cause session resets
      */
     case BGP_ATTR_ORIGIN:
     case BGP_ATTR_AS_PATH:
@@ -748,7 +1187,7 @@ bgp_attr_malformed (struct peer *peer, u_char type, u_char flag,
     case BGP_ATTR_MP_UNREACH_NLRI:
     case BGP_ATTR_EXT_COMMUNITIES:
       bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, subcode,
-                                 startp, length);
+                                 notify_datap, length);
       return BGP_ATTR_PARSE_ERROR;
   }
   
@@ -756,13 +1195,13 @@ bgp_attr_malformed (struct peer *peer, u_char type, u_char flag,
    * the whole session to be reset. Instead treat it as a withdrawal
    * of the routes, if possible.
    */
-  if (CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS)
-      && CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
-      && CHECK_FLAG (flag, BGP_ATTR_FLAG_PARTIAL))
+  if (CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS)
+      && CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL)
+      && CHECK_FLAG (flags, BGP_ATTR_FLAG_PARTIAL))
     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.
@@ -771,16 +1210,14 @@ bgp_attr_malformed (struct peer *peer, u_char type, u_char flag,
    being diagnosed is defined by RFC as either a "well-known" or an "optional,
    non-transitive" attribute. */
 static void
-bgp_attr_flags_diagnose
-(
-  struct peer * peer,
-  const u_int8_t attr_code,
-  u_int8_t desired_flags, /* how RFC says it must be */
-  u_int8_t real_flags     /* on wire                 */
+bgp_attr_flags_diagnose (struct bgp_attr_parser_args *args,
+                         u_int8_t desired_flags /* how RFC says it must be */
 )
 {
   u_char seen = 0, i;
-
+  u_char real_flags = args->flags;
+  const u_int8_t attr_code = args->type;
+  
   desired_flags &= ~BGP_ATTR_FLAG_EXTLEN;
   real_flags &= ~BGP_ATTR_FLAG_EXTLEN;
   for (i = 0; i <= 2; i++) /* O,T,P, but not E */
@@ -790,36 +1227,112 @@ bgp_attr_flags_diagnose
       CHECK_FLAG (real_flags,    attr_flag_str[i].key)
     )
     {
-      zlog (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;
     }
-  assert (seen);
+  if (!seen)
+    {
+      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);
+    }
+}
+
+/* Required flags for attributes. EXTLEN will be masked off when testing,
+ * as will PARTIAL for optional+transitive attributes.
+ */
+const u_int8_t attr_flags_values [] = {
+  [BGP_ATTR_ORIGIN] =           BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_AS_PATH] =          BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_NEXT_HOP] =         BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_MULTI_EXIT_DISC] =  BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_LOCAL_PREF] =       BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_ATOMIC_AGGREGATE] = BGP_ATTR_FLAG_TRANS,
+  [BGP_ATTR_AGGREGATOR] =       BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_COMMUNITIES] =      BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_ORIGINATOR_ID] =    BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_CLUSTER_LIST] =     BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_MP_REACH_NLRI] =    BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_MP_UNREACH_NLRI] =  BGP_ATTR_FLAG_OPTIONAL,
+  [BGP_ATTR_EXT_COMMUNITIES] =  BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+  [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 = array_size(attr_flags_values) - 1;
+
+static int
+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;
+  
+  /* there may be attributes we don't know about */
+  if (attr_code > attr_flags_values_max)
+    return 0;
+  if (attr_flags_values[attr_code] == 0)
+    return 0;
+  
+  /* RFC4271, "For well-known attributes, the Transitive bit MUST be set to
+   * 1."
+   */
+  if (!CHECK_FLAG (BGP_ATTR_FLAG_OPTIONAL, flags)
+      && !CHECK_FLAG (BGP_ATTR_FLAG_TRANS, flags))
+    {
+      zlog_err ("%s well-known attributes must have transitive flag set (%x)",
+                LOOKUP (attr_str, attr_code), flags);
+      return 1;
+    }
+  
+  /* "For well-known attributes and for optional non-transitive attributes,
+   *  the Partial bit MUST be set to 0." 
+   */
+  if (CHECK_FLAG (flags, BGP_ATTR_FLAG_PARTIAL))
+    {
+      if (!CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL))
+        {
+          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_err ("%s optional + transitive attribute "
+                    "must NOT have the partial flag set (%x)",
+                    LOOKUP (attr_str, attr_code), flags);
+          return 1;
+        }
+    }
+  
+  /* Optional transitive attributes may go through speakers that don't
+   * reocgnise them and set the Partial bit.
+   */
+  if (CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL)
+      && CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS))
+    SET_FLAG (mask, BGP_ATTR_FLAG_PARTIAL);
+  
+  if ((flags & ~mask)
+      == attr_flags_values[attr_code])
+    return 0;
+  
+  bgp_attr_flags_diagnose (args, attr_flags_values[attr_code]);
+  return 1;
 }
 
 /* Get origin attribute of the update message. */
 static bgp_attr_parse_ret_t
-bgp_attr_origin (struct peer *peer, bgp_size_t length, 
-                struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_origin (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total;
-
-  /* total is entire attribute length include Attribute Flags (1),
-     Attribute Type code (1) and Attribute length (1 or 2).  */
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
-  /* If any recognized attribute has Attribute Flags that conflict
-     with the Attribute Type Code, then the Error Subcode is set to
-     Attribute Flags Error.  The Data field contains the erroneous
-     attribute (type, length and value). */
-  if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_TRANS)
-  {
-    bgp_attr_flags_diagnose (peer, BGP_ATTR_ORIGIN, BGP_ATTR_FLAG_TRANS, flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag, BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, startp, total);
-  }
-
+  struct peer *const peer = args->peer;
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
   /* If any recognized attribute has Attribute Length that conflicts
      with the expected length (based on the attribute type code), then
      the Error Subcode is set to Attribute Length Error.  The Data
@@ -827,11 +1340,10 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
      value). */
   if (length != 1)
     {
-      zlog (peer->log, LOG_ERR, "Origin attribute length is not one %d",
-           length);
-      return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag,
+      zlog_err ("Origin attribute length is not one %d", length);
+      return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                                 startp, total);
+                                 args->total);
     }
 
   /* Fetch origin attribute. */
@@ -844,11 +1356,10 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
       && (attr->origin != BGP_ORIGIN_EGP)
       && (attr->origin != BGP_ORIGIN_INCOMPLETE))
     {
-      zlog (peer->log, LOG_ERR, "Origin attribute value is invalid %d",
-             attr->origin);
-      return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag,
+      zlog_err ("Origin attribute value is invalid %d", attr->origin);
+      return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_INVAL_ORIGIN,
-                                 startp, total);
+                                 args->total);
     }
 
   /* Set oring attribute flag. */
@@ -860,58 +1371,24 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
 /* Parse AS path information.  This function is wrapper of
    aspath_parse. */
 static int
-bgp_attr_aspath (struct peer *peer, bgp_size_t length, 
-                struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_aspath (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total;
-
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
-  /* Flags check. */
-  if (CHECK_FLAG (flag, BGP_ATTR_FLAG_PARTIAL))
-    {
-      zlog (peer->log, LOG_ERR,
-           "AS_PATH attribute must not be flagged as \"partial\" (%u)", flag);
-      return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag,
-                                BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                                startp, total);
-    }
-
-  if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
-    {
-      zlog (peer->log, LOG_ERR,
-            "AS_PATH attribute must be flagged as \"transitive\" (%u)", flag);
-      return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
-                                BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                                startp, total);
-    }
-  
-  if (CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL))
-    {
-      zlog (peer->log, LOG_ERR, 
-            "AS_PATH attribute must not be flagged as \"optional\" (%u)", flag);
-      
-      return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
-                                 BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                                 startp, total);
-    }
-
-  /*
-   * peer with AS4 => will get 4Byte ASnums
-   * otherwise, will get 16 Bit
-   */
-  attr->aspath = aspath_parse (peer->ibuf, length, 
-                               CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV));
+  struct attr *const attr = args->attr;
+  struct peer *const peer = args->peer; 
+  const bgp_size_t length = args->length;
+  
+  /*
+   * peer with AS4 => will get 4Byte ASnums
+   * otherwise, will get 16 Bit
+   */
+  attr->aspath = aspath_parse (peer->ibuf, length, 
+                               CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV));
 
   /* 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);
-      return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
-                                 BGP_NOTIFY_UPDATE_MAL_AS_PATH,
-                                 NULL, 0);
+      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);
     }
 
   /* Set aspath attribute flag. */
@@ -921,7 +1398,7 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length,
 }
 
 static bgp_attr_parse_ret_t
-bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag)
+bgp_attr_aspath_check (struct peer *const peer, struct attr *const attr)
 {
   /* These checks were part of bgp_attr_aspath, but with
    * as4 we should to check aspath things when
@@ -933,29 +1410,26 @@ bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag)
   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);
-      return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
-                                 BGP_NOTIFY_UPDATE_MAL_AS_PATH,
-                                 NULL, 0);
+      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;
     }
 
   /* 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);
-          return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
-                                     BGP_NOTIFY_UPDATE_MAL_AS_PATH,
-                                     NULL, 0);
+          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;
        }
     }
 
@@ -975,36 +1449,21 @@ bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag)
 /* Parse AS4 path information.  This function is another wrapper of
    aspath_parse. */
 static int
-bgp_attr_as4_path (struct peer *peer, bgp_size_t length,
-                struct attr *attr, u_char flag, u_char *startp,
-                struct aspath **as4_path)
+bgp_attr_as4_path (struct bgp_attr_parser_args *args, struct aspath **as4_path)
 {
-  bgp_size_t total;
-
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
-  /* Flag check. */
-  if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
-      || !CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
-    {
-      zlog (peer->log, LOG_ERR, 
-           "As4-Path attribute flag isn't optional/transitive %d", flag);
-      return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
-                                 BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                                 startp, total);
-    }
-
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
   *as4_path = aspath_parse (peer->ibuf, length, 1);
 
   /* 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);
-      return bgp_attr_malformed (peer, BGP_ATTR_AS4_PATH, flag,
+      zlog_err ("Malformed AS4 path from %s, length is %d", peer->host, length);
+      return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_MAL_AS_PATH,
-                                 NULL, 0);
+                                 0);
     }
 
   /* Set aspath attribute flag. */
@@ -1016,30 +1475,22 @@ bgp_attr_as4_path (struct peer *peer, bgp_size_t length,
 
 /* Nexthop attribute. */
 static bgp_attr_parse_ret_t
-bgp_attr_nexthop (struct peer *peer, bgp_size_t length, 
-                 struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_nexthop (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total;
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
   in_addr_t nexthop_h, nexthop_n;
 
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
-  /* Flags check. */
-  if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_TRANS)
-  {
-    bgp_attr_flags_diagnose (peer, BGP_ATTR_NEXT_HOP, BGP_ATTR_FLAG_TRANS, flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_NEXT_HOP, flag, BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, startp, total);
-  }
-
   /* 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 (peer, BGP_ATTR_NEXT_HOP, flag,
+      return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                                 startp, total);
+                                 args->total);
     }
 
   /* According to section 6.3 of RFC4271, syntactically incorrect NEXT_HOP
@@ -1049,14 +1500,15 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
      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);
-      return bgp_attr_malformed (peer, BGP_ATTR_NEXT_HOP, flag,
+      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,
-                                 startp, total);
+                                 args->total);
     }
 
   attr->nexthop.s_addr = nexthop_n;
@@ -1067,31 +1519,20 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
 
 /* MED atrribute. */
 static bgp_attr_parse_ret_t
-bgp_attr_med (struct peer *peer, bgp_size_t length, 
-             struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_med (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total;
-
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
-  /* Flag checks. */
-  if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_OPTIONAL)
-  {
-    bgp_attr_flags_diagnose (peer, BGP_ATTR_MULTI_EXIT_DISC, BGP_ATTR_FLAG_OPTIONAL, flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_MULTI_EXIT_DISC, flag,
-                               BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                               startp, total);
-  }
-
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
   /* 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 (peer, BGP_ATTR_MULTI_EXIT_DISC, flag,
+      return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                                 startp, total);
+                                 args->total);
     }
 
   attr->med = stream_getl (peer->ibuf);
@@ -1103,33 +1544,25 @@ bgp_attr_med (struct peer *peer, bgp_size_t length,
 
 /* Local preference attribute. */
 static bgp_attr_parse_ret_t
-bgp_attr_local_pref (struct peer *peer, bgp_size_t length, 
-                    struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_local_pref (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total;
-
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-  /* Flag checks. */
-  if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_TRANS)
-  {
-    bgp_attr_flags_diagnose (peer, BGP_ATTR_LOCAL_PREF, BGP_ATTR_FLAG_TRANS, flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_LOCAL_PREF, flag,
-                               BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                               startp, total);
-  }
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
   /* Length check. */
   if (length != 4)
   {
-    zlog (peer->log, LOG_ERR, "LOCAL_PREF attribute length isn't 4 [%u]", length);
-    return bgp_attr_malformed (peer, BGP_ATTR_LOCAL_PREF, flag,
+    zlog_err ("LOCAL_PREF attribute length isn't 4 [%u]", length);
+    return bgp_attr_malformed (args,
                                BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                               startp, total);
+                               args->total);
   }
 
   /* 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;
@@ -1145,28 +1578,18 @@ bgp_attr_local_pref (struct peer *peer, bgp_size_t length,
 
 /* Atomic aggregate. */
 static int
-bgp_attr_atomic (struct peer *peer, bgp_size_t length, 
-                struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_atomic (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total;
-
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-  /* Flag checks. */
-  if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_TRANS)
-  {
-    bgp_attr_flags_diagnose (peer, BGP_ATTR_ATOMIC_AGGREGATE, BGP_ATTR_FLAG_TRANS, flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_ATOMIC_AGGREGATE, flag,
-                               BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                               startp, total);
-  }
-
+  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);
-      return bgp_attr_malformed (peer, BGP_ATTR_ATOMIC_AGGREGATE, flag,
+      zlog_err ("ATOMIC_AGGREGATE attribute length isn't 0 [%u]", length);
+      return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                                 startp, total);
+                                 args->total);
     }
 
   /* Set atomic aggregate flag. */
@@ -1177,41 +1600,25 @@ bgp_attr_atomic (struct peer *peer, bgp_size_t length,
 
 /* Aggregator attribute */
 static int
-bgp_attr_aggregator (struct peer *peer, bgp_size_t length,
-                    struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_aggregator (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;
+  
   int wantedlen = 6;
   struct attr_extra *attre = bgp_attr_extra_get (attr);
-  bgp_size_t total;
   
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-  /* Flags check. */
-  if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL))
-  {
-    zlog (peer->log, LOG_ERR,
-          "AGGREGATOR attribute must be flagged as \"optional\" (%u)", flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_AGGREGATOR, flag,
-                               BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                               startp, total);
-  }
-  if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
-  {
-    zlog (peer->log, LOG_ERR,
-          "AGGREGATOR attribute must be flagged as \"transitive\" (%u)", flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_AGGREGATOR, flag,
-                               BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                               startp, total);
-  }
   /* peer with AS4 will send 4 Byte AS, peer without will send 2 Byte */
   if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV))
     wantedlen = 8;
   
   if (length != wantedlen)
     {
-      zlog (peer->log, LOG_ERR, "AGGREGATOR attribute length isn't %u [%u]", wantedlen, length);
-      return bgp_attr_malformed (peer, BGP_ATTR_AGGREGATOR, flag,
+      zlog_err ("AGGREGATOR attribute length isn't %u [%u]", wantedlen, length);
+      return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                                 startp, total);
+                                 args->total);
     }
   
   if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) )
@@ -1228,18 +1635,22 @@ bgp_attr_aggregator (struct peer *peer, bgp_size_t length,
 
 /* New Aggregator attribute */
 static bgp_attr_parse_ret_t
-bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length,
-                    struct attr *attr, u_char flag, 
-                    as_t *as4_aggregator_as,
-                    struct in_addr *as4_aggregator_addr)
+bgp_attr_as4_aggregator (struct bgp_attr_parser_args *args,
+                        as_t *as4_aggregator_as,
+                        struct in_addr *as4_aggregator_addr)
 {
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+      
   if (length != 8)
     {
-      zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]", length);
-      return bgp_attr_malformed (peer, BGP_ATTR_AS4_AGGREGATOR, flag,
+      zlog_err ("New Aggregator length is not 8 [%d]", length);
+      return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                                 NULL, 0);
+                                 0);
     }
+  
   *as4_aggregator_as = stream_getl (peer->ibuf);
   as4_aggregator_addr->s_addr = stream_get_ipv4 (peer->ibuf);
 
@@ -1251,14 +1662,25 @@ bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length,
 /* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH.
  */
 static bgp_attr_parse_ret_t
-bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
+bgp_attr_munge_as4_attrs (struct peer *const peer,
+                          struct attr *const attr,
                           struct aspath *as4_path, as_t as4_aggregator,
                           struct in_addr *as4_aggregator_addr)
 {
   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
@@ -1280,24 +1702,6 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
       return BGP_ATTR_PARSE_PROCEED;
     }
   
-  if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH))
-      && !(attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))))
-    {
-      /* Hu? This is not supposed to happen at all!
-       * got as4_path and no aspath,
-       *   This should already
-       *   have been handled by 'well known attributes missing'
-       *   But... yeah, paranoia
-       * Take this as a "malformed attribute"
-       */
-      zlog (peer->log, LOG_ERR, 
-            "%s BGP not AS4 capable peer sent AS4_PATH but"
-            " no AS_PATH, cant do anything here", peer->host);
-      return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
-                                 BGP_NOTIFY_UPDATE_MAL_ATTR,
-                                 NULL, 0);
-    }
-
   /* We have a asn16 peer.  First, look for AS4_AGGREGATOR
    * because that may override AS4_PATH
    */
@@ -1356,20 +1760,20 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
   /* 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;
 }
 
 /* Community attribute. */
 static bgp_attr_parse_ret_t
-bgp_attr_community (struct peer *peer, bgp_size_t length, 
-                   struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_community (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total
-    = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;  
+  const bgp_size_t length = args->length;
   
   if (length == 0)
     {
@@ -1384,9 +1788,9 @@ bgp_attr_community (struct peer *peer, bgp_size_t length,
   stream_forward_getp (peer->ibuf, length);
 
   if (!attr->community)
-    return bgp_attr_malformed (peer, BGP_ATTR_COMMUNITIES, flag,
+    return bgp_attr_malformed (args,
                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
-                               startp, total);
+                               args->total);
   
   attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
 
@@ -1395,28 +1799,20 @@ bgp_attr_community (struct peer *peer, bgp_size_t length,
 
 /* Originator ID attribute. */
 static bgp_attr_parse_ret_t
-bgp_attr_originator_id (struct peer *peer, bgp_size_t length, 
-                       struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_originator_id (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total;
-
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-  /* Flag checks. */
-  if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_OPTIONAL)
-  {
-    bgp_attr_flags_diagnose (peer, BGP_ATTR_ORIGINATOR_ID, BGP_ATTR_FLAG_OPTIONAL, flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_ORIGINATOR_ID, flag,
-                               BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                               startp, total);
-  }
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
   /* 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 (peer, BGP_ATTR_ORIGINATOR_ID, flag,
+      return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                                 startp, total);
+                                 args->total);
     }
 
   (bgp_attr_extra_get (attr))->originator_id.s_addr 
@@ -1429,28 +1825,19 @@ bgp_attr_originator_id (struct peer *peer, bgp_size_t length,
 
 /* Cluster list attribute. */
 static bgp_attr_parse_ret_t
-bgp_attr_cluster_list (struct peer *peer, bgp_size_t length, 
-                      struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_cluster_list (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total;
-
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-  /* Flag checks. */
-  if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_OPTIONAL)
-  {
-    bgp_attr_flags_diagnose (peer, BGP_ATTR_CLUSTER_LIST, BGP_ATTR_FLAG_OPTIONAL, flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_CLUSTER_LIST, flag,
-                               BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                               startp, total);
-  }
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  const bgp_size_t length = args->length;
+  
   /* 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 (peer, BGP_ATTR_CLUSTER_LIST, flag,
-                                 BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                                 startp, total);
+      return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                 args->total);
     }
 
   (bgp_attr_extra_get (attr))->cluster 
@@ -1466,27 +1853,19 @@ bgp_attr_cluster_list (struct peer *peer, bgp_size_t length,
 
 /* Multiprotocol reachability information parse. */
 int
-bgp_mp_reach_parse (struct peer *peer, const bgp_size_t length,
-                    struct attr *attr, const u_char flag, u_char *startp, struct bgp_nlri *mp_update)
+bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
+                    struct bgp_nlri *mp_update)
 {
   afi_t afi;
   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;
+  const bgp_size_t length = args->length;
   struct attr_extra *attre = bgp_attr_extra_get(attr);
-  bgp_size_t total;
-
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-  /* Flag checks. */
-  if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_OPTIONAL)
-  {
-    bgp_attr_flags_diagnose (peer, BGP_ATTR_MP_REACH_NLRI, BGP_ATTR_FLAG_OPTIONAL, flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_MP_REACH_NLRI, flag,
-                               BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                               startp, total);
-  }
+  
   /* Set end of packet. */
   s = BGP_INPUT(peer);
   start = stream_get_getp(s);
@@ -1498,7 +1877,7 @@ bgp_mp_reach_parse (struct peer *peer, const bgp_size_t length,
     {
       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. */
@@ -1512,57 +1891,75 @@ bgp_mp_reach_parse (struct peer *peer, const bgp_size_t length,
     {
       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;
     }
   
   {
@@ -1578,18 +1975,7 @@ bgp_mp_reach_parse (struct peer *peer, const bgp_size_t length,
     {
       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;
@@ -1599,51 +1985,36 @@ bgp_mp_reach_parse (struct peer *peer, const bgp_size_t length,
 
   stream_forward_getp (s, nlri_len);
 
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI);
+
   return BGP_ATTR_PARSE_PROCEED;
 #undef LEN_LEFT
 }
 
 /* Multiprotocol unreachable parse */
 int
-bgp_mp_unreach_parse (struct peer *peer, const bgp_size_t length,
-                      const u_char flag, u_char *startp,
+bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
                      struct bgp_nlri *mp_withdraw)
 {
   struct stream *s;
   afi_t afi;
   safi_t safi;
   u_int16_t withdraw_len;
-  int ret;
-  bgp_size_t total;
-
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-  /* Flag checks. */
-  if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_OPTIONAL)
-  {
-    bgp_attr_flags_diagnose (peer, BGP_ATTR_MP_UNREACH_NLRI, BGP_ATTR_FLAG_OPTIONAL, flag);
-    return bgp_attr_malformed (peer, BGP_ATTR_MP_UNREACH_NLRI, flag,
-                               BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
-                               startp, total);
-  }
+  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);
@@ -1651,16 +2022,18 @@ bgp_mp_unreach_parse (struct peer *peer, const bgp_size_t length,
 
   stream_forward_getp (s, withdraw_len);
 
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI);
+
   return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* Extended Community attribute. */
 static bgp_attr_parse_ret_t
-bgp_attr_ext_communities (struct peer *peer, bgp_size_t length, 
-                         struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_ext_communities (struct bgp_attr_parser_args *args)
 {
-  bgp_size_t total
-    = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+  struct peer *const peer = args->peer;  
+  struct attr *const attr = args->attr;  
+  const bgp_size_t length = args->length;
   
   if (length == 0)
     {
@@ -1676,47 +2049,177 @@ bgp_attr_ext_communities (struct peer *peer, bgp_size_t length,
   stream_forward_getp (peer->ibuf, length);
   
   if (!attr->extra->ecommunity)
-    return bgp_attr_malformed (peer, BGP_ATTR_EXT_COMMUNITIES,
-                               flag, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
-                               startp, total);
+    return bgp_attr_malformed (args,
+                               BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                               args->total);
   
   attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
 
   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 peer *peer, struct attr *attr, u_char flag,
-                 u_char type, bgp_size_t length, u_char *startp)
+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;
-
-  if (BGP_DEBUG (normal, NORMAL))
-  zlog_debug ("%s Unknown attribute is received (type %d, length %d)",
-             peer->host, type, length);
+  struct peer *const peer = args->peer; 
+  struct attr *const attr = args->attr;
+  u_char *const startp = args->startp;
+  const u_char type = args->type;
+  const u_char flag = args->flags;  
+  const bgp_size_t length = args->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);
 
-  /* Adjest total length to include type and length. */
-  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
   /* If any of the mandatory well-known attributes are not recognized,
      then the Error Subcode is set to Unrecognized Well-known
      Attribute.  The Data field contains the unrecognized attribute
      (type, length and value). */
   if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL))
     {
-      return bgp_attr_malformed (peer, type, flag,
+      return bgp_attr_malformed (args,
                                  BGP_NOTIFY_UPDATE_UNREC_ATTR,
-                                 startp, total);
+                                 args->total);
     }
 
   /* Unrecognized non-transitive optional attributes must be quietly
@@ -1748,8 +2251,57 @@ bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag,
   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)
@@ -1765,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);
@@ -1780,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, 
@@ -1803,17 +2354,16 @@ 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, 
                           BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
          return BGP_ATTR_PARSE_ERROR;
        }
-
+      
       /* Check extended attribue length bit. */
       if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN))
        length = stream_getw (BGP_INPUT (peer));
@@ -1826,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, 
@@ -1845,80 +2394,147 @@ 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,
+          .length = length,
+          .attr = attr,
+          .type = type,
+          .flags = flag,
+          .startp = startp,
+          .total = attr_endp - startp,
+        };
+      
+       
+      /* If any recognized attribute has Attribute Flags that conflict
+         with the Attribute Type Code, then the Error Subcode is set to
+         Attribute Flags Error.  The Data field contains the erroneous
+         attribute (type, length and value). */
+      if (bgp_attr_flag_invalid (&attr_args))
+        {
+          bgp_attr_parse_ret_t ret;
+          ret = bgp_attr_malformed (&attr_args,
+                                    BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+                                    attr_args.total);
+          if (ret == BGP_ATTR_PARSE_PROCEED)
+            continue;
+          return ret;
+        }
 
       /* OK check attribute and store it's value. */
       switch (type)
        {
        case BGP_ATTR_ORIGIN:
-         ret = bgp_attr_origin (peer, length, attr, flag, startp);
+         ret = bgp_attr_origin (&attr_args);
          break;
        case BGP_ATTR_AS_PATH:
-         ret = bgp_attr_aspath (peer, length, attr, flag, startp);
+         ret = bgp_attr_aspath (&attr_args);
          break;
        case BGP_ATTR_AS4_PATH:
-         ret = bgp_attr_as4_path (peer, length, attr, flag, startp, &as4_path);
+         ret = bgp_attr_as4_path (&attr_args, &as4_path);
          break;
        case BGP_ATTR_NEXT_HOP: 
-         ret = bgp_attr_nexthop (peer, length, attr, flag, startp);
+         ret = bgp_attr_nexthop (&attr_args);
          break;
        case BGP_ATTR_MULTI_EXIT_DISC:
-         ret = bgp_attr_med (peer, length, attr, flag, startp);
+         ret = bgp_attr_med (&attr_args);
          break;
        case BGP_ATTR_LOCAL_PREF:
-         ret = bgp_attr_local_pref (peer, length, attr, flag, startp);
+         ret = bgp_attr_local_pref (&attr_args);
          break;
        case BGP_ATTR_ATOMIC_AGGREGATE:
-         ret = bgp_attr_atomic (peer, length, attr, flag, startp);
+         ret = bgp_attr_atomic (&attr_args);
          break;
        case BGP_ATTR_AGGREGATOR:
-         ret = bgp_attr_aggregator (peer, length, attr, flag, startp);
+         ret = bgp_attr_aggregator (&attr_args);
          break;
        case BGP_ATTR_AS4_AGGREGATOR:
-         ret = bgp_attr_as4_aggregator (peer, length, attr, flag,
-                                        &as4_aggregator, 
+         ret = bgp_attr_as4_aggregator (&attr_args,
+                                        &as4_aggregator,
                                         &as4_aggregator_addr);
          break;
        case BGP_ATTR_COMMUNITIES:
-         ret = bgp_attr_community (peer, length, attr, flag, startp);
+         ret = bgp_attr_community (&attr_args);
          break;
        case BGP_ATTR_ORIGINATOR_ID:
-         ret = bgp_attr_originator_id (peer, length, attr, flag, startp);
+         ret = bgp_attr_originator_id (&attr_args);
          break;
        case BGP_ATTR_CLUSTER_LIST:
-         ret = bgp_attr_cluster_list (peer, length, attr, flag, startp);
+         ret = bgp_attr_cluster_list (&attr_args);
          break;
        case BGP_ATTR_MP_REACH_NLRI:
-         ret = bgp_mp_reach_parse (peer, length, attr, flag, startp, mp_update);
+         ret = bgp_mp_reach_parse (&attr_args, mp_update);
          break;
        case BGP_ATTR_MP_UNREACH_NLRI:
-         ret = bgp_mp_unreach_parse (peer, length, flag, startp, mp_withdraw);
+         ret = bgp_mp_unreach_parse (&attr_args, mp_withdraw);
          break;
        case BGP_ATTR_EXT_COMMUNITIES:
-         ret = bgp_attr_ext_communities (peer, length, attr, flag, startp);
+         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 (peer, attr, flag, type, length, startp);
+         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;
@@ -1926,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;
@@ -1938,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);
@@ -1953,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);
@@ -1964,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.
@@ -1974,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, flag, 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;
@@ -2007,59 +2640,273 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
    */
   if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH)))
     {
-      ret = bgp_attr_aspath_check (peer, attr, flag);
+      ret = bgp_attr_aspath_check (peer, attr);
       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;
@@ -2069,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);
@@ -2083,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);
@@ -2135,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);
@@ -2235,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);
@@ -2275,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)))
@@ -2373,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)
            {
@@ -2477,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);
@@ -2486,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. */
@@ -2542,6 +3344,7 @@ bgp_attr_init (void)
   ecommunity_init ();
   cluster_init ();
   transit_init ();
+  encap_init ();
 }
 
 void
@@ -2553,6 +3356,7 @@ bgp_attr_finish (void)
   ecommunity_finish ();
   cluster_finish ();
   transit_finish ();
+  encap_finish ();
 }
 
 /* Make attribute packet. */
@@ -2564,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);
@@ -2658,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;
@@ -2674,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);