]>
git.ipfire.org Git - thirdparty/bird.git/blob - filter/f-inst.c
2 * Filters: Instructions themselves
4 * Copyright 1998 Pavel Machek <pavel@ucw.cz>
5 * Copyright 2018 Maria Matejka <mq@jmq.cz>
6 * Copyright 2018 CZ.NIC z.s.p.o.
8 * Can be freely distributed and used under the terms of the GNU GPL.
10 * Filter instructions. You shall define your instruction only here
13 * Beware. This file is interpreted by M4 macros. These macros
14 * may be more stupid than you could imagine. If something strange
15 * happens after changing this file, compare the results before and
16 * after your change (see the Makefile to find out where the results are)
17 * and see what really happened.
19 * This file is not directly a C source code -> it is a generator input
20 * for several C sources; every instruction block gets expanded into many
23 * All the arguments are processed literally; if you need an argument including comma,
24 * you have to quote it by [[ ... ]]
26 * What is the syntax here?
27 * m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args
28 * m4_dnl ARG(num, type); argument, its id (in data fields) and type
29 * m4_dnl ARG_ANY(num); argument with no type check
30 * m4_dnl LINE(num, unused); this argument has to be converted to its own f_line
31 * m4_dnl SYMBOL; symbol handed from config
32 * m4_dnl STATIC_ATTR; static attribute definition
33 * m4_dnl DYNAMIC_ATTR; dynamic attribute definition
34 * m4_dnl RTC; route table config
35 * m4_dnl ACCESS_RTE; this instruction needs route
36 * m4_dnl ACCESS_EATTRS; this instruction needs extended attributes
38 * m4_dnl FID_MEMBER( custom instruction member
39 * m4_dnl C type, for storage in structs
40 * m4_dnl name in f_inst, how the member is named before linearization
41 * m4_dnl name in f_line_item, how the member is named afterwards
42 * m4_dnl comparator for same(), if different, this should be TRUE (CAVEAT)
43 * m4_dnl dump format string debug -> format string for bvsnprintf
44 * m4_dnl dump format args appropriate args
45 * m4_dnl interpreter body how to deal with this on execution
48 * m4_dnl RESULT(type, union-field, value); putting this on value stack
49 * m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly
50 * m4_dnl RESULT_VOID; return undef
53 * Other code is just copied into the interpreter part.
55 * If you want to write something really special, see FI_CALL
56 * or FI_CONSTANT or whatever else to see how to use the FID_*
60 /* Binary operators */
64 RESULT(T_INT
, i
, v1
.val
.i
+ v2
.val
.i
);
66 INST(FI_SUBTRACT
, 2, 1) {
69 RESULT(T_INT
, i
, v1
.val
.i
- v2
.val
.i
);
71 INST(FI_MULTIPLY
, 2, 1) {
74 RESULT(T_INT
, i
, v1
.val
.i
* v2
.val
.i
);
76 INST(FI_DIVIDE
, 2, 1) {
79 if (v2
.val
.i
== 0) runtime( "Mother told me not to divide by 0" );
80 RESULT(T_INT
, i
, v1
.val
.i
/ v2
.val
.i
);
96 INST(FI_PAIR_CONSTRUCT
, 2, 1) {
101 if ((u1
> 0xFFFF) || (u2
> 0xFFFF))
102 runtime( "Can't operate with value out of bounds in pair constructor" );
103 RESULT(T_PAIR
, i
, (u1
<< 16) | u2
);
105 INST(FI_EC_CONSTRUCT
, 2, 1) {
109 FID_MEMBER(enum ec_subtype
, ecs
, ecs
, f1
->ecs
!= f2
->ecs
, ec subtype
%s
, ec_subtype_str(item
->ecs
), enum ec_subtype ecs
= whati
->ecs
);
111 int check
, ipv4_used
;
114 if (v1
.type
== T_INT
) {
115 ipv4_used
= 0; key
= v1
.val
.i
;
117 else if (v1
.type
== T_QUAD
) {
118 ipv4_used
= 1; key
= v1
.val
.i
;
120 /* IP->Quad implicit conversion */
121 else if (val_is_ip4(&v1
)) {
122 ipv4_used
= 1; key
= ipa_to_u32(v1
.val
.ip
);
125 runtime("Argument 1 of instruction FI_EC_CONSTRUCT must be integer or IPv4 address, got 0x%02x");
129 if (ecs
== EC_GENERIC
) {
130 check
= 0; RESULT(T_EC
, ec
, ec_generic(key
, val
));
132 else if (ipv4_used
) {
133 check
= 1; RESULT(T_EC
, ec
, ec_ip4(ecs
, key
, val
));
135 else if (key
< 0x10000) {
136 check
= 0; RESULT(T_EC
, ec
, ec_as2(ecs
, key
, val
));
139 check
= 1; RESULT(T_EC
, ec
, ec_as4(ecs
, key
, val
));
142 if (check
&& (val
> 0xFFFF))
143 runtime("Value %u > %u out of bounds in EC constructor", val
, 0xFFFF);
146 INST(FI_LC_CONSTRUCT
, 3, 1) {
150 RESULT(T_LC
, lc
, [[(lcomm
) { v1
.val
.i
, v2
.val
.i
, v3
.val
.i
}]]);
153 INST(FI_PATHMASK_CONSTRUCT
, 0, 1) {
155 FID_MEMBER(uint
, count
, count
, f1
->count
!= f2
->count
, number of items
%u
, item
->count
);
160 for (const struct f_inst
*tt
= f1
; tt
; tt
= tt
->next
, len
++)
161 if (tt
->fi_code
!= FI_CONSTANT
)
167 if (fstk
->vcnt
< whati
->count
) /* TODO: make this check systematic */
168 runtime("Construction of BGP path mask from %u elements must have at least that number of elements", whati
->count
);
170 struct f_path_mask
*pm
= lp_alloc(fs
->pool
, sizeof(struct f_path_mask
) + whati
->count
* sizeof(struct f_path_mask_item
));
171 for (uint i
=0; i
<whati
->count
; i
++) {
172 #define pv fstk->vstk[fstk->vcnt - whati->count + i]
174 case T_PATH_MASK_ITEM
:
175 pm
->item
[i
] = pv
.val
.pmi
;
178 pm
->item
[i
] = (struct f_path_mask_item
) {
184 runtime( "Error resolving path mask template: value not an integer" );
188 fstk
->vcnt
-= whati
->count
;
189 pm
->len
= whati
->count
;
191 RESULT(T_PATH_MASK
, path_mask
, pm
);
194 /* Relational operators */
199 RESULT(T_BOOL
, i
, !val_same(&v1
, &v2
));
205 RESULT(T_BOOL
, i
, val_same(&v1
, &v2
));
211 int i
= val_compare(&v1
, &v2
);
212 if (i
== F_CMP_ERROR
)
213 runtime( "Can't compare values of incompatible types" );
214 RESULT(T_BOOL
, i
, (i
== -1));
220 int i
= val_compare(&v1
, &v2
);
221 if (i
== F_CMP_ERROR
)
222 runtime( "Can't compare values of incompatible types" );
223 RESULT(T_BOOL
, i
, (i
!= 1));
228 RESULT(T_BOOL
, i
, !v1
.val
.i
);
231 INST(FI_MATCH
, 2, 1) {
234 int i
= val_in_range(&v1
, &v2
);
235 if (i
== F_CMP_ERROR
)
236 runtime( "~ applied on unknown type pair" );
237 RESULT(T_BOOL
, i
, !!i
);
240 INST(FI_NOT_MATCH
, 2, 1) {
243 int i
= val_in_range(&v1
, &v2
);
244 if (i
== F_CMP_ERROR
)
245 runtime( "!~ applied on unknown type pair" );
246 RESULT(T_BOOL
, i
, !i
);
249 INST(FI_DEFINED
, 1, 1) {
251 RESULT(T_BOOL
, i
, (v1
.type
!= T_VOID
) && !undef_value(v1
));
254 INST(FI_TYPE
, 1, 1) {
255 ARG_ANY(1); /* There may be more types supporting this operation */
259 RESULT(T_ENUM_NETTYPE
, i
, v1
.val
.net
->type
);
262 runtime( "Can't determine type of this item" );
266 INST(FI_IS_V4
, 1, 1) {
268 RESULT(T_BOOL
, i
, ipa_is_ip4(v1
.val
.ip
));
271 /* Set to indirect value prepared in v1 */
272 INST(FI_VAR_SET
, 1, 0) {
275 if ((sym
->class != (SYM_VARIABLE
| v1
.type
)) && (v1
.type
!= T_VOID
))
277 /* IP->Quad implicit conversion */
278 if ((sym
->class == (SYM_VARIABLE
| T_QUAD
)) && val_is_ip4(&v1
))
279 v1
= (struct f_val
) {
281 .val
.i
= ipa_to_u32(v1
.val
.ip
),
284 runtime( "Assigning to variable of incompatible type" );
287 fstk
->vstk
[curline
.vbase
+ sym
->offset
] = v1
;
290 INST(FI_VAR_GET
, 0, 1) {
292 RESULT_VAL(fstk
->vstk
[curline
.vbase
+ sym
->offset
]);
295 /* some constants have value in a[1], some in *a[0].p, strange. */
296 INST(FI_CONSTANT
, 0, 1) { /* integer (or simple type) constant, string, set, or prefix_set */
301 [[ !val_same(&(f1
->val
), &(f2
->val
)) ]],
303 val_dump(&(item
->val
))
306 RESULT_VAL(whati
->val
);
308 INST(FI_PRINT
, 1, 0) {
310 val_format(&(v1
), &fs
->buf
);
312 INST(FI_CONDITION
, 1, 0) {
319 INST(FI_PRINT_AND_DIE
, 0, 0) {
329 dest
->items
[pos
].flags
|= FIF_PRINTED
;
333 FID_MEMBER(enum filter_return
, fret
, fret
, f1
->fret
!= f2
->fret
, %s
, filter_return_str(item
->fret
), enum filter_return fret
= whati
->fret
);
335 if ((fret
== F_NOP
|| (fret
!= F_NONL
&& (what
->flags
& FIF_PRINTED
))) &&
336 !(fs
->flags
& FF_SILENT
))
337 log_commit(*L_INFO
, &fs
->buf
);
341 die( "Filter asked me to die" );
343 /* Should take care about turning ACCEPT into MODIFY */
345 case F_REJECT
: /* FIXME (noncritical) Should print complete route along with reason to reject route */
346 return fret
; /* We have to return now, no more processing. */
351 bug( "unknown return type: Can't happen");
355 INST(FI_RTA_GET
, 0, 1) { /* rta access */
359 struct rta
*rta
= (*fs
->rte
)->attrs
;
363 case SA_FROM
: RESULT(sa
.f_type
, ip
, rta
->from
); break;
364 case SA_GW
: RESULT(sa
.f_type
, ip
, rta
->nh
.gw
); break;
365 case SA_NET
: RESULT(sa
.f_type
, net
, (*fs
->rte
)->net
->n
.addr
); break;
366 case SA_PROTO
: RESULT(sa
.f_type
, s
, rta
->src
->proto
->name
); break;
367 case SA_SOURCE
: RESULT(sa
.f_type
, i
, rta
->source
); break;
368 case SA_SCOPE
: RESULT(sa
.f_type
, i
, rta
->scope
); break;
369 case SA_DEST
: RESULT(sa
.f_type
, i
, rta
->dest
); break;
370 case SA_IFNAME
: RESULT(sa
.f_type
, s
, rta
->nh
.iface
? rta
->nh
.iface
->name
: ""); break;
371 case SA_IFINDEX
: RESULT(sa
.f_type
, i
, rta
->nh
.iface
? rta
->nh
.iface
->index
: 0); break;
374 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
379 INST(FI_RTA_SET
, 1, 0) {
383 if (sa
.f_type
!= v1
.type
)
384 runtime( "Attempt to set static attribute to incompatible type" );
388 struct rta
*rta
= (*fs
->rte
)->attrs
;
393 rta
->from
= v1
.val
.ip
;
398 ip_addr ip
= v1
.val
.ip
;
399 neighbor
*n
= neigh_find(rta
->src
->proto
, ip
, NULL
, 0);
400 if (!n
|| (n
->scope
== SCOPE_HOST
))
401 runtime( "Invalid gw address" );
403 rta
->dest
= RTD_UNICAST
;
405 rta
->nh
.iface
= n
->iface
;
407 rta
->hostentry
= NULL
;
412 rta
->scope
= v1
.val
.i
;
418 if ((i
!= RTD_BLACKHOLE
) && (i
!= RTD_UNREACHABLE
) && (i
!= RTD_PROHIBIT
))
419 runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
422 rta
->nh
.gw
= IPA_NONE
;
423 rta
->nh
.iface
= NULL
;
425 rta
->hostentry
= NULL
;
431 struct iface
*ifa
= if_find_by_name(v1
.val
.s
);
433 runtime( "Invalid iface name" );
435 rta
->dest
= RTD_UNICAST
;
436 rta
->nh
.gw
= IPA_NONE
;
439 rta
->hostentry
= NULL
;
444 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
449 INST(FI_EA_GET
, 0, 1) { /* Access to extended attributes */
454 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
457 /* A special case: undefined as_path looks like empty as_path */
458 if (da
.type
== EAF_TYPE_AS_PATH
) {
459 RESULT(T_PATH
, ad
, &null_adata
);
463 /* The same special case for int_set */
464 if (da
.type
== EAF_TYPE_INT_SET
) {
465 RESULT(T_CLIST
, ad
, &null_adata
);
469 /* The same special case for ec_set */
470 if (da
.type
== EAF_TYPE_EC_SET
) {
471 RESULT(T_ECLIST
, ad
, &null_adata
);
475 /* The same special case for lc_set */
476 if (da
.type
== EAF_TYPE_LC_SET
) {
477 RESULT(T_LCLIST
, ad
, &null_adata
);
481 /* Undefined value */
486 switch (e
->type
& EAF_TYPE_MASK
) {
488 RESULT(da
.f_type
, i
, e
->u
.data
);
490 case EAF_TYPE_ROUTER_ID
:
491 RESULT(T_QUAD
, i
, e
->u
.data
);
493 case EAF_TYPE_OPAQUE
:
494 RESULT(T_ENUM_EMPTY
, i
, 0);
496 case EAF_TYPE_IP_ADDRESS
:
497 RESULT(T_IP
, ip
, *((ip_addr
*) e
->u
.ptr
->data
));
499 case EAF_TYPE_AS_PATH
:
500 RESULT(T_PATH
, ad
, e
->u
.ptr
);
502 case EAF_TYPE_BITFIELD
:
503 RESULT(T_BOOL
, i
, !!(e
->u
.data
& (1u << da
.bit
)));
505 case EAF_TYPE_INT_SET
:
506 RESULT(T_CLIST
, ad
, e
->u
.ptr
);
508 case EAF_TYPE_EC_SET
:
509 RESULT(T_ECLIST
, ad
, e
->u
.ptr
);
511 case EAF_TYPE_LC_SET
:
512 RESULT(T_LCLIST
, ad
, e
->u
.ptr
);
518 bug("Unknown dynamic attribute type");
523 INST(FI_EA_SET
, 1, 0) {
529 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
532 l
->flags
= EALF_SORTED
;
534 l
->attrs
[0].id
= da
.ea_code
;
535 l
->attrs
[0].flags
= 0;
536 l
->attrs
[0].type
= da
.type
| EAF_ORIGINATED
| EAF_FRESH
;
540 if (v1
.type
!= da
.f_type
)
541 runtime( "Setting int attribute to non-int value" );
542 l
->attrs
[0].u
.data
= v1
.val
.i
;
545 case EAF_TYPE_ROUTER_ID
:
546 /* IP->Quad implicit conversion */
547 if (val_is_ip4(&v1
)) {
548 l
->attrs
[0].u
.data
= ipa_to_u32(v1
.val
.ip
);
551 /* T_INT for backward compatibility */
552 if ((v1
.type
!= T_QUAD
) && (v1
.type
!= T_INT
))
553 runtime( "Setting quad attribute to non-quad value" );
554 l
->attrs
[0].u
.data
= v1
.val
.i
;
557 case EAF_TYPE_OPAQUE
:
558 runtime( "Setting opaque attribute is not allowed" );
560 case EAF_TYPE_IP_ADDRESS
:
562 runtime( "Setting ip attribute to non-ip value" );
563 int len
= sizeof(ip_addr
);
564 struct adata
*ad
= lp_alloc(fs
->pool
, sizeof(struct adata
) + len
);
566 (* (ip_addr
*) ad
->data
) = v1
.val
.ip
;
567 l
->attrs
[0].u
.ptr
= ad
;
569 case EAF_TYPE_AS_PATH
:
570 if (v1
.type
!= T_PATH
)
571 runtime( "Setting path attribute to non-path value" );
572 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
574 case EAF_TYPE_BITFIELD
:
575 if (v1
.type
!= T_BOOL
)
576 runtime( "Setting bit in bitfield attribute to non-bool value" );
578 /* First, we have to find the old value */
579 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
580 u32 data
= e
? e
->u
.data
: 0;
583 l
->attrs
[0].u
.data
= data
| (1u << da
.bit
);
585 l
->attrs
[0].u
.data
= data
& ~(1u << da
.bit
);
588 case EAF_TYPE_INT_SET
:
589 if (v1
.type
!= T_CLIST
)
590 runtime( "Setting clist attribute to non-clist value" );
591 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
593 case EAF_TYPE_EC_SET
:
594 if (v1
.type
!= T_ECLIST
)
595 runtime( "Setting eclist attribute to non-eclist value" );
596 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
598 case EAF_TYPE_LC_SET
:
599 if (v1
.type
!= T_LCLIST
)
600 runtime( "Setting lclist attribute to non-lclist value" );
601 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
603 default: bug("Unknown type in e,S");
607 l
->next
= *fs
->eattrs
;
612 INST(FI_EA_UNSET
, 0, 0) {
618 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
621 l
->flags
= EALF_SORTED
;
623 l
->attrs
[0].id
= da
.ea_code
;
624 l
->attrs
[0].flags
= 0;
625 l
->attrs
[0].type
= EAF_TYPE_UNDEF
| EAF_ORIGINATED
| EAF_FRESH
;
626 l
->attrs
[0].u
.data
= 0;
629 l
->next
= *fs
->eattrs
;
634 INST(FI_PREF_GET
, 0, 1) {
636 RESULT(T_INT
, i
, (*fs
->rte
)->pref
);
639 INST(FI_PREF_SET
, 1, 0) {
642 if (v1
.val
.i
> 0xFFFF)
643 runtime( "Setting preference value out of bounds" );
645 (*fs
->rte
)->pref
= v1
.val
.i
;
648 INST(FI_LENGTH
, 1, 1) { /* Get length of */
651 case T_NET
: RESULT(T_INT
, i
, net_pxlen(v1
.val
.net
)); break;
652 case T_PATH
: RESULT(T_INT
, i
, as_path_getlen(v1
.val
.ad
)); break;
653 case T_CLIST
: RESULT(T_INT
, i
, int_set_get_size(v1
.val
.ad
)); break;
654 case T_ECLIST
: RESULT(T_INT
, i
, ec_set_get_size(v1
.val
.ad
)); break;
655 case T_LCLIST
: RESULT(T_INT
, i
, lc_set_get_size(v1
.val
.ad
)); break;
656 default: runtime( "Prefix, path, clist or eclist expected" );
660 INST(FI_SADR_SRC
, 1, 1) { /* Get SADR src prefix */
662 if (!net_is_sadr(v1
.val
.net
))
663 runtime( "SADR expected" );
665 net_addr_ip6_sadr
*net
= (void *) v1
.val
.net
;
666 net_addr
*src
= lp_alloc(fs
->pool
, sizeof(net_addr_ip6
));
667 net_fill_ip6(src
, net
->src_prefix
, net
->src_pxlen
);
669 RESULT(T_NET
, net
, src
);
672 INST(FI_ROA_MAXLEN
, 1, 1) { /* Get ROA max prefix length */
674 if (!net_is_roa(v1
.val
.net
))
675 runtime( "ROA expected" );
677 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
678 ((net_addr_roa4
*) v1
.val
.net
)->max_pxlen
:
679 ((net_addr_roa6
*) v1
.val
.net
)->max_pxlen
);
682 INST(FI_ROA_ASN
, 1, 1) { /* Get ROA ASN */
684 if (!net_is_roa(v1
.val
.net
))
685 runtime( "ROA expected" );
687 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
688 ((net_addr_roa4
*) v1
.val
.net
)->asn
:
689 ((net_addr_roa6
*) v1
.val
.net
)->asn
);
692 INST(FI_IP
, 1, 1) { /* Convert prefix to ... */
694 RESULT(T_IP
, ip
, net_prefix(v1
.val
.net
));
697 INST(FI_ROUTE_DISTINGUISHER
, 1, 1) {
699 if (!net_is_vpn(v1
.val
.net
))
700 runtime( "VPN address expected" );
701 RESULT(T_RD
, ec
, net_rd(v1
.val
.net
));
704 INST(FI_AS_PATH_FIRST
, 1, 1) { /* Get first ASN from AS PATH */
707 as_path_get_first(v1
.val
.ad
, &as
);
708 RESULT(T_INT
, i
, as
);
711 INST(FI_AS_PATH_LAST
, 1, 1) { /* Get last ASN from AS PATH */
714 as_path_get_last(v1
.val
.ad
, &as
);
715 RESULT(T_INT
, i
, as
);
718 INST(FI_AS_PATH_LAST_NAG
, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */
720 RESULT(T_INT
, i
, as_path_get_last_nonaggregated(v1
.val
.ad
));
723 INST(FI_RETURN
, 1, 1) {
724 /* Acquire the return value */
726 uint retpos
= fstk
->vcnt
;
728 /* Drop every sub-block including ourselves */
729 while ((fstk
->ecnt
-- > 0) && !(fstk
->estk
[fstk
->ecnt
].emask
& FE_RETURN
))
732 /* Now we are at the caller frame; if no such, try to convert to accept/reject. */
734 if (fstk
->vstk
[retpos
].type
== T_BOOL
)
735 if (fstk
->vstk
[retpos
].val
.i
)
741 runtime("Can't return non-bool from non-function");
743 /* Set the value stack position, overwriting the former implicit void */
744 fstk
->vcnt
= fstk
->estk
[fstk
->ecnt
].ventry
- 1;
746 /* Copy the return value */
747 RESULT_VAL(fstk
->vstk
[retpos
]);
750 INST(FI_CALL
, 0, 1) {
753 /* Push the body on stack */
754 LINEX(sym
->function
);
755 curline
.emask
|= FE_RETURN
;
757 /* Before this instruction was called, there was the T_VOID
758 * automatic return value pushed on value stack and also
759 * sym->function->args function arguments. Setting the
760 * vbase to point to first argument. */
761 ASSERT(curline
.ventry
>= sym
->function
->args
);
762 curline
.ventry
-= sym
->function
->args
;
763 curline
.vbase
= curline
.ventry
;
765 /* Storage for local variables */
766 memset(&(fstk
->vstk
[fstk
->vcnt
]), 0, sizeof(struct f_val
) * sym
->function
->vars
);
767 fstk
->vcnt
+= sym
->function
->vars
;
770 INST(FI_DROP_RESULT
, 1, 0) {
774 INST(FI_SWITCH
, 1, 0) {
777 FID_MEMBER(const struct f_tree
*, tree
, tree
, [[!same_tree(f1
->tree
, f2
->tree
)]], tree
%p
, item
->tree
, const struct f_tree
*tree
= whati
->tree
);
779 const struct f_tree
*t
= find_tree(tree
, &v1
);
782 t
= find_tree(tree
, &v1
);
784 debug( "No else statement?\n");
788 /* It is actually possible to have t->data NULL */
793 INST(FI_IP_MASK
, 2, 1) { /* IP.MASK(val) */
796 RESULT(T_IP
, ip
, [[ ipa_is_ip4(v1
.val
.ip
) ?
797 ipa_from_ip4(ip4_and(ipa_to_ip4(v1
.val
.ip
), ip4_mkmask(v2
.val
.i
))) :
798 ipa_from_ip6(ip6_and(ipa_to_ip6(v1
.val
.ip
), ip6_mkmask(v2
.val
.i
))) ]]);
801 INST(FI_PATH_PREPEND
, 2, 1) { /* Path prepend */
804 RESULT(T_PATH
, ad
, [[ as_path_prepend(fs
->pool
, v1
.val
.ad
, v2
.val
.i
) ]]);
807 INST(FI_CLIST_ADD
, 2, 1) { /* (Extended) Community list add */
810 if (v1
.type
== T_PATH
)
811 runtime("Can't add to path");
813 else if (v1
.type
== T_CLIST
)
815 /* Community (or cluster) list */
818 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
819 RESULT(T_CLIST
, ad
, [[ int_set_add(fs
->pool
, v1
.val
.ad
, v2
.val
.i
) ]]);
820 /* IP->Quad implicit conversion */
821 else if (val_is_ip4(&v2
))
822 RESULT(T_CLIST
, ad
, [[ int_set_add(fs
->pool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
823 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
))
824 runtime("Can't add set");
825 else if (v2
.type
== T_CLIST
)
826 RESULT(T_CLIST
, ad
, [[ int_set_union(fs
->pool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
828 runtime("Can't add non-pair");
831 else if (v1
.type
== T_ECLIST
)
833 /* v2.val is either EC or EC-set */
834 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
))
835 runtime("Can't add set");
836 else if (v2
.type
== T_ECLIST
)
837 RESULT(T_ECLIST
, ad
, [[ ec_set_union(fs
->pool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
838 else if (v2
.type
!= T_EC
)
839 runtime("Can't add non-ec");
841 RESULT(T_ECLIST
, ad
, [[ ec_set_add(fs
->pool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
844 else if (v1
.type
== T_LCLIST
)
846 /* v2.val is either LC or LC-set */
847 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
))
848 runtime("Can't add set");
849 else if (v2
.type
== T_LCLIST
)
850 RESULT(T_LCLIST
, ad
, [[ lc_set_union(fs
->pool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
851 else if (v2
.type
!= T_LC
)
852 runtime("Can't add non-lc");
854 RESULT(T_LCLIST
, ad
, [[ lc_set_add(fs
->pool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
859 runtime("Can't add to non-[e|l]clist");
862 INST(FI_CLIST_DEL
, 2, 1) { /* (Extended) Community list add or delete */
865 if (v1
.type
== T_PATH
)
867 const struct f_tree
*set
= NULL
;
870 if (v2
.type
== T_INT
)
872 else if ((v2
.type
== T_SET
) && (v2
.val
.t
->from
.type
== T_INT
))
875 runtime("Can't delete non-integer (set)");
877 RESULT(T_PATH
, ad
, [[ as_path_filter(fs
->pool
, v1
.val
.ad
, set
, key
, 0) ]]);
880 else if (v1
.type
== T_CLIST
)
882 /* Community (or cluster) list */
885 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
886 RESULT(T_CLIST
, ad
, [[ int_set_del(fs
->pool
, v1
.val
.ad
, v2
.val
.i
) ]]);
887 /* IP->Quad implicit conversion */
888 else if (val_is_ip4(&v2
))
889 RESULT(T_CLIST
, ad
, [[ int_set_del(fs
->pool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
890 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
891 RESULT(T_CLIST
, ad
, [[ clist_filter(fs
->pool
, v1
.val
.ad
, &v2
, 0) ]]);
893 runtime("Can't delete non-pair");
896 else if (v1
.type
== T_ECLIST
)
898 /* v2.val is either EC or EC-set */
899 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
900 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fs
->pool
, v1
.val
.ad
, &v2
, 0) ]]);
901 else if (v2
.type
!= T_EC
)
902 runtime("Can't delete non-ec");
904 RESULT(T_ECLIST
, ad
, [[ ec_set_del(fs
->pool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
907 else if (v1
.type
== T_LCLIST
)
909 /* v2.val is either LC or LC-set */
910 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
911 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fs
->pool
, v1
.val
.ad
, &v2
, 0) ]]);
912 else if (v2
.type
!= T_LC
)
913 runtime("Can't delete non-lc");
915 RESULT(T_LCLIST
, ad
, [[ lc_set_del(fs
->pool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
919 runtime("Can't delete in non-[e|l]clist");
922 INST(FI_CLIST_FILTER
, 2, 1) { /* (Extended) Community list add or delete */
925 if (v1
.type
== T_PATH
)
929 if ((v2
.type
== T_SET
) && (v2
.val
.t
->from
.type
== T_INT
))
930 RESULT(T_PATH
, ad
, [[ as_path_filter(fs
->pool
, v1
.val
.ad
, v2
.val
.t
, key
, 1) ]]);
932 runtime("Can't filter integer");
935 else if (v1
.type
== T_CLIST
)
937 /* Community (or cluster) list */
940 if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
941 RESULT(T_CLIST
, ad
, [[ clist_filter(fs
->pool
, v1
.val
.ad
, &v2
, 1) ]]);
943 runtime("Can't filter pair");
946 else if (v1
.type
== T_ECLIST
)
948 /* v2.val is either EC or EC-set */
949 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
950 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fs
->pool
, v1
.val
.ad
, &v2
, 1) ]]);
952 runtime("Can't filter ec");
955 else if (v1
.type
== T_LCLIST
)
957 /* v2.val is either LC or LC-set */
958 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
959 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fs
->pool
, v1
.val
.ad
, &v2
, 1) ]]);
961 runtime("Can't filter lc");
965 runtime("Can't filter non-[e|l]clist");
968 INST(FI_ROA_CHECK_IMPLICIT
, 0, 1) { /* ROA Check */
972 const net_addr
*net
= (*fs
->rte
)->net
->n
.addr
;
974 /* We ignore temporary attributes, probably not a problem here */
975 /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
976 eattr
*e
= ea_find(*fs
->eattrs
, EA_CODE(PROTOCOL_BGP
, 0x02));
978 if (!e
|| ((e
->type
& EAF_TYPE_MASK
) != EAF_TYPE_AS_PATH
))
979 runtime("Missing AS_PATH attribute");
982 as_path_get_last(e
->u
.ptr
, &as
);
985 runtime("Missing ROA table");
987 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
988 runtime("Table type must be either ROA4 or ROA6");
990 if (table
->addr_type
!= (net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
991 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
993 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, net
, as
) ]]);
996 INST(FI_ROA_CHECK_EXPLICIT
, 2, 1) { /* ROA Check */
1004 runtime("Missing ROA table");
1006 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1007 runtime("Table type must be either ROA4 or ROA6");
1009 if (table
->addr_type
!= (v1
.val
.net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1010 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1012 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, v1
.val
.net
, as
) ]]);
1016 INST(FI_FORMAT
, 1, 0) { /* Format */
1018 RESULT(T_STRING
, s
, val_format_str(fs
, &v1
));
1021 INST(FI_ASSERT
, 1, 0) { /* Birdtest Assert */
1023 FID_MEMBER(const char *, s
, s
, [[strcmp(f1
->s
, f2
->s
)]], string
\"%s
\", item
->s
);
1025 if (!bt_assert_hook
)
1026 runtime("No bt_assert hook registered, can't assert");
1028 bt_assert_hook(res
.val
.i
, what
);