[bgpd] Merge AS4 support

2007-10-14 Paul Jakma <paul.jakma@sun.com>

	* NEWS: Note that MRT dumps are now version 2
	* (general) Merge in Juergen Kammer's AS4 patch.

2007-09-27 Paul Jakma <paul.jakma@sun.com>

	* bgp_aspath.c: (assegment_normalise) remove duplicates from
	  from sets.
	  (aspath_reconcile_as4) disregard a broken part of the RFC around
	  error handling in path reconciliation.
	* aspath_test.c: Test dupe-weeding from sets.
	  Test that reconciliation merges AS_PATH and AS4_PATH where
	  former is shorter than latter.

2007-09-26 Paul Jakma <paul.jakma@sun.com>

	* aspath_test.c: Test AS4_PATH reconcilation where length
	  of AS_PATH and AS4_PATH is same.

2007-09-25 Paul Jakma <paul.jakma@sun.com>

	* bgp_open.c: (peek_for_as4_capability) Fix to work.
	* bgp_packet.c: (bgp_open_receive) Fix sanity check of as4.
	* tests/bgp_capability_test.c: (general) Extend tests to validate
	  peek_for_as4_capability.
	  Add test of full OPEN Option block, with multiple capabilities,
	  both as a series of Option, and a single option.
	  Add some crap to beginning of stream, to prevent code depending
	  on getp == 0.

2007-09-18 Paul Jakma <paul.jakma@sun.com>

	* bgp_open.c: (bgp_capability_as4) debug printf inline with others.
	  (peek_for_as4_capability) There's no need to signal failure, as
	  failure is better dealt with through full capability parser -
	  just return the AS4, simpler.
	* bgp_packet.c: (bgp_open_receive) Update to match
	  peek_for_as4_capability change.
	  Allow use of BGP_AS_TRANS by 2b speakers.
	  Use NOTIFY_OPEN_ERR rather than CEASE for OPEN parsing errors.
	  (bgp_capability_msg_parse) missing argument to debug print
	  (bgp_capability_receive) missing return values.
	* tests/bgp_capability_test.c: (parse_test) update for changes to
	  peek_for_as4_capability

2007-07-25 Paul Jakma <paul.jakma@sun.com>

	* Remove 2-byte size macros, just make existing macros take
	  argument to indicate which size to use.
	  Adjust all users - typically they want '1'.
	* bgp_aspath.c: (aspath_has_as4) New, return 1 if there are any
	  as4's in a path.
	  (aspath_put) Return the number of bytes actually written, to
	  fix the bug Juergen noted: Splitting of segments will change
	  the number of bytes written from that already written to the
	  AS_PATH header.
	  (aspath_snmp_pathseg) Pass 2-byte flag to aspath_put. SNMP
	  is still defined as 2b.
	  (aspath_aggregate) fix latent bug.
	  (aspath_reconcile_as4) AS_PATH+NEW_AS_PATH reconciliation
	  function.
	  (aspath_key_make) Hash the AS_PATH string, rather than
	  just taking the addition of assegment ASes as the hash value,
	  hopefully sligthly more collision resistant.
	  (bgp_attr_munge_as4_attrs) Collide the NEW_ attributes
	  together with the OLD 2-byte forms, code Juergen
	  had in bgp_attr_parse but re-organised a bit.
	  (bgp_attr_parse) Bunch of code from Juergen moves
	  to previous function.
	  (bgp_packet_attribute) Compact significantly by
	  just /always/ using extended-length attr header.
	  Fix bug Juergen noted, by using aspath_put's
	  (new) returned size value for the attr header rather
	  than the (guesstimate) of aspath_size() - the two could
	  differ when aspath_put had to split large segments, unlikely
	  this bug was ever hit in the 'wild'.
	  (bgp_dump_routes_attr) Always use extended-len and
	  use aspath_put return for header length. Output 4b ASN
	  for AS_PATH and AGGREGATOR.
	* bgp_ecommunity.c: (ecommunity_{hash_make,cmp}) fix
	  hash callback declarations to match prototypes.
	  (ecommunity_gettoken) Updated for ECOMMUNITY_ENCODE_AS4,
	  complete rewrite of Juergen's changes (no asdot support)
	* bgp_open.c: (bgp_capability_as4) New, does what it says
	  on the tin.
	  (peek_for_as4_capability) Rewritten to use streams and
	  bgp_capability_as4.
	* bgp_packet.c: (bgp_open_send) minor edit
	  checked (in the abstract at least) with Juergen.
	  Changes are to be more accepting, e.g, allow AS_TRANS on
	  a 2-byte session.
	* (general) Update all commands to use CMD_AS_RANGE.
	* bgp_vty.c: (bgp_clear) Fix return vals to use CMD_..
	  Remove stuff replicated by VTY_GET_LONG
	  (bgp_clear_vty) Return bgp_clear directly to vty.
	* tests/aspath_test.c: Exercise 32bit parsing. Test reconcile
	  function.
	* tests/ecommunity_test.c: New, test AS4 ecommunity changes,
	  positive test only at this time, error cases not tested yet.

