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