]> git.puffer.fish Git - matthieu/frr.git/commitdiff
Support for BGP Large Communities
authorJob Snijders <job@instituut.net>
Tue, 15 Nov 2016 10:00:39 +0000 (19:00 +0900)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 18 Jan 2017 23:38:53 +0000 (18:38 -0500)
BGP Large Communities are a novel way to signal information between
networks. An example of a Large Community is: "2914:65400:38016". Large
BGP Communities are composed of three 4-byte integers, separated by a
colon. This is easy to remember and accommodates advanced routing
policies in relation to 4-Byte ASNs.

This feature was developed by:
Keyur Patel <keyur@arrcus.com> (Arrcus, Inc.),
Job Snijders <job@ntt.net> (NTT Communications),
David Lamparter <equinox@opensourcerouting.org>
and Donald Sharp <sharpd@cumulusnetworks.com>

Signed-off-by: Job Snijders <job@ntt.net>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
20 files changed:
bgpd/Makefile.am
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_clist.c
bgpd/bgp_clist.h
bgpd/bgp_ecommunity.c
bgpd/bgp_encap.c
bgpd/bgp_lcommunity.c [new file with mode: 0644]
bgpd/bgp_lcommunity.h [new file with mode: 0644]
bgpd/bgp_memory.c
bgpd/bgp_memory.h
bgpd/bgp_mpath.c
bgpd/bgp_packet.c
bgpd/bgp_route.c
bgpd/bgp_routemap.c
bgpd/bgp_vty.c
bgpd/bgpd.c
bgpd/bgpd.h
lib/routemap.c
lib/routemap.h

index 69c0504af45145f519ba3714b65cdb757b5719e7..6e36a950d6642e75b763bd7de95bd5b256805804 100644 (file)
@@ -75,7 +75,8 @@ libbgp_a_SOURCES = \
        bgpd.c bgp_fsm.c bgp_aspath.c bgp_community.c bgp_attr.c \
        bgp_debug.c bgp_route.c bgp_zebra.c bgp_open.c bgp_routemap.c \
        bgp_packet.c bgp_network.c bgp_filter.c bgp_regex.c bgp_clist.c \
-       bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \
+       bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_lcommunity.c \
+       bgp_mplsvpn.c bgp_nexthop.c \
        bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \
         bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c \
        bgp_encap.c bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC)
@@ -85,7 +86,8 @@ noinst_HEADERS = \
        bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \
        bgp_network.h bgp_open.h bgp_packet.h bgp_regex.h bgp_route.h \
        bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \
-       bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \
+       bgp_ecommunity.h bgp_lcommunity.h \
+       bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \
        bgp_advertise.h bgp_snmp.h bgp_vty.h bgp_mpath.h bgp_nht.h \
         bgp_updgrp.h bgp_bfd.h bgp_encap.h bgp_encap_tlv.h bgp_encap_types.h \
        $(BGP_VNC_RFAPI_HD) 
index 4cf1f0daed7ac3f0d88917e106b905b59c2c3172..fc9440dddc8ffe5912c946e372f725b7df7be465 100644 (file)
@@ -42,6 +42,7 @@ 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"
+#include "bgpd/bgp_lcommunity.h"
 #include "bgpd/bgp_updgrp.h"
 #include "bgpd/bgp_encap_types.h"
 #if ENABLE_BGP_VNC
@@ -76,6 +77,7 @@ static const struct message attr_str [] =
 #if ENABLE_BGP_VNC
   { BGP_ATTR_VNC,              "VNC" },
 #endif
+  { BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY" }
 };
 static const int attr_str_max = array_size(attr_str);
 
@@ -670,6 +672,8 @@ attrhash_key_make (void *p)
   
   if (extra)
     {
+      if (extra->lcommunity)
+       MIX(lcommunity_hash_make (extra->lcommunity));
       if (extra->ecommunity)
         MIX(ecommunity_hash_make (extra->ecommunity));
       if (extra->cluster)
@@ -718,6 +722,7 @@ attrhash_cmp (const void *p1, const void *p2)
           && IPV6_ADDR_SAME (&ae1->mp_nexthop_local, &ae2->mp_nexthop_local)
           && IPV4_ADDR_SAME (&ae1->mp_nexthop_global_in, &ae2->mp_nexthop_global_in)
           && ae1->ecommunity == ae2->ecommunity
+         && ae1->lcommunity == ae2->lcommunity
           && ae1->cluster == ae2->cluster
           && ae1->transit == ae2->transit
          && (ae1->encap_tunneltype == ae2->encap_tunneltype)
@@ -836,6 +841,13 @@ bgp_attr_intern (struct attr *attr)
             attre->ecommunity->refcnt++;
           
         }
+      if (attre->lcommunity)
+        {
+          if (! attre->lcommunity->refcnt)
+            attre->lcommunity = lcommunity_intern (attre->lcommunity);
+          else
+            attre->lcommunity->refcnt++;
+        }
       if (attre->cluster)
         {
           if (! attre->cluster->refcnt)
@@ -1026,6 +1038,10 @@ bgp_attr_unintern_sub (struct attr *attr)
       if (attr->extra->ecommunity)
         ecommunity_unintern (&attr->extra->ecommunity);
       UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES));
+
+      if (attr->extra->lcommunity)
+        lcommunity_unintern (&attr->extra->lcommunity);
+      UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES));
       
       if (attr->extra->cluster)
         cluster_unintern (attr->extra->cluster);
@@ -1096,6 +1112,8 @@ bgp_attr_flush (struct attr *attr)
 
       if (attre->ecommunity && ! attre->ecommunity->refcnt)
         ecommunity_free (&attre->ecommunity);
+      if (attre->lcommunity && ! attre->lcommunity->refcnt)
+       lcommunity_free (&attre->lcommunity);
       if (attre->cluster && ! attre->cluster->refcnt)
         {
           cluster_free (attre->cluster);
@@ -1254,6 +1272,7 @@ const u_int8_t attr_flags_values [] = {
   [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,
+  [BGP_ATTR_LARGE_COMMUNITIES] = BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
 };
 static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
 
@@ -2042,6 +2061,40 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
   return BGP_ATTR_PARSE_PROCEED;
 }
 
+/* Large Community attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_large_community (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;
+
+  /*
+   * Large community follows new attribute format.
+   */
+  if (length == 0)
+    {
+      if (attr->extra)
+        attr->extra->lcommunity = NULL;
+      /* Empty extcomm doesn't seem to be invalid per se */
+      return BGP_ATTR_PARSE_PROCEED;
+    }
+
+  (bgp_attr_extra_get (attr))->lcommunity =
+    lcommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length);
+  /* XXX: fix ecommunity_parse to use stream API */
+  stream_forward_getp (peer->ibuf, length);
+
+  if (attr->extra && !attr->extra->lcommunity)
+    return bgp_attr_malformed (args,
+                               BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                               args->total);
+
+  attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
+
+  return BGP_ATTR_PARSE_PROCEED;
+}
+
 /* Extended Community attribute. */
 static bgp_attr_parse_ret_t
 bgp_attr_ext_communities (struct bgp_attr_parser_args *args)
@@ -2063,7 +2116,7 @@ bgp_attr_ext_communities (struct bgp_attr_parser_args *args)
   /* XXX: fix ecommunity_parse to use stream API */
   stream_forward_getp (peer->ibuf, length);
   
-  if (!attr->extra->ecommunity)
+  if (attr->extra && !attr->extra->ecommunity)
     return bgp_attr_malformed (args,
                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
                                args->total);
@@ -2477,6 +2530,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
        case BGP_ATTR_COMMUNITIES:
          ret = bgp_attr_community (&attr_args);
          break;
+       case BGP_ATTR_LARGE_COMMUNITIES:
+         ret = bgp_attr_large_community (&attr_args);
+         break;
        case BGP_ATTR_ORIGINATOR_ID:
          ret = bgp_attr_originator_id (&attr_args);
          break;
@@ -3101,6 +3157,28 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
       stream_put (s, attr->community->val, attr->community->size * 4);
     }
 
+  /*
+   * Large Community attribute.
+   */
+  if (attr->extra &&
+      CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)
+      && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES)))
+    {
+      if (attr->extra->lcommunity->size * 12 > 255)
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+         stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES);
+         stream_putw (s, attr->extra->lcommunity->size * 12);
+       }
+      else
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+         stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES);
+         stream_putc (s, attr->extra->lcommunity->size * 12);
+       }
+      stream_put (s, attr->extra->lcommunity->val, attr->extra->lcommunity->size * 12);
+    }
+
   /* Route Reflector. */
   if (peer->sort == BGP_PEER_IBGP
       && from
@@ -3333,6 +3411,7 @@ bgp_attr_init (void)
   attrhash_init ();
   community_init ();
   ecommunity_init ();
+  lcommunity_init ();
   cluster_init ();
   transit_init ();
   encap_init ();
@@ -3345,6 +3424,7 @@ bgp_attr_finish (void)
   attrhash_finish ();
   community_finish ();
   ecommunity_finish ();
+  lcommunity_finish ();
   cluster_finish ();
   transit_finish ();
   encap_finish ();
@@ -3448,6 +3528,25 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
       stream_put (s, attr->community->val, attr->community->size * 4);
     }
 
+  /* Large Community attribute. */
+  if (attr->extra && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES))
+    {
+      if (attr->extra->lcommunity->size * 12 > 255)
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+         stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES);
+         stream_putw (s, attr->extra->lcommunity->size * 12);
+       }
+      else
+       {
+         stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+         stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES);
+         stream_putc (s, attr->extra->lcommunity->size * 12);
+       }
+
+      stream_put (s, attr->extra->lcommunity->val, attr->extra->lcommunity->size * 12);
+    }
+
   /* 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 == BGP_ATTR_NHLEN_IPV6_GLOBAL ||
index 6e639078d60a014f7a1c4df3f1aa89b0a507c0bd..9c931051d8759d835af259847360a473a2f8b4e2 100644 (file)
@@ -92,6 +92,9 @@ struct attr_extra
 
   /* Extended Communities attribute. */
   struct ecommunity *ecommunity;
+
+  /* Large Communities attribute. */
+  struct lcommunity *lcommunity;
   
   /* Route-Reflector Cluster attribute */
   struct cluster_list *cluster;
index 9032b1e2f4fbaef48791deaa241184d0e54d5733..47192f0f03c677aaa777ae26e80e3cc3556dadda 100644 (file)
@@ -29,6 +29,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_community.h"
 #include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
 #include "bgpd/bgp_aspath.h"
 #include "bgpd/bgp_regex.h"
 #include "bgpd/bgp_clist.h"
@@ -45,6 +46,8 @@ community_list_master_lookup (struct community_list_handler *ch, int master)
        return &ch->community_list;
       case EXTCOMMUNITY_LIST_MASTER:
        return &ch->extcommunity_list;
+      case LARGE_COMMUNITY_LIST_MASTER:
+       return &ch->lcommunity_list;
       }
   return NULL;
 }
@@ -66,6 +69,10 @@ community_entry_free (struct community_entry *entry)
       if (entry->u.com)
         community_free (entry->u.com);
       break;
+    case LARGE_COMMUNITY_LIST_STANDARD:
+      if (entry->u.lcom)
+        lcommunity_free (&entry->u.lcom);
+      break;
     case EXTCOMMUNITY_LIST_STANDARD:
       /* In case of standard extcommunity-list, configuration string
          is made by ecommunity_ecom2str().  */
@@ -76,6 +83,7 @@ community_entry_free (struct community_entry *entry)
       break;
     case COMMUNITY_LIST_EXPANDED:
     case EXTCOMMUNITY_LIST_EXPANDED:
+    case LARGE_COMMUNITY_LIST_EXPANDED:
       if (entry->config)
         XFREE (MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
       if (entry->reg)
@@ -320,8 +328,13 @@ community_list_entry_lookup (struct community_list *list, const void *arg,
           if (entry->direct == direct && ecommunity_cmp (entry->u.ecom, arg))
             return entry;
           break;
+        case LARGE_COMMUNITY_LIST_STANDARD:
+          if (entry->direct == direct && lcommunity_cmp (entry->u.lcom, arg))
+            return entry;
+          break;
         case COMMUNITY_LIST_EXPANDED:
         case EXTCOMMUNITY_LIST_EXPANDED:
+        case LARGE_COMMUNITY_LIST_EXPANDED:
           if (entry->direct == direct && strcmp (entry->config, arg) == 0)
             return entry;
           break;
@@ -447,6 +460,91 @@ community_regexp_match (struct community *com, regex_t * reg)
   return 0;
 }
 
+static char *
+lcommunity_str_get (struct lcommunity *lcom, int i)
+{
+  struct lcommunity_val lcomval;
+  u_int32_t globaladmin;
+  u_int32_t localdata1;
+  u_int32_t localdata2;
+  char *str;
+  u_char *ptr;
+  char *pnt;
+
+  ptr = lcom->val;
+  ptr += (i * LCOMMUNITY_SIZE);
+
+  memcpy (&lcomval, ptr, LCOMMUNITY_SIZE);
+
+  /* Allocate memory.  48 bytes taken off bgp_lcommunity.c */
+  str = pnt = XMALLOC (MTYPE_LCOMMUNITY_STR, 48);
+
+  ptr = (u_char *)lcomval.val;
+  globaladmin = (*ptr++ << 24);
+  globaladmin |= (*ptr++ << 16);
+  globaladmin |= (*ptr++ << 8);
+  globaladmin |= (*ptr++);
+
+  localdata1 = (*ptr++ << 24);
+  localdata1 |= (*ptr++ << 16);
+  localdata1 |= (*ptr++ << 8);
+  localdata1 |= (*ptr++);
+
+  localdata2 = (*ptr++ << 24);
+  localdata2 |= (*ptr++ << 16);
+  localdata2 |= (*ptr++ << 8);
+  localdata2 |= (*ptr++);
+
+  sprintf (pnt, "%u:%u:%u", globaladmin, localdata1, localdata2);
+  pnt += strlen (pnt);
+  *pnt = '\0';
+
+  return str;
+}
+
+/* Internal function to perform regular expression match for
+ *  * a single community. */
+static int
+lcommunity_regexp_include (regex_t * reg, struct lcommunity *lcom, int i)
+{
+  const char *str;
+
+  /* When there is no communities attribute it is treated as empty
+ *      string.  */
+  if (lcom == NULL || lcom->size == 0)
+    str = "";
+  else
+    str = lcommunity_str_get (lcom, i);
+
+  /* Regular expression match.  */
+  if (regexec (reg, str, 0, NULL, 0) == 0)
+    return 1;
+
+  /* No match.  */
+  return 0;
+}
+
+static int
+lcommunity_regexp_match (struct lcommunity *com, regex_t * reg)
+{
+  const char *str;
+
+  /* When there is no communities attribute it is treated as empty
+     string.  */
+  if (com == NULL || com->size == 0)
+    str = "";
+  else
+    str = lcommunity_str (com);
+
+  /* Regular expression match.  */
+  if (regexec (reg, str, 0, NULL, 0) == 0)
+    return 1;
+
+  /* No match.  */
+  return 0;
+}
+
+
 static int
 ecommunity_regexp_match (struct ecommunity *ecom, regex_t * reg)
 {
@@ -546,6 +644,30 @@ community_list_match (struct community *com, struct community_list *list)
   return 0;
 }
 
+int
+lcommunity_list_match (struct lcommunity *lcom, struct community_list *list)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      if (entry->any)
+        return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+
+      if (entry->style == LARGE_COMMUNITY_LIST_STANDARD)
+        {
+          if (lcommunity_match (lcom, entry->u.lcom))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+        }
+      else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED)
+        {
+          if (lcommunity_regexp_match (lcom, entry->reg))
+            return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+        }
+    }
+  return 0;
+}
+
 int
 ecommunity_list_match (struct ecommunity *ecom, struct community_list *list)
 {
@@ -694,12 +816,17 @@ community_list_dup_check (struct community_list *list,
           if (community_cmp (entry->u.com, new->u.com))
             return 1;
           break;
+        case LARGE_COMMUNITY_LIST_STANDARD:
+          if (lcommunity_cmp (entry->u.lcom, new->u.lcom))
+            return 1;
+          break;
         case EXTCOMMUNITY_LIST_STANDARD:
           if (ecommunity_cmp (entry->u.ecom, new->u.ecom))
             return 1;
           break;
         case COMMUNITY_LIST_EXPANDED:
         case EXTCOMMUNITY_LIST_EXPANDED:
+       case LARGE_COMMUNITY_LIST_EXPANDED:
           if (strcmp (entry->config, new->config) == 0)
             return 1;
           break;
@@ -817,6 +944,185 @@ community_list_unset (struct community_list_handler *ch,
   return 0;
 }
 
+/* Delete all permitted large communities in the list from com.  */
+struct lcommunity *
+lcommunity_list_match_delete (struct lcommunity *lcom,
+                             struct community_list *list)
+{
+  struct community_entry *entry;
+  u_int32_t com_index_to_delete[lcom->size];
+  u_char *ptr;
+  int delete_index = 0;
+  int i;
+
+  /* Loop over each lcommunity value and evaluate each against the
+   * community-list.  If we need to delete a community value add its index to
+   * com_index_to_delete.
+   */
+  ptr = lcom->val;
+  for (i = 0; i < lcom->size; i++)
+    {
+      ptr += (i * LCOMMUNITY_SIZE);
+      for (entry = list->head; entry; entry = entry->next)
+        {
+          if (entry->any)
+            {
+              if (entry->direct == COMMUNITY_PERMIT)
+                {
+                  com_index_to_delete[delete_index] = i;
+                  delete_index++;
+                }
+              break;
+            }
+
+          else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD)
+                   && lcommunity_include (entry->u.lcom, ptr) )
+            {
+              if (entry->direct == COMMUNITY_PERMIT)
+                {
+                  com_index_to_delete[delete_index] = i;
+                  delete_index++;
+                }
+              break;
+            }
+
+          else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD)
+                   && lcommunity_regexp_include (entry->reg, lcom, i))
+            {
+              if (entry->direct == COMMUNITY_PERMIT)
+                {
+                  com_index_to_delete[delete_index] = i;
+                  delete_index++;
+                }
+              break;
+            }
+         }
+     }
+
+  /* Delete all of the communities we flagged for deletion */
+  ptr = lcom->val;
+  for (i = delete_index-1; i >= 0; i--)
+    {
+      ptr += (com_index_to_delete[i] * LCOMMUNITY_SIZE);
+      lcommunity_del_val (lcom, ptr);
+    }
+
+  return lcom;
+}
+
+/* Set lcommunity-list.  */
+int
+lcommunity_list_set (struct community_list_handler *ch,
+                    const char *name, const char *str, int direct, int style)
+{
+  struct community_entry *entry = NULL;
+  struct community_list *list;
+  struct lcommunity *lcom = NULL;
+  regex_t *regex = NULL;
+
+  /* Get community list. */
+  list = community_list_get (ch, name, LARGE_COMMUNITY_LIST_MASTER);
+
+  /* When community-list already has entry, new entry should have same
+     style.  If you want to have mixed style community-list, you can
+     comment out this check.  */
+  if (!community_list_empty_p (list))
+    {
+      struct community_entry *first;
+
+      first = list->head;
+
+      if (style != first->style)
+       {
+         return (first->style == COMMUNITY_LIST_STANDARD
+                 ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
+                 : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
+       }
+    }
+
+  if (str)
+    {
+      if (style == LARGE_COMMUNITY_LIST_STANDARD)
+       lcom = lcommunity_str2com (str);
+      else
+       regex = bgp_regcomp (str);
+
+      if (! lcom && ! regex)
+       return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+    }
+
+  entry = community_entry_new ();
+  entry->direct = direct;
+  entry->style = style;
+  entry->any = (str ? 0 : 1);
+  entry->u.lcom = lcom;
+  entry->reg = regex;
+  if (lcom)
+    entry->config = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_COMMUNITY_LIST);
+  else if (regex)
+    entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str);
+  else
+    entry->config = NULL;
+
+  /* Do not put duplicated community entry.  */
+  if (community_list_dup_check (list, entry))
+    community_entry_free (entry);
+  else
+    community_list_entry_add (list, entry);
+
+  return 0;
+}
+
+/* Unset community-list.  When str is NULL, delete all of
+   community-list entry belongs to the specified name.  */
+int
+lcommunity_list_unset (struct community_list_handler *ch,
+                      const char *name, const char *str,
+                      int direct, int style)
+{
+  struct community_entry *entry = NULL;
+  struct community_list *list;
+  struct lcommunity *lcom = NULL;
+  regex_t *regex = NULL;
+
+  /* Lookup community list.  */
+  list = community_list_lookup (ch, name, LARGE_COMMUNITY_LIST_MASTER);
+  if (list == NULL)
+    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+  /* Delete all of entry belongs to this community-list.  */
+  if (!str)
+    {
+      community_list_delete (list);
+      return 0;
+    }
+
+  if (style == LARGE_COMMUNITY_LIST_STANDARD)
+    lcom = lcommunity_str2com (str);
+  else
+    regex = bgp_regcomp (str);
+
+  if (! lcom && ! regex)
+    return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+
+  if (lcom)
+    entry = community_list_entry_lookup (list, lcom, direct);
+  else
+    entry = community_list_entry_lookup (list, str, direct);
+
+  if (lcom)
+    lcommunity_free (&lcom);
+  if (regex)
+    bgp_regex_free (regex);
+
+  if (!entry)
+    return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+  community_list_entry_delete (list, entry, style);
+
+  return 0;
+}
+
 /* Set extcommunity-list.  */
 int
 extcommunity_list_set (struct community_list_handler *ch,
@@ -959,6 +1265,12 @@ community_list_terminate (struct community_list_handler *ch)
   while ((list = cm->str.head) != NULL)
     community_list_delete (list);
 
+  cm = &ch->lcommunity_list;
+  while ((list = cm->num.head) != NULL)
+    community_list_delete (list);
+  while ((list = cm->str.head) != NULL)
+    community_list_delete (list);
+
   cm = &ch->extcommunity_list;
   while ((list = cm->num.head) != NULL)
     community_list_delete (list);
index 277ab7226c53a528c11238adb31913cca1bb315b..68e45c8f7bcde409d49848977ce84eca76398507 100644 (file)
@@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 /* Master Community-list. */
 #define COMMUNITY_LIST_MASTER          0
 #define EXTCOMMUNITY_LIST_MASTER       1
+#define LARGE_COMMUNITY_LIST_MASTER    2
 
 /* Community-list deny and permit.  */
 #define COMMUNITY_DENY                 0
@@ -38,6 +39,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #define COMMUNITY_LIST_EXPANDED        1 /* Expanded community-list.  */
 #define EXTCOMMUNITY_LIST_STANDARD     2 /* Standard extcommunity-list.  */
 #define EXTCOMMUNITY_LIST_EXPANDED     3 /* Expanded extcommunity-list.  */
+#define LARGE_COMMUNITY_LIST_STANDARD  4 /* Standard Large community-list.  */
+#define LARGE_COMMUNITY_LIST_EXPANDED  5 /* Expanded Large community-list.  */
 
 /* Community-list.  */
 struct community_list
@@ -80,6 +83,7 @@ struct community_entry
   {
     struct community *com;
     struct ecommunity *ecom;
+    struct lcommunity *lcom;
   } u;
 
   /* Configuration string.  */
@@ -112,6 +116,9 @@ struct community_list_handler
 
   /* Exteded community-list.  */
   struct community_list_master extcommunity_list;
+
+  /* Large community-list.  */
+  struct community_list_master lcommunity_list;
 };
 
 /* Error code of community-list.  */