2007-07-25 Juergen Kammer <j.kammer@eurodata.de>

	* (general) AS4 support.
	* bgpd.h: as_t changes to 4-bytes.
	* bgp_aspath.h: Add BGP_AS4_MAX and BGP_AS_TRANS defines.
	* bgp_aspath.c: AS_VALUE_SIZE becomes 4-byte, AS16_VALUE_SIZE
	  added for 2-byte.
	  Add AS16 versions of length calc macros.
	  (aspath_count_numas) New, count number of ASes.
	  (aspath_has_as4) New, return 1 if there are any as4's in a
	  path.
	  (assegments_parse) Interpret assegment as 4 or 2 byte,
	  according to how the caller instructs us, with a new
	  argument.
	  (aspath_parse) Add use32bit argument to pass to
	  assegments_parse. Adjust all its callers to pass 1, unless
	  otherwise noted.
	  (assegment_data_put) Adjust to be able to write 2 or 4 byte
	  AS, according to new use32bit argument.
	  (aspath_put) Adjust to write 2 or 4.
	  (aspath_gettoken) Use a long for passed in asno.
	* bgp_attr.c: (attr_str) Add BGP_ATTR_AS4_PATH and
	  BGP_ATTR_AS4_AGGREGATOR.
	  (bgp_attr_aspath) Call aspath_parse with right 2/4 arg, as
	  determined by received-capability flag.
	  (bgp_attr_aspath_check) New, code previously in attr_aspath
	  but moved to new func so it can be run after NEW_AS_PATH
	  reconciliation.
	  (bgp_attr_as4_path) New, handle NEW_AS_PATH.
	  (bgp_attr_aggregator) Adjust to cope with 2/4 byte ASes.
	  (bgp_attr_as4_aggregator) New, read NEW_AGGREGATOR.
	  (bgp_attr_parse) Add handoffs to previous parsers for the two
	  new AS4 NEW_ attributes.
	  Various checks added for NEW/OLD reconciliation.
	  (bgp_packet_attribute) Support 2/4 for AS_PATH and
	  AGGREGATOR, detect when NEW_ attrs need to be sent.
	* bgp_debug.{c,h}: Add 'debug bgp as4'.
	* bgp_dump.c: MRTv2 support, unconditionally enabled, which
	  supports AS4. Based on patches from Erik (RIPE?).
	* bgp_ecommunity.c: (ecommunity_ecom2str) ECOMMUNITY_ENCODE_AS4
	  support.
	* bgp_open.c: (peek_for_as4_capability) New, peek for AS4
	  capability prior to full capability parsing, so we know which
	  ASN to use for struct peer lookup.
	  (bgp_open_capability) Always send AS4 capability.
	* bgp_packet.c: (bgp_open_send) AS4 handling for AS field
	  (bgp_open_receive) Peek for AS4 capability first, and figure
	  out which AS to believe.
	* bgp_vty.c: (bgp_show_peer) Print AS4 cap
	* tests/aspath_test.c: Support asn32 changes, call aspath_parse
	  with 16 bit.
	* vtysh/extract.pl: AS4 compatibility for router bgp ASNUMBER
	* vtysh/extract.pl.in: AS4 compatibility for router bgp ASNUMBER
	* vtysh/vtysh.c: AS4 compatibility for router bgp ASNUMBER
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 9d13ca6..b463b3c 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -56,8 +56,10 @@
   { BGP_ATTR_RCID_PATH,        "RCID_PATH" },
   { BGP_ATTR_MP_REACH_NLRI,    "MP_REACH_NLRI" },
   { BGP_ATTR_MP_UNREACH_NLRI,  "MP_UNREACH_NLRI" },
