]>
git.ipfire.org Git - thirdparty/bird.git/blob - filter/f-inst.c
edc9779408d90f049a6d4075b704aafbde60d006
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 - 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_PRINT
, 1, 0) {
326 val_format(&(v1
), &fs
->buf
);
328 INST(FI_CONDITION
, 1, 0) {
335 INST(FI_PRINT_AND_DIE
, 0, 0) {
346 dest
->items
[pos
].flags
|= FIF_PRINTED
;
350 FID_MEMBER(enum filter_return
, fret
, f1
->fret
!= f2
->fret
, %s
, filter_return_str(item
->fret
));
352 if ((fret
== F_NOP
|| (fret
!= F_NONL
&& (what
->flags
& FIF_PRINTED
))) &&
353 !(fs
->flags
& FF_SILENT
))
354 log_commit(*L_INFO
, &fs
->buf
);
358 die( "Filter asked me to die" );
360 /* Should take care about turning ACCEPT into MODIFY */
362 case F_REJECT
: /* FIXME (noncritical) Should print complete route along with reason to reject route */
363 return fret
; /* We have to return now, no more processing. */
368 bug( "unknown return type: Can't happen");
372 INST(FI_RTA_GET
, 0, 1) { /* rta access */
376 struct rta
*rta
= (*fs
->rte
)->attrs
;
380 case SA_FROM
: RESULT(sa
.f_type
, ip
, rta
->from
); break;
381 case SA_GW
: RESULT(sa
.f_type
, ip
, rta
->nh
.gw
); break;
382 case SA_NET
: RESULT(sa
.f_type
, net
, (*fs
->rte
)->net
->n
.addr
); break;
383 case SA_PROTO
: RESULT(sa
.f_type
, s
, rta
->src
->proto
->name
); break;
384 case SA_SOURCE
: RESULT(sa
.f_type
, i
, rta
->source
); break;
385 case SA_SCOPE
: RESULT(sa
.f_type
, i
, rta
->scope
); break;
386 case SA_DEST
: RESULT(sa
.f_type
, i
, rta
->dest
); break;
387 case SA_IFNAME
: RESULT(sa
.f_type
, s
, rta
->nh
.iface
? rta
->nh
.iface
->name
: ""); break;
388 case SA_IFINDEX
: RESULT(sa
.f_type
, i
, rta
->nh
.iface
? rta
->nh
.iface
->index
: 0); break;
391 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
396 INST(FI_RTA_SET
, 1, 0) {
400 if (sa
.f_type
!= v1
.type
)
401 runtime( "Attempt to set static attribute to incompatible type" );
405 struct rta
*rta
= (*fs
->rte
)->attrs
;
410 rta
->from
= v1
.val
.ip
;
415 ip_addr ip
= v1
.val
.ip
;
416 neighbor
*n
= neigh_find(rta
->src
->proto
, ip
, NULL
, 0);
417 if (!n
|| (n
->scope
== SCOPE_HOST
))
418 runtime( "Invalid gw address" );
420 rta
->dest
= RTD_UNICAST
;
422 rta
->nh
.iface
= n
->iface
;
424 rta
->hostentry
= NULL
;
429 rta
->scope
= v1
.val
.i
;
435 if ((i
!= RTD_BLACKHOLE
) && (i
!= RTD_UNREACHABLE
) && (i
!= RTD_PROHIBIT
))
436 runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
439 rta
->nh
.gw
= IPA_NONE
;
440 rta
->nh
.iface
= NULL
;
442 rta
->hostentry
= NULL
;
448 struct iface
*ifa
= if_find_by_name(v1
.val
.s
);
450 runtime( "Invalid iface name" );
452 rta
->dest
= RTD_UNICAST
;
453 rta
->nh
.gw
= IPA_NONE
;
456 rta
->hostentry
= NULL
;
461 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
466 INST(FI_EA_GET
, 0, 1) { /* Access to extended attributes */
471 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
474 /* A special case: undefined as_path looks like empty as_path */
475 if (da
.type
== EAF_TYPE_AS_PATH
) {
476 RESULT(T_PATH
, ad
, &null_adata
);
480 /* The same special case for int_set */
481 if (da
.type
== EAF_TYPE_INT_SET
) {
482 RESULT(T_CLIST
, ad
, &null_adata
);
486 /* The same special case for ec_set */
487 if (da
.type
== EAF_TYPE_EC_SET
) {
488 RESULT(T_ECLIST
, ad
, &null_adata
);
492 /* The same special case for lc_set */
493 if (da
.type
== EAF_TYPE_LC_SET
) {
494 RESULT(T_LCLIST
, ad
, &null_adata
);
498 /* Undefined value */
503 switch (e
->type
& EAF_TYPE_MASK
) {
505 RESULT(da
.f_type
, i
, e
->u
.data
);
507 case EAF_TYPE_ROUTER_ID
:
508 RESULT(T_QUAD
, i
, e
->u
.data
);
510 case EAF_TYPE_OPAQUE
:
511 RESULT(T_ENUM_EMPTY
, i
, 0);
513 case EAF_TYPE_IP_ADDRESS
:
514 RESULT(T_IP
, ip
, *((ip_addr
*) e
->u
.ptr
->data
));
516 case EAF_TYPE_AS_PATH
:
517 RESULT(T_PATH
, ad
, e
->u
.ptr
);
519 case EAF_TYPE_BITFIELD
:
520 RESULT(T_BOOL
, i
, !!(e
->u
.data
& (1u << da
.bit
)));
522 case EAF_TYPE_INT_SET
:
523 RESULT(T_CLIST
, ad
, e
->u
.ptr
);
525 case EAF_TYPE_EC_SET
:
526 RESULT(T_ECLIST
, ad
, e
->u
.ptr
);
528 case EAF_TYPE_LC_SET
:
529 RESULT(T_LCLIST
, ad
, e
->u
.ptr
);
535 bug("Unknown dynamic attribute type");
540 INST(FI_EA_SET
, 1, 0) {
546 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
549 l
->flags
= EALF_SORTED
;
551 l
->attrs
[0].id
= da
.ea_code
;
552 l
->attrs
[0].flags
= 0;
553 l
->attrs
[0].type
= da
.type
| EAF_ORIGINATED
| EAF_FRESH
;
557 if (v1
.type
!= da
.f_type
)
558 runtime( "Setting int attribute to non-int value" );
559 l
->attrs
[0].u
.data
= v1
.val
.i
;
562 case EAF_TYPE_ROUTER_ID
:
563 /* IP->Quad implicit conversion */
564 if (val_is_ip4(&v1
)) {
565 l
->attrs
[0].u
.data
= ipa_to_u32(v1
.val
.ip
);
568 /* T_INT for backward compatibility */
569 if ((v1
.type
!= T_QUAD
) && (v1
.type
!= T_INT
))
570 runtime( "Setting quad attribute to non-quad value" );
571 l
->attrs
[0].u
.data
= v1
.val
.i
;
574 case EAF_TYPE_OPAQUE
:
575 runtime( "Setting opaque attribute is not allowed" );
577 case EAF_TYPE_IP_ADDRESS
:
579 runtime( "Setting ip attribute to non-ip value" );
580 int len
= sizeof(ip_addr
);
581 struct adata
*ad
= lp_alloc(fs
->pool
, sizeof(struct adata
) + len
);
583 (* (ip_addr
*) ad
->data
) = v1
.val
.ip
;
584 l
->attrs
[0].u
.ptr
= ad
;
586 case EAF_TYPE_AS_PATH
:
587 if (v1
.type
!= T_PATH
)
588 runtime( "Setting path attribute to non-path value" );
589 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
591 case EAF_TYPE_BITFIELD
:
592 if (v1
.type
!= T_BOOL
)
593 runtime( "Setting bit in bitfield attribute to non-bool value" );
595 /* First, we have to find the old value */
596 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
597 u32 data
= e
? e
->u
.data
: 0;
600 l
->attrs
[0].u
.data
= data
| (1u << da
.bit
);
602 l
->attrs
[0].u
.data
= data
& ~(1u << da
.bit
);
605 case EAF_TYPE_INT_SET
:
606 if (v1
.type
!= T_CLIST
)
607 runtime( "Setting clist attribute to non-clist value" );
608 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
610 case EAF_TYPE_EC_SET
:
611 if (v1
.type
!= T_ECLIST
)
612 runtime( "Setting eclist attribute to non-eclist value" );
613 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
615 case EAF_TYPE_LC_SET
:
616 if (v1
.type
!= T_LCLIST
)
617 runtime( "Setting lclist attribute to non-lclist value" );
618 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
620 default: bug("Unknown type in e,S");
624 l
->next
= *fs
->eattrs
;
629 INST(FI_EA_UNSET
, 0, 0) {
635 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
638 l
->flags
= EALF_SORTED
;
640 l
->attrs
[0].id
= da
.ea_code
;
641 l
->attrs
[0].flags
= 0;
642 l
->attrs
[0].type
= EAF_TYPE_UNDEF
| EAF_ORIGINATED
| EAF_FRESH
;
643 l
->attrs
[0].u
.data
= 0;
646 l
->next
= *fs
->eattrs
;
651 INST(FI_PREF_GET
, 0, 1) {
653 RESULT(T_INT
, i
, (*fs
->rte
)->pref
);
656 INST(FI_PREF_SET
, 1, 0) {
659 if (v1
.val
.i
> 0xFFFF)
660 runtime( "Setting preference value out of bounds" );
662 (*fs
->rte
)->pref
= v1
.val
.i
;
665 INST(FI_LENGTH
, 1, 1) { /* Get length of */
668 case T_NET
: RESULT(T_INT
, i
, net_pxlen(v1
.val
.net
)); break;
669 case T_PATH
: RESULT(T_INT
, i
, as_path_getlen(v1
.val
.ad
)); break;
670 case T_CLIST
: RESULT(T_INT
, i
, int_set_get_size(v1
.val
.ad
)); break;
671 case T_ECLIST
: RESULT(T_INT
, i
, ec_set_get_size(v1
.val
.ad
)); break;
672 case T_LCLIST
: RESULT(T_INT
, i
, lc_set_get_size(v1
.val
.ad
)); break;
673 default: runtime( "Prefix, path, clist or eclist expected" );
677 INST(FI_SADR_SRC
, 1, 1) { /* Get SADR src prefix */
679 if (!net_is_sadr(v1
.val
.net
))
680 runtime( "SADR expected" );
682 net_addr_ip6_sadr
*net
= (void *) v1
.val
.net
;
683 net_addr
*src
= falloc(sizeof(net_addr_ip6
));
684 net_fill_ip6(src
, net
->src_prefix
, net
->src_pxlen
);
686 RESULT(T_NET
, net
, src
);
689 INST(FI_ROA_MAXLEN
, 1, 1) { /* Get ROA max prefix length */
691 if (!net_is_roa(v1
.val
.net
))
692 runtime( "ROA expected" );
694 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
695 ((net_addr_roa4
*) v1
.val
.net
)->max_pxlen
:
696 ((net_addr_roa6
*) v1
.val
.net
)->max_pxlen
);
699 INST(FI_ROA_ASN
, 1, 1) { /* Get ROA ASN */
701 if (!net_is_roa(v1
.val
.net
))
702 runtime( "ROA expected" );
704 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
705 ((net_addr_roa4
*) v1
.val
.net
)->asn
:
706 ((net_addr_roa6
*) v1
.val
.net
)->asn
);
709 INST(FI_IP
, 1, 1) { /* Convert prefix to ... */
711 RESULT(T_IP
, ip
, net_prefix(v1
.val
.net
));
714 INST(FI_ROUTE_DISTINGUISHER
, 1, 1) {
716 if (!net_is_vpn(v1
.val
.net
))
717 runtime( "VPN address expected" );
718 RESULT(T_RD
, ec
, net_rd(v1
.val
.net
));
721 INST(FI_AS_PATH_FIRST
, 1, 1) { /* Get first ASN from AS PATH */
724 as_path_get_first(v1
.val
.ad
, &as
);
725 RESULT(T_INT
, i
, as
);
728 INST(FI_AS_PATH_LAST
, 1, 1) { /* Get last ASN from AS PATH */
731 as_path_get_last(v1
.val
.ad
, &as
);
732 RESULT(T_INT
, i
, as
);
735 INST(FI_AS_PATH_LAST_NAG
, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */
737 RESULT(T_INT
, i
, as_path_get_last_nonaggregated(v1
.val
.ad
));
740 INST(FI_RETURN
, 1, 1) {
742 /* Acquire the return value */
744 uint retpos
= fstk
->vcnt
;
746 /* Drop every sub-block including ourselves */
747 while ((fstk
->ecnt
-- > 0) && !(fstk
->estk
[fstk
->ecnt
].emask
& FE_RETURN
))
750 /* Now we are at the caller frame; if no such, try to convert to accept/reject. */
752 if (fstk
->vstk
[retpos
].type
== T_BOOL
)
753 if (fstk
->vstk
[retpos
].val
.i
)
759 runtime("Can't return non-bool from non-function");
761 /* Set the value stack position, overwriting the former implicit void */
762 fstk
->vcnt
= fstk
->estk
[fstk
->ecnt
].ventry
- 1;
764 /* Copy the return value */
765 RESULT_VAL(fstk
->vstk
[retpos
]);
768 INST(FI_CALL
, 0, 1) {
772 /* Push the body on stack */
773 LINEX(sym
->function
);
774 curline
.emask
|= FE_RETURN
;
776 /* Before this instruction was called, there was the T_VOID
777 * automatic return value pushed on value stack and also
778 * sym->function->args function arguments. Setting the
779 * vbase to point to first argument. */
780 ASSERT(curline
.ventry
>= sym
->function
->args
);
781 curline
.ventry
-= sym
->function
->args
;
782 curline
.vbase
= curline
.ventry
;
784 /* Storage for local variables */
785 memset(&(fstk
->vstk
[fstk
->vcnt
]), 0, sizeof(struct f_val
) * sym
->function
->vars
);
786 fstk
->vcnt
+= sym
->function
->vars
;
789 INST(FI_DROP_RESULT
, 1, 0) {
794 INST(FI_SWITCH
, 1, 0) {
797 FID_MEMBER(struct f_tree
*, tree
, [[!same_tree(f1
->tree
, f2
->tree
)]], tree
%p
, item
->tree
);
799 const struct f_tree
*t
= find_tree(tree
, &v1
);
802 t
= find_tree(tree
, &v1
);
804 debug( "No else statement?\n");
805 FID_HIC(,break,return NULL
);
808 /* It is actually possible to have t->data NULL */
813 INST(FI_IP_MASK
, 2, 1) { /* IP.MASK(val) */
816 RESULT(T_IP
, ip
, [[ ipa_is_ip4(v1
.val
.ip
) ?
817 ipa_from_ip4(ip4_and(ipa_to_ip4(v1
.val
.ip
), ip4_mkmask(v2
.val
.i
))) :
818 ipa_from_ip6(ip6_and(ipa_to_ip6(v1
.val
.ip
), ip6_mkmask(v2
.val
.i
))) ]]);
821 INST(FI_PATH_PREPEND
, 2, 1) { /* Path prepend */
824 RESULT(T_PATH
, ad
, [[ as_path_prepend(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
827 INST(FI_CLIST_ADD
, 2, 1) { /* (Extended) Community list add */
830 if (v1
.type
== T_PATH
)
831 runtime("Can't add to path");
833 else if (v1
.type
== T_CLIST
)
835 /* Community (or cluster) list */
838 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
839 RESULT(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
840 /* IP->Quad implicit conversion */
841 else if (val_is_ip4(&v2
))
842 RESULT(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
843 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
))
844 runtime("Can't add set");
845 else if (v2
.type
== T_CLIST
)
846 RESULT(T_CLIST
, ad
, [[ int_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
848 runtime("Can't add non-pair");
851 else if (v1
.type
== T_ECLIST
)
853 /* v2.val is either EC or EC-set */
854 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
))
855 runtime("Can't add set");
856 else if (v2
.type
== T_ECLIST
)
857 RESULT(T_ECLIST
, ad
, [[ ec_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
858 else if (v2
.type
!= T_EC
)
859 runtime("Can't add non-ec");
861 RESULT(T_ECLIST
, ad
, [[ ec_set_add(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
864 else if (v1
.type
== T_LCLIST
)
866 /* v2.val is either LC or LC-set */
867 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
))
868 runtime("Can't add set");
869 else if (v2
.type
== T_LCLIST
)
870 RESULT(T_LCLIST
, ad
, [[ lc_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
871 else if (v2
.type
!= T_LC
)
872 runtime("Can't add non-lc");
874 RESULT(T_LCLIST
, ad
, [[ lc_set_add(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
879 runtime("Can't add to non-[e|l]clist");
882 INST(FI_CLIST_DEL
, 2, 1) { /* (Extended) Community list add or delete */
885 if (v1
.type
== T_PATH
)
887 const struct f_tree
*set
= NULL
;
890 if (v2
.type
== T_INT
)
892 else if ((v2
.type
== T_SET
) && (v2
.val
.t
->from
.type
== T_INT
))
895 runtime("Can't delete non-integer (set)");
897 RESULT(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, set
, key
, 0) ]]);
900 else if (v1
.type
== T_CLIST
)
902 /* Community (or cluster) list */
905 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
906 RESULT(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
907 /* IP->Quad implicit conversion */
908 else if (val_is_ip4(&v2
))
909 RESULT(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
910 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
911 RESULT(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
913 runtime("Can't delete non-pair");
916 else if (v1
.type
== T_ECLIST
)
918 /* v2.val is either EC or EC-set */
919 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
920 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
921 else if (v2
.type
!= T_EC
)
922 runtime("Can't delete non-ec");
924 RESULT(T_ECLIST
, ad
, [[ ec_set_del(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
927 else if (v1
.type
== T_LCLIST
)
929 /* v2.val is either LC or LC-set */
930 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
931 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
932 else if (v2
.type
!= T_LC
)
933 runtime("Can't delete non-lc");
935 RESULT(T_LCLIST
, ad
, [[ lc_set_del(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
939 runtime("Can't delete in non-[e|l]clist");
942 INST(FI_CLIST_FILTER
, 2, 1) { /* (Extended) Community list add or delete */
945 if (v1
.type
== T_PATH
)
949 if ((v2
.type
== T_SET
) && (v2
.val
.t
->from
.type
== T_INT
))
950 RESULT(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, v2
.val
.t
, key
, 1) ]]);
952 runtime("Can't filter integer");
955 else if (v1
.type
== T_CLIST
)
957 /* Community (or cluster) list */
960 if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
961 RESULT(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
963 runtime("Can't filter pair");
966 else if (v1
.type
== T_ECLIST
)
968 /* v2.val is either EC or EC-set */
969 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
970 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
972 runtime("Can't filter ec");
975 else if (v1
.type
== T_LCLIST
)
977 /* v2.val is either LC or LC-set */
978 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
979 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
981 runtime("Can't filter lc");
985 runtime("Can't filter non-[e|l]clist");
988 INST(FI_ROA_CHECK_IMPLICIT
, 0, 1) { /* ROA Check */
991 struct rtable
*table
= rtc
->table
;
994 const net_addr
*net
= (*fs
->rte
)->net
->n
.addr
;
996 /* We ignore temporary attributes, probably not a problem here */
997 /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
998 eattr
*e
= ea_find(*fs
->eattrs
, EA_CODE(PROTOCOL_BGP
, 0x02));
1000 if (!e
|| ((e
->type
& EAF_TYPE_MASK
) != EAF_TYPE_AS_PATH
))
1001 runtime("Missing AS_PATH attribute");
1004 as_path_get_last(e
->u
.ptr
, &as
);
1007 runtime("Missing ROA table");
1009 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1010 runtime("Table type must be either ROA4 or ROA6");
1012 if (table
->addr_type
!= (net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1013 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1015 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, net
, as
) ]]);
1018 INST(FI_ROA_CHECK_EXPLICIT
, 2, 1) { /* ROA Check */
1023 struct rtable
*table
= rtc
->table
;
1028 runtime("Missing ROA table");
1030 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1031 runtime("Table type must be either ROA4 or ROA6");
1033 if (table
->addr_type
!= (v1
.val
.net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1034 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1036 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, v1
.val
.net
, as
) ]]);
1040 INST(FI_FORMAT
, 1, 0) { /* Format */
1042 RESULT(T_STRING
, s
, val_format_str(fpool
, &v1
));
1045 INST(FI_ASSERT
, 1, 0) { /* Birdtest Assert */
1048 FID_MEMBER(char *, s
, [[strcmp(f1
->s
, f2
->s
)]], string
\"%s
\", item
->s
);
1052 if (!bt_assert_hook
)
1053 runtime("No bt_assert hook registered, can't assert");
1055 bt_assert_hook(v1
.val
.i
, what
);