@@ -139,6 +146,12 @@ extern int extcommunity_list_set (struct community_list_handler *ch,
 extern int extcommunity_list_unset (struct community_list_handler *ch,
                                    const char *name, const char *str,
                                    int direct, int style, int delete_all);
+extern int lcommunity_list_set (struct community_list_handler *ch,
+                               const char *name, const char *str,
+                               int direct, int style);
+extern int lcommunity_list_unset (struct community_list_handler *ch,
+                                 const char *name, const char *str,
+                                 int direct, int style);
 
 extern struct community_list_master *
 community_list_master_lookup (struct community_list_handler *, int);
@@ -148,9 +161,12 @@ community_list_lookup (struct community_list_handler *, const char *, int);
 
 extern int community_list_match (struct community *, struct community_list *);
 extern int ecommunity_list_match (struct ecommunity *, struct community_list *);
+extern int lcommunity_list_match (struct lcommunity *, struct community_list *);
 extern int community_list_exact_match (struct community *,
                                       struct community_list *);
 extern struct community *
 community_list_match_delete (struct community *, struct community_list *);
-
+extern struct lcommunity *
+lcommunity_list_match_delete (struct lcommunity *lcom,
+                             struct community_list *list);
 #endif /* _QUAGGA_BGP_CLIST_H */
index b65af9e1fabb5c58002d41223d9aabd20c57f01e..6689883d94efa53e4a0d60ce98ba3a3c67b4358b 100644 (file)
@@ -29,6 +29,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
 #include "bgpd/bgp_aspath.h"
 
 /* Hash of community attribute. */
index 75a348e3fe86a24aad60679fbf828c14beccd3f7..418f2873c9e3ec5a50bde35ca85b2daac9c4a257 100644 (file)
@@ -41,6 +41,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_route.h"
 #include "bgpd/bgp_attr.h"
 #include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
 #include "bgpd/bgp_mplsvpn.h"
 #include "bgpd/bgp_vty.h"
 #include "bgpd/bgp_encap.h"
diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c
new file mode 100644 (file)
index 0000000..a4b6d96
--- /dev/null
@@ -0,0 +1,563 @@
+/* BGP Large Communities Attribute
+
+Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
+
+This file is part of GNU Zebra.
+
+GNU Zebra is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU Zebra is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+#include "prefix.h"
+#include "command.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_aspath.h"
+
+/* Hash of community attribute. */
+static struct hash *lcomhash;
+
+/* Allocate a new lcommunities.  */
+static struct lcommunity *
+lcommunity_new (void)
+{
+  return (struct lcommunity *) XCALLOC (MTYPE_LCOMMUNITY,
+                                       sizeof (struct lcommunity));
+}
+
+/* Allocate lcommunities.  */
+void
+lcommunity_free (struct lcommunity **lcom)
+{
+  if ((*lcom)->val)
+    XFREE (MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
+  if ((*lcom)->str)
+    XFREE (MTYPE_LCOMMUNITY_STR, (*lcom)->str);
+  XFREE (MTYPE_LCOMMUNITY, *lcom);
+  lcom = NULL;
+}
+
+/* Add a new Large Communities value to Large Communities
+   Attribute structure.  When the value is already exists in the
+   structure, we don't add the value.  Newly added value is sorted by
+   numerical order.  When the value is added to the structure return 1
+   else return 0.  */
+static int
+lcommunity_add_val (struct lcommunity *lcom, struct lcommunity_val *lval)
+{
+  u_int8_t *p;
+  int ret;
+  int c;
+
+  /* When this is fist value, just add it.  */
+  if (lcom->val == NULL)
+    {
+      lcom->size++;
+      lcom->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom_length (lcom));
+      memcpy (lcom->val, lval->val, LCOMMUNITY_SIZE);
+      return 1;
+    }
+
+  /* If the value already exists in the structure return 0.  */
+  c = 0;
+  for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++)
+    {
+      ret = memcmp (p, lval->val, LCOMMUNITY_SIZE);
+      if (ret == 0)
+        return 0;
+      if (ret > 0)
+        break;
+    }
+
+  /* Add the value to the structure with numerical sorting.  */
+  lcom->size++;
+  lcom->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length (lcom));
+
+  memmove (lcom->val + (c + 1) * LCOMMUNITY_SIZE,
+          lcom->val + c * LCOMMUNITY_SIZE,
+          (lcom->size - 1 - c) * LCOMMUNITY_SIZE);
+  memcpy (lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
+
+  return 1;
+}
+
+/* This function takes pointer to Large Communites strucutre then
+   create a new Large Communities structure by uniq and sort each
+   Large Communities value.  */
+struct lcommunity *
+lcommunity_uniq_sort (struct lcommunity *lcom)
+{
+  int i;
+  struct lcommunity *new;
+  struct lcommunity_val *lval;
+
+  if (! lcom)
+    return NULL;
+
+  new = lcommunity_new ();
+
+  for (i = 0; i < lcom->size; i++)
+    {
+      lval = (struct lcommunity_val *) (lcom->val + (i * LCOMMUNITY_SIZE));
+      lcommunity_add_val (new, lval);
+    }
+  return new;
+}
+
+/* Parse Large Communites Attribute in BGP packet.  */
+struct lcommunity *
+lcommunity_parse (u_int8_t *pnt, u_short length)
+{
+  struct lcommunity tmp;
+  struct lcommunity *new;
+
+  /* Length check.  */
+  if (length % LCOMMUNITY_SIZE)
+    return NULL;
+
+  /* Prepare tmporary structure for making a new Large Communities
+     Attribute.  */
+  tmp.size = length / LCOMMUNITY_SIZE;
+  tmp.val = pnt;
+
+  /* Create a new Large Communities Attribute by uniq and sort each
+     Large Communities value  */
+  new = lcommunity_uniq_sort (&tmp);
+
+  return lcommunity_intern (new);
+}
+
+/* Duplicate the Large Communities Attribute structure.  */
+struct lcommunity *
+lcommunity_dup (struct lcommunity *lcom)
+{
+  struct lcommunity *new;
+
+  new = XCALLOC (MTYPE_LCOMMUNITY, sizeof (struct lcommunity));
+  new->size = lcom->size;
+  if (new->size)
+    {
+      new->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom->size * LCOMMUNITY_SIZE);
+      memcpy (new->val, lcom->val, lcom->size * LCOMMUNITY_SIZE);
+    }
+  else
+    new->val = NULL;
+  return new;
+}
+
+/* Retrun string representation of communities attribute. */
+char *
+lcommunity_str (struct lcommunity *lcom)
+{
+  if (! lcom->str)
+    lcom->str = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_DISPLAY);
+  return lcom->str;
+}
+
+/* Merge two Large Communities Attribute structure.  */
+struct lcommunity *
+lcommunity_merge (struct lcommunity *lcom1, struct lcommunity *lcom2)
+{
+  if (lcom1->val)
+    lcom1->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom1->val,
+                          (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE);
+  else
+    lcom1->val = XMALLOC (MTYPE_LCOMMUNITY_VAL,
+                         (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE);
+
+  memcpy (lcom1->val + (lcom1->size * LCOMMUNITY_SIZE),
+         lcom2->val, lcom2->size * LCOMMUNITY_SIZE);
+  lcom1->size += lcom2->size;
+
+  return lcom1;
+}
+
+/* Intern Large Communities Attribute.  */
+struct lcommunity *
+lcommunity_intern (struct lcommunity *lcom)
+{
+  struct lcommunity *find;
+
+  assert (lcom->refcnt == 0);
+
+  find = (struct lcommunity *) hash_get (lcomhash, lcom, hash_alloc_intern);
+
+  if (find != lcom)
+    lcommunity_free (&lcom);
+
+  find->refcnt++;
+
+  if (! find->str)
+    find->str = lcommunity_lcom2str (find, LCOMMUNITY_FORMAT_DISPLAY);
+
+  return find;
+}
+
+/* Unintern Large Communities Attribute.  */
+void
+lcommunity_unintern (struct lcommunity **lcom)
+{
+  struct lcommunity *ret;
+
+  if ((*lcom)->refcnt)
+    (*lcom)->refcnt--;
+
+  /* Pull off from hash.  */
+  if ((*lcom)->refcnt == 0)
+    {
+      /* Large community must be in the hash.  */
+      ret = (struct lcommunity *) hash_release (lcomhash, *lcom);
+      assert (ret != NULL);
+
+      lcommunity_free (lcom);
+    }
+}
+
+/* Utility function to make hash key.  */
+unsigned int
+lcommunity_hash_make (void *arg)
+{
+  const struct lcommunity *lcom = arg;
+  int size = lcom->size * LCOMMUNITY_SIZE;
+  u_int8_t *pnt = lcom->val;
+  unsigned int key = 0;
+  int c;
+
+  for (c = 0; c < size; c += LCOMMUNITY_SIZE)
+    {
+      key += pnt[c];
+      key += pnt[c + 1];
+      key += pnt[c + 2];
+      key += pnt[c + 3];
+      key += pnt[c + 4];
+      key += pnt[c + 5];
+      key += pnt[c + 6];
+      key += pnt[c + 7];
+      key += pnt[c + 8];
+      key += pnt[c + 9];
+      key += pnt[c + 10];
+      key += pnt[c + 11];
+    }
+
+  return key;
+}
+
+/* Compare two Large Communities Attribute structure.  */
+int
+lcommunity_cmp (const void *arg1, const void *arg2)
+{
+  const struct lcommunity *lcom1 = arg1;
+  const struct lcommunity *lcom2 = arg2;
+
+  return (lcom1->size == lcom2->size
+         && memcmp (lcom1->val, lcom2->val, lcom1->size * LCOMMUNITY_SIZE) == 0);
+}
+
+/* Return communities hash.  */
+struct hash *
+lcommunity_hash (void)
+{
+  return lcomhash;
+}
+
+/* Initialize Large Comminities related hash. */
+void
+lcommunity_init (void)
+{
+  lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp);
+}
+
+void
+lcommunity_finish (void)
+{
+  hash_free (lcomhash);
+  lcomhash = NULL;
+}
+
+/* Large Communities token enum. */
+enum lcommunity_token
+{
+  lcommunity_token_unknown = 0,
+  lcommunity_token_val,
+};
+
+/* Get next Large Communities token from the string. */
+static const char *
+lcommunity_gettoken (const char *str, struct lcommunity_val *lval,
+                    enum lcommunity_token *token)
+{
+  const char *p = str;
+
+  /* Skip white space. */
+  while (isspace ((int) *p))
+    {
+      p++;
+      str++;
+    }
+
+  /* Check the end of the line. */
+  if (*p == '\0')
+    return NULL;
+
+  /* Community value. */
+  if (isdigit ((int) *p))
+    {
+      int separator = 0;
+      int digit = 0;
+      u_int32_t globaladmin = 0;
+      u_int32_t localdata1 = 0;
+      u_int32_t localdata2 = 0;
+
+      while (isdigit ((int) *p) || *p == ':')
+       {
+         if (*p == ':')
+           {
+             if (separator == 2)
+               {
+                 *token = lcommunity_token_unknown;
+                 return NULL;
+               }
+             else
+               {
+                 separator++;
+                 digit = 0;
+                 if (separator == 1) {
+                   globaladmin = localdata2;
+                 } else {
+                   localdata1 = localdata2;
+                 }
+                 localdata2 = 0;
+               }
+           }
+         else
+           {
+             digit = 1;
+             localdata2 *= 10;
+             localdata2 += (*p - '0');
+           }
+         p++;
+       }
+      if (! digit)
+       {
+         *token = lcommunity_token_unknown;
+         return NULL;
+       }
+
+      /*
+       * Copy the large comm.
+       */
+      lval->val[0] = (globaladmin >> 24) & 0xff;
+      lval->val[1] = (globaladmin >> 16) & 0xff;
+      lval->val[2] = (globaladmin >> 8) & 0xff;
+      lval->val[3] = globaladmin & 0xff;
+      lval->val[4] = (localdata1 >> 24) & 0xff;
+      lval->val[5] = (localdata1 >> 16) & 0xff;
+      lval->val[6] = (localdata1 >> 8) & 0xff;
+      lval->val[7] = localdata1 & 0xff;
+      lval->val[8] = (localdata2 >> 24) & 0xff;
+      lval->val[9] = (localdata2 >> 16) & 0xff;
+      lval->val[10] = (localdata2 >> 8) & 0xff;
+      lval->val[11] = localdata2 & 0xff;
+
+      *token = lcommunity_token_val;
+      return p;
+    }
+  *token = lcommunity_token_unknown;
+  return p;
+}
+
+/*
+  Convert string to large community attribute.
+  When type is already known, please specify both str and type.
+
+  When string includes keyword for each large community value.
+  Please specify keyword_included as non-zero value.
+*/
+struct lcommunity *
+lcommunity_str2com (const char *str)
+{
+    struct lcommunity *lcom = NULL;
+    enum lcommunity_token token = lcommunity_token_unknown;
+    struct lcommunity_val lval;
+
+    while ((str = lcommunity_gettoken (str, &lval, &token)))
+    {
+        switch (token)
+        {
+            case lcommunity_token_val:
+                if (lcom == NULL)
+                    lcom = lcommunity_new ();
+                lcommunity_add_val (lcom, &lval);
+                break;
+            case lcommunity_token_unknown:
+            default:
+                if (lcom)
+                    lcommunity_free (&lcom);
+                return NULL;
+        }
+    }
+    return lcom;
+}
+
+int
+lcommunity_include (struct lcommunity *lcom, u_char *ptr)
+{
+  int i;
+  u_char *lcom_ptr;
+
+  lcom_ptr = lcom->val;
+  for (i = 0; i < lcom->size; i++) {
+    lcom_ptr += (i * LCOMMUNITY_SIZE);
+    if (memcmp (ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
+      return 1;
+  }
+  return 0;
+}
+
+/* Convert large community attribute to string.
+   The large coms will be in 65535:65531:0 format.
+*/
+char *
+lcommunity_lcom2str (struct lcommunity *lcom, int format)
+{
+  int i;
+  u_int8_t *pnt;
+#define LCOMMUNITY_STR_DEFAULT_LEN  40
+  int str_size;
+  int str_pnt;
+  char *str_buf;
+  int len = 0;
+  int first = 1;
+  u_int32_t  globaladmin, localdata1, localdata2;
+
+  if (lcom->size == 0)
+    {
+      str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, 1);
+      str_buf[0] = '\0';
+      return str_buf;
+    }
+
+  /* Prepare buffer.  */
+  str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1);
+  str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1;
+  str_pnt = 0;
+
+  for (i = 0; i < lcom->size; i++)
+    {
+      /* Make it sure size is enough.  */
+      while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size)
+       {
+         str_size *= 2;
+         str_buf = XREALLOC (MTYPE_LCOMMUNITY_STR, str_buf, str_size);
+       }
+
+      /* Space between each value.  */
+      if (! first)
+       str_buf[str_pnt++] = ' ';
+
+      pnt = lcom->val + (i * 12);
+
+      globaladmin = (*pnt++ << 24);
+      globaladmin |= (*pnt++ << 16);
+      globaladmin |= (*pnt++ << 8);
+      globaladmin |= (*pnt++);
+
+      localdata1 = (*pnt++ << 24);
+      localdata1 |= (*pnt++ << 16);
+      localdata1 |= (*pnt++ << 8);
+      localdata1 |= (*pnt++);
+
+      localdata2 = (*pnt++ << 24);
+      localdata2 |= (*pnt++ << 16);
+      localdata2 |= (*pnt++ << 8);
+      localdata2 |= (*pnt++);
+
+      len = sprintf( str_buf + str_pnt, "%u:%u:%u", globaladmin,
+                    localdata1, localdata2);
+      str_pnt += len;
+      first = 0;
+    }
+  return str_buf;
+}
+
+int
+lcommunity_match (const struct lcommunity *lcom1,
+                  const struct lcommunity *lcom2)
+{
+  int i = 0;
+  int j = 0;
+
+  if (lcom1 == NULL && lcom2 == NULL)
+    return 1;
+
+  if (lcom1 == NULL || lcom2 == NULL)
+    return 0;
+
+  if (lcom1->size < lcom2->size)
+    return 0;
+
+  /* Every community on com2 needs to be on com1 for this to match */
+  while (i < lcom1->size && j < lcom2->size)
+    {
+      if (memcmp (lcom1->val + (i*12), lcom2->val + (j*12), LCOMMUNITY_SIZE) == 0)
+        j++;
+      i++;
+    }
+
+  if (j == lcom2->size)
+    return 1;
+  else
+    return 0;
+}
+
+/* Delete one lcommunity. */
+void
+lcommunity_del_val (struct lcommunity *lcom, u_char *ptr)
+{
+  int i = 0;
+  int c = 0;
+
+  if (! lcom->val)
+    return;
+
+  while (i < lcom->size)
+    {
+      if (memcmp (lcom->val + i*LCOMMUNITY_SIZE, ptr, LCOMMUNITY_SIZE) == 0)
+       {
+         c = lcom->size -i -1;
+
+         if (c > 0)
+           memmove (lcom->val + i*LCOMMUNITY_SIZE, lcom->val + (i + 1)*LCOMMUNITY_SIZE, c * LCOMMUNITY_SIZE);
+
+         lcom->size--;
+
+         if (lcom->size > 0)
+           lcom->val = XREALLOC (MTYPE_COMMUNITY_VAL, lcom->val,
+                                lcom_length (lcom));
+         else
+           {
+             XFREE (MTYPE_COMMUNITY_VAL, lcom->val);
+             lcom->val = NULL;
+           }
+         return;
+       }
+      i++;
+    }
+}
diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h
new file mode 100644 (file)
index 0000000..7841b4b
--- /dev/null
@@ -0,0 +1,75 @@
+/* BGP Large Communities Attribute.
+
+Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
+
+This file is part of GNU Zebra.
+
+GNU Zebra is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU Zebra is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Zebra; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_LCOMMUNITY_H
+#define _QUAGGA_BGP_LCOMMUNITY_H
+
+/* Extended communities attribute string format.  */
+#define LCOMMUNITY_FORMAT_ROUTE_MAP            0
+#define LCOMMUNITY_FORMAT_COMMUNITY_LIST       1
+#define LCOMMUNITY_FORMAT_DISPLAY              2
+
+/* Large Communities value is twelve octets long.  */
+#define LCOMMUNITY_SIZE                        12
+
+/* Large Communities attribute.  */
+struct lcommunity
+{
+  /* Reference counter.  */
+  unsigned long refcnt;
+
+  /* Size of Extended Communities attribute.  */
+  int size;
+
+  /* Extended Communities value.  */
+  u_int8_t *val;
+
+  /* Human readable format string.  */
+  char *str;
+};
+
+/* Extended community value is eight octet.  */
+struct lcommunity_val
+{
+  char val[LCOMMUNITY_SIZE];
+};
+
+#define lcom_length(X)    ((X)->size * LCOMMUNITY_SIZE)
+
+extern void lcommunity_init (void);
+extern void lcommunity_finish (void);
+extern void lcommunity_free (struct lcommunity **);
+extern struct lcommunity *lcommunity_parse (u_int8_t *, u_short);
+extern struct lcommunity *lcommunity_dup (struct lcommunity *);
+extern struct lcommunity *lcommunity_merge (struct lcommunity *, struct lcommunity *);
+extern struct lcommunity *lcommunity_uniq_sort (struct lcommunity *);
+extern struct lcommunity *lcommunity_intern (struct lcommunity *);
+extern int lcommunity_cmp (const void *, const void *);
+extern void lcommunity_unintern (struct lcommunity **);
+extern unsigned int lcommunity_hash_make (void *);
+extern struct hash *lcommunity_hash (void);
+extern struct lcommunity *lcommunity_str2com (const char *);
+extern char *lcommunity_lcom2str (struct lcommunity *, int);
+extern int lcommunity_match (const struct lcommunity *, const struct lcommunity *);
+extern char *lcommunity_str (struct lcommunity *);
+extern int lcommunity_include (struct lcommunity *lcom, u_char *ptr);
+extern void lcommunity_del_val (struct lcommunity *lcom, u_char *ptr);
+#endif /* _QUAGGA_BGP_LCOMMUNITY_H */
index 72c0311c171060a062b954b14fcc12e03d545f0e..85e32645ee45a501998b4b7f1c40dea532b18a6d 100644 (file)
@@ -111,3 +111,7 @@ DEFINE_MTYPE(BGPD, ENCAP_TLV,               "ENCAP TLV")
 
 DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS,      "BGP TEA Options")
 DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS_VALUE, "BGP TEA Options Value")
