]>
git.ipfire.org Git - thirdparty/bird.git/blob - nest/a-path.c
2 * BIRD -- Path Operations
4 * (c) 2000 Martin Mares <mj@ucw.cz>
5 * (c) 2000 Pavel Machek <pavel@ucw.cz>
7 * Can be freely distributed and used under the terms of the GNU GPL.
10 #include "nest/bird.h"
11 #include "nest/route.h"
12 #include "nest/attrs.h"
13 #include "lib/resource.h"
14 #include "lib/unaligned.h"
15 #include "lib/string.h"
16 #include "filter/f-util.h"
18 // static inline void put_as(byte *data, u32 as) { put_u32(data, as); }
19 // static inline u32 get_as(byte *data) { return get_u32(data); }
21 #define put_as put_u32
22 #define get_as get_u32
23 #define BS 4 /* Default block size of ASN (autonomous system number) */
25 #define BAD(DSC, VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; })
28 as_path_valid(byte
*data
, uint len
, int bs
, int confed
, char *err
, uint elen
)
37 BAD("segment framing error", 0);
39 /* Process one AS path segment */
41 uint slen
= 2 + bs
* pos
[1];
44 BAD("segment framing error", len
);
49 case AS_PATH_SEQUENCE
:
52 case AS_PATH_CONFED_SEQUENCE
:
53 case AS_PATH_CONFED_SET
:
55 BAD("AS_CONFED* segment", type
);
59 BAD("unknown segment", type
);
63 BAD("zero-length segment", type
);
73 if (bsnprintf(err
, elen
, "%s (%u) at %d", err_dsc
, err_val
, (int) (pos
- data
)) < 0)
80 as_path_16to32(byte
*dst
, const byte
*src
, uint len
)
83 const byte
*end
= src
+ len
;
92 for (i
= 0; i
< n
; i
++)
94 put_u32(dst
, get_u16(src
));
104 as_path_32to16(byte
*dst
, const byte
*src
, uint len
)
107 const byte
*end
= src
+ len
;
116 for (i
= 0; i
< n
; i
++)
118 put_u16(dst
, get_u32(src
));
128 as_path_contains_as4(const struct adata
*path
)
130 const byte
*pos
= path
->data
;
131 const byte
*end
= pos
+ path
->length
;
139 for (i
= 0; i
< n
; i
++)
141 if (get_as(pos
) > 0xFFFF)
152 as_path_contains_confed(const struct adata
*path
)
154 const byte
*pos
= path
->data
;
155 const byte
*end
= pos
+ path
->length
;
160 uint slen
= 2 + BS
* pos
[1];
162 if ((type
== AS_PATH_CONFED_SEQUENCE
) ||
163 (type
== AS_PATH_CONFED_SET
))
173 as_path_strip_confed(struct linpool
*pool
, const struct adata
*path
)
175 struct adata
*res
= lp_alloc_adata(pool
, path
->length
);
176 const byte
*src
= path
->data
;
177 const byte
*end
= src
+ path
->length
;
178 byte
*dst
= res
->data
;
183 uint slen
= 2 + BS
* src
[1];
185 /* Copy regular segments */
186 if ((type
== AS_PATH_SET
) || (type
== AS_PATH_SEQUENCE
))
188 memcpy(dst
, src
, slen
);
195 /* Fix the result length */
196 res
->length
= dst
- res
->data
;
202 as_path_prepend2(struct linpool
*pool
, const struct adata
*op
, int seq
, u32 as
)
205 const byte
*pos
= op
->data
;
206 uint len
= op
->length
;
208 if (len
&& (pos
[0] == seq
) && (pos
[1] < 255))
210 /* Starting with matching segment => just prepend the AS number */
211 np
= lp_alloc_adata(pool
, len
+ BS
);
213 np
->data
[1] = pos
[1] + 1;
214 put_as(np
->data
+ 2, as
);
216 uint dlen
= BS
* pos
[1];
217 memcpy(np
->data
+ 2 + BS
, pos
+ 2, dlen
);
218 ADVANCE(pos
, len
, 2 + dlen
);
222 /* Create a new path segment */
223 np
= lp_alloc_adata(pool
, len
+ 2 + BS
);
226 put_as(np
->data
+ 2, as
);
231 byte
*dst
= np
->data
+ 2 + BS
* np
->data
[1];
233 memcpy(dst
, pos
, len
);
241 as_path_to_old(struct linpool
*pool
, const struct adata
*path
)
243 struct adata
*res
= lp_alloc_adata(pool
, path
->length
);
244 byte
*pos
= res
->data
;
245 byte
*end
= pos
+ res
->length
;
249 /* Copy the whole path */
250 memcpy(res
->data
, path
->data
, path
->length
);
252 /* Replace 32-bit AS numbers with AS_TRANS */
258 for (i
= 0; i
< n
; i
++)
262 put_as(pos
, AS_TRANS
);
272 * Cut the path to the length @num, measured to the usual path metric. Note that
273 * AS_CONFED_* segments have zero length and must be added if they are on edge.
274 * In contrast to other as_path_* functions, @path is modified in place.
277 as_path_cut(struct linpool
*pool
, const struct adata
*path
, uint num
)
279 const byte
*pos
= path
->data
;
280 const byte
*end
= pos
+ path
->length
;
290 case AS_PATH_SET
: n
= 1; break;
291 case AS_PATH_SEQUENCE
: n
= l
; break;
292 case AS_PATH_CONFED_SEQUENCE
: n
= 0; break;
293 case AS_PATH_CONFED_SET
: n
= 0; break;
294 default: bug("as_path_cut: Invalid path segment");
297 /* Cannot add whole segment, so try partial one and finish */
300 const byte
*nend
= pos
;
302 nend
+= 2 + BS
* num
;
304 struct adata
*res
= lp_alloc_adata(pool
, path
->length
);
305 res
->length
= nend
- (const byte
*) path
->data
;
306 memcpy(res
->data
, path
->data
, res
->length
);
310 byte
*dpos
= ((byte
*) res
->data
) + (pos
- (const byte
*) path
->data
);
321 struct adata
*res
= lp_alloc_adata(pool
, path
->length
);
322 res
->length
= path
->length
;
323 memcpy(res
->data
, path
->data
, res
->length
);
328 * Merge (concatenate) paths @p1 and @p2 and return the result.
329 * In contrast to other as_path_* functions, @p1 and @p2 may be reused.
332 as_path_merge(struct linpool
*pool
, const struct adata
*p1
, const struct adata
*p2
)
340 struct adata
*res
= lp_alloc_adata(pool
, p1
->length
+ p2
->length
);
341 memcpy(res
->data
, p1
->data
, p1
->length
);
342 memcpy(res
->data
+ p1
->length
, p2
->data
, p2
->length
);
348 as_path_format(const struct adata
*path
, byte
*bb
, uint size
)
350 buffer buf
= { .start
= bb
, .pos
= bb
, .end
= bb
+ size
}, *b
= &buf
;
351 const byte
*pos
= path
->data
;
352 const byte
*end
= pos
+ path
->length
;
353 const char *ops
, *cls
;
365 case AS_PATH_SET
: ops
= "{"; cls
= "}"; break;
366 case AS_PATH_SEQUENCE
: ops
= NULL
; cls
= NULL
; break;
367 case AS_PATH_CONFED_SEQUENCE
: ops
= "("; cls
= ")"; break;
368 case AS_PATH_CONFED_SET
: ops
= "({"; cls
= "})"; break;
369 default: bug("Invalid path segment");
377 buffer_print(b
, len
? "%u " : "%u", get_as(pos
));
388 /* Handle overflow */
389 if (b
->pos
== b
->end
)
390 strcpy(b
->end
- 12, "...");
394 as_path_getlen(const struct adata
*path
)
396 const byte
*pos
= path
->data
;
397 const byte
*end
= pos
+ path
->length
;
408 case AS_PATH_SET
: n
= 1; break;
409 case AS_PATH_SEQUENCE
: n
= l
; break;
410 case AS_PATH_CONFED_SEQUENCE
: n
= 0; break;
411 case AS_PATH_CONFED_SET
: n
= 0; break;
412 default: bug("as_path_getlen: Invalid path segment");
423 as_path_get_last(const struct adata
*path
, u32
*orig_as
)
425 const byte
*pos
= path
->data
;
426 const byte
*end
= pos
+ path
->length
;
442 case AS_PATH_CONFED_SET
:
446 case AS_PATH_SEQUENCE
:
447 case AS_PATH_CONFED_SEQUENCE
:
448 val
= get_as(pos
+ BS
* (len
- 1));
453 bug("Invalid path segment");
465 as_path_get_last_nonaggregated(const struct adata
*path
)
467 const byte
*pos
= path
->data
;
468 const byte
*end
= pos
+ path
->length
;
483 case AS_PATH_CONFED_SET
:
486 case AS_PATH_SEQUENCE
:
487 case AS_PATH_CONFED_SEQUENCE
:
488 val
= get_as(pos
+ BS
* (len
- 1));
492 bug("Invalid path segment");
502 as_path_get_first(const struct adata
*path
, u32
*last_as
)
504 const u8
*p
= path
->data
;
506 if ((path
->length
== 0) || (p
[0] != AS_PATH_SEQUENCE
) || (p
[1] == 0))
509 *last_as
= get_as(p
+2);
514 as_path_get_first_regular(const struct adata
*path
, u32
*last_as
)
516 const byte
*pos
= path
->data
;
517 const byte
*end
= pos
+ path
->length
;
530 case AS_PATH_SEQUENCE
:
534 *last_as
= get_as(pos
);
537 case AS_PATH_CONFED_SEQUENCE
:
538 case AS_PATH_CONFED_SET
:
542 bug("Invalid path segment");
552 as_path_contains(const struct adata
*path
, u32 as
, int min
)
554 const u8
*p
= path
->data
;
555 const u8
*q
= p
+path
->length
;
575 as_path_match_set(const struct adata
*path
, const struct f_tree
*set
)
577 const u8
*p
= path
->data
;
578 const u8
*q
= p
+path
->length
;
587 struct f_val v
= {T_INT
, .val
.i
= get_as(p
)};
588 if (find_tree(set
, &v
))
598 as_path_filter(struct linpool
*pool
, const struct adata
*path
, const struct f_tree
*set
, u32 key
, int pos
)
603 int len
= path
->length
;
604 const u8
*p
= path
->data
;
605 const u8
*q
= path
->data
+ len
;
613 /* Read block header (type and length) */
620 for (i
= 0; i
< sn
; i
++)
627 struct f_val v
= {T_INT
, .val
.i
= as
};
628 match
= !!find_tree(set
, &v
);
645 /* Nonempty block, set block header and advance */
653 if (nl
== path
->length
)
656 struct adata
*res
= lp_alloc(pool
, sizeof(struct adata
) + nl
);
658 memcpy(res
->data
, buf
, nl
);
676 parse_path(const struct adata
*path
, struct pm_pos
*pp
)
678 const byte
*pos
= path
->data
;
679 const byte
*end
= pos
+ path
->length
;
680 struct pm_pos
*op
= pp
;
692 case AS_PATH_CONFED_SET
:
695 pp
->val
.sp
= pos
- 1;
701 case AS_PATH_SEQUENCE
:
702 case AS_PATH_CONFED_SEQUENCE
:
703 for (i
= 0; i
< len
; i
++)
707 pp
->val
.asn
= get_as(pos
);
715 bug("Invalid path segment");
723 pm_match(struct pm_pos
*pos
, u32 asn
, u32 asn2
)
727 return ((pos
->val
.asn
>= asn
) && (pos
->val
.asn
<= asn2
));
729 const u8
*p
= pos
->val
.sp
;
733 for (i
= 0; i
< len
; i
++)
735 gas
= get_as(p
+ i
* BS
);
737 if ((gas
>= asn
) && (gas
<= asn2
))
745 pm_mark(struct pm_pos
*pos
, int i
, int plen
, int *nl
, int *nh
)
752 for (j
= i
+ 1; (j
< plen
) && pos
[j
].set
&& (! pos
[j
].mark
); j
++)
756 /* We are going downwards, therefore every mark is
757 new low and just the first mark is new high */
759 *nl
= i
+ (pos
[i
].set
? 0 : 1);
765 /* AS path matching is nontrivial. Because AS path can
766 * contain sets, it is not a plain wildcard matching. A set
767 * in an AS path is interpreted as it might represent any
768 * sequence of AS numbers from that set (possibly with
769 * repetitions). So it is also a kind of a pattern,
770 * more complicated than a path mask.
772 * The algorithm for AS path matching is a variant
773 * of nondeterministic finite state machine, where
774 * positions in AS path are states, and items in
775 * path mask are input for that finite state machine.
776 * During execution of the algorithm we maintain a set
777 * of marked states - a state is marked if it can be
778 * reached by any walk through NFSM with regard to
779 * currently processed part of input. When we process
780 * next part of mask, we advance each marked state.
781 * We start with marked first position, when we
782 * run out of marked positions, we reject. When
783 * we process the whole mask, we accept if final position
784 * (auxiliary position after last real position in AS path)
788 as_path_match(const struct adata
*path
, const struct f_path_mask
*mask
)
790 struct pm_pos pos
[2048 + 1];
791 int plen
= parse_path(path
, pos
);
796 /* l and h are bound of interval of positions where
805 for (uint m
=0; m
< mask
->len
; m
++)
807 /* We remove this mark to not step after pos[plen] */
810 switch (mask
->item
[m
].kind
)
813 for (i
= l
; i
<= plen
; i
++)
818 case PM_ASN
: /* Define single ASN as ASN..ASN - very narrow interval */
819 val2
= val
= mask
->item
[m
].asn
;
822 bug("Expressions should be evaluated on AS path mask construction.");
824 val
= mask
->item
[m
].from
;
825 val2
= mask
->item
[m
].to
;
830 for (i
= h
; i
>= l
; i
--)
834 if ((mask
->item
[m
].kind
== PM_QUESTION
) || pm_match(pos
+ i
, val
, val2
))
835 pm_mark(pos
, i
, plen
, &nl
, &nh
);
847 return pos
[plen
].mark
;