Currently, the per-interface source-validation rules create 2 nftables rules per interface (one rule to match and one rule to exit the processing early). This scales poorly with a large number of interfaces. If a user has 20 wireguard interfaces (wg1-20) where they want strict uRPF, but have multiple WAN interfaces that they are okay with being asymmetric, then they must apply the strict policy only on the wireguard interfaces. 20 interfaces would create 40 rules. This means traffic that hits the last wireguard interface would traverse 40 rules.
By moving the validation config to vmaps, this means at worst there would be 3 rules that are needed to traverse, which would be a per-interface strict rule, a per-interface loose rule, and a global rule. More typically there'd only be one to 2 rules....a huge improvement over the existing 41 rules that would be processed worst case (40 per-interface rules + the global rule).
Existing implementation (10 interfaces set to strict; no global policy):
table ip raw {
chain vyos_global_rpfilter {
return
}
chain vyos_rpfilter {
type filter hook prerouting priority raw; policy accept;
iifname "eth2.210" fib saddr . iif oif 0 counter drop
iifname "eth2.210" counter return
iifname "eth2.209" fib saddr . iif oif 0 counter drop
iifname "eth2.209" counter return
iifname "eth2.208" fib saddr . iif oif 0 counter drop
iifname "eth2.208" counter return
iifname "eth2.207" fib saddr . iif oif 0 counter drop
iifname "eth2.207" counter return
iifname "eth2.206" fib saddr . iif oif 0 counter drop
iifname "eth2.206" counter return
iifname "eth2.205" fib saddr . iif oif 0 counter drop
iifname "eth2.205" counter return
iifname "eth2.204" fib saddr . iif oif 0 counter drop
iifname "eth2.204" counter return
iifname "eth2.203" fib saddr . iif oif 0 counter drop
iifname "eth2.203" counter return
iifname "eth2.202" fib saddr . iif oif 0 counter drop
iifname "eth2.202" counter return
iifname "eth2.201" fib saddr . iif oif 0 counter drop
iifname "eth2.201" counter return
counter jump vyos_global_rpfilter
}
}Proposed implementation (10 interfaces set to strict; no global policy):
table ip raw {
map rpfilter_strict {
typeof fib saddr . iif oif : verdict
counter
elements = {
0 : drop,
"eth2.201" : accept,
"eth2.202" : accept,
"eth2.203" : accept,
"eth2.204" : accept,
"eth2.205" : accept,
"eth2.206" : accept,
"eth2.207" : accept,
"eth2.208" : accept,
"eth2.209" : accept,
"eth2.210" : accept
}
}
map rpfilter_loose {
typeof fib saddr oif : verdict
counter
}
set rpfilter_strict_ifaces {
type ifname
flags constant
elements = {
"eth2.201",
"eth2.202",
"eth2.203",
"eth2.204",
"eth2.205",
"eth2.206",
"eth2.207",
"eth2.208",
"eth2.209",
"eth2.2010"
}
}
set rpfilter_loose_ifaces {
type ifname
flags constant
}
chain vyos_global_rpfilter {
}
chain vyos_rpfilter {
type filter hook prerouting priority raw; policy accept;
iifname @rpfilter_strict_ifaces fib saddr . iif oif vmap @rpfilter_strict
counter jump vyos_global_rpfilter
}
}