+
+DEFINE_MTYPE(BGPD, LCOMMUNITY, "Large Community")
+DEFINE_MTYPE(BGPD, LCOMMUNITY_STR, "Large Community display string")
+DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value")
index a4ce8b891b2259d698119dde6e9dbd363b40e565..341fb235d00c4e34a1d174f1578223b2dd6df7db 100644 (file)
@@ -108,4 +108,7 @@ DECLARE_MTYPE(ENCAP_TLV)
 DECLARE_MTYPE(BGP_TEA_OPTIONS)
 DECLARE_MTYPE(BGP_TEA_OPTIONS_VALUE)
 
+DECLARE_MTYPE(LCOMMUNITY)
+DECLARE_MTYPE(LCOMMUNITY_STR)
+DECLARE_MTYPE(LCOMMUNITY_VAL)
 #endif /* _QUAGGA_BGP_MEMORY_H */
index 1701c70441486e9a7215cb1c52ffce7adc246374..f564ff16916df2804745d0983676523bd9d64b0b 100644 (file)
@@ -38,6 +38,7 @@
 #include "bgpd/bgp_aspath.h"
 #include "bgpd/bgp_community.h"
 #include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
 #include "bgpd/bgp_mpath.h"
 
 /*
@@ -662,6 +663,7 @@ bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
   u_char origin;
   struct community *community, *commerge;
   struct ecommunity *ecomm, *ecommerge;
+  struct lcommunity *lcomm, *lcommerge;
   struct attr_extra *ae;
   struct attr attr = { 0 };
 
@@ -698,6 +700,7 @@ bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
       community = attr.community ? community_dup (attr.community) : NULL;
       ae = attr.extra;
       ecomm = (ae && ae->ecommunity) ? ecommunity_dup (ae->ecommunity) : NULL;
+      lcomm = (ae && ae->lcommunity) ? lcommunity_dup (ae->lcommunity) : NULL;
 
       for (mpinfo = bgp_info_mpath_first (new_best); mpinfo;
            mpinfo = bgp_info_mpath_next (mpinfo))
@@ -733,6 +736,17 @@ bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
               else
                 ecomm = ecommunity_dup (ae->ecommunity);
             }
+          if (ae && ae->lcommunity)
+            {
+              if (lcomm)
+                {
+                  lcommerge = lcommunity_merge (lcomm, ae->lcommunity);
+                  lcomm = lcommunity_uniq_sort (lcommerge);
+                  lcommunity_free (&lcommerge);
+                }
+              else
+                lcomm = lcommunity_dup (ae->lcommunity);
+            }
         }
 
       attr.aspath = aspath;
index 38470a3c7e9ff6d513da368d55afacd633d6e9b5..4c95f1b8f5a31ee1e34cb2acc4e0d61f55ad6fd7 100644 (file)
@@ -46,6 +46,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_aspath.h"
 #include "bgpd/bgp_community.h"
 #include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
 #include "bgpd/bgp_network.h"
 #include "bgpd/bgp_mplsvpn.h"
 #include "bgpd/bgp_encap.h"
index 26c500af1b4d5e42f1d679169d297ac352b0a6cd..33b7bc1251d4617dc8466ad907e8e282587f8d40 100644 (file)
@@ -1,5 +1,6 @@
 /* BGP routing information
    Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro
+   Copyright (C) 2016 Job Snijders <job@instituut.net>
 
 This file is part of GNU Zebra.
 
@@ -46,6 +47,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_regex.h"
 #include "bgpd/bgp_community.h"
 #include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
 #include "bgpd/bgp_clist.h"
 #include "bgpd/bgp_packet.h"
 #include "bgpd/bgp_filter.h"
@@ -7059,7 +7061,12 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
             }
         }
 
-      /* Line 6 display Originator, Cluster-id */
+      /* Line 6 display Large community */
+      if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))
+       vty_out (vty, "      Large Community: %s%s",
+                attr->extra->lcommunity->str, VTY_NEWLINE);
+
+      /* Line 7 display Originator, Cluster-id */
       if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) ||
          (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)))
        {
@@ -7115,7 +7122,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
       if (binfo->extra && binfo->extra->damp_info)
        bgp_damp_info_vty (vty, binfo, json_path);
 
-      /* Line 7 display Addpath IDs */
+      /* Line 8 display Addpath IDs */
       if (binfo->addpath_rx_id || binfo->addpath_tx_id)
         {
           if (json_paths)
@@ -7170,7 +7177,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
             }
         }
 
