mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Filter: Implement for loops
For loops allow to iterate over elements in compound data like BGP paths or community lists. The syntax is: for [ <type> ] <variable> in <expr> do <command-body>
This commit is contained in:
parent
1ac8e11bba
commit
cb339a3067
@ -1683,7 +1683,8 @@ prefix and an ASN as arguments.
|
|||||||
<sect>Control structures
|
<sect>Control structures
|
||||||
<label id="control-structures">
|
<label id="control-structures">
|
||||||
|
|
||||||
<p>Filters support two control structures: conditions and case switches.
|
<p>Filters support several control structures: conditions, for loops and case
|
||||||
|
switches.
|
||||||
|
|
||||||
<p>Syntax of a condition is: <cf>if <M>boolean expression</M> then <m/commandT/;
|
<p>Syntax of a condition is: <cf>if <M>boolean expression</M> then <m/commandT/;
|
||||||
else <m/commandF/;</cf> and you can use <cf>{ <m/command1/; <m/command2/;
|
else <m/commandF/;</cf> and you can use <cf>{ <m/command1/; <m/command2/;
|
||||||
@ -1691,6 +1692,14 @@ else <m/commandF/;</cf> and you can use <cf>{ <m/command1/; <m/command2/;
|
|||||||
omitted. If the <cf><m>boolean expression</m></cf> is true, <m/commandT/ is
|
omitted. If the <cf><m>boolean expression</m></cf> is true, <m/commandT/ is
|
||||||
executed, otherwise <m/commandF/ is executed.
|
executed, otherwise <m/commandF/ is executed.
|
||||||
|
|
||||||
|
<p>For loops allow to iterate over elements in compound data like BGP paths or
|
||||||
|
community lists. The syntax is: <cf>for [ <m/type/ ] <m/variable/ in <m/expr/
|
||||||
|
do <m/command/;</cf> and you can also use compound command like in conditions.
|
||||||
|
The expression is evaluated to a compound data, then for each element from such
|
||||||
|
data the command is executed with the item assigned to the variable. A variable
|
||||||
|
may be an existing one (when just name is used) or a locally defined (when type
|
||||||
|
and name is used). In both cases, it must have the same type as elements.
|
||||||
|
|
||||||
<p>The <cf>case</cf> is similar to case from Pascal. Syntax is <cf>case
|
<p>The <cf>case</cf> is similar to case from Pascal. Syntax is <cf>case
|
||||||
<m/expr/ { else: | <m/num_or_prefix [ .. num_or_prefix]/: <m/statement/ ; [
|
<m/expr/ { else: | <m/num_or_prefix [ .. num_or_prefix]/: <m/statement/ ; [
|
||||||
... ] }</cf>. The expression after <cf>case</cf> can be of any type which can be
|
... ] }</cf>. The expression after <cf>case</cf> can be of any type which can be
|
||||||
@ -1703,16 +1712,21 @@ neither of the <cf/:/ clauses, the statements after <cf/else:/ are executed.
|
|||||||
<p>Here is example that uses <cf/if/ and <cf/case/ structures:
|
<p>Here is example that uses <cf/if/ and <cf/case/ structures:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
|
if 1234 = i then printn "."; else {
|
||||||
|
print "not 1234";
|
||||||
|
print "You need {} around multiple commands";
|
||||||
|
}
|
||||||
|
|
||||||
|
for int asn in bgp_path do {
|
||||||
|
printn "ASN: ", asn;
|
||||||
|
if asn < 65536 then print " (2B)"; else print " (4B)";
|
||||||
|
}
|
||||||
|
|
||||||
case arg1 {
|
case arg1 {
|
||||||
2: print "two"; print "I can do more commands without {}";
|
2: print "two"; print "I can do more commands without {}";
|
||||||
3 .. 5: print "three to five";
|
3 .. 5: print "three to five";
|
||||||
else: print "something else";
|
else: print "something else";
|
||||||
}
|
}
|
||||||
|
|
||||||
if 1234 = i then printn "."; else {
|
|
||||||
print "not 1234";
|
|
||||||
print "You need {} around multiple commands";
|
|
||||||
}
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const
|
|||||||
|
|
||||||
checker = f_new_inst(FI_EQ, expr, getter);
|
checker = f_new_inst(FI_EQ, expr, getter);
|
||||||
setter->next = checker;
|
setter->next = checker;
|
||||||
|
|
||||||
return assert_done(setter, start, end);
|
return assert_done(setter, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,6 +307,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
|||||||
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
|
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
|
||||||
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
|
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
|
||||||
IF, THEN, ELSE, CASE,
|
IF, THEN, ELSE, CASE,
|
||||||
|
FOR, IN, DO,
|
||||||
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
||||||
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
|
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
|
||||||
PREFERENCE,
|
PREFERENCE,
|
||||||
@ -342,6 +343,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
|||||||
%type <v> set_atom switch_atom fipa
|
%type <v> set_atom switch_atom fipa
|
||||||
%type <px> fprefix
|
%type <px> fprefix
|
||||||
%type <t> get_cf_position
|
%type <t> get_cf_position
|
||||||
|
%type <s> for_var
|
||||||
|
|
||||||
CF_GRAMMAR
|
CF_GRAMMAR
|
||||||
|
|
||||||
@ -899,6 +901,11 @@ var:
|
|||||||
$$ = f_new_inst(FI_VAR_INIT, $3, sym);
|
$$ = f_new_inst(FI_VAR_INIT, $3, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for_var:
|
||||||
|
type symbol { $$ = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
|
||||||
|
| CF_SYM_KNOWN { $$ = $1; cf_assert_symbol($1, SYM_VARIABLE); }
|
||||||
|
;
|
||||||
|
|
||||||
cmd:
|
cmd:
|
||||||
'{' cmds_scoped '}' {
|
'{' cmds_scoped '}' {
|
||||||
$$ = $2;
|
$$ = $2;
|
||||||
@ -909,6 +916,18 @@ cmd:
|
|||||||
| IF term THEN cmd ELSE cmd {
|
| IF term THEN cmd ELSE cmd {
|
||||||
$$ = f_new_inst(FI_CONDITION, $2, $4, $6);
|
$$ = f_new_inst(FI_CONDITION, $2, $4, $6);
|
||||||
}
|
}
|
||||||
|
| FOR {
|
||||||
|
/* Reserve space for walk data on stack */
|
||||||
|
cf_push_scope(NULL);
|
||||||
|
conf_this_scope->slots += 2;
|
||||||
|
} for_var IN
|
||||||
|
/* Parse term in the parent scope */
|
||||||
|
{ conf_this_scope->active = 0; } term { conf_this_scope->active = 1; }
|
||||||
|
DO cmd {
|
||||||
|
cf_pop_scope();
|
||||||
|
$$ = f_new_inst(FI_FOR_INIT, $6, $3);
|
||||||
|
$$->next = f_new_inst(FI_FOR_NEXT, $3, $9);
|
||||||
|
}
|
||||||
| CF_SYM_KNOWN '=' term ';' {
|
| CF_SYM_KNOWN '=' term ';' {
|
||||||
switch ($1->class) {
|
switch ($1->class) {
|
||||||
case SYM_VARIABLE_RANGE:
|
case SYM_VARIABLE_RANGE:
|
||||||
|
@ -72,6 +72,7 @@ enum f_type
|
|||||||
f_type_element_type(enum f_type t)
|
f_type_element_type(enum f_type t)
|
||||||
{
|
{
|
||||||
switch(t) {
|
switch(t) {
|
||||||
|
case T_PATH: return T_INT;
|
||||||
case T_CLIST: return T_PAIR;
|
case T_CLIST: return T_PAIR;
|
||||||
case T_ECLIST: return T_EC;
|
case T_ECLIST: return T_EC;
|
||||||
case T_LCLIST: return T_LC;
|
case T_LCLIST: return T_LC;
|
||||||
|
@ -87,6 +87,11 @@
|
|||||||
* m4_dnl RESULT_VOID; return undef
|
* m4_dnl RESULT_VOID; return undef
|
||||||
* m4_dnl }
|
* m4_dnl }
|
||||||
*
|
*
|
||||||
|
* Note that runtime arguments m4_dnl (ARG*, VARARG) must be defined before
|
||||||
|
* parse-time arguments m4_dnl (LINE, SYMBOL, ...). During linearization,
|
||||||
|
* first ones move position in f_line by linearizing arguments first, while
|
||||||
|
* second ones store data to the current position.
|
||||||
|
*
|
||||||
* Also note that the { ... } blocks are not respected by M4 at all.
|
* Also note that the { ... } blocks are not respected by M4 at all.
|
||||||
* If you get weird unmatched-brace-pair errors, check what it generated and why.
|
* If you get weird unmatched-brace-pair errors, check what it generated and why.
|
||||||
* What is really considered as one instruction is not the { ... } block
|
* What is really considered as one instruction is not the { ... } block
|
||||||
@ -543,6 +548,94 @@
|
|||||||
RESULT_VAL(val);
|
RESULT_VAL(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INST(FI_FOR_INIT, 1, 0) {
|
||||||
|
NEVER_CONSTANT;
|
||||||
|
ARG_ANY(1);
|
||||||
|
SYMBOL;
|
||||||
|
|
||||||
|
FID_NEW_BODY()
|
||||||
|
ASSERT((sym->class & ~0xff) == SYM_VARIABLE);
|
||||||
|
|
||||||
|
/* Static type check */
|
||||||
|
if (f1->type)
|
||||||
|
{
|
||||||
|
enum f_type t_var = (sym->class & 0xff);
|
||||||
|
enum f_type t_arg = f_type_element_type(f1->type);
|
||||||
|
if (!t_arg)
|
||||||
|
cf_error("Value of expression in FOR must be iterable, got %s",
|
||||||
|
f_type_name(f1->type));
|
||||||
|
if (t_var != t_arg)
|
||||||
|
cf_error("Loop variable '%s' in FOR must be %s, is %s",
|
||||||
|
sym->name, f_type_name(t_arg), f_type_name(t_var));
|
||||||
|
}
|
||||||
|
|
||||||
|
FID_INTERPRET_BODY()
|
||||||
|
|
||||||
|
/* Dynamic type check */
|
||||||
|
if ((sym->class & 0xff) != f_type_element_type(v1.type))
|
||||||
|
runtime("Mismatched argument and variable type");
|
||||||
|
|
||||||
|
/* Setup the index */
|
||||||
|
v2 = (struct f_val) { .type = T_INT, .val.i = 0 };
|
||||||
|
|
||||||
|
/* Keep v1 and v2 on the stack */
|
||||||
|
fstk->vcnt += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
INST(FI_FOR_NEXT, 2, 0) {
|
||||||
|
NEVER_CONSTANT;
|
||||||
|
SYMBOL;
|
||||||
|
|
||||||
|
/* Type checks are done in FI_FOR_INIT */
|
||||||
|
|
||||||
|
/* Loop variable */
|
||||||
|
struct f_val *var = &fstk->vstk[curline.vbase + sym->offset];
|
||||||
|
int step = 0;
|
||||||
|
|
||||||
|
switch(v1.type)
|
||||||
|
{
|
||||||
|
case T_PATH:
|
||||||
|
var->type = T_INT;
|
||||||
|
step = as_path_walk(v1.val.ad, &v2.val.i, &var->val.i);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_CLIST:
|
||||||
|
var->type = T_PAIR;
|
||||||
|
step = int_set_walk(v1.val.ad, &v2.val.i, &var->val.i);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_ECLIST:
|
||||||
|
var->type = T_EC;
|
||||||
|
step = ec_set_walk(v1.val.ad, &v2.val.i, &var->val.ec);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_LCLIST:
|
||||||
|
var->type = T_LC;
|
||||||
|
step = lc_set_walk(v1.val.ad, &v2.val.i, &var->val.lc);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
runtime( "Clist or lclist expected" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step)
|
||||||
|
{
|
||||||
|
/* Keep v1 and v2 on the stack */
|
||||||
|
fstk->vcnt += 2;
|
||||||
|
|
||||||
|
/* Repeat this instruction */
|
||||||
|
curline.pos--;
|
||||||
|
|
||||||
|
/* Execute the loop body */
|
||||||
|
LINE(1, 0);
|
||||||
|
|
||||||
|
/* Space for loop variable, may be unused */
|
||||||
|
fstk->vcnt += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
var->type = T_VOID;
|
||||||
|
}
|
||||||
|
|
||||||
INST(FI_CONDITION, 1, 0) {
|
INST(FI_CONDITION, 1, 0) {
|
||||||
ARG(1, T_BOOL);
|
ARG(1, T_BOOL);
|
||||||
if (v1.val.i)
|
if (v1.val.i)
|
||||||
|
@ -758,6 +758,15 @@ int set set12;
|
|||||||
bt_assert(delete(p2, [4..5]) = prepend(prepend(prepend(prepend(+empty+, 3), 3), 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_assert(format([= 1 2+ 3 =]) = "[= 1 2 + 3 =]");
|
||||||
|
|
||||||
|
# iteration over path
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
for int i in p2 do {
|
||||||
|
x = x + i;
|
||||||
|
y = y + x;
|
||||||
|
}
|
||||||
|
bt_assert(x = 18 && y = 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_test_suite(t_path, "Testing paths");
|
bt_test_suite(t_path, "Testing paths");
|
||||||
@ -884,6 +893,12 @@ clist r;
|
|||||||
bt_assert(format(r) = "(clist (2,1) (1,3) (2,2) (3,1) (2,3))");
|
bt_assert(format(r) = "(clist (2,1) (1,3) (2,2) (3,1) (2,3))");
|
||||||
bt_assert(r.min = (1,3));
|
bt_assert(r.min = (1,3));
|
||||||
bt_assert(r.max = (3,1));
|
bt_assert(r.max = (3,1));
|
||||||
|
|
||||||
|
# iteration over clist
|
||||||
|
int x = 0;
|
||||||
|
for pair c in r do
|
||||||
|
x = x + c.asn * c.asn * c.data;
|
||||||
|
bt_assert(x = 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_test_suite(t_clist, "Testing lists of communities");
|
bt_test_suite(t_clist, "Testing lists of communities");
|
||||||
@ -999,6 +1014,13 @@ eclist r;
|
|||||||
bt_assert(format(r) = "(eclist (rt, 2, 1) (rt, 1, 3) (rt, 2, 2) (rt, 3, 1) (rt, 2, 3))");
|
bt_assert(format(r) = "(eclist (rt, 2, 1) (rt, 1, 3) (rt, 2, 2) (rt, 3, 1) (rt, 2, 3))");
|
||||||
bt_assert(r.min = (rt, 1, 3));
|
bt_assert(r.min = (rt, 1, 3));
|
||||||
bt_assert(r.max = (rt, 3, 1));
|
bt_assert(r.max = (rt, 3, 1));
|
||||||
|
|
||||||
|
# iteration over eclist
|
||||||
|
int x = 0;
|
||||||
|
for ec c in r do
|
||||||
|
if c > (rt, 2, 0) && c < (rt, 3, 0) then
|
||||||
|
x = x + 1;
|
||||||
|
bt_assert(x = 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_test_suite(t_eclist, "Testing lists of extended communities");
|
bt_test_suite(t_eclist, "Testing lists of extended communities");
|
||||||
@ -1117,6 +1139,19 @@ lclist r;
|
|||||||
bt_assert(format(r) = "(lclist (2, 3, 3) (1, 2, 3) (2, 3, 1) (3, 1, 2) (2, 1, 3))");
|
bt_assert(format(r) = "(lclist (2, 3, 3) (1, 2, 3) (2, 3, 1) (3, 1, 2) (2, 1, 3))");
|
||||||
bt_assert(r.min = (1, 2, 3));
|
bt_assert(r.min = (1, 2, 3));
|
||||||
bt_assert(r.max = (3, 1, 2));
|
bt_assert(r.max = (3, 1, 2));
|
||||||
|
|
||||||
|
# iteration over lclist
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
lc mx = (0, 0, 0);
|
||||||
|
for lc c in r do {
|
||||||
|
int asn2 = c.asn * c.asn;
|
||||||
|
x = x + asn2 * c.data1;
|
||||||
|
y = y + asn2 * c.data2;
|
||||||
|
if c > mx then mx = c;
|
||||||
|
}
|
||||||
|
bt_assert(x = 39 && y = 49);
|
||||||
|
bt_assert(mx = r.max);
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_test_suite(t_lclist, "Testing lists of large communities");
|
bt_test_suite(t_lclist, "Testing lists of large communities");
|
||||||
@ -1580,13 +1615,16 @@ filter vpn_filter
|
|||||||
bt_assert(net.type != NET_IP6);
|
bt_assert(net.type != NET_IP6);
|
||||||
bt_assert(net.rd = 0:1:2);
|
bt_assert(net.rd = 0:1:2);
|
||||||
|
|
||||||
|
bool b = false;
|
||||||
case (net.type) {
|
case (net.type) {
|
||||||
NET_IP4: print "IPV4";
|
NET_IP4: print "IPV4";
|
||||||
NET_IP6: print "IPV6";
|
NET_IP6: print "IPV6";
|
||||||
|
else: b = true;
|
||||||
}
|
}
|
||||||
|
bt_assert(b);
|
||||||
|
|
||||||
bt_check_assign(from, 10.20.30.40);
|
bt_check_assign(from, 10.20.30.40);
|
||||||
bt_check_assign(gw, 55.55.55.44);
|
# bt_check_assign(gw, 55.55.55.44);
|
||||||
|
|
||||||
bgp_community.add((3,5));
|
bgp_community.add((3,5));
|
||||||
bgp_ext_community.add((ro, 135, 999));
|
bgp_ext_community.add((ro, 135, 999));
|
||||||
|
@ -669,6 +669,35 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_va
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
as_path_walk(const struct adata *path, uint *pos, uint *val)
|
||||||
|
{
|
||||||
|
if (!path)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const u8 *p = path->data;
|
||||||
|
const u8 *q = p + path->length;
|
||||||
|
uint n, x = *pos;
|
||||||
|
|
||||||
|
while (p < q)
|
||||||
|
{
|
||||||
|
n = p[1];
|
||||||
|
p += 2;
|
||||||
|
|
||||||
|
if (x < n)
|
||||||
|
{
|
||||||
|
*val = get_as(p + x * BS);
|
||||||
|
*pos += 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
p += n * BS;
|
||||||
|
x -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct pm_pos
|
struct pm_pos
|
||||||
{
|
{
|
||||||
|
48
nest/a-set.c
48
nest/a-set.c
@ -693,3 +693,51 @@ lc_set_max(const struct adata *list, lcomm *val)
|
|||||||
*val = (lcomm) { res[0], res[1], res[2] };
|
*val = (lcomm) { res[0], res[1], res[2] };
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
int_set_walk(const struct adata *list, uint *pos, uint *val)
|
||||||
|
{
|
||||||
|
if (!list)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (*pos >= (uint) int_set_get_size(list))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
u32 *res = int_set_get_data(list) + *pos;
|
||||||
|
*val = *res;
|
||||||
|
*pos += 1;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ec_set_walk(const struct adata *list, uint *pos, u64 *val)
|
||||||
|
{
|
||||||
|
if (!list)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (*pos >= (uint) int_set_get_size(list))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
u32 *res = int_set_get_data(list) + *pos;
|
||||||
|
*val = ec_generic(res[0], res[1]);
|
||||||
|
*pos += 2;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
lc_set_walk(const struct adata *list, uint *pos, lcomm *val)
|
||||||
|
{
|
||||||
|
if (!list)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (*pos >= (uint) int_set_get_size(list))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
u32 *res = int_set_get_data(list) + *pos;
|
||||||
|
*val = (lcomm) { res[0], res[1], res[2] };
|
||||||
|
*pos += 3;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
@ -51,6 +51,7 @@ u32 as_path_get_last_nonaggregated(const struct adata *path);
|
|||||||
int as_path_contains(const struct adata *path, u32 as, int min);
|
int as_path_contains(const struct adata *path, u32 as, int min);
|
||||||
int as_path_match_set(const struct adata *path, const struct f_tree *set);
|
int as_path_match_set(const struct adata *path, const struct f_tree *set);
|
||||||
const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_val *set, int pos);
|
const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_val *set, int pos);
|
||||||
|
int as_path_walk(const struct adata *path, uint *pos, uint *val);
|
||||||
|
|
||||||
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
|
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
|
||||||
{ return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as); }
|
{ return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as); }
|
||||||
@ -225,6 +226,9 @@ int lc_set_min(const struct adata *list, lcomm *val);
|
|||||||
int int_set_max(const struct adata *list, u32 *val);
|
int int_set_max(const struct adata *list, u32 *val);
|
||||||
int ec_set_max(const struct adata *list, u64 *val);
|
int ec_set_max(const struct adata *list, u64 *val);
|
||||||
int lc_set_max(const struct adata *list, lcomm *val);
|
int lc_set_max(const struct adata *list, lcomm *val);
|
||||||
|
int int_set_walk(const struct adata *list, uint *pos, u32 *val);
|
||||||
|
int ec_set_walk(const struct adata *list, uint *pos, u64 *val);
|
||||||
|
int lc_set_walk(const struct adata *list, uint *pos, lcomm *val);
|
||||||
|
|
||||||
void ec_set_sort_x(struct adata *set); /* Sort in place */
|
void ec_set_sort_x(struct adata *set); /* Sort in place */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user