[bgpd] Trim memory usage of BGP routes

2007-05-03 Paul Jakma <paul.jakma@sun.com>

	* bgp_route.h: (struct info) Move less frequently used
	  fields to a lazily allocated struct info_extra.
	  Export bgp_info_extra_get
	* bgp_route.c: (bgp_info_extra_new) allocate extra
	  (bgp_info_extra_free) Free damp info and the info_extra.
	  (bgp_info_extra_get) Retrieve the info_extra of a struct
	  info, allocating as required.
	  (generally) adjust to use info->extra
	* bgp_damp.c: (generally) use bgp_info_extra_get to access
	  dampinfo
	* bgp_attr.h: Move rarely allocated attributes from struct attr
	  to a struct attr_extra, for a substantial saving in size of
	  struct attr.
	* bgp_attr.c: (bgp_attr_extra_{new,free}), new, self-explanatory.
	  (bgp_attr_extra_get) Get the attr_extra for a given struct
	  attr, allocating it if needs be.
	  (bgp_attr_dup) Shallow copy the struct attr and its attr_extra.
	  (generally) adjust to know about attr->extra.
	* bgp_debug.c: (bgp_dump_attr) ditto
	* bgp_vty.c: (show_bgp_memory) print attr and info extra sizes.
	* bgp_nexthop.c: (generally) adjust to know about attr->extra
	  and info->extra.
	* bgp_{packet,routemap,snmp,zebra}.c: ditto
	* lib/memtypes.c: Add MTYPE_ATTR_EXTRA and MTYPE_BGP_ROUTE_EXTRA
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 28f0160..07c9413 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -282,9 +282,47 @@
 }
 
 /* Attribute hash routines. */
-
 struct hash *attrhash;
 
+static struct attr_extra *
+bgp_attr_extra_new (void)
+{
+  return XCALLOC (MTYPE_ATTR_EXTRA, sizeof (struct attr_extra));
+}
+
+void
+bgp_attr_extra_free (struct attr *attr)
+{
+  if (attr->extra)
+    {
+      XFREE (MTYPE_ATTR_EXTRA, attr->extra);
+      attr->extra = NULL;
+    }
+}
+
+struct attr_extra *
+bgp_attr_extra_get (struct attr *attr)
+{
+  if (!attr->extra)
+    attr->extra = bgp_attr_extra_new();
+  return attr->extra;
+}
+
+/* Shallow copy of an attribute
+ * Though, not so shallow that it doesn't copy the contents
+ * of the attr_extra pointed to by 'extra'
+ */
+void
+bgp_attr_dup (struct attr *new, struct attr *orig)
+{
+  *new = *orig;
+  if (orig->extra)
+    {
+      new->extra = bgp_attr_extra_new();
+      *new->extra = *orig->extra;
+    }
+}
+
 unsigned long int
 attr_count (void)
 {
@@ -307,33 +345,41 @@
   key += attr->nexthop.s_addr;
   key += attr->med;
   key += attr->local_pref;
-  key += attr->aggregator_as;
-  key += attr->aggregator_addr.s_addr;
-  key += attr->weight;
-
-  key += attr->mp_nexthop_global_in.s_addr;
+  
+  if (attr->extra)
+    {
+      key += attr->extra->aggregator_as;
+      key += attr->extra->aggregator_addr.s_addr;
+      key += attr->extra->weight;
+      key += attr->extra->mp_nexthop_global_in.s_addr;
+    }
+  
   if (attr->aspath)
     key += aspath_key_make (attr->aspath);
   if (attr->community)
     key += community_hash_make (attr->community);
-  if (attr->ecommunity)
-    key += ecommunity_hash_make (attr->ecommunity);
-  if (attr->cluster)
-    key += cluster_hash_key_make (attr->cluster);
-  if (attr->transit)
-    key += transit_hash_key_make (attr->transit);
+  
+  if (attr->extra)
+    {
+      if (attr->extra->ecommunity)
+        key += ecommunity_hash_make (attr->extra->ecommunity);
+      if (attr->extra->cluster)
+        key += cluster_hash_key_make (attr->extra->cluster);
+      if (attr->extra->transit)
+        key += transit_hash_key_make (attr->extra->transit);
 
 #ifdef HAVE_IPV6
- {
-   int i;
-   
-   key += attr->mp_nexthop_len;
-   for (i = 0; i < 16; i++)
-     key += attr->mp_nexthop_global.s6_addr[i];
-   for (i = 0; i < 16; i++)
-     key += attr->mp_nexthop_local.s6_addr[i];
- }
+      {
+        int i;
+        
+        key += attr->extra->mp_nexthop_len;
+        for (i = 0; i < 16; i++)
+          key += attr->extra->mp_nexthop_global.s6_addr[i];
+        for (i = 0; i < 16; i++)
+          key += attr->extra->mp_nexthop_local.s6_addr[i];
+      }
 #endif /* HAVE_IPV6 */
+    }
 
   return key;
 }
