mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 06:38:04 +00:00 
			
		
		
		
	* (global) The great bgpd extern and static'ification. * bgp_routemap.c: remove unused ROUTE_MATCH_ASPATH_OLD code (route_set_metric_compile) fix u_int32_t to ULONG_MAX comparison warnings. * bgp_route.h: (bgp_process, bgp_withdraw, bgp_update) export these used by various files which had their own private declarations, in the case of mplsvpn - incorrect.
		
			
				
	
	
		
			683 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			683 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* BGP Extended Communities Attribute
 | 
						||
   Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
 | 
						||
 | 
						||
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 "bgpd/bgpd.h"
 | 
						||
#include "bgpd/bgp_ecommunity.h"
 | 
						||
 | 
						||
/* Hash of community attribute. */
 | 
						||
struct hash *ecomhash;
 | 
						||
 | 
						||
/* Allocate a new ecommunities.  */
 | 
						||
struct ecommunity *
 | 
						||
ecommunity_new ()
 | 
						||
{
 | 
						||
  return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY,
 | 
						||
					sizeof (struct ecommunity));
 | 
						||
}
 | 
						||
 | 
						||
/* Allocate ecommunities.  */
 | 
						||
void
 | 
						||
ecommunity_free (struct ecommunity *ecom)
 | 
						||
{
 | 
						||
  if (ecom->val)
 | 
						||
    XFREE (MTYPE_ECOMMUNITY_VAL, ecom->val);
 | 
						||
  if (ecom->str)
 | 
						||
    XFREE (MTYPE_ECOMMUNITY_STR, ecom->str);
 | 
						||
  XFREE (MTYPE_ECOMMUNITY, ecom);
 | 
						||
}
 | 
						||
 | 
						||
/* Add a new Extended Communities value to Extended 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
 | 
						||
ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval)
 | 
						||
{
 | 
						||
  u_int8_t *p;
 | 
						||
  int ret;
 | 
						||
  int c;
 | 
						||
 | 
						||
  /* When this is fist value, just add it.  */
 | 
						||
  if (ecom->val == NULL)
 | 
						||
    {
 | 
						||
      ecom->size++;
 | 
						||
      ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom));
 | 
						||
      memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE);
 | 
						||
      return 1;
 | 
						||
    }
 | 
						||
 | 
						||
  /* If the value already exists in the structure return 0.  */
 | 
						||
  c = 0;
 | 
						||
  for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++)
 | 
						||
    {
 | 
						||
      ret = memcmp (p, eval->val, ECOMMUNITY_SIZE);
 | 
						||
      if (ret == 0)
 | 
						||
        return 0;
 | 
						||
      if (ret > 0)
 | 
						||
        break;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Add the value to the structure with numerical sorting.  */
 | 
						||
  ecom->size++;
 | 
						||
  ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom));
 | 
						||
 | 
						||
  memmove (ecom->val + (c + 1) * ECOMMUNITY_SIZE,
 | 
						||
	   ecom->val + c * ECOMMUNITY_SIZE,
 | 
						||
	   (ecom->size - 1 - c) * ECOMMUNITY_SIZE);
 | 
						||
  memcpy (ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE);
 | 
						||
 | 
						||
  return 1;
 | 
						||
}
 | 
						||
 | 
						||
/* This function takes pointer to Extended Communites strucutre then
 | 
						||
   create a new Extended Communities structure by uniq and sort each
 | 
						||
   Exteneded Communities value.  */
 | 
						||
static struct ecommunity *
 | 
						||
ecommunity_uniq_sort (struct ecommunity *ecom)
 | 
						||
{
 | 
						||
  int i;
 | 
						||
  struct ecommunity *new;
 | 
						||
  struct ecommunity_val *eval;
 | 
						||
  
 | 
						||
  if (! ecom)
 | 
						||
    return NULL;
 | 
						||
  
 | 
						||
  new = ecommunity_new ();;
 | 
						||
  
 | 
						||
  for (i = 0; i < ecom->size; i++)
 | 
						||
    {
 | 
						||
      eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
 | 
						||
      ecommunity_add_val (new, eval);
 | 
						||
    }
 | 
						||
  return new;
 | 
						||
}
 | 
						||
 | 
						||
