Writing filters that change output depending on peer
Hello, we are having a bit of a "special case" on various ASNs that we'd like to implement as clean as possible in bird: For a number of ASNs that we control we would like to emit the same set of networks (as in filtered, the actualy networks might be different) PLUS a test network that allows us to verify that the specific link is working from the outside. Or in other words: - ASN_A sends routes defined in filter "to_outside" to EXT_ASN_1 PLUS 2001:db8:1::/48 - ASN_A sends routes defined in filter "to_outside" to EXT_ASN_2 PLUS 2001:db8:2::/48 - ASN_B sends routes defined in filter "to_outside" to EXT_ASN_3 PLUS 2001:db8:3::/48 - ASN_B sends routes defined in filter "to_outside" to EXT_ASN_4 PLUS 2001:db8:4::/48 ... The routes defined in "to_outside" are basically all locally known /48's of a specific /32. What I was looking for is whether I can match on the peer in the filter, something like that: -------------------------------------------------------------------------------- function has_internet_length() -> bool { if ((net.type = NET_IP6 && net.len > 48) || (net.type = NET_IP4 && net.len > 24)) then { return false; } return true; } filter to_outside { if(!has_internet_length()) then reject; case peer in ext_asn_1: if(net ~ [ 2001:db8:1::/48 ]) then accept; ext_asn_2: if(net ~ [ 2001:db8:2::/48 ]) then accept; ext_asn_3: if(net ~ [ 2001:db8:3::/48 ]) then accept; ext_asn_4: if(net ~ [ 2001:db8:4::/48 ]) then accept; ... reject; } -------------------------------------------------------------------------------- Obviously above syntax is not bird syntax, but I think it is clear what I mean. It would be possible to write some functions and then for each peer define a different filter only differing by a single line, but it "feels wrong" to repeat all that code: -------------------------------------------------------------------------------- filter asn_a_to_ext_asn_1 { if(some_main_function()) then accept; if(net ~ [ 2001:db8:1::/48 ]) then accept; } filter asn_a_to_ext_asn_2 { if(some_main_function()) then accept; if(net ~ [ 2001:db8:1::/48 ]) then accept; } filter asn_b_to_ext_asn_3 { if(some_main_function()) then accept; if(net ~ [ 2001:db8:3::/48 ]) then accept; } filter asn_b_to_ext_asn_3 { if(some_main_function()) then accept; if(net ~ [ 2001:db8:4::/48 ]) then accept; } -------------------------------------------------------------------------------- How would you solve that cleanly in bird? I'm looking forward to your ideas. BR, Nico -- Sustainable and modern Infrastructures by ungleich.ch
... to follow up, from the documentation of bird 2: string proto The name of the protocol which the route has been imported from. Read-only. What I am looking for is the opposite: string proto_to_or_destination_or_better_name The name of the protocol in which the filter or function is being called in. Allows to match on the destination. Example: if(proto_to_or_destination_or_better_name = "asn_1" and net ...) then accept; (obviously I made up the above description, but that's exactly what I'm looking for). Still curious on what others think about this. -- Sustainable and modern Infrastructures by ungleich.ch
Hi Nico, I remembered some related question around a year ago: https://trubka.network.cz/pipermail/bird-users/2024-September/017839.html AFAIK such feature hasn't been implemented yet. Regards, Alexander On Sat, Sep 13, 2025, 00:44 Nico Schottelius via Bird-users < bird-users@network.cz> wrote:
... to follow up, from the documentation of bird 2:
string proto
The name of the protocol which the route has been imported from. Read-only.
What I am looking for is the opposite:
string proto_to_or_destination_or_better_name
The name of the protocol in which the filter or function is being called in. Allows to match on the destination.
Example:
if(proto_to_or_destination_or_better_name = "asn_1" and net ...) then accept;
(obviously I made up the above description, but that's exactly what I'm looking for).
Still curious on what others think about this.
-- Sustainable and modern Infrastructures by ungleich.ch
Hola! On Sat, Sep 13, 2025 at 09:14:33AM +0200, Alexander Zubkov via Bird-users wrote:
I remembered some related question around a year ago: https://trubka.network.cz/pipermail/bird-users/2024-September/017839.html
AFAIK such feature hasn't been implemented yet.
Hasn't been implemented yet but it may finally be time to look at it soon. On Sat, Sep 13, 2025, 00:44 Nico Schottelius via Bird-users wrote:
string proto_to_or_destination_or_better_name
The name of the protocol in which the filter or function is being called in. Allows to match on the destination.
Let's have a configuration like this: ``` function foo() { return proto_to_or_destination_or_better_name = "bar"; } protocol bgp bar from yippie { ipv6 { import where foo(); export where foo(); }; ipv4 { import where foo(); export where foo(); }; } ``` and some more irrelevant boilerplate, like the `protocol device`. Now, a bunch of more and more cursed questions because I have an evil mind. - CLI: `show route export bar` - CLI: `show route where foo()` - CLI: `eval foo` - Conf: `protocol bgp yay { require graceful restart ( foo() ); }` - Conf: `template bgp yippie { require graceful restart ( foo() ); }` - Conf: `ipv6 table bastu { sorted ( foo() ); };` - Conf: `function meow() { if foo() then return 1; else return 2; } router id ( meow() );` I hope that I haven't botched the syntax, I'm too lazy to try it out. My questions are basically how BIRD would be expected to behave in these situations with this feature. I'm not trying to down the requested feature, and I have a bunch of ideas how this kind of feature may actually behave and what should be implemented … but before I disclose that, I wanna see whether the community has something maybe better or easier. Thank you for opening this topic! Maria -- Maria Matejka (she/her) | BIRD Team Leader | CZ.NIC, z.s.p.o.
Good morning Maria, you have a very valid point I haven't though about yet. You are right that currently filters and functions can just be used in the CLI w/o a "context". And maybe that is also the answer as to how bird should behave: if no "context" (or peer or whatever it will be named in the end) is supplied, I think bird should behave the same as before. And we can even formalise this a bit and update the previous description: string proto_to_or_destination_or_better_name The name of the protocol in which the filter or function is being called in. Allows to match on the destination. Might contain the empty string, if the evaluated outside a protocol context. Thus: Maria Matejka <maria.matejka@nic.cz> writes:
Let’s have a configuration like this:
function foo() { return proto_to_or_destination_or_better_name = "bar"; }
protocol bgp bar from yippie { ipv6 { import where foo(); export where foo(); }; ipv4 { import where foo(); export where foo(); }; }
and some more irrelevant boilerplate, like the protocol device.
Now, a bunch of more and more cursed questions because I have an evil mind.
* CLI: show route export bar
proto_to_or_destination_or_better_name is unset -> false
* CLI: show route where foo()
proto_to_or_destination_or_better_name is unset -> false
* CLI: eval foo
proto_to_or_destination_or_better_name is unset -> false Now comes a however: however it might be helpful then to add a context alike syntax: show route where foo() context proto bar --> true show route where foo() context proto foo --> no routes
* Conf: protocol bgp yay { require graceful restart ( foo() ); }
proto_to_or_destination_or_better_name is = yay -> false
* Conf: template bgp yippie { require graceful restart ( foo() ); }
proto_to_or_destination_or_better_name is = yippie -> false
* Conf: ipv6 table bastu { sorted ( foo() ); };
I'm not familiar with the sorted function, so no clue
* Conf: function meow() { if foo() then return 1; else return 2; } router id ( meow() );
Same as above, depends on where meow() is called. I think in the end, the actual value of proto_to_or_destination_or_better_name should be handled similar to how "net" is passed down: - set inside a protocol ...{} context - empty/unset outside of that context
I hope that I haven’t botched the syntax, I’m too lazy to try it out. My questions are basically how BIRD would be expected to behave in these situations with this feature.
Hope that clarifies above.
I’m not trying to down the requested feature, and I have a bunch of ideas how this kind of feature may actually behave and what should be implemented … but before I disclose that, I wanna see whether the community has something maybe better or easier.
Sounds reasonable!
Thank you for opening this topic!
Thank you for being open and continuing to provide a great software! Sunny greetings from Lachen SZ, Nico -- Sustainable and modern Infrastructures by ungleich.ch
Good … now already afternoon, Nico! On Sun, Sep 14, 2025 at 11:23:05AM +0200, Nico Schottelius wrote:
you have a very valid point I haven't though about yet. You are right that currently filters and functions can just be used in the CLI w/o a "context".
And maybe that is also the answer as to how bird should behave: if no "context" (or peer or whatever it will be named in the end) is supplied, I think bird should behave the same as before.
Yes, not only regular route filtering, there is multiple usage of the same filter engine where the context is different.
And we can even formalise this a bit and update the previous description:
string proto_to_or_destination_or_better_name
The name of the protocol in which the filter or function is being called in. Allows to match on the destination.
Might contain the empty string, if the evaluated outside a protocol context.
Empty string or undefined?
* CLI: show route export bar
proto_to_or_destination_or_better_name is unset -> false
Well, this call is intended as "how do routes export to bar" check so here the name would be probably better set as the filter should imho run in the protocol context. Otherwise you get different results than what gets exported, and the users get confused.
* CLI: show route where foo()
proto_to_or_destination_or_better_name is unset -> false
The question here is whether the filter engine should just silently treat the parameter as empty, or complain. I'm a little inclined for complaining because people may forget that they have target-specific behavior, try this while debugging, and get confused.
* CLI: eval foo
proto_to_or_destination_or_better_name is unset -> false
The same as above. Please note that if you ask for `eval net`, you get a runtime error because there is no route at all.
Now comes a however: however it might be helpful then to add a context alike syntax:
show route where foo() context proto bar
--> true
show route where foo() context proto foo
--> no routes
This looks viable, even though maybe a little bit clumsy.
* Conf: protocol bgp yay { require graceful restart ( foo() ); }
proto_to_or_destination_or_better_name is = yay -> false
Indeed.
* Conf: template bgp yippie { require graceful restart ( foo() ); }
proto_to_or_destination_or_better_name is = yippie -> false
But then the template is used to create the protocol `bar` and one may expect there that we would evaluate the expression in the protocol context …
* Conf: ipv6 table bastu { sorted ( foo() ); };
I'm not familiar with the sorted function, so no clue
It's just a table parameter. I would say that the function may make use of some similar context but regarding the table involved instead.
* Conf: function meow() { if foo() then return 1; else return 2; } router id ( meow() );
Same as above, depends on where meow() is called.
In toplevel config, no block. Basically these config uses are rudimentary uses of the filter engine to evaluate some config values. And the question is how to make this work the right way.
I think in the end, the actual value of proto_to_or_destination_or_better_name should be handled similar to how "net" is passed down:
- set inside a protocol ...{} context - empty/unset outside of that context
Ha, you got to the same conclusion as myself above.
Thank you for opening this topic!
Thank you for being open and continuing to provide a great software!
We try our best. With open-source, one can actually quite openly discus what is going on and how things are going to be implemented. In the end, we need to collect the feature requirements early enough to not waste time implementing things wrongly. Enjoy your sunny day! Maria -- Maria Matejka (she/her) | BIRD Team Leader | CZ.NIC, z.s.p.o.
Il giorno 12 set 2025, alle ore 21:06, Nico Schottelius via Bird-users <bird-users@network.cz> ha scritto:
string proto_to_or_destination_or_better_name
The name of the protocol in which the filter or function is being called in Still curious on what others think about this.
This is the thing that I am missing for all these 10 years of working with BIRD!
participants (4)
-
Alexander Javoronkov -
Alexander Zubkov -
Maria Matejka -
Nico Schottelius