mirror of
				https://gitlab.labs.nic.cz/labs/bird.git
				synced 2024-05-11 16:54:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			442 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			442 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *	BIRD -- Path Operations
 | |
|  *
 | |
|  *	(c) 2000 Martin Mares <mj@ucw.cz>
 | |
|  *	(c) 2000 Pavel Machek <pavel@ucw.cz>
 | |
|  *
 | |
|  *	Can be freely distributed and used under the terms of the GNU GPL.
 | |
|  */
 | |
| 
 | |
| #include "nest/bird.h"
 | |
| #include "nest/route.h"
 | |
| #include "nest/attrs.h"
 | |
| #include "lib/resource.h"
 | |
| #include "lib/unaligned.h"
 | |
| #include "lib/string.h"
 | |
| #include "filter/filter.h"
 | |
| 
 | |
| 
 | |
| // static inline void put_as(byte *data, u32 as) { put_u32(data, as); }
 | |
| // static inline u32 get_as(byte *data) { return get_u32(data); }
 | |
| 
 | |
| #define put_as put_u32
 | |
| #define get_as get_u32
 | |
| #define BS  4
 | |
| 
 | |
| struct adata *
 | |
| as_path_prepend(struct linpool *pool, struct adata *olda, u32 as)
 | |
| {
 | |
|   struct adata *newa;
 | |
| 
 | |
|   if (olda->length && olda->data[0] == AS_PATH_SEQUENCE && olda->data[1] < 255)
 | |
|     /* Starting with sequence => just prepend the AS number */
 | |
|     {
 | |
|       int nl = olda->length + BS;
 | |
|       newa = lp_alloc(pool, sizeof(struct adata) + nl);
 | |
|       newa->length = nl;
 | |
|       newa->data[0] = AS_PATH_SEQUENCE;
 | |
|       newa->data[1] = olda->data[1] + 1;
 | |
|       memcpy(newa->data + BS + 2, olda->data + 2, olda->length - 2);
 | |
|     }
 | |
|   else /* Create new path segment */
 | |
|     {
 | |
|       int nl = olda->length + BS + 2;
 | |
|       newa = lp_alloc(pool, sizeof(struct adata) + nl);
 | |
|       newa->length = nl;
 | |
|       newa->data[0] = AS_PATH_SEQUENCE;
 | |
|       newa->data[1] = 1;
 | |
|       memcpy(newa->data + BS + 2, olda->data, olda->length);
 | |
|     }
 | |
|   put_as(newa->data + 2, as);
 | |
|   return newa;
 | |
| }
 | |
| 
 | |
| int
 | |
| as_path_convert_to_old(struct adata *path, byte *dst, int *new_used)
 | |
