]>
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/data.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.
276 as_path_cut(struct linpool
*pool
, const struct adata
*path
, uint num
)
278 const byte
*pos
= path
->data
;
279 const byte
*end
= pos
+ path
->length
;
289 case AS_PATH_SET
: n
= 1; break;
290 case AS_PATH_SEQUENCE
: n
= l
; break;
291 case AS_PATH_CONFED_SEQUENCE
: n
= 0; break;
292 case AS_PATH_CONFED_SET
: n
= 0; break;
293 default: bug("as_path_cut: Invalid path segment");
296 /* Cannot add whole segment, so try partial one and finish */
299 const byte
*nend
= pos
;
301 nend
+= 2 + BS
* num
;
303 struct adata
*res
= lp_alloc_adata(pool
, path
->length
);
304 res
->length
= nend
- (const byte
*) path
->data
;
305 memcpy(res
->data
, path
->data
, res
->length
);
309 byte
*dpos
= ((byte
*) res
->data
) + (pos
- (const byte
*) path
->data
);
320 struct adata
*res
= lp_alloc_adata(pool
, path
->length
);
321 res
->length
= path
->length
;
322 memcpy(res
->data
, path
->data
, res
->length
);
327 * Merge (concatenate) paths @p1 and @p2 and return the result.
328 * In contrast to other as_path_* functions, @p1 and @p2 may be reused.
331 as_path_merge(struct linpool
*pool
, const struct adata
*p1
, const struct adata
*p2
)
339 struct adata
*res
= lp_alloc_adata(pool
, p1
->length
+ p2
->length
);
340 memcpy(res
->data
, p1
->data
, p1
->length
);
341 memcpy(res
->data
+ p1
->length
, p2
->data
, p2
->length
);
347 as_path_format(const struct adata
*path
, byte
*bb
, uint size
)
349 buffer buf
= { .start
= bb
, .pos
= bb
, .end
= bb
+ size
}, *b
= &buf
;
350 const byte
*pos
= path
->data
;
351 const byte
*end
= pos
+ path
->length
;
352 const char *ops
, *cls
;
364 case AS_PATH_SET
: ops
= "{"; cls
= "}"; break;
365 case AS_PATH_SEQUENCE
: ops
= NULL
; cls
= NULL
; break;
366 case AS_PATH_CONFED_SEQUENCE
: ops
= "("; cls
= ")"; break;
367 case AS_PATH_CONFED_SET
: ops
= "({"; cls
= "})"; break;
368 default: bug("Invalid path segment");
376 buffer_print(b
, len
? "%u " : "%u", get_as(pos
));
387 /* Handle overflow */
388 if (b
->pos
== b
->end
)
389 strcpy(b
->end
- 12, "...");
393 as_path_getlen(const struct adata
*path
)
395 const byte
*pos
= path
->data
;
396 const byte
*end
= pos
+ path
->length
;
407 case AS_PATH_SET
: n
= 1; break;
408 case AS_PATH_SEQUENCE
: n
= l
; break;
409 case AS_PATH_CONFED_SEQUENCE
: n
= 0; break;
410 case AS_PATH_CONFED_SET
: n
= 0; break;
411 default: bug("as_path_getlen: Invalid path segment");
422 as_path_get_last(const struct adata
*path
, u32
*orig_as
)
424 const byte
*pos
= path
->data
;
425 const byte
*end
= pos
+ path
->length
;
441 case AS_PATH_CONFED_SET
:
445 case AS_PATH_SEQUENCE
:
446 case AS_PATH_CONFED_SEQUENCE
:
447 val
= get_as(pos
+ BS
* (len
- 1));
452 bug("Invalid path segment");
464 as_path_get_last_nonaggregated(const struct adata
*path
)
466 const byte
*pos
= path
->data
;
467 const byte
*end
= pos
+ path
->length
;
482 case AS_PATH_CONFED_SET
:
485 case AS_PATH_SEQUENCE
:
486 case AS_PATH_CONFED_SEQUENCE
:
487 val
= get_as(pos
+ BS
* (len
- 1));
491 bug("Invalid path segment");
501 as_path_get_first(const struct adata
*path
, u32
*last_as
)
503 const u8
*p
= path
->data
;
505 if ((path
->length
== 0) || (p
[0] != AS_PATH_SEQUENCE
) || (p
[1] == 0))
508 *last_as
= get_as(p
+2);
513 as_path_get_first_regular(const struct adata
*path
, u32
*last_as
)
515 const byte
*pos
= path
->data
;
516 const byte
*end
= pos
+ path
->length
;
529 case AS_PATH_SEQUENCE
:
533 *last_as
= get_as(pos
);
536 case AS_PATH_CONFED_SEQUENCE
:
537 case AS_PATH_CONFED_SET
:
541 bug("Invalid path segment");
551 as_path_contains(const struct adata
*path
, u32 as
, int min
)
553 const u8
*p
= path
->data
;
554 const u8
*q
= p
+path
->length
;
574 as_path_match_set(const struct adata
*path
, const struct f_tree
*set
)
576 const u8
*p
= path
->data
;
577 const u8
*q
= p
+path
->length
;
586 struct f_val v
= {T_INT
, .val
.i
= get_as(p
)};
587 if (find_tree(set
, &v
))
597 as_path_filter(struct linpool
*pool
, const struct adata
*path
, const struct f_tree
*set
, u32 key
, int pos
)
602 int len
= path
->length
;
603 const u8
*p
= path
->data
;
604 const u8
*q
= path
->data
+ len
;
612 /* Read block header (type and length) */
619 for (i
= 0; i
< sn
; i
++)
626 struct f_val v
= {T_INT
, .val
.i
= as
};
627 match
= !!find_tree(set
, &v
);
644 /* Nonempty block, set block header and advance */
652 if (nl
== path
->length
)
655 struct adata
*res
= lp_alloc(pool
, sizeof(struct adata
) + nl
);
657 memcpy(res
->data
, buf
, nl
);
675 parse_path(const struct adata
*path
, struct pm_pos
*pp
)
677 const byte
*pos
= path
->data
;
678 const byte
*end
= pos
+ path
->length
;
679 struct pm_pos
*op
= pp
;
691 case AS_PATH_CONFED_SET
:
694 pp
->val
.sp
= pos
- 1;
700 case AS_PATH_SEQUENCE
:
701 case AS_PATH_CONFED_SEQUENCE
:
702 for (i
= 0; i
< len
; i
++)
706 pp
->val
.asn
= get_as(pos
);
714 bug("Invalid path segment");
722 pm_match(struct pm_pos
*pos
, u32 asn
, u32 asn2
)
726 return ((pos
->val
.asn
>= asn
) && (pos
->val
.asn
<= asn2
));
728 const u8
*p
= pos
->val
.sp
;
732 for (i
= 0; i
< len
; i
++)
734 gas
= get_as(p
+ i
* BS
);
736 if ((gas
>= asn
) && (gas
<= asn2
))
744 pm_match_set(struct pm_pos
*pos
, struct f_tree
*set
)
746 struct f_val asn
= { .type
= T_INT
};
750 asn
.val
.i
= pos
->val
.asn
;
751 return !!find_tree(set
, &asn
);
754 const u8
*p
= pos
->val
.sp
;
758 for (i
= 0; i
< len
; i
++)
760 asn
.val
.i
= get_as(p
+ i
* BS
);
761 if (find_tree(set
, &asn
))
769 pm_mark(struct pm_pos
*pos
, int i
, int plen
, int *nl
, int *nh
)
776 for (j
= i
+ 1; (j
< plen
) && pos
[j
].set
&& (! pos
[j
].mark
); j
++)
780 /* We are going downwards, therefore every mark is
781 new low and just the first mark is new high */
783 *nl
= i
+ (pos
[i
].set
? 0 : 1);
789 /* AS path matching is nontrivial. Because AS path can
790 * contain sets, it is not a plain wildcard matching. A set
791 * in an AS path is interpreted as it might represent any
792 * sequence of AS numbers from that set (possibly with
793 * repetitions). So it is also a kind of a pattern,
794 * more complicated than a path mask.
796 * The algorithm for AS path matching is a variant
797 * of nondeterministic finite state machine, where
798 * positions in AS path are states, and items in
799 * path mask are input for that finite state machine.
800 * During execution of the algorithm we maintain a set
801 * of marked states - a state is marked if it can be
802 * reached by any walk through NFSM with regard to
803 * currently processed part of input. When we process
804 * next part of mask, we advance each marked state.
805 * We start with marked first position, when we
806 * run out of marked positions, we reject. When
807 * we process the whole mask, we accept if final position
808 * (auxiliary position after last real position in AS path)
812 as_path_match(const struct adata
*path
, const struct f_path_mask
*mask
)
814 struct pm_pos pos
[2048 + 1];
815 int plen
= parse_path(path
, pos
);
820 /* l and h are bound of interval of positions where
829 for (uint m
=0; m
< mask
->len
; m
++)
831 /* We remove this mark to not step after pos[plen] */
834 switch (mask
->item
[m
].kind
)
837 for (i
= l
; i
<= plen
; i
++)
842 case PM_ASN
: /* Define single ASN as ASN..ASN - very narrow interval */
843 val2
= val
= mask
->item
[m
].asn
;
846 bug("Expressions should be evaluated on AS path mask construction.");
848 val
= mask
->item
[m
].from
;
849 val2
= mask
->item
[m
].to
;
855 for (i
= h
; i
>= l
; i
--)
859 if ((mask
->item
[m
].kind
== PM_QUESTION
) ||
860 ((mask
->item
[m
].kind
!= PM_ASN_SET
) ?
861 pm_match(pos
+ i
, val
, val2
) :
862 pm_match_set(pos
+ i
, mask
->item
[m
].set
)))
863 pm_mark(pos
, i
, plen
, &nl
, &nh
);
875 return pos
[plen
].mark
;