1 /* AS path management routines.
2 Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro
3 Copyright (C) 2005 Sun Microsystems, Inc.
5 This file is part of GNU Zebra.
7 GNU Zebra is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 GNU Zebra is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Zebra; see the file COPYING. If not, write to the Free
19 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
33 #include "bgpd/bgpd.h"
34 #include "bgpd/bgp_aspath.h"
35 #include "bgpd/bgp_debug.h"
36 #include "bgpd/bgp_attr.h"
38 /* Attr. Flags and Attr. Type Code. */
39 #define AS_HEADER_SIZE 2
41 /* Now FOUR octets are used for AS value. */
42 #define AS_VALUE_SIZE sizeof (as_t)
43 /* This is the old one */
44 #define AS16_VALUE_SIZE sizeof (as16_t)
46 /* Maximum protocol segment length value */
47 #define AS_SEGMENT_MAX 255
49 /* The following length and size macros relate specifically to Quagga's
50 * internal representation of AS-Segments, not per se to the on-wire
51 * sizes and lengths. At present (200508) they sort of match, however
52 * the ONLY functions which should now about the on-wire syntax are
53 * aspath_put, assegment_put and assegment_parse.
55 * aspath_put returns bytes written, the only definitive record of
56 * size of wire-format attribute..
59 /* Calculated size in bytes of ASN segment data to hold N ASN's */
60 #define ASSEGMENT_DATA_SIZE(N,S) \
61 ((N) * ( (S) ? AS_VALUE_SIZE : AS16_VALUE_SIZE) )
63 /* Calculated size of segment struct to hold N ASN's */
64 #define ASSEGMENT_SIZE(N,S) (AS_HEADER_SIZE + ASSEGMENT_DATA_SIZE (N,S))
66 /* AS segment octet length. */
67 #define ASSEGMENT_LEN(X,S) ASSEGMENT_SIZE((X)->length,S)
69 /* AS_SEQUENCE segments can be packed together */
70 /* Can the types of X and Y be considered for packing? */
71 #define ASSEGMENT_TYPES_PACKABLE(X,Y) \
72 ( ((X)->type == (Y)->type) \
73 && ((X)->type == AS_SEQUENCE))
74 /* Types and length of X,Y suitable for packing? */
75 #define ASSEGMENTS_PACKABLE(X,Y) \
76 ( ASSEGMENT_TYPES_PACKABLE( (X), (Y)) \
77 && ( ((X)->length + (Y)->length) <= AS_SEGMENT_MAX ) )
79 /* As segment header - the on-wire representation
80 * NOT the internal representation!
82 struct assegment_header
88 /* Hash for aspath. This is the top level structure of AS path. */
91 /* Stream for SNMP. See aspath_snmp_pathseg */
92 static struct stream
*snmp_stream
;
95 assegment_data_new (int num
)
97 return (XCALLOC (MTYPE_AS_SEG_DATA
, ASSEGMENT_DATA_SIZE (num
, 1)));
101 assegment_data_free (as_t
*asdata
)
103 XFREE (MTYPE_AS_SEG_DATA
,asdata
);
106 /* Get a new segment. Note that 0 is an allowed length,
107 * and will result in a segment with no allocated data segment.
108 * the caller should immediately assign data to the segment, as the segment
109 * otherwise is not generally valid
111 static struct assegment
*
112 assegment_new (u_char type
, u_short length
)
114 struct assegment
*new;
116 new = XCALLOC (MTYPE_AS_SEG
, sizeof (struct assegment
));
119 new->as
= assegment_data_new (length
);
121 new->length
= length
;
128 assegment_free (struct assegment
*seg
)
134 XFREE (MTYPE_AS_SEG_DATA
, seg
->as
);
135 memset (seg
, 0xfe, sizeof(struct assegment
));
136 XFREE (MTYPE_AS_SEG
, seg
);
141 /* free entire chain of segments */
143 assegment_free_all (struct assegment
*seg
)
145 struct assegment
*prev
;
151 assegment_free (prev
);
155 /* Duplicate just the given assegment and its data */
156 static struct assegment
*
157 assegment_dup (struct assegment
*seg
)
159 struct assegment
*new;
161 new = assegment_new (seg
->type
, seg
->length
);
162 memcpy (new->as
, seg
->as
, ASSEGMENT_DATA_SIZE (new->length
, 1) );
167 /* Duplicate entire chain of assegments, return the head */
168 static struct assegment
*
169 assegment_dup_all (struct assegment
*seg
)
171 struct assegment
*new = NULL
;
172 struct assegment
*head
= NULL
;
178 new->next
= assegment_dup (seg
);
182 head
= new = assegment_dup (seg
);
189 /* prepend the as number to given segment, given num of times */
190 static struct assegment
*
191 assegment_prepend_asns (struct assegment
*seg
, as_t asnum
, int num
)
198 if (num
>= AS_SEGMENT_MAX
)
199 return seg
; /* we don't do huge prepends */
201 newas
= assegment_data_new (seg
->length
+ num
);
206 for (i
= 0; i
< num
; i
++)
209 memcpy (newas
+ num
, seg
->as
, ASSEGMENT_DATA_SIZE (seg
->length
, 1));
210 XFREE (MTYPE_AS_SEG_DATA
, seg
->as
);
216 assegment_free_all (seg
);
220 /* append given array of as numbers to the segment */
221 static struct assegment
*
222 assegment_append_asns (struct assegment
*seg
, as_t
*asnos
, int num
)
226 newas
= XREALLOC (MTYPE_AS_SEG_DATA
, seg
->as
,
227 ASSEGMENT_DATA_SIZE (seg
->length
+ num
, 1));
232 memcpy (seg
->as
+ seg
->length
, asnos
, ASSEGMENT_DATA_SIZE(num
, 1));
237 assegment_free_all (seg
);
242 int_cmp (const void *p1
, const void *p2
)
244 const as_t
*as1
= p1
;
245 const as_t
*as2
= p2
;
247 return (*as1
== *as2
)
248 ? 0 : ( (*as1
> *as2
) ? 1 : -1);
251 /* normalise the segment.
252 * In particular, merge runs of AS_SEQUENCEs into one segment
253 * Internally, we do not care about the wire segment length limit, and
254 * we want each distinct AS_PATHs to have the exact same internal
255 * representation - eg, so that our hashing actually works..
257 static struct assegment
*
258 assegment_normalise (struct assegment
*head
)
260 struct assegment
*seg
= head
, *pin
;
261 struct assegment
*tmp
;
270 /* Sort values SET segments, for determinism in paths to aid
271 * creation of hash values / path comparisons
272 * and because it helps other lesser implementations ;)
274 if (seg
->type
== AS_SET
|| seg
->type
== AS_CONFED_SET
)
279 qsort (seg
->as
, seg
->length
, sizeof(as_t
), int_cmp
);
282 for (i
=1; i
< seg
->length
; i
++)
284 if (seg
->as
[tail
] == seg
->as
[i
])
289 seg
->as
[tail
] = seg
->as
[i
];
291 /* seg->length can be 0.. */
293 seg
->length
= tail
+ 1;
296 /* read ahead from the current, pinned segment while the segments
297 * are packable/mergeable. Append all following packable segments
298 * to the segment we have pinned and remove these appended
301 while (pin
->next
&& ASSEGMENT_TYPES_PACKABLE(pin
, pin
->next
))
306 /* append the next sequence to the pinned sequence */
307 pin
= assegment_append_asns (pin
, seg
->as
, seg
->length
);
309 /* bypass the next sequence */
310 pin
->next
= seg
->next
;
312 /* get rid of the now referenceless segment */
313 assegment_free (tmp
);
322 static struct aspath
*
325 struct aspath
*aspath
;
327 aspath
= XMALLOC (MTYPE_AS_PATH
, sizeof (struct aspath
));
328 memset (aspath
, 0, sizeof (struct aspath
));
332 /* Free AS path structure. */
334 aspath_free (struct aspath
*aspath
)
338 if (aspath
->segments
)
339 assegment_free_all (aspath
->segments
);
341 XFREE (MTYPE_AS_STR
, aspath
->str
);
342 XFREE (MTYPE_AS_PATH
, aspath
);
345 /* Unintern aspath from AS path bucket. */
347 aspath_unintern (struct aspath
*aspath
)
354 if (aspath
->refcnt
== 0)
356 /* This aspath must exist in aspath hash table. */
357 ret
= hash_release (ashash
, aspath
);
358 assert (ret
!= NULL
);
359 aspath_free (aspath
);
363 /* Return the start or end delimiters for a particular Segment type */
364 #define AS_SEG_START 0
367 aspath_delimiter_char (u_char type
, u_char which
)
375 } aspath_delim_char
[] =
377 { AS_SET
, '{', '}' },
378 { AS_CONFED_SET
, '[', ']' },
379 { AS_CONFED_SEQUENCE
, '(', ')' },
383 for (i
= 0; aspath_delim_char
[i
].type
!= 0; i
++)
385 if (aspath_delim_char
[i
].type
== type
)
387 if (which
== AS_SEG_START
)
388 return aspath_delim_char
[i
].start
;
389 else if (which
== AS_SEG_END
)
390 return aspath_delim_char
[i
].end
;
396 /* countup asns from this segment and index onward */
398 assegment_count_asns (struct assegment
*seg
, int from
)
404 count
+= seg
->length
;
407 count
+= (seg
->length
- from
);
416 aspath_count_confeds (struct aspath
*aspath
)
419 struct assegment
*seg
= aspath
->segments
;
423 if (seg
->type
== AS_CONFED_SEQUENCE
)
424 count
+= seg
->length
;
425 else if (seg
->type
== AS_CONFED_SET
)
434 aspath_count_hops (struct aspath
*aspath
)
437 struct assegment
*seg
= aspath
->segments
;
441 if (seg
->type
== AS_SEQUENCE
)
442 count
+= seg
->length
;
443 else if (seg
->type
== AS_SET
)
451 /* Estimate size aspath /might/ take if encoded into an
454 * This is a quick estimate, not definitive! aspath_put()
455 * may return a different number!!
458 aspath_size (struct aspath
*aspath
)
461 struct assegment
*seg
= aspath
->segments
;
465 size
+= ASSEGMENT_SIZE(seg
->length
, 1);
471 /* Return highest public ASN in path */
473 aspath_highest (struct aspath
*aspath
)
475 struct assegment
*seg
= aspath
->segments
;
481 for (i
= 0; i
< seg
->length
; i
++)
482 if (seg
->as
[i
] > highest
483 && (seg
->as
[i
] < BGP_PRIVATE_AS_MIN
484 || seg
->as
[i
] > BGP_PRIVATE_AS_MAX
))
485 highest
= seg
->as
[i
];
491 /* Return 1 if there are any 4-byte ASes in the path */
493 aspath_has_as4 (struct aspath
*aspath
)
495 struct assegment
*seg
= aspath
->segments
;
500 for (i
= 0; i
< seg
->length
; i
++)
501 if (seg
->as
[i
] > BGP_AS_MAX
)
508 /* Return number of as numbers in in path */
510 aspath_count_numas (struct aspath
*aspath
)
512 struct assegment
*seg
= aspath
->segments
;
524 /* Convert aspath structure to string expression. */
526 aspath_make_str_count (struct aspath
*as
)
528 struct assegment
*seg
;
536 str_buf
= XMALLOC (MTYPE_AS_STR
, 1);
543 /* ASN takes 5 chars at least, plus seperator, see below.
544 * If there is one differing segment type, we need an additional
545 * 2 chars for segment delimiters, and the final '\0'.
546 * Hopefully this is large enough to avoid hitting the realloc
547 * code below for most common sequences.
549 * With 32bit ASNs, this range will increase, but only worth changing
550 * once there are significant numbers of ASN >= 100000
552 #define ASN_STR_LEN (5 + 1)
553 str_size
= MAX (assegment_count_asns (seg
, 0) * ASN_STR_LEN
+ 2 + 1,
554 ASPATH_STR_DEFAULT_LEN
);
555 str_buf
= XMALLOC (MTYPE_AS_STR
, str_size
);
562 /* Check AS type validity. Set seperator for segment */
570 case AS_CONFED_SEQUENCE
:
574 XFREE (MTYPE_AS_STR
, str_buf
);
578 /* We might need to increase str_buf, particularly if path has
579 * differing segments types, our initial guesstimate above will
580 * have been wrong. need 5 chars for ASN, a seperator each and
581 * potentially two segment delimiters, plus a space between each
582 * segment and trailing zero.
584 * This may need to revised if/when significant numbers of
585 * ASNs >= 100000 are assigned and in-use on the internet...
587 #define SEGMENT_STR_LEN(X) (((X)->length * ASN_STR_LEN) + 2 + 1 + 1)
588 if ( (len
+ SEGMENT_STR_LEN(seg
)) > str_size
)
590 str_size
= len
+ SEGMENT_STR_LEN(seg
);
591 str_buf
= XREALLOC (MTYPE_AS_STR
, str_buf
, str_size
);
594 #undef SEGMENT_STR_LEN
596 if (seg
->type
!= AS_SEQUENCE
)
597 len
+= snprintf (str_buf
+ len
, str_size
- len
,
599 aspath_delimiter_char (seg
->type
, AS_SEG_START
));
601 /* write out the ASNs, with their seperators, bar the last one*/
602 for (i
= 0; i
< seg
->length
; i
++)
604 len
+= snprintf (str_buf
+ len
, str_size
- len
, "%u", seg
->as
[i
]);
606 if (i
< (seg
->length
- 1))
607 len
+= snprintf (str_buf
+ len
, str_size
- len
, "%c", seperator
);
610 if (seg
->type
!= AS_SEQUENCE
)
611 len
+= snprintf (str_buf
+ len
, str_size
- len
, "%c",
612 aspath_delimiter_char (seg
->type
, AS_SEG_END
));
614 len
+= snprintf (str_buf
+ len
, str_size
- len
, " ");
619 assert (len
< str_size
);
627 aspath_str_update (struct aspath
*as
)
630 XFREE (MTYPE_AS_STR
, as
->str
);
631 as
->str
= aspath_make_str_count (as
);
634 /* Intern allocated AS path. */
636 aspath_intern (struct aspath
*aspath
)
640 /* Assert this AS path structure is not interned. */
641 assert (aspath
->refcnt
== 0);
643 /* Check AS path hash. */
644 find
= hash_get (ashash
, aspath
, hash_alloc_intern
);
647 aspath_free (aspath
);
652 find
->str
= aspath_make_str_count (find
);
657 /* Duplicate aspath structure. Created same aspath structure but
658 reference count and AS path string is cleared. */
660 aspath_dup (struct aspath
*aspath
)
664 new = XCALLOC (MTYPE_AS_PATH
, sizeof (struct aspath
));
666 if (aspath
->segments
)
667 new->segments
= assegment_dup_all (aspath
->segments
);
669 new->segments
= NULL
;
671 new->str
= aspath_make_str_count (aspath
);
677 aspath_hash_alloc (void *arg
)
679 struct aspath
*aspath
;
681 /* New aspath structure is needed. */
682 aspath
= aspath_dup (arg
);
684 /* Malformed AS path value. */
687 aspath_free (aspath
);
694 /* parse as-segment byte stream in struct assegment */
695 static struct assegment
*
696 assegments_parse (struct stream
*s
, size_t length
, int use32bit
)
698 struct assegment_header segh
;
699 struct assegment
*seg
, *prev
= NULL
, *head
= NULL
;
702 /* empty aspath (ie iBGP or somesuch) */
706 if (BGP_DEBUG (as4
, AS4_SEGMENT
))
707 zlog_debug ("[AS4SEG] Parse aspath segment: got total byte length %lu",
708 (unsigned long) length
);
710 if ( (STREAM_READABLE(s
) < length
)
711 || (STREAM_READABLE(s
) < AS_HEADER_SIZE
)
712 || (length
% AS16_VALUE_SIZE
))
715 while ( (STREAM_READABLE(s
) > AS_HEADER_SIZE
)
721 /* softly softly, get the header first on its own */
722 segh
.type
= stream_getc (s
);
723 segh
.length
= stream_getc (s
);
725 seg_size
= ASSEGMENT_SIZE(segh
.length
, use32bit
);
727 if (BGP_DEBUG (as4
, AS4_SEGMENT
))
728 zlog_debug ("[AS4SEG] Parse aspath segment: got type %d, length %d",
729 segh
.type
, segh
.length
);
732 if ( ((bytes
+ seg_size
) > length
)
733 /* 1771bis 4.3b: seg length contains one or more */
734 || (segh
.length
== 0)
735 /* Paranoia in case someone changes type of segment length */
736 || ((sizeof segh
.length
> 1) && (segh
.length
> AS_SEGMENT_MAX
)) )
739 assegment_free_all (head
);
743 /* now its safe to trust lengths */
744 seg
= assegment_new (segh
.type
, segh
.length
);
748 else /* it's the first segment */
751 for (i
= 0; i
< segh
.length
; i
++)
752 seg
->as
[i
] = (use32bit
) ? stream_getl (s
) : stream_getw (s
);
756 if (BGP_DEBUG (as4
, AS4_SEGMENT
))
757 zlog_debug ("[AS4SEG] Parse aspath segment: Bytes now: %lu",
758 (unsigned long) bytes
);
763 return assegment_normalise (head
);
766 /* AS path parse function. pnt is a pointer to byte stream and length
767 is length of byte stream. If there is same AS path in the the AS
768 path hash then return it else make new AS path structure. */
770 aspath_parse (struct stream
*s
, size_t length
, int use32bit
)
775 /* If length is odd it's malformed AS path. */
776 /* Nit-picking: if (use32bit == 0) it is malformed if odd,
777 * otherwise its malformed when length is larger than 2 and (length-2)
778 * is not dividable by 4.
779 * But... this time we're lazy
781 if (length
% AS16_VALUE_SIZE
)
784 memset (&as
, 0, sizeof (struct aspath
));
785 as
.segments
= assegments_parse (s
, length
, use32bit
);
787 /* If already same aspath exist then return it. */
788 find
= hash_get (ashash
, &as
, aspath_hash_alloc
);
790 /* aspath_hash_alloc dupes segments too. that probably could be
793 assegment_free_all (as
.segments
);
795 XFREE (MTYPE_AS_STR
, as
.str
);
805 assegment_data_put (struct stream
*s
, as_t
*as
, int num
, int use32bit
)
808 assert (num
<= AS_SEGMENT_MAX
);
810 for (i
= 0; i
< num
; i
++)
812 stream_putl (s
, as
[i
]);
815 if ( as
[i
] <= BGP_AS_MAX
)
816 stream_putw(s
, as
[i
]);
818 stream_putw(s
, BGP_AS_TRANS
);
823 assegment_header_put (struct stream
*s
, u_char type
, int length
)
826 assert (length
<= AS_SEGMENT_MAX
);
827 stream_putc (s
, type
);
828 lenp
= stream_get_endp (s
);
829 stream_putc (s
, length
);
833 /* write aspath data to stream */
835 aspath_put (struct stream
*s
, struct aspath
*as
, int use32bit
)
837 struct assegment
*seg
= as
->segments
;
840 if (!seg
|| seg
->length
== 0)
846 * Hey, what do we do when we have > STREAM_WRITABLE(s) here?
847 * At the moment, we would write out a partial aspath, and our peer
848 * will complain and drop the session :-/
850 * The general assumption here is that many things tested will
851 * never happen. And, in real live, up to now, they have not.
853 while (seg
&& (ASSEGMENT_LEN(seg
, use32bit
) <= STREAM_WRITEABLE(s
)))
855 struct assegment
*next
= seg
->next
;
860 /* Overlength segments have to be split up */
861 while ( (seg
->length
- written
) > AS_SEGMENT_MAX
)
863 assegment_header_put (s
, seg
->type
, AS_SEGMENT_MAX
);
864 assegment_data_put (s
, seg
->as
, AS_SEGMENT_MAX
, use32bit
);
865 written
+= AS_SEGMENT_MAX
;
866 bytes
+= ASSEGMENT_SIZE (written
, use32bit
);
869 /* write the final segment, probably is also the first */
870 lenp
= assegment_header_put (s
, seg
->type
, seg
->length
- written
);
871 assegment_data_put (s
, (seg
->as
+ written
), seg
->length
- written
,
874 /* Sequence-type segments can be 'packed' together
875 * Case of a segment which was overlength and split up
876 * will be missed here, but that doesn't matter.
878 while (next
&& ASSEGMENTS_PACKABLE (seg
, next
))
880 /* NB: We should never normally get here given we
881 * normalise aspath data when parse them. However, better
882 * safe than sorry. We potentially could call
883 * assegment_normalise here instead, but it's cheaper and
884 * easier to do it on the fly here rather than go through
885 * the segment list twice every time we write out
889 /* Next segment's data can fit in this one */
890 assegment_data_put (s
, next
->as
, next
->length
, use32bit
);
892 /* update the length of the segment header */
893 stream_putc_at (s
, lenp
, seg
->length
- written
+ next
->length
);
894 asns_packed
+= next
->length
;
899 bytes
+= ASSEGMENT_SIZE (seg
->length
- written
+ asns_packed
,
907 /* This is for SNMP BGP4PATHATTRASPATHSEGMENT
908 * We have no way to manage the storage, so we use a static stream
909 * wrapper around aspath_put.
912 aspath_snmp_pathseg (struct aspath
*as
, size_t *varlen
)
914 #define SNMP_PATHSEG_MAX 1024
917 snmp_stream
= stream_new (SNMP_PATHSEG_MAX
);
919 stream_reset (snmp_stream
);
926 aspath_put (snmp_stream
, as
, 0); /* use 16 bit for now here */
928 *varlen
= stream_get_endp (snmp_stream
);
929 return stream_pnt(snmp_stream
);
932 #define min(A,B) ((A) < (B) ? (A) : (B))
934 static struct assegment
*
935 aspath_aggregate_as_set_add (struct aspath
*aspath
, struct assegment
*asset
,
940 /* If this is first AS set member, create new as-set segment. */
943 asset
= assegment_new (AS_SET
, 1);
944 if (! aspath
->segments
)
945 aspath
->segments
= asset
;
948 struct assegment
*seg
= aspath
->segments
;
953 asset
->type
= AS_SET
;
959 /* Check this AS value already exists or not. */
960 for (i
= 0; i
< asset
->length
; i
++)
961 if (asset
->as
[i
] == as
)
965 asset
->as
= XREALLOC (MTYPE_AS_SEG_DATA
, asset
->as
,
966 asset
->length
* AS_VALUE_SIZE
);
967 asset
->as
[asset
->length
- 1] = as
;
974 /* Modify as1 using as2 for aggregation. */
976 aspath_aggregate (struct aspath
*as1
, struct aspath
*as2
)
982 struct assegment
*seg1
= as1
->segments
;
983 struct assegment
*seg2
= as2
->segments
;
984 struct aspath
*aspath
= NULL
;
985 struct assegment
*asset
;
986 struct assegment
*prevseg
= NULL
;
993 /* First of all check common leading sequence. */
996 /* Check segment type. */
997 if (seg1
->type
!= seg2
->type
)
1000 /* Minimum segment length. */
1001 minlen
= min (seg1
->length
, seg2
->length
);
1003 for (match
= 0; match
< minlen
; match
++)
1004 if (seg1
->as
[match
] != seg2
->as
[match
])
1009 struct assegment
*seg
= assegment_new (seg1
->type
, 0);
1011 seg
= assegment_append_asns (seg
, seg1
->as
, match
);
1015 aspath
= aspath_new ();
1016 aspath
->segments
= seg
;
1019 prevseg
->next
= seg
;
1024 if (match
!= minlen
|| match
!= seg1
->length
1025 || seg1
->length
!= seg2
->length
)
1033 aspath
= aspath_new();
1035 /* Make as-set using rest of all information. */
1039 for (i
= from
; i
< seg1
->length
; i
++)
1040 asset
= aspath_aggregate_as_set_add (aspath
, asset
, seg1
->as
[i
]);
1049 for (i
= from
; i
< seg2
->length
; i
++)
1050 asset
= aspath_aggregate_as_set_add (aspath
, asset
, seg2
->as
[i
]);
1056 assegment_normalise (aspath
->segments
);
1057 aspath_str_update (aspath
);
1061 /* When a BGP router receives an UPDATE with an MP_REACH_NLRI
1062 attribute, check the leftmost AS number in the AS_PATH attribute is
1063 or not the peer's AS number. */
1065 aspath_firstas_check (struct aspath
*aspath
, as_t asno
)
1067 if ( (aspath
== NULL
) || (aspath
->segments
== NULL
) )
1070 if (aspath
->segments
1071 && (aspath
->segments
->type
== AS_SEQUENCE
)
1072 && (aspath
->segments
->as
[0] == asno
))
1078 /* AS path loop check. If aspath contains asno then return >= 1. */
1080 aspath_loop_check (struct aspath
*aspath
, as_t asno
)
1082 struct assegment
*seg
;
1085 if ( (aspath
== NULL
) || (aspath
->segments
== NULL
) )
1088 seg
= aspath
->segments
;
1094 for (i
= 0; i
< seg
->length
; i
++)
1095 if (seg
->as
[i
] == asno
)
1103 /* When all of AS path is private AS return 1. */
1105 aspath_private_as_check (struct aspath
*aspath
)
1107 struct assegment
*seg
;
1109 if ( !(aspath
&& aspath
->segments
) )
1112 seg
= aspath
->segments
;
1118 for (i
= 0; i
< seg
->length
; i
++)
1120 if ( (seg
->as
[i
] < BGP_PRIVATE_AS_MIN
)
1121 || (seg
->as
[i
] > BGP_PRIVATE_AS_MAX
) )
1129 /* Merge as1 to as2. as2 should be uninterned aspath. */
1130 static struct aspath
*
1131 aspath_merge (struct aspath
*as1
, struct aspath
*as2
)
1133 struct assegment
*last
, *new;
1138 last
= new = assegment_dup_all (as1
->segments
);
1140 /* find the last valid segment */
1141 while (last
&& last
->next
)
1144 last
->next
= as2
->segments
;
1145 as2
->segments
= new;
1146 aspath_str_update (as2
);
1150 /* Prepend as1 to as2. as2 should be uninterned aspath. */
1152 aspath_prepend (struct aspath
*as1
, struct aspath
*as2
)
1154 struct assegment
*seg1
;
1155 struct assegment
*seg2
;
1160 seg1
= as1
->segments
;
1161 seg2
= as2
->segments
;
1163 /* If as2 is empty, only need to dupe as1's chain onto as2 */
1166 as2
->segments
= assegment_dup_all (as1
->segments
);
1167 aspath_str_update (as2
);
1171 /* If as1 is empty AS, no prepending to do. */
1175 /* find the tail as1's segment chain. */
1176 while (seg1
&& seg1
->next
)
1179 /* Compare last segment type of as1 and first segment type of as2. */
1180 if (seg1
->type
!= seg2
->type
)
1181 return aspath_merge (as1
, as2
);
1183 if (seg1
->type
== AS_SEQUENCE
)
1185 /* We have two chains of segments, as1->segments and seg2,
1186 * and we have to attach them together, merging the attaching
1187 * segments together into one.
1189 * 1. dupe as1->segments onto head of as2
1190 * 2. merge seg2's asns onto last segment of this new chain
1191 * 3. attach chain after seg2
1194 /* dupe as1 onto as2's head */
1195 seg1
= as2
->segments
= assegment_dup_all (as1
->segments
);
1197 /* refind the tail of as2, reusing seg1 */
1198 while (seg1
&& seg1
->next
)
1201 /* merge the old head, seg2, into tail, seg1 */
1202 seg1
= assegment_append_asns (seg1
, seg2
->as
, seg2
->length
);
1204 /* bypass the merged seg2, and attach any chain after it to
1205 * chain descending from as2's head
1207 seg1
->next
= seg2
->next
;
1209 /* seg2 is now referenceless and useless*/
1210 assegment_free (seg2
);
1212 /* we've now prepended as1's segment chain to as2, merging
1213 * the inbetween AS_SEQUENCE of seg2 in the process
1215 aspath_str_update (as2
);
1220 /* AS_SET merge code is needed at here. */
1221 return aspath_merge (as1
, as2
);
1223 /* XXX: Ermmm, what if as1 has multiple segments?? */
1228 /* Iterate over AS_PATH segments and wipe all occurences of the
1229 * listed AS numbers. Hence some segments may lose some or even
1230 * all data on the way, the operation is implemented as a smarter
1231 * version of aspath_dup(), which allocates memory to hold the new
1232 * data, not the original. The new AS path is returned.
1235 aspath_filter_exclude (struct aspath
* source
, struct aspath
* exclude_list
)
1237 struct assegment
* srcseg
, * exclseg
, * lastseg
;
1238 struct aspath
* newpath
;
1240 newpath
= aspath_new();
1243 for (srcseg
= source
->segments
; srcseg
; srcseg
= srcseg
->next
)
1245 unsigned i
, y
, newlen
= 0, done
= 0, skip_as
;
1246 struct assegment
* newseg
;
1248 /* Find out, how much ASns are we going to pick from this segment.
1249 * We can't perform filtering right inline, because the size of
1250 * the new segment isn't known at the moment yet.
1252 for (i
= 0; i
< srcseg
->length
; i
++)
1255 for (exclseg
= exclude_list
->segments
; exclseg
&& !skip_as
; exclseg
= exclseg
->next
)
1256 for (y
= 0; y
< exclseg
->length
; y
++)
1257 if (srcseg
->as
[i
] == exclseg
->as
[y
])
1260 // There's no sense in testing the rest of exclusion list, bail out.
1266 /* newlen is now the number of ASns to copy */
1270 /* Actual copying. Allocate memory and iterate once more, performing filtering. */
1271 newseg
= assegment_new (srcseg
->type
, newlen
);
1272 for (i
= 0; i
< srcseg
->length
; i
++)
1275 for (exclseg
= exclude_list
->segments
; exclseg
&& !skip_as
; exclseg
= exclseg
->next
)
1276 for (y
= 0; y
< exclseg
->length
; y
++)
1277 if (srcseg
->as
[i
] == exclseg
->as
[y
])
1284 newseg
->as
[done
++] = srcseg
->as
[i
];
1286 /* At his point newlen must be equal to done, and both must be positive. Append
1287 * the filtered segment to the gross result. */
1289 newpath
->segments
= newseg
;
1291 lastseg
->next
= newseg
;
1294 aspath_str_update (newpath
);
1295 /* We are happy returning even an empty AS_PATH, because the administrator
1296 * might expect this very behaviour. There's a mean to avoid this, if necessary,
1297 * by having a match rule against certain AS_PATH regexps in the route-map index.
1299 aspath_free (source
);
1303 /* Add specified AS to the leftmost of aspath. */
1304 static struct aspath
*
1305 aspath_add_one_as (struct aspath
*aspath
, as_t asno
, u_char type
)
1307 struct assegment
*assegment
= aspath
->segments
;
1309 /* In case of empty aspath. */
1310 if (assegment
== NULL
|| assegment
->length
== 0)
1312 aspath
->segments
= assegment_new (type
, 1);
1313 aspath
->segments
->as
[0] = asno
;
1316 assegment_free (assegment
);
1321 if (assegment
->type
== type
)
1322 aspath
->segments
= assegment_prepend_asns (aspath
->segments
, asno
, 1);
1325 /* create new segment
1326 * push it onto head of aspath's segment chain
1328 struct assegment
*newsegment
;
1330 newsegment
= assegment_new (type
, 1);
1331 newsegment
->as
[0] = asno
;
1333 newsegment
->next
= assegment
;
1334 aspath
->segments
= newsegment
;
1340 /* Add specified AS to the leftmost of aspath. */
1342 aspath_add_seq (struct aspath
*aspath
, as_t asno
)
1344 return aspath_add_one_as (aspath
, asno
, AS_SEQUENCE
);
1347 /* Compare leftmost AS value for MED check. If as1's leftmost AS and
1348 as2's leftmost AS is same return 1. */
1350 aspath_cmp_left (struct aspath
*aspath1
, struct aspath
*aspath2
)
1352 struct assegment
*seg1
= NULL
;
1353 struct assegment
*seg2
= NULL
;
1355 if (!(aspath1
&& aspath2
))
1358 seg1
= aspath1
->segments
;
1359 seg2
= aspath2
->segments
;
1361 /* find first non-confed segments for each */
1362 while (seg1
&& ((seg1
->type
== AS_CONFED_SEQUENCE
)
1363 || (seg1
->type
== AS_CONFED_SET
)))
1366 while (seg2
&& ((seg2
->type
== AS_CONFED_SEQUENCE
)
1367 || (seg2
->type
== AS_CONFED_SET
)))
1372 && (seg1
->type
== AS_SEQUENCE
) && (seg2
->type
== AS_SEQUENCE
)))
1375 if (seg1
->as
[0] == seg2
->as
[0])
1381 /* Truncate an aspath after a number of hops, and put the hops remaining
1382 * at the front of another aspath. Needed for AS4 compat.
1384 * Returned aspath is a /new/ aspath, which should either by free'd or
1385 * interned by the caller, as desired.
1388 aspath_reconcile_as4 ( struct aspath
*aspath
, struct aspath
*as4path
)
1390 struct assegment
*seg
, *newseg
, *prevseg
= NULL
;
1391 struct aspath
*newpath
= NULL
, *mergedpath
;
1392 int hops
, cpasns
= 0;
1397 seg
= aspath
->segments
;
1399 /* CONFEDs should get reconciled too.. */
1400 hops
= (aspath_count_hops (aspath
) + aspath_count_confeds (aspath
))
1401 - aspath_count_hops (as4path
);
1405 if (BGP_DEBUG (as4
, AS4
))
1406 zlog_warn ("[AS4] Fewer hops in AS_PATH than NEW_AS_PATH");
1407 /* Something's gone wrong. The RFC says we should now ignore AS4_PATH,
1408 * which is daft behaviour - it contains vital loop-detection
1409 * information which must have been removed from AS_PATH.
1411 hops
= aspath_count_hops (aspath
);
1415 return aspath_dup (as4path
);
1417 if ( BGP_DEBUG(as4
, AS4
))
1418 zlog_debug("[AS4] got AS_PATH %s and AS4_PATH %s synthesizing now",
1419 aspath
->str
, as4path
->str
);
1421 while (seg
&& hops
> 0)
1428 cpasns
= seg
->length
;
1430 case AS_CONFED_SEQUENCE
:
1431 /* Should never split a confed-sequence, if hop-count
1432 * suggests we must then something's gone wrong somewhere.
1434 * Most important goal is to preserve AS_PATHs prime function
1435 * as loop-detector, so we fudge the numbers so that the entire
1436 * confed-sequence is merged in.
1438 if (hops
< seg
->length
)
1440 if (BGP_DEBUG (as4
, AS4
))
1441 zlog_debug ("[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls"
1442 " across 2/4 ASN boundary somewhere, broken..");
1446 cpasns
= MIN(seg
->length
, hops
);
1447 hops
-= seg
->length
;
1450 assert (cpasns
<= seg
->length
);
1452 newseg
= assegment_new (seg
->type
, 0);
1453 newseg
= assegment_append_asns (newseg
, seg
->as
, cpasns
);
1457 newpath
= aspath_new ();
1458 newpath
->segments
= newseg
;
1461 prevseg
->next
= newseg
;
1467 /* We may be able to join some segments here, and we must
1468 * do this because... we want normalised aspaths in out hash
1469 * and we do not want to stumble in aspath_put.
1471 mergedpath
= aspath_merge (newpath
, aspath_dup(as4path
));
1472 aspath_free (newpath
);
1473 mergedpath
->segments
= assegment_normalise (mergedpath
->segments
);
1474 aspath_str_update (mergedpath
);
1476 if ( BGP_DEBUG(as4
, AS4
))
1477 zlog_debug ("[AS4] result of synthesizing is %s",
1483 /* Compare leftmost AS value for MED check. If as1's leftmost AS and
1484 as2's leftmost AS is same return 1. (confederation as-path
1487 aspath_cmp_left_confed (struct aspath
*aspath1
, struct aspath
*aspath2
)
1489 if (! (aspath1
&& aspath2
) )
1492 if ( !(aspath1
->segments
&& aspath2
->segments
) )
1495 if ( (aspath1
->segments
->type
!= AS_CONFED_SEQUENCE
)
1496 || (aspath2
->segments
->type
!= AS_CONFED_SEQUENCE
) )
1499 if (aspath1
->segments
->as
[0] == aspath2
->segments
->as
[0])
1505 /* Delete all leading AS_CONFED_SEQUENCE/SET segments from aspath.
1506 * See RFC3065, 6.1 c1 */
1508 aspath_delete_confed_seq (struct aspath
*aspath
)
1510 struct assegment
*seg
;
1512 if (!(aspath
&& aspath
->segments
))
1515 seg
= aspath
->segments
;
1517 /* "if the first path segment of the AS_PATH is
1518 * of type AS_CONFED_SEQUENCE,"
1520 if (aspath
->segments
->type
!= AS_CONFED_SEQUENCE
)
1523 /* "... that segment and any immediately following segments
1524 * of the type AS_CONFED_SET or AS_CONFED_SEQUENCE are removed
1525 * from the AS_PATH attribute,"
1528 (seg
->type
== AS_CONFED_SEQUENCE
|| seg
->type
== AS_CONFED_SET
))
1530 aspath
->segments
= seg
->next
;
1531 assegment_free (seg
);
1532 seg
= aspath
->segments
;
1534 aspath_str_update (aspath
);
1538 /* Add new AS number to the leftmost part of the aspath as
1539 AS_CONFED_SEQUENCE. */
1541 aspath_add_confed_seq (struct aspath
*aspath
, as_t asno
)
1543 return aspath_add_one_as (aspath
, asno
, AS_CONFED_SEQUENCE
);
1546 /* Add new as value to as path structure. */
1548 aspath_as_add (struct aspath
*as
, as_t asno
)
1550 struct assegment
*seg
= as
->segments
;
1555 /* Last segment search procedure. */
1559 assegment_append_asns (seg
, &asno
, 1);
1562 /* Add new as segment to the as path. */
1564 aspath_segment_add (struct aspath
*as
, int type
)
1566 struct assegment
*seg
= as
->segments
;
1567 struct assegment
*new = assegment_new (type
, 0);
1582 return aspath_parse (NULL
, 0, 1); /* 32Bit ;-) */
1586 aspath_empty_get (void)
1588 struct aspath
*aspath
;
1590 aspath
= aspath_new ();
1591 aspath
->str
= aspath_make_str_count (aspath
);
1598 return ashash
->count
;
1602 Theoretically, one as path can have:
1604 One BGP packet size should be less than 4096.
1605 One BGP attribute size should be less than 4096 - BGP header size.
1606 One BGP aspath size should be less than 4096 - BGP header size -
1607 BGP mandantry attribute size.
1610 /* AS path string lexical token enum. */
1616 as_token_confed_seq_start
,
1617 as_token_confed_seq_end
,
1618 as_token_confed_set_start
,
1619 as_token_confed_set_end
,
1623 /* Return next token and point for string parse. */
1625 aspath_gettoken (const char *buf
, enum as_token
*token
, u_long
*asno
)
1627 const char *p
= buf
;
1629 /* Skip seperators (space for sequences, ',' for sets). */
1630 while (isspace ((int) *p
) || *p
== ',')
1633 /* Check the end of the string and type specify characters
1640 *token
= as_token_set_start
;
1644 *token
= as_token_set_end
;
1648 *token
= as_token_confed_seq_start
;
1652 *token
= as_token_confed_seq_end
;
1656 *token
= as_token_confed_set_start
;
1660 *token
= as_token_confed_set_end
;
1665 /* Check actual AS value. */
1666 if (isdigit ((int) *p
))
1670 *token
= as_token_asval
;
1674 while (isdigit ((int) *p
))
1677 asval
+= (*p
- '0');
1684 /* There is no match then return unknown token. */
1685 *token
= as_token_unknown
;
1690 aspath_str2aspath (const char *str
)
1692 enum as_token token
= as_token_unknown
;
1695 struct aspath
*aspath
;
1698 aspath
= aspath_new ();
1700 /* We start default type as AS_SEQUENCE. */
1701 as_type
= AS_SEQUENCE
;
1704 while ((str
= aspath_gettoken (str
, &token
, &asno
)) != NULL
)
1708 case as_token_asval
:
1711 aspath_segment_add (aspath
, as_type
);
1714 aspath_as_add (aspath
, asno
);
1716 case as_token_set_start
:
1718 aspath_segment_add (aspath
, as_type
);
1721 case as_token_set_end
:
1722 as_type
= AS_SEQUENCE
;
1725 case as_token_confed_seq_start
:
1726 as_type
= AS_CONFED_SEQUENCE
;
1727 aspath_segment_add (aspath
, as_type
);
1730 case as_token_confed_seq_end
:
1731 as_type
= AS_SEQUENCE
;
1734 case as_token_confed_set_start
:
1735 as_type
= AS_CONFED_SET
;
1736 aspath_segment_add (aspath
, as_type
);
1739 case as_token_confed_set_end
:
1740 as_type
= AS_SEQUENCE
;
1743 case as_token_unknown
:
1745 aspath_free (aspath
);
1750 aspath
->str
= aspath_make_str_count (aspath
);
1755 /* Make hash value by raw aspath data. */
1757 aspath_key_make (void *p
)
1759 struct aspath
* aspath
= (struct aspath
*) p
;
1760 unsigned int key
= 0;
1763 aspath_str_update (aspath
);
1765 key
= jhash (aspath
->str
, strlen(aspath
->str
), 2334325);
1770 /* If two aspath have same value then return 1 else return 0 */
1772 aspath_cmp (void *arg1
, void *arg2
)
1774 struct assegment
*seg1
= ((struct aspath
*)arg1
)->segments
;
1775 struct assegment
*seg2
= ((struct aspath
*)arg2
)->segments
;
1777 while (seg1
|| seg2
)
1780 if ((!seg1
&& seg2
) || (seg1
&& !seg2
))
1782 if (seg1
->type
!= seg2
->type
)
1784 if (seg1
->length
!= seg2
->length
)
1786 for (i
= 0; i
< seg1
->length
; i
++)
1787 if (seg1
->as
[i
] != seg2
->as
[i
])
1795 /* AS path hash initialize. */
1799 ashash
= hash_create_size (32767, aspath_key_make
, aspath_cmp
);
1803 aspath_finish (void)
1808 stream_free (snmp_stream
);
1811 /* return and as path value */
1813 aspath_print (struct aspath
*as
)
1815 return (as
? as
->str
: NULL
);
1818 /* Printing functions */
1819 /* Feed the AS_PATH to the vty; the suffix string follows it only in case
1820 * AS_PATH wasn't empty.
1823 aspath_print_vty (struct vty
*vty
, const char *format
, struct aspath
*as
, const char * suffix
)
1826 vty_out (vty
, format
, as
->str
);
1827 if (strlen (as
->str
) && strlen (suffix
))
1828 vty_out (vty
, "%s", suffix
);
1832 aspath_show_all_iterator (struct hash_backet
*backet
, struct vty
*vty
)
1836 as
= (struct aspath
*) backet
->data
;
1838 vty_out (vty
, "[%p:%u] (%ld) ", backet
, backet
->key
, as
->refcnt
);
1839 vty_out (vty
, "%s%s", as
->str
, VTY_NEWLINE
);
1842 /* Print all aspath and hash information. This function is used from
1843 `show ip bgp paths' command. */
1845 aspath_print_all_vty (struct vty
*vty
)
1847 hash_iterate (ashash
,
1848 (void (*) (struct hash_backet
*, void *))
1849 aspath_show_all_iterator
,