-  { BGP_ATTR_EXT_COMMUNITIES,  "BGP_ATTR_EXT_COMMUNITIES" },
-  { BGP_ATTR_AS_PATHLIMIT,     "BGP_ATTR_AS_PATHLIMIT" },
+  { BGP_ATTR_EXT_COMMUNITIES,  "EXT_COMMUNITIES" },
+  { BGP_ATTR_AS4_PATH,         "AS4_PATH" }, 
+  { BGP_ATTR_AS4_AGGREGATOR,   "AS4_AGGREGATOR" }, 
+  { BGP_ATTR_AS_PATHLIMIT,     "AS_PATHLIMIT" },
   { 0, NULL }
 };
 int attr_str_max = sizeof(attr_str)/sizeof(attr_str[0]);
@@ -794,8 +796,6 @@
 bgp_attr_aspath (struct peer *peer, bgp_size_t length, 
 		 struct attr *attr, u_char flag, u_char *startp)
 {
-  struct bgp *bgp;
-  struct aspath *aspath;
   bgp_size_t total;
 
   total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
@@ -813,8 +813,14 @@
       return -1;
     }
 
+  /*
+   * 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. */
-  attr->aspath = aspath_parse (peer->ibuf, length);
   if (! attr->aspath)
     {
       zlog (peer->log, LOG_ERR, "Malformed AS path length is %d", length);
@@ -824,6 +830,28 @@
       return -1;
     }
 
+  /* Forward pointer. */
+/*  stream_forward_getp (peer->ibuf, length);*/
+
+  /* Set aspath attribute flag. */
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
+
+  return 0;
+}
+
+static int bgp_attr_aspath_check( struct peer *peer, 
+		struct attr *attr)
+{
+  /* These checks were part of bgp_attr_aspath, but with
+   * as4 we should to check aspath things when
+   * aspath synthesizing with as4_path has already taken place.
+   * Otherwise we check ASPATH and use the synthesized thing, and that is
+   * not right.
+   * So do the checks later, i.e. here
+   */
+  struct bgp *bgp = peer->bgp;
+  struct aspath *aspath;
+
   bgp = peer->bgp;
     
   /* First AS check for EBGP. */
@@ -851,11 +879,20 @@
       attr->aspath = aspath_intern (aspath);
     }
 
-  /* Forward pointer. */
-/*  stream_forward_getp (peer->ibuf, length);*/
+  return 0;
+
+}
+
+/* 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, struct aspath **as4_path)
+{
+  *as4_path = aspath_parse (peer->ibuf, length, 1);
 
   /* Set aspath attribute flag. */
-  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH);
 
   return 0;
 }
@@ -981,18 +1018,27 @@
 bgp_attr_aggregator (struct peer *peer, bgp_size_t length,
 		     struct attr *attr, u_char flag)
 {
+  int wantedlen = 6;
   struct attr_extra *attre = bgp_attr_extra_get (attr);
   
-  if (length != 6)
+  /* 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 length is not 6 [%d]", length);
+      zlog (peer->log, LOG_ERR, "Aggregator length is not %d [%d]", wantedlen, length);
 
       bgp_notify_send (peer,
 		       BGP_NOTIFY_UPDATE_ERR,
 		       BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
       return -1;
     }
-  attre->aggregator_as = stream_getw (peer->ibuf);
+  
+  if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) )
+    attre->aggregator_as = stream_getl (peer->ibuf);
+  else
+    attre->aggregator_as = stream_getw (peer->ibuf);
   attre->aggregator_addr.s_addr = stream_get_ipv4 (peer->ibuf);
 
   /* Set atomic aggregate flag. */
@@ -1001,6 +1047,145 @@
   return 0;
 }
 
