]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: Refine extended community handling
authorvivek <vivek@cumulusnetworks.com>
Mon, 15 May 2017 19:31:01 +0000 (12:31 -0700)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 12 Jul 2017 16:29:27 +0000 (12:29 -0400)
Define helper functions to form different kinds of route targets. Also,
refine functions that encode extended communities as well as generate
a string from an extended community.

Signed-off-by: Vivek Venkatraman <vivek@cumulusnetworks.com>
Reviewed-by: Donald Sharp <sharpd@cumulusnetworks.com>
bgpd/bgp_ecommunity.c
bgpd/bgp_ecommunity.h

index 0555d1bbe32a3472849f3eced99b566e24f99942..bb2ef260eea07cbab3ce1e836153f3a40c4de723 100644 (file)
@@ -304,6 +304,61 @@ enum ecommunity_token
   ecommunity_token_val,
 };
 
+/*
+ * Encode BGP extended community from passed values. Supports types
+ * defined in RFC 4360 and well-known sub-types.
+ */
+static int
+ecommunity_encode (u_char type, u_char sub_type, int trans,
+                   as_t as, struct in_addr ip, u_int32_t val,
+                   struct ecommunity_val *eval)
+{
+  assert (eval);
+  if (type == ECOMMUNITY_ENCODE_AS)
+    {
+      if (as > BGP_AS_MAX)
+        return -1;
+    }
+  else if (type == ECOMMUNITY_ENCODE_IP
+           || type == ECOMMUNITY_ENCODE_AS4)
+    {
+      if (val > UINT16_MAX)
+        return -1;
+    }
+
+  /* Fill in the values. */
+  eval->val[0] = type;
+  if (!trans)
+    eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
+  eval->val[1] = sub_type;
+  if (type == ECOMMUNITY_ENCODE_AS)
+    {
+      eval->val[2] = (as >> 8) & 0xff;
+      eval->val[3] = as & 0xff;
+      eval->val[4] = (val >> 24) & 0xff;
+      eval->val[5] = (val >> 16) & 0xff;
+      eval->val[6] = (val >> 8) & 0xff;
+      eval->val[7] = val & 0xff;
+    }
+  else if (type == ECOMMUNITY_ENCODE_IP)
+    {
+      memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
+      eval->val[6] = (val >> 8) & 0xff;
+      eval->val[7] = val & 0xff;
+    }
+  else
+    {
+      eval->val[2] = (as >> 24) & 0xff;
+      eval->val[3] = (as >> 16) & 0xff;
+      eval->val[4] = (as >> 8) & 0xff;
+      eval->val[5] =  as & 0xff;
+      eval->val[6] = (val >> 8) & 0xff;
+      eval->val[7] = val & 0xff;
+    }
+
+  return 0;
+}
+
 /* Get next Extended Communities token from the string. */
 static const char *
 ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
@@ -318,6 +373,7 @@ ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
   struct in_addr ip;
   as_t as = 0;
   u_int32_t val = 0;
+  u_char ecomm_type;
   char buf[INET_ADDRSTRLEN + 1];
 
   /* Skip white space. */
@@ -452,44 +508,15 @@ ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
   if (!digit || !separator)
     goto error;
 
-  /* Encode result into routing distinguisher.  */
+  /* Encode result into extended community.  */
   if (dot)
-    {
-      if (val > UINT16_MAX)
-        goto error;
-      
-      eval->val[0] = ECOMMUNITY_ENCODE_IP;
-      eval->val[1] = 0;
-      memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
-      eval->val[6] = (val >> 8) & 0xff;
-      eval->val[7] = val & 0xff;
-    }
+    ecomm_type = ECOMMUNITY_ENCODE_IP;
   else if (as > BGP_AS_MAX)
