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