@@ -347,23 +393,33 @@
   if (attr1->flag == attr2->flag
       && attr1->origin == attr2->origin
       && attr1->nexthop.s_addr == attr2->nexthop.s_addr
-      && attr1->med == attr2->med
-      && attr1->local_pref == attr2->local_pref
-      && attr1->aggregator_as == attr2->aggregator_as
-      && attr1->aggregator_addr.s_addr == attr2->aggregator_addr.s_addr
-      && attr1->weight == attr2->weight
-#ifdef HAVE_IPV6
-      && attr1->mp_nexthop_len == attr2->mp_nexthop_len
-      && IPV6_ADDR_SAME (&attr1->mp_nexthop_global, &attr2->mp_nexthop_global)
-      && IPV6_ADDR_SAME (&attr1->mp_nexthop_local, &attr2->mp_nexthop_local)
-#endif /* HAVE_IPV6 */
-      && IPV4_ADDR_SAME (&attr1->mp_nexthop_global_in, &attr2->mp_nexthop_global_in)
       && attr1->aspath == attr2->aspath
       && attr1->community == attr2->community
-      && attr1->ecommunity == attr2->ecommunity
-      && attr1->cluster == attr2->cluster
-      && attr1->transit == attr2->transit)
-    return 1;
+      && attr1->med == attr2->med
+      && attr1->local_pref == attr2->local_pref)
+    {
+      struct attr_extra *ae1 = attr1->extra;
+      struct attr_extra *ae2 = attr2->extra;
+      
+      if (ae1 && ae2
+          && ae1->aggregator_as == ae2->aggregator_as
+          && ae1->aggregator_addr.s_addr == ae2->aggregator_addr.s_addr
+          && ae1->weight == ae2->weight
+#ifdef HAVE_IPV6
+          && ae1->mp_nexthop_len == ae2->mp_nexthop_len
+          && IPV6_ADDR_SAME (&ae1->mp_nexthop_global, &ae2->mp_nexthop_global)
+          && IPV6_ADDR_SAME (&ae1->mp_nexthop_local, &ae2->mp_nexthop_local)
+#endif /* HAVE_IPV6 */
+          && 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)
+        return 1;
+      else if (ae1 || ae2)
+        return 0;
+      /* neither attribute has extra attributes, so they're same */
+      return 1;
+    }
   else
     return 0;
 }
@@ -400,6 +456,11 @@
 
   attr = XMALLOC (MTYPE_ATTR, sizeof (struct attr));
   *attr = *val;
+  if (val->extra)
+    {
+      attr->extra = bgp_attr_extra_new ();
+      *attr->extra = *val->extra;
+    }
   attr->refcnt = 0;
   return attr;
 }
@@ -425,26 +486,31 @@
       else
 	attr->community->refcnt++;
     }