-      /* Line 8 display Uptime */
+      /* Line 9 display Uptime */
       tbuf = time(NULL) - (bgp_clock() - binfo->uptime);
       if (json_paths)
         {
@@ -7227,6 +7234,9 @@ enum bgp_show_type
   bgp_show_type_community_exact,
   bgp_show_type_community_list,
   bgp_show_type_community_list_exact,
+  bgp_show_type_lcommunity_all,
+  bgp_show_type_lcommunity,
+  bgp_show_type_lcommunity_list,
   bgp_show_type_flap_statistics,
   bgp_show_type_flap_neighbor,
   bgp_show_type_dampend_paths,
@@ -7416,6 +7426,32 @@ bgp_show_table (struct vty *vty, struct bgp *bgp, struct bgp_table *table,
                 if (! community_list_exact_match (ri->attr->community, list))
                   continue;
               }
+           if (type == bgp_show_type_community_all)
+             {
+               if (! ri->attr->community)
+                 continue;
+             }
+           if (type == bgp_show_type_lcommunity)
+             {
+               struct lcommunity *lcom = output_arg;
+
+               if (! ri->attr->extra || ! ri->attr->extra->lcommunity ||
+                   ! lcommunity_match (ri->attr->extra->lcommunity, lcom))
+                 continue;
+             }
+           if (type == bgp_show_type_lcommunity_list)
+             {
+               struct community_list *list = output_arg;
+
+               if (! ri->attr->extra ||
+                   ! lcommunity_list_match (ri->attr->extra->lcommunity, list))
+                 continue;
+             }
+           if (type == bgp_show_type_lcommunity_all)
+             {
+               if (! ri->attr->extra || ! ri->attr->extra->lcommunity)
+                 continue;
+             }
             if (type == bgp_show_type_dampend_paths
                 || type == bgp_show_type_damp_neighbor)
               {
@@ -7832,6 +7868,63 @@ bgp_show_route (struct vty *vty, const char *view_name, const char *ip_str,
                                   use_json);
 }
 
+static int
+bgp_show_lcommunity (struct vty *vty, struct bgp *bgp, int argc,
+                    struct cmd_token **argv, afi_t afi, safi_t safi, u_char uj)
+{
+  struct lcommunity *lcom;
+  struct buffer *b;
+  int i;
+  char *str;
+  int first = 0;
+
+  b = buffer_new (1024);
+  for (i = 0; i < argc; i++)
+    {
+      if (first)
+        buffer_putc (b, ' ');
+      else
+       {
+         if (strmatch (argv[i]->text, "<AA:BB:CC>"))
+           {
+             first = 1;
+             buffer_putstr (b, argv[i]->arg);
+           }
+       }
+    }
+  buffer_putc (b, '\0');
+
+  str = buffer_getstr (b);
+  buffer_free (b);
+
+  lcom = lcommunity_str2com (str);
+  XFREE (MTYPE_TMP, str);
+  if (! lcom)
+    {
+      vty_out (vty, "%% Large-community malformed: %s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show (vty, bgp, afi, safi, bgp_show_type_lcommunity, lcom, uj);
+}
+
+static int
+bgp_show_lcommunity_list (struct vty *vty, struct bgp *bgp, const char *lcom,
+                         afi_t afi, safi_t safi, u_char uj)
+{
+  struct community_list *list;
+
+  list = community_list_lookup (bgp_clist, lcom, LARGE_COMMUNITY_LIST_MASTER);
+  if (list == NULL)
+    {
+      vty_out (vty, "%% %s is not a valid large-community-list name%s", lcom,
+              VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show (vty, bgp, afi, safi, bgp_show_type_lcommunity_list, list, uj);
+}
+
 /* BGP route print out function. */
 DEFUN (show_ip_bgp_ipv4,
        show_ip_bgp_ipv4_cmd,
@@ -7844,6 +7937,8 @@ DEFUN (show_ip_bgp_ipv4,
              |filter-list WORD\
              |community [<AA:NN|local-AS|no-advertise|no-export> [exact-match]]\
              |community-list <(1-500)|WORD> [exact-match]\
+             |large-community [<AA:BB:CC>...]\
+             |large-community-list <(1-500)|WORD>\
              |A.B.C.D/M longer-prefixes\
              |X:X::X:X/M longer-prefixes>\
           ] [json]",
@@ -7878,6 +7973,14 @@ DEFUN (show_ip_bgp_ipv4,
        "community-list number\n"
        "community-list name\n"
        "Exact match of the communities\n"
+       "Display routes matching the large-communities\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "large-community number\n"
+       "Display routes matching the large-community-list\n"
+       "large-community-list number\n"
+       "large-community-list name\n"
        "IPv4 prefix\n"
        "Display route and more specific routes\n"
        "IPv6 prefix\n"
@@ -7967,6 +8070,17 @@ DEFUN (show_ip_bgp_ipv4,
           exact_match = 1;
         return bgp_show_community_list (vty, vrf, clist_number_or_name, exact_match, afi, safi);
       }
+    else if (strmatch(argv[idx]->text, "large-community"))
+      {
+       if (strmatch(argv[idx+1]->text, "<AA:BB:CC>"))
+         return bgp_show_lcommunity (vty, bgp, argc, argv, afi, safi, uj);
+       else
+         return bgp_show (vty, bgp, afi, safi, bgp_show_type_lcommunity_all, NULL, uj);
+      }
+    else if (strmatch(argv[idx]->text, "large-community-list"))
+      {
+       return bgp_show_lcommunity_list (vty, bgp, argv[idx+1]->arg, afi, safi, uj);
+      }
     /* prefix-longer */
     else if (argv[idx]->type == IPV4_TKN || argv[idx]->type == IPV6_TKN)
       return bgp_show_prefix_longer (vty, vrf, argv[idx + 1]->arg, afi, safi, bgp_show_type_prefix_longer);
@@ -10527,6 +10641,7 @@ bgp_route_init (void)
   install_element (VIEW_NODE, &show_ip_bgp_ipv4_cmd);
   install_element (VIEW_NODE, &show_ip_bgp_route_cmd);
   install_element (VIEW_NODE, &show_ip_bgp_regexp_cmd);
+
   install_element (VIEW_NODE, &show_ip_bgp_instance_neighbor_advertised_route_cmd);
   install_element (VIEW_NODE, &show_ip_bgp_neighbor_routes_cmd);
   install_element (VIEW_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd);
@@ -10537,6 +10652,7 @@ bgp_route_init (void)
  /* BGP dampening clear commands */
   install_element (ENABLE_NODE, &clear_ip_bgp_dampening_cmd);
   install_element (ENABLE_NODE, &clear_ip_bgp_dampening_prefix_cmd);
+
   install_element (ENABLE_NODE, &clear_ip_bgp_dampening_address_cmd);
   install_element (ENABLE_NODE, &clear_ip_bgp_dampening_address_mask_cmd);
 
index 4f7f51fbbb8776f4080e34d64795a6215a26fd3b..c4b3ddee36b293ab8ff5095aa933231da2b53865 100644 (file)
@@ -52,6 +52,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_filter.h"
 #include "bgpd/bgp_mplsvpn.h"
 #include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
 #include "bgpd/bgp_vty.h"
 #include "bgpd/bgp_debug.h"
 
@@ -84,6 +85,8 @@ o Cisco route-map
       as-path tag       :  Not yet
       automatic-tag     :  (This will not be implemented by bgpd)
       community         :  Done
+      large-community   :  Done
+      large-comm-list   :  Done
       comm-list         :  Not yet
       dampning          :  Not yet
       default           :  (This will not be implemented by bgpd)
@@ -847,6 +850,78 @@ struct route_map_rule_cmd route_match_community_cmd =
   route_match_community_free
 };
 
+/* Match function for lcommunity match. */
+static route_map_result_t
+route_match_lcommunity (void *rule, struct prefix *prefix,
+                      route_map_object_t type, void *object)
+{
+  struct community_list *list;
+  struct bgp_info *bgp_info;
+  struct rmap_community *rcom;
+
+  if (type == RMAP_BGP)
+    {
+      bgp_info = object;
+      rcom = rule;
+
+      list = community_list_lookup (bgp_clist, rcom->name,
+                                   LARGE_COMMUNITY_LIST_MASTER);
+      if (! list)
+       return RMAP_NOMATCH;
+
+      if (bgp_info->attr->extra &&
+         lcommunity_list_match (bgp_info->attr->extra->lcommunity, list))
+       return RMAP_MATCH;
+
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Compile function for community match. */
+static void *
+route_match_lcommunity_compile (const char *arg)
+{
+  struct rmap_community *rcom;
+  int len;
+  char *p;
+
+  rcom = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_community));
+
+  p = strchr (arg, ' ');
+  if (p)
+    {
+      len = p - arg;
+      rcom->name = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, len + 1);
+      memcpy (rcom->name, arg, len);
+    }
+  else
+    {
+      rcom->name = XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+      rcom->exact = 0;
+    }
+  return rcom;
+}
+
+/* Compile function for community match. */
+static void
+route_match_lcommunity_free (void *rule)
+{
+  struct rmap_community *rcom = rule;
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom->name);
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom);
+}
+
+/* Route map commands for community matching. */
+struct route_map_rule_cmd route_match_lcommunity_cmd =
+{
+  "large-community",
+  route_match_lcommunity,
+  route_match_lcommunity_compile,
+  route_match_lcommunity_free
+};
+
+
 /* Match function for extcommunity match. */
 static route_map_result_t
 route_match_ecommunity (void *rule, struct prefix *prefix,
@@ -1544,6 +1619,225 @@ struct route_map_rule_cmd route_set_community_cmd =
   route_set_community_free,
 };
 
+/* `set community COMMUNITY' */
+struct rmap_lcom_set
+{
+  struct lcommunity *lcom;
+  int additive;
+  int none;
+};
+
+
+/* For lcommunity set mechanism. */
+static route_map_result_t
+route_set_lcommunity (void *rule, struct prefix *prefix,
+                    route_map_object_t type, void *object)
+{
+  struct rmap_lcom_set *rcs;
+  struct bgp_info *binfo;
+  struct attr *attr;
+  struct lcommunity *new = NULL;
+  struct lcommunity *old;
+  struct lcommunity *merge;
+
+  if (type == RMAP_BGP)
+    {
+      rcs = rule;
+      binfo = object;
+      attr = binfo->attr;
+      old = (attr->extra) ? attr->extra->lcommunity : NULL;
+
+      /* "none" case.  */
+      if (rcs->none)
+       {
+         attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES));
+         if (attr->extra) {
+           attr->extra->lcommunity = NULL;
+         }
+         /* See the longer comment down below. */
+         if (old && old->refcnt == 0)
+           lcommunity_free(&old);
+         return RMAP_OKAY;
+       }
+
+      if (rcs->additive && old)
+       {
+         merge = lcommunity_merge (lcommunity_dup (old), rcs->lcom);
+
+         /* HACK: if the old large-community is not intern'd,
+           * we should free it here, or all reference to it may be lost.
+           * Really need to cleanup attribute caching sometime.
+           */
+         if (old->refcnt == 0)
+           lcommunity_free (&old);
+         new = lcommunity_uniq_sort (merge);
+         lcommunity_free (&merge);
+       }
+      else
+       new = lcommunity_dup (rcs->lcom);
+
+       /* will be interned by caller if required */
+       if (attr->extra) {
+         attr->extra->lcommunity = new;
+       }
+      attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Compile function for set community. */
+static void *
+route_set_lcommunity_compile (const char *arg)
+{
+  struct rmap_lcom_set *rcs;
+  struct lcommunity *lcom = NULL;
+  char *sp;
+  int additive = 0;
+  int none = 0;
+
+  if (strcmp (arg, "none") == 0)
+    none = 1;
+  else
+    {
+      sp = strstr (arg, "additive");
+
+      if (sp && sp > arg)
+       {
+         /* "additive" keyworkd is included.  */
+         additive = 1;
+         *(sp - 1) = '\0';
+       }
+
+      lcom = lcommunity_str2com (arg);
+
+     if (additive)
+       *(sp - 1) = ' ';
+
+     if (! lcom)
+       return NULL;
+    }
+
+  rcs = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_com_set));
+  rcs->lcom = lcom;
+  rcs->additive = additive;
+  rcs->none = none;
+
+  return rcs;
+}
+
+/* Free function for set lcommunity. */
+static void
+route_set_lcommunity_free (void *rule)
+{
+  struct rmap_lcom_set *rcs = rule;
+
+  if (rcs->lcom) {
+    lcommunity_free (&rcs->lcom);
+  }
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rcs);
+}
+
+/* Set community rule structure. */
+struct route_map_rule_cmd route_set_lcommunity_cmd =
+{
+  "large-community",
+  route_set_lcommunity,
+  route_set_lcommunity_compile,
+  route_set_lcommunity_free,
+};
+
+/* `set large-comm-list (<1-99>|<100-500>|WORD) delete' */
+
+/* For large community set mechanism. */
+static route_map_result_t
+route_set_lcommunity_delete (void *rule, struct prefix *prefix,
+                            route_map_object_t type, void *object)
+{
+  struct community_list *list;
+  struct lcommunity *merge;
+  struct lcommunity *new;
+  struct lcommunity *old;
+  struct bgp_info *binfo;
+
+  if (type == RMAP_BGP)
+    {
+      if (! rule)
+       return RMAP_OKAY;
+
+      binfo = object;
+      list = community_list_lookup (bgp_clist, rule,
+                                   LARGE_COMMUNITY_LIST_MASTER);
+      old = ((binfo->attr->extra) ? binfo->attr->extra->lcommunity : NULL);
+
+      if (list && old)
+       {
+         merge = lcommunity_list_match_delete (lcommunity_dup (old), list);
+         new = lcommunity_uniq_sort (merge);
+         lcommunity_free (&merge);
+
+         /* HACK: if the old community is not intern'd,
+          * we should free it here, or all reference to it may be lost.
+          * Really need to cleanup attribute caching sometime.
+          */
+         if (old->refcnt == 0)
+           lcommunity_free (&old);
+
+         if (new->size == 0)
+           {
+             binfo->attr->extra->lcommunity = NULL;
+             binfo->attr->flag &= ~ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
+             lcommunity_free (&new);
+           }
+         else
+           {
+             binfo->attr->extra->lcommunity = new;
+             binfo->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
+           }
+       }
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Compile function for set lcommunity. */
+static void *
+route_set_lcommunity_delete_compile (const char *arg)
+{
+  char *p;
+  char *str;
+  int len;
+
+  p = strchr (arg, ' ');
+  if (p)
+    {
+      len = p - arg;
+      str = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, len + 1);
+      memcpy (str, arg, len);
+    }
+  else
+    str = NULL;
+
+  return str;
+}
+
+/* Free function for set lcommunity. */
+static void
+route_set_lcommunity_delete_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set lcommunity rule structure. */
+struct route_map_rule_cmd route_set_lcommunity_delete_cmd =
+{
+  "large-comm-list",
+  route_set_lcommunity_delete,
+  route_set_lcommunity_delete_compile,
+  route_set_lcommunity_delete_free,
+};
+
+
 /* `set comm-list (<1-99>|<100-500>|WORD) delete' */
 
 /* For community set mechanism. */
@@ -3114,7 +3408,32 @@ DEFUN (no_match_community,
                                 RMAP_EVENT_CLIST_DELETED);
 }
 
