[bgpd] Add support for AS_PATHLIMIT / draft-ietf-idr-as-pathlimit

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

	* (general) Support for draft-ietf-idr-as-pathlimit-03.
	* bgp_attr.h: (struct attr) Add pathlimit struct
          bgp_attr.c: (attr_str) Add BGP_ATTR_AS_PATHLIMIT string.
          (attrhash_key_make) tally pathlimit too
          (attrhash_cmp) cmp pathlimit attr
          (bgp_attr_aspathlimit) New, parse AS_PATHLIMIT attr.
          (bgp_attr_parse) ditto
          (bgp_packet_attribute) Write out AS_PATHLIMIT when set
          (bgp_dump_routes_attr) ditto
         * bgp_route.h: (struct bgp_static) Add TTL field
         * bgp_route.c: (bgp_announce_check) Drop paths that are over
           their hop-count TTL before sending via EBGP.
           Mangle ASN in pathlimit for confeds/private as best we can.
           (bgp_static_update_{rsclient,main}) Add any configure pathlimit
           information.
           (bgp_pathlimit_update_parents) New, update atomic-aggr setting for
           parents of an aspathlimit'ed static.
           (bgp_static_set) Add TTL argument, for all the 'bgp network'
           commands.
           Call previous for TTL changed statics.
           (bgp_static_unset) Call pathlimit_update_parents.
           (various bgp network commands) Add 'pathlimit <0-255>' qualifier
           to all the various forms, bar route-map - which can set ttl
           itself.
         * bgp_routemap.c: (general) Add support for 'set pathlimit ttl' and
           'match pathlimit as'.
         * doc/bgpd.texi: Document 'network ... pathlimit <ttl>'
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 07c9413..23d9586 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -56,6 +56,8 @@
   { 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" },
   { 0, NULL }
 };
 int attr_str_max = sizeof(attr_str)/sizeof(attr_str[0]);
@@ -345,6 +347,11 @@
   key += attr->nexthop.s_addr;
   key += attr->med;
   key += attr->local_pref;
+  if (attr->pathlimit.as)
+    {
+      key += attr->pathlimit.ttl;
+      key += attr->pathlimit.as;
+    }
   
   if (attr->extra)
     {
@@ -396,7 +403,9 @@
       && 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->pathlimit.ttl == attr2->pathlimit.ttl
+      && attr1->pathlimit.as == attr2->pathlimit.as)
     {
       struct attr_extra *ae1 = attr1->extra;
       struct attr_extra *ae2 = attr2->extra;
@@ -676,6 +685,42 @@
     }
 }
 
+/* Parse AS_PATHLIMIT attribute in an UPDATE */
+static int
+bgp_attr_aspathlimit (struct peer *peer, bgp_size_t length,
+                      struct attr *attr, u_char flag, u_char *startp)
+{
+  bgp_size_t total;
+  
+  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+  
+  if (flag != (BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL))
+    {
+      zlog (peer->log, LOG_ERR, 
+	    "AS-Pathlimit attribute flag isn't transitive %d", flag);
+      bgp_notify_send_with_data (peer, 
+				 BGP_NOTIFY_UPDATE_ERR, 
+				 BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+				 startp, total);
+      return -1;
+    }
+  
+  if (length != 5)
+    {
+      zlog (peer->log, LOG_ERR, 
+	    "AS-Pathlimit length, %u, is not 5", length);
+      bgp_notify_send_with_data (peer, 
+				 BGP_NOTIFY_UPDATE_ERR, 
+				 BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+				 startp, total);
+      return -1;
+    }
+  
+  attr->pathlimit.ttl = stream_getc (BGP_INPUT(peer));
+  attr->pathlimit.as = stream_getl (BGP_INPUT(peer));
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATHLIMIT);
+  return 0;
+}
 /* Get origin attribute of the update message. */
 static int
 bgp_attr_origin (struct peer *peer, bgp_size_t length, 
@@ -1290,7 +1335,7 @@
 	{
 	  /* XXX warning: long int format, int arg (arg 5) */
 	  zlog (peer->log, LOG_WARNING, 
-		"%s error BGP attribute length %ld is smaller than min len",
+		"%s error BGP attribute length %lu is smaller than min len",
 		peer->host, endp - STREAM_PNT (BGP_INPUT (peer)));
 
 	  bgp_notify_send (peer, 
@@ -1386,6 +1431,9 @@
 	case BGP_ATTR_EXT_COMMUNITIES:
 	  ret = bgp_attr_ext_communities (peer, length, attr, flag);
 	  break;
+        case BGP_ATTR_AS_PATHLIMIT:
+          ret = bgp_attr_aspathlimit (peer, length, attr, flag, startp);
+          break;
 	default:
 	  ret = bgp_attr_unknown (peer, attr, flag, type, length, startp);
 	  break;
@@ -1824,7 +1872,25 @@
 	    }
 	}
     }
-
+  
+  /* AS-Pathlimit */
+  if (attr->pathlimit.ttl)
+    {
+      u_int32_t as = attr->pathlimit.as;
+      
+      /* should already have been done in announce_check(), 
+       * but just in case..
+       */
+      if (!as)
+        as = peer->local_as;
+      
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_AS_PATHLIMIT);
+      stream_putc (s, 5);
+      stream_putc (s, attr->pathlimit.ttl);
+      stream_putl (s, as);
+    }
+  
   /* Unknown transit attribute. */
   if (attr->extra && attr->extra->transit)
     stream_put (s, attr->extra->transit->val, attr->extra->transit->length);
@@ -2034,6 +2100,16 @@
     }
 #endif /* HAVE_IPV6 */
 
+  /* AS-Pathlimit */
+  if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AS_PATHLIMIT))
+    {
+      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+      stream_putc (s, BGP_ATTR_AS_PATHLIMIT);
+      stream_putc (s, 5);
+      stream_putc (s, attr->pathlimit.ttl);
+      stream_putl (s, attr->pathlimit.as);
+    }
+
   /* Return total size of attribute. */
   len = stream_get_endp (s) - cp - 2;
   stream_putw_at (s, cp, len);