+/* New Aggregator attribute */
+static int
+bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length,
+		     struct attr *attr, as_t *as4_aggregator_as,
+		     struct in_addr *as4_aggregator_addr)
+{
+  if (length != 8)
+    {
+      zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]", length);
+
+      bgp_notify_send (peer,
+		       BGP_NOTIFY_UPDATE_ERR,
+		       BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+      return -1;
+    }
+  *as4_aggregator_as = stream_getl (peer->ibuf);
+  as4_aggregator_addr->s_addr = stream_get_ipv4 (peer->ibuf);
+
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR);
+
+  return 0;
+}
+
+/* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH.
+ */
+static int
+bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *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 ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
+    {
+      /* peer can do AS4, so we ignore AS4_PATH and AS4_AGGREGATOR
+       * if given.
+       * It is worth a warning though, because the peer really
+       * should not send them
+       */
+      if (BGP_DEBUG(as4, AS4))
+        {
+          if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH)))
+            zlog_debug ("[AS4] %s %s AS4_PATH",
+                        peer->host, "AS4 capable peer, yet it sent");
+          
+          if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR)))
+            zlog_debug ("[AS4] %s %s AS4_AGGREGATOR",
+                        peer->host, "AS4 capable peer, yet it sent");
+        }
+      
+      return 0;
+    }
+  
+  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);
+      bgp_notify_send (peer, 
+                       BGP_NOTIFY_UPDATE_ERR, 
+                       BGP_NOTIFY_UPDATE_MAL_ATTR);
+      return -1;
+    }
+
+  /* We have a asn16 peer.  First, look for AS4_AGGREGATOR
+   * because that may override AS4_PATH
+   */
+  if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR) ) )
+    {
+      assert (attre);
+      
+      if ( attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR) ) )
+        {
+          /* received both.
+           * if the as_number in aggregator is not AS_TRANS,
+           *  then AS4_AGGREGATOR and AS4_PATH shall be ignored
+           *        and the Aggregator shall be taken as 
+           *        info on the aggregating node, and the AS_PATH
+           *        shall be taken as the AS_PATH
+           *  otherwise
+           *        the Aggregator shall be ignored and the
+           *        AS4_AGGREGATOR shall be taken as the
+           *        Aggregating node and the AS_PATH is to be
+           *        constructed "as in all other cases"
+           */
+          if ( attre->aggregator_as != BGP_AS_TRANS )
+            {
+              /* ignore */
+              if ( BGP_DEBUG(as4, AS4))
+                zlog_debug ("[AS4] %s BGP not AS4 capable peer" 
+                            " send AGGREGATOR != AS_TRANS and"
+                            " AS4_AGGREGATOR, so ignore"
+                            " AS4_AGGREGATOR and AS4_PATH", peer->host);
+              ignore_as4_path = 1;
+            }
+          else
+            {
+              /* "New_aggregator shall be taken as aggregator" */
+              attre->aggregator_as = as4_aggregator;
+              attre->aggregator_addr.s_addr = as4_aggregator_addr->s_addr;
+            }
+        }
+      else
+        {
+          /* We received a AS4_AGGREGATOR but no AGGREGATOR.
+           * That is bogus - but reading the conditions
+           * we have to handle AS4_AGGREGATOR as if it were
+           * AGGREGATOR in that case
+           */
+          if ( BGP_DEBUG(as4, AS4))
+            zlog_debug ("[AS4] %s BGP not AS4 capable peer send"
+                        " AS4_AGGREGATOR but no AGGREGATOR, will take"
+                        " it as if AGGREGATOR with AS_TRANS had been there", peer->host);
+          attre->aggregator_as = as4_aggregator;
+          /* sweep it under the carpet and simulate a "good" AGGREGATOR */
+          attr->flag |= (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR));
+        }
+    }
+
+  /* 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);
+    }
+  return 0;
+}
+
 /* Community attribute. */
 static int
 bgp_attr_community (struct peer *peer, bgp_size_t length, 
@@ -1318,11 +1503,16 @@
 {
   int ret;
   u_char flag;
-  u_char type;
+  u_char type = 0;
   bgp_size_t length;
   u_char *startp, *endp;
   u_char *attr_endp;
   u_char seen[BGP_ATTR_BITMAP_SIZE];
+  /* we need the as4_path only until we have synthesized the as_path with it */
+  /* same goes for as4_aggregator */
+  struct aspath *as4_path = NULL;
+  as_t as4_aggregator = 0;
+  struct in_addr as4_aggregator_addr = { 0 };
 
   /* Initialize bitmap. */
   memset (seen, 0, BGP_ATTR_BITMAP_SIZE);
@@ -1339,7 +1529,8 @@
 	  /* 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, endp - STREAM_PNT (BGP_INPUT (peer)));
+		peer->host,
+		(unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer))));
 
 	  bgp_notify_send (peer, 
 			   BGP_NOTIFY_UPDATE_ERR, 
@@ -1401,6 +1592,9 @@
 	case BGP_ATTR_AS_PATH:
 	  ret = bgp_attr_aspath (peer, length, attr, flag, startp);
 	  break;
+	case BGP_ATTR_AS4_PATH:
+	  ret = bgp_attr_as4_path (peer, length, attr, &as4_path );
+	  break;
 	case BGP_ATTR_NEXT_HOP:	
 	  ret = bgp_attr_nexthop (peer, length, attr, flag, startp);
 	  break;
@@ -1416,6 +1610,9 @@
 	case BGP_ATTR_AGGREGATOR:
 	  ret = bgp_attr_aggregator (peer, length, attr, flag);
 	  break;
+	case BGP_ATTR_AS4_AGGREGATOR:
+	  ret = bgp_attr_as4_aggregator (peer, length, attr, &as4_aggregator, &as4_aggregator_addr);
+	  break;
 	case BGP_ATTR_COMMUNITIES:
 	  ret = bgp_attr_community (peer, length, attr, flag);
 	  break;
@@ -1480,6 +1677,51 @@
       return -1;
     }
 
+  /* 
+   * At this place we can see whether we got AS4_PATH and/or
+   * AS4_AGGREGATOR from a 16Bit peer and act accordingly.
+   * We can not do this before we've read all attributes because
+   * the as4 handling does not say whether AS4_PATH has to be sent
+   * after AS_PATH or not - and when AS4_AGGREGATOR will be send
+   * in relationship to AGGREGATOR.
+   * 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.
+   */
+  if (bgp_attr_munge_as4_attrs (peer, attr, as4_path,
+                                as4_aggregator, &as4_aggregator_addr))
+    return -1;
+
+  /* At this stage, we have done all fiddling with as4, and the
+   * resulting info is in attr->aggregator resp. attr->aspath
+   * so we can chuck as4_aggregator and as4_path alltogether in
+   * order to save memory
+   */
+  if ( as4_path )
+    {
+      aspath_unintern( as4_path ); /* unintern - it is in the hash */
+      as4_path = NULL;
+      /* The flag that we got this is still there, but that does not
+       * do any trouble
+       */
+    }
+  /*
+   * The "rest" of the code does nothing with as4_aggregator.
+   * there is no memory attached specifically which is not part
+   * of the attr.
+   * so ignoring just means do nothing.
+   */
+  /*
+   * Finally do the checks on the aspath we did not do yet
+   * because we waited for a potentially synthesized aspath.
+   */
+  if ( attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS_PATH)))
+    {
+      ret = bgp_attr_aspath_check( peer, attr );
+      if ( ret < 0 )
+	return ret;
+    }
+
   /* Finally intern unknown attribute. */
   if (attr->extra && attr->extra->transit)
     attr->extra->transit = transit_intern (attr->extra->transit);