+DEFUN (match_lcommunity,
+       match_lcommunity_cmd,
+       "match large-community [(1-99)|(100-500)|WORD]",
+       MATCH_STR
+       "Match BGP large community list\n"
+       "Large Community-list number (standard)\n"
+       "Large Community-list number (expanded)\n"
+       "Large Community-list name\n")
+{
+  return bgp_route_match_add (vty, "large-community", argv[2]->arg,
+                             RMAP_EVENT_LLIST_ADDED);
+}
 
+DEFUN (no_match_lcommunity,
+       no_match_lcommunity_cmd,
+       "no match large-community [(1-99)|(100-500)|WORD]",
+       NO_STR
+       MATCH_STR
+       "Match BGP large community list\n"
+       "Large Community-list number (standard)\n"
+       "Large Community-list number (expanded)\n"
+       "Large Community-list name\n")
+{
+  return bgp_route_match_delete (vty, "large-community", NULL,
+                                RMAP_EVENT_LLIST_DELETED);
+}
 
 DEFUN (match_ecommunity,
        match_ecommunity_cmd,
@@ -3547,6 +3866,86 @@ DEFUN (no_set_community_delete,
                              "comm-list", NULL);
 }
 
+DEFUN (set_lcommunity,
+       set_lcommunity_cmd,
+       "set large-community AA:BB:CC...",
+       SET_STR
+       "BGP large community attribute\n"
+       "Large Community number in aa:bb:cc format or additive\n")
+{
+  int ret;
+  char *str;
+
+  str = argv_concat (argv, argc, 2);
+  ret = generic_set_add (vty, VTY_GET_CONTEXT(route_map_index), "large-community", str);
+  XFREE (MTYPE_TMP, str);
+
+  return ret;
+}
+
+DEFUN (set_lcommunity_none,
+       set_lcommunity_none_cmd,
+       "set large-community none",
+       SET_STR
+       "BGP large community attribute\n"
+       "No large community attribute\n")
+{
+  return generic_set_add (vty, VTY_GET_CONTEXT(route_map_index),
+                         "large-community", "none");
+}
+
+DEFUN (no_set_lcommunity,
+       no_set_lcommunity_cmd,
+       "no set large-community <none|[AA:BB:CC...]>",
+       NO_STR
+       SET_STR
+       "BGP large community attribute\n"
+       "No community attribute\n"
+       "Large community\n"
+       "Large community in AA:BB:CC... format or additive\n")
+{
+  return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index),
+                            "large-community", NULL);
+}
+
+
+DEFUN (set_lcommunity_delete,
+       set_lcommunity_delete_cmd,
+       "set large-comm-list <(1-99)|(100-500)|WORD> delete",
+       SET_STR
+       "set BGP large community list (for deletion)\n"
+       "Large Community-list number (standard)\n"
+       "Large Communitly-list number (expanded)\n"
+       "Large Community-list name\n"
+       "Delete matching large communities\n")
+{
+  char *str;
+
+  str = XCALLOC (MTYPE_TMP, strlen (argv[2]->arg) + strlen (" delete") + 1);
+  strcpy (str, argv[2]->arg);
+  strcpy (str + strlen (argv[2]->arg), " delete");
+
+  generic_set_add (vty, VTY_GET_CONTEXT(route_map_index),
+                  "large-comm-list", str);
+
+  XFREE (MTYPE_TMP, str);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_set_lcommunity_delete,
+       no_set_lcommunity_delete_cmd,
+       "no set large-comm-list [<(1-99|(100-500)|word)> delete]",
+       NO_STR
+       SET_STR
+       "set BGP large community list (for deletion)\n"
+       "Large Community-list number (standard)\n"
+       "Large Communitly-list number (expanded)\n"
+       "Large Community-list name\n"
+       "Delete matching large communities\n")
+{
+  return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index),
+                            "large-comm-list", NULL);
+}
 
 DEFUN (set_ecommunity_rt,
        set_ecommunity_rt_cmd,
@@ -4002,6 +4401,7 @@ bgp_route_map_init (void)
   route_map_install_match (&route_match_ip_route_source_prefix_list_cmd);
   route_map_install_match (&route_match_aspath_cmd);
   route_map_install_match (&route_match_community_cmd);
+  route_map_install_match (&route_match_lcommunity_cmd);
   route_map_install_match (&route_match_ecommunity_cmd);
   route_map_install_match (&route_match_local_pref_cmd);
   route_map_install_match (&route_match_metric_cmd);
@@ -4021,6 +4421,8 @@ bgp_route_map_init (void)
   route_map_install_set (&route_set_aggregator_as_cmd);
   route_map_install_set (&route_set_community_cmd);
   route_map_install_set (&route_set_community_delete_cmd);
+  route_map_install_set (&route_set_lcommunity_cmd);
+  route_map_install_set (&route_set_lcommunity_delete_cmd);
   route_map_install_set (&route_set_vpnv4_nexthop_cmd);
   route_map_install_set (&route_set_originator_id_cmd);
   route_map_install_set (&route_set_ecommunity_rt_cmd);
@@ -4042,6 +4444,8 @@ bgp_route_map_init (void)
   install_element (RMAP_NODE, &match_community_cmd);
   install_element (RMAP_NODE, &match_community_exact_cmd);
   install_element (RMAP_NODE, &no_match_community_cmd);
+  install_element (RMAP_NODE, &match_lcommunity_cmd);
+  install_element (RMAP_NODE, &no_match_lcommunity_cmd);
   install_element (RMAP_NODE, &match_ecommunity_cmd);
   install_element (RMAP_NODE, &no_match_ecommunity_cmd);
   install_element (RMAP_NODE, &match_origin_cmd);
@@ -4071,6 +4475,11 @@ bgp_route_map_init (void)
   install_element (RMAP_NODE, &no_set_community_cmd);
   install_element (RMAP_NODE, &set_community_delete_cmd);
   install_element (RMAP_NODE, &no_set_community_delete_cmd);
+  install_element (RMAP_NODE, &set_lcommunity_cmd);
+  install_element (RMAP_NODE, &set_lcommunity_none_cmd);
+  install_element (RMAP_NODE, &no_set_lcommunity_cmd);
+  install_element (RMAP_NODE, &set_lcommunity_delete_cmd);
+  install_element (RMAP_NODE, &no_set_lcommunity_delete_cmd);
   install_element (RMAP_NODE, &set_ecommunity_rt_cmd);
   install_element (RMAP_NODE, &no_set_ecommunity_rt_cmd);
   install_element (RMAP_NODE, &set_ecommunity_soo_cmd);
index 6d23af03e4c2aaa8df71e6db4763d83e2dafafb2..0797756f6525ef86b2a6a2efef171b5b41f217f4 100644 (file)
@@ -41,6 +41,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_aspath.h"
 #include "bgpd/bgp_community.h"
 #include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
 #include "bgpd/bgp_damp.h"
 #include "bgpd/bgp_debug.h"
 #include "bgpd/bgp_fsm.h"
@@ -3748,13 +3749,15 @@ DEFUN (no_neighbor_send_community,
 /* neighbor send-community extended. */
 DEFUN (neighbor_send_community_type,
        neighbor_send_community_type_cmd,
-       "neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|extended|standard>",
+       "neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|all|extended|standard|large>",
        NEIGHBOR_STR
        NEIGHBOR_ADDR_STR2
        "Send Community attribute to this neighbor\n"
        "Send Standard and Extended Community attributes\n"
+       "Send Standard, Large and Extended Community attributes\n"
        "Send Extended Community attributes\n"
-       "Send Standard Community attributes\n")
+       "Send Standard Community attributes\n"
+       "Send Large Community attributes\n")
 {
   int idx = 0;
   u_int32_t flag = 0;
@@ -3765,25 +3768,35 @@ DEFUN (neighbor_send_community_type,
     SET_FLAG (flag, PEER_FLAG_SEND_COMMUNITY);
   else if (argv_find (argv, argc, "extended", &idx))
     SET_FLAG (flag, PEER_FLAG_SEND_EXT_COMMUNITY);
+  else if (argv_find (argv, argc, "large", &idx))
+    SET_FLAG (flag, PEER_FLAG_SEND_LARGE_COMMUNITY);
+  else if (argv_find (argv, argc, "both", &idx))
+    {
+      SET_FLAG (flag, PEER_FLAG_SEND_COMMUNITY);
+      SET_FLAG (flag, PEER_FLAG_SEND_EXT_COMMUNITY);
+    }
   else
-  {
-    SET_FLAG (flag, PEER_FLAG_SEND_COMMUNITY);
-    SET_FLAG (flag, PEER_FLAG_SEND_EXT_COMMUNITY);
-  }
+    {
+      SET_FLAG (flag, PEER_FLAG_SEND_COMMUNITY);
+      SET_FLAG (flag, PEER_FLAG_SEND_EXT_COMMUNITY);
+      SET_FLAG (flag, PEER_FLAG_SEND_LARGE_COMMUNITY);
+    }
 
   return peer_af_flag_set_vty (vty, peer, bgp_node_afi (vty), bgp_node_safi (vty), flag);
 }
 
 DEFUN (no_neighbor_send_community_type,
        no_neighbor_send_community_type_cmd,
-       "no neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|extended|standard>",
+       "no neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|all|extended|standard|large>",
        NO_STR
        NEIGHBOR_STR
        NEIGHBOR_ADDR_STR2
        "Send Community attribute to this neighbor\n"
        "Send Standard and Extended Community attributes\n"
+       "Send Standard, Large and Extended Community attributes\n"
        "Send Extended Community attributes\n"
-       "Send Standard Community attributes\n")
+       "Send Standard Community attributes\n"
+       "Send Large Community attributes\n")
 {
   int idx_peer = 2;
   int idx_type = 4;
@@ -3795,11 +3808,21 @@ DEFUN (no_neighbor_send_community_type,
     return peer_af_flag_unset_vty (vty, argv[idx_peer]->arg, bgp_node_afi (vty),
                                   bgp_node_safi (vty),
                                   PEER_FLAG_SEND_EXT_COMMUNITY);
+  if (strncmp (argv[idx_type]->arg, "l", 1) == 0)
+    return peer_af_flag_unset_vty (vty, argv[idx_peer]->arg, bgp_node_afi (vty),
+                                  bgp_node_safi (vty),
+                                  PEER_FLAG_SEND_LARGE_COMMUNITY);
+  if (strncmp (argv[idx_type]->arg, "b", 1) == 0)
+    return peer_af_flag_unset_vty (vty, argv[idx_peer]->arg, bgp_node_afi (vty),
+                                  bgp_node_safi (vty),
+                                  PEER_FLAG_SEND_COMMUNITY |
+                                  PEER_FLAG_SEND_EXT_COMMUNITY);
 
   return peer_af_flag_unset_vty (vty, argv[idx_peer]->arg, bgp_node_afi (vty),
                                 bgp_node_safi (vty),
                                 (PEER_FLAG_SEND_COMMUNITY |
-                                 PEER_FLAG_SEND_EXT_COMMUNITY));
+                                 PEER_FLAG_SEND_EXT_COMMUNITY|
+                                 PEER_FLAG_SEND_LARGE_COMMUNITY));
 }
 
 /* neighbor soft-reconfig. */
