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