-    {
-      if (val > UINT16_MAX)
-        goto error;
-      
-      eval->val[0] = ECOMMUNITY_ENCODE_AS4;
-      eval->val[1] = 0;
-      eval->val[2] = (as >>24) & 0xff;
-      eval->val[3] = (as >>16) & 0xff;
-      eval->val[4] = (as >>8) & 0xff;
-      eval->val[5] =  as & 0xff;
-      eval->val[6] = (val >> 8) & 0xff;
-      eval->val[7] = val & 0xff;
-    }
+    ecomm_type = ECOMMUNITY_ENCODE_AS4;
   else
-    {
-      eval->val[0] = ECOMMUNITY_ENCODE_AS;
-      eval->val[1] = 0;
-      
-      eval->val[2] = (as >>8) & 0xff;
-      eval->val[3] = as & 0xff;
-      eval->val[4] = (val >>24) & 0xff;
-      eval->val[5] = (val >>16) & 0xff;
-      eval->val[6] = (val >>8) & 0xff;
-      eval->val[7] = val & 0xff;
-    }
+    ecomm_type = ECOMMUNITY_ENCODE_AS;
+  if (ecommunity_encode (ecomm_type, 0, 1, as, ip, val, eval))
+        goto error;
   *token = ecommunity_token_val;
   return p;
 
@@ -581,6 +608,81 @@ ecommunity_str2com (const char *str, int type, int keyword_included)
   return ecom;
 }
 
