Hi, I suggest to add a new operator: prepend_times(P, A, N) - that will allow to prepend AS A to path P several times (N). Now we need to use some function like this:
function add_prepends(int asn, int num) { if num = 1 then { bgp_path.prepend(asn); } else if num = 2 then { bgp_path.prepend(asn); bgp_path.prepend(asn); } else if num = 3 then { ... } Usually the number of prepends is observable, so this function can fit most of cases, but with available operator it will be a bit more convenient, a little syntactic sugar. But it is not something that is hard to live without, of course. In my patch, I chose to "join" implementations of prepend() and prepend_times(). It is questionable, because it requires some additional supporting stuff if I understood right. So may be it is better to implement it as separate operators.
commit 876c19f58a5814241bd5110f6cd2f6889a581691 Author: Alexander Zubkov <gr...@qrator.net> Date: Sat Jan 1 06:05:45 2022 +0100 Filter: add operator to prepend bgppath multiple times New operator allows to prepend ASN A to path P N times: prepend_times(P, A, N) P.prepend_times(A, N) diff --git a/filter/config.Y b/filter/config.Y index 8916ea97..78228a1f 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -22,6 +22,12 @@ static inline u32 pair_b(u32 p) { return p & 0xFFFF; } #define f_generate_complex(fi_code, da, arg) \ f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da) +#define f_generate_complex2(fi_code, da, arg1, arg2) \ + f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg1, arg2), da) + +#define f_new_int_const(arg) \ + f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = arg, }) + /* * Sets and their items are during parsing handled as lists, linked * through left ptr. The first item in a list also contains a pointer @@ -286,7 +292,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, DATA, DATA1, DATA2, DEFINED, ADD, DELETE, CONTAINS, RESET, - PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH, + PREPEND, PREPEND_TIMES, FIRST, LAST, LAST_NONAGGREGATED, MATCH, MIN, MAX, EMPTY, FILTER, WHERE, EVAL, ATTRIBUTE, @@ -816,7 +822,8 @@ term: | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_clist); } | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_eclist); } | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_lclist); } - | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); } + | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5, f_new_int_const(1)); } + | PREPEND_TIMES '(' term ',' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5, $7); } | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); } | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); } | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); } @@ -908,7 +915,8 @@ cmd: } | dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($1); } - | dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, $1, $5 ); } + | dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex2( FI_PATH_PREPEND, $1, $5, f_new_int_const(1) ); } + | dynamic_attr '.' PREPEND_TIMES '(' term ',' term ')' ';' { $$ = f_generate_complex2( FI_PATH_PREPEND, $1, $5, $7 ); } | dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD, $1, $5 ); } | dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_DEL, $1, $5 ); } | dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_FILTER, $1, $5 ); } diff --git a/filter/f-inst.c b/filter/f-inst.c index 901d2939..5974f343 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -1143,10 +1143,15 @@ ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))) ]]); } - INST(FI_PATH_PREPEND, 2, 1) { /* Path prepend */ + INST(FI_PATH_PREPEND, 3, 1) { /* Path prepend */ ARG(1, T_PATH); ARG(2, T_INT); - RESULT(T_PATH, ad, [[ as_path_prepend(fpool, v1.val.ad, v2.val.i) ]]); + ARG(3, T_INT); + struct adata *path = v1.val.ad; + for (int i = 0; i < v3.val.i; ++i) { + path = as_path_prepend(fpool, path, v2.val.i); + } + RESULT(T_PATH, ad, path); } INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */ diff --git a/filter/test.conf b/filter/test.conf index 6a28e4b3..2a64792f 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -665,6 +665,14 @@ int set set12; bt_assert(delete(p2, [4..5]) = prepend(prepend(prepend(prepend(+empty+, 3), 3), 2), 1)); bt_assert(format([= 1 2+ 3 =]) = "[= 1 2 + 3 =]"); + + p2 = prepend_times( + empty +, 1, 3 ); + p2 = prepend_times( p2, 2, 2 ); + p2 = prepend_times( p2, 3, 0 ); + p2 = prepend_times( p2, 4, 1 ); + + bt_assert(format(p2) = "(path 4 2 2 1 1 1)"); + bt_assert(p2.len = 6); } bt_test_suite(t_path, "Testing paths");