]>
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, how the member is named
41 * m4_dnl comparator for same(), if different, this should be TRUE (CAVEAT)
42 * m4_dnl dump format string debug -> format string for bvsnprintf
43 * m4_dnl dump format args appropriate args
46 * m4_dnl RESULT(type, union-field, value); putting this on value stack
47 * m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly
48 * m4_dnl RESULT_VOID; return undef
51 * Other code is just copied into the interpreter part.
53 * If you want to write something really special, see FI_CALL
54 * or FI_CONSTANT or whatever else to see how to use the FID_*
58 /* Binary operators */
62 RESULT(T_INT
, i
, v1
.val
.i
+ v2
.val
.i
);
64 INST(FI_SUBTRACT
, 2, 1) {
67 RESULT(T_INT
, i
, v1
.val
.i
- v2
.val
.i
);
69 INST(FI_MULTIPLY
, 2, 1) {
72 RESULT(T_INT
, i
, v1
.val
.i
* v2
.val
.i
);
74 INST(FI_DIVIDE
, 2, 1) {
77 if (v2
.val
.i
== 0) runtime( "Mother told me not to divide by 0" );
78 RESULT(T_INT
, i
, v1
.val
.i
/ v2
.val
.i
);
94 INST(FI_PAIR_CONSTRUCT
, 2, 1) {
99 if ((u1
> 0xFFFF) || (u2
> 0xFFFF))
100 runtime( "Can't operate with value out of bounds in pair constructor" );
101 RESULT(T_PAIR
, i
, (u1
<< 16) | u2
);
103 INST(FI_EC_CONSTRUCT
, 2, 1) {
107 FID_MEMBER(enum ec_subtype
, ecs
, f1
->ecs
!= f2
->ecs
, ec subtype
%s
, ec_subtype_str(item
->ecs
));
109 int check
, ipv4_used
;
112 if (v1
.type
== T_INT
) {
113 ipv4_used
= 0; key
= v1
.val
.i
;
115 else if (v1
.type
== T_QUAD
) {
116 ipv4_used
= 1; key
= v1
.val
.i
;
118 /* IP->Quad implicit conversion */
119 else if (val_is_ip4(&v1
)) {
120 ipv4_used
= 1; key
= ipa_to_u32(v1
.val
.ip
);
123 runtime("Argument 1 of instruction FI_EC_CONSTRUCT must be integer or IPv4 address, got 0x%02x");
127 if (ecs
== EC_GENERIC
) {
128 check
= 0; RESULT(T_EC
, ec
, ec_generic(key
, val
));
130 else if (ipv4_used
) {
131 check
= 1; RESULT(T_EC
, ec
, ec_ip4(ecs
, key
, val
));
133 else if (key
< 0x10000) {
134 check
= 0; RESULT(T_EC
, ec
, ec_as2(ecs
, key
, val
));
137 check
= 1; RESULT(T_EC
, ec
, ec_as4(ecs
, key
, val
));
140 if (check
&& (val
> 0xFFFF))
141 runtime("Value %u > %u out of bounds in EC constructor", val
, 0xFFFF);
144 INST(FI_LC_CONSTRUCT
, 3, 1) {
148 RESULT(T_LC
, lc
, [[(lcomm
) { v1
.val
.i
, v2
.val
.i
, v3
.val
.i
}]]);
151 INST(FI_PATHMASK_CONSTRUCT
, 0, 1) {
153 FID_MEMBER(uint
, count
, f1
->count
!= f2
->count
, number of items
%u
, item
->count
);
157 for (const struct f_inst
*tt
= f1
; tt
; tt
= tt
->next
, len
++);
160 struct f_inst
**items
;
162 items
= alloca(len
* sizeof(struct f_inst
*));
163 for (uint i
=0; f1
; i
++) {
173 if (fstk
->vcnt
< whati
->count
) /* TODO: make this check systematic */
174 runtime("Construction of BGP path mask from %u elements must have at least that number of elements", whati
->count
);
176 #define pv fstk->vstk[fstk->vcnt - whati->count + i]
179 #define pv items[i]->i_FI_CONSTANT.val
182 struct f_path_mask
*pm
= falloc(sizeof(struct f_path_mask
) + whati
->count
* sizeof(struct f_path_mask_item
));
183 for (uint i
=0; i
<whati
->count
; i
++) {
185 case T_PATH_MASK_ITEM
:
186 pm
->item
[i
] = pv
.val
.pmi
;
189 pm
->item
[i
] = (struct f_path_mask_item
) {
195 runtime( "Error resolving path mask template: value not an integer" );
200 fstk
->vcnt
-= whati
->count
;
203 pm
->len
= whati
->count
;
204 RESULT(T_PATH_MASK
, path_mask
, pm
);
207 /* Relational operators */
212 RESULT(T_BOOL
, i
, !val_same(&v1
, &v2
));
218 RESULT(T_BOOL
, i
, val_same(&v1
, &v2
));
224 int i
= val_compare(&v1
, &v2
);
225 if (i
== F_CMP_ERROR
)
226 runtime( "Can't compare values of incompatible types" );
227 RESULT(T_BOOL
, i
, (i
== -1));
233 int i
= val_compare(&v1
, &v2
);
234 if (i
== F_CMP_ERROR
)
235 runtime( "Can't compare values of incompatible types" );
236 RESULT(T_BOOL
, i
, (i
!= 1));
241 RESULT(T_BOOL
, i
, !v1
.val
.i
);
244 INST(FI_MATCH
, 2, 1) {
247 int i
= val_in_range(&v1
, &v2
);
248 if (i
== F_CMP_ERROR
)
249 runtime( "~ applied on unknown type pair" );
250 RESULT(T_BOOL
, i
, !!i
);
253 INST(FI_NOT_MATCH
, 2, 1) {
256 int i
= val_in_range(&v1
, &v2
);
257 if (i
== F_CMP_ERROR
)
258 runtime( "!~ applied on unknown type pair" );
259 RESULT(T_BOOL
, i
, !i
);
262 INST(FI_DEFINED
, 1, 1) {
264 RESULT(T_BOOL
, i
, (v1
.type
!= T_VOID
) && !undef_value(v1
));
267 INST(FI_TYPE
, 1, 1) {
268 ARG_ANY(1); /* There may be more types supporting this operation */
272 RESULT(T_ENUM_NETTYPE
, i
, v1
.val
.net
->type
);
275 runtime( "Can't determine type of this item" );
279 INST(FI_IS_V4
, 1, 1) {
281 RESULT(T_BOOL
, i
, ipa_is_ip4(v1
.val
.ip
));
284 /* Set to indirect value prepared in v1 */
285 INST(FI_VAR_SET
, 1, 0) {
290 if ((sym
->class != (SYM_VARIABLE
| v1
.type
)) && (v1
.type
!= T_VOID
))
292 /* IP->Quad implicit conversion */
293 if ((sym
->class == (SYM_VARIABLE
| T_QUAD
)) && val_is_ip4(&v1
))
294 v1
= (struct f_val
) {
296 .val
.i
= ipa_to_u32(v1
.val
.ip
),
299 runtime( "Assigning to variable of incompatible type" );
302 fstk
->vstk
[curline
.vbase
+ sym
->offset
] = v1
;
305 INST(FI_VAR_GET
, 0, 1) {
308 RESULT_VAL(fstk
->vstk
[curline
.vbase
+ sym
->offset
]);
311 /* some constants have value in a[1], some in *a[0].p, strange. */
312 INST(FI_CONSTANT
, 0, 1) { /* integer (or simple type) constant, string, set, or prefix_set */
316 [[ !val_same(&(f1
->val
), &(f2
->val
)) ]],
318 val_dump(&(item
->val
))
323 INST(FI_CONDITION
, 1, 0) {
331 INST(FI_PRINT
, 0, 0) {
334 FID_MEMBER_IN(uint
, count
, f1
->count
!= f2
->count
, number of items
%u
, item
->count
);
338 for (const struct f_inst
*tt
= f1
; tt
; tt
= tt
->next
, len
++)
344 #define pv fstk->vstk[fstk->vcnt - whati->count + i]
346 for (uint i
=0; i
<whati
->count
; i
++)
347 val_format(&(pv
), &fs
->buf
);
350 fstk
->vcnt
-= whati
->count
;
355 FID_MEMBER(enum filter_return
, fret
, f1
->fret
!= f2
->fret
, %s
, filter_return_str(item
->fret
));
357 if (fs
->buf
.start
< fs
->buf
.pos
)
358 log_commit(*L_INFO
, &fs
->buf
);
360 switch (whati
->fret
) {
362 die( "Filter asked me to die" );
364 /* Should take care about turning ACCEPT into MODIFY */
366 case F_REJECT
: /* FIXME (noncritical) Should print complete route along with reason to reject route */
367 return fret
; /* We have to return now, no more processing. */
371 bug( "unknown return type: Can't happen");
375 INST(FI_RTA_GET
, 0, 1) { /* rta access */
379 struct rta
*rta
= (*fs
->rte
)->attrs
;
383 case SA_FROM
: RESULT(sa
.f_type
, ip
, rta
->from
); break;
384 case SA_GW
: RESULT(sa
.f_type
, ip
, rta
->nh
.gw
); break;
385 case SA_NET
: RESULT(sa
.f_type
, net
, (*fs
->rte
)->net
->n
.addr
); break;
386 case SA_PROTO
: RESULT(sa
.f_type
, s
, rta
->src
->proto
->name
); break;
387 case SA_SOURCE
: RESULT(sa
.f_type
, i
, rta
->source
); break;
388 case SA_SCOPE
: RESULT(sa
.f_type
, i
, rta
->scope
); break;
389 case SA_DEST
: RESULT(sa
.f_type
, i
, rta
->dest
); break;
390 case SA_IFNAME
: RESULT(sa
.f_type
, s
, rta
->nh
.iface
? rta
->nh
.iface
->name
: ""); break;
391 case SA_IFINDEX
: RESULT(sa
.f_type
, i
, rta
->nh
.iface
? rta
->nh
.iface
->index
: 0); break;
394 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
399 INST(FI_RTA_SET
, 1, 0) {
403 if (sa
.f_type
!= v1
.type
)
404 runtime( "Attempt to set static attribute to incompatible type" );
408 struct rta
*rta
= (*fs
->rte
)->attrs
;
413 rta
->from
= v1
.val
.ip
;
418 ip_addr ip
= v1
.val
.ip
;
419 neighbor
*n
= neigh_find(rta
->src
->proto
, ip
, NULL
, 0);
420 if (!n
|| (n
->scope
== SCOPE_HOST
))
421 runtime( "Invalid gw address" );
423 rta
->dest
= RTD_UNICAST
;
425 rta
->nh
.iface
= n
->iface
;
427 rta
->hostentry
= NULL
;
432 rta
->scope
= v1
.val
.i
;
438 if ((i
!= RTD_BLACKHOLE
) && (i
!= RTD_UNREACHABLE
) && (i
!= RTD_PROHIBIT
))
439 runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
442 rta
->nh
.gw
= IPA_NONE
;
443 rta
->nh
.iface
= NULL
;
445 rta
->hostentry
= NULL
;
451 struct iface
*ifa
= if_find_by_name(v1
.val
.s
);
453 runtime( "Invalid iface name" );
455 rta
->dest
= RTD_UNICAST
;
456 rta
->nh
.gw
= IPA_NONE
;
459 rta
->hostentry
= NULL
;
464 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
469 INST(FI_EA_GET
, 0, 1) { /* Access to extended attributes */
474 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
477 /* A special case: undefined as_path looks like empty as_path */
478 if (da
.type
== EAF_TYPE_AS_PATH
) {
479 RESULT(T_PATH
, ad
, &null_adata
);
483 /* The same special case for int_set */
484 if (da
.type
== EAF_TYPE_INT_SET
) {
485 RESULT(T_CLIST
, ad
, &null_adata
);
489 /* The same special case for ec_set */
490 if (da
.type
== EAF_TYPE_EC_SET
) {
491 RESULT(T_ECLIST
, ad
, &null_adata
);
495 /* The same special case for lc_set */
496 if (da
.type
== EAF_TYPE_LC_SET
) {
497 RESULT(T_LCLIST
, ad
, &null_adata
);
501 /* Undefined value */
506 switch (e
->type
& EAF_TYPE_MASK
) {
508 RESULT(da
.f_type
, i
, e
->u
.data
);
510 case EAF_TYPE_ROUTER_ID
:
511 RESULT(T_QUAD
, i
, e
->u
.data
);
513 case EAF_TYPE_OPAQUE
:
514 RESULT(T_ENUM_EMPTY
, i
, 0);
516 case EAF_TYPE_IP_ADDRESS
:
517 RESULT(T_IP
, ip
, *((ip_addr
*) e
->u
.ptr
->data
));
519 case EAF_TYPE_AS_PATH
:
520 RESULT(T_PATH
, ad
, e
->u
.ptr
);
522 case EAF_TYPE_BITFIELD
:
523 RESULT(T_BOOL
, i
, !!(e
->u
.data
& (1u << da
.bit
)));
525 case EAF_TYPE_INT_SET
:
526 RESULT(T_CLIST
, ad
, e
->u
.ptr
);
528 case EAF_TYPE_EC_SET
:
529 RESULT(T_ECLIST
, ad
, e
->u
.ptr
);
531 case EAF_TYPE_LC_SET
:
532 RESULT(T_LCLIST
, ad
, e
->u
.ptr
);
538 bug("Unknown dynamic attribute type");
543 INST(FI_EA_SET
, 1, 0) {
549 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
552 l
->flags
= EALF_SORTED
;
554 l
->attrs
[0].id
= da
.ea_code
;
555 l
->attrs
[0].flags
= 0;
556 l
->attrs
[0].type
= da
.type
| EAF_ORIGINATED
| EAF_FRESH
;
560 if (v1
.type
!= da
.f_type
)
561 runtime( "Setting int attribute to non-int value" );
562 l
->attrs
[0].u
.data
= v1
.val
.i
;
565 case EAF_TYPE_ROUTER_ID
:
566 /* IP->Quad implicit conversion */
567 if (val_is_ip4(&v1
)) {
568 l
->attrs
[0].u
.data
= ipa_to_u32(v1
.val
.ip
);
571 /* T_INT for backward compatibility */
572 if ((v1
.type
!= T_QUAD
) && (v1
.type
!= T_INT
))
573 runtime( "Setting quad attribute to non-quad value" );
574 l
->attrs
[0].u
.data
= v1
.val
.i
;
577 case EAF_TYPE_OPAQUE
:
578 runtime( "Setting opaque attribute is not allowed" );
580 case EAF_TYPE_IP_ADDRESS
:
582 runtime( "Setting ip attribute to non-ip value" );
583 int len
= sizeof(ip_addr
);
584 struct adata
*ad
= lp_alloc(fs
->pool
, sizeof(struct adata
) + len
);
586 (* (ip_addr
*) ad
->data
) = v1
.val
.ip
;
587 l
->attrs
[0].u
.ptr
= ad
;
589 case EAF_TYPE_AS_PATH
:
590 if (v1
.type
!= T_PATH
)
591 runtime( "Setting path attribute to non-path value" );
592 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
594 case EAF_TYPE_BITFIELD
:
595 if (v1
.type
!= T_BOOL
)
596 runtime( "Setting bit in bitfield attribute to non-bool value" );
598 /* First, we have to find the old value */
599 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
600 u32 data
= e
? e
->u
.data
: 0;
603 l
->attrs
[0].u
.data
= data
| (1u << da
.bit
);
605 l
->attrs
[0].u
.data
= data
& ~(1u << da
.bit
);
608 case EAF_TYPE_INT_SET
:
609 if (v1
.type
!= T_CLIST
)
610 runtime( "Setting clist attribute to non-clist value" );
611 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
613 case EAF_TYPE_EC_SET
:
614 if (v1
.type
!= T_ECLIST
)
615 runtime( "Setting eclist attribute to non-eclist value" );
616 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
618 case EAF_TYPE_LC_SET
:
619 if (v1
.type
!= T_LCLIST
)
620 runtime( "Setting lclist attribute to non-lclist value" );
621 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
623 default: bug("Unknown type in e,S");
627 l
->next
= *fs
->eattrs
;
632 INST(FI_EA_UNSET
, 0, 0) {
638 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
641 l
->flags
= EALF_SORTED
;
643 l
->attrs
[0].id
= da
.ea_code
;
644 l
->attrs
[0].flags
= 0;
645 l
->attrs
[0].type
= EAF_TYPE_UNDEF
| EAF_ORIGINATED
| EAF_FRESH
;
646 l
->attrs
[0].u
.data
= 0;
649 l
->next
= *fs
->eattrs
;
654 INST(FI_PREF_GET
, 0, 1) {
656 RESULT(T_INT
, i
, (*fs
->rte
)->pref
);
659 INST(FI_PREF_SET
, 1, 0) {
662 if (v1
.val
.i
> 0xFFFF)
663 runtime( "Setting preference value out of bounds" );
665 (*fs
->rte
)->pref
= v1
.val
.i
;
668 INST(FI_LENGTH
, 1, 1) { /* Get length of */
671 case T_NET
: RESULT(T_INT
, i
, net_pxlen(v1
.val
.net
)); break;
672 case T_PATH
: RESULT(T_INT
, i
, as_path_getlen(v1
.val
.ad
)); break;
673 case T_CLIST
: RESULT(T_INT
, i
, int_set_get_size(v1
.val
.ad
)); break;
674 case T_ECLIST
: RESULT(T_INT
, i
, ec_set_get_size(v1
.val
.ad
)); break;
675 case T_LCLIST
: RESULT(T_INT
, i
, lc_set_get_size(v1
.val
.ad
)); break;
676 default: runtime( "Prefix, path, clist or eclist expected" );
680 INST(FI_SADR_SRC
, 1, 1) { /* Get SADR src prefix */
682 if (!net_is_sadr(v1
.val
.net
))
683 runtime( "SADR expected" );
685 net_addr_ip6_sadr
*net
= (void *) v1
.val
.net
;
686 net_addr
*src
= falloc(sizeof(net_addr_ip6
));
687 net_fill_ip6(src
, net
->src_prefix
, net
->src_pxlen
);
689 RESULT(T_NET
, net
, src
);
692 INST(FI_ROA_MAXLEN
, 1, 1) { /* Get ROA max prefix length */
694 if (!net_is_roa(v1
.val
.net
))
695 runtime( "ROA expected" );
697 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
698 ((net_addr_roa4
*) v1
.val
.net
)->max_pxlen
:
699 ((net_addr_roa6
*) v1
.val
.net
)->max_pxlen
);
702 INST(FI_ROA_ASN
, 1, 1) { /* Get ROA ASN */
704 if (!net_is_roa(v1
.val
.net
))
705 runtime( "ROA expected" );
707 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
708 ((net_addr_roa4
*) v1
.val
.net
)->asn
:
709 ((net_addr_roa6
*) v1
.val
.net
)->asn
);
712 INST(FI_IP
, 1, 1) { /* Convert prefix to ... */
714 RESULT(T_IP
, ip
, net_prefix(v1
.val
.net
));
717 INST(FI_ROUTE_DISTINGUISHER
, 1, 1) {
719 if (!net_is_vpn(v1
.val
.net
))
720 runtime( "VPN address expected" );
721 RESULT(T_RD
, ec
, net_rd(v1
.val
.net
));
724 INST(FI_AS_PATH_FIRST
, 1, 1) { /* Get first ASN from AS PATH */
727 as_path_get_first(v1
.val
.ad
, &as
);
728 RESULT(T_INT
, i
, as
);
731 INST(FI_AS_PATH_LAST
, 1, 1) { /* Get last ASN from AS PATH */
734 as_path_get_last(v1
.val
.ad
, &as
);
735 RESULT(T_INT
, i
, as
);
738 INST(FI_AS_PATH_LAST_NAG
, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */
740 RESULT(T_INT
, i
, as_path_get_last_nonaggregated(v1
.val
.ad
));
743 INST(FI_RETURN
, 1, 1) {
745 /* Acquire the return value */
747 uint retpos
= fstk
->vcnt
;
749 /* Drop every sub-block including ourselves */
750 while ((fstk
->ecnt
-- > 0) && !(fstk
->estk
[fstk
->ecnt
].emask
& FE_RETURN
))
753 /* Now we are at the caller frame; if no such, try to convert to accept/reject. */
755 if (fstk
->vstk
[retpos
].type
== T_BOOL
)
756 if (fstk
->vstk
[retpos
].val
.i
)
762 runtime("Can't return non-bool from non-function");
764 /* Set the value stack position, overwriting the former implicit void */
765 fstk
->vcnt
= fstk
->estk
[fstk
->ecnt
].ventry
- 1;
767 /* Copy the return value */
768 RESULT_VAL(fstk
->vstk
[retpos
]);
771 INST(FI_CALL
, 0, 1) {
775 /* Push the body on stack */
776 LINEX(sym
->function
);
777 curline
.emask
|= FE_RETURN
;
779 /* Before this instruction was called, there was the T_VOID
780 * automatic return value pushed on value stack and also
781 * sym->function->args function arguments. Setting the
782 * vbase to point to first argument. */
783 ASSERT(curline
.ventry
>= sym
->function
->args
);
784 curline
.ventry
-= sym
->function
->args
;
785 curline
.vbase
= curline
.ventry
;
787 /* Storage for local variables */
788 memset(&(fstk
->vstk
[fstk
->vcnt
]), 0, sizeof(struct f_val
) * sym
->function
->vars
);
789 fstk
->vcnt
+= sym
->function
->vars
;
792 INST(FI_DROP_RESULT
, 1, 0) {
797 INST(FI_SWITCH
, 1, 0) {
800 FID_MEMBER(struct f_tree
*, tree
, [[!same_tree(f1
->tree
, f2
->tree
)]], tree
%p
, item
->tree
);
802 const struct f_tree
*t
= find_tree(tree
, &v1
);
805 t
= find_tree(tree
, &v1
);
807 debug( "No else statement?\n");
808 FID_HIC(,break,return NULL
);
811 /* It is actually possible to have t->data NULL */
816 INST(FI_IP_MASK
, 2, 1) { /* IP.MASK(val) */
819 RESULT(T_IP
, ip
, [[ ipa_is_ip4(v1
.val
.ip
) ?
820 ipa_from_ip4(ip4_and(ipa_to_ip4(v1
.val
.ip
), ip4_mkmask(v2
.val
.i
))) :
821 ipa_from_ip6(ip6_and(ipa_to_ip6(v1
.val
.ip
), ip6_mkmask(v2
.val
.i
))) ]]);
824 INST(FI_PATH_PREPEND
, 2, 1) { /* Path prepend */
827 RESULT(T_PATH
, ad
, [[ as_path_prepend(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
830 INST(FI_CLIST_ADD
, 2, 1) { /* (Extended) Community list add */
833 if (v1
.type
== T_PATH
)
834 runtime("Can't add to path");
836 else if (v1
.type
== T_CLIST
)
838 /* Community (or cluster) list */
841 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
842 RESULT(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
843 /* IP->Quad implicit conversion */
844 else if (val_is_ip4(&v2
))
845 RESULT(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
846 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
))
847 runtime("Can't add set");
848 else if (v2
.type
== T_CLIST
)
849 RESULT(T_CLIST
, ad
, [[ int_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
851 runtime("Can't add non-pair");
854 else if (v1
.type
== T_ECLIST
)
856 /* v2.val is either EC or EC-set */
857 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
))
858 runtime("Can't add set");
859 else if (v2
.type
== T_ECLIST
)
860 RESULT(T_ECLIST
, ad
, [[ ec_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
861 else if (v2
.type
!= T_EC
)
862 runtime("Can't add non-ec");
864 RESULT(T_ECLIST
, ad
, [[ ec_set_add(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
867 else if (v1
.type
== T_LCLIST
)
869 /* v2.val is either LC or LC-set */
870 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
))
871 runtime("Can't add set");
872 else if (v2
.type
== T_LCLIST
)
873 RESULT(T_LCLIST
, ad
, [[ lc_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
874 else if (v2
.type
!= T_LC
)
875 runtime("Can't add non-lc");
877 RESULT(T_LCLIST
, ad
, [[ lc_set_add(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
882 runtime("Can't add to non-[e|l]clist");
885 INST(FI_CLIST_DEL
, 2, 1) { /* (Extended) Community list add or delete */
888 if (v1
.type
== T_PATH
)
890 const struct f_tree
*set
= NULL
;
893 if (v2
.type
== T_INT
)
895 else if ((v2
.type
== T_SET
) && (v2
.val
.t
->from
.type
== T_INT
))
898 runtime("Can't delete non-integer (set)");
900 RESULT(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, set
, key
, 0) ]]);
903 else if (v1
.type
== T_CLIST
)
905 /* Community (or cluster) list */
908 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
909 RESULT(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
910 /* IP->Quad implicit conversion */
911 else if (val_is_ip4(&v2
))
912 RESULT(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
913 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
914 RESULT(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
916 runtime("Can't delete non-pair");
919 else if (v1
.type
== T_ECLIST
)
921 /* v2.val is either EC or EC-set */
922 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
923 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
924 else if (v2
.type
!= T_EC
)
925 runtime("Can't delete non-ec");
927 RESULT(T_ECLIST
, ad
, [[ ec_set_del(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
930 else if (v1
.type
== T_LCLIST
)
932 /* v2.val is either LC or LC-set */
933 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
934 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
935 else if (v2
.type
!= T_LC
)
936 runtime("Can't delete non-lc");
938 RESULT(T_LCLIST
, ad
, [[ lc_set_del(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
942 runtime("Can't delete in non-[e|l]clist");
945 INST(FI_CLIST_FILTER
, 2, 1) { /* (Extended) Community list add or delete */
948 if (v1
.type
== T_PATH
)
952 if ((v2
.type
== T_SET
) && (v2
.val
.t
->from
.type
== T_INT
))
953 RESULT(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, v2
.val
.t
, key
, 1) ]]);
955 runtime("Can't filter integer");
958 else if (v1
.type
== T_CLIST
)
960 /* Community (or cluster) list */
963 if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
964 RESULT(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
966 runtime("Can't filter pair");
969 else if (v1
.type
== T_ECLIST
)
971 /* v2.val is either EC or EC-set */
972 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
973 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
975 runtime("Can't filter ec");
978 else if (v1
.type
== T_LCLIST
)
980 /* v2.val is either LC or LC-set */
981 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
982 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
984 runtime("Can't filter lc");
988 runtime("Can't filter non-[e|l]clist");
991 INST(FI_ROA_CHECK_IMPLICIT
, 0, 1) { /* ROA Check */
994 struct rtable
*table
= rtc
->table
;
997 const net_addr
*net
= (*fs
->rte
)->net
->n
.addr
;
999 /* We ignore temporary attributes, probably not a problem here */
1000 /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
1001 eattr
*e
= ea_find(*fs
->eattrs
, EA_CODE(PROTOCOL_BGP
, 0x02));
1003 if (!e
|| ((e
->type
& EAF_TYPE_MASK
) != EAF_TYPE_AS_PATH
))
1004 runtime("Missing AS_PATH attribute");
1007 as_path_get_last(e
->u
.ptr
, &as
);
1010 runtime("Missing ROA table");
1012 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1013 runtime("Table type must be either ROA4 or ROA6");
1015 if (table
->addr_type
!= (net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1016 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1018 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, net
, as
) ]]);
1021 INST(FI_ROA_CHECK_EXPLICIT
, 2, 1) { /* ROA Check */
1026 struct rtable
*table
= rtc
->table
;
1031 runtime("Missing ROA table");
1033 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1034 runtime("Table type must be either ROA4 or ROA6");
1036 if (table
->addr_type
!= (v1
.val
.net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1037 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1039 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, v1
.val
.net
, as
) ]]);
1043 INST(FI_FORMAT
, 1, 0) { /* Format */
1045 RESULT(T_STRING
, s
, val_format_str(fpool
, &v1
));
1048 INST(FI_ASSERT
, 1, 0) { /* Birdtest Assert */
1052 FID_MEMBER(char *, s
, [[strcmp(f1
->s
, f2
->s
)]], string
%s
, item
->s
);
1056 if (!bt_assert_hook
)
1057 runtime("No bt_assert hook registered, can't assert");
1059 bt_assert_hook(v1
.val
.i
, what
);