| /* AS path management routines. |
| Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro |
| |
| 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 "vector.h" |
| #include "vty.h" |
| #include "str.h" |
| #include "log.h" |
| |
| #include "bgpd/bgpd.h" |
| #include "bgpd/bgp_aspath.h" |
| |
| /* Attr. Flags and Attr. Type Code. */ |
| #define AS_HEADER_SIZE 2 |
| |
| /* Two octet is used for AS value. */ |
| #define AS_VALUE_SIZE sizeof (as_t) |
| |
| /* AS segment octet length. */ |
| #define ASSEGMENT_LEN(X) ((X)->length * AS_VALUE_SIZE + AS_HEADER_SIZE) |
| |
| /* To fetch and store as segment value. */ |
| struct assegment |
| { |
| u_char type; |
| u_char length; |
| as_t asval[1]; |
| }; |
| |
| /* Hash for aspath. This is the top level structure of AS path. */ |
| struct hash *ashash; |
| |
| static struct aspath * |
| aspath_new () |
| { |
| struct aspath *aspath; |
| |
| aspath = XMALLOC (MTYPE_AS_PATH, sizeof (struct aspath)); |
| memset (aspath, 0, sizeof (struct aspath)); |
| return aspath; |
| } |
| |
| /* Free AS path structure. */ |
| void |
| aspath_free (struct aspath *aspath) |
| { |
| if (!aspath) |
| return; |
| if (aspath->data) |
| XFREE (MTYPE_AS_SEG, aspath->data); |
| if (aspath->str) |
| XFREE (MTYPE_AS_STR, aspath->str); |
| XFREE (MTYPE_AS_PATH, aspath); |
| } |
| |
| /* Unintern aspath from AS path bucket. */ |
| void |
| aspath_unintern (struct aspath *aspath) |
| { |
| struct aspath *ret; |
| |
| if (aspath->refcnt) |
| aspath->refcnt--; |
| |
| if (aspath->refcnt == 0) |
| { |
| /* This aspath must exist in aspath hash table. */ |
| ret = hash_release (ashash, aspath); |
| assert (ret != NULL); |
| aspath_free (aspath); |
| } |
| } |
| |
| /* Return the start or end delimiters for a particular Segment type */ |
| #define AS_SEG_START 0 |
| #define AS_SEG_END 1 |
| static char |
| aspath_delimiter_char (u_char type, u_char which) |
| { |
| int i; |
| struct |
| { |
| int type; |
| char start; |
| char end; |
| } aspath_delim_char [] = |
| { |
| { AS_SET, '{', '}' }, |
| { AS_SEQUENCE, ' ', ' ' }, |
| { AS_CONFED_SET, '[', ']' }, |
| { AS_CONFED_SEQUENCE, '(', ')' }, |
| { 0 } |
| }; |
| |
| for (i = 0; aspath_delim_char[i].type != 0; i++) |
| { |
| if (aspath_delim_char[i].type == type) |
| { |
| if (which == AS_SEG_START) |
| return aspath_delim_char[i].start; |
| else if (which == AS_SEG_END) |
| return aspath_delim_char[i].end; |
| } |
| } |
| return ' '; |
| } |
| |
| /* Convert aspath structure to string expression. */ |
| char * |
| aspath_make_str_count (struct aspath *as) |
| { |
| int space; |
| u_char type; |
| caddr_t pnt; |
| caddr_t end; |
| struct assegment *assegment; |
| int str_size = ASPATH_STR_DEFAULT_LEN; |
| int str_pnt; |
| u_char *str_buf; |
| int count = 0; |
| |
| /* Empty aspath. */ |
| if (as->length == 0) |
| { |
| str_buf = XMALLOC (MTYPE_AS_STR, 1); |
| str_buf[0] = '\0'; |
| as->count = count; |
| return str_buf; |
| } |
| |
| /* Set default value. */ |
| space = 0; |
| type = AS_SEQUENCE; |
| |
| /* Set initial pointer. */ |
| pnt = as->data; |
| end = pnt + as->length; |
| |
| str_buf = XMALLOC (MTYPE_AS_STR, str_size); |
| str_pnt = 0; |
| |
| assegment = (struct assegment *) pnt; |
| |
| while (pnt < end) |
| { |
| int i; |
| int estimate_len; |
| |
| /* For fetch value. */ |
| assegment = (struct assegment *) pnt; |
| |
| /* Check AS type validity. */ |
| if ((assegment->type != AS_SET) && |
| (assegment->type != AS_SEQUENCE) && |
| (assegment->type != AS_CONFED_SET) && |
| (assegment->type != AS_CONFED_SEQUENCE)) |
| { |
| XFREE (MTYPE_AS_STR, str_buf); |
| return NULL; |
| } |
| |
| /* Check AS length. */ |
| if ((pnt + (assegment->length * AS_VALUE_SIZE) + AS_HEADER_SIZE) > end) |
| { |
| XFREE (MTYPE_AS_STR, str_buf); |
| return NULL; |
| } |
| |
| /* Buffer length check. */ |
| estimate_len = ((assegment->length * 6) + 4); |
| |
| /* String length check. */ |
| while (str_pnt + estimate_len >= str_size) |
| { |
| str_size *= 2; |
| str_buf = XREALLOC (MTYPE_AS_STR, str_buf, str_size); |
| } |
| |
| /* If assegment type is changed, print previous type's end |
| character. */ |
| if (type != AS_SEQUENCE) |
| str_buf[str_pnt++] = aspath_delimiter_char (type, AS_SEG_END); |
| if (space) |
| str_buf[str_pnt++] = ' '; |
| |
| if (assegment->type != AS_SEQUENCE) |
| str_buf[str_pnt++] = aspath_delimiter_char (assegment->type, AS_SEG_START); |
| |
| space = 0; |
| |
| /* Increment count - ignoring CONFED SETS/SEQUENCES */ |
| if (assegment->type != AS_CONFED_SEQUENCE |
| && assegment->type != AS_CONFED_SET) |
| { |
| if (assegment->type == AS_SEQUENCE) |
| count += assegment->length; |
| else if (assegment->type == AS_SET) |
| count++; |
| } |
| |
| for (i = 0; i < assegment->length; i++) |
| { |
| int len; |
| |
| if (space) |
| { |
| if (assegment->type == AS_SET |
| || assegment->type == AS_CONFED_SET) |
| str_buf[str_pnt++] = ','; |
| else |
| str_buf[str_pnt++] = ' '; |
| } |
| else |
| space = 1; |
| |
| len = sprintf (str_buf + str_pnt, "%d", ntohs (assegment->asval[i])); |
| str_pnt += len; |
| } |
| |
| type = assegment->type; |
| pnt += (assegment->length * AS_VALUE_SIZE) + AS_HEADER_SIZE; |
| } |
| |
| if (assegment->type != AS_SEQUENCE) |
| str_buf[str_pnt++] = aspath_delimiter_char (assegment->type, AS_SEG_END); |
| |
| str_buf[str_pnt] = '\0'; |
| |
| as->count = count; |
| |
| return str_buf; |
| } |
| |
| /* Intern allocated AS path. */ |
| struct aspath * |
| aspath_intern (struct aspath *aspath) |
| { |
| struct aspath *find; |
| |
| /* Assert this AS path structure is not interned. */ |
| assert (aspath->refcnt == 0); |
| |
| /* Check AS path hash. */ |
| find = hash_get (ashash, aspath, hash_alloc_intern); |
| |
| if (find != aspath) |
| aspath_free (aspath); |
| |
| find->refcnt++; |
| |
| if (! find->str) |
| find->str = aspath_make_str_count (find); |
| |
| return find; |
| } |
| |
| /* Duplicate aspath structure. Created same aspath structure but |
| reference count and AS path string is cleared. */ |
| struct aspath * |
| aspath_dup (struct aspath *aspath) |
| { |
| struct aspath *new; |
| |
| new = XMALLOC (MTYPE_AS_PATH, sizeof (struct aspath)); |
| memset (new, 0, sizeof (struct aspath)); |
| |
| new->length = aspath->length; |
| |
| if (new->length) |
| { |
| new->data = XMALLOC (MTYPE_AS_SEG, aspath->length); |
| memcpy (new->data, aspath->data, aspath->length); |
| } |
| else |
| new->data = NULL; |
| |
| /* new->str = aspath_make_str_count (aspath); */ |
| |
| return new; |
| } |
| |
| void * |
| aspath_hash_alloc (struct aspath *arg) |
| { |
| struct aspath *aspath; |
| |
| /* New aspath strucutre is needed. */ |
| aspath = XMALLOC (MTYPE_AS_PATH, sizeof (struct aspath)); |
| memset ((void *) aspath, 0, sizeof (struct aspath)); |
| aspath->length = arg->length; |
| |
| /* In case of IBGP connection aspath's length can be zero. */ |
| if (arg->length) |
| { |
| aspath->data = XMALLOC (MTYPE_AS_SEG, arg->length); |
| memcpy (aspath->data, arg->data, arg->length); |
| } |
| else |
| aspath->data = NULL; |
| |
| /* Make AS path string. */ |
| aspath->str = aspath_make_str_count (aspath); |
| |
| /* Malformed AS path value. */ |
| if (! aspath->str) |
| { |
| aspath_free (aspath); |
| return NULL; |
| } |
| |
| return aspath; |
| } |
| |
| /* AS path parse function. pnt is a pointer to byte stream and length |
| is length of byte stream. If there is same AS path in the the AS |
| path hash then return it else make new AS path structure. */ |
| struct aspath * |
| aspath_parse (caddr_t pnt, int length) |
| { |
| struct aspath as; |
| struct aspath *find; |
| |
| /* If length is odd it's malformed AS path. */ |
| if (length % 2) |
| return NULL; |
| |
| /* Looking up aspath hash entry. */ |
| as.data = pnt; |
| as.length = length; |
| |
| /* If already same aspath exist then return it. */ |
| find = hash_get (ashash, &as, aspath_hash_alloc); |
| if (! find) |
| return NULL; |
| find->refcnt++; |
| |
| return find; |
| } |
| |
| #define min(A,B) ((A) < (B) ? (A) : (B)) |
| |
| #define ASSEGMENT_SIZE(N) (AS_HEADER_SIZE + ((N) * AS_VALUE_SIZE)) |
| |
| struct aspath * |
| aspath_aggregate_segment_copy (struct aspath *aspath, struct assegment *seg, |
| int i) |
| { |
| struct assegment *newseg; |
| |
| if (! aspath->data) |
| { |
| aspath->data = XMALLOC (MTYPE_AS_SEG, ASSEGMENT_SIZE (i)); |
| newseg = (struct assegment *) aspath->data; |
| aspath->length = ASSEGMENT_SIZE (i); |
| } |
| else |
| { |
| aspath->data = XREALLOC (MTYPE_AS_SEG, aspath->data, |
| aspath->length + ASSEGMENT_SIZE (i)); |
| newseg = (struct assegment *) (aspath->data + aspath->length); |
| aspath->length += ASSEGMENT_SIZE (i); |
| } |
| |
| newseg->type = seg->type; |
| newseg->length = i; |
| memcpy (newseg->asval, seg->asval, (i * AS_VALUE_SIZE)); |
| |
| return aspath; |
| } |
| |
| struct assegment * |
| aspath_aggregate_as_set_add (struct aspath *aspath, struct assegment *asset, |
| as_t as) |
| { |
| int i; |
| |
| /* If this is first AS set member, create new as-set segment. */ |
| if (asset == NULL) |
| { |
| if (! aspath->data) |
| { |
| aspath->data = XMALLOC (MTYPE_AS_SEG, ASSEGMENT_SIZE (1)); |
| asset = (struct assegment *) aspath->data; |
| aspath->length = ASSEGMENT_SIZE (1); |
| } |
| else |
| { |
| aspath->data = XREALLOC (MTYPE_AS_SEG, aspath->data, |
| aspath->length + ASSEGMENT_SIZE (1)); |
| asset = (struct assegment *) (aspath->data + aspath->length); |
| aspath->length += ASSEGMENT_SIZE (1); |
| } |
| asset->type = AS_SET; |
| asset->length = 1; |
| asset->asval[0] = as; |
| } |
| else |
| { |
| size_t offset; |
| |
| /* Check this AS value already exists or not. */ |
| for (i = 0; i < asset->length; i++) |
| if (asset->asval[i] == as) |
| return asset; |
| |
| offset = (caddr_t) asset - (caddr_t) aspath->data; |
| aspath->data = XREALLOC (MTYPE_AS_SEG, aspath->data, |
| aspath->length + AS_VALUE_SIZE); |
| |
| asset = (struct assegment *) (aspath->data + offset); |
| aspath->length += AS_VALUE_SIZE; |
| asset->asval[asset->length] = as; |
| asset->length++; |
| } |
| |
| return asset; |
| } |
| |
| /* Modify as1 using as2 for aggregation. */ |
| struct aspath * |
| aspath_aggregate (struct aspath *as1, struct aspath *as2) |
| { |
| int i; |
| int minlen; |
| int match; |
| int match1; |
| int match2; |
| caddr_t cp1; |
| caddr_t cp2; |
| caddr_t end1; |
| caddr_t end2; |
| struct assegment *seg1; |
| struct assegment *seg2; |
| struct aspath *aspath; |
| struct assegment *asset; |
| |
| match = 0; |
| minlen = 0; |
| aspath = NULL; |
| asset = NULL; |
| cp1 = as1->data; |
| end1 = as1->data + as1->length; |
| cp2 = as2->data; |
| end2 = as2->data + as2->length; |
| |
| seg1 = (struct assegment *) cp1; |
| seg2 = (struct assegment *) cp2; |
| |
| /* First of all check common leading sequence. */ |
| while ((cp1 < end1) && (cp2 < end2)) |
| { |
| /* Check segment type. */ |
| if (seg1->type != seg2->type) |
| break; |
| |
| /* Minimum segment length. */ |
| minlen = min (seg1->length, seg2->length); |
| |
| for (match = 0; match < minlen; match++) |
| if (seg1->asval[match] != seg2->asval[match]) |
| break; |
| |
| if (match) |
| { |
| if (! aspath) |
| aspath = aspath_new(); |
| aspath = aspath_aggregate_segment_copy (aspath, seg1, match); |
| } |
| |
| if (match != minlen || match != seg1->length |
| || seg1->length != seg2->length) |
| break; |
| |
| cp1 += ((seg1->length * AS_VALUE_SIZE) + AS_HEADER_SIZE); |
| cp2 += ((seg2->length * AS_VALUE_SIZE) + AS_HEADER_SIZE); |
| |
| seg1 = (struct assegment *) cp1; |
| seg2 = (struct assegment *) cp2; |
| } |
| |
| if (! aspath) |
| aspath = aspath_new(); |
| |
| /* Make as-set using rest of all information. */ |
| match1 = match; |
| while (cp1 < end1) |
| { |
| seg1 = (struct assegment *) cp1; |
| |
| for (i = match1; i < seg1->length; i++) |
| asset = aspath_aggregate_as_set_add (aspath, asset, seg1->asval[i]); |
| |
| match1 = 0; |
| cp1 += ((seg1->length * AS_VALUE_SIZE) + AS_HEADER_SIZE); |
| } |
| |
| match2 = match; |
| while (cp2 < end2) |
| { |
| seg2 = (struct assegment *) cp2; |
| |
| for (i = match2; i < seg2->length; i++) |
| asset = aspath_aggregate_as_set_add (aspath, asset, seg2->asval[i]); |
| |
| match2 = 0; |
| cp2 += ((seg2->length * AS_VALUE_SIZE) + AS_HEADER_SIZE); |
| } |
| |
| return aspath; |
| } |
| |
| /* When a BGP router receives an UPDATE with an MP_REACH_NLRI |
| attribute, check the leftmost AS number in the AS_PATH attribute is |
| or not the peer's AS number. */ |
| int |
| aspath_firstas_check (struct aspath *aspath, as_t asno) |
| { |
| caddr_t pnt; |
| struct assegment *assegment; |
| |
| if (aspath == NULL) |
| return 0; |
| |
| pnt = aspath->data; |
| assegment = (struct assegment *) pnt; |
| |
| if (assegment |
| && assegment->type == AS_SEQUENCE |
| && assegment->asval[0] == htons (asno)) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* AS path loop check. If aspath contains asno then return 1. */ |
| int |
| aspath_loop_check (struct aspath *aspath, as_t asno) |
| { |
| caddr_t pnt; |
| caddr_t end; |
| struct assegment *assegment; |
| int count = 0; |
| |
| if (aspath == NULL) |
| return 0; |
| |
| pnt = aspath->data; |
| end = aspath->data + aspath->length; |
| |
| while (pnt < end) |
| { |
| int i; |
| assegment = (struct assegment *) pnt; |
| |
| for (i = 0; i < assegment->length; i++) |
| if (assegment->asval[i] == htons (asno)) |
| count++; |
| |
| pnt += (assegment->length * AS_VALUE_SIZE) + AS_HEADER_SIZE; |
| } |
| return count; |
| } |
| |
| /* When all of AS path is private AS return 1. */ |
| int |
| aspath_private_as_check (struct aspath *aspath) |
| { |
| caddr_t pnt; |
| caddr_t end; |
| struct assegment *assegment; |
| |
| if (aspath == NULL) |
| return 0; |
| |
| if (aspath->length == 0) |
| return 0; |
| |
| pnt = aspath->data; |
| end = aspath->data + aspath->length; |
| |
| while (pnt < end) |
| { |
| int i; |
| assegment = (struct assegment *) pnt; |
| |
| for (i = 0; i < assegment->length; i++) |
| { |
| if (ntohs (assegment->asval[i]) < BGP_PRIVATE_AS_MIN |
| || ntohs (assegment->asval[i]) > BGP_PRIVATE_AS_MAX) |
| return 0; |
| } |
| pnt += (assegment->length * AS_VALUE_SIZE) + AS_HEADER_SIZE; |
| } |
| return 1; |
| } |
| |
| /* Merge as1 to as2. as2 should be uninterned aspath. */ |
| struct aspath * |
| aspath_merge (struct aspath *as1, struct aspath *as2) |
| { |
| caddr_t data; |
| |
| if (! as1 || ! as2) |
| return NULL; |
| |
| data = XMALLOC (MTYPE_AS_SEG, as1->length + as2->length); |
| memcpy (data, as1->data, as1->length); |
| memcpy (data + as1->length, as2->data, as2->length); |
| |
| XFREE (MTYPE_AS_SEG, as2->data); |
| as2->data = data; |
| as2->length += as1->length; |
| as2->count += as1->count; |
| return as2; |
| } |
| |
| /* Prepend as1 to as2. as2 should be uninterned aspath. */ |
| struct aspath * |
| aspath_prepend (struct aspath *as1, struct aspath *as2) |
| { |
| caddr_t pnt; |
| caddr_t end; |
| struct assegment *seg1 = NULL; |
| struct assegment *seg2 = NULL; |
| |
| if (! as1 || ! as2) |
| return NULL; |
| |
| seg2 = (struct assegment *) as2->data; |
| |
| /* In case of as2 is empty AS. */ |
| if (seg2 == NULL) |
| { |
| as2->length = as1->length; |
| as2->data = XMALLOC (MTYPE_AS_SEG, as1->length); |
| as2->count = as1->count; |
| memcpy (as2->data, as1->data, as1->length); |
| return as2; |
| } |
| |
| /* assegment points last segment of as1. */ |
| pnt = as1->data; |
| end = as1->data + as1->length; |
| while (pnt < end) |
| { |
| seg1 = (struct assegment *) pnt; |
| pnt += (seg1->length * AS_VALUE_SIZE) + AS_HEADER_SIZE; |
| } |
| |
| /* In case of as1 is empty AS. */ |
| if (seg1 == NULL) |
| return as2; |
| |
| /* Compare last segment type of as1 and first segment type of as2. */ |
| if (seg1->type != seg2->type) |
| return aspath_merge (as1, as2); |
| |
| if (seg1->type == AS_SEQUENCE) |
| { |
| caddr_t newdata; |
| struct assegment *seg = NULL; |
| |
| newdata = XMALLOC (MTYPE_AS_SEG, |
| as1->length + as2->length - AS_HEADER_SIZE); |
| memcpy (newdata, as1->data, as1->length); |
| seg = (struct assegment *) (newdata + ((caddr_t)seg1 - as1->data)); |
| seg->length += seg2->length; |
| memcpy (newdata + as1->length, as2->data + AS_HEADER_SIZE, |
| as2->length - AS_HEADER_SIZE); |
| |
| XFREE (MTYPE_AS_SEG, as2->data); |
| as2->data = newdata; |
| as2->length += (as1->length - AS_HEADER_SIZE); |
| as2->count += as1->count; |
| |
| return as2; |
| } |
| else |
| { |
| /* AS_SET merge code is needed at here. */ |
| return aspath_merge (as1, as2); |
| } |
| |
| /* Not reached */ |
| } |
| |
| /* Add specified AS to the leftmost of aspath. */ |
| static struct aspath * |
| aspath_add_one_as (struct aspath *aspath, as_t asno, u_char type) |
| { |
| struct assegment *assegment; |
| |
| assegment = (struct assegment *) aspath->data; |
| |
| /* In case of empty aspath. */ |
| if (assegment == NULL || assegment->length == 0) |
| { |
| aspath->length = AS_HEADER_SIZE + AS_VALUE_SIZE; |
| |
| if (assegment) |
| aspath->data = XREALLOC (MTYPE_AS_SEG, aspath->data, aspath->length); |
| else |
| aspath->data = XMALLOC (MTYPE_AS_SEG, aspath->length); |
| |
| assegment = (struct assegment *) aspath->data; |
| assegment->type = type; |
| assegment->length = 1; |
| assegment->asval[0] = htons (asno); |
| |
| return aspath; |
| } |
| |
| if (assegment->type == type) |
| { |
| caddr_t newdata; |
| struct assegment *newsegment; |
| |
| newdata = XMALLOC (MTYPE_AS_SEG, aspath->length + AS_VALUE_SIZE); |
| newsegment = (struct assegment *) newdata; |
| |
| newsegment->type = type; |
| newsegment->length = assegment->length + 1; |
| newsegment->asval[0] = htons (asno); |
| |
| memcpy (newdata + AS_HEADER_SIZE + AS_VALUE_SIZE, |
| aspath->data + AS_HEADER_SIZE, |
| aspath->length - AS_HEADER_SIZE); |
| |
| XFREE (MTYPE_AS_SEG, aspath->data); |
| |
| aspath->data = newdata; |
| aspath->length += AS_VALUE_SIZE; |
| } else { |
| caddr_t newdata; |
| struct assegment *newsegment; |
| |
| newdata = XMALLOC (MTYPE_AS_SEG, aspath->length + AS_VALUE_SIZE + AS_HEADER_SIZE); |
| newsegment = (struct assegment *) newdata; |
| |
| newsegment->type = type; |
| newsegment->length = 1; |
| newsegment->asval[0] = htons (asno); |
| |
| memcpy (newdata + AS_HEADER_SIZE + AS_VALUE_SIZE, |
| aspath->data, |
| aspath->length); |
| |
| XFREE (MTYPE_AS_SEG, aspath->data); |
| |
| aspath->data = newdata; |
| aspath->length += AS_HEADER_SIZE + AS_VALUE_SIZE; |
| } |
| |
| return aspath; |
| } |
| |
| /* Add specified AS to the leftmost of aspath. */ |
| struct aspath * |
| aspath_add_seq (struct aspath *aspath, as_t asno) |
| { |
| return aspath_add_one_as (aspath, asno, AS_SEQUENCE); |
| } |
| |
| /* Compare leftmost AS value for MED check. If as1's leftmost AS and |
| as2's leftmost AS is same return 1. */ |
| int |
| aspath_cmp_left (struct aspath *aspath1, struct aspath *aspath2) |
| { |
| struct assegment *seg1; |
| struct assegment *seg2; |
| as_t as1; |
| as_t as2; |
| |
| seg1 = (struct assegment *) aspath1->data; |
| seg2 = (struct assegment *) aspath2->data; |
| |
| while (seg1 && seg1->length |
| && (seg1->type == AS_CONFED_SEQUENCE || seg1->type == AS_CONFED_SET)) |
| seg1 = (struct assegment *) ((caddr_t) seg1 + ASSEGMENT_LEN (seg1)); |
| while (seg2 && seg2->length |
| && (seg2->type == AS_CONFED_SEQUENCE || seg2->type == AS_CONFED_SET)) |
| seg2 = (struct assegment *) ((caddr_t) seg2 + ASSEGMENT_LEN (seg2)); |
| |
| /* Check as1's */ |
| if (seg1 == NULL || seg1->length == 0 || seg1->type != AS_SEQUENCE) |
| return 0; |
| as1 = seg1->asval[0]; |
| |
| if (seg2 == NULL || seg2->length == 0 || seg2->type != AS_SEQUENCE) |
| return 0; |
| as2 = seg2->asval[0]; |
| |
| if (as1 == as2) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* Compare leftmost AS value for MED check. If as1's leftmost AS and |
| as2's leftmost AS is same return 1. (confederation as-path |
| only). */ |
| int |
| aspath_cmp_left_confed (struct aspath *aspath1, struct aspath *aspath2) |
| { |
| struct assegment *seg1; |
| struct assegment *seg2; |
| |
| as_t as1; |
| as_t as2; |
| |
| if (aspath1->count || aspath2->count) |
| return 0; |
| |
| seg1 = (struct assegment *) aspath1->data; |
| seg2 = (struct assegment *) aspath2->data; |
| |
| /* Check as1's */ |
| if (seg1 == NULL || seg1->length == 0 || seg1->type != AS_CONFED_SEQUENCE) |
| return 0; |
| as1 = seg1->asval[0]; |
| |
| /* Check as2's */ |
| if (seg2 == NULL || seg2->length == 0 || seg2->type != AS_CONFED_SEQUENCE) |
| return 0; |
| as2 = seg2->asval[0]; |
| |
| if (as1 == as2) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* Delete first sequential AS_CONFED_SEQUENCE from aspath. */ |
| struct aspath * |
| aspath_delete_confed_seq (struct aspath *aspath) |
| { |
| int seglen; |
| struct assegment *assegment; |
| |
| if (! aspath) |
| return aspath; |
| |
| assegment = (struct assegment *) aspath->data; |
| |
| while (assegment) |
| { |
| if (assegment->type != AS_CONFED_SEQUENCE) |
| return aspath; |
| |
| seglen = ASSEGMENT_LEN (assegment); |
| |
| if (seglen == aspath->length) |
| { |
| XFREE (MTYPE_AS_SEG, aspath->data); |
| aspath->data = NULL; |
| aspath->length = 0; |
| } |
| else |
| { |
| memcpy (aspath->data, aspath->data + seglen, |
| aspath->length - seglen); |
| aspath->data = XREALLOC (MTYPE_AS_SEG, aspath->data, |
| aspath->length - seglen); |
| aspath->length -= seglen; |
| } |
| |
| assegment = (struct assegment *) aspath->data; |
| } |
| return aspath; |
| } |
| |
| /* Add new AS number to the leftmost part of the aspath as |
| AS_CONFED_SEQUENCE. */ |
| struct aspath* |
| aspath_add_confed_seq (struct aspath *aspath, as_t asno) |
| { |
| return aspath_add_one_as (aspath, asno, AS_CONFED_SEQUENCE); |
| } |
| |
| /* Add new as value to as path structure. */ |
| void |
| aspath_as_add (struct aspath *as, as_t asno) |
| { |
| caddr_t pnt; |
| caddr_t end; |
| struct assegment *assegment; |
| |
| /* Increase as->data for new as value. */ |
| as->data = XREALLOC (MTYPE_AS_SEG, as->data, as->length + 2); |
| as->length += 2; |
| |
| pnt = as->data; |
| end = as->data + as->length; |
| assegment = (struct assegment *) pnt; |
| |
| /* Last segment search procedure. */ |
| while (pnt + 2 < end) |
| { |
| assegment = (struct assegment *) pnt; |
| |
| /* We add 2 for segment_type and segment_length and segment |
| value assegment->length * 2. */ |
| pnt += (AS_HEADER_SIZE + (assegment->length * AS_VALUE_SIZE)); |
| } |
| |
| assegment->asval[assegment->length] = htons (asno); |
| assegment->length++; |
| } |
| |
| /* Add new as segment to the as path. */ |
| void |
| aspath_segment_add (struct aspath *as, int type) |
| { |
| struct assegment *assegment; |
| |
| if (as->data == NULL) |
| { |
| as->data = XMALLOC (MTYPE_AS_SEG, 2); |
| assegment = (struct assegment *) as->data; |
| as->length = 2; |
| } |
| else |
| { |
| as->data = XREALLOC (MTYPE_AS_SEG, as->data, as->length + 2); |
| assegment = (struct assegment *) (as->data + as->length); |
| as->length += 2; |
| } |
| |
| assegment->type = type; |
| assegment->length = 0; |
| } |
| |
| struct aspath * |
| aspath_empty () |
| { |
| return aspath_parse (NULL, 0); |
| } |
| |
| struct aspath * |
| aspath_empty_get () |
| { |
| struct aspath *aspath; |
| |
| aspath = aspath_new (); |
| aspath->str = aspath_make_str_count (aspath); |
| return aspath; |
| } |
| |
| unsigned long |
| aspath_count () |
| { |
| return ashash->count; |
| } |
| |
| /* |
| Theoretically, one as path can have: |
| |
| One BGP packet size should be less than 4096. |
| One BGP attribute size should be less than 4096 - BGP header size. |
| One BGP aspath size should be less than 4096 - BGP header size - |
| BGP mandantry attribute size. |
| */ |
| |
| /* AS path string lexical token enum. */ |
| enum as_token |
| { |
| as_token_asval, |
| as_token_set_start, |
| as_token_set_end, |
| as_token_confed_start, |
| as_token_confed_end, |
| as_token_unknown |
| }; |
| |
| /* Return next token and point for string parse. */ |
| char * |
| aspath_gettoken (char *buf, enum as_token *token, u_short *asno) |
| { |
| char *p = buf; |
| |
| /* Skip space. */ |
| while (isspace ((int) *p)) |
| p++; |
| |
| /* Check the end of the string and type specify characters |
| (e.g. {}()). */ |
| switch (*p) |
| { |
| case '\0': |
| return NULL; |
| break; |
| case '{': |
| *token = as_token_set_start; |
| p++; |
| return p; |
| break; |
| case '}': |
| *token = as_token_set_end; |
| p++; |
| return p; |
| break; |
| case '(': |
| *token = as_token_confed_start; |
| p++; |
| return p; |
| break; |
| case ')': |
| *token = as_token_confed_end; |
| p++; |
| return p; |
| break; |
| } |
| |
| /* Check actual AS value. */ |
| if (isdigit ((int) *p)) |
| { |
| u_short asval; |
| |
| *token = as_token_asval; |
| asval = (*p - '0'); |
| p++; |
| while (isdigit ((int) *p)) |
| { |
| asval *= 10; |
| asval += (*p - '0'); |
| p++; |
| } |
| *asno = asval; |
| return p; |
| } |
| |
| /* There is no match then return unknown token. */ |
| *token = as_token_unknown; |
| return p++; |
| } |
| |
| struct aspath * |
| aspath_str2aspath (char *str) |
| { |
| enum as_token token; |
| u_short as_type; |
| u_short asno; |
| struct aspath *aspath; |
| int needtype; |
| |
| aspath = aspath_new (); |
| |
| /* We start default type as AS_SEQUENCE. */ |
| as_type = AS_SEQUENCE; |
| needtype = 1; |
| |
| while ((str = aspath_gettoken (str, &token, &asno)) != NULL) |
| { |
| switch (token) |
| { |
| case as_token_asval: |
| if (needtype) |
| { |
| aspath_segment_add (aspath, as_type); |
| needtype = 0; |
| } |
| aspath_as_add (aspath, asno); |
| break; |
| case as_token_set_start: |
| as_type = AS_SET; |
| aspath_segment_add (aspath, as_type); |
| needtype = 0; |
| break; |
| case as_token_set_end: |
| as_type = AS_SEQUENCE; |
| needtype = 1; |
| break; |
| case as_token_confed_start: |
| as_type = AS_CONFED_SEQUENCE; |
| aspath_segment_add (aspath, as_type); |
| needtype = 0; |
| break; |
| case as_token_confed_end: |
| as_type = AS_SEQUENCE; |
| needtype = 1; |
| break; |
| case as_token_unknown: |
| default: |
| return NULL; |
| break; |
| } |
| } |
| |
| aspath->str = aspath_make_str_count (aspath); |
| |
| return aspath; |
| } |
| |
| /* Make hash value by raw aspath data. */ |
| unsigned int |
| aspath_key_make (struct aspath *aspath) |
| { |
| unsigned int key = 0; |
| int length; |
| unsigned short *pnt; |
| |
| length = aspath->length / 2; |
| pnt = (unsigned short *) aspath->data; |
| |
| while (length) |
| { |
| key += *pnt++; |
| length--; |
| } |
| |
| return key; |
| } |
| |
| /* If two aspath have same value then return 1 else return 0 */ |
| int |
| aspath_cmp (struct aspath *as1, struct aspath *as2) |
| { |
| if (as1->length == as2->length |
| && !memcmp (as1->data, as2->data, as1->length)) |
| return 1; |
| else |
| return 0; |
| } |
| |
| /* AS path hash initialize. */ |
| void |
| aspath_init () |
| { |
| ashash = hash_create_size (32767, aspath_key_make, aspath_cmp); |
| } |
| |
| /* return and as path value */ |
| const char * |
| aspath_print (struct aspath *as) |
| { |
| return as->str; |
| } |
| |
| /* Printing functions */ |
| void |
| aspath_print_vty (struct vty *vty, struct aspath *as) |
| { |
| vty_out (vty, "%s", as->str); |
| } |
| |
| void |
| aspath_show_all_iterator (struct hash_backet *backet, struct vty *vty) |
| { |
| struct aspath *as; |
| |
| as = (struct aspath *) backet->data; |
| |
| vty_out (vty, "[%p:%u] (%ld) ", backet, backet->key, as->refcnt); |
| vty_out (vty, "%s%s", as->str, VTY_NEWLINE); |
| } |
| |
| /* Print all aspath and hash information. This function is used from |
| `show ip bgp paths' command. */ |
| void |
| aspath_print_all_vty (struct vty *vty) |
| { |
| hash_iterate (ashash, |
| (void (*) (struct hash_backet *, void *)) |
| aspath_show_all_iterator, |
| vty); |
| } |