-  if (attr->ecommunity)
+  if (attr->extra)
     {
-      if (! attr->ecommunity->refcnt)
-	attr->ecommunity = ecommunity_intern (attr->ecommunity);
-      else
-	attr->ecommunity->refcnt++;
-    }
-  if (attr->cluster)
-    {
-      if (! attr->cluster->refcnt)
-	attr->cluster = cluster_intern (attr->cluster);
-      else
-	attr->cluster->refcnt++;
-    }
-  if (attr->transit)
-    {
-      if (! attr->transit->refcnt)
-	attr->transit = transit_intern (attr->transit);
-      else
-	attr->transit->refcnt++;
+      struct attr_extra *attre = attr->extra;
+      
+      if (attre->ecommunity)
+        {
+          if (! attre->ecommunity->refcnt)
+            attre->ecommunity = ecommunity_intern (attre->ecommunity);
+          else
+            attre->ecommunity->refcnt++;
+        }
+      if (attre->cluster)
+        {
+          if (! attre->cluster->refcnt)
+            attre->cluster = cluster_intern (attre->cluster);
+          else
+            attre->cluster->refcnt++;
+        }
+      if (attre->transit)
+        {
+          if (! attre->transit->refcnt)
+            attre->transit = transit_intern (attre->transit);
+          else
+            attre->transit->refcnt++;
+        }
     }
 
   find = (struct attr *) hash_get (attrhash, attr, bgp_attr_hash_alloc);
@@ -459,15 +525,16 @@
 bgp_attr_default_set (struct attr *attr, u_char origin)
 {
   memset (attr, 0, sizeof (struct attr));
-
+  bgp_attr_extra_get (attr);
+  
   attr->origin = origin;
   attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
   attr->aspath = aspath_empty ();
   attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
-  attr->weight = BGP_ATTR_DEFAULT_WEIGHT;
+  attr->extra->weight = BGP_ATTR_DEFAULT_WEIGHT;
   attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
 #ifdef HAVE_IPV6
-  attr->mp_nexthop_len = IPV6_MAX_BYTELEN;
+  attr->extra->mp_nexthop_len = IPV6_MAX_BYTELEN;
 #endif
 
   return attr;
@@ -480,10 +547,16 @@
 {
   struct attr attr;
   struct attr *new;
-
+  struct attr_extra *attre;
+  
+  memset (&attr, 0, sizeof (struct attr));
+  attre = bgp_attr_extra_get (&attr);
+  
   bgp_attr_default_set(&attr, origin);
 
   new = bgp_attr_intern (&attr);
+  bgp_attr_extra_free (&attr);
+  
   aspath_unintern (new->aspath);
   return new;
 }
@@ -495,9 +568,11 @@
 {
   struct attr attr;
   struct attr *new;
+  struct attr_extra *attre;
 
   memset (&attr, 0, sizeof (struct attr));
-
+  attre = bgp_attr_extra_get (&attr);
+  
   /* Origin attribute. */
   attr.origin = origin;
   attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
@@ -518,20 +593,22 @@
       attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
     }
 
-  attr.weight = BGP_ATTR_DEFAULT_WEIGHT;
+  attre->weight = BGP_ATTR_DEFAULT_WEIGHT;
 #ifdef HAVE_IPV6
-  attr.mp_nexthop_len = IPV6_MAX_BYTELEN;
+  attre->mp_nexthop_len = IPV6_MAX_BYTELEN;
 #endif
   if (! as_set)
     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))
-    attr.aggregator_as = bgp->confed_id;
+    attre->aggregator_as = bgp->confed_id;
   else
-    attr.aggregator_as = bgp->as;
-  attr.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;
 }
@@ -543,23 +620,27 @@
   struct attr *ret;
   struct aspath *aspath;
   struct community *community;
-  struct ecommunity *ecommunity;
-  struct cluster_list *cluster;
-  struct transit *transit;
+  struct ecommunity *ecommunity = NULL;
+  struct cluster_list *cluster = NULL;
+  struct transit *transit = NULL;
 
   /* Decrement attribute reference. */
   attr->refcnt--;
   aspath = attr->aspath;
   community = attr->community;
