<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <style>
html {
  line-height: 1.2;
  font-family: serif;
  font-size: 0.9em;
  color: black; 
  background-color: white;
}
body {
  margin: 0;
  margin-right: auto;
  max-width: 36em;
  padding: 1em;
  hyphens: auto;
  overflow-wrap: break-word;
  text-rendering: optimizeLegibility;
  font-kerning: normal;
}
@media print {
  body {
    background-color: transparent;
    color: black;
    font-size: 11pt;
  }
  p, h2, h3 {
    orphans: 3;
    widows: 3;
  }
  h2, h3, h4 {
    page-break-after: avoid;
  }
}
p {
  margin: 1em 0;
}
a {
  color: black;
}
a:visited {
  color: black;
}
img {
  max-width: 100%;
}
h1, h2, h3, h4, h5, h6 {
  margin-top: 1.4em;
}
h5, h6 {
  font-size: 1em;
  font-style: italic;
}
h6 {
  font-weight: normal;
}
ol, ul {
  padding-left: 1.7em;
  margin-top: 1em;
}
li > ol, li > ul {
  margin-top: 0;
}
blockquote {
  margin: 0.5em;
  padding-left: 0.5em;
  border-left: 2px solid #e6e6e6;
  color: #444;
}
code {
  font-family: 'Lucida Console', monospace;
  font-size: 95%;
  margin: 0;
}
pre {
  margin: 1em 0;
  overflow: auto;
  max-width: unset;
  width: fit-content;
}
pre code {
  padding: 0;
  overflow: visible;
  overflow-wrap: normal;
  max-width: unset;
  white-space: pre-wrap;
}
pre code span {
  white-space: pre;
}
.sourceCode {
 background-color: transparent;
 overflow: visible;
}

code.diff span.kw,
code.diff span.dt {
  font-weight: bold;
}

code.diff span.va {
  background-color: rgba(192, 255, 192, 64);
  color: rgb(0, 64, 0);
}

code.diff span.st {
  background-color: rgba(255, 192, 192, 64);
  color: rgb(64, 0, 0);
}

pre.diff {
  background-color: rgb(240, 240, 240);
  padding: 0.4em;
  border: 1pt solid grey;
}

hr {
  background-color: black;
  border: none;
  height: 1px;
  margin: 1em 0;
}
table {
  margin: 1em 0;
  border-collapse: collapse;
  width: 100%;
  overflow-x: auto;
  display: block;
  font-variant-numeric: lining-nums tabular-nums;
}
table caption {
  margin-bottom: 0.75em;
}
tbody {
  margin-top: 0.5em;
  border-top: 1px solid black;
  border-bottom: 1px solid black;
}
th {
  border-top: 1px solid black;
  padding: 0.25em 0.5em 0.25em 0.5em;
}
td {
  padding: 0.125em 0.5em 0.25em 0.5em;
}
header {
  margin-bottom: 4em;
  text-align: center;
}
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
q { quotes: "„" "”" "»" "«"; }
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
  </style>
</head>
<body>
<p>Good … now already afternoon, Nico!</p>
<p>On Sun, Sep 14, 2025 at 11:23:05AM +0200, Nico Schottelius wrote:</p>
<blockquote>
<p>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”.</p>
<p>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.</p>
</blockquote>
<p>Yes, not only regular route filtering, there is multiple usage of the
same filter engine where the context is different.</p>
<blockquote>
<p>And we can even formalise this a bit and update the previous
description:</p>
<pre><code>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.</code></pre>
</blockquote>
<p>Empty string or undefined?</p>
<blockquote>
<blockquote>
<ul>
<li>CLI: show route export bar</li>
</ul>
</blockquote>
<p>proto_to_or_destination_or_better_name is unset -> false</p>
</blockquote>
<p>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.</p>
<p>Otherwise you get different results than what gets exported, and the
users get confused.</p>
<blockquote>
<blockquote>
<ul>
<li>CLI: show route where foo()</li>
</ul>
</blockquote>
<p>proto_to_or_destination_or_better_name is unset -> false</p>
</blockquote>
<p>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.</p>
<blockquote>
<blockquote>
<ul>
<li>CLI: eval foo</li>
</ul>
</blockquote>
<p>proto_to_or_destination_or_better_name is unset -> false</p>
</blockquote>
<p>The same as above. Please note that if you ask for
<code>eval net</code>, you get a runtime error because there is no route
at all.</p>
<blockquote>
<p>Now comes a however: however it might be helpful then to add a
context alike syntax:</p>
<p>show route where foo() context proto bar</p>
<p>–> true</p>
<p>show route where foo() context proto foo</p>
<p>–> no routes</p>
</blockquote>
<p>This looks viable, even though maybe a little bit clumsy.</p>
<blockquote>
<blockquote>
<ul>
<li>Conf: protocol bgp yay { require graceful restart ( foo() ); }</li>
</ul>
</blockquote>
<p>proto_to_or_destination_or_better_name is = yay -> false</p>
</blockquote>
<p>Indeed.</p>
<blockquote>
<blockquote>
<ul>
<li>Conf: template bgp yippie { require graceful restart ( foo() );
}</li>
</ul>
</blockquote>
<p>proto_to_or_destination_or_better_name is = yippie -> false</p>
</blockquote>
<p>But then the template is used to create the protocol <code>bar</code>
and one may expect there that we would evaluate the expression in the
protocol context …</p>
<blockquote>
<blockquote>
<ul>
<li>Conf: ipv6 table bastu { sorted ( foo() ); };</li>
</ul>
</blockquote>
<p>I’m not familiar with the sorted function, so no clue</p>
</blockquote>
<p>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.</p>
<blockquote>
<blockquote>
<ul>
<li>Conf: function meow() { if foo() then return 1; else return 2; }
router id ( meow() );</li>
</ul>
</blockquote>
<p>Same as above, depends on where meow() is called.</p>
</blockquote>
<p>In toplevel config, no block.</p>
<p>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.</p>
<blockquote>
<p>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:</p>
<ul>
<li>set inside a protocol …{} context</li>
<li>empty/unset outside of that context</li>
</ul>
</blockquote>
<p>Ha, you got to the same conclusion as myself above.</p>
<blockquote>
<blockquote>
<p>Thank you for opening this topic!</p>
</blockquote>
<p>Thank you for being open and continuing to provide a great
software!</p>
</blockquote>
<p>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.</p>
<p>Enjoy your sunny day!<br />
Maria</p>
<p>–<br />
Maria Matejka (she/her) | BIRD Team Leader | CZ.NIC, z.s.p.o.</p>
</body>
</html>