123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905 |
- /* AS path management routines.
- Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro
- Copyright (C) 2005 Sun Microsystems, Inc.
- 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 "stream.h"
- #include "jhash.h"
- #include "bgpd/bgpd.h"
- #include "bgpd/bgp_aspath.h"
- #include "bgpd/bgp_debug.h"
- #include "bgpd/bgp_attr.h"
- /* Attr. Flags and Attr. Type Code. */
- #define AS_HEADER_SIZE 2
- /* Now FOUR octets are used for AS value. */
- #define AS_VALUE_SIZE sizeof (as_t)
- /* This is the old one */
- #define AS16_VALUE_SIZE sizeof (as16_t)
- /* Maximum protocol segment length value */
- #define AS_SEGMENT_MAX 255
- /* The following length and size macros relate specifically to Quagga's
- * internal representation of AS-Segments, not per se to the on-wire
- * sizes and lengths. At present (200508) they sort of match, however
- * the ONLY functions which should now about the on-wire syntax are
- * aspath_put, assegment_put and assegment_parse.
- *
- * aspath_put returns bytes written, the only definitive record of
- * size of wire-format attribute..
- */
- /* Calculated size in bytes of ASN segment data to hold N ASN's */
- #define ASSEGMENT_DATA_SIZE(N,S) \
- ((N) * ( (S) ? AS_VALUE_SIZE : AS16_VALUE_SIZE) )
- /* Calculated size of segment struct to hold N ASN's */
- #define ASSEGMENT_SIZE(N,S) (AS_HEADER_SIZE + ASSEGMENT_DATA_SIZE (N,S))
- /* AS segment octet length. */
- #define ASSEGMENT_LEN(X,S) ASSEGMENT_SIZE((X)->length,S)
- /* AS_SEQUENCE segments can be packed together */
- /* Can the types of X and Y be considered for packing? */
- #define ASSEGMENT_TYPES_PACKABLE(X,Y) \
- ( ((X)->type == (Y)->type) \
- && ((X)->type == AS_SEQUENCE))
- /* Types and length of X,Y suitable for packing? */
- #define ASSEGMENTS_PACKABLE(X,Y) \
- ( ASSEGMENT_TYPES_PACKABLE( (X), (Y)) \
- && ( ((X)->length + (Y)->length) <= AS_SEGMENT_MAX ) )
- /* As segment header - the on-wire representation
- * NOT the internal representation!
- */
- struct assegment_header
- {
- u_char type;
- u_char length;
- };
- /* Hash for aspath. This is the top level structure of AS path. */
- static struct hash *ashash;
- /* Stream for SNMP. See aspath_snmp_pathseg */
- static struct stream *snmp_stream;
- static inline as_t *
- assegment_data_new (int num)
- {
- return (XCALLOC (MTYPE_AS_SEG_DATA, ASSEGMENT_DATA_SIZE (num, 1)));
- }
- static inline void
- assegment_data_free (as_t *asdata)
- {
- XFREE (MTYPE_AS_SEG_DATA,asdata);
- }
- /* Get a new segment. Note that 0 is an allowed length,
- * and will result in a segment with no allocated data segment.
- * the caller should immediately assign data to the segment, as the segment
- * otherwise is not generally valid
- */
- static struct assegment *
- assegment_new (u_char type, u_short length)
- {
- struct assegment *new;
-
- new = XCALLOC (MTYPE_AS_SEG, sizeof (struct assegment));
-
- if (length)
- new->as = assegment_data_new (length);
-
- new->length = length;
- new->type = type;
-
- return new;
- }
- static void
- assegment_free (struct assegment *seg)
- {
- if (!seg)
- return;
-
- if (seg->as)
- XFREE (MTYPE_AS_SEG_DATA, seg->as);
- memset (seg, 0xfe, sizeof(struct assegment));
- XFREE (MTYPE_AS_SEG, seg);
-
- return;
- }
- /* free entire chain of segments */
- static void
- assegment_free_all (struct assegment *seg)
- {
- struct assegment *prev;
-
- while (seg)
- {
- prev = seg;
- seg = seg->next;
- assegment_free (prev);
- }
- }
- /* Duplicate just the given assegment and its data */
- static struct assegment *
- assegment_dup (struct assegment *seg)
- {
- struct assegment *new;
-
- new = assegment_new (seg->type, seg->length);
- memcpy (new->as, seg->as, ASSEGMENT_DATA_SIZE (new->length, 1) );
-
- return new;
- }
- /* Duplicate entire chain of assegments, return the head */
- static struct assegment *
- assegment_dup_all (struct assegment *seg)
- {
- struct assegment *new = NULL;
- struct assegment *head = NULL;
-
- while (seg)
- {
- if (head)
- {
- new->next = assegment_dup (seg);
- new = new->next;
- }
- else
- head = new = assegment_dup (seg);
-
- seg = seg->next;
- }
- return head;
- }
- /* prepend the as number to given segment, given num of times */
- static struct assegment *
- assegment_prepend_asns (struct assegment *seg, as_t asnum, int num)
- {
- as_t *newas;
-
- if (!num)
- return seg;
-
- if (num >= AS_SEGMENT_MAX)
- return seg; /* we don't do huge prepends */
-
- newas = assegment_data_new (seg->length + num);
-
- if (newas)
- {
- int i;
- for (i = 0; i < num; i++)
- newas[i] = asnum;
-
- memcpy (newas + num, seg->as, ASSEGMENT_DATA_SIZE (seg->length, 1));
- XFREE (MTYPE_AS_SEG_DATA, seg->as);
- seg->as = newas;
- seg->length += num;
- return seg;
- }
- assegment_free_all (seg);
- return NULL;
- }
- /* append given array of as numbers to the segment */
- static struct assegment *
- assegment_append_asns (struct assegment *seg, as_t *asnos, int num)
- {
- as_t *newas;
-
- newas = XREALLOC (MTYPE_AS_SEG_DATA, seg->as,
- ASSEGMENT_DATA_SIZE (seg->length + num, 1));
- if (newas)
- {
- seg->as = newas;
- memcpy (seg->as + seg->length, asnos, ASSEGMENT_DATA_SIZE(num, 1));
- seg->length += num;
- return seg;
- }
- assegment_free_all (seg);
- return NULL;
- }
- static int
- int_cmp (const void *p1, const void *p2)
- {
- const as_t *as1 = p1;
- const as_t *as2 = p2;
-
- return (*as1 == *as2)
- ? 0 : ( (*as1 > *as2) ? 1 : -1);
- }
- /* normalise the segment.
- * In particular, merge runs of AS_SEQUENCEs into one segment
- * Internally, we do not care about the wire segment length limit, and
- * we want each distinct AS_PATHs to have the exact same internal
- * representation - eg, so that our hashing actually works..
- */
- static struct assegment *
- assegment_normalise (struct assegment *head)
- {
- struct assegment *seg = head, *pin;
- struct assegment *tmp;
-
- if (!head)
- return head;
-
- while (seg)
- {
- pin = seg;
-
- /* Sort values SET segments, for determinism in paths to aid
- * creation of hash values / path comparisons
- * and because it helps other lesser implementations ;)
- */
- if (seg->type == AS_SET || seg->type == AS_CONFED_SET)
- {
- int tail = 0;
- int i;
-
- qsort (seg->as, seg->length, sizeof(as_t), int_cmp);
-
- /* weed out dupes */
- for (i=1; i < seg->length; i++)
- {
- if (seg->as[tail] == seg->as[i])
- continue;
-
- tail++;
- if (tail < i)
- seg->as[tail] = seg->as[i];
- }
- /* seg->length can be 0.. */
- if (seg->length)
- seg->length = tail + 1;
- }
- /* read ahead from the current, pinned segment while the segments
- * are packable/mergeable. Append all following packable segments
- * to the segment we have pinned and remove these appended
- * segments.
- */
- while (pin->next && ASSEGMENT_TYPES_PACKABLE(pin, pin->next))
- {
- tmp = pin->next;
- seg = pin->next;
-
- /* append the next sequence to the pinned sequence */
- pin = assegment_append_asns (pin, seg->as, seg->length);
-
- /* bypass the next sequence */
- pin->next = seg->next;
-
- /* get rid of the now referenceless segment */
- assegment_free (tmp);
-
- }
- seg = pin->next;
- }
- return head;
- }
- static struct aspath *
- aspath_new (void)
- {
- return XCALLOC (MTYPE_AS_PATH, sizeof (struct aspath));
- }
- /* Free AS path structure. */
- void
- aspath_free (struct aspath *aspath)
- {
- if (!aspath)
- return;
- if (aspath->segments)
- assegment_free_all (aspath->segments);
- 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_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 ' ';
- }
- /* countup asns from this segment and index onward */
- static int
- assegment_count_asns (struct assegment *seg, int from)
- {
- int count = 0;
- while (seg)
- {
- if (!from)
- count += seg->length;
- else
- {
- count += (seg->length - from);
- from = 0;
- }
- seg = seg->next;
- }
- return count;
- }
- unsigned int
- aspath_count_confeds (struct aspath *aspath)
- {
- int count = 0;
- struct assegment *seg = aspath->segments;
-
- while (seg)
- {
- if (seg->type == AS_CONFED_SEQUENCE)
- count += seg->length;
- else if (seg->type == AS_CONFED_SET)
- count++;
-
- seg = seg->next;
- }
- return count;
- }
- unsigned int
- aspath_count_hops (struct aspath *aspath)
- {
- int count = 0;
- struct assegment *seg = aspath->segments;
-
- while (seg)
- {
- if (seg->type == AS_SEQUENCE)
- count += seg->length;
- else if (seg->type == AS_SET)
- count++;
-
- seg = seg->next;
- }
- return count;
- }
- /* Estimate size aspath /might/ take if encoded into an
- * ASPATH attribute.
- *
- * This is a quick estimate, not definitive! aspath_put()
- * may return a different number!!
- */
- unsigned int
- aspath_size (struct aspath *aspath)
- {
- int size = 0;
- struct assegment *seg = aspath->segments;
-
- while (seg)
- {
- size += ASSEGMENT_SIZE(seg->length, 1);
- seg = seg->next;
- }
- return size;
- }
- /* Return highest public ASN in path */
- as_t
- aspath_highest (struct aspath *aspath)
- {
- struct assegment *seg = aspath->segments;
- as_t highest = 0;
- unsigned int i;
-
- while (seg)
- {
- for (i = 0; i < seg->length; i++)
- if (seg->as[i] > highest
- && (seg->as[i] < BGP_PRIVATE_AS_MIN
- || seg->as[i] > BGP_PRIVATE_AS_MAX))
- highest = seg->as[i];
- seg = seg->next;
- }
- return highest;
- }
- /* Return 1 if there are any 4-byte ASes in the path */
- unsigned int
- aspath_has_as4 (struct aspath *aspath)
- {
- struct assegment *seg = aspath->segments;
- unsigned int i;
-
- while (seg)
- {
- for (i = 0; i < seg->length; i++)
- if (seg->as[i] > BGP_AS_MAX)
- return 1;
- seg = seg->next;
- }
- return 0;
- }
- /* Convert aspath structure to string expression. */
- static char *
- aspath_make_str_count (struct aspath *as)
- {
- struct assegment *seg;
- int str_size;
- int len = 0;
- char *str_buf;
- /* Empty aspath. */
- if (!as->segments)
- {
- str_buf = XMALLOC (MTYPE_AS_STR, 1);
- str_buf[0] = '\0';
- return str_buf;
- }
-
- seg = as->segments;
-
- /* ASN takes 5 to 10 chars plus seperator, see below.
- * If there is one differing segment type, we need an additional
- * 2 chars for segment delimiters, and the final '\0'.
- * Hopefully this is large enough to avoid hitting the realloc
- * code below for most common sequences.
- *
- * This was changed to 10 after the well-known BGP assertion, which
- * had hit some parts of the Internet in May of 2009.
- */
- #define ASN_STR_LEN (10 + 1)
- str_size = MAX (assegment_count_asns (seg, 0) * ASN_STR_LEN + 2 + 1,
- ASPATH_STR_DEFAULT_LEN);
- str_buf = XMALLOC (MTYPE_AS_STR, str_size);
- while (seg)
- {
- int i;
- char seperator;
-
- /* Check AS type validity. Set seperator for segment */
- switch (seg->type)
- {
- case AS_SET:
- case AS_CONFED_SET:
- seperator = ',';
- break;
- case AS_SEQUENCE:
- case AS_CONFED_SEQUENCE:
- seperator = ' ';
- break;
- default:
- XFREE (MTYPE_AS_STR, str_buf);
- return NULL;
- }
-
- /* We might need to increase str_buf, particularly if path has
- * differing segments types, our initial guesstimate above will
- * have been wrong. Need 10 chars for ASN, a seperator each and
- * potentially two segment delimiters, plus a space between each
- * segment and trailing zero.
- *
- * This definitely didn't work with the value of 5 bytes and
- * 32-bit ASNs.
- */
- #define SEGMENT_STR_LEN(X) (((X)->length * ASN_STR_LEN) + 2 + 1 + 1)
- if ( (len + SEGMENT_STR_LEN(seg)) > str_size)
- {
- str_size = len + SEGMENT_STR_LEN(seg);
- str_buf = XREALLOC (MTYPE_AS_STR, str_buf, str_size);
- }
- #undef ASN_STR_LEN
- #undef SEGMENT_STR_LEN
-
- if (seg->type != AS_SEQUENCE)
- len += snprintf (str_buf + len, str_size - len,
- "%c",
- aspath_delimiter_char (seg->type, AS_SEG_START));
-
- /* write out the ASNs, with their seperators, bar the last one*/
- for (i = 0; i < seg->length; i++)
- {
- len += snprintf (str_buf + len, str_size - len, "%u", seg->as[i]);
-
- if (i < (seg->length - 1))
- len += snprintf (str_buf + len, str_size - len, "%c", seperator);
- }
-
- if (seg->type != AS_SEQUENCE)
- len += snprintf (str_buf + len, str_size - len, "%c",
- aspath_delimiter_char (seg->type, AS_SEG_END));
- if (seg->next)
- len += snprintf (str_buf + len, str_size - len, " ");
-
- seg = seg->next;
- }
-
- assert (len < str_size);
-
- str_buf[len] = '\0';
- return str_buf;
- }
- static void
- aspath_str_update (struct aspath *as)
- {
- if (as->str)
- XFREE (MTYPE_AS_STR, as->str);
- as->str = aspath_make_str_count (as);
- }
- /* 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 = XCALLOC (MTYPE_AS_PATH, sizeof (struct aspath));
- if (aspath->segments)
- new->segments = assegment_dup_all (aspath->segments);
- else
- new->segments = NULL;
- new->str = aspath_make_str_count (aspath);
- return new;
- }
- static void *
- aspath_hash_alloc (void *arg)
- {
- struct aspath *aspath;
- /* New aspath structure is needed. */
- aspath = aspath_dup (arg);
-
- /* Malformed AS path value. */
- if (! aspath->str)
- {
- aspath_free (aspath);
- return NULL;
- }
- return aspath;
- }
- /* parse as-segment byte stream in struct assegment
- *
- * Returns NULL if the AS_PATH or AS4_PATH is not valid.
- */
- static struct assegment *
- assegments_parse (struct stream *s, size_t length, int use32bit, int as4_path)
- {
- struct assegment_header segh;
- struct assegment *seg, *prev = NULL, *head = NULL;
-
- assert (length > 0); /* does not expect empty AS_PATH or AS4_PATH */
-
- if (BGP_DEBUG (as4, AS4_SEGMENT))
- zlog_debug ("[AS4SEG] Parse aspath segment: got total byte length %lu",
- (unsigned long) length);
- /* double check that length does not exceed stream */
- if (STREAM_READABLE(s) < length)
- return NULL;
-
- /* deal with each segment in turn */
- while (length > 0)
- {
- int i;
- size_t seg_size;
-
- /* softly softly, get the header first on its own */
- if (length < AS_HEADER_SIZE)
- {
- assegment_free_all (head);
- return NULL;
- }
-
- segh.type = stream_getc (s);
- segh.length = stream_getc (s);
-
- seg_size = ASSEGMENT_SIZE(segh.length, use32bit);
- /* includes the header bytes */
- if (BGP_DEBUG (as4, AS4_SEGMENT))
- zlog_debug ("[AS4SEG] Parse aspath segment: got type %d, length %d",
- segh.type, segh.length);
-
- switch (segh.type)
- {
- case AS_SEQUENCE:
- case AS_SET:
- break ;
- case AS_CONFED_SEQUENCE:
- case AS_CONFED_SET:
- if (!as4_path)
- break ;
- /* RFC4893 3: "invalid for the AS4_PATH attribute" */
- /* fall through */
- default: /* reject unknown or invalid AS_PATH segment types */
- seg_size = 0 ;
- }
-
- /* Stop now if segment is not valid (discarding anything collected to date)
- *
- * RFC4271 4.3, Path Attributes, b) AS_PATH:
- *
- * "path segment value field contains one or more AS numbers"
- */
- if ((seg_size == 0) || (seg_size > length) || (segh.length == 0))
- {
- assegment_free_all (head);
- return NULL;
- }
-
- length -= seg_size ;
-
- /* now its safe to trust lengths */
- seg = assegment_new (segh.type, segh.length);
-
- if (head)
- prev->next = seg;
- else /* it's the first segment */
- head = prev = seg;
-
- for (i = 0; i < segh.length; i++)
- seg->as[i] = (use32bit) ? stream_getl (s) : stream_getw (s);
- if (BGP_DEBUG (as4, AS4_SEGMENT))
- zlog_debug ("[AS4SEG] Parse aspath segment: length left: %lu",
- (unsigned long) length);
-
- prev = seg;
- }
-
- return assegment_normalise (head);
- }
- /* AS path parse function -- parses AS_PATH and AS4_PATH attributes
- *
- * Requires: s -- stream, currently positioned before first segment
- * of AS_PATH or AS4_PATH (ie after attribute header)
- * length -- length of the value of the AS_PATH or AS4_PATH
- * use32bit -- true <=> 4Byte ASN, otherwise 2Byte ASN
- * as4_path -- true <=> AS4_PATH, otherwise AS_PATH
- *
- * Returns: if valid: address of struct aspath in the hash of known aspaths,
- * with reference count incremented.
- * else: NULL
- *
- * NB: empty AS path (length == 0) is valid. The returned struct aspath will
- * have segments == NULL and str == zero length string (unique).
- */
- struct aspath *
- aspath_parse (struct stream *s, size_t length, int use32bit, int as4_path)
- {
- struct aspath as;
- struct aspath *find;
- /* Parse each segment and construct normalised list of struct assegment */
- memset (&as, 0, sizeof (struct aspath));
- if (length != 0)
- {
- as.segments = assegments_parse (s, length, use32bit, as4_path);
- if (as.segments == NULL)
- return NULL ; /* Invalid AS_PATH or AS4_PATH */
- } ;
-
- /* If already same aspath exist then return it. */
- find = hash_get (ashash, &as, aspath_hash_alloc);
-
- assert(find) ; /* valid aspath, so must find or create */
-
- /* aspath_hash_alloc dupes segments too. that probably could be
- * optimised out.
- */
- assegment_free_all (as.segments);
- if (as.str)
- XFREE (MTYPE_AS_STR, as.str);
-
- find->refcnt++;
- return find;
- }
- static inline void
- assegment_data_put (struct stream *s, as_t *as, int num, int use32bit)
- {
- int i;
- assert (num <= AS_SEGMENT_MAX);
-
- for (i = 0; i < num; i++)
- if ( use32bit )
- stream_putl (s, as[i]);
- else
- {
- if ( as[i] <= BGP_AS_MAX )
- stream_putw(s, as[i]);
- else
- stream_putw(s, BGP_AS_TRANS);
- }
- }
- static inline size_t
- assegment_header_put (struct stream *s, u_char type, int length)
- {
- size_t lenp;
- assert (length <= AS_SEGMENT_MAX);
- stream_putc (s, type);
- lenp = stream_get_endp (s);
- stream_putc (s, length);
- return lenp;
- }
- /* write aspath data to stream */
- size_t
- aspath_put (struct stream *s, struct aspath *as, int use32bit )
- {
- struct assegment *seg = as->segments;
- size_t bytes = 0;
-
- if (!seg || seg->length == 0)
- return 0;
-
- if (seg)
- {
- /*
- * Hey, what do we do when we have > STREAM_WRITABLE(s) here?
- * At the moment, we would write out a partial aspath, and our peer
- * will complain and drop the session :-/
- *
- * The general assumption here is that many things tested will
- * never happen. And, in real live, up to now, they have not.
- */
- while (seg && (ASSEGMENT_LEN(seg, use32bit) <= STREAM_WRITEABLE(s)))
- {
- struct assegment *next = seg->next;
- int written = 0;
- int asns_packed = 0;
- size_t lenp;
-
- /* Overlength segments have to be split up */
- while ( (seg->length - written) > AS_SEGMENT_MAX)
- {
- assegment_header_put (s, seg->type, AS_SEGMENT_MAX);
- assegment_data_put (s, seg->as, AS_SEGMENT_MAX, use32bit);
- written += AS_SEGMENT_MAX;
- bytes += ASSEGMENT_SIZE (written, use32bit);
- }
-
- /* write the final segment, probably is also the first */
- lenp = assegment_header_put (s, seg->type, seg->length - written);
- assegment_data_put (s, (seg->as + written), seg->length - written,
- use32bit);
-
- /* Sequence-type segments can be 'packed' together
- * Case of a segment which was overlength and split up
- * will be missed here, but that doesn't matter.
- */
- while (next && ASSEGMENTS_PACKABLE (seg, next))
- {
- /* NB: We should never normally get here given we
- * normalise aspath data when parse them. However, better
- * safe than sorry. We potentially could call
- * assegment_normalise here instead, but it's cheaper and
- * easier to do it on the fly here rather than go through
- * the segment list twice every time we write out
- * aspath's.
- */
-
- /* Next segment's data can fit in this one */
- assegment_data_put (s, next->as, next->length, use32bit);
-
- /* update the length of the segment header */
- stream_putc_at (s, lenp, seg->length - written + next->length);
- asns_packed += next->length;
-
- next = next->next;
- }
-
- bytes += ASSEGMENT_SIZE (seg->length - written + asns_packed,
- use32bit);
- seg = next;
- }
- }
- return bytes;
- }
- /* This is for SNMP BGP4PATHATTRASPATHSEGMENT
- * We have no way to manage the storage, so we use a static stream
- * wrapper around aspath_put.
- */
- u_char *
- aspath_snmp_pathseg (struct aspath *as, size_t *varlen)
- {
- #define SNMP_PATHSEG_MAX 1024
- if (!snmp_stream)
- snmp_stream = stream_new (SNMP_PATHSEG_MAX);
- else
- stream_reset (snmp_stream);
-
- if (!as)
- {
- *varlen = 0;
- return NULL;
- }
- aspath_put (snmp_stream, as, 0); /* use 16 bit for now here */
-
- *varlen = stream_get_endp (snmp_stream);
- return stream_pnt(snmp_stream);
- }
-
- #define min(A,B) ((A) < (B) ? (A) : (B))
- static 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)
- {
- asset = assegment_new (AS_SET, 1);
- if (! aspath->segments)
- aspath->segments = asset;
- else
- {
- struct assegment *seg = aspath->segments;
- while (seg->next)
- seg = seg->next;
- seg->next = asset;
- }
- asset->type = AS_SET;
- asset->length = 1;
- asset->as[0] = as;
- }
- else
- {
- /* Check this AS value already exists or not. */
- for (i = 0; i < asset->length; i++)
- if (asset->as[i] == as)
- return asset;
-
- asset->length++;
- asset->as = XREALLOC (MTYPE_AS_SEG_DATA, asset->as,
- asset->length * AS_VALUE_SIZE);
- asset->as[asset->length - 1] = as;
- }
-
- 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 from;
- struct assegment *seg1 = as1->segments;
- struct assegment *seg2 = as2->segments;
- struct aspath *aspath = NULL;
- struct assegment *asset;
- struct assegment *prevseg = NULL;
- match = 0;
- minlen = 0;
- aspath = NULL;
- asset = NULL;
- /* First of all check common leading sequence. */
- while (seg1 && seg2)
- {
- /* 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->as[match] != seg2->as[match])
- break;
- if (match)
- {
- struct assegment *seg = assegment_new (seg1->type, 0);
-
- seg = assegment_append_asns (seg, seg1->as, match);
- if (! aspath)
- {
- aspath = aspath_new ();
- aspath->segments = seg;
- }
- else
- prevseg->next = seg;
-
- prevseg = seg;
- }
- if (match != minlen || match != seg1->length
- || seg1->length != seg2->length)
- break;
-
- seg1 = seg1->next;
- seg2 = seg2->next;
- }
- if (! aspath)
- aspath = aspath_new();
- /* Make as-set using rest of all information. */
- from = match;
- while (seg1)
- {
- for (i = from; i < seg1->length; i++)
- asset = aspath_aggregate_as_set_add (aspath, asset, seg1->as[i]);
-
- from = 0;
- seg1 = seg1->next;
- }
- from = match;
- while (seg2)
- {
- for (i = from; i < seg2->length; i++)
- asset = aspath_aggregate_as_set_add (aspath, asset, seg2->as[i]);
- from = 0;
- seg2 = seg2->next;
- }
-
- assegment_normalise (aspath->segments);
- aspath_str_update (aspath);
- 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)
- {
- if ( (aspath == NULL) || (aspath->segments == NULL) )
- return 0;
-
- if (aspath->segments
- && (aspath->segments->type == AS_SEQUENCE)
- && (aspath->segments->as[0] == 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)
- {
- struct assegment *seg;
- int count = 0;
- if ( (aspath == NULL) || (aspath->segments == NULL) )
- return 0;
-
- seg = aspath->segments;
-
- while (seg)
- {
- int i;
-
- for (i = 0; i < seg->length; i++)
- if (seg->as[i] == asno)
- count++;
-
- seg = seg->next;
- }
- return count;
- }
- /* When all of AS path is private AS return 1. */
- int
- aspath_private_as_check (struct aspath *aspath)
- {
- struct assegment *seg;
-
- if ( !(aspath && aspath->segments) )
- return 0;
-
- seg = aspath->segments;
- while (seg)
- {
- int i;
-
- for (i = 0; i < seg->length; i++)
- {
- if ( (seg->as[i] < BGP_PRIVATE_AS_MIN)
- || (seg->as[i] > BGP_PRIVATE_AS_MAX) )
- return 0;
- }
- seg = seg->next;
- }
- return 1;
- }
- /* AS path confed check. If aspath contains confed set or sequence then return 1. */
- int
- aspath_confed_check (struct aspath *aspath)
- {
- struct assegment *seg;
- if ( !(aspath && aspath->segments) )
- return 0;
- seg = aspath->segments;
- while (seg)
- {
- if (seg->type == AS_CONFED_SET || seg->type == AS_CONFED_SEQUENCE)
- return 1;
- seg = seg->next;
- }
- return 0;
- }
- /* Leftmost AS path segment confed check. If leftmost AS segment is of type
- AS_CONFED_SEQUENCE or AS_CONFED_SET then return 1. */
- int
- aspath_left_confed_check (struct aspath *aspath)
- {
- if ( !(aspath && aspath->segments) )
- return 0;
- if ( (aspath->segments->type == AS_CONFED_SEQUENCE)
- || (aspath->segments->type == AS_CONFED_SET) )
- return 1;
- return 0;
- }
- /* Merge as1 to as2. as2 should be uninterned aspath. */
- static struct aspath *
- aspath_merge (struct aspath *as1, struct aspath *as2)
- {
- struct assegment *last, *new;
- if (! as1 || ! as2)
- return NULL;
- last = new = assegment_dup_all (as1->segments);
-
- /* find the last valid segment */
- while (last && last->next)
- last = last->next;
-
- last->next = as2->segments;
- as2->segments = new;
- aspath_str_update (as2);
- return as2;
- }
- /* Prepend as1 to as2. as2 should be uninterned aspath. */
- struct aspath *
- aspath_prepend (struct aspath *as1, struct aspath *as2)
- {
- struct assegment *seg1;
- struct assegment *seg2;
- if (! as1 || ! as2)
- return NULL;
-
- seg1 = as1->segments;
- seg2 = as2->segments;
-
- /* If as2 is empty, only need to dupe as1's chain onto as2 */
- if (seg2 == NULL)
- {
- as2->segments = assegment_dup_all (as1->segments);
- aspath_str_update (as2);
- return as2;
- }
-
- /* If as1 is empty AS, no prepending to do. */
- if (seg1 == NULL)
- return as2;
-
- /* find the tail as1's segment chain. */
- while (seg1 && seg1->next)
- seg1 = seg1->next;
- /* Delete any AS_CONFED_SEQUENCE segment from as2. */
- if (seg1->type == AS_SEQUENCE && seg2->type == AS_CONFED_SEQUENCE)
- as2 = aspath_delete_confed_seq (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)
- {
- /* We have two chains of segments, as1->segments and seg2,
- * and we have to attach them together, merging the attaching
- * segments together into one.
- *
- * 1. dupe as1->segments onto head of as2
- * 2. merge seg2's asns onto last segment of this new chain
- * 3. attach chain after seg2
- */
-
- /* dupe as1 onto as2's head */
- seg1 = as2->segments = assegment_dup_all (as1->segments);
-
- /* refind the tail of as2, reusing seg1 */
- while (seg1 && seg1->next)
- seg1 = seg1->next;
-
- /* merge the old head, seg2, into tail, seg1 */
- seg1 = assegment_append_asns (seg1, seg2->as, seg2->length);
-
- /* bypass the merged seg2, and attach any chain after it to
- * chain descending from as2's head
- */
- seg1->next = seg2->next;
-
- /* seg2 is now referenceless and useless*/
- assegment_free (seg2);
-
- /* we've now prepended as1's segment chain to as2, merging
- * the inbetween AS_SEQUENCE of seg2 in the process
- */
- aspath_str_update (as2);
- return as2;
- }
- else
- {
- /* AS_SET merge code is needed at here. */
- return aspath_merge (as1, as2);
- }
- /* XXX: Ermmm, what if as1 has multiple segments?? */
-
- /* Not reached */
- }
- /* Iterate over AS_PATH segments and wipe all occurences of the
- * listed AS numbers. Hence some segments may lose some or even
- * all data on the way, the operation is implemented as a smarter
- * version of aspath_dup(), which allocates memory to hold the new
- * data, not the original. The new AS path is returned.
- */
- struct aspath *
- aspath_filter_exclude (struct aspath * source, struct aspath * exclude_list)
- {
- struct assegment * srcseg, * exclseg, * lastseg;
- struct aspath * newpath;
- newpath = aspath_new();
- lastseg = NULL;
- for (srcseg = source->segments; srcseg; srcseg = srcseg->next)
- {
- unsigned i, y, newlen = 0, done = 0, skip_as;
- struct assegment * newseg;
- /* Find out, how much ASns are we going to pick from this segment.
- * We can't perform filtering right inline, because the size of
- * the new segment isn't known at the moment yet.
- */
- for (i = 0; i < srcseg->length; i++)
- {
- skip_as = 0;
- for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next)
- for (y = 0; y < exclseg->length; y++)
- if (srcseg->as[i] == exclseg->as[y])
- {
- skip_as = 1;
- // There's no sense in testing the rest of exclusion list, bail out.
- break;
- }
- if (!skip_as)
- newlen++;
- }
- /* newlen is now the number of ASns to copy */
- if (!newlen)
- continue;
- /* Actual copying. Allocate memory and iterate once more, performing filtering. */
- newseg = assegment_new (srcseg->type, newlen);
- for (i = 0; i < srcseg->length; i++)
- {
- skip_as = 0;
- for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next)
- for (y = 0; y < exclseg->length; y++)
- if (srcseg->as[i] == exclseg->as[y])
- {
- skip_as = 1;
- break;
- }
- if (skip_as)
- continue;
- newseg->as[done++] = srcseg->as[i];
- }
- /* At his point newlen must be equal to done, and both must be positive. Append
- * the filtered segment to the gross result. */
- if (!lastseg)
- newpath->segments = newseg;
- else
- lastseg->next = newseg;
- lastseg = newseg;
- }
- aspath_str_update (newpath);
- /* We are happy returning even an empty AS_PATH, because the administrator
- * might expect this very behaviour. There's a mean to avoid this, if necessary,
- * by having a match rule against certain AS_PATH regexps in the route-map index.
- */
- aspath_free (source);
- return newpath;
- }
- /* 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 = aspath->segments;
- /* In case of empty aspath. */
- if (assegment == NULL || assegment->length == 0)
- {
- aspath->segments = assegment_new (type, 1);
- aspath->segments->as[0] = asno;
-
- if (assegment)
- assegment_free (assegment);
- return aspath;
- }
- if (assegment->type == type)
- aspath->segments = assegment_prepend_asns (aspath->segments, asno, 1);
- else
- {
- /* create new segment
- * push it onto head of aspath's segment chain
- */
- struct assegment *newsegment;
-
- newsegment = assegment_new (type, 1);
- newsegment->as[0] = asno;
-
- newsegment->next = assegment;
- aspath->segments = newsegment;
- }
- 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 (const struct aspath *aspath1, const struct aspath *aspath2)
- {
- const struct assegment *seg1 = NULL;
- const struct assegment *seg2 = NULL;
- if (!(aspath1 && aspath2))
- return 0;
- seg1 = aspath1->segments;
- seg2 = aspath2->segments;
- /* find first non-confed segments for each */
- while (seg1 && ((seg1->type == AS_CONFED_SEQUENCE)
- || (seg1->type == AS_CONFED_SET)))
- seg1 = seg1->next;
- while (seg2 && ((seg2->type == AS_CONFED_SEQUENCE)
- || (seg2->type == AS_CONFED_SET)))
- seg2 = seg2->next;
- /* Check as1's */
- if (!(seg1 && seg2
- && (seg1->type == AS_SEQUENCE) && (seg2->type == AS_SEQUENCE)))
- return 0;
-
- if (seg1->as[0] == seg2->as[0])
- return 1;
- return 0;
- }
- /* Truncate an aspath after a number of hops, and put the hops remaining
- * at the front of another aspath. Needed for AS4 compat.
- *
- * Returned aspath is a /new/ aspath, which should either by free'd or
- * interned by the caller, as desired.
- */
- struct aspath *
- aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path)
- {
- struct assegment *seg, *newseg, *prevseg = NULL;
- struct aspath *newpath = NULL, *mergedpath;
- int hops, cpasns = 0;
-
- if (!aspath)
- return NULL;
-
- seg = aspath->segments;
-
- /* CONFEDs should get reconciled too.. */
- hops = (aspath_count_hops (aspath) + aspath_count_confeds (aspath))
- - aspath_count_hops (as4path);
-
- if (hops < 0)
- {
- if (BGP_DEBUG (as4, AS4))
- zlog_warn ("[AS4] Fewer hops in AS_PATH than NEW_AS_PATH");
- /* Something's gone wrong. The RFC says we should now ignore AS4_PATH,
- * which is daft behaviour - it contains vital loop-detection
- * information which must have been removed from AS_PATH.
- */
- hops = aspath_count_hops (aspath);
- }
-
- if (!hops)
- return aspath_dup (as4path);
-
- if ( BGP_DEBUG(as4, AS4))
- zlog_debug("[AS4] got AS_PATH %s and AS4_PATH %s synthesizing now",
- aspath->str, as4path->str);
- while (seg && hops > 0)
- {
- switch (seg->type)
- {
- case AS_SET:
- case AS_CONFED_SET:
- hops--;
- cpasns = seg->length;
- break;
- case AS_CONFED_SEQUENCE:
- /* Should never split a confed-sequence, if hop-count
- * suggests we must then something's gone wrong somewhere.
- *
- * Most important goal is to preserve AS_PATHs prime function
- * as loop-detector, so we fudge the numbers so that the entire
- * confed-sequence is merged in.
- */
- if (hops < seg->length)
- {
- if (BGP_DEBUG (as4, AS4))
- zlog_debug ("[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls"
- " across 2/4 ASN boundary somewhere, broken..");
- hops = seg->length;
- }
- case AS_SEQUENCE:
- cpasns = MIN(seg->length, hops);
- hops -= seg->length;
- }
-
- assert (cpasns <= seg->length);
-
- newseg = assegment_new (seg->type, 0);
- newseg = assegment_append_asns (newseg, seg->as, cpasns);
- if (!newpath)
- {
- newpath = aspath_new ();
- newpath->segments = newseg;
- }
- else
- prevseg->next = newseg;
- prevseg = newseg;
- seg = seg->next;
- }
-
- /* We may be able to join some segments here, and we must
- * do this because... we want normalised aspaths in out hash
- * and we do not want to stumble in aspath_put.
- */
- mergedpath = aspath_merge (newpath, aspath_dup(as4path));
- aspath_free (newpath);
- mergedpath->segments = assegment_normalise (mergedpath->segments);
- aspath_str_update (mergedpath);
-
- if ( BGP_DEBUG(as4, AS4))
- zlog_debug ("[AS4] result of synthesizing is %s",
- mergedpath->str);
-
- return mergedpath;
- }
- /* 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 (const struct aspath *aspath1, const struct aspath *aspath2)
- {
- if (! (aspath1 && aspath2) )
- return 0;
-
- if ( !(aspath1->segments && aspath2->segments) )
- return 0;
-
- if ( (aspath1->segments->type != AS_CONFED_SEQUENCE)
- || (aspath2->segments->type != AS_CONFED_SEQUENCE) )
- return 0;
-
- if (aspath1->segments->as[0] == aspath2->segments->as[0])
- return 1;
- return 0;
- }
- /* Delete all leading AS_CONFED_SEQUENCE/SET segments from aspath.
- * See RFC3065, 6.1 c1 */
- struct aspath *
- aspath_delete_confed_seq (struct aspath *aspath)
- {
- struct assegment *seg;
- if (!(aspath && aspath->segments))
- return aspath;
- seg = aspath->segments;
-
- /* "if the first path segment of the AS_PATH is
- * of type AS_CONFED_SEQUENCE,"
- */
- if (aspath->segments->type != AS_CONFED_SEQUENCE)
- return aspath;
- /* "... that segment and any immediately following segments
- * of the type AS_CONFED_SET or AS_CONFED_SEQUENCE are removed
- * from the AS_PATH attribute,"
- */
- while (seg &&
- (seg->type == AS_CONFED_SEQUENCE || seg->type == AS_CONFED_SET))
- {
- aspath->segments = seg->next;
- assegment_free (seg);
- seg = aspath->segments;
- }
- aspath_str_update (aspath);
- 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. */
- static void
- aspath_as_add (struct aspath *as, as_t asno)
- {
- struct assegment *seg = as->segments;
- if (!seg)
- return;
-
- /* Last segment search procedure. */
- while (seg->next)
- seg = seg->next;
- assegment_append_asns (seg, &asno, 1);
- }
- /* Add new as segment to the as path. */
- static void
- aspath_segment_add (struct aspath *as, int type)
- {
- struct assegment *seg = as->segments;
- struct assegment *new = assegment_new (type, 0);
- if (seg)
- {
- while (seg->next)
- seg = seg->next;
- seg->next = new;
- }
- else
- as->segments = new;
- }
- struct aspath *
- aspath_empty (void)
- {
- return aspath_parse (NULL, 0, 1, 0); /* 32Bit ;-) not AS4_PATH */
- }
- struct aspath *
- aspath_empty_get (void)
- {
- struct aspath *aspath;
- aspath = aspath_new ();
- aspath->str = aspath_make_str_count (aspath);
- return aspath;
- }
- unsigned long
- aspath_count (void)
- {
- 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_seq_start,
- as_token_confed_seq_end,
- as_token_confed_set_start,
- as_token_confed_set_end,
- as_token_unknown
- };
- /* Return next token and point for string parse. */
- static const char *
- aspath_gettoken (const char *buf, enum as_token *token, u_long *asno)
- {
- const char *p = buf;
- /* Skip seperators (space for sequences, ',' for sets). */
- while (isspace ((int) *p) || *p == ',')
- p++;
- /* Check the end of the string and type specify characters
- (e.g. {}()). */
- switch (*p)
- {
- case '\0':
- return NULL;
- case '{':
- *token = as_token_set_start;
- p++;
- return p;
- case '}':
- *token = as_token_set_end;
- p++;
- return p;
- case '(':
- *token = as_token_confed_seq_start;
- p++;
- return p;
- case ')':
- *token = as_token_confed_seq_end;
- p++;
- return p;
- case '[':
- *token = as_token_confed_set_start;
- p++;
- return p;
- case ']':
- *token = as_token_confed_set_end;
- p++;
- return p;
- }
- /* Check actual AS value. */
- if (isdigit ((int) *p))
- {
- as_t 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 (const char *str)
- {
- enum as_token token = as_token_unknown;
- u_short as_type;
- u_long asno = 0;
- 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_seq_start:
- as_type = AS_CONFED_SEQUENCE;
- aspath_segment_add (aspath, as_type);
- needtype = 0;
- break;
- case as_token_confed_seq_end:
- as_type = AS_SEQUENCE;
- needtype = 1;
- break;
- case as_token_confed_set_start:
- as_type = AS_CONFED_SET;
- aspath_segment_add (aspath, as_type);
- needtype = 0;
- break;
- case as_token_confed_set_end:
- as_type = AS_SEQUENCE;
- needtype = 1;
- break;
- case as_token_unknown:
- default:
- aspath_free (aspath);
- return NULL;
- }
- }
- aspath->str = aspath_make_str_count (aspath);
- return aspath;
- }
- /* Make hash value by raw aspath data. */
- unsigned int
- aspath_key_make (void *p)
- {
- struct aspath * aspath = (struct aspath *) p;
- unsigned int key = 0;
- if (!aspath->str)
- aspath_str_update (aspath);
-
- key = jhash (aspath->str, strlen(aspath->str), 2334325);
- return key;
- }
- /* If two aspath have same value then return 1 else return 0 */
- static int
- aspath_cmp (const void *arg1, const void *arg2)
- {
- const struct assegment *seg1 = ((const struct aspath *)arg1)->segments;
- const struct assegment *seg2 = ((const struct aspath *)arg2)->segments;
-
- while (seg1 || seg2)
- {
- int i;
- if ((!seg1 && seg2) || (seg1 && !seg2))
- return 0;
- if (seg1->type != seg2->type)
- return 0;
- if (seg1->length != seg2->length)
- return 0;
- for (i = 0; i < seg1->length; i++)
- if (seg1->as[i] != seg2->as[i])
- return 0;
- seg1 = seg1->next;
- seg2 = seg2->next;
- }
- return 1;
- }
- /* AS path hash initialize. */
- void
- aspath_init (void)
- {
- ashash = hash_create_size (32767, aspath_key_make, aspath_cmp);
- }
- void
- aspath_finish (void)
- {
- hash_free (ashash);
- ashash = NULL;
-
- if (snmp_stream)
- stream_free (snmp_stream);
- }
- /* return and as path value */
- const char *
- aspath_print (struct aspath *as)
- {
- return (as ? as->str : NULL);
- }
- /* Printing functions */
- /* Feed the AS_PATH to the vty; the suffix string follows it only in case
- * AS_PATH wasn't empty.
- */
- void
- aspath_print_vty (struct vty *vty, const char *format, struct aspath *as, const char * suffix)
- {
- assert (format);
- vty_out (vty, format, as->str);
- if (strlen (as->str) && strlen (suffix))
- vty_out (vty, "%s", suffix);
- }
- static 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);
- }
|