-  ecommunity = attr->ecommunity;
-  cluster = attr->cluster;
-  transit = attr->transit;
+  if (attr->extra)
+    {
+      ecommunity = attr->extra->ecommunity;
+      cluster = attr->extra->cluster;
+      transit = attr->extra->transit;
+    }
 
   /* If reference becomes zero then free attribute object. */
   if (attr->refcnt == 0)
     {    
       ret = hash_release (attrhash, attr);
       assert (ret != NULL);
+      bgp_attr_extra_free (attr);
       XFREE (MTYPE_ATTR, attr);
     }
 
@@ -583,12 +664,16 @@
     aspath_free (attr->aspath);
   if (attr->community && ! attr->community->refcnt)
     community_free (attr->community);
-  if (attr->ecommunity && ! attr->ecommunity->refcnt)
-    ecommunity_free (attr->ecommunity);
-  if (attr->cluster && ! attr->cluster->refcnt)
-    cluster_free (attr->cluster);
-  if (attr->transit && ! attr->transit->refcnt)
-    transit_free (attr->transit);
+  if (attr->extra)
+    {
+      struct attr_extra *attre = attr->extra;
+      if (attre->ecommunity && ! attre->ecommunity->refcnt)
+        ecommunity_free (attre->ecommunity);
+      if (attre->cluster && ! attre->cluster->refcnt)
+        cluster_free (attre->cluster);
+      if (attre->transit && ! attre->transit->refcnt)
+        transit_free (attre->transit);
+    }
 }
 
 /* Get origin attribute of the update message. */
@@ -851,6 +936,8 @@
 bgp_attr_aggregator (struct peer *peer, bgp_size_t length,
 		     struct attr *attr, u_char flag)
 {
+  struct attr_extra *attre = bgp_attr_extra_get (attr);
+  
   if (length != 6)
     {
       zlog (peer->log, LOG_ERR, "Aggregator length is not 6 [%d]", length);
@@ -860,8 +947,8 @@
 		       BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
       return -1;
     }
-  attr->aggregator_as = stream_getw (peer->ibuf);
-  attr->aggregator_addr.s_addr = stream_get_ipv4 (peer->ibuf);
+  attre->aggregator_as = stream_getw (peer->ibuf);
+  attre->aggregator_addr.s_addr = stream_get_ipv4 (peer->ibuf);
 
   /* Set atomic aggregate flag. */
   attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
@@ -903,7 +990,8 @@
       return -1;
     }
 
-  attr->originator_id.s_addr = stream_get_ipv4 (peer->ibuf);
+  (bgp_attr_extra_get (attr))->originator_id.s_addr 
+    = stream_get_ipv4 (peer->ibuf);
 
   attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID);
 
@@ -926,8 +1014,8 @@
       return -1;
     }
 
-  attr->cluster = cluster_parse ((struct in_addr *)stream_pnt (peer->ibuf), 
-                                 length);
+  (bgp_attr_extra_get (attr))->cluster 
+    = cluster_parse ((struct in_addr *)stream_pnt (peer->ibuf), length);
 
   stream_forward_getp (peer->ibuf, length);;
 
@@ -947,6 +1035,7 @@
   size_t start;
   int ret;
   struct stream *s;
+  struct attr_extra *attre = bgp_attr_extra_get(attr);
   
   /* Set end of packet. */
   s = BGP_INPUT(peer);
@@ -962,16 +1051,16 @@
   safi = stream_getc (s);
 
   /* Get nexthop length. */
-  attr->mp_nexthop_len = stream_getc (s);
+  attre->mp_nexthop_len = stream_getc (s);
   
-  if (STREAM_READABLE(s) < attr->mp_nexthop_len)
+  if (STREAM_READABLE(s) < attre->mp_nexthop_len)
     return -1;
   
   /* Nexthop length check. */