+static int
+ecommunity_rt_soo_str (char *buf, u_int8_t *pnt, int type,
+                       int sub_type, int format)
+{
+  int len = 0;
+  const char *prefix;
+
+  /* For parse Extended Community attribute tupple. */
+  struct ecommunity_as
+  {
+    as_t as;
+    u_int32_t val;
+  } eas;
+
+  struct ecommunity_ip
+  {
+    struct in_addr ip;
+    u_int16_t val;
+  } eip;
+
+
+  /* Determine prefix for string, if any. */
+  switch (format)
+    {
+      case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
+        prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
+        break;
+      case ECOMMUNITY_FORMAT_DISPLAY:
+        prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
+        break;
+      case ECOMMUNITY_FORMAT_ROUTE_MAP:
+        prefix = "";
+        break;
+      default:
+        prefix = "";
+        break;
+    }
+
+  /* Put string into buffer.  */
+  if (type == ECOMMUNITY_ENCODE_AS4)
+    {
+      eas.as = (*pnt++ << 24);
+      eas.as |= (*pnt++ << 16);
+      eas.as |= (*pnt++ << 8);
+      eas.as |= (*pnt++);
+      eas.val = (*pnt++ << 8);
+      eas.val |= (*pnt++);
+
+      len = sprintf (buf, "%s%u:%u", prefix, eas.as, eas.val);
+    }
+  else if (type == ECOMMUNITY_ENCODE_AS)
+    {
+      eas.as = (*pnt++ << 8);
+      eas.as |= (*pnt++);
+
+      eas.val = (*pnt++ << 24);
+      eas.val |= (*pnt++ << 16);
+      eas.val |= (*pnt++ << 8);
+      eas.val |= (*pnt++);
+
+      len = sprintf (buf, "%s%u:%u", prefix, eas.as, eas.val);
+    }
+  else if (type == ECOMMUNITY_ENCODE_IP)
+    {
+      memcpy (&eip.ip, pnt, 4);
+      pnt += 4;
+      eip.val = (*pnt++ << 8);
+      eip.val |= (*pnt++);
+
+      len = sprintf (buf, "%s%s:%u", prefix, inet_ntoa (eip.ip), eip.val);
+    }
+
+  return len;
+}
+
 /* Convert extended community attribute to string.  
 
    Due to historical reason of industry standard implementation, there
@@ -610,29 +712,15 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
 {
   int i;
   u_int8_t *pnt;
-  int encode = 0;
   int type = 0;
+  int sub_type = 0;
 #define ECOMMUNITY_STR_DEFAULT_LEN  27
   int str_size;
   int str_pnt;
   char *str_buf;
-  const char *prefix;
   int len = 0;
   int first = 1;
 
-  /* For parse Extended Community attribute tupple. */
-  struct ecommunity_as
-  {
-    as_t as;
-    u_int32_t val;
-  } eas;
-
-  struct ecommunity_ip
-  {
-    struct in_addr ip;
-    u_int16_t val;
-  } eip;
-
   if (ecom->size == 0)
     {
       str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
@@ -648,6 +736,8 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
 
   for (i = 0; i < ecom->size; i++)
     {
+      int unk_ecom = 0;
+
       /* Make it sure size is enough.  */
       while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
        {
@@ -662,39 +752,39 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
       pnt = ecom->val + (i * 8);
 
       /* High-order octet of type. */
-      encode = *pnt++;
+      type = *pnt++;
 
-      switch (encode)
+      if (type == ECOMMUNITY_ENCODE_AS ||
+          type == ECOMMUNITY_ENCODE_IP ||
+          type == ECOMMUNITY_ENCODE_AS4)
         {
-        case ECOMMUNITY_ENCODE_AS:
-        case ECOMMUNITY_ENCODE_IP:
-        case ECOMMUNITY_ENCODE_AS4:
-          break;
-
-        case ECOMMUNITY_ENCODE_OPAQUE:
-          if(filter == ECOMMUNITY_ROUTE_TARGET)
-            {
-              continue;
-            }
+          /* Low-order octet of type. */
+          sub_type = *pnt++;
+          if (sub_type != ECOMMUNITY_ROUTE_TARGET &&
+              sub_type != ECOMMUNITY_SITE_ORIGIN)
+            unk_ecom = 1;
+          else
+            len = ecommunity_rt_soo_str (str_buf + str_pnt, pnt, type,
+                                         sub_type, format);
+        }
+      else if (type == ECOMMUNITY_ENCODE_OPAQUE)
+        {
+          if (filter == ECOMMUNITY_ROUTE_TARGET)
+            continue;
           if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)
             {
               uint16_t tunneltype;
               memcpy (&tunneltype, pnt + 5, 2);
               tunneltype = ntohs(tunneltype);
               len = sprintf (str_buf + str_pnt, "ET:%d", tunneltype);
-              str_pnt += len;
-              first = 0;
-              continue;
-            }
-          len = sprintf (str_buf + str_pnt, "?");
-          str_pnt += len;
-          first = 0;
-          continue;
-        case ECOMMUNITY_ENCODE_EVPN:
-          if(filter == ECOMMUNITY_ROUTE_TARGET)
-            {
-              continue;
             }
+          else
+            unk_ecom = 1;
+        }
+      else if (type == ECOMMUNITY_ENCODE_EVPN)
+        {
+          if (filter == ECOMMUNITY_ROUTE_TARGET)
+            continue;
           if (*pnt == ECOMMUNITY_SITE_ORIGIN)
             {
               char macaddr[6];
@@ -703,91 +793,20 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
               len = sprintf(str_buf + str_pnt, "EVPN:%02x:%02x:%02x:%02x:%02x:%02x",
                             macaddr[0], macaddr[1], macaddr[2],
                             macaddr[3], macaddr[4], macaddr[5]);
-              str_pnt += len;
-              first = 0;
-              continue;
             }
-          len = sprintf (str_buf + str_pnt, "?");
-          str_pnt += len;
-          first = 0;
-          continue;
-        default:
-          len = sprintf (str_buf + str_pnt, "?");
-          str_pnt += len;
-          first = 0;
-          continue;
+          else
+            unk_ecom = 1;
         }
+      else
+        unk_ecom = 1;
 
-      /* Low-order octet of type. */
-      type = *pnt++;
-      if (type !=  ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
-       {
-         len = sprintf (str_buf + str_pnt, "?");
-         str_pnt += len;
-         first = 0;
-         continue;
-       }
-
-      switch (format)
-       {
-       case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
-         prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
-         break;
-       case ECOMMUNITY_FORMAT_DISPLAY:
-         prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
-         break;
-       case ECOMMUNITY_FORMAT_ROUTE_MAP:
-         prefix = "";
-         break;
-       default:
-         prefix = "";
-         break;
-       }
+      if (unk_ecom)
+        len = sprintf (str_buf + str_pnt, "?");
 
-      /* Put string into buffer.  */
-      if (encode == ECOMMUNITY_ENCODE_AS4)
-       {
-         eas.as = (*pnt++ << 24);
-         eas.as |= (*pnt++ << 16);
-         eas.as |= (*pnt++ << 8);
-         eas.as |= (*pnt++);
-
-         eas.val = (*pnt++ << 8);
-         eas.val |= (*pnt++);
-
-         len = sprintf( str_buf + str_pnt, "%s%u:%u", prefix,
-                        eas.as, eas.val );
-         str_pnt += len;
-         first = 0;
-       }
-      if (encode == ECOMMUNITY_ENCODE_AS)
-       {
-         eas.as = (*pnt++ << 8);
-         eas.as |= (*pnt++);
-
-         eas.val = (*pnt++ << 24);
-         eas.val |= (*pnt++ << 16);
-         eas.val |= (*pnt++ << 8);
-         eas.val |= (*pnt++);
-
-         len = sprintf (str_buf + str_pnt, "%s%u:%u", prefix,
-                        eas.as, eas.val);
-         str_pnt += len;
-         first = 0;
-       }
-      else if (encode == ECOMMUNITY_ENCODE_IP)
-       {
-         memcpy (&eip.ip, pnt, 4);
-         pnt += 4;
-         eip.val = (*pnt++ << 8);
-         eip.val |= (*pnt++);
-
-         len = sprintf (str_buf + str_pnt, "%s%s:%u", prefix,
-                        inet_ntoa (eip.ip), eip.val);
-         str_pnt += len;
-         first = 0;
-       }
+      str_pnt += len;
+      first = 0;
     }
+
   return str_buf;
 }
 
index d6006e81d584bd5197fdbfac22bdcbd81c340048..8981ea34652d88ff903b4a930098b840b5d933f1 100644 (file)
@@ -76,6 +76,54 @@ struct ecommunity_val
 
 #define ecom_length(X)    ((X)->size * ECOMMUNITY_SIZE)
 
+/*
+ * Encode BGP Route Target AS:nn.
+ */
+static inline void
+encode_route_target_as (as_t as, u_int32_t val,
+                        struct ecommunity_val *eval)
+{
+  eval->val[0] = ECOMMUNITY_ENCODE_AS;
+  eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
+  eval->val[2] = (as >> 8) & 0xff;
+  eval->val[3] = as & 0xff;
+  eval->val[4] = (val >> 24) & 0xff;
+  eval->val[5] = (val >> 16) & 0xff;
+  eval->val[6] = (val >> 8) & 0xff;
+  eval->val[7] = val & 0xff;
+}
+
+/*
+ * Encode BGP Route Target IP:nn.
+ */
+static inline void
+encode_route_target_ip (struct in_addr ip, u_int16_t val,
+                        struct ecommunity_val *eval)
+{
+  eval->val[0] = ECOMMUNITY_ENCODE_IP;
+  eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
+  memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
+  eval->val[6] = (val >> 8) & 0xff;
+  eval->val[7] = val & 0xff;
+}
+
+/*
+ * Encode BGP Route Target AS4:nn.
+ */
+static inline void
+encode_route_target_as4 (as_t as, u_int16_t val,
+                         struct ecommunity_val *eval)
+{
+  eval->val[0] = ECOMMUNITY_ENCODE_AS4;
+  eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
+  eval->val[2] = (as >> 24) & 0xff;
+  eval->val[3] = (as >> 16) & 0xff;
+  eval->val[4] = (as >> 8) & 0xff;
+  eval->val[5] =  as & 0xff;
+  eval->val[6] = (val >> 8) & 0xff;
+  eval->val[7] = val & 0xff;
+}
+
 extern void ecommunity_init (void);
 extern void ecommunity_finish (void);
 extern void ecommunity_free (struct ecommunity **);