/* Parse Extended Communites Attribute in BGP packet.  */
 | 
						||
struct ecommunity *
 | 
						||
ecommunity_parse (u_int8_t *pnt, u_short length)
 | 
						||
{
 | 
						||
  struct ecommunity tmp;
 | 
						||
  struct ecommunity *new;
 | 
						||
 | 
						||
  /* Length check.  */
 | 
						||
  if (length % ECOMMUNITY_SIZE)
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  /* Prepare tmporary structure for making a new Extended Communities
 | 
						||
     Attribute.  */
 | 
						||
  tmp.size = length / ECOMMUNITY_SIZE;
 | 
						||
  tmp.val = pnt;
 | 
						||
 | 
						||
  /* Create a new Extended Communities Attribute by uniq and sort each
 | 
						||
     Extended Communities value  */
 | 
						||
  new = ecommunity_uniq_sort (&tmp);
 | 
						||
 | 
						||
  return ecommunity_intern (new);
 | 
						||
}
 | 
						||
 | 
						||
/* Duplicate the Extended Communities Attribute structure.  */
 | 
						||
struct ecommunity *
 | 
						||
ecommunity_dup (struct ecommunity *ecom)
 | 
						||
{
 | 
						||
  struct ecommunity *new;
 | 
						||
 | 
						||
  new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity));
 | 
						||
  new->size = ecom->size;
 | 
						||
  if (new->size)
 | 
						||
    {
 | 
						||
      new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
 | 
						||
      memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    new->val = NULL;
 | 
						||
  return new;
 | 
						||
}
 | 
						||
 | 
						||
/* Retrun string representation of communities attribute. */
 | 
						||
char *
 | 
						||
ecommunity_str (struct ecommunity *ecom)
 | 
						||
{
 | 
						||
  if (! ecom->str)
 | 
						||
    ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
 | 
						||
  return ecom->str;
 | 
						||
}
 | 
						||
 | 
						||
/* Merge two Extended Communities Attribute structure.  */
 | 
						||
struct ecommunity *
 | 
						||
ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
 | 
						||
{
 | 
						||
  if (ecom1->val)
 | 
						||
    ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val, 
 | 
						||
			   (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
 | 
						||
  else
 | 
						||
    ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL,
 | 
						||
			  (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
 | 
						||
 | 
						||
  memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE),
 | 
						||
	  ecom2->val, ecom2->size * ECOMMUNITY_SIZE);
 | 
						||
  ecom1->size += ecom2->size;
 | 
						||
 | 
						||
  return ecom1;
 | 
						||
}
 | 
						||
 | 
						||
/* Intern Extended Communities Attribute.  */
 | 
						||
struct ecommunity *
 | 
						||
ecommunity_intern (struct ecommunity *ecom)
 | 
						||
{
 | 
						||
  struct ecommunity *find;
 | 
						||
 | 
						||
  assert (ecom->refcnt == 0);
 | 
						||
 | 
						||
  find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern);
 | 
						||
 | 
						||
  if (find != ecom)
 | 
						||
    ecommunity_free (ecom);
 | 
						||
 | 
						||
  find->refcnt++;
 | 
						||
 | 
						||
  if (! find->str)
 | 
						||
    find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY);
 | 
						||
 | 
						||
  return find;
 | 
						||
}
 | 
						||
 | 
						||
/* Unintern Extended Communities Attribute.  */
 | 
						||
void
 | 
						||
