diff options
| author | xTITUSMAXIMUSX <chad@chadhigh.com> | 2026-04-30 20:43:46 -0500 |
|---|---|---|
| committer | sarthurdev <965089+sarthurdev@users.noreply.github.com> | 2026-05-19 23:33:48 +0200 |
| commit | a263b2b62e72b208fd330f9055aefca29640ff2b (patch) | |
| tree | 8195c5bcdae8ac09e03bc69dfe977057a93aa6a5 /python | |
| parent | 2430dd1db9c81f4f61d7629a1d9d9dbcd798a4b2 (diff) | |
| download | vyos-1x-a263b2b62e72b208fd330f9055aefca29640ff2b.tar.gz vyos-1x-a263b2b62e72b208fd330f9055aefca29640ff2b.zip | |
geoip: T8590: fix initialization failure and set clobbering on boot and commit
Three related bugs prevented GeoIP nftables sets from being populated
correctly at boot and when incrementally modifying firewall or policy
route rules.
1. geoip_updated() always returned False
The previous implementation called node_changed() and then searched
the result with dict_search_recursive(changes, 'geoip'). This could
never match: node_changed() is annotated `-> list` and returns a flat
list of the immediate top-level child names whose subtree changed
(e.g. ['ipv4'] for the firewall path, or route names for policy
route). The 'geoip' key sits several levels deeper than those names,
so dict_search_recursive — which only walks dicts and lists for a
matching dict key — never yielded a hit, and geoip_updated() always
returned False. As a consequence, geoip_update() was never triggered
by an incremental add or change of a GeoIP rule; the only path that
ever populated /run/nftables-geoip.conf was the explicit \"update
geoip\" command or the fallback when geoip_refresh() failed.
Fix: bypass node_changed() and call get_config_diff() /
get_child_nodes_diff() directly with expand_nodes=Diff.ADD|Diff.DELETE
and recursive=True. In that mode, the 'add' and 'delete' values in
the returned dict are full nested subtrees of the config diff, so
dict_search_recursive correctly finds 'geoip' wherever it appears in
the change set.
2. GeoIP block executed unconditionally, breaking the boot sequence
geoip_sets() always returns {'name': [], 'ipv6_name': []}. A
non-empty dict is truthy in Python regardless of whether its values
are empty lists, so the guards \"if geoip_sets:\" and
\"if 'name' in geoip_sets:\" were always True.
On boot, when policy_route.py is invoked as a dependent of
firewall.py (triggered by group_resync), it entered the GeoIP block
even with no policy route GeoIP rules configured. Because
/run/nftables-geoip.conf did not yet exist, geoip_refresh() returned
False and geoip_update(policy=policy) was called with an empty
policy. This created /run/nftables-geoip.conf containing only empty
table stubs. When firewall.py subsequently called geoip_refresh(),
the file existed and nft loaded it successfully — so the
geoip_update(firewall) call was never reached and the firewall GeoIP
sets stayed empty for the entire uptime of the router.
Fix: check the actual list contents instead of the container dict:
if geoip_sets['name'] or geoip_sets['ipv6_name'].
3. geoip_update() clobbered the other caller's sets
geoip_update() renders /run/nftables-geoip.conf from both
firewall_sets and policy_sets in a single pass. When called with
only one argument (as firewall.py and policy_route.py each do), the
other argument defaulted to None and that half of the file was
rendered empty, erasing whatever the other script had written. The
geoip-update helper used by \"update geoip\" and the weekly cron was
unaffected because it always passes both arguments, which masked
this bug in normal manual operation.
Fix: when either argument is absent, read the missing config from
the live Config session before building the set tables, so every
invocation writes the complete combined firewall + policy file.
Diffstat (limited to 'python')
0 files changed, 0 insertions, 0 deletions