-  switch (attr->mp_nexthop_len)
+  switch (attre->mp_nexthop_len)
     {
     case 4:
-      stream_get (&attr->mp_nexthop_global_in, s, 4);
+      stream_get (&attre->mp_nexthop_global_in, s, 4);
       break;
     case 12:
       {
@@ -980,35 +1069,35 @@
 
 	rd_high = stream_getl (s);
 	rd_low = stream_getl (s);
-	stream_get (&attr->mp_nexthop_global_in, s, 4);
+	stream_get (&attre->mp_nexthop_global_in, s, 4);
       }
       break;
 #ifdef HAVE_IPV6
     case 16:
-      stream_get (&attr->mp_nexthop_global, s, 16);
+      stream_get (&attre->mp_nexthop_global, s, 16);
       break;
     case 32:
-      stream_get (&attr->mp_nexthop_global, s, 16);
-      stream_get (&attr->mp_nexthop_local, s, 16);
-      if (! IN6_IS_ADDR_LINKLOCAL (&attr->mp_nexthop_local))
+      stream_get (&attre->mp_nexthop_global, s, 16);
+      stream_get (&attre->mp_nexthop_local, s, 16);
+      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,
-		       inet_ntop (AF_INET6, &attr->mp_nexthop_global,
+		       inet_ntop (AF_INET6, &attre->mp_nexthop_global,
 				  buf1, INET6_ADDRSTRLEN),
-		       inet_ntop (AF_INET6, &attr->mp_nexthop_local,
+		       inet_ntop (AF_INET6, &attre->mp_nexthop_local,
 				  buf2, INET6_ADDRSTRLEN));
 
-	  attr->mp_nexthop_len = 16;
+	  attre->mp_nexthop_len = 16;
 	}
       break;
 #endif /* HAVE_IPV6 */
     default:
       zlog_info ("Wrong multiprotocol next hop length: %d", 
-		 attr->mp_nexthop_len);
+		 attre->mp_nexthop_len);
       return -1;
     }
 
@@ -1089,10 +1178,13 @@
 			  struct attr *attr, u_char flag)
 {
   if (length == 0)
-    attr->ecommunity = NULL;
+    {
+      if (attr->extra)
+        attr->extra->ecommunity = NULL;
+    }
   else
     {
-      attr->ecommunity = 
+      (bgp_attr_extra_get (attr))->ecommunity = 
         ecommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length);
       stream_forward_getp (peer->ibuf, length);
     }