ecommunity_unintern (struct ecommunity *ecom)
 | 
						||
{
 | 
						||
  struct ecommunity *ret;
 | 
						||
 | 
						||
  if (ecom->refcnt)
 | 
						||
    ecom->refcnt--;
 | 
						||
 | 
						||
  /* Pull off from hash.  */
 | 
						||
  if (ecom->refcnt == 0)
 | 
						||
    {
 | 
						||
      /* Extended community must be in the hash.  */
 | 
						||
      ret = (struct ecommunity *) hash_release (ecomhash, ecom);
 | 
						||
      assert (ret != NULL);
 | 
						||
 | 
						||
      ecommunity_free (ecom);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/* Utinity function to make hash key.  */
 | 
						||
unsigned int
 | 
						||
ecommunity_hash_make (struct ecommunity *ecom)
 | 
						||
{
 | 
						||
  int c;
 | 
						||
  unsigned int key;
 | 
						||
  u_int8_t *pnt;
 | 
						||
 | 
						||
  key = 0;
 | 
						||
  pnt = ecom->val;
 | 
						||
  
 | 
						||
  for (c = 0; c < ecom->size * ECOMMUNITY_SIZE; c++)
 | 
						||
    key += pnt[c];
 | 
						||
 | 
						||
  return key;
 | 
						||
}
 | 
						||
 | 
						||
/* Compare two Extended Communities Attribute structure.  */
 | 
						||
int
 | 
						||
ecommunity_cmp (const struct ecommunity *ecom1, 
 | 
						||
                const struct ecommunity *ecom2)
 | 
						||
{
 | 
						||
  if (ecom1->size == ecom2->size
 | 
						||
      && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0)
 | 
						||
    return 1;
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/* Initialize Extended Comminities related hash. */
 | 
						||
void
 | 
						||
ecommunity_init ()
 | 
						||
{
 | 
						||
  ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
 | 
						||
}
 | 
						||
 | 
						||
/* Extended Communities token enum. */
 | 
						||
enum ecommunity_token
 | 
						||
{
 | 
						||
  ecommunity_token_rt,
 | 
						||
  ecommunity_token_soo,
 | 
						||
  ecommunity_token_val,
 | 
						||
  ecommunity_token_unknown
 | 
						||
};
 | 
						||
 | 
						||
/* Get next Extended Communities token from the string. */
 | 
						||
static const char *
 | 
						||
ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
 | 
						||
		     enum ecommunity_token *token)
 | 
						||
{
 | 
						||
  int ret;
 | 
						||
  int dot = 0;
 | 
						||
  int digit = 0;
 | 
						||
  int separator = 0;
 | 
						||
  u_int32_t val_low = 0;
 | 
						||
  u_int32_t val_high = 0;
 | 
						||
  const char *p = str;
 | 
						||
  struct in_addr ip;
 | 
						||
  char ipstr[INET_ADDRSTRLEN + 1];
 | 
						||
 | 
						||
  /* Skip white space. */
 | 
						||
  while (isspace ((int) *p))
 | 
						||
    {
 | 
						||
      p++;
 | 
						||
      str++;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Check the end of the line. */
 | 
						||
  if (*p == '\0')
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  /* "rt" and "soo" keyword parse. */
 | 
						||
  if (! isdigit ((int) *p)) 
 | 
						||
    {
 | 
						||
      /* "rt" match check.  */
 | 
						||
      if (tolower ((int) *p) == 'r')
 | 
						||
	{
 | 
						||
	  p++;
 | 
						||
 	  if (tolower ((int) *p) == 't')
 | 
						||
	    {
 | 
						||
	      p++;
 | 
						||
	      *token = ecommunity_token_rt;
 | 
						||
	      return p;
 | 
						||
	    }
 | 
						||
	  if (isspace ((int) *p) || *p == '\0')
 | 
						||
	    {
 | 
						||
	      *token = ecommunity_token_rt;
 | 
						||
	      return p;
 | 
						||
	    }
 | 
						||
	  goto error;
 | 
						||
	}
 | 
						||
      /* "soo" match check.  */
 | 
						||
      else if (tolower ((int) *p) == 's')
 | 
						||
	{
 | 
						||
	  p++;
 | 
						||
 	  if (tolower ((int) *p) == 'o')
 | 
						||
	    {
 | 
						||
	      p++;
 | 
						||
	      if (tolower ((int) *p) == 'o')
 | 
						||
		{
 | 
						||
		  p++;
 | 
						||
		  *token = ecommunity_token_soo;
 | 
						||
		  return p;
 | 
						||
		}
 | 
						||
	      if (isspace ((int) *p) || *p == '\0')
 | 
						||
		{
 | 
						||
		  *token = ecommunity_token_soo;
 | 
						||
		  return p;
 | 
						||
		}
 | 
						||
	      goto error;
 | 
						||
	    }
 | 
						||
	  if (isspace ((int) *p) || *p == '\0')
 | 
						||
	    {
 | 
						||
	      *token = ecommunity_token_soo;
 | 
						||
	      return p;
 | 
						||
	    }
 | 
						||
	  goto error;
 | 
						||
	}
 | 
						||
      goto error;
 | 
						||
    }
 | 
						||
  
 | 
						||
  while (isdigit ((int) *p) || *p == ':' || *p == '.') 
 | 
						||
    {
 | 
						||
      if (*p == ':') 
 | 
						||
	{
 | 
						||
	  if (separator)
 | 
						||
	    goto error;
 | 
						||
 | 
						||
	  separator = 1;
 | 
						||
	  digit = 0;
 | 
						||
 | 
						||
	  if (dot)
 | 
						||
	    {
 | 
						||
	      if ((p - str) > INET_ADDRSTRLEN)
 | 
						||
		goto error;
 | 
						||
 | 
						||
	      memset (ipstr, 0, INET_ADDRSTRLEN + 1);
 | 
						||
	      memcpy (ipstr, str, p - str);
 | 
						||
 | 
						||
	      ret = inet_aton (ipstr, &ip);
 | 
						||
	      if (ret == 0)
 | 
						||
		goto error;
 | 
						||
	    }
 | 
						||
	  else
 | 
						||
	    val_high = val_low;
 | 
						||
 | 
						||
	  val_low = 0;
 | 
						||
	}
 | 
						||
      else if (*p == '.')
 | 
						||
	{
 | 
						||
	  if (separator)
 | 
						||
	    goto error;
 | 
						||
	  dot++;
 | 
						||
	  if (dot > 4)
 | 
						||
	    goto error;
 | 
						||
	}
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  digit = 1;
 | 
						||
	  val_low *= 10;
 | 
						||
	  val_low += (*p - '0');
 | 
						||
	}
 | 
						||
      p++;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Low digit part must be there. */
 | 
						||
  if (! digit || ! separator)
 | 
						||
    goto error;
 | 
						||
 | 
						||
  /* Encode result into routing distinguisher.  */
 | 
						||
  if (dot)
 | 
						||
    {
 | 
						||
      eval->val[0] = ECOMMUNITY_ENCODE_IP;
 | 
						||
      eval->val[1] = 0;
 | 
						||
      memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
 | 
						||
      eval->val[6] = (val_low >> 8) & 0xff;
 | 
						||
      eval->val[7] = val_low & 0xff;
 | 
						||
    }
 | 
						||
  else
 | 
						||
    {
 | 
						||
      eval->val[0] = ECOMMUNITY_ENCODE_AS;
 | 
						||
      eval->val[1] = 0;
 | 
						||
      eval->val[2] = (val_high >>8) & 0xff;
 | 
						||
      eval->val[3] = val_high & 0xff;
 | 
						||
      eval->val[4] = (val_low >>24) & 0xff;
 | 
						||
      eval->val[5] = (val_low >>16) & 0xff;
 | 
						||
      eval->val[6] = (val_low >>8) & 0xff;
 | 
						||
      eval->val[7] = val_low & 0xff;
 | 
						||
    }
 | 
						||
  *token = ecommunity_token_val;
 | 
						||
  return p;
 | 
						||
 | 
						||
 error:
 | 
						||
  *token = ecommunity_token_unknown;
 | 
						||
  return p;
 | 
						||
}
 | 
						||
 | 
						||
/* Convert string to extended community attribute. 
 | 
						||
 | 
						||
   When type is already known, please specify both str and type.  str
 | 
						||
   should not include keyword such as "rt" and "soo".  Type is
 | 
						||
   ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
 | 
						||
   keyword_included should be zero.
 | 
						||
 | 
						||
   For example route-map's "set extcommunity" command case:
 | 
						||
 | 
						||
   "rt 100:1 100:2 100:3"        -> str = "100:1 100:2 100:3"
 | 
						||
                                    type = ECOMMUNITY_ROUTE_TARGET
 | 
						||
                                    keyword_included = 0
 | 
						||
 | 
						||
   "soo 100:1"                   -> str = "100:1"
 | 
						||
                                    type = ECOMMUNITY_SITE_ORIGIN
 | 
						||
                                    keyword_included = 0
 | 
						||
 | 
						||
   When string includes keyword for each extended community value.
 | 
						||
   Please specify keyword_included as non-zero value.
 | 
						||
 | 
						||
   For example standard extcommunity-list case:
 | 
						||
 | 
						||
   "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
 | 
						||
                                    type = 0
 | 
						||
                                    keyword_include = 1
 | 
						||
*/
 | 
						||
struct ecommunity *
 | 
						||
ecommunity_str2com (const char *str, int type, int keyword_included)
 | 
						||
{
 | 
						||
  struct ecommunity *ecom = NULL;
 | 
						||
  enum ecommunity_token token;
 | 
						||
  struct ecommunity_val eval;
 | 
						||
  int keyword = 0;
 | 
						||
 | 
						||
  while ((str = ecommunity_gettoken (str, &eval, &token)))
 | 
						||
    {
 | 
						||
      switch (token)
 | 
						||
	{
 | 
						||
	case ecommunity_token_rt:
 | 
						||
	case ecommunity_token_soo:
 | 
						||
	  if (! keyword_included || keyword)
 | 
						||
	    {
 | 
						||
	      if (ecom)
 | 
						||
		ecommunity_free (ecom);
 | 
						||
	      return NULL;
 | 
						||
	    }
 | 
						||
	  keyword = 1;
 | 
						||
 | 
						||
	  if (token == ecommunity_token_rt)
 | 
						||
	    {
 | 
						||
	      type = ECOMMUNITY_ROUTE_TARGET;
 | 
						||
	    }
 | 
						||
	  if (token == ecommunity_token_soo)
 | 
						||
	    {
 | 
						||
	      type = ECOMMUNITY_SITE_ORIGIN;
 | 
						||
	    }
 | 
						||
	  break;
 | 
						||
	case ecommunity_token_val:
 | 
						||
	  if (keyword_included)
 | 
						||
	    {
 | 
						||
	      if (! keyword)
 | 
						||
		{
 | 
						||
		  if (ecom)
 | 
						||
		    ecommunity_free (ecom);
 | 
						||
		  return NULL;
 | 
						||
		}
 | 
						||
	      keyword = 0;
 | 
						||
	    }
 | 
						||
	  if (ecom == NULL)
 | 
						||
	    ecom = ecommunity_new ();
 | 
						||
	  eval.val[1] = type;
 | 
						||
	  ecommunity_add_val (ecom, &eval);
 | 
						||
	  break;
 | 
						||
	case ecommunity_token_unknown:
 | 
						||
	default:
 | 
						||
	  if (ecom)
 | 
						||
	    ecommunity_free (ecom);
 | 
						||
	  return NULL;
 | 
						||
	  break;
 | 
						||
	}
 | 
						||
    }
 | 
						||
  return ecom;
 | 
						||
}
 | 
						||
 | 
						||
/* Convert extended community attribute to string.  
 | 
						||
 | 
						||
   Due to historical reason of industry standard implementation, there
 | 
						||
   are three types of format.
 | 
						||
 | 
						||
   route-map set extcommunity format
 | 
						||
        "rt 100:1 100:2"
 | 
						||
        "soo 100:3"
 | 
						||
 | 
						||
   extcommunity-list
 | 
						||
        "rt 100:1 rt 100:2 soo 100:3"
 | 
						||
 | 
						||
   "show ip bgp" and extcommunity-list regular expression matching
 | 
						||
        "RT:100:1 RT:100:2 SoO:100:3"
 | 
						||
 | 
						||
   For each formath please use below definition for format:
 | 
						||
 | 
						||
   ECOMMUNITY_FORMAT_ROUTE_MAP
 | 
						||
   ECOMMUNITY_FORMAT_COMMUNITY_LIST
 | 
						||
   ECOMMUNITY_FORMAT_DISPLAY
 | 
						||
*/
 | 
						||
char *
 | 
						||
ecommunity_ecom2str (struct ecommunity *ecom, int format)
 | 
						||
{
 | 
						||
  int i;
 | 
						||
  u_int8_t *pnt;
 | 
						||
  int encode = 0;
 | 
						||
  int type = 0;
 | 
						||
#define ECOMMUNITY_STR_DEFAULT_LEN  26
 | 
						||
  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);
 | 
						||
      str_buf[0] = '\0';
 | 
						||
      return str_buf;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Prepare buffer.  */
 | 
						||
  str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
 | 
						||
  str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
 | 
						||
  str_pnt = 0;
 | 
						||
 | 
						||
  for (i = 0; i < ecom->size; i++)
 | 
						||
    {
 | 
						||
      /* Space between each value.  */
 | 
						||
      if (! first)
 | 
						||
	str_buf[str_pnt++] = ' ';
 | 
						||
 | 
						||
      pnt = ecom->val + (i * 8);
 | 
						||
 | 
						||
      /* High-order octet of type. */
 | 
						||
      encode = *pnt++;
 | 
						||
      if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP)
 | 
						||
	{
 | 
						||
	  len = sprintf (str_buf + str_pnt, "?");
 | 
						||
	  str_pnt += len;
 | 
						||
	  first = 0;
 | 
						||
	  continue;
 | 
						||
	}
 | 
						||
      
 | 
						||
      /* Low-order octet of type. */
 | 
						||
      type = *pnt++;
 | 
						||
      if (type !=  ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
 | 
						||
	{
 | 
						||
	  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;
 | 
						||
	}
 | 
						||
 | 
						||
      /* Make it sure size is enough.  */
 | 
						||
      while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
 | 
						||
	{
 | 
						||
	  str_size *= 2;
 | 
						||
	  str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
 | 
						||
	}
 | 
						||
 | 
						||
      /* Put string into buffer.  */
 | 
						||
      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%d:%d", 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:%d", prefix,
 | 
						||
			 inet_ntoa (eip.ip), eip.val);
 | 
						||
	  str_pnt += len;
 | 
						||
	  first = 0;
 | 
						||
	}
 | 
						||
    }
 | 
						||
  return str_buf;
 | 
						||
}
 | 
						||
 | 
						||
int
 | 
						||
ecommunity_match (const struct ecommunity *ecom1, 
 | 
						||
                  const struct ecommunity *ecom2)
 | 
						||
{
 | 
						||
  int i = 0;
 | 
						||
  int j = 0;
 | 
						||
 | 
						||
  if (ecom1 == NULL && ecom2 == NULL)
 | 
						||
    return 1;
 | 
						||
 | 
						||
  if (ecom1 == NULL || ecom2 == NULL)
 | 
						||
    return 0;
 | 
						||
 | 
						||
  if (ecom1->size < ecom2->size)
 | 
						||
    return 0;
 | 
						||
 | 
						||
  /* Every community on com2 needs to be on com1 for this to match */
 | 
						||
  while (i < ecom1->size && j < ecom2->size)
 | 
						||
    {
 | 
						||
      if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0)
 | 
						||
        j++;
 | 
						||
      i++;
 | 
						||
    }
 | 
						||
 | 
						||
  if (j == ecom2->size)
 | 
						||
    return 1;
 | 
						||
  else
 | 
						||
    return 0;
 | 
						||
}
 | 
						||
 |