| {
 | |
|   byte *src = path->data;
 | |
|   byte *src_end = src + path->length;
 | |
|   byte *dst_start = dst;
 | |
|   u32 as;
 | |
|   int i, n;
 | |
|   *new_used = 0;
 | |
| 
 | |
|   while (src < src_end)
 | |
|     {
 | |
|       n = src[1];
 | |
|       *dst++ = *src++;
 | |
|       *dst++ = *src++;
 | |
| 
 | |
|       for(i=0; i<n; i++)
 | |
| 	{
 | |
| 	  as = get_u32(src);
 | |
| 	  if (as > 0xFFFF) 
 | |
| 	    {
 | |
| 	      as = AS_TRANS;
 | |
| 	      *new_used = 1;
 | |
| 	    }
 | |
| 	  put_u16(dst, as);
 | |
| 	  src += 4;
 | |
| 	  dst += 2;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return dst - dst_start;
 | |
| }
 | |
| 
 | |
| int
 | |
| as_path_convert_to_new(struct adata *path, byte *dst, int req_as)
 | |
| {
 | |
|   byte *src = path->data;
 | |
|   byte *src_end = src + path->length;
 | |
|   byte *dst_start = dst;
 | |
|   u32 as;
 | |
|   int i, t, n;
 | |
| 
 | |
| 
 | |
|   while ((src < src_end) && (req_as > 0))
 | |
|     {
 | |
|       t = *src++;
 | |
|       n = *src++;
 | |
| 
 | |
|       if (t == AS_PATH_SEQUENCE)
 | |
| 	{
 | |
| 	  if (n > req_as)
 | |
| 	    n = req_as;
 | |
| 
 | |
| 	  req_as -= n;
 | |
| 	}
 | |
|       else // t == AS_PATH_SET
 | |
| 	req_as--;
 | |
| 
 | |
|       *dst++ = t;
 | |
|       *dst++ = n;
 | |
| 
 | |
|       for(i=0; i<n; i++)
 | |
| 	{
 | |
| 	  as = get_u16(src);
 | |
| 	  put_u32(dst, as);
 | |
| 	  src += 2;
 | |
| 	  dst += 4;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return dst - dst_start;
 | |
| }
 | |
| 
 | |
| void
 | |
| as_path_format(struct adata *path, byte *buf, unsigned int size)
 | |
| {
 | |
|   byte *p = path->data;
 | |
|   byte *e = p + path->length;
 | |
|   byte *end = buf + size - 16;
 | |
|   int sp = 1;
 | |
|   int l, isset;
 | |
| 
 | |
|   while (p < e)
 | |
|     {
 | |
|       if (buf > end)
 | |
| 	{
 | |
| 	  strcpy(buf, " ...");
 | |
| 	  return;
 | |
| 	}
 | |
|       isset = (*p++ == AS_PATH_SET);
 | |
|       l = *p++;
 | |
|       if (isset)
 | |
| 	{
 | |
| 	  if (!sp)
 | |
| 	    *buf++ = ' ';
 | |
| 	  *buf++ = '{';
 | |
| 	  sp = 0;
 | |
| 	}
 | |
|       while (l-- && buf <= end)
 | |
| 	{
 | |
| 	  if (!sp)
 | |
| 	    *buf++ = ' ';
 | |
| 	  buf += bsprintf(buf, "%u", get_as(p));
 | |
| 	  p += BS;
 | |
| 	  sp = 0;
 | |
| 	}
 | |
|       if (isset)
 | |
| 	{
 | |
| 	  *buf++ = ' ';
 | |
| 	  *buf++ = '}';
 | |
| 	  sp = 0;
 | |
| 	}
 | |
|     }
 | |
|   *buf = 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| as_path_getlen(struct adata *path)
 | |
| {
 | |
|   return as_path_getlen_int(path, BS);
 | |
| }
 | |
| 
 | |
| int
 | |
| as_path_getlen_int(struct adata *path, int bs)
 | |
| {
 | |
|   int res = 0;
 | |
|   u8 *p = path->data;
 | |
|   u8 *q = p+path->length;
 | |
|   int len;
 | |
| 
 | |
|   while (p<q)
 | |
|     {
 | |
|       switch (*p++)
 | |
| 	{
 | |
| 	case AS_PATH_SET:      len = *p++; res++;      p += bs * len; break;
 | |
| 	case AS_PATH_SEQUENCE: len = *p++; res += len; p += bs * len; break;
 | |
| 	default: bug("as_path_getlen: Invalid path segment");
 | |
| 	}
 | |
|     }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| int
 | |
| as_path_get_last(struct adata *path, u32 *orig_as)
 | |
| {
 | |
|   int found = 0;
 | |
|   u32 res = 0;
 | |
|   u8 *p = path->data;
 | |
|   u8 *q = p+path->length;
 | |
|   int len;
 | |
| 
 | |
|   while (p<q)
 | |
|     {
 | |
|       switch (*p++)
 | |
| 	{
 | |
| 	case AS_PATH_SET:
 | |
| 	  if (len = *p++)
 | |
| 	    {
 | |
| 	      found = 0;
 | |
| 	      p += BS * len;
 | |
| 	    }
 | |
| 	  break;
 | |
| 	case AS_PATH_SEQUENCE:
 | |
| 	  if (len = *p++)
 | |
| 	    {
 | |
| 	      found = 1;
 | |
| 	      res = get_as(p + BS * (len - 1));
 | |
| 	      p += BS * len;
 | |
| 	    }
 | |
| 	  break;
 | |
| 	default: bug("as_path_get_first: Invalid path segment");
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (found)
 | |
|     *orig_as = res;
 | |
|   return found;
 | |
| }
 | |
| 
 | |
| int
 | |
| as_path_get_first(struct adata *path, u32 *last_as)
 | |
| {
 | |
|   u8 *p = path->data;
 | |
| 
 | |
|   if ((path->length == 0) || (p[0] != AS_PATH_SEQUENCE) || (p[1] == 0))
 | |
|     return 0;
 | |
|   else
 | |
|     {
 | |
|       *last_as = get_as(p+2);
 | |
|       return 1;
 | |
|     }
 | |
| }
 | |
| 
 | |
| int
 | |
| as_path_is_member(struct adata *path, u32 as)
 | |
| {
 | |
|   u8 *p = path->data;
 | |
|   u8 *q = p+path->length;
 | |
|   int i, n;
 | |
| 
 | |
|   while (p<q)
 | |
|     {
 | |
|       n = p[1];
 | |
|       p += 2;
 | |
|       for(i=0; i<n; i++)
 | |
| 	{
 | |
| 	  if (get_as(p) == as)
 | |
| 	    return 1;
 | |
| 	  p += BS;
 | |
| 	}
 | |
|     }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| struct pm_pos
 | |
| {
 | |
|   u8 set;
 | |
|   u8 mark;
 | |
|   union
 | |
|   {
 | |
|     char *sp;
 | |
|     u32 asn;
 | |
|   } val;
 | |
| };
 | |
| 
 | |
| static int
 | |
| parse_path(struct adata *path, struct pm_pos *pos)
 | |
| {
 | |
|   u8 *p = path->data;
 | |
|   u8 *q = p + path->length;
 | |
|   struct pm_pos *opos = pos;
 | |
|   int i, len;
 | |
| 
 | |
| 
 | |
|   while (p < q)
 | |
|     switch (*p++)
 | |
|       {
 | |
|       case AS_PATH_SET:
 | |
| 	pos->set = 1;
 | |
| 	pos->mark = 0;
 | |
| 	pos->val.sp = p;
 | |
| 	len = *p;
 | |
| 	p += 1 + BS * len;
 | |
| 	pos++;
 | |
| 	break;
 | |
|       
 | |
|       case AS_PATH_SEQUENCE:
 | |
| 	len = *p++;
 | |
| 	for (i = 0; i < len; i++)
 | |
| 	  {
 | |
| 	    pos->set = 0;
 | |
| 	    pos->mark = 0;
 | |
| 	    pos->val.asn = get_as(p);
 | |
| 	    p += BS;
 | |
| 	    pos++;
 | |
| 	  }
 | |
| 	break;
 | |
| 
 | |
|       default:
 | |
| 	bug("as_path_match: Invalid path component");
 | |
|       }
 | |
|   
 | |
|   return pos - opos;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| pm_match(struct pm_pos *pos, u32 asn)
 | |
| {
 | |
|   if (! pos->set)
 | |
|     return pos->val.asn == asn;
 | |
| 
 | |
|   u8 *p = pos->val.sp;
 | |
|   int len = *p++;
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < len; i++)
 | |
|     if (get_as(p + i * BS) == asn)
 | |
|       return 1;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh)
 | |
| {
 | |
|   int j;
 | |
| 
 | |
|   if (pos[i].set)
 | |
|     pos[i].mark = 1;
 | |
|   
 | |
|   for (j = i + 1; (j < plen) && pos[j].set && (! pos[j].mark); j++)
 | |
|     pos[j].mark = 1;
 | |
|   pos[j].mark = 1;
 | |
| 
 | |
|   /* We are going downwards, therefore every mark is
 | |
|      new low and just the first mark is new high */
 | |
| 
 | |
|   *nl = i + (pos[i].set ? 0 : 1);
 | |
| 
 | |
|   if (*nh < 0)
 | |
|     *nh = j;
 | |
| }
 | |
| 
 | |
| /* AS path matching is nontrivial. Because AS path can
 | |
|  * contain sets, it is not a plain wildcard matching. A set 
 | |
|  * in an AS path is interpreted as it might represent any
 | |
|  * sequence of AS numbers from that set (possibly with
 | |
|  * repetitions). So it is also a kind of a pattern,
 | |
|  * more complicated than a path mask.
 | |
|  *
 | |
|  * The algorithm for AS path matching is a variant
 | |
|  * of nondeterministic finite state machine, where
 | |
|  * positions in AS path are states, and items in
 | |
|  * path mask are input for that finite state machine.
 | |
|  * During execution of the algorithm we maintain a set
 | |
|  * of marked states - a state is marked if it can be
 | |
|  * reached by any walk through NFSM with regard to
 | |
|  * currently processed part of input. When we process
 | |
|  * next part of mask, we advance each marked state.
 | |
|  * We start with marked first position, when we
 | |
|  * run out of marked positions, we reject. When
 | |
|  * we process the whole mask, we accept iff final position
 | |
|  * (auxiliary position after last real position in AS path)
 | |
|  * is marked.
 | |
|  */
 | |
| 
 | |
| int
 | |
| as_path_match(struct adata *path, struct f_path_mask *mask)
 | |
| {
 | |
|   struct pm_pos pos[2048 + 1];
 | |
|   int plen = parse_path(path, pos);
 | |
|   int l, h, i, nh, nl;
 | |
|   u32 val = 0;
 | |
| 
 | |
|   /* l and h are bound of interval of positions where
 | |
|      are marked states */
 | |
| 
 | |
|   pos[plen].set = 0;
 | |
|   pos[plen].mark = 0;
 | |
| 
 | |
|   l = h = 0;
 | |
|   pos[0].mark = 1;
 | |
|   
 | |
|   while (mask)
 | |
|     {
 | |
|       /* We remove this mark to not step after pos[plen] */
 | |
|       pos[plen].mark = 0;
 | |
| 
 | |
|       switch (mask->kind)
 | |
| 	{
 | |
| 	case PM_ASTERISK:
 | |
| 	  for (i = l; i <= plen; i++)
 | |
| 	    pos[i].mark = 1;
 | |
| 	  h = plen;
 | |
| 	  break;
 | |
| 
 | |
| 	case PM_ASN:
 | |
| 	  val = mask->val;
 | |
| 	  goto step;
 | |
| 	case PM_ASN_EXPR:
 | |
| 	  val = f_eval_asn((struct f_inst *) mask->val);
 | |
| 	  goto step;
 | |
| 	case PM_QUESTION:
 | |
| 	step:
 | |
| 	  nh = nl = -1;
 | |
| 	  for (i = h; i >= l; i--)
 | |
| 	    if (pos[i].mark)
 | |
| 	      {
 | |
| 		pos[i].mark = 0;
 | |
| 		if ((mask->kind == PM_QUESTION) || pm_match(pos + i, val))
 | |
| 		  pm_mark(pos, i, plen, &nl, &nh);
 | |
| 	      }
 | |
| 
 | |
| 	  if (nh < 0)
 | |
| 	    return 0;
 | |
| 
 | |
| 	  h = nh;
 | |
| 	  l = nl;
 | |
| 	  break;
 | |
| 	}
 | |
| 
 | |
|       mask = mask->next;
 | |
|     }
 | |
| 
 | |
|   return pos[plen].mark;
 | |
| }
 |