summaryrefslogtreecommitdiff
path: root/src/migration-scripts
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-11-02 14:41:30 +0100
committerGitHub <noreply@github.com>2026-11-02 14:41:30 +0100
commitb812fd297f416ccea1dbf67f4af8e135ba68e408 (patch)
tree4b8af99ff850588bb328f278136c1f2319993482 /src/migration-scripts
parentded66339096464636f4c5910c010c637184d1a3e (diff)
parentf819e149290ea56583b3a4b1cef3c5f86a01ab37 (diff)
downloadvyos-1x-b812fd297f416ccea1dbf67f4af8e135ba68e408.tar.gz
vyos-1x-b812fd297f416ccea1dbf67f4af8e135ba68e408.zip
Merge pull request #5582 from ruben-herold/T9482-static-same-distance
static: T9482: handle a blackhole next to a next-hop of the same distance
Diffstat (limited to 'src/migration-scripts')
-rw-r--r--src/migration-scripts/quagga/12-to-1392
1 files changed, 92 insertions, 0 deletions
diff --git a/src/migration-scripts/quagga/12-to-13 b/src/migration-scripts/quagga/12-to-13
new file mode 100644
index 000000000..3d9a1df37
--- /dev/null
+++ b/src/migration-scripts/quagga/12-to-13
@@ -0,0 +1,92 @@
+# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+# T9482:
+# FRR staticd refuses a blackhole or reject route next to a next-hop or
+# interface route of the same prefix with the same distance. frr-reload adds
+# all new lines in one transaction, so on boot such a configuration lost
+# every static route. Set "disable" on that blackhole or reject route, the
+# next-hop or interface route is kept.
+# A prefix with both a blackhole and a reject route rendered only the
+# blackhole route, the reject route gets "disable" the same way.
+
+from vyos.configtree import ConfigTree
+
+
+def distance(config: ConfigTree, path: list) -> int:
+ if config.exists(path + ['distance']):
+ return int(config.return_value(path + ['distance']))
+ return 1
+
+
+def migrate_static(config: ConfigTree, base: list) -> None:
+ if not config.exists(base):
+ return
+
+ sources = [base]
+ if config.exists(base + ['table']):
+ sources += [base + ['table', table] for table in config.list_nodes(base + ['table'])]
+
+ for source in sources:
+ for route in ['route', 'route6']:
+ route_base = source + [route]
+ if not config.exists(route_base):
+ continue
+ for prefix in config.list_nodes(route_base):
+ prefix_base = route_base + [prefix]
+ # Both at once used to render only the blackhole route, the
+ # reject route was ignored. Routes in a table were not checked,
+ # so such a configuration can exist.
+ blackhole_base = prefix_base + ['blackhole']
+ reject_base = prefix_base + ['reject']
+ if (
+ config.exists(blackhole_base)
+ and config.exists(reject_base)
+ and not config.exists(blackhole_base + ['disable'])
+ and not config.exists(reject_base + ['disable'])
+ ):
+ config.set(reject_base + ['disable'])
+
+ for discard in ['blackhole', 'reject']:
+ discard_base = prefix_base + [discard]
+ if not config.exists(discard_base):
+ continue
+ if config.exists(discard_base + ['disable']):
+ continue
+ discard_distance = distance(config, discard_base)
+
+ conflict = False
+ # dhcp-interface routes use the default distance
+ if discard_distance == 1 and config.exists(prefix_base + ['dhcp-interface']):
+ conflict = True
+ for node in ['interface', 'next-hop']:
+ node_base = prefix_base + [node]
+ if not config.exists(node_base):
+ continue
+ for name in config.list_nodes(node_base):
+ if config.exists(node_base + [name, 'disable']):
+ continue
+ if distance(config, node_base + [name]) == discard_distance:
+ conflict = True
+ if conflict:
+ config.set(discard_base + ['disable'])
+
+
+def migrate(config: ConfigTree) -> None:
+ migrate_static(config, ['protocols', 'static'])
+
+ if config.exists(['vrf', 'name']):
+ for vrf in config.list_nodes(['vrf', 'name']):
+ migrate_static(config, ['vrf', 'name', vrf, 'protocols', 'static'])