@@ -6132,6 +6155,12 @@ DEFUN (show_bgp_memory,
              mtype_memstr (memstrbuf, sizeof (memstrbuf),
                          count * sizeof (struct ecommunity)),
              VTY_NEWLINE);
+  if ((count = mtype_stats_alloc (MTYPE_LCOMMUNITY)))
+    vty_out (vty, "%ld BGP large-community entries, using %s of memory%s",
+            count,
+             mtype_memstr (memstrbuf, sizeof (memstrbuf),
+                         count * sizeof (struct lcommunity)),
+             VTY_NEWLINE);
 
   if ((count = mtype_stats_alloc (MTYPE_CLUSTER)))
     vty_out (vty, "%ld Cluster lists, using %s of memory%s", count,
@@ -7109,12 +7138,16 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi,
       if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
         vty_out (vty, "  MED is propagated unchanged to this neighbor%s", VTY_NEWLINE);
       if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
-          || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY))
+          || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)
+          || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY))
         {
           vty_out (vty, "  Community attribute sent to this neighbor");
           if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
-             && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY))
-           vty_out (vty, "(both)%s", VTY_NEWLINE);
+              && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)
+              && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY))
+            vty_out (vty, "(all)%s", VTY_NEWLINE);
+          else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY))
+            vty_out (vty, "(large)%s", VTY_NEWLINE);
           else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY))
            vty_out (vty, "(extended)%s", VTY_NEWLINE);
           else
@@ -8637,6 +8670,36 @@ DEFUN (show_ip_bgp_community_info,
   return CMD_SUCCESS;
 }
 
+static void
+lcommunity_show_all_iterator (struct hash_backet *backet, struct vty *vty)
+{
+  struct lcommunity *lcom;
+
+  lcom = (struct lcommunity *) backet->data;
+  vty_out (vty, "[%p] (%ld) %s%s", (void *)backet, lcom->refcnt,
+          lcommunity_str (lcom), VTY_NEWLINE);
+}
+
+/* Show BGP's community internal data. */
+DEFUN (show_ip_bgp_lcommunity_info,
+       show_ip_bgp_lcommunity_info_cmd,
+       "show ip bgp large-community-info",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "List all bgp large-community information\n")
+{
+  vty_out (vty, "Address Refcnt Large-community%s", VTY_NEWLINE);
+
+  hash_iterate (lcommunity_hash (),
+               (void (*) (struct hash_backet *, void *))
+               lcommunity_show_all_iterator,
+               vty);
+
+  return CMD_SUCCESS;
+}
+
+
 DEFUN (show_ip_bgp_attr_info,
        show_ip_bgp_attr_info_cmd,
        "show [ip] bgp attribute-info",
@@ -10738,6 +10801,8 @@ bgp_vty_init (void)
   /* "show [ip] bgp community" commands. */
   install_element (VIEW_NODE, &show_ip_bgp_community_info_cmd);
 
+  /* "show ip bgp large-community" commands. */
+  install_element (VIEW_NODE, &show_ip_bgp_lcommunity_info_cmd);
   /* "show [ip] bgp attribute-info" commands. */
   install_element (VIEW_NODE, &show_ip_bgp_attr_info_cmd);
 
@@ -11070,6 +11135,324 @@ DEFUN (show_ip_community_list_arg,
   return CMD_SUCCESS;
 }
 
+/*
+ * Large Community code.
+ */
+static int
+lcommunity_list_set_vty (struct vty *vty, int argc, struct cmd_token **argv,
+                        int style, int reject_all_digit_name)
+{
+  int ret;
+  int direct;
+  char *str;
+  int idx = 0;
+  char *cl_name;
+
+  direct = argv_find (argv, argc, "permit", &idx) ? COMMUNITY_PERMIT : COMMUNITY_DENY;
+
+  /* All digit name check.  */
+  idx = 0;
+  argv_find (argv, argc, "WORD", &idx);
+  argv_find (argv, argc, "(1-99)", &idx);
+  argv_find (argv, argc, "(100-500)", &idx);
+  cl_name = argv[idx]->arg;
+  if (reject_all_digit_name && all_digit (cl_name))
+    {
+      vty_out (vty, "%% Community name cannot have all digits%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  argv_find (argv, argc, "AA:BB:CC", &idx);
+  argv_find (argv, argc, "LINE", &idx);
+  /* Concat community string argument. */
+  if (idx)
+    str = argv_concat (argv, argc, idx);
+  else
+    str = NULL;
+
+  ret = lcommunity_list_set (bgp_clist, cl_name, str, direct, style);
+
+  /* Free temporary community list string allocated by
+     argv_concat().  */
+  if (str)
+    XFREE (MTYPE_TMP, str);
+
+  if (ret < 0)
+    {
+      community_list_perror (vty, ret);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+static int
+lcommunity_list_unset_vty (struct vty *vty, int argc, struct cmd_token **argv,
+                          int style)
+{
+  int ret;
+  int direct = 0;
+  char *str = NULL;
+  int idx = 0;
+
+  argv_find (argv, argc, "permit", &idx);
+  argv_find (argv, argc, "deny", &idx);
+
+  if (idx)
+    {
+      /* Check the list direct. */
+      if (strncmp (argv[idx]->arg, "p", 1) == 0)
+       direct = COMMUNITY_PERMIT;
+      else
+       direct = COMMUNITY_DENY;
+
+      idx = 0;
+      argv_find (argv, argc, "LINE", &idx);
+      argv_find (argv, argc, "AA:AA:NN", &idx);
+      /* Concat community string argument.  */
+      str = argv_concat (argv, argc, idx);
+    }
+
+  idx = 0;
+  argv_find (argv, argc, "(1-99)", &idx);
+  argv_find (argv, argc, "(100-500)", &idx);
+  argv_find (argv, argc, "WORD", &idx);
+
+  /* Unset community list.  */
+  ret = lcommunity_list_unset (bgp_clist, argv[idx]->arg, str, direct, style);
+
+  /* Free temporary community list string allocated by
+     argv_concat().  */
+  if (str)
+    XFREE (MTYPE_TMP, str);
+
+  if (ret < 0)
+    {
+      community_list_perror (vty, ret);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* "large-community-list" keyword help string.  */
+#define LCOMMUNITY_LIST_STR "Add a large community list entry\n"
+#define LCOMMUNITY_VAL_STR  "large community in 'aa:bb:cc' format\n"
+
+DEFUN (ip_lcommunity_list_standard,
+       ip_lcommunity_list_standard_cmd,
+       "ip large-community-list (1-99) <deny|permit> [AA:BB:CC...]",
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (standard)\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       LCOMMUNITY_VAL_STR)
+{
+  return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD, 0);
+}
+
+DEFUN (ip_lcommunity_list_expanded,
+       ip_lcommunity_list_expanded_cmd,
+       "ip large-community-list (100-500) <deny|permit> LINE...",
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (expanded)\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED, 0);
+}
+
+DEFUN (ip_lcommunity_list_name_standard,
+       ip_lcommunity_list_name_standard_cmd,
+       "ip large-community-list standard WORD <deny|permit> [AA:BB.CC...]",
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify standard large-community-list\n"
+       "Large Community list name\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       LCOMMUNITY_VAL_STR)
+{
+  return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD, 1);
+}
+
+DEFUN (ip_lcommunity_list_name_expanded,
+       ip_lcommunity_list_name_expanded_cmd,
+       "ip large-community-list expanded WORD <deny|permit> LINE...",
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify expanded large-community-list\n"
+       "Large Community list name\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED, 1);
+}
+
+DEFUN (no_ip_lcommunity_list_standard_all,
+       no_ip_lcommunity_list_standard_all_cmd,
+       "no ip large-community-list <(1-99)|(100-500)|WORD>",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (standard)\n"
+       "Large Community list number (expanded)\n"
+       "Large Community list name\n")
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_lcommunity_list_name_expanded_all,
+       no_ip_lcommunity_list_name_expanded_all_cmd,
+       "no ip large-community-list expanded WORD",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify expanded large-community-list\n"
+       "Large Community list name\n")
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_lcommunity_list_standard,
+       no_ip_lcommunity_list_standard_cmd,
+       "no ip large-community-list (1-99) <deny|permit> AA:AA:NN...",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (standard)\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       LCOMMUNITY_VAL_STR)
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_lcommunity_list_expanded,
+       no_ip_lcommunity_list_expanded_cmd,
+       "no ip large-community-list (100-500) <deny|permit> LINE...",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Large Community list number (expanded)\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_ip_lcommunity_list_name_standard,
+       no_ip_lcommunity_list_name_standard_cmd,
+       "no ip large-community-list standard WORD <deny|permit> AA:AA:NN...",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify standard large-community-list\n"
+       "Large Community list name\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       LCOMMUNITY_VAL_STR)
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_ip_lcommunity_list_name_expanded,
+       no_ip_lcommunity_list_name_expanded_cmd,
+       "no ip large-community-list expanded WORD <deny|permit> LINE...",
+       NO_STR
+       IP_STR
+       LCOMMUNITY_LIST_STR
+       "Specify expanded large-community-list\n"
+       "Large community list name\n"
+       "Specify large community to reject\n"
+       "Specify large community to accept\n"
+       "An ordered list as a regular-expression\n")
+{
+  return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED);
+}
+
+static void
+lcommunity_list_show (struct vty *vty, struct community_list *list)
+{
+  struct community_entry *entry;
+
+  for (entry = list->head; entry; entry = entry->next)
+    {
+      if (entry == list->head)
+       {
+         if (all_digit (list->name))
+           vty_out (vty, "Large community %s list %s%s",
+                    entry->style == EXTCOMMUNITY_LIST_STANDARD ?
+                    "standard" : "(expanded) access",
+                    list->name, VTY_NEWLINE);
+         else
+           vty_out (vty, "Named large community %s list %s%s",
+                    entry->style == EXTCOMMUNITY_LIST_STANDARD ?
+                    "standard" : "expanded",
+                    list->name, VTY_NEWLINE);
+       }
+      if (entry->any)
+       vty_out (vty, "    %s%s",
+                community_direct_str (entry->direct), VTY_NEWLINE);
+      else
+       vty_out (vty, "    %s %s%s",
+                community_direct_str (entry->direct),
+                entry->style == EXTCOMMUNITY_LIST_STANDARD ?
+                entry->u.ecom->str : entry->config,
+                VTY_NEWLINE);
+    }
+}
+
+DEFUN (show_ip_lcommunity_list,
+       show_ip_lcommunity_list_cmd,
+       "show ip large-community-list",
+       SHOW_STR
+       IP_STR
+       "List large-community list\n")
+{
+  struct community_list *list;
+  struct community_list_master *cm;
+
+  cm = community_list_master_lookup (bgp_clist, LARGE_COMMUNITY_LIST_MASTER);
+  if (! cm)
+    return CMD_SUCCESS;
+
+  for (list = cm->num.head; list; list = list->next)
+    lcommunity_list_show (vty, list);
+
+  for (list = cm->str.head; list; list = list->next)
+    lcommunity_list_show (vty, list);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_lcommunity_list_arg,
+       show_ip_lcommunity_list_arg_cmd,
+       "show ip large-community-list <(1-500)|WORD>",
+       SHOW_STR
+       IP_STR
+       "List large-community list\n"
+       "large-community-list number\n"
+       "large-community-list name\n")
+{
+  struct community_list *list;
+
+  list = community_list_lookup (bgp_clist, argv[3]->arg, LARGE_COMMUNITY_LIST_MASTER);
+  if (! list)
+    {
+      vty_out (vty, "%% Can't find extcommunity-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  lcommunity_list_show (vty, list);
+
+  return CMD_SUCCESS;
+}
+
 /* "extcommunity-list" keyword help string.  */
 #define EXTCOMMUNITY_LIST_STR "Add a extended community list entry\n"
 #define EXTCOMMUNITY_VAL_STR  "Extended community attribute in 'rt aa:nn_or_IPaddr:nn' OR 'soo aa:nn_or_IPaddr:nn' format\n"
@@ -11379,6 +11762,30 @@ community_list_config_write (struct vty *vty)
                 community_list_config_str (entry), VTY_NEWLINE);
        write++;
       }
+
+
+    /* lcommunity-list.  */
+  cm = community_list_master_lookup (bgp_clist, LARGE_COMMUNITY_LIST_MASTER);
+
+  for (list = cm->num.head; list; list = list->next)
+    for (entry = list->head; entry; entry = entry->next)
+      {
+       vty_out (vty, "ip large-community-list %s %s %s%s",
+                list->name, community_direct_str (entry->direct),
+                community_list_config_str (entry), VTY_NEWLINE);
+       write++;
+      }
+  for (list = cm->str.head; list; list = list->next)
+    for (entry = list->head; entry; entry = entry->next)
+      {
+       vty_out (vty, "ip large-community-list %s %s %s %s%s",
+                entry->style == LARGE_COMMUNITY_LIST_STANDARD
+                ? "standard" : "expanded",
+                list->name, community_direct_str (entry->direct),
+                community_list_config_str (entry), VTY_NEWLINE);
+       write++;
+      }
+
   return write;
 }
 
