]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_aspath.c
bgpd: fix crash with vpnv4 soft-reconfiguration
[mirror_frr.git] / bgpd / bgp_aspath.c
CommitLineData
718e3744 1/* AS path management routines.
2 Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro
fe69a505 3 Copyright (C) 2005 Sun Microsystems, Inc.
718e3744 4
5This file is part of GNU Zebra.
6
7GNU Zebra is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by the
9Free Software Foundation; either version 2, or (at your option) any
10later version.
11
12GNU Zebra is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Zebra; see the file COPYING. If not, write to the Free
19Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
2002111-1307, USA. */
21
22#include <zebra.h>
23
24#include "hash.h"
25#include "memory.h"
26#include "vector.h"
27#include "vty.h"
28#include "str.h"
29#include "log.h"
fe69a505 30#include "stream.h"
0b2aa3a0 31#include "jhash.h"
718e3744 32
33#include "bgpd/bgpd.h"
34#include "bgpd/bgp_aspath.h"
0b2aa3a0
PJ
35#include "bgpd/bgp_debug.h"
36#include "bgpd/bgp_attr.h"
718e3744 37\f
38/* Attr. Flags and Attr. Type Code. */
39#define AS_HEADER_SIZE 2
40
0b2aa3a0 41/* Now FOUR octets are used for AS value. */
718e3744 42#define AS_VALUE_SIZE sizeof (as_t)
0b2aa3a0
PJ
43/* This is the old one */
44#define AS16_VALUE_SIZE sizeof (as16_t)
718e3744 45
fe69a505 46/* Maximum protocol segment length value */
47#define AS_SEGMENT_MAX 255
48
49/* The following length and size macros relate specifically to Quagga's
50 * internal representation of AS-Segments, not per se to the on-wire
51 * sizes and lengths. At present (200508) they sort of match, however
52 * the ONLY functions which should now about the on-wire syntax are
53 * aspath_put, assegment_put and assegment_parse.
0b2aa3a0
PJ
54 *
55 * aspath_put returns bytes written, the only definitive record of
56 * size of wire-format attribute..
fe69a505 57 */
58
59/* Calculated size in bytes of ASN segment data to hold N ASN's */
0b2aa3a0
PJ
60#define ASSEGMENT_DATA_SIZE(N,S) \
61 ((N) * ( (S) ? AS_VALUE_SIZE : AS16_VALUE_SIZE) )
718e3744 62
fe69a505 63/* Calculated size of segment struct to hold N ASN's */
0b2aa3a0 64#define ASSEGMENT_SIZE(N,S) (AS_HEADER_SIZE + ASSEGMENT_DATA_SIZE (N,S))
fe69a505 65
66/* AS segment octet length. */
0b2aa3a0 67#define ASSEGMENT_LEN(X,S) ASSEGMENT_SIZE((X)->length,S)
fe69a505 68
69/* AS_SEQUENCE segments can be packed together */
70/* Can the types of X and Y be considered for packing? */
71#define ASSEGMENT_TYPES_PACKABLE(X,Y) \
72 ( ((X)->type == (Y)->type) \
73 && ((X)->type == AS_SEQUENCE))
74/* Types and length of X,Y suitable for packing? */
75#define ASSEGMENTS_PACKABLE(X,Y) \
76 ( ASSEGMENT_TYPES_PACKABLE( (X), (Y)) \
77 && ( ((X)->length + (Y)->length) <= AS_SEGMENT_MAX ) )
78
79/* As segment header - the on-wire representation
80 * NOT the internal representation!
81 */
82struct assegment_header
718e3744 83{
84 u_char type;
85 u_char length;
718e3744 86};
87
88/* Hash for aspath. This is the top level structure of AS path. */
da88ea82 89static struct hash *ashash;
8fdc32ab 90
91/* Stream for SNMP. See aspath_snmp_pathseg */
92static struct stream *snmp_stream;
718e3744 93\f
f63f06da 94static as_t *
fe69a505 95assegment_data_new (int num)
96{
0b2aa3a0 97 return (XCALLOC (MTYPE_AS_SEG_DATA, ASSEGMENT_DATA_SIZE (num, 1)));
fe69a505 98}
99
f63f06da 100static void
fe69a505 101assegment_data_free (as_t *asdata)
102{
103 XFREE (MTYPE_AS_SEG_DATA,asdata);
104}
105
106/* Get a new segment. Note that 0 is an allowed length,
107 * and will result in a segment with no allocated data segment.
108 * the caller should immediately assign data to the segment, as the segment
109 * otherwise is not generally valid
110 */
111static struct assegment *
112assegment_new (u_char type, u_short length)
113{
114 struct assegment *new;
115
116 new = XCALLOC (MTYPE_AS_SEG, sizeof (struct assegment));
117
118 if (length)
119 new->as = assegment_data_new (length);
120
121 new->length = length;
122 new->type = type;
123
124 return new;
125}
126
127static void
128assegment_free (struct assegment *seg)
129{
130 if (!seg)
131 return;
132
133 if (seg->as)
134 XFREE (MTYPE_AS_SEG_DATA, seg->as);
135 memset (seg, 0xfe, sizeof(struct assegment));
136 XFREE (MTYPE_AS_SEG, seg);
137
138 return;
139}
140
141/* free entire chain of segments */
142static void
143assegment_free_all (struct assegment *seg)
144{
145 struct assegment *prev;
146
147 while (seg)
148 {
149 prev = seg;
150 seg = seg->next;
151 assegment_free (prev);
152 }
153}
154
155/* Duplicate just the given assegment and its data */
156static struct assegment *
157assegment_dup (struct assegment *seg)
158{
159 struct assegment *new;
160
161 new = assegment_new (seg->type, seg->length);
0b2aa3a0 162 memcpy (new->as, seg->as, ASSEGMENT_DATA_SIZE (new->length, 1) );
fe69a505 163
164 return new;
165}
166
167/* Duplicate entire chain of assegments, return the head */
168static struct assegment *
169assegment_dup_all (struct assegment *seg)
170{
171 struct assegment *new = NULL;
172 struct assegment *head = NULL;
173
174 while (seg)
175 {
176 if (head)
177 {
178 new->next = assegment_dup (seg);
179 new = new->next;
180 }
181 else
182 head = new = assegment_dup (seg);
183
184 seg = seg->next;
185 }
186 return head;
187}
188
189/* prepend the as number to given segment, given num of times */
190static struct assegment *
191assegment_prepend_asns (struct assegment *seg, as_t asnum, int num)
192{
193 as_t *newas;
194
195 if (!num)
196 return seg;
197
198 if (num >= AS_SEGMENT_MAX)
199 return seg; /* we don't do huge prepends */
200
201 newas = assegment_data_new (seg->length + num);
202
203 if (newas)
204 {
205 int i;
206 for (i = 0; i < num; i++)
207 newas[i] = asnum;
208
0b2aa3a0 209 memcpy (newas + num, seg->as, ASSEGMENT_DATA_SIZE (seg->length, 1));
fe69a505 210 XFREE (MTYPE_AS_SEG_DATA, seg->as);
211 seg->as = newas;
212 seg->length += num;
213 return seg;
214 }
215
216 assegment_free_all (seg);
217 return NULL;
218}
219
220/* append given array of as numbers to the segment */
221static struct assegment *
222assegment_append_asns (struct assegment *seg, as_t *asnos, int num)
223{
02335429 224 as_t *newas;
225
226 newas = XREALLOC (MTYPE_AS_SEG_DATA, seg->as,
0b2aa3a0 227 ASSEGMENT_DATA_SIZE (seg->length + num, 1));
fe69a505 228
02335429 229 if (newas)
fe69a505 230 {
02335429 231 seg->as = newas;
0b2aa3a0 232 memcpy (seg->as + seg->length, asnos, ASSEGMENT_DATA_SIZE(num, 1));
fe69a505 233 seg->length += num;
234 return seg;
235 }
236
237 assegment_free_all (seg);
238 return NULL;
239}
240
241static int
242int_cmp (const void *p1, const void *p2)
243{
244 const as_t *as1 = p1;
245 const as_t *as2 = p2;
246
247 return (*as1 == *as2)
248 ? 0 : ( (*as1 > *as2) ? 1 : -1);
249}
250
251/* normalise the segment.
252 * In particular, merge runs of AS_SEQUENCEs into one segment
253 * Internally, we do not care about the wire segment length limit, and
254 * we want each distinct AS_PATHs to have the exact same internal
255 * representation - eg, so that our hashing actually works..
256 */
257static struct assegment *
258assegment_normalise (struct assegment *head)
259{
260 struct assegment *seg = head, *pin;
261 struct assegment *tmp;
262
263 if (!head)
264 return head;
265
266 while (seg)
267 {
268 pin = seg;
269
270 /* Sort values SET segments, for determinism in paths to aid
271 * creation of hash values / path comparisons
272 * and because it helps other lesser implementations ;)
273 */
274 if (seg->type == AS_SET || seg->type == AS_CONFED_SET)
0b2aa3a0
PJ
275 {
276 int tail = 0;
277 int i;
278
279 qsort (seg->as, seg->length, sizeof(as_t), int_cmp);
280
281 /* weed out dupes */
282 for (i=1; i < seg->length; i++)
283 {
284 if (seg->as[tail] == seg->as[i])
285 continue;
286
287 tail++;
288 if (tail < i)
289 seg->as[tail] = seg->as[i];
290 }
291 /* seg->length can be 0.. */
292 if (seg->length)
293 seg->length = tail + 1;
294 }
fe69a505 295
296 /* read ahead from the current, pinned segment while the segments
297 * are packable/mergeable. Append all following packable segments
298 * to the segment we have pinned and remove these appended
299 * segments.
300 */
301 while (pin->next && ASSEGMENT_TYPES_PACKABLE(pin, pin->next))
302 {
303 tmp = pin->next;
304 seg = pin->next;
305
306 /* append the next sequence to the pinned sequence */
307 pin = assegment_append_asns (pin, seg->as, seg->length);
308
309 /* bypass the next sequence */
310 pin->next = seg->next;
311
312 /* get rid of the now referenceless segment */
313 assegment_free (tmp);
314
315 }
316
317 seg = pin->next;
318 }
319 return head;
320}
321\f
718e3744 322static struct aspath *
fe69a505 323aspath_new (void)
718e3744 324{
393deb9b 325 return XCALLOC (MTYPE_AS_PATH, sizeof (struct aspath));
718e3744 326}
327
328/* Free AS path structure. */
329void
330aspath_free (struct aspath *aspath)
331{
332 if (!aspath)
333 return;
fe69a505 334 if (aspath->segments)
335 assegment_free_all (aspath->segments);
718e3744 336 if (aspath->str)
337 XFREE (MTYPE_AS_STR, aspath->str);
338 XFREE (MTYPE_AS_PATH, aspath);
339}
340
341/* Unintern aspath from AS path bucket. */
342void
f6f434b2 343aspath_unintern (struct aspath **aspath)
718e3744 344{
345 struct aspath *ret;
f6f434b2
PJ
346 struct aspath *asp = *aspath;
347
348 if (asp->refcnt)
349 asp->refcnt--;
718e3744 350
f6f434b2 351 if (asp->refcnt == 0)
718e3744 352 {
353 /* This aspath must exist in aspath hash table. */
f6f434b2 354 ret = hash_release (ashash, asp);
718e3744 355 assert (ret != NULL);
f6f434b2
PJ
356 aspath_free (asp);
357 *aspath = NULL;
718e3744 358 }
359}
360
361/* Return the start or end delimiters for a particular Segment type */
362#define AS_SEG_START 0
363#define AS_SEG_END 1
364static char
365aspath_delimiter_char (u_char type, u_char which)
366{
367 int i;
368 struct
369 {
370 int type;
371 char start;
372 char end;
373 } aspath_delim_char [] =
374 {
375 { AS_SET, '{', '}' },
718e3744 376 { AS_CONFED_SET, '[', ']' },
377 { AS_CONFED_SEQUENCE, '(', ')' },
378 { 0 }
379 };
380
381 for (i = 0; aspath_delim_char[i].type != 0; i++)
382 {
383 if (aspath_delim_char[i].type == type)
384 {
385 if (which == AS_SEG_START)
386 return aspath_delim_char[i].start;
387 else if (which == AS_SEG_END)
388 return aspath_delim_char[i].end;
389 }
390 }
391 return ' ';
392}
393
014b670e
DO
394/* countup asns from this segment and index onward */
395static int
396assegment_count_asns (struct assegment *seg, int from)
397{
398 int count = 0;
399 while (seg)
400 {
401 if (!from)
402 count += seg->length;
403 else
404 {
405 count += (seg->length - from);
406 from = 0;
407 }
408 seg = seg->next;
409 }
410 return count;
411}
412
fe69a505 413unsigned int
414aspath_count_confeds (struct aspath *aspath)
415{
416 int count = 0;
417 struct assegment *seg = aspath->segments;
418
419 while (seg)
420 {
421 if (seg->type == AS_CONFED_SEQUENCE)
422 count += seg->length;
423 else if (seg->type == AS_CONFED_SET)
424 count++;
425
426 seg = seg->next;
427 }
428 return count;
429}
430
431unsigned int
432aspath_count_hops (struct aspath *aspath)
433{
434 int count = 0;
435 struct assegment *seg = aspath->segments;
436
437 while (seg)
438 {
439 if (seg->type == AS_SEQUENCE)
440 count += seg->length;
441 else if (seg->type == AS_SET)
442 count++;
443
444 seg = seg->next;
445 }
446 return count;
447}
448
0b2aa3a0
PJ
449/* Estimate size aspath /might/ take if encoded into an
450 * ASPATH attribute.
451 *
452 * This is a quick estimate, not definitive! aspath_put()
453 * may return a different number!!
454 */
fe69a505 455unsigned int
456aspath_size (struct aspath *aspath)
457{
458 int size = 0;
459 struct assegment *seg = aspath->segments;
460
461 while (seg)
462 {
0b2aa3a0 463 size += ASSEGMENT_SIZE(seg->length, 1);
fe69a505 464 seg = seg->next;
465 }
466 return size;
467}
468
2815e61f
PJ
469/* Return highest public ASN in path */
470as_t
471aspath_highest (struct aspath *aspath)
472{
473 struct assegment *seg = aspath->segments;
474 as_t highest = 0;
475 unsigned int i;
476
477 while (seg)
478 {
479 for (i = 0; i < seg->length; i++)
480 if (seg->as[i] > highest
481 && (seg->as[i] < BGP_PRIVATE_AS_MIN
482 || seg->as[i] > BGP_PRIVATE_AS_MAX))
483 highest = seg->as[i];
484 seg = seg->next;
485 }
486 return highest;
487}
488
0b2aa3a0
PJ
489/* Return 1 if there are any 4-byte ASes in the path */
490unsigned int
491aspath_has_as4 (struct aspath *aspath)
492{
493 struct assegment *seg = aspath->segments;
494 unsigned int i;
495
496 while (seg)
497 {
498 for (i = 0; i < seg->length; i++)
499 if (seg->as[i] > BGP_AS_MAX)
500 return 1;
501 seg = seg->next;
502 }
503 return 0;
504}
505
718e3744 506/* Convert aspath structure to string expression. */
94f2b392 507static char *
718e3744 508aspath_make_str_count (struct aspath *as)
509{
fe69a505 510 struct assegment *seg;
511 int str_size;
512 int len = 0;
c9e52be3 513 char *str_buf;
718e3744 514
515 /* Empty aspath. */
fe69a505 516 if (!as->segments)
718e3744 517 {
518 str_buf = XMALLOC (MTYPE_AS_STR, 1);
519 str_buf[0] = '\0';
718e3744 520 return str_buf;
521 }
fe69a505 522
523 seg = as->segments;
524
014b670e
DO
525 /* ASN takes 5 to 10 chars plus seperator, see below.
526 * If there is one differing segment type, we need an additional
527 * 2 chars for segment delimiters, and the final '\0'.
528 * Hopefully this is large enough to avoid hitting the realloc
529 * code below for most common sequences.
530 *
531 * This was changed to 10 after the well-known BGP assertion, which
532 * had hit some parts of the Internet in May of 2009.
533 */
534#define ASN_STR_LEN (10 + 1)
535 str_size = MAX (assegment_count_asns (seg, 0) * ASN_STR_LEN + 2 + 1,
536 ASPATH_STR_DEFAULT_LEN);
718e3744 537 str_buf = XMALLOC (MTYPE_AS_STR, str_size);
718e3744 538
fe69a505 539 while (seg)
718e3744 540 {
541 int i;
fe69a505 542 char seperator;
718e3744 543
fe69a505 544 /* Check AS type validity. Set seperator for segment */
545 switch (seg->type)
546 {
547 case AS_SET:
548 case AS_CONFED_SET:
549 seperator = ',';
550 break;
551 case AS_SEQUENCE:
552 case AS_CONFED_SEQUENCE:
553 seperator = ' ';
554 break;
555 default:
556 XFREE (MTYPE_AS_STR, str_buf);
557 return NULL;
558 }
559
014b670e
DO
560 /* We might need to increase str_buf, particularly if path has
561 * differing segments types, our initial guesstimate above will
562 * have been wrong. Need 10 chars for ASN, a seperator each and
563 * potentially two segment delimiters, plus a space between each
564 * segment and trailing zero.
565 *
566 * This definitely didn't work with the value of 5 bytes and
567 * 32-bit ASNs.
568 */
569#define SEGMENT_STR_LEN(X) (((X)->length * ASN_STR_LEN) + 2 + 1 + 1)
570 if ( (len + SEGMENT_STR_LEN(seg)) > str_size)
571 {
572 str_size = len + SEGMENT_STR_LEN(seg);
573 str_buf = XREALLOC (MTYPE_AS_STR, str_buf, str_size);
574 }
575#undef ASN_STR_LEN
576#undef SEGMENT_STR_LEN
577
fe69a505 578 if (seg->type != AS_SEQUENCE)
014b670e
DO
579 len += snprintf (str_buf + len, str_size - len,
580 "%c",
581 aspath_delimiter_char (seg->type, AS_SEG_START));
fe69a505 582
583 /* write out the ASNs, with their seperators, bar the last one*/
584 for (i = 0; i < seg->length; i++)
585 {
586 len += snprintf (str_buf + len, str_size - len, "%u", seg->as[i]);
587
588 if (i < (seg->length - 1))
589 len += snprintf (str_buf + len, str_size - len, "%c", seperator);
590 }
591
592 if (seg->type != AS_SEQUENCE)
593 len += snprintf (str_buf + len, str_size - len, "%c",
594 aspath_delimiter_char (seg->type, AS_SEG_END));
595 if (seg->next)
596 len += snprintf (str_buf + len, str_size - len, " ");
597
598 seg = seg->next;
718e3744 599 }
fe69a505 600
601 assert (len < str_size);
602
603 str_buf[len] = '\0';
718e3744 604
605 return str_buf;
606}
607
fe69a505 608static void
609aspath_str_update (struct aspath *as)
610{
611 if (as->str)
612 XFREE (MTYPE_AS_STR, as->str);
613 as->str = aspath_make_str_count (as);
614}
615
718e3744 616/* Intern allocated AS path. */
617struct aspath *
618aspath_intern (struct aspath *aspath)
619{
620 struct aspath *find;
621
622 /* Assert this AS path structure is not interned. */
623 assert (aspath->refcnt == 0);
624
625 /* Check AS path hash. */
626 find = hash_get (ashash, aspath, hash_alloc_intern);
627
628 if (find != aspath)
fe69a505 629 aspath_free (aspath);
718e3744 630
631 find->refcnt++;
632
633 if (! find->str)
634 find->str = aspath_make_str_count (find);
635
636 return find;
637}
638
639/* Duplicate aspath structure. Created same aspath structure but
640 reference count and AS path string is cleared. */
641struct aspath *
642aspath_dup (struct aspath *aspath)
643{
644 struct aspath *new;
645
fe69a505 646 new = XCALLOC (MTYPE_AS_PATH, sizeof (struct aspath));
718e3744 647
fe69a505 648 if (aspath->segments)
649 new->segments = assegment_dup_all (aspath->segments);
718e3744 650 else
fe69a505 651 new->segments = NULL;
718e3744 652
fe69a505 653 new->str = aspath_make_str_count (aspath);
718e3744 654
655 return new;
656}
657
94f2b392 658static void *
fe69a505 659aspath_hash_alloc (void *arg)
718e3744 660{
661 struct aspath *aspath;
662
0b2aa3a0 663 /* New aspath structure is needed. */
fe69a505 664 aspath = aspath_dup (arg);
665
718e3744 666 /* Malformed AS path value. */
667 if (! aspath->str)
668 {
669 aspath_free (aspath);
670 return NULL;
671 }
672
673 return aspath;
674}
675
ab005298 676/* parse as-segment byte stream in struct assegment */
b881c707
PJ
677static int
678assegments_parse (struct stream *s, size_t length,
679 struct assegment **result, int use32bit)
fe69a505 680{
681 struct assegment_header segh;
682 struct assegment *seg, *prev = NULL, *head = NULL;
ab005298 683 size_t bytes = 0;
fe69a505 684
ab005298
PJ
685 /* empty aspath (ie iBGP or somesuch) */
686 if (length == 0)
b881c707 687 return 0;
fe69a505 688
0b2aa3a0
PJ
689 if (BGP_DEBUG (as4, AS4_SEGMENT))
690 zlog_debug ("[AS4SEG] Parse aspath segment: got total byte length %lu",
691 (unsigned long) length);
ab005298
PJ
692 /* basic checks */
693 if ((STREAM_READABLE(s) < length)
694 || (STREAM_READABLE(s) < AS_HEADER_SIZE)
695 || (length % AS16_VALUE_SIZE ))
b881c707 696 return -1;
fe69a505 697
ab005298 698 while (bytes < length)
fe69a505 699 {
700 int i;
cddb8112 701 size_t seg_size;
fe69a505 702
ab005298 703 if ((length - bytes) <= AS_HEADER_SIZE)
cddb8112 704 {
ab005298
PJ
705 if (head)
706 assegment_free_all (head);
b881c707 707 return -1;
fdbc8e77
PJ
708 }
709
ab005298 710 /* softly softly, get the header first on its own */
fe69a505 711 segh.type = stream_getc (s);
712 segh.length = stream_getc (s);
713
0b2aa3a0
PJ
714 seg_size = ASSEGMENT_SIZE(segh.length, use32bit);
715
716 if (BGP_DEBUG (as4, AS4_SEGMENT))
717 zlog_debug ("[AS4SEG] Parse aspath segment: got type %d, length %d",
718 segh.type, segh.length);
719
ab005298
PJ
720 /* check it.. */
721 if ( ((bytes + seg_size) > length)
722 /* 1771bis 4.3b: seg length contains one or more */
723 || (segh.length == 0)
724 /* Paranoia in case someone changes type of segment length.
725 * Shift both values by 0x10 to make the comparison operate
726 * on more, than 8 bits (otherwise it's a warning, bug #564).
2eb445e1 727 */
ab005298
PJ
728 || ((sizeof segh.length > 1)
729 && (0x10 + segh.length > 0x10 + AS_SEGMENT_MAX)))
fe69a505 730 {
ab005298 731 if (head)
fe69a505 732 assegment_free_all (head);
b881c707 733 return -1;
fe69a505 734 }
735
fdbc8e77
PJ
736 switch (segh.type)
737 {
738 case AS_SEQUENCE:
739 case AS_SET:
fdbc8e77
PJ
740 case AS_CONFED_SEQUENCE:
741 case AS_CONFED_SET:
ab005298
PJ
742 break;
743 default:
744 if (head)
745 assegment_free_all (head);
b881c707 746 return -1;
fe69a505 747 }
748
749 /* now its safe to trust lengths */
750 seg = assegment_new (segh.type, segh.length);
751
752 if (head)
753 prev->next = seg;
754 else /* it's the first segment */
755 head = prev = seg;
756
757 for (i = 0; i < segh.length; i++)
0b2aa3a0
PJ
758 seg->as[i] = (use32bit) ? stream_getl (s) : stream_getw (s);
759
ab005298
PJ
760 bytes += seg_size;
761
0b2aa3a0 762 if (BGP_DEBUG (as4, AS4_SEGMENT))
ab005298
PJ
763 zlog_debug ("[AS4SEG] Parse aspath segment: Bytes now: %lu",
764 (unsigned long) bytes);
fe69a505 765
766 prev = seg;
767 }
768
b881c707
PJ
769 *result = assegment_normalise (head);
770 return 0;
fe69a505 771}
772
ab005298
PJ
773/* AS path parse function. pnt is a pointer to byte stream and length
774 is length of byte stream. If there is same AS path in the the AS
b881c707
PJ
775 path hash then return it else make new AS path structure.
776
777 On error NULL is returned.
cddb8112 778 */
718e3744 779struct aspath *
ab005298 780aspath_parse (struct stream *s, size_t length, int use32bit)
718e3744 781{
782 struct aspath as;
783 struct aspath *find;
784
ab005298
PJ
785 /* If length is odd it's malformed AS path. */
786 /* Nit-picking: if (use32bit == 0) it is malformed if odd,
787 * otherwise its malformed when length is larger than 2 and (length-2)
788 * is not dividable by 4.
789 * But... this time we're lazy
790 */
791 if (length % AS16_VALUE_SIZE )
792 return NULL;
cddb8112 793
ab005298 794 memset (&as, 0, sizeof (struct aspath));
b881c707
PJ
795 if (assegments_parse (s, length, &as.segments, use32bit) < 0)
796 return NULL;
fe69a505 797
718e3744 798 /* If already same aspath exist then return it. */
799 find = hash_get (ashash, &as, aspath_hash_alloc);
02335429 800
801 /* aspath_hash_alloc dupes segments too. that probably could be
802 * optimised out.
803 */
804 assegment_free_all (as.segments);
0b2aa3a0
PJ
805 if (as.str)
806 XFREE (MTYPE_AS_STR, as.str);
02335429 807
ab005298
PJ
808 if (! find)
809 return NULL;
718e3744 810 find->refcnt++;
811
812 return find;
813}
814
f63f06da 815static void
0b2aa3a0 816assegment_data_put (struct stream *s, as_t *as, int num, int use32bit)
fe69a505 817{
818 int i;
819 assert (num <= AS_SEGMENT_MAX);
820
821 for (i = 0; i < num; i++)
0b2aa3a0
PJ
822 if ( use32bit )
823 stream_putl (s, as[i]);
824 else
825 {
826 if ( as[i] <= BGP_AS_MAX )
827 stream_putw(s, as[i]);
828 else
829 stream_putw(s, BGP_AS_TRANS);
830 }
fe69a505 831}
718e3744 832
f63f06da 833static size_t
fe69a505 834assegment_header_put (struct stream *s, u_char type, int length)
718e3744 835{
fe69a505 836 size_t lenp;
837 assert (length <= AS_SEGMENT_MAX);
838 stream_putc (s, type);
839 lenp = stream_get_endp (s);
840 stream_putc (s, length);
841 return lenp;
842}
718e3744 843
fe69a505 844/* write aspath data to stream */
0b2aa3a0
PJ
845size_t
846aspath_put (struct stream *s, struct aspath *as, int use32bit )
fe69a505 847{
848 struct assegment *seg = as->segments;
0b2aa3a0 849 size_t bytes = 0;
fe69a505 850
851 if (!seg || seg->length == 0)
0b2aa3a0 852 return 0;
fe69a505 853
854 if (seg)
718e3744 855 {
0b2aa3a0
PJ
856 /*
857 * Hey, what do we do when we have > STREAM_WRITABLE(s) here?
858 * At the moment, we would write out a partial aspath, and our peer
859 * will complain and drop the session :-/
860 *
861 * The general assumption here is that many things tested will
862 * never happen. And, in real live, up to now, they have not.
863 */
864 while (seg && (ASSEGMENT_LEN(seg, use32bit) <= STREAM_WRITEABLE(s)))
fe69a505 865 {
0b2aa3a0 866 struct assegment *next = seg->next;
fe69a505 867 int written = 0;
0b2aa3a0 868 int asns_packed = 0;
fe69a505 869 size_t lenp;
870
871 /* Overlength segments have to be split up */
872 while ( (seg->length - written) > AS_SEGMENT_MAX)
873 {
874 assegment_header_put (s, seg->type, AS_SEGMENT_MAX);
0b2aa3a0 875 assegment_data_put (s, seg->as, AS_SEGMENT_MAX, use32bit);
fe69a505 876 written += AS_SEGMENT_MAX;
0b2aa3a0 877 bytes += ASSEGMENT_SIZE (written, use32bit);
fe69a505 878 }
879
880 /* write the final segment, probably is also the first */
881 lenp = assegment_header_put (s, seg->type, seg->length - written);
0b2aa3a0
PJ
882 assegment_data_put (s, (seg->as + written), seg->length - written,
883 use32bit);
fe69a505 884
885 /* Sequence-type segments can be 'packed' together
886 * Case of a segment which was overlength and split up
887 * will be missed here, but that doesn't matter.
888 */
0b2aa3a0 889 while (next && ASSEGMENTS_PACKABLE (seg, next))
fe69a505 890 {
891 /* NB: We should never normally get here given we
892 * normalise aspath data when parse them. However, better
893 * safe than sorry. We potentially could call
894 * assegment_normalise here instead, but it's cheaper and
895 * easier to do it on the fly here rather than go through
896 * the segment list twice every time we write out
897 * aspath's.
898 */
899
900 /* Next segment's data can fit in this one */
0b2aa3a0 901 assegment_data_put (s, next->as, next->length, use32bit);
fe69a505 902
903 /* update the length of the segment header */
0b2aa3a0
PJ
904 stream_putc_at (s, lenp, seg->length - written + next->length);
905 asns_packed += next->length;
906
907 next = next->next;
fe69a505 908 }
0b2aa3a0
PJ
909
910 bytes += ASSEGMENT_SIZE (seg->length - written + asns_packed,
911 use32bit);
912 seg = next;
fe69a505 913 }
718e3744 914 }
0b2aa3a0 915 return bytes;
fe69a505 916}
917
918/* This is for SNMP BGP4PATHATTRASPATHSEGMENT
919 * We have no way to manage the storage, so we use a static stream
920 * wrapper around aspath_put.
921 */
922u_char *
923aspath_snmp_pathseg (struct aspath *as, size_t *varlen)
924{
925#define SNMP_PATHSEG_MAX 1024
8fdc32ab 926
927 if (!snmp_stream)
928 snmp_stream = stream_new (SNMP_PATHSEG_MAX);
718e3744 929 else
8fdc32ab 930 stream_reset (snmp_stream);
fe69a505 931
932 if (!as)
718e3744 933 {
fe69a505 934 *varlen = 0;
935 return NULL;
718e3744 936 }
0b2aa3a0 937 aspath_put (snmp_stream, as, 0); /* use 16 bit for now here */
fe69a505 938
8fdc32ab 939 *varlen = stream_get_endp (snmp_stream);
940 return stream_pnt(snmp_stream);
718e3744 941}
fe69a505 942
943#define min(A,B) ((A) < (B) ? (A) : (B))
718e3744 944
94f2b392 945static struct assegment *
718e3744 946aspath_aggregate_as_set_add (struct aspath *aspath, struct assegment *asset,
947 as_t as)
948{
949 int i;
950
951 /* If this is first AS set member, create new as-set segment. */
952 if (asset == NULL)
953 {
fe69a505 954 asset = assegment_new (AS_SET, 1);
955 if (! aspath->segments)
956 aspath->segments = asset;
718e3744 957 else
fe69a505 958 {
959 struct assegment *seg = aspath->segments;
960 while (seg->next)
961 seg = seg->next;
962 seg->next = asset;
963 }
718e3744 964 asset->type = AS_SET;
965 asset->length = 1;
fe69a505 966 asset->as[0] = as;
718e3744 967 }
968 else
969 {
718e3744 970 /* Check this AS value already exists or not. */
971 for (i = 0; i < asset->length; i++)
fe69a505 972 if (asset->as[i] == as)
718e3744 973 return asset;
fe69a505 974
718e3744 975 asset->length++;
fe69a505 976 asset->as = XREALLOC (MTYPE_AS_SEG_DATA, asset->as,
977 asset->length * AS_VALUE_SIZE);
978 asset->as[asset->length - 1] = as;
718e3744 979 }
fe69a505 980
718e3744 981
982 return asset;
983}
984
985/* Modify as1 using as2 for aggregation. */
986struct aspath *
987aspath_aggregate (struct aspath *as1, struct aspath *as2)
988{
989 int i;
990 int minlen;
991 int match;
fe69a505 992 int from;
993 struct assegment *seg1 = as1->segments;
994 struct assegment *seg2 = as2->segments;
0b2aa3a0 995 struct aspath *aspath = NULL;
718e3744 996 struct assegment *asset;
0b2aa3a0 997 struct assegment *prevseg = NULL;
718e3744 998
999 match = 0;
1000 minlen = 0;
1001 aspath = NULL;
1002 asset = NULL;
718e3744 1003
1004 /* First of all check common leading sequence. */
fe69a505 1005 while (seg1 && seg2)
0b2aa3a0 1006 {
718e3744 1007 /* Check segment type. */
1008 if (seg1->type != seg2->type)
1009 break;
1010
1011 /* Minimum segment length. */
1012 minlen = min (seg1->length, seg2->length);
1013
1014 for (match = 0; match < minlen; match++)
fe69a505 1015 if (seg1->as[match] != seg2->as[match])
718e3744 1016 break;
1017
1018 if (match)
1019 {
0b2aa3a0
PJ
1020 struct assegment *seg = assegment_new (seg1->type, 0);
1021
1022 seg = assegment_append_asns (seg, seg1->as, match);
1023
718e3744 1024 if (! aspath)
0b2aa3a0
PJ
1025 {
1026 aspath = aspath_new ();
1027 aspath->segments = seg;
1028 }
1029 else
1030 prevseg->next = seg;
1031
1032 prevseg = seg;
718e3744 1033 }
1034
1035 if (match != minlen || match != seg1->length
1036 || seg1->length != seg2->length)
1037 break;
fe69a505 1038
1039 seg1 = seg1->next;
1040 seg2 = seg2->next;
718e3744 1041 }
1042
1043 if (! aspath)
1044 aspath = aspath_new();
1045
1046 /* Make as-set using rest of all information. */
fe69a505 1047 from = match;
1048 while (seg1)
718e3744 1049 {
fe69a505 1050 for (i = from; i < seg1->length; i++)
1051 asset = aspath_aggregate_as_set_add (aspath, asset, seg1->as[i]);
1052
1053 from = 0;
1054 seg1 = seg1->next;
718e3744 1055 }
1056
fe69a505 1057 from = match;
1058 while (seg2)
718e3744 1059 {
fe69a505 1060 for (i = from; i < seg2->length; i++)
1061 asset = aspath_aggregate_as_set_add (aspath, asset, seg2->as[i]);
718e3744 1062
fe69a505 1063 from = 0;
1064 seg2 = seg2->next;
718e3744 1065 }
fe69a505 1066
1067 assegment_normalise (aspath->segments);
1068 aspath_str_update (aspath);
718e3744 1069 return aspath;
1070}
1071
1072/* When a BGP router receives an UPDATE with an MP_REACH_NLRI
1073 attribute, check the leftmost AS number in the AS_PATH attribute is
1074 or not the peer's AS number. */
1075int
1076aspath_firstas_check (struct aspath *aspath, as_t asno)
1077{
fe69a505 1078 if ( (aspath == NULL) || (aspath->segments == NULL) )
718e3744 1079 return 0;
fe69a505 1080
1081 if (aspath->segments
1082 && (aspath->segments->type == AS_SEQUENCE)
1083 && (aspath->segments->as[0] == asno ))
718e3744 1084 return 1;
1085
1086 return 0;
1087}
1088
1f742f21 1089/* AS path loop check. If aspath contains asno then return >= 1. */
718e3744 1090int
1091aspath_loop_check (struct aspath *aspath, as_t asno)
1092{
fe69a505 1093 struct assegment *seg;
718e3744 1094 int count = 0;
1095
1f742f21 1096 if ( (aspath == NULL) || (aspath->segments == NULL) )
718e3744 1097 return 0;
fe69a505 1098
1099 seg = aspath->segments;
1100
1101 while (seg)
718e3744 1102 {
1103 int i;
718e3744 1104
fe69a505 1105 for (i = 0; i < seg->length; i++)
1106 if (seg->as[i] == asno)
718e3744 1107 count++;
fe69a505 1108
1109 seg = seg->next;
718e3744 1110 }
1111 return count;
1112}
1113
1114/* When all of AS path is private AS return 1. */
1115int
1116aspath_private_as_check (struct aspath *aspath)
1117{
fe69a505 1118 struct assegment *seg;
1119
1120 if ( !(aspath && aspath->segments) )
718e3744 1121 return 0;
fe69a505 1122
1123 seg = aspath->segments;
718e3744 1124
fe69a505 1125 while (seg)
718e3744 1126 {
1127 int i;
718e3744 1128
fe69a505 1129 for (i = 0; i < seg->length; i++)
718e3744 1130 {
fe69a505 1131 if ( (seg->as[i] < BGP_PRIVATE_AS_MIN)
1132 || (seg->as[i] > BGP_PRIVATE_AS_MAX) )
718e3744 1133 return 0;
1134 }
fe69a505 1135 seg = seg->next;
718e3744 1136 }
1137 return 1;
1138}
1139
ca87e1d3
VT
1140/* AS path confed check. If aspath contains confed set or sequence then return 1. */
1141int
1142aspath_confed_check (struct aspath *aspath)
1143{
1144 struct assegment *seg;
1145
1146 if ( !(aspath && aspath->segments) )
1147 return 0;
1148
1149 seg = aspath->segments;
1150
1151 while (seg)
1152 {
1153 if (seg->type == AS_CONFED_SET || seg->type == AS_CONFED_SEQUENCE)
1154 return 1;
1155 seg = seg->next;
1156 }
1157 return 0;
1158}
1159
1160/* Leftmost AS path segment confed check. If leftmost AS segment is of type
1161 AS_CONFED_SEQUENCE or AS_CONFED_SET then return 1. */
1162int
1163aspath_left_confed_check (struct aspath *aspath)
1164{
1165
1166 if ( !(aspath && aspath->segments) )
1167 return 0;
1168
1169 if ( (aspath->segments->type == AS_CONFED_SEQUENCE)
1170 || (aspath->segments->type == AS_CONFED_SET) )
1171 return 1;
1172
1173 return 0;
1174}
1175
718e3744 1176/* Merge as1 to as2. as2 should be uninterned aspath. */
94f2b392 1177static struct aspath *
718e3744 1178aspath_merge (struct aspath *as1, struct aspath *as2)
1179{
fe69a505 1180 struct assegment *last, *new;
718e3744 1181
1182 if (! as1 || ! as2)
1183 return NULL;
1184
fe69a505 1185 last = new = assegment_dup_all (as1->segments);
1186
1187 /* find the last valid segment */
1188 while (last && last->next)
1189 last = last->next;
1190
1191 last->next = as2->segments;
1192 as2->segments = new;
1193 aspath_str_update (as2);
718e3744 1194 return as2;
1195}
1196
1197/* Prepend as1 to as2. as2 should be uninterned aspath. */
1198struct aspath *
1199aspath_prepend (struct aspath *as1, struct aspath *as2)
1200{
fe69a505 1201 struct assegment *seg1;
1202 struct assegment *seg2;
718e3744 1203
1204 if (! as1 || ! as2)
1205 return NULL;
fe69a505 1206
1207 seg1 = as1->segments;
1208 seg2 = as2->segments;
1209
1210 /* If as2 is empty, only need to dupe as1's chain onto as2 */
718e3744 1211 if (seg2 == NULL)
1212 {
fe69a505 1213 as2->segments = assegment_dup_all (as1->segments);
1214 aspath_str_update (as2);
718e3744 1215 return as2;
1216 }
fe69a505 1217
1218 /* If as1 is empty AS, no prepending to do. */
718e3744 1219 if (seg1 == NULL)
1220 return as2;
fe69a505 1221
1222 /* find the tail as1's segment chain. */
1223 while (seg1 && seg1->next)
1224 seg1 = seg1->next;
718e3744 1225
736d4408
VT
1226 /* Delete any AS_CONFED_SEQUENCE segment from as2. */
1227 if (seg1->type == AS_SEQUENCE && seg2->type == AS_CONFED_SEQUENCE)
1228 as2 = aspath_delete_confed_seq (as2);
1229
718e3744 1230 /* Compare last segment type of as1 and first segment type of as2. */
1231 if (seg1->type != seg2->type)
1232 return aspath_merge (as1, as2);
1233
1234 if (seg1->type == AS_SEQUENCE)
1235 {
fe69a505 1236 /* We have two chains of segments, as1->segments and seg2,
1237 * and we have to attach them together, merging the attaching
1238 * segments together into one.
1239 *
1240 * 1. dupe as1->segments onto head of as2
1241 * 2. merge seg2's asns onto last segment of this new chain
1242 * 3. attach chain after seg2
1243 */
718e3744 1244
fe69a505 1245 /* dupe as1 onto as2's head */
1246 seg1 = as2->segments = assegment_dup_all (as1->segments);
1247
1248 /* refind the tail of as2, reusing seg1 */
1249 while (seg1 && seg1->next)
1250 seg1 = seg1->next;
1251
1252 /* merge the old head, seg2, into tail, seg1 */
1253 seg1 = assegment_append_asns (seg1, seg2->as, seg2->length);
1254
1255 /* bypass the merged seg2, and attach any chain after it to
1256 * chain descending from as2's head
1257 */
1258 seg1->next = seg2->next;
1259
1260 /* seg2 is now referenceless and useless*/
1261 assegment_free (seg2);
1262
1263 /* we've now prepended as1's segment chain to as2, merging
1264 * the inbetween AS_SEQUENCE of seg2 in the process
1265 */
1266 aspath_str_update (as2);
718e3744 1267 return as2;
1268 }
1269 else
1270 {
1271 /* AS_SET merge code is needed at here. */
1272 return aspath_merge (as1, as2);
1273 }
fe69a505 1274 /* XXX: Ermmm, what if as1 has multiple segments?? */
1275
718e3744 1276 /* Not reached */
1277}
1278
841f7a57
DO
1279/* Iterate over AS_PATH segments and wipe all occurences of the
1280 * listed AS numbers. Hence some segments may lose some or even
1281 * all data on the way, the operation is implemented as a smarter
1282 * version of aspath_dup(), which allocates memory to hold the new
1283 * data, not the original. The new AS path is returned.
1284 */
1285struct aspath *
1286aspath_filter_exclude (struct aspath * source, struct aspath * exclude_list)
1287{
1288 struct assegment * srcseg, * exclseg, * lastseg;
1289 struct aspath * newpath;
1290
1291 newpath = aspath_new();
1292 lastseg = NULL;
1293
1294 for (srcseg = source->segments; srcseg; srcseg = srcseg->next)
1295 {
1296 unsigned i, y, newlen = 0, done = 0, skip_as;
1297 struct assegment * newseg;
1298
1299 /* Find out, how much ASns are we going to pick from this segment.
1300 * We can't perform filtering right inline, because the size of
1301 * the new segment isn't known at the moment yet.
1302 */
1303 for (i = 0; i < srcseg->length; i++)
1304 {
1305 skip_as = 0;
1306 for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next)
1307 for (y = 0; y < exclseg->length; y++)
1308 if (srcseg->as[i] == exclseg->as[y])
1309 {
1310 skip_as = 1;
1311 // There's no sense in testing the rest of exclusion list, bail out.
1312 break;
1313 }
1314 if (!skip_as)
1315 newlen++;
1316 }
1317 /* newlen is now the number of ASns to copy */
1318 if (!newlen)
1319 continue;
1320
1321 /* Actual copying. Allocate memory and iterate once more, performing filtering. */
1322 newseg = assegment_new (srcseg->type, newlen);
1323 for (i = 0; i < srcseg->length; i++)
1324 {
1325 skip_as = 0;
1326 for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next)
1327 for (y = 0; y < exclseg->length; y++)
1328 if (srcseg->as[i] == exclseg->as[y])
1329 {
1330 skip_as = 1;
1331 break;
1332 }
1333 if (skip_as)
1334 continue;
1335 newseg->as[done++] = srcseg->as[i];
1336 }
1337 /* At his point newlen must be equal to done, and both must be positive. Append
1338 * the filtered segment to the gross result. */
1339 if (!lastseg)
1340 newpath->segments = newseg;
1341 else
1342 lastseg->next = newseg;
1343 lastseg = newseg;
1344 }
1345 aspath_str_update (newpath);
1346 /* We are happy returning even an empty AS_PATH, because the administrator
1347 * might expect this very behaviour. There's a mean to avoid this, if necessary,
1348 * by having a match rule against certain AS_PATH regexps in the route-map index.
1349 */
1350 aspath_free (source);
1351 return newpath;
1352}
1353
718e3744 1354/* Add specified AS to the leftmost of aspath. */
1355static struct aspath *
1356aspath_add_one_as (struct aspath *aspath, as_t asno, u_char type)
1357{
fe69a505 1358 struct assegment *assegment = aspath->segments;
718e3744 1359
1360 /* In case of empty aspath. */
1361 if (assegment == NULL || assegment->length == 0)
1362 {
fe69a505 1363 aspath->segments = assegment_new (type, 1);
1364 aspath->segments->as[0] = asno;
1365
718e3744 1366 if (assegment)
fe69a505 1367 assegment_free (assegment);
718e3744 1368
1369 return aspath;
1370 }
1371
1372 if (assegment->type == type)
fe69a505 1373 aspath->segments = assegment_prepend_asns (aspath->segments, asno, 1);
1374 else
718e3744 1375 {
fe69a505 1376 /* create new segment
1377 * push it onto head of aspath's segment chain
1378 */
718e3744 1379 struct assegment *newsegment;
fe69a505 1380
1381 newsegment = assegment_new (type, 1);
1382 newsegment->as[0] = asno;
1383
1384 newsegment->next = assegment;
1385 aspath->segments = newsegment;
718e3744 1386 }
1387
1388 return aspath;
1389}
1390
1391/* Add specified AS to the leftmost of aspath. */
1392struct aspath *
1393aspath_add_seq (struct aspath *aspath, as_t asno)
1394{
1395 return aspath_add_one_as (aspath, asno, AS_SEQUENCE);
1396}
1397
1398/* Compare leftmost AS value for MED check. If as1's leftmost AS and
1399 as2's leftmost AS is same return 1. */
1400int
ffe11cfb 1401aspath_cmp_left (const struct aspath *aspath1, const struct aspath *aspath2)
718e3744 1402{
ffe11cfb
SH
1403 const struct assegment *seg1 = NULL;
1404 const struct assegment *seg2 = NULL;
718e3744 1405
fe69a505 1406 if (!(aspath1 && aspath2))
1407 return 0;
718e3744 1408
fe69a505 1409 seg1 = aspath1->segments;
1410 seg2 = aspath2->segments;
718e3744 1411
fe69a505 1412 /* find first non-confed segments for each */
1413 while (seg1 && ((seg1->type == AS_CONFED_SEQUENCE)
1414 || (seg1->type == AS_CONFED_SET)))
1415 seg1 = seg1->next;
718e3744 1416
fe69a505 1417 while (seg2 && ((seg2->type == AS_CONFED_SEQUENCE)
1418 || (seg2->type == AS_CONFED_SET)))
1419 seg2 = seg2->next;
718e3744 1420
fe69a505 1421 /* Check as1's */
1422 if (!(seg1 && seg2
1423 && (seg1->type == AS_SEQUENCE) && (seg2->type == AS_SEQUENCE)))
1424 return 0;
1425
1426 if (seg1->as[0] == seg2->as[0])
718e3744 1427 return 1;
1428
1429 return 0;
1430}
1431
0b2aa3a0
PJ
1432/* Truncate an aspath after a number of hops, and put the hops remaining
1433 * at the front of another aspath. Needed for AS4 compat.
1434 *
1435 * Returned aspath is a /new/ aspath, which should either by free'd or
1436 * interned by the caller, as desired.
1437 */
1438struct aspath *
1439aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path)
1440{
1441 struct assegment *seg, *newseg, *prevseg = NULL;
1442 struct aspath *newpath = NULL, *mergedpath;
1443 int hops, cpasns = 0;
1444
1445 if (!aspath)
1446 return NULL;
1447
1448 seg = aspath->segments;
1449
1450 /* CONFEDs should get reconciled too.. */
1451 hops = (aspath_count_hops (aspath) + aspath_count_confeds (aspath))
1452 - aspath_count_hops (as4path);
1453
1454 if (hops < 0)
1455 {
1456 if (BGP_DEBUG (as4, AS4))
1457 zlog_warn ("[AS4] Fewer hops in AS_PATH than NEW_AS_PATH");
1458 /* Something's gone wrong. The RFC says we should now ignore AS4_PATH,
1459 * which is daft behaviour - it contains vital loop-detection
1460 * information which must have been removed from AS_PATH.
1461 */
1462 hops = aspath_count_hops (aspath);
1463 }
1464
1465 if (!hops)
1466 return aspath_dup (as4path);
1467
1468 if ( BGP_DEBUG(as4, AS4))
1469 zlog_debug("[AS4] got AS_PATH %s and AS4_PATH %s synthesizing now",
1470 aspath->str, as4path->str);
1471
1472 while (seg && hops > 0)
1473 {
1474 switch (seg->type)
1475 {
1476 case AS_SET:
1477 case AS_CONFED_SET:
1478 hops--;
1479 cpasns = seg->length;
1480 break;
1481 case AS_CONFED_SEQUENCE:
1482 /* Should never split a confed-sequence, if hop-count
1483 * suggests we must then something's gone wrong somewhere.
1484 *
1485 * Most important goal is to preserve AS_PATHs prime function
1486 * as loop-detector, so we fudge the numbers so that the entire
1487 * confed-sequence is merged in.
1488 */
1489 if (hops < seg->length)
1490 {
1491 if (BGP_DEBUG (as4, AS4))
1492 zlog_debug ("[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls"
1493 " across 2/4 ASN boundary somewhere, broken..");
1494 hops = seg->length;
1495 }
1496 case AS_SEQUENCE:
1497 cpasns = MIN(seg->length, hops);
1498 hops -= seg->length;
1499 }
1500
1501 assert (cpasns <= seg->length);
1502
1503 newseg = assegment_new (seg->type, 0);
1504 newseg = assegment_append_asns (newseg, seg->as, cpasns);
1505
1506 if (!newpath)
1507 {
1508 newpath = aspath_new ();
1509 newpath->segments = newseg;
1510 }
1511 else
1512 prevseg->next = newseg;
1513
1514 prevseg = newseg;
1515 seg = seg->next;
1516 }
1517
1518 /* We may be able to join some segments here, and we must
1519 * do this because... we want normalised aspaths in out hash
1520 * and we do not want to stumble in aspath_put.
1521 */
1522 mergedpath = aspath_merge (newpath, aspath_dup(as4path));
1523 aspath_free (newpath);
1524 mergedpath->segments = assegment_normalise (mergedpath->segments);
1525 aspath_str_update (mergedpath);
1526
1527 if ( BGP_DEBUG(as4, AS4))
1528 zlog_debug ("[AS4] result of synthesizing is %s",
1529 mergedpath->str);
1530
1531 return mergedpath;
1532}
1533
718e3744 1534/* Compare leftmost AS value for MED check. If as1's leftmost AS and
1535 as2's leftmost AS is same return 1. (confederation as-path
1536 only). */
1537int
ffe11cfb 1538aspath_cmp_left_confed (const struct aspath *aspath1, const struct aspath *aspath2)
718e3744 1539{
fe69a505 1540 if (! (aspath1 && aspath2) )
718e3744 1541 return 0;
fe69a505 1542
ad72740e 1543 if ( !(aspath1->segments && aspath2->segments) )
1544 return 0;
1545
fe69a505 1546 if ( (aspath1->segments->type != AS_CONFED_SEQUENCE)
1547 || (aspath2->segments->type != AS_CONFED_SEQUENCE) )
718e3744 1548 return 0;
fe69a505 1549
1550 if (aspath1->segments->as[0] == aspath2->segments->as[0])
718e3744 1551 return 1;
1552
1553 return 0;
1554}
1555
fe69a505 1556/* Delete all leading AS_CONFED_SEQUENCE/SET segments from aspath.
1557 * See RFC3065, 6.1 c1 */
718e3744 1558struct aspath *
1559aspath_delete_confed_seq (struct aspath *aspath)
1560{
fe69a505 1561 struct assegment *seg;
718e3744 1562
fe69a505 1563 if (!(aspath && aspath->segments))
718e3744 1564 return aspath;
1565
fe69a505 1566 seg = aspath->segments;
1567
1568 /* "if the first path segment of the AS_PATH is
1569 * of type AS_CONFED_SEQUENCE,"
1570 */
1571 if (aspath->segments->type != AS_CONFED_SEQUENCE)
1572 return aspath;
718e3744 1573
fe69a505 1574 /* "... that segment and any immediately following segments
1575 * of the type AS_CONFED_SET or AS_CONFED_SEQUENCE are removed
1576 * from the AS_PATH attribute,"
1577 */
1578 while (seg &&
1579 (seg->type == AS_CONFED_SEQUENCE || seg->type == AS_CONFED_SET))
718e3744 1580 {
fe69a505 1581 aspath->segments = seg->next;
1582 assegment_free (seg);
1583 seg = aspath->segments;
718e3744 1584 }
fe69a505 1585 aspath_str_update (aspath);
718e3744 1586 return aspath;
1587}
1588
1589/* Add new AS number to the leftmost part of the aspath as
1590 AS_CONFED_SEQUENCE. */
1591struct aspath*
1592aspath_add_confed_seq (struct aspath *aspath, as_t asno)
1593{
1594 return aspath_add_one_as (aspath, asno, AS_CONFED_SEQUENCE);
1595}
1596
1597/* Add new as value to as path structure. */
94f2b392 1598static void
718e3744 1599aspath_as_add (struct aspath *as, as_t asno)
1600{
fe69a505 1601 struct assegment *seg = as->segments;
718e3744 1602
fe69a505 1603 if (!seg)
1604 return;
1605
93c1749c
AS
1606 /* Last segment search procedure. */
1607 while (seg->next)
1608 seg = seg->next;
1609
fe69a505 1610 assegment_append_asns (seg, &asno, 1);
718e3744 1611}
1612
1613/* Add new as segment to the as path. */
94f2b392 1614static void
718e3744 1615aspath_segment_add (struct aspath *as, int type)
1616{
fe69a505 1617 struct assegment *seg = as->segments;
1618 struct assegment *new = assegment_new (type, 0);
718e3744 1619
93c1749c
AS
1620 if (seg)
1621 {
1622 while (seg->next)
1623 seg = seg->next;
1624 seg->next = new;
1625 }
718e3744 1626 else
93c1749c 1627 as->segments = new;
718e3744 1628}
1629
1630struct aspath *
94f2b392 1631aspath_empty (void)
718e3744 1632{
ab005298 1633 return aspath_parse (NULL, 0, 1); /* 32Bit ;-) */
718e3744 1634}
1635
1636struct aspath *
94f2b392 1637aspath_empty_get (void)
718e3744 1638{
1639 struct aspath *aspath;
1640
1641 aspath = aspath_new ();
1642 aspath->str = aspath_make_str_count (aspath);
1643 return aspath;
1644}
1645
1646unsigned long
fe69a505 1647aspath_count (void)
718e3744 1648{
1649 return ashash->count;
1650}
1651\f
1652/*
1653 Theoretically, one as path can have:
1654
1655 One BGP packet size should be less than 4096.
1656 One BGP attribute size should be less than 4096 - BGP header size.
1657 One BGP aspath size should be less than 4096 - BGP header size -
1658 BGP mandantry attribute size.
1659*/
1660
1661/* AS path string lexical token enum. */
1662enum as_token
1663{
1664 as_token_asval,
1665 as_token_set_start,
1666 as_token_set_end,
fe69a505 1667 as_token_confed_seq_start,
1668 as_token_confed_seq_end,
1669 as_token_confed_set_start,
1670 as_token_confed_set_end,
718e3744 1671 as_token_unknown
1672};
1673
1674/* Return next token and point for string parse. */
94f2b392 1675static const char *
0b2aa3a0 1676aspath_gettoken (const char *buf, enum as_token *token, u_long *asno)
718e3744 1677{
fd79ac91 1678 const char *p = buf;
718e3744 1679
fe69a505 1680 /* Skip seperators (space for sequences, ',' for sets). */
1681 while (isspace ((int) *p) || *p == ',')
718e3744 1682 p++;
1683
1684 /* Check the end of the string and type specify characters
1685 (e.g. {}()). */
1686 switch (*p)
1687 {
1688 case '\0':
1689 return NULL;
718e3744 1690 case '{':
1691 *token = as_token_set_start;
1692 p++;
1693 return p;
718e3744 1694 case '}':
1695 *token = as_token_set_end;
1696 p++;
1697 return p;
718e3744 1698 case '(':
fe69a505 1699 *token = as_token_confed_seq_start;
718e3744 1700 p++;
1701 return p;
718e3744 1702 case ')':
fe69a505 1703 *token = as_token_confed_seq_end;
1704 p++;
1705 return p;
fe69a505 1706 case '[':
1707 *token = as_token_confed_set_start;
1708 p++;
1709 return p;
fe69a505 1710 case ']':
1711 *token = as_token_confed_set_end;
718e3744 1712 p++;
1713 return p;
718e3744 1714 }
1715
1716 /* Check actual AS value. */
1717 if (isdigit ((int) *p))
1718 {
10819ece 1719 as_t asval;
0b2aa3a0 1720
718e3744 1721 *token = as_token_asval;
1722 asval = (*p - '0');
1723 p++;
0b2aa3a0 1724
718e3744 1725 while (isdigit ((int) *p))
0b2aa3a0
PJ
1726 {
1727 asval *= 10;
1728 asval += (*p - '0');
1729 p++;
1730 }
718e3744 1731 *asno = asval;
1732 return p;
1733 }
1734
1735 /* There is no match then return unknown token. */
1736 *token = as_token_unknown;
1737 return p++;
1738}
1739
1740struct aspath *
fd79ac91 1741aspath_str2aspath (const char *str)
718e3744 1742{
3fff6ffc 1743 enum as_token token = as_token_unknown;
718e3744 1744 u_short as_type;
0b2aa3a0 1745 u_long asno = 0;
718e3744 1746 struct aspath *aspath;
1747 int needtype;
1748
1749 aspath = aspath_new ();
1750
1751 /* We start default type as AS_SEQUENCE. */
1752 as_type = AS_SEQUENCE;
1753 needtype = 1;
1754
1755 while ((str = aspath_gettoken (str, &token, &asno)) != NULL)
1756 {
1757 switch (token)
1758 {
1759 case as_token_asval:
1760 if (needtype)
1761 {
1762 aspath_segment_add (aspath, as_type);
1763 needtype = 0;
1764 }
1765 aspath_as_add (aspath, asno);
1766 break;
1767 case as_token_set_start:
1768 as_type = AS_SET;
1769 aspath_segment_add (aspath, as_type);
1770 needtype = 0;
1771 break;
1772 case as_token_set_end:
1773 as_type = AS_SEQUENCE;
1774 needtype = 1;
1775 break;
fe69a505 1776 case as_token_confed_seq_start:
718e3744 1777 as_type = AS_CONFED_SEQUENCE;
1778 aspath_segment_add (aspath, as_type);
1779 needtype = 0;
1780 break;
fe69a505 1781 case as_token_confed_seq_end:
1782 as_type = AS_SEQUENCE;
1783 needtype = 1;
1784 break;
1785 case as_token_confed_set_start:
1786 as_type = AS_CONFED_SET;
1787 aspath_segment_add (aspath, as_type);
1788 needtype = 0;
1789 break;
1790 case as_token_confed_set_end:
718e3744 1791 as_type = AS_SEQUENCE;
1792 needtype = 1;
1793 break;
1794 case as_token_unknown:
1795 default:
fe69a505 1796 aspath_free (aspath);
718e3744 1797 return NULL;
718e3744 1798 }
1799 }
1800
1801 aspath->str = aspath_make_str_count (aspath);
1802
1803 return aspath;
1804}
1805\f
1806/* Make hash value by raw aspath data. */
1807unsigned int
923de654 1808aspath_key_make (void *p)
718e3744 1809{
923de654 1810 struct aspath * aspath = (struct aspath *) p;
718e3744 1811 unsigned int key = 0;
718e3744 1812
0b2aa3a0
PJ
1813 if (!aspath->str)
1814 aspath_str_update (aspath);
1815
1816 key = jhash (aspath->str, strlen(aspath->str), 2334325);
718e3744 1817
1818 return key;
1819}
1820
1821/* If two aspath have same value then return 1 else return 0 */
96450faf 1822int
ffe11cfb 1823aspath_cmp (const void *arg1, const void *arg2)
718e3744 1824{
aea339f7
DO
1825 const struct assegment *seg1 = ((const struct aspath *)arg1)->segments;
1826 const struct assegment *seg2 = ((const struct aspath *)arg2)->segments;
fe69a505 1827
1828 while (seg1 || seg2)
1829 {
1830 int i;
1831 if ((!seg1 && seg2) || (seg1 && !seg2))
1832 return 0;
1833 if (seg1->type != seg2->type)
1834 return 0;
1835 if (seg1->length != seg2->length)
1836 return 0;
1837 for (i = 0; i < seg1->length; i++)
1838 if (seg1->as[i] != seg2->as[i])
1839 return 0;
1840 seg1 = seg1->next;
1841 seg2 = seg2->next;
1842 }
1843 return 1;
718e3744 1844}
1845
1846/* AS path hash initialize. */
1847void
94f2b392 1848aspath_init (void)
718e3744 1849{
1850 ashash = hash_create_size (32767, aspath_key_make, aspath_cmp);
1851}
8fdc32ab 1852
1853void
1854aspath_finish (void)
1855{
1856 hash_free (ashash);
228da428 1857 ashash = NULL;
8fdc32ab 1858
1859 if (snmp_stream)
1860 stream_free (snmp_stream);
1861}
718e3744 1862\f
1863/* return and as path value */
1864const char *
1865aspath_print (struct aspath *as)
1866{
fe69a505 1867 return (as ? as->str : NULL);
718e3744 1868}
1869
1870/* Printing functions */
841f7a57
DO
1871/* Feed the AS_PATH to the vty; the suffix string follows it only in case
1872 * AS_PATH wasn't empty.
1873 */
718e3744 1874void
841f7a57 1875aspath_print_vty (struct vty *vty, const char *format, struct aspath *as, const char * suffix)
718e3744 1876{
b2518c1e
PJ
1877 assert (format);
1878 vty_out (vty, format, as->str);
841f7a57
DO
1879 if (strlen (as->str) && strlen (suffix))
1880 vty_out (vty, "%s", suffix);
718e3744 1881}
1882
94f2b392 1883static void
718e3744 1884aspath_show_all_iterator (struct hash_backet *backet, struct vty *vty)
1885{
1886 struct aspath *as;
1887
1888 as = (struct aspath *) backet->data;
1889
efa9f830 1890 vty_out (vty, "[%p:%u] (%ld) ", backet, backet->key, as->refcnt);
718e3744 1891 vty_out (vty, "%s%s", as->str, VTY_NEWLINE);
1892}
1893
1894/* Print all aspath and hash information. This function is used from
1895 `show ip bgp paths' command. */
1896void
1897aspath_print_all_vty (struct vty *vty)
1898{
1899 hash_iterate (ashash,
1900 (void (*) (struct hash_backet *, void *))
1901 aspath_show_all_iterator,
1902 vty);
1903}