Merge bgpd changeset 1184 from Zebra repository by Rivo Nurges.
diff --git a/bgpd/ChangeLog b/bgpd/ChangeLog
index cdcd83b..3adb996 100644
--- a/bgpd/ChangeLog
+++ b/bgpd/ChangeLog
@@ -1,3 +1,8 @@
+2004-05-20  Akihiro Mizutani <mizutani@net-chef.net>
+
+	* bgp_ecommunity.c: Transit ecommunity support.
+	* bgp_ecommunity.c: Fix for unknown community crush.
+
 2005-05-20 Kunihiro Ishiguro  <kunihiro@ipinfusion.com>
 
 	* *: Maximum prefix threshold support.
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 91a0e07..7d48374 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -1653,19 +1653,67 @@
   if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) 
       && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
     {
-      if (attr->ecommunity->size * 8 > 255)
+      if (peer_sort (peer) == BGP_PEER_IBGP || peer_sort (peer) == BGP_PEER_CONFED)
 	{
-	  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);
+	  if (attr->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);
+	    }
+	  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_put (s, attr->ecommunity->val, attr->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);
+	  u_char *pnt;
+	  int tbit;
+	  int ecom_tr_size = 0;
+	  int i;
+
+	  for (i = 0; i < attr->ecommunity->size; i++)
+	    {
+	      pnt = attr->ecommunity->val + (i * 8);
+	      tbit = *pnt;
+
+	      if (CHECK_FLAG (tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE))
+		continue;
+
+	      ecom_tr_size++;
+	    }
+
+	  if (ecom_tr_size)
+	    {
+	      if (ecom_tr_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, ecom_tr_size * 8);
+		}
+	      else
+		{
+		  stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+		  stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
+		  stream_putc (s, ecom_tr_size * 8);
+		}
+
+	      for (i = 0; i < attr->ecommunity->size; i++)
+		{
+		  pnt = attr->ecommunity->val + (i * 8);
+		  tbit = *pnt;
+
+		  if (CHECK_FLAG (tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE))
+		    continue;
+
+		  stream_put (s, pnt, 8);
+		}
+	    }
 	}
-      stream_put (s, attr->ecommunity->val, attr->ecommunity->size * 8);
     }
 
   /* Unknown transit attribute. */
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 2f9cc94..4adbcf5 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -158,6 +158,15 @@
   return new;
 }
 
+/* Retrun string representation of communities attribute. */
+char *
+ecommunity_str (struct ecommunity *ecom)
+{
+  if (! ecom->str)
+    ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
+  return ecom->str;
+}
+
 /* Merge two Extended Communities Attribute structure.  */
 struct ecommunity *
 ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
@@ -559,24 +568,30 @@
 
   for (i = 0; i < ecom->size; i++)
     {
+      /* Space between each value.  */
+      if (! first)
+	str_buf[str_pnt++] = ' ';
+
       pnt = ecom->val + (i * 8);
 
       /* High-order octet of type. */
       encode = *pnt++;
       if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP)
 	{
-	  if (str_buf)
-	    XFREE (MTYPE_ECOMMUNITY_STR, str_buf);
-	  return "Unknown";
+	  len = sprintf (str_buf + str_pnt, "?");
+	  str_pnt += len;
+	  first = 0;
+	  continue;
 	}
       
       /* Low-order octet of type. */
       type = *pnt++;
       if (type !=  ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
 	{
-	  if (str_buf)
-	    XFREE (MTYPE_ECOMMUNITY_STR, str_buf);
-	  return "Unknown";
+	  len = sprintf (str_buf + str_pnt, "?");
+	  str_pnt += len;
+	  first = 0;
+	  continue;
 	}
 
       switch (format)
@@ -591,9 +606,7 @@
 	  prefix = "";
 	  break;
 	default:
-	  if (str_buf)
-	    XFREE (MTYPE_ECOMMUNITY_STR, str_buf);
-	  return "Unknown";
+	  prefix = "";
 	  break;
 	}
 
@@ -604,10 +617,6 @@
 	  str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
 	}
 
-      /* Space between each value.  */
-      if (! first)
-	str_buf[str_pnt++] = ' ';
-
       /* Put string into buffer.  */
       if (encode == ECOMMUNITY_ENCODE_AS)
 	{
@@ -639,3 +648,33 @@
     }
   return str_buf;
 }
+
+int
+ecommunity_match (struct ecommunity *ecom1, struct ecommunity *ecom2)
+{
+  int i = 0;
+  int j = 0;
+
+  if (ecom1 == NULL && ecom2 == NULL)
+    return 1;
+
+  if (ecom1 == NULL || ecom2 == NULL)
+    return 0;
+
+  if (ecom1->size < ecom2->size)
+    return 0;
+
+  /* Every community on com2 needs to be on com1 for this to match */
+  while (i < ecom1->size && j < ecom2->size)
+    {
+      if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0)
+        j++;
+      i++;
+    }
+
+  if (j == ecom2->size)
+    return 1;
+  else
+    return 0;
+}
+
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index 678d130..e266471 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -34,6 +34,9 @@
 /* Extended Communities value is eight octet long.  */
 #define ECOMMUNITY_SIZE                        8
 
+/* Extended Communities type flag.  */
+#define ECOMMUNITY_FLAG_NON_TRANSITIVE      0x40  
+
 /* Extended Communities attribute.  */
 struct ecommunity
 {
@@ -70,3 +73,5 @@
 unsigned int ecommunity_hash_make (struct ecommunity *);
 struct ecommunity *ecommunity_str2com (char *, int, int);
 char *ecommunity_ecom2str (struct ecommunity *, int);
+int ecommunity_match (struct ecommunity *, struct ecommunity *);
+char *ecommunity_str (struct ecommunity *);
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 9995c02..341a192 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -273,7 +273,7 @@
 #endif /* MPLS_VPN */
 
       if (STREAM_REMAIN (s) 
-	  <= (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + PSIZE (rn->p.prefixlen)))
+	  < (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + PSIZE (rn->p.prefixlen)))
 	break;
 
       if (stream_empty (s))