@@ -1108,6 +1200,7 @@
 {
   bgp_size_t total;
   struct transit *transit;
+  struct attr_extra *attre;
 
   if (BGP_DEBUG (normal, NORMAL))
   zlog_debug ("%s Unknown attribute is received (type %d, length %d)",
@@ -1149,13 +1242,13 @@
   SET_FLAG (*startp, BGP_ATTR_FLAG_PARTIAL);
 
   /* Store transitive attribute to the end of attr->transit. */
-  if (! attr->transit)
+  if (! ((attre = bgp_attr_extra_get(attr))->transit) )
     {
-      attr->transit = XMALLOC (MTYPE_TRANSIT, sizeof (struct transit));
-      memset (attr->transit, 0, sizeof (struct transit));
+      attre->transit = XMALLOC (MTYPE_TRANSIT, sizeof (struct transit));
+      memset (attre->transit, 0, sizeof (struct transit));
     }
 
-  transit = attr->transit;
+  transit = attre->transit;
 
   if (transit->val)
     transit->val = XREALLOC (MTYPE_TRANSIT_VAL, transit->val, 
@@ -1337,8 +1430,8 @@
     }
 
   /* Finally intern unknown attribute. */
-  if (attr->transit)
-    attr->transit = transit_intern (attr->transit);
+  if (attr->extra && attr->extra->transit)
+    attr->extra->transit = transit_intern (attr->extra->transit);
 
   return 0;
 }
@@ -1500,11 +1593,12 @@
   /* Aggregator. */
   if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
     {
+      assert (attr->extra);
       stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
       stream_putc (s, BGP_ATTR_AGGREGATOR);
       stream_putc (s, 6);
-      stream_putw (s, attr->aggregator_as);
-      stream_put_ipv4 (s, attr->aggregator_addr.s_addr);
+      stream_putw (s, attr->extra->aggregator_as);
+      stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
     }
 
   /* Community attribute. */
@@ -1531,13 +1625,15 @@
       && from
       && peer_sort (from) == BGP_PEER_IBGP)
     {
+      assert (attr->extra);
+      
       /* Originator ID. */
       stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
       stream_putc (s, BGP_ATTR_ORIGINATOR_ID);
       stream_putc (s, 4);
 
       if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
-	stream_put_in_addr (s, &attr->originator_id);
+	stream_put_in_addr (s, &attr->extra->originator_id);
       else 
         stream_put_in_addr (s, &from->remote_id);
 
@@ -1545,15 +1641,16 @@
       stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
       stream_putc (s, BGP_ATTR_CLUSTER_LIST);
       
-      if (attr->cluster)
+      if (attr->extra->cluster)
 	{
-	  stream_putc (s, attr->cluster->length + 4);
+	  stream_putc (s, attr->extra->cluster->length + 4);
 	  /* If this peer configuration's parent BGP has cluster_id. */
 	  if (bgp->config & BGP_CONFIG_CLUSTER_ID)
 	    stream_put_in_addr (s, &bgp->cluster_id);
 	  else
 	    stream_put_in_addr (s, &bgp->router_id);
-	  stream_put (s, attr->cluster->list, attr->cluster->length);
+	  stream_put (s, attr->extra->cluster->list, 
+	              attr->extra->cluster->length);
 	}
       else
 	{
@@ -1571,22 +1668,25 @@
   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);	/* Length of this attribute. */
+      stream_putc (s, 0);	/* Marker: Attribute length. */
       stream_putw (s, AFI_IP6);	/* AFI */
       stream_putc (s, safi);	/* SAFI */
 
-      stream_putc (s, attr->mp_nexthop_len);
+      stream_putc (s, attre->mp_nexthop_len);
 
-      if (attr->mp_nexthop_len == 16)
-	stream_put (s, &attr->mp_nexthop_global, 16);
-      else if (attr->mp_nexthop_len == 32)
+      if (attre->mp_nexthop_len == 16)
+	stream_put (s, &attre->mp_nexthop_global, 16);
+      else if (attre->mp_nexthop_len == 32)
 	{
-	  stream_put (s, &attr->mp_nexthop_global, 16);
-	  stream_put (s, &attr->mp_nexthop_local, 16);
+	  stream_put (s, &attre->mp_nexthop_global, 16);
+	  stream_put (s, &attre->mp_nexthop_local, 16);
 	}
       
       /* SNPA */
@@ -1607,7 +1707,7 @@
       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_putc (s, 0);	/* Marker: Attribute Length. */
       stream_putw (s, AFI_IP);	/* AFI */
       stream_putc (s, SAFI_MULTICAST);	/* SAFI */
 
@@ -1638,7 +1738,7 @@
       stream_putc (s, 12);
       stream_putl (s, 0);
       stream_putl (s, 0);
-      stream_put (s, &attr->mp_nexthop_global_in, 4);
+      stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
 
       /* SNPA */
       stream_putc (s, 0);
@@ -1657,21 +1757,26 @@
   if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) 
       && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
     {
-      if (peer_sort (peer) == BGP_PEER_IBGP || peer_sort (peer) == BGP_PEER_CONFED)
+      struct attr_extra *attre = attr->extra;
+      
+      assert (attre);
+      
+      if (peer_sort (peer) == BGP_PEER_IBGP 
+          || peer_sort (peer) == BGP_PEER_CONFED)
 	{
-	  if (attr->ecommunity->size * 8 > 255)
+	  if (attre->ecommunity->size * 8 > 255)
 	    {
 	      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
 	      stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
-	      stream_putw (s, attr->ecommunity->size * 8);
+	      stream_putw (s, attre->ecommunity->size * 8);
 	    }
 	  else
 	    {
 	      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
 	      stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
-	      stream_putc (s, attr->ecommunity->size * 8);
+	      stream_putc (s, attre->ecommunity->size * 8);
 	    }
-	  stream_put (s, attr->ecommunity->val, attr->ecommunity->size * 8);
+	  stream_put (s, attre->ecommunity->val, attre->ecommunity->size * 8);
 	}
       else
 	{
@@ -1680,9 +1785,9 @@
 	  int ecom_tr_size = 0;
 	  int i;
 
-	  for (i = 0; i < attr->ecommunity->size; i++)
+	  for (i = 0; i < attre->ecommunity->size; i++)
 	    {
-	      pnt = attr->ecommunity->val + (i * 8);
+	      pnt = attre->ecommunity->val + (i * 8);
 	      tbit = *pnt;
 
 	      if (CHECK_FLAG (tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE))
@@ -1706,9 +1811,9 @@
 		  stream_putc (s, ecom_tr_size * 8);
 		}
 
-	      for (i = 0; i < attr->ecommunity->size; i++)
+	      for (i = 0; i < attre->ecommunity->size; i++)
 		{
-		  pnt = attr->ecommunity->val + (i * 8);
+		  pnt = attre->ecommunity->val + (i * 8);
 		  tbit = *pnt;
 
 		  if (CHECK_FLAG (tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE))
@@ -1721,8 +1826,8 @@
     }
 
   /* Unknown transit attribute. */
-  if (attr->transit)
-    stream_put (s, attr->transit->val, attr->transit->length);
+  if (attr->extra && attr->extra->transit)
+    stream_put (s, attr->extra->transit->val, attr->extra->transit->length);
 
   /* Return total size of attribute. */
   return stream_get_endp (s) - cp;
@@ -1869,11 +1974,12 @@
   /* Aggregator. */
   if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
     {
+      assert (attr->extra);
       stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
       stream_putc (s, BGP_ATTR_AGGREGATOR);
       stream_putc (s, 6);
-      stream_putw (s, attr->aggregator_as);
-      stream_put_ipv4 (s, attr->aggregator_addr.s_addr);
+      stream_putw (s, attr->extra->aggregator_as);
+      stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
     }
 
   /* Community attribute. */
@@ -1896,25 +2002,26 @@
 
 #ifdef HAVE_IPV6
   /* Add a MP_NLRI attribute to dump the IPv6 next hop */
-  if(prefix != NULL && prefix->family == AF_INET6 && 
-     (attr->mp_nexthop_len == 16 || attr->mp_nexthop_len == 32) )
+  if (prefix != NULL && prefix->family == AF_INET6 && attr->extra &&
+     (attr->extra->mp_nexthop_len == 16 || attr->extra->mp_nexthop_len == 32) )
     {
       int sizep;
-
+      struct attr_extra *attre = attr->extra;
+      
       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
       stream_putc(s, BGP_ATTR_MP_REACH_NLRI);
       sizep = stream_get_endp (s);
 
       /* MP header */
-      stream_putc (s, 0);		/* Length of this attribute. */
+      stream_putc (s, 0);		/* Marker: Attribute length. */
       stream_putw(s, AFI_IP6);		/* AFI */
       stream_putc(s, SAFI_UNICAST);	/* SAFI */
 
       /* Next hop */
-      stream_putc(s, attr->mp_nexthop_len);
-      stream_put(s, &attr->mp_nexthop_global, 16);
-      if(attr->mp_nexthop_len == 32)
-        stream_put(s, &attr->mp_nexthop_local, 16);
+      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);
 
       /* SNPA */
       stream_putc(s, 0);