diff --git a/doc/bird.sgml b/doc/bird.sgml index 1cb3fc4f..70d970f7 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -549,7 +549,8 @@ incompatible with each other (that is to prevent you from shooting in the foot). (using path ˜ [= 2 3 5 * =] syntax). The masks resemble wildcard patterns as used by UNIX shells. Autonomous system numbers match themselves, bgp_path is 4 3 2 1, then: + sequence of arbitrary AS numbers and bgp_path is 4 3 2 1, then: bgp_path ˜ [= * 4 3 * =] is true, but bgp_path ˜ [= * 4 5 * =] is false. There is also old syntax that uses / .. / instead of [= .. =] and ? instead of *. diff --git a/filter/config.Y b/filter/config.Y index b6a0f470..6d9b064d 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -284,14 +284,15 @@ bgp_path: ; bgp_path_tail1: - NUM bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->val = $1; $$->any = 0; } - | '*' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->val = 0; $$->any = 1; } + NUM bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } + | '*' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val = 0; } + | '?' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; $$->val = 0; } | { $$ = NULL; } ; bgp_path_tail2: - NUM bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->val = $1; $$->any = 0; } - | '?' bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->val = 0; $$->any = 1; } + NUM bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } + | '?' bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val = 0; } | { $$ = NULL; } ; diff --git a/filter/filter.c b/filter/filter.c index 2e13c752..313d2fa7 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -82,10 +82,10 @@ pm_format(struct f_path_mask *p, byte *buf, unsigned int size) return; } - if (p->any) - buf += bsprintf(buf, " *"); - else + if (p->kind == PM_ASN) buf += bsprintf(buf, " %u", p->val); + else + buf += bsprintf(buf, (p->kind == PM_ASTERISK) ? " *" : " ?"); p = p->next; } diff --git a/nest/a-path.c b/nest/a-path.c index 7ac50e1b..f5499877 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -280,79 +280,171 @@ as_path_is_member(struct adata *path, u32 as) } +struct pm_pos +{ + u8 set; + u8 mark; + union + { + char *sp; + u32 asn; + } val; +}; -#define MASK_PLUS do { mask = mask->next; if (!mask) return next == q; \ - asterisk = mask->any; \ - if (asterisk) { mask = mask->next; if (!mask) { return 1; } } \ - } while(0) +static int +parse_path(struct adata *path, struct pm_pos *pos) +{ + int bs = bgp_as4_support ? 4 : 2; + u8 *p = path->data; + u8 *q = p + path->length; + struct pm_pos *opos = pos; + int i, j, len; + + + while (p < q) + switch (*p++) + { + case AS_PATH_SET: + pos->set = 1; + pos->mark = 0; + pos->val.sp = p; + len = *p; + p += 1 + bs * len; + pos++; + break; + + case AS_PATH_SEQUENCE: + len = *p++; + for (i = 0; i < len; i++) + { + pos->set = 0; + pos->mark = 0; + pos->val.asn = get_as(p); + p += bs; + pos++; + } + break; + + default: + bug("as_path_match: Invalid path component"); + } + + return pos - opos; +} + + +static int +pm_match(struct pm_pos *pos, u32 asn) +{ + if (! pos->set) + return pos->val.asn == asn; + + int bs = bgp_as4_support ? 4 : 2; + u8 *p = pos->val.sp; + int len = *p++; + int i; + + for (i = 0; i < len; i++) + if (get_as(p + i * bs) == asn) + return 1; + + return 0; +} + +static void +pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh) +{ + int j; + + if (pos[i].set) + pos[i].mark = 1; + + for (j = i + 1; (j < plen) && pos[j].set && (! pos[j].mark); j++) + pos[j].mark = 1; + pos[j].mark = 1; + + /* We are going downwards, therefore every mark is + new low and just the first mark is new high */ + + *nl = i + (pos[i].set ? 0 : 1); + + if (*nh < 0) + *nh = j; +} + +/* AS path matching is nontrivial. Because AS path can + * contain sets, it is not a plain wildcard matching. A set + * in an AS path is interpreted as it might represent any + * sequence of AS numbers from that set (possibly with + * repetitions). So it is also a kind of a pattern, + * more complicated than a path mask. + * + * The algorithm for AS path matching is a variant + * of nondeterministic finite state machine, where + * positions in AS path are states, and items in + * path mask are input for that finite state machine. + * During execution of the algorithm we maintain a set + * of marked states - a state is marked if it can be + * reached by any walk through NFSM with regard to + * currently processed part of input. When we process + * next part of mask, we advance each marked state. + * We start with marked first position, when we + * run out of marked positions, we reject. When + * we process the whole mask, we accept iff final position + * (auxiliary position after last real position in AS path) + * is marked. + */ int as_path_match(struct adata *path, struct f_path_mask *mask) { - int bs = bgp_as4_support ? 4 : 2; - int i; - int asterisk = 0; - u8 *p = path->data; - u8 *q = p+path->length; - int len; - u8 *next; - u32 as; + struct pm_pos pos[2048 + 1]; + int plen = parse_path(path, pos); + int l, h, i, nh, nl; - if (!mask) - return ! path->length; + /* l and h are bound of interval of positions where + are marked states */ - asterisk = mask->any; - if (asterisk) - { mask = mask->next; if (!mask) return 1; } + pos[plen].set = 0; + pos[plen].mark = 0; - while (pval)) { - MASK_PLUS; - goto retry; - } - if (!asterisk && (as == mask->val)) { - p = next; - MASK_PLUS; - goto okay; - } - p += bs; - } - if (!asterisk) - return 0; - okay: ; - } - break; + l = h = 0; + pos[0].mark = 1; + + while (mask) + { + /* We remove this mark to not step after pos[plen] */ + pos[plen].mark = 0; - case AS_PATH_SEQUENCE: - len = *p++; - for (i=0; ival)) - MASK_PLUS; - else if (!asterisk) { - if (as != mask->val) + switch (mask->kind) + { + case PM_ASTERISK: + for (i = l; i <= plen; i++) + pos[i].mark = 1; + h = plen; + break; + + case PM_QUESTION: + case PM_ASN: + nh = -1; + for (i = h; i >= l; i--) + if (pos[i].mark) + { + pos[i].mark = 0; + if ((mask->kind == PM_QUESTION) || pm_match(pos + i, mask->val)) + pm_mark(pos, i, plen, &nl, &nh); + } + + if (nh < 0) return 0; - MASK_PLUS; + + h = nh; + l = nl; + break; } - p += bs; - } - break; - default: - bug("as_path_match: Invalid path component"); + mask = mask->next; } - } - return 0; -} + return pos[plen].mark; +} diff --git a/nest/attrs.h b/nest/attrs.h index c12ce81d..5542be6f 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -32,15 +32,16 @@ int as_path_get_first(struct adata *path, u32 *orig_as); int as_path_get_last(struct adata *path, u32 *last_as); int as_path_is_member(struct adata *path, u32 as); +#define PM_ASN 0 +#define PM_QUESTION 1 +#define PM_ASTERISK 2 struct f_path_mask { struct f_path_mask *next; + int kind; u32 val; - int any; }; -// #define PM_ANY -1 - int as_path_match(struct adata *path, struct f_path_mask *mask); /* a-set.c */