@@ -11409,4 +11816,18 @@ community_list_vty (void)
   install_element (CONFIG_NODE, &no_ip_extcommunity_list_expanded_all_cmd);
   install_element (VIEW_NODE, &show_ip_extcommunity_list_cmd);
   install_element (VIEW_NODE, &show_ip_extcommunity_list_arg_cmd);
+
+  /* Large Community List */
+  install_element (CONFIG_NODE, &ip_lcommunity_list_standard_cmd);
+  install_element (CONFIG_NODE, &ip_lcommunity_list_expanded_cmd);
+  install_element (CONFIG_NODE, &ip_lcommunity_list_name_standard_cmd);
+  install_element (CONFIG_NODE, &ip_lcommunity_list_name_expanded_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_standard_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_expanded_all_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_standard_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_expanded_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_standard_cmd);
+  install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_expanded_cmd);
+  install_element (VIEW_NODE, &show_ip_lcommunity_list_cmd);
+  install_element (VIEW_NODE, &show_ip_lcommunity_list_arg_cmd);
 }
index 5457822f3be672e61dd2b14b30913fc6869a8acb..968eebd2998c0092af50f71382c173afe551c684 100644 (file)
@@ -902,6 +902,7 @@ peer_af_flag_reset (struct peer *peer, afi_t afi, safi_t safi)
     {
       SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
       SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY);
+      SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY);
     }
 
   /* Clear neighbor default_originate_rmap */
@@ -1206,6 +1207,7 @@ peer_new (struct bgp *bgp)
          {
            SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
            SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY);
+           SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY);
          }
        peer->orf_plist[afi][safi] = NULL;
       }
@@ -3702,6 +3704,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] =
   {
     { PEER_FLAG_SEND_COMMUNITY,           1, peer_change_reset_out },
     { PEER_FLAG_SEND_EXT_COMMUNITY,       1, peer_change_reset_out },
+    { PEER_FLAG_SEND_LARGE_COMMUNITY,     1, peer_change_reset_out },
     { PEER_FLAG_NEXTHOP_SELF,             1, peer_change_reset_out },
     { PEER_FLAG_REFLECTOR_CLIENT,         1, peer_change_reset },
     { PEER_FLAG_RSERVER_CLIENT,           1, peer_change_reset },
@@ -6982,10 +6985,17 @@ bgp_config_write_peer_af (struct vty *vty, struct bgp *bgp,
   if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
     {
       if (peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)
-          && peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))
+          && peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)
+          && peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY))
         {
           afi_header_vty_out (vty, afi, safi, write,
-                              "  neighbor %s send-community both%s",
+                              "  neighbor %s send-community all%s",
+                              addr, VTY_NEWLINE);
+        }
+      else if (peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY))
+        {
+          afi_header_vty_out (vty, afi, safi, write,
+                              "  neighbor %s send-community large%s",
                               addr, VTY_NEWLINE);
         }
       else if (peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))
@@ -7006,10 +7016,19 @@ bgp_config_write_peer_af (struct vty *vty, struct bgp *bgp,
       if (!peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY) &&
           (!g_peer || peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)) &&
           !peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY) &&
-          (!g_peer || peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)))
+          (!g_peer || peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) &&
+          !peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY) &&
+          (!g_peer || peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY)))
+        {
+          afi_header_vty_out (vty, afi, safi, write,
+                              "  no neighbor %s send-community all%s",
+                              addr, VTY_NEWLINE);
+        }
+      else if (!peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY) &&
+               (!g_peer || peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY)))
         {
           afi_header_vty_out (vty, afi, safi, write,
-                              "  no neighbor %s send-community both%s",
+                              "  no neighbor %s send-community large%s",
                               addr, VTY_NEWLINE);
         }
       else if (!peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY) &&
index 2718805130c66308d6ed951ec0ced372024481a5..814bb506c81491d642bebccfac1b4956040b6595 100644 (file)
@@ -705,6 +705,7 @@ struct peer
 #define PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS (1 << 23) /* addpath-tx-bestpath-per-AS */
 #define PEER_FLAG_WEIGHT                    (1 << 24) /* weight */
 #define PEER_FLAG_ALLOWAS_IN_ORIGIN         (1 << 25) /* allowas-in origin */
+#define PEER_FLAG_SEND_LARGE_COMMUNITY      (1 << 26) /* Send large Communities */
 
   /* MD5 password */
   char *password;
@@ -963,6 +964,7 @@ struct bgp_nlri
 #define BGP_ATTR_AS4_AGGREGATOR                 18
 #define BGP_ATTR_AS_PATHLIMIT                   21
 #define BGP_ATTR_ENCAP                          23
+#define BGP_ATTR_LARGE_COMMUNITIES              32
 #if ENABLE_BGP_VNC
 #define BGP_ATTR_VNC                           255
 #endif
index 74bae1fd761504cc3d8ddc785552d8743668270c..70f3069a3630e56e4da163b720df8b94d9877571 100644 (file)
@@ -710,6 +710,7 @@ enum route_map_dep_type
     ROUTE_MAP_DEP_RMAP = 1,
     ROUTE_MAP_DEP_CLIST,
     ROUTE_MAP_DEP_ECLIST,
+    ROUTE_MAP_DEP_LCLIST,
     ROUTE_MAP_DEP_PLIST,
     ROUTE_MAP_DEP_ASPATH,
     ROUTE_MAP_DEP_FILTER,
@@ -1819,6 +1820,7 @@ route_map_dep_update (struct hash *dephash, const char *dep_name,
     case RMAP_EVENT_CLIST_ADDED:
     case RMAP_EVENT_ECLIST_ADDED:
     case RMAP_EVENT_ASLIST_ADDED:
+    case RMAP_EVENT_LLIST_ADDED:
     case RMAP_EVENT_CALL_ADDED:
     case RMAP_EVENT_FILTER_ADDED:
       if (rmap_debug)
@@ -1840,6 +1842,7 @@ route_map_dep_update (struct hash *dephash, const char *dep_name,
     case RMAP_EVENT_CLIST_DELETED:
     case RMAP_EVENT_ECLIST_DELETED:
     case RMAP_EVENT_ASLIST_DELETED:
+    case RMAP_EVENT_LLIST_DELETED:
     case RMAP_EVENT_CALL_DELETED:
     case RMAP_EVENT_FILTER_DELETED:
       if (rmap_debug)
@@ -1902,6 +1905,10 @@ route_map_get_dep_hash (route_map_event_t event)
     case RMAP_EVENT_ASLIST_DELETED:
       upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ASPATH];
       break;
+    case RMAP_EVENT_LLIST_ADDED:
+    case RMAP_EVENT_LLIST_DELETED:
+      upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_LCLIST];
+      break;
     case RMAP_EVENT_CALL_ADDED:
     case RMAP_EVENT_CALL_DELETED:
       upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP];
index b52f7289b061eb5db45155c70ab9d776b9acc1b0..b378c64eae62743529be4d954c14df872a76dd0e 100644 (file)
@@ -84,6 +84,8 @@ typedef enum
   RMAP_EVENT_CLIST_DELETED,
   RMAP_EVENT_ECLIST_ADDED,
   RMAP_EVENT_ECLIST_DELETED,
+  RMAP_EVENT_LLIST_ADDED,
+  RMAP_EVENT_LLIST_DELETED,
   RMAP_EVENT_ASLIST_ADDED,
   RMAP_EVENT_ASLIST_DELETED,
   RMAP_EVENT_FILTER_ADDED,