@@ -1530,8 +1772,11 @@
 		      struct prefix_rd *prd, u_char *tag)
 {
   size_t cp;
-  unsigned int aspath_data_size;
+  size_t aspath_sizep;
   struct aspath *aspath;
+  int send_as4_path = 0;
+  int send_as4_aggregator = 0;
+  int use32bit = (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) ? 1 : 0;
 
   if (! bgp)
     bgp = bgp_get_default ();
@@ -1578,25 +1823,27 @@
   else
     aspath = attr->aspath;
 
-  /* AS path attribute extended length bit check. */
-  aspath_data_size = aspath_size (aspath);
-  if (aspath_data_size > 255)
-    {
-      stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
-      stream_putc (s, BGP_ATTR_AS_PATH);
-      stream_putw (s, aspath_data_size);
-    }
-  else
-    {
-      stream_putc (s, BGP_ATTR_FLAG_TRANS);
-      stream_putc (s, BGP_ATTR_AS_PATH);
-      stream_putc (s, aspath_data_size);
-    }
-  aspath_put (s, aspath);
+  /* If peer is not AS4 capable, then:
+   * - send the created AS_PATH out as AS4_PATH (optional, transitive),
+   *   but ensure that no AS_CONFED_SEQUENCE and AS_CONFED_SET path segment
+   *   types are in it (i.e. exclude them if they are there)
+   *   AND do this only if there is at least one asnum > 65535 in the path!
+   * - send an AS_PATH out, but put 16Bit ASnums in it, not 32bit, and change
+   *   all ASnums > 65535 to BGP_AS_TRANS
+   */
 
-  if (aspath != attr->aspath)
-    aspath_free (aspath);
-
+  stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+  stream_putc (s, BGP_ATTR_AS_PATH);
+  aspath_sizep = stream_get_endp (s);
+  stream_putw (s, 0);
+  stream_putw_at (s, aspath_sizep, aspath_put (s, aspath, use32bit));
+  
+  /* OLD session may need NEW_AS_PATH sent, if there are 4-byte ASNs 
+   * in the path
+   */
+  if (!use32bit && aspath_has_as4 (aspath))
+      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)
     {
@@ -1645,10 +1892,36 @@
   if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
     {
       assert (attr->extra);
+      
+      /* Common to BGP_ATTR_AGGREGATOR, regardless of ASN size */
       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->extra->aggregator_as);
+      
+      if (use32bit)
+        {
+          /* AS4 capable peer */
+          stream_putc (s, 8);
+          stream_putl (s, attr->extra->aggregator_as);
+        }
+      else
+        {
+          /* 2-byte AS peer */
+          stream_putc (s, 6);
+          
+          /* Is ASN representable in 2-bytes? Or must AS_TRANS be used? */
+          if ( attr->extra->aggregator_as > 65535 )
+            {
+              stream_putw (s, BGP_AS_TRANS);
+              
+              /* we have to send AS4_AGGREGATOR, too.
+               * we'll do that later in order to send attributes in ascending
+               * order.
+               */
+              send_as4_aggregator = 1;
+            }
+          else
+            stream_putw (s, (u_int16_t) attr->extra->aggregator_as);
+        }
       stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
     }
 
