diff --git a/doc/bird.sgml b/doc/bird.sgml
index 1808d04c..786f124b 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -1471,11 +1471,16 @@ in the foot).
bgp_path
is 4 3 2 1, then: bgp_path ˜ [= * 4 3 * =] is true,
- but bgp_path ˜ [= * 4 5 * =] is false. BGP mask
- expressions can also contain integer expressions enclosed in parenthesis
- and integer variables, for example [= * 4 (1+2) a =]. You can
- also use ranges (e.g. [= * 3..5 2 100..200 * =]) and sets
- (e.g. [= 1 2 [3, 5, 7] * =]).
+ but bgp_path ˜ [= * 4 5 * =] is false. There is also
+ [= 1 2+ 3 =] matches both path 1 2 3 and path
+ 1 2 2 2 3, but not 1 3 nor 1 2 4 3. Note that while [= * 4 (1+2) a =].
+ You can also use ranges (e.g. [= * 3..5 2 100..200 * =])
+ and sets (e.g. [= 1 2 [3, 5, 7] * =]).
Clist is similar to a set, except that unlike other sets, it can be
diff --git a/filter/config.Y b/filter/config.Y
index 77424a8b..557a951f 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -662,6 +662,7 @@ bgp_path_tail:
}
| '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }); $$->next = $2; }
| '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }); $$->next = $2; }
+ | '+' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_LOOP }, }); $$->next = $2; }
| bgp_path_expr bgp_path_tail { $$ = $1; $$->next = $2; }
| { $$ = NULL; }
;
diff --git a/filter/data.c b/filter/data.c
index 62edd9e0..40220255 100644
--- a/filter/data.c
+++ b/filter/data.c
@@ -93,6 +93,8 @@ adata_empty(struct linpool *pool, int l)
static void
pm_format(const struct f_path_mask *p, buffer *buf)
{
+ int loop = 0;
+
buffer_puts(buf, "[= ");
for (uint i=0; ilen; i++)
@@ -111,6 +113,10 @@ pm_format(const struct f_path_mask *p, buffer *buf)
buffer_puts(buf, "* ");
break;
+ case PM_LOOP:
+ loop = 1;
+ break;
+
case PM_ASN_RANGE:
buffer_print(buf, "%u..%u ", p->item[i].from, p->item[i].to);
break;
@@ -119,6 +125,11 @@ pm_format(const struct f_path_mask *p, buffer *buf)
ASSERT(0);
}
+ if (loop && (p->item[i].kind != PM_LOOP))
+ {
+ buffer_puts(buf, "+ ");
+ loop = 0;
+ }
}
buffer_puts(buf, "=]");
diff --git a/filter/f-inst.c b/filter/f-inst.c
index 63a6bdab..df908e26 100644
--- a/filter/f-inst.c
+++ b/filter/f-inst.c
@@ -311,6 +311,17 @@
for (uint i=0; ivarcount; i++) {
switch (vv(i).type) {
case T_PATH_MASK_ITEM:
+ if (vv(i).val.pmi.kind == PM_LOOP)
+ {
+ if (i == 0)
+ runtime("Path mask iterator '+' cannot be first");
+
+ /* We want PM_LOOP as prefix operator */
+ pm->item[i] = pm->item[i - 1];
+ pm->item[i - 1] = vv(i).val.pmi;
+ break;
+ }
+
pm->item[i] = vv(i).val.pmi;
break;
diff --git a/filter/test.conf b/filter/test.conf
index 634816fd..63af25bb 100644
--- a/filter/test.conf
+++ b/filter/test.conf
@@ -646,7 +646,6 @@ int set set12;
bt_assert(delete(p2, 3) = prepend(prepend(prepend(prepend(+empty+, 1), 2), 4), 5));
bt_assert(filter(p2, [1..3]) = prepend(prepend(prepend(+empty+, 1), 2), 3));
- pm1 = [= 1 2 * 3 4 5 =];
p2 = prepend( + empty +, 5 );
p2 = prepend( p2, 4 );
p2 = prepend( p2, 3 );
@@ -654,9 +653,17 @@ int set set12;
p2 = prepend( p2, 2 );
p2 = prepend( p2, 1 );
- bt_assert(p2 ~ pm1);
+ bt_assert(p2 !~ [= 1 2 3 4 5 =]);
+ bt_assert(p2 ~ [= 1 2 * 4 5 =]);
+ bt_assert(p2 ~ [= 1 2 * 3 4 5 =]);
+ bt_assert(p2 ~ [= 1 2 3+ 4 5 =]);
+ bt_assert(p2 ~ [= 1 2 3+ 4+ 5 =]);
+ bt_assert(p2 !~ [= 1 2 3+ 5+ 4 5 =]);
+ bt_assert(p2 !~ [= 1 2 3 3 5+ 4 5 =]);
bt_assert(delete(p2, 3) = prepend(prepend(prepend(prepend(+empty+, 5), 4), 2), 1));
bt_assert(delete(p2, [4..5]) = prepend(prepend(prepend(prepend(+empty+, 3), 3), 2), 1));
+
+ bt_assert(format([= 1 2+ 3 =]) = "[= 1 2 + 3 =]");
}
bt_test_suite(t_path, "Testing paths");
diff --git a/nest/a-path.c b/nest/a-path.c
index cffd46ab..2e34a3d1 100644
--- a/nest/a-path.c
+++ b/nest/a-path.c
@@ -727,7 +727,7 @@ parse_path(const struct adata *path, struct pm_pos *pp)
}
static int
-pm_match(struct pm_pos *pos, u32 asn, u32 asn2)
+pm_match_val(const struct pm_pos *pos, u32 asn, u32 asn2)
{
u32 gas;
if (! pos->set)
@@ -749,7 +749,7 @@ pm_match(struct pm_pos *pos, u32 asn, u32 asn2)
}
static int
-pm_match_set(struct pm_pos *pos, const struct f_tree *set)
+pm_match_set(const struct pm_pos *pos, const struct f_tree *set)
{
struct f_val asn = { .type = T_INT };
@@ -773,25 +773,34 @@ pm_match_set(struct pm_pos *pos, const struct f_tree *set)
return 0;
}
-static void
-pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh)
+static inline int
+pm_match(const struct pm_pos *pos, const struct f_path_mask_item *mask, u32 asn, u32 asn2)
{
- int j;
+ return ((mask->kind == PM_QUESTION) ||
+ ((mask->kind != PM_ASN_SET) ?
+ pm_match_val(pos, asn, asn2) :
+ pm_match_set(pos, mask->set)));
+}
- if (pos[i].set)
- pos[i].mark = 1;
+static void
+pm_mark(struct pm_pos *pos, int *i, int plen, int *nl, int *nh)
+{
+ int j = *i;
+
+ if (pos[j].set)
+ do { pos[j].mark = 1; j++; }
+ while ((j < plen) && pos[j].set);
+ else
+ j++;
- 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 */
+ /* Update low, high based on first and last marked pos */
+ int l = pos[*i].set ? *i : j;
- *nl = i + (pos[i].set ? 0 : 1);
-
- if (*nh < 0)
- *nh = j;
+ *nl = (*nl < 0) ? l : MIN(*nl, l);
+ *nh = MAX(*nh, j);
+ *i = j;
}
/* AS path matching is nontrivial. Because AS path can
@@ -821,7 +830,7 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
{
struct pm_pos pos[2048 + 1];
int plen = parse_path(path, pos);
- int l, h, i, nh, nl;
+ int l, h, i, nh, nl, last, loop;
u32 val = 0;
u32 val2 = 0;
@@ -831,7 +840,7 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
pos[plen].set = 0;
pos[plen].mark = 0;
- l = h = 0;
+ l = h = loop = 0;
pos[0].mark = 1;
for (uint m=0; m < mask->len; m++)
@@ -847,6 +856,10 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
h = plen;
break;
+ case PM_LOOP:
+ loop = 1;
+ break;
+
case PM_ASN: /* Define single ASN as ASN..ASN - very narrow interval */
val2 = val = mask->item[m].asn;
goto step;
@@ -860,15 +873,22 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
case PM_ASN_SET:
step:
nh = nl = -1;
+ last = plen;
for (i = h; i >= l; i--)
if (pos[i].mark)
{
pos[i].mark = 0;
- if ((mask->item[m].kind == PM_QUESTION) ||
- ((mask->item[m].kind != PM_ASN_SET) ?
- pm_match(pos + i, val, val2) :
- pm_match_set(pos + i, mask->item[m].set)))
- pm_mark(pos, i, plen, &nl, &nh);
+ int j = i;
+
+ match:
+ if (pm_match(pos + j, &mask->item[m], val, val2))
+ {
+ pm_mark(pos, &j, plen, &nl, &nh);
+ if (loop && (j < last))
+ goto match;
+ }
+
+ last = i;
}
if (nh < 0)
@@ -876,6 +896,7 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
h = nh;
l = nl;
+ loop = 0;
break;
}
}
diff --git a/nest/attrs.h b/nest/attrs.h
index d88a73f3..50da817b 100644
--- a/nest/attrs.h
+++ b/nest/attrs.h
@@ -61,6 +61,7 @@ static inline struct adata *as_path_prepend(struct linpool *pool, const struct a
#define PM_ASN_EXPR 3
#define PM_ASN_RANGE 4
#define PM_ASN_SET 5
+#define PM_LOOP 6
struct f_path_mask_item {
union {