@@ -1873,6 +2146,47 @@
 	    }
 	}
     }
+
+  if ( send_as4_path )
+    {
+      /* If the peer is NOT As4 capable, AND */
+      /* there are ASnums > 65535 in path  THEN
+       * give out AS4_PATH */
+
+      /* Get rid of all AS_CONFED_SEQUENCE and AS_CONFED_SET
+       * path segments!
+       * Hm, I wonder...  confederation things *should* only be at
+       * the beginning of an aspath, right?  Then we should use
+       * aspath_delete_confed_seq for this, because it is already
+       * there! (JK) 
+       * Folks, talk to me: what is reasonable here!?
+       */
+      aspath = aspath_delete_confed_seq (aspath);
+
+      stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
+      stream_putc (s, BGP_ATTR_AS4_PATH);
+      aspath_sizep = stream_get_endp (s);
+      stream_putw (s, 0);
+      stream_putw_at (s, aspath_sizep, aspath_put (s, aspath, 1));
+    }
+
+  if (aspath != attr->aspath)
+    aspath_free (aspath);
+
+  if ( send_as4_aggregator ) 
+    {
+      assert (attr->extra);
+
+      /* send AS4_AGGREGATOR, at this place */
+      /* this section of code moved here in order to ensure the correct
+       * *ascending* order of attributes
+       */
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_AS4_AGGREGATOR);
+      stream_putc (s, 8);
+      stream_putl (s, attr->extra->aggregator_as);
+      stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
+    }
   
   /* AS-Pathlimit */
   if (attr->pathlimit.ttl)
@@ -1967,7 +2281,7 @@
 {
   unsigned long cp;
   unsigned long len;
-  unsigned int aspathlen;
+  size_t aspath_lenp;
   struct aspath *aspath;
 
   /* Remember current pointer. */
@@ -1983,20 +2297,13 @@
   stream_putc (s, attr->origin);
 
   aspath = attr->aspath;
-
-  if ( (aspathlen = aspath_size (aspath)) > 255 )
-    {
-      stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
-      stream_putc (s, BGP_ATTR_AS_PATH);
-      stream_putw (s, aspathlen);
-    }
-  else
-    {
-      stream_putc (s, BGP_ATTR_FLAG_TRANS);
-      stream_putc (s, BGP_ATTR_AS_PATH);
-      stream_putc (s, aspathlen);
-    }
-  aspath_put (s, aspath);
+  
+  stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+  stream_putc (s, BGP_ATTR_AS_PATH);
+  aspath_lenp = stream_get_endp (s);
+  stream_putw (s, 0);
+  
+  stream_putw_at (s, aspath_lenp, aspath_put (s, aspath, 1));
 
   /* Nexthop attribute. */
   /* If it's an IPv6 prefix, don't dump the IPv4 nexthop to save space */
@@ -2044,8 +2351,8 @@
       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->extra->aggregator_as);
+      stream_putc (s, 8);
+      stream_putl (s, attr->extra->aggregator_as);
       stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
     }