From d871fe9c4c65de87232802ed54b263c9b2824391 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Thu, 28 Aug 2025 14:46:48 +0200 Subject: [PATCH 1/2] bgp: T7760: deprecate per bgp vrf instance system-as node Originating from the bug in T7665. To avoid potential issues down the line - and given that there's no compelling technical reason to retain the system-as CLI node under per-VRF BGP configuration, which cannot be achieved through alternative means - the maintainers have collectively decided to deprecate the following command: set vrf name protocols bgp system-as Starting with VyOS 1.4.4, this CLI command will be considered deprecated. While it will still be accepted, it will no longer have any operational effect. A deprecation warning will be displayed at commit time, indicating that the BGP ASN from the global BGP configuration is now used instead. A migration script will handle the transition and perform the following actions: * Ensure a global BGP configuration exists; if not, initialize one. * Iterate over all configured VRFs to determine whether a BGP instance exists * For any insance, update the configuration to use the global system-as and apply the local-as ASN no-prepend replace-as option on all affected neighbors to preserve existing behavior. * If a neighbor is already configured with a local-as directive, that neighbor will be excluded from the migration process, as it already follows a custom configuration. * Add allowas-in per neighbor option. Required to not deny prefix received updates due to as-path contains our own global ASN. --- data/templates/frr/bgpd.frr.j2 | 6 +- .../include/bgp/protocol-common-config.xml.i | 12 - .../include/version/bgp-version.xml.i | 2 +- interface-definitions/protocols_bgp.xml.in | 12 + interface-definitions/vrf.xml.in | 12 + .../config-tests/bgp-evpn-l3vpn-pe-router | 3 - .../bgp-evpn-l3vpn-vrf-different-asn | 107 +++++++ smoketest/config-tests/vrf-bgp-pppoe-underlay | 2 +- .../configs/bgp-evpn-l3vpn-vrf-different-asn | 300 ++++++++++++++++++ smoketest/scripts/cli/test_protocols_bgp.py | 62 ++-- src/conf_mode/protocols_bgp.py | 34 +- src/migration-scripts/bgp/6-to-7 | 108 +++++++ 12 files changed, 588 insertions(+), 72 deletions(-) create mode 100644 smoketest/config-tests/bgp-evpn-l3vpn-vrf-different-asn create mode 100644 smoketest/configs/bgp-evpn-l3vpn-vrf-different-asn create mode 100644 src/migration-scripts/bgp/6-to-7 diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2 index e5a75090f9..169487dbd5 100644 --- a/data/templates/frr/bgpd.frr.j2 +++ b/data/templates/frr/bgpd.frr.j2 @@ -259,7 +259,11 @@ {# j2lint: disable=jinja-statements-delimeter #} {%- endmacro -%} ! -router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }} +{% if vrf is vyos_defined %} + router bgp {{ dependent_vrfs.default.protocols.bgp.system_as }} vrf {{ vrf }} +{% else %} + router bgp {{ system_as }} +{% endif %} {% if parameters.ebgp_requires_policy is vyos_defined %} bgp ebgp-requires-policy {% else %} diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index ab016884e1..6c9424be5c 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -1005,18 +1005,6 @@ - - - Autonomous System Number (ASN) - - u32:1-4294967294 - Autonomous System Number - - - - - - BGP neighbor diff --git a/interface-definitions/include/version/bgp-version.xml.i b/interface-definitions/include/version/bgp-version.xml.i index c902761515..21fddf9ae0 100644 --- a/interface-definitions/include/version/bgp-version.xml.i +++ b/interface-definitions/include/version/bgp-version.xml.i @@ -1,3 +1,3 @@ - + diff --git a/interface-definitions/protocols_bgp.xml.in b/interface-definitions/protocols_bgp.xml.in index e1a8229991..5f932ac86a 100644 --- a/interface-definitions/protocols_bgp.xml.in +++ b/interface-definitions/protocols_bgp.xml.in @@ -9,6 +9,18 @@ #include + + + Autonomous System Number (ASN) + + u32:1-4294967294 + Autonomous System Number + + + + + + diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index 3fa95076ee..0d1033e252 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -57,6 +57,18 @@ #include + + + Autonomous System Number (ASN) - DEPRECATED + + u32:1-4294967294 + Autonomous System Number + + + + + + diff --git a/smoketest/config-tests/bgp-evpn-l3vpn-pe-router b/smoketest/config-tests/bgp-evpn-l3vpn-pe-router index f867c221e9..0f10475f6f 100644 --- a/smoketest/config-tests/bgp-evpn-l3vpn-pe-router +++ b/smoketest/config-tests/bgp-evpn-l3vpn-pe-router @@ -105,12 +105,10 @@ set system syslog local facility all level 'info' set system syslog local facility local7 level 'debug' set vrf name blue protocols bgp address-family ipv4-unicast redistribute connected set vrf name blue protocols bgp address-family l2vpn-evpn advertise ipv4 unicast -set vrf name blue protocols bgp system-as '100' set vrf name blue table '2000' set vrf name blue vni '2000' set vrf name green protocols bgp address-family ipv4-unicast redistribute connected set vrf name green protocols bgp address-family l2vpn-evpn advertise ipv4 unicast -set vrf name green protocols bgp system-as '100' set vrf name green table '4000' set vrf name green vni '4000' set vrf name mgmt protocols static route 0.0.0.0/0 next-hop 192.0.2.62 @@ -118,6 +116,5 @@ set vrf name mgmt protocols static route6 ::/0 next-hop 2001:db8:ffff::1 set vrf name mgmt table '1000' set vrf name red protocols bgp address-family ipv4-unicast redistribute connected set vrf name red protocols bgp address-family l2vpn-evpn advertise ipv4 unicast -set vrf name red protocols bgp system-as '100' set vrf name red table '3000' set vrf name red vni '3000' diff --git a/smoketest/config-tests/bgp-evpn-l3vpn-vrf-different-asn b/smoketest/config-tests/bgp-evpn-l3vpn-vrf-different-asn new file mode 100644 index 0000000000..5f3f33b8f7 --- /dev/null +++ b/smoketest/config-tests/bgp-evpn-l3vpn-vrf-different-asn @@ -0,0 +1,107 @@ +set interfaces bridge br2000 address '10.10.7.1/30' +set interfaces bridge br2000 description 'customer blue' +set interfaces bridge br2000 member interface eth2.2600 +set interfaces bridge br2000 member interface vxlan2000 +set interfaces bridge br2000 vrf 'blue' +set interfaces dummy dum0 address '10.255.253.1/32' +set interfaces dummy dum0 address '10.255.253.24/32' +set interfaces ethernet eth0 duplex 'auto' +set interfaces ethernet eth0 offload gro +set interfaces ethernet eth0 offload gso +set interfaces ethernet eth0 offload sg +set interfaces ethernet eth0 offload tso +set interfaces ethernet eth0 speed 'auto' +set interfaces ethernet eth0 vif 6 address '192.168.0.1/24' +set interfaces ethernet eth0 vif 6 vrf 'green' +set interfaces ethernet eth1 duplex 'auto' +set interfaces ethernet eth1 speed 'auto' +set interfaces ethernet eth1 vif 101 address '192.0.2.1/31' +set interfaces ethernet eth1 vif 888 address '192.0.2.3/31' +set interfaces ethernet eth1 vif 888 vrf 'blue' +set interfaces ethernet eth2 duplex 'auto' +set interfaces ethernet eth2 speed 'auto' +set interfaces ethernet eth2 vif 1111 address '192.0.2.11/31' +set interfaces ethernet eth2 vif 1111 vrf 'red' +set interfaces ethernet eth2 vif 1113 address '192.0.2.13/31' +set interfaces ethernet eth2 vif 1113 vrf 'red' +set interfaces ethernet eth2 vif 2600 +set interfaces vxlan vxlan2000 mtu '1500' +set interfaces vxlan vxlan2000 parameters nolearning +set interfaces vxlan vxlan2000 port '4789' +set interfaces vxlan vxlan2000 source-address '10.255.253.1' +set interfaces vxlan vxlan2000 vni '2000' +set protocols bgp address-family l2vpn-evpn advertise ipv4 unicast +set protocols bgp address-family l2vpn-evpn advertise-all-vni +set protocols bgp neighbor 10.255.253.9 address-family ipv4-unicast allowas-in number '10' +set protocols bgp neighbor 10.255.253.9 address-family ipv4-unicast nexthop-self +set protocols bgp neighbor 10.255.253.9 remote-as 'internal' +set protocols bgp neighbor 10.255.253.9 update-source '10.255.253.1' +set protocols bgp neighbor 10.255.253.25 peer-group 'EVPN' +set protocols bgp parameters log-neighbor-changes +set protocols bgp parameters network-import-check +set protocols bgp parameters router-id '10.255.253.1' +set protocols bgp peer-group EVPN address-family l2vpn-evpn +set protocols bgp peer-group EVPN remote-as '100' +set protocols bgp peer-group EVPN update-source 'dum0' +set protocols bgp system-as '100' +set protocols ospf interface dum0 area '0' +set protocols ospf interface eth1.101 area '0' +set protocols ospf interface eth1.101 network 'broadcast' +set protocols ospf log-adjacency-changes detail +set protocols ospf parameters router-id '10.255.253.1' +set service ntp allow-client address '0.0.0.0/0' +set service ntp allow-client address '::/0' +set service ntp server 0.pool.ntp.org +set service ntp server 1.pool.ntp.org +set service ntp server 2.pool.ntp.org +set service ssh disable-host-validation +set service ssh port '22' +set system config-management commit-revisions '20' +set system conntrack modules ftp +set system conntrack modules h323 +set system conntrack modules nfs +set system conntrack modules pptp +set system conntrack modules sip +set system conntrack modules sqlnet +set system conntrack modules tftp +set system console device ttyS0 speed '115200' +set system domain-name 'vyos.net' +set system host-name 'evpn-l3vpn-vrf-foo' +set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0' +set system login user vyos authentication plaintext-password '' +set system option ctrl-alt-delete 'reboot' +set system option reboot-on-panic +set system syslog console facility all level 'emerg' +set system syslog console facility mail level 'info' +set system syslog local facility all level 'info' +set system syslog local facility local7 level 'debug' +set system syslog preserve-fqdn +set system time-zone 'Europe/Berlin' +set vrf name blue protocols bgp address-family ipv4-unicast redistribute connected +set vrf name blue protocols bgp neighbor 192.0.2.2 address-family ipv4-unicast allowas-in number '1' +set vrf name blue protocols bgp neighbor 192.0.2.2 address-family ipv4-unicast maximum-prefix '20' +set vrf name blue protocols bgp neighbor 192.0.2.2 local-as 667 no-prepend replace-as +set vrf name blue protocols bgp neighbor 192.0.2.2 remote-as '100' +set vrf name blue protocols bgp neighbor 192.0.2.4 address-family ipv4-unicast maximum-prefix '40' +set vrf name blue protocols bgp neighbor 192.0.2.4 local-as 200 +set vrf name blue protocols bgp neighbor 192.0.2.4 remote-as '300' +set vrf name blue protocols bgp neighbor 192.0.2.6 address-family ipv4-unicast allowas-in number '7' +set vrf name blue protocols bgp neighbor 192.0.2.6 local-as 667 no-prepend replace-as +set vrf name blue protocols bgp neighbor 192.0.2.6 remote-as '112' +set vrf name blue table '2001' +set vrf name blue vni '2001' +set vrf name green table '4001' +set vrf name red protocols bgp address-family ipv4-unicast redistribute connected +set vrf name red protocols bgp neighbor 192.0.2.10 peer-group 'foo' +set vrf name red protocols bgp neighbor 192.0.2.12 address-family ipv4-unicast allowas-in number '10' +set vrf name red protocols bgp neighbor 192.0.2.12 local-as 65412 no-prepend replace-as +set vrf name red protocols bgp neighbor 192.0.2.12 peer-group 'bar' +set vrf name red protocols bgp neighbor 192.0.2.14 local-as 1111 no-prepend replace-as +set vrf name red protocols bgp neighbor 192.0.2.14 peer-group 'foo' +set vrf name red protocols bgp peer-group bar address-family ipv4-unicast allowas-in number '10' +set vrf name red protocols bgp peer-group bar remote-as '333' +set vrf name red protocols bgp peer-group foo address-family ipv4-unicast soft-reconfiguration inbound +set vrf name red protocols bgp peer-group foo local-as 1000 +set vrf name red protocols bgp peer-group foo remote-as '300' +set vrf name red table '3001' +set vrf name red vni '3001' diff --git a/smoketest/config-tests/vrf-bgp-pppoe-underlay b/smoketest/config-tests/vrf-bgp-pppoe-underlay index e3c765a9a9..4ce1a43287 100644 --- a/smoketest/config-tests/vrf-bgp-pppoe-underlay +++ b/smoketest/config-tests/vrf-bgp-pppoe-underlay @@ -107,6 +107,7 @@ set policy prefix-list6 AS100-origin-v6 rule 10 action 'permit' set policy prefix-list6 AS100-origin-v6 rule 10 prefix '2001:db8:200::/40' set policy prefix-list6 AS200-origin-v6 rule 10 action 'permit' set policy prefix-list6 AS200-origin-v6 rule 10 prefix '2001:db8:100::/40' +set protocols bgp system-as '100' set protocols static route 100.64.50.0/23 next-hop 100.64.51.221 set protocols static route 192.0.2.255/32 interface pppoe7 set protocols static route6 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128 interface pppoe7 @@ -178,7 +179,6 @@ set vrf name vyos-test-01 protocols bgp peer-group AS100v6 address-family ipv6-u set vrf name vyos-test-01 protocols bgp peer-group AS100v6 capability dynamic set vrf name vyos-test-01 protocols bgp peer-group AS100v6 remote-as 'internal' set vrf name vyos-test-01 protocols bgp peer-group AS100v6 update-source 'dum0' -set vrf name vyos-test-01 protocols bgp system-as '100' set vrf name vyos-test-01 protocols static route 100.64.50.0/23 blackhole set vrf name vyos-test-01 protocols static route 100.64.51.32/27 next-hop 100.64.51.5 set vrf name vyos-test-01 protocols static route 192.168.0.0/24 next-hop 100.64.51.220 diff --git a/smoketest/configs/bgp-evpn-l3vpn-vrf-different-asn b/smoketest/configs/bgp-evpn-l3vpn-vrf-different-asn new file mode 100644 index 0000000000..b94f76c887 --- /dev/null +++ b/smoketest/configs/bgp-evpn-l3vpn-vrf-different-asn @@ -0,0 +1,300 @@ +interfaces { + bridge br2000 { + address "10.10.7.1/30" + description "customer blue" + member { + interface eth2.2600 { + } + interface vxlan2000 { + } + } + vrf "blue" + } + dummy dum0 { + address "10.255.253.1/32" + address "10.255.253.24/32" + } + ethernet eth0 { + duplex "auto" + offload { + gro + gso + sg + tso + } + speed "auto" + vif 6 { + address "192.168.0.1/24" + vrf "green" + } + } + ethernet eth1 { + duplex "auto" + speed "auto" + vif 101 { + address "192.0.2.1/31" + } + vif 888 { + address "192.0.2.3/31" + vrf "blue" + } + } + ethernet eth2 { + duplex "auto" + speed "auto" + vif 1111 { + address "192.0.2.11/31" + vrf "red" + } + vif 1113 { + address "192.0.2.13/31" + vrf "red" + } + vif 2600 { + } + } + vxlan vxlan2000 { + mtu "1500" + parameters { + nolearning + } + port "4789" + source-address "10.255.253.1" + vni "2000" + } +} +protocols { + bgp { + address-family { + l2vpn-evpn { + advertise { + ipv4 { + unicast + } + } + advertise-all-vni + } + } + neighbor 10.255.253.9 { + address-family { + ipv4-unicast { + allowas-in { + number "10" + } + nexthop-self + } + } + remote-as "internal" + update-source "10.255.253.1" + } + neighbor 10.255.253.25 { + peer-group "EVPN" + } + parameters { + log-neighbor-changes + network-import-check + router-id "10.255.253.1" + } + peer-group EVPN { + address-family { + l2vpn-evpn + } + remote-as "100" + update-source "dum0" + } + system-as "100" + } + ospf { + interface dum0 { + area "0" + } + interface eth1.101 { + area "0" + network "broadcast" + } + log-adjacency-changes { + detail + } + parameters { + router-id "10.255.253.1" + } + } +} +service { + ntp { + allow-client { + address "0.0.0.0/0" + address "::/0" + } + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + ssh { + disable-host-validation + port "22" + } +} +system { + config-management { + commit-revisions "20" + } + conntrack { + modules { + ftp + h323 + nfs + pptp + sip + sqlnet + tftp + } + } + console { + device ttyS0 { + speed "115200" + } + } + domain-name "vyos.net" + host-name "evpn-l3vpn-vrf-foo" + login { + user vyos { + authentication { + encrypted-password "$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0" + plaintext-password "" + } + } + } + option { + ctrl-alt-delete "reboot" + reboot-on-panic + } + syslog { + console { + facility all { + level "emerg" + } + facility mail { + level "info" + } + } + global { + facility all { + level "info" + } + facility local7 { + level "debug" + } + preserve-fqdn + } + } + time-zone "Europe/Berlin" +} +vrf { + name blue { + protocols { + bgp { + address-family { + ipv4-unicast { + redistribute { + connected + } + } + } + neighbor 192.0.2.2 { + address-family { + ipv4-unicast { + maximum-prefix "20" + } + } + remote-as "100" + } + neighbor 192.0.2.4 { + address-family { + ipv4-unicast { + maximum-prefix "40" + } + } + local-as 200 { + } + remote-as "300" + } + neighbor 192.0.2.6 { + address-family { + ipv4-unicast { + allowas-in { + number "6" + } + } + } + remote-as "112" + } + system-as "667" + } + } + table "2001" + vni "2001" + } + name green { + table "4001" + } + name red { + protocols { + bgp { + address-family { + ipv4-unicast { + redistribute { + connected + } + } + } + neighbor 192.0.2.10 { + peer-group "foo" + } + neighbor 192.0.2.12 { + peer-group "bar" + } + neighbor 192.0.2.14 { + local-as 1111 { + no-prepend { + replace-as + } + } + peer-group "foo" + } + peer-group bar { + address-family { + ipv4-unicast { + allowas-in { + number "10" + } + } + } + remote-as "333" + } + peer-group foo { + address-family { + ipv4-unicast { + soft-reconfiguration { + inbound + } + } + } + local-as 1000 { + } + remote-as "300" + } + system-as "65412" + } + } + table "3001" + vni "3001" + } +} + +// Warning: Do not remove the following line. +// vyos-config-version: "bgp@6:broadcast-relay@1:cluster@2:config-management@1:conntrack@6:conntrack-sync@2:container@2:dhcp-relay@2:dhcp-server@8:dhcpv6-server@1:dns-dynamic@4:dns-forwarding@4:firewall@15:flow-accounting@1:https@6:ids@1:interfaces@32:ipoe-server@3:ipsec@13:isis@3:l2tp@9:lldp@2:mdns@1:monitoring@1:nat@8:nat66@3:ntp@3:openconnect@3:ospf@2:pim@1:policy@8:pppoe-server@10:pptp@5:qos@2:quagga@11:reverse-proxy@1:rip@1:rpki@2:salt@1:snmp@3:ssh@2:sstp@6:system@27:vrf@3:vrrp@4:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2" +// Release version: 1.4.3 diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 43bf00238b..630e267092 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -230,7 +230,7 @@ def tearDown(self): def create_bgp_instances_for_import_test(self): table = '1000' self.cli_set(import_vrf_base + [import_vrf, 'table', table]) - self.cli_set(import_vrf_base + [import_vrf, 'protocols', 'bgp', 'system-as', ASN]) + self.cli_set(import_vrf_base + [import_vrf, 'protocols', 'bgp']) def verify_frr_config(self, peer, peer_config, frrconfig): # recurring patterns to verify for both a simple neighbor and a peer-group @@ -1050,7 +1050,6 @@ def test_bgp_10_vrf_simple(self): for vrf in vrfs: vrf_base = ['vrf', 'name', vrf] self.cli_set(vrf_base + ['table', table]) - self.cli_set(vrf_base + ['protocols', 'bgp', 'system-as', ASN]) self.cli_set(vrf_base + ['protocols', 'bgp', 'parameters', 'router-id', router_id]) table = str(int(table) + 1000) @@ -1192,31 +1191,7 @@ def test_bgp_14_remote_as_peer_group_override(self): self.assertIn(f' neighbor {peer_group} peer-group', frrconfig) self.assertIn(f' neighbor {peer_group} remote-as {remote_asn}', frrconfig) - def test_bgp_15_local_as_ebgp(self): - # https://vyos.dev/T4560 - # local-as allowed only for ebgp peers - - neighbor = '192.0.2.99' - remote_asn = '500' - local_asn = '400' - - self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', ASN]) - self.cli_set(base_path + ['neighbor', neighbor, 'local-as', local_asn]) - - # check validate() - local-as allowed only for ebgp peers - with self.assertRaises(ConfigSessionError): - self.cli_commit() - - self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', remote_asn]) - - self.cli_commit() - - frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit') - self.assertIn(f'router bgp {ASN}', frrconfig) - self.assertIn(f' neighbor {neighbor} remote-as {remote_asn}', frrconfig) - self.assertIn(f' neighbor {neighbor} local-as {local_asn}', frrconfig) - - def test_bgp_16_import_rd_rt_compatibility(self): + def test_bgp_15_import_rd_rt_compatibility(self): # Verify if import vrf and rd vpn export # exist in the same address family self.create_bgp_instances_for_import_test() @@ -1229,7 +1204,7 @@ def test_bgp_16_import_rd_rt_compatibility(self): with self.assertRaises(ConfigSessionError): self.cli_commit() - def test_bgp_17_import_rd_rt_compatibility(self): + def test_bgp_16_import_rd_rt_compatibility(self): # Verify if vrf that is in import vrf list contains rd vpn export self.create_bgp_instances_for_import_test() self.cli_set( @@ -1252,7 +1227,7 @@ def test_bgp_17_import_rd_rt_compatibility(self): with self.assertRaises(ConfigSessionError): self.cli_commit() - def test_bgp_18_deleting_import_vrf(self): + def test_bgp_17_deleting_import_vrf(self): # Verify deleting vrf that is in import vrf list self.create_bgp_instances_for_import_test() self.cli_set( @@ -1269,7 +1244,7 @@ def test_bgp_18_deleting_import_vrf(self): with self.assertRaises(ConfigSessionError): self.cli_commit() - def test_bgp_19_deleting_default_vrf(self): + def test_bgp_18_deleting_default_vrf(self): # Verify deleting existent vrf default if other vrfs were created self.create_bgp_instances_for_import_test() self.cli_commit() @@ -1281,7 +1256,7 @@ def test_bgp_19_deleting_default_vrf(self): with self.assertRaises(ConfigSessionError): self.cli_commit() - def test_bgp_20_import_rd_rt_compatibility(self): + def test_bgp_19_import_rd_rt_compatibility(self): # Verify if vrf that has rd vpn export is in import vrf of other vrfs self.create_bgp_instances_for_import_test() self.cli_set( @@ -1303,7 +1278,7 @@ def test_bgp_20_import_rd_rt_compatibility(self): with self.assertRaises(ConfigSessionError): self.cli_commit() - def test_bgp_21_import_unspecified_vrf(self): + def test_bgp_20_import_unspecified_vrf(self): # Verify if vrf that is in import is unspecified self.create_bgp_instances_for_import_test() self.cli_set( @@ -1312,7 +1287,7 @@ def test_bgp_21_import_unspecified_vrf(self): with self.assertRaises(ConfigSessionError): self.cli_commit() - def test_bgp_22_interface_mpls_forwarding(self): + def test_bgp_21_interface_mpls_forwarding(self): interfaces = Section.interfaces('ethernet', vlan=False) for interface in interfaces: self.cli_set(base_path + ['interface', interface, 'mpls', 'forwarding']) @@ -1324,7 +1299,7 @@ def test_bgp_22_interface_mpls_forwarding(self): self.assertIn(f'interface {interface}', frrconfig) self.assertIn(f' mpls bgp forwarding', frrconfig) - def test_bgp_23_vrf_interface_mpls_forwarding(self): + def test_bgp_22_vrf_interface_mpls_forwarding(self): self.create_bgp_instances_for_import_test() interfaces = Section.interfaces('ethernet', vlan=False) for interface in interfaces: @@ -1339,7 +1314,7 @@ def test_bgp_23_vrf_interface_mpls_forwarding(self): self.assertIn(f' mpls bgp forwarding', frrconfig) self.cli_delete(['interfaces', 'ethernet', interface, 'vrf']) - def test_bgp_24_srv6_sid(self): + def test_bgp_23_srv6_sid(self): locator_name = 'VyOS_foo' sid = 'auto' nexthop_ipv4 = '192.0.0.1' @@ -1386,7 +1361,7 @@ def test_bgp_24_srv6_sid(self): self.assertIn(f' sid vpn export {sid}', afiv6_config) self.assertIn(f' nexthop vpn export {nexthop_ipv6}', afiv6_config) - def test_bgp_25_ipv4_labeled_unicast_peer_group(self): + def test_bgp_24_ipv4_labeled_unicast_peer_group(self): pg_ipv4 = 'foo4' ipv4_max_prefix = '20' ipv4_prefix = '192.0.2.0/24' @@ -1411,7 +1386,7 @@ def test_bgp_25_ipv4_labeled_unicast_peer_group(self): self.assertIn(f' neighbor {pg_ipv4} activate', afiv4_config) self.assertIn(f' neighbor {pg_ipv4} maximum-prefix {ipv4_max_prefix}', afiv4_config) - def test_bgp_26_ipv6_labeled_unicast_peer_group(self): + def test_bgp_25_ipv6_labeled_unicast_peer_group(self): pg_ipv6 = 'foo6' ipv6_max_prefix = '200' ipv6_prefix = '2001:db8:1000::/64' @@ -1437,7 +1412,7 @@ def test_bgp_26_ipv6_labeled_unicast_peer_group(self): self.assertIn(f' neighbor {pg_ipv6} activate', afiv6_config) self.assertIn(f' neighbor {pg_ipv6} maximum-prefix {ipv6_max_prefix}', afiv6_config) - def test_bgp_27_route_reflector_client(self): + def test_bgp_26_route_reflector_client(self): int_neighbors = ['192.0.2.2', '192.0.2.3', '192.0.2.4', '192.0.2.5'] int_interfaces = ['dum0', 'dum1', 'dum2', 'dum3'] int_pg_names = ['SMOKETESTINT0', 'SMOKETESTINT1', 'SMOKETESTINT2'] @@ -1472,7 +1447,7 @@ def _set_neighbor_3(neighbor, remote_as_type): interface_cmd = ['interface'] if neighbor.startswith('dum') else [] self.cli_set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'route-reflector-client']) self.cli_set(base_path + ['neighbor', neighbor] + interface_cmd + ['remote-as', remote_as_type]) - + set_neighbor_funcs = [_set_neighbor_0, _set_neighbor_1, _set_neighbor_2, _set_neighbor_3] for remote_as_type in remote_as_types: for func_count, set_neighbor_func in enumerate(set_neighbor_funcs): @@ -1484,7 +1459,7 @@ def _set_neighbor_3(neighbor, remote_as_type): self.cli_discard() else: self.cli_commit() - + frrconfig = self.getFRRconfig(f'router bgp {ASN}', endsection='^exit', substring=' address-family ipv4 unicast', endsubsection='^ exit-address-family') neighbor_has_rr_client = [ int_neighbors[0], int_neighbors[3], @@ -1497,7 +1472,7 @@ def _set_neighbor_3(neighbor, remote_as_type): self.cli_delete(['interfaces', 'dummy']) self.cli_commit() - def test_bgp_28_peer_group_member_all_internal_or_external(self): + def test_bgp_27_peer_group_member_all_internal_or_external(self): def _common_config_check(conf, include_ras=True): if include_ras: self.assertIn(f'neighbor {int_neighbors[0]} remote-as {ASN}', conf) @@ -1589,13 +1564,12 @@ def test_bgp_29_peer_group_remote_as_equal_local_as(self): self.assertIn(f'neighbor OVERLAY remote-as {int(ASN) + 1}', conf) self.assertIn(f'neighbor OVERLAY local-as {int(ASN) + 1}', conf) - def test_bgp_30_import_vrf_routemap(self): + def test_bgp_30_import_vrf_route_map(self): router_id = '127.0.0.3' table = '1000' vrf = 'red' vrf_base = ['vrf', 'name', vrf] self.cli_set(vrf_base + ['table', table]) - self.cli_set(vrf_base + ['protocols', 'bgp', 'system-as', ASN]) self.cli_set( vrf_base + ['protocols', 'bgp', 'parameters', 'router-id', router_id]) @@ -1682,4 +1656,4 @@ def test_bgp_99_bmp(self): self.assertIn(f'bmp connect {target_address} port {target_port} min-retry {min_retry} max-retry {max_retry}', frrconfig) if __name__ == '__main__': - unittest.main(verbosity=2) + unittest.main(verbosity=2, failfast=True) diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index fe5740a185..ab0e7e44a4 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -18,6 +18,7 @@ from sys import argv from vyos.base import Warning +from vyos.base import DeprecationWarning from vyos.config import Config from vyos.configverify import has_frr_protocol_in_dict from vyos.configverify import verify_prefix_list @@ -210,8 +211,27 @@ def verify(config_dict): return None - if 'system_as' not in bgp: - raise ConfigError('BGP system-as number must be defined!') + ERR_MSG_GLOBAL_VRF_AS_MISSING = 'BGP "system-as" number must be defined! Use "set protocols ' \ + 'bgp system-as " to define a global BGP instance AS number.' + system_as = None + if vrf: + system_as = dict_search('dependent_vrfs.default.protocols.bgp.system_as', bgp) + if not system_as: + raise ConfigError(ERR_MSG_GLOBAL_VRF_AS_MISSING) + + if 'system_as' in bgp: + tmp_as = bgp['system_as'] + DeprecationWarning(f'CLI command "vrf name {vrf} protocols bgp system-as ' \ + f'{tmp_as}" is ignored and will be removed in VyOS 1.5! ' \ + f'\n\nGlobal "protocols bgp system-as {system_as}" option ' \ + 'applies, use per neighbor "local-as" option to override.') + + elif 'system_as' not in bgp: + raise ConfigError(ERR_MSG_GLOBAL_VRF_AS_MISSING) + + # Cache global defined system AS number used in further checks + if not system_as: + system_as = bgp['system_as'] # Verify BMP if 'bmp' in bgp: @@ -257,7 +277,7 @@ def verify(config_dict): if 'remote_as' in peer_config: is_ibgp = True if peer_config['remote_as'] != 'internal' and \ - peer_config['remote_as'] != bgp['system_as']: + peer_config['remote_as'] != system_as: is_ibgp = False if peer_group not in peer_groups_context: @@ -278,7 +298,7 @@ def verify(config_dict): # Neighbor local-as override can not be the same as the local-as # we use for this BGP instane! asn = list(peer_config['local_as'].keys())[0] - if asn == bgp['system_as']: + if asn == system_as: raise ConfigError('Cannot have local-as same as system-as number') # Neighbor AS specified for local-as and remote-as can not be the same @@ -369,12 +389,6 @@ def verify(config_dict): if 'source_interface' in peer_config['interface']: raise ConfigError(f'"source-interface" option not allowed for neighbor "{peer}"') - # Local-AS allowed only for EBGP peers - if 'local_as' in peer_config: - remote_as = verify_remote_as(peer_config, bgp) - if remote_as == bgp['system_as']: - raise ConfigError(f'local-as configured for "{peer}", allowed only for eBGP peers!') - for afi in ['ipv4_unicast', 'ipv4_multicast', 'ipv4_labeled_unicast', 'ipv4_flowspec', 'ipv6_unicast', 'ipv6_multicast', 'ipv6_labeled_unicast', 'ipv6_flowspec', 'l2vpn_evpn']: diff --git a/src/migration-scripts/bgp/6-to-7 b/src/migration-scripts/bgp/6-to-7 new file mode 100644 index 0000000000..7a11bbf3a5 --- /dev/null +++ b/src/migration-scripts/bgp/6-to-7 @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# +# Copyright VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# T7760: Remove per VRF setting for system-as option and replace it with +# local-as if required. + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + vrf_base = ['vrf', 'name'] + bgp_base = ['protocols', 'bgp'] + + if not config.exists(vrf_base): + return + + global_asn = None + if config.exists(bgp_base + ['system-as']): + global_asn = config.return_value(bgp_base + ['system-as']) + + for vrf in config.list_nodes(vrf_base): + # bail out early if there is no per VRF BGP instance defined + vrf_bgp_base = vrf_base + [vrf] + bgp_base + if not config.exists(vrf_bgp_base): + continue + + # This is a mandatory node in the old design but keep it optional + # if one want's to load a weird config + if config.exists(vrf_bgp_base + ['system-as']): + system_as = config.return_value(vrf_bgp_base + ['system-as']) + config.delete(vrf_bgp_base + ['system-as']) + + # If there is no existing global BGP instance - start one + if not config.exists(bgp_base): + config.set(bgp_base + ['system-as'], value=system_as) + global_asn = system_as + + vrf_neighbor_base = vrf_bgp_base + ['neighbor'] + vrf_peer_group_base = vrf_bgp_base + ['peer-group'] + if config.exists(vrf_neighbor_base): + for neighbor in config.list_nodes(vrf_neighbor_base): + # Neighbor already has local-as option set, do not touch it + if config.exists(vrf_neighbor_base + [neighbor, 'local-as']): + #print(f'VRF {vrf} BGP neighbor {neighbor} has local-as set - do not migrate!') + continue + + # Check if the neighbor uses a peer-group which has the local-as + # option set, do not touch it either + peer_group = None + if config.exists(vrf_neighbor_base + [neighbor, 'peer-group']): + peer_group = config.return_value(vrf_neighbor_base + [neighbor, 'peer-group']) + # Check if the peer-group has a local-as option set + if config.exists(vrf_peer_group_base + [peer_group, 'local-as']): + #print(f'VRF {vrf} BGP neighbor {neighbor} uses peer-group {peer_group} which has local-as set - do not migrate!') + continue + + # BGP local-as option is only allowed for eBGP speakers + if global_asn == system_as: + continue + + config.set(vrf_neighbor_base + [neighbor, 'local-as', system_as, 'no-prepend', 'replace-as']) + config.set_tag(vrf_neighbor_base + [neighbor, 'local-as']) + + # We do also need to take care about BGP internas. When using local-as routes with our own AS + # previously in the path will get rejected: + # bgpd: x.x.x.x(Unknown) rcvd UPDATE about 192.0.2.0/24 IPv4 unicast -- DENIED due to: as-path contains our own AS; + # Set allowas-in option + allowas_in_numer = ['allowas-in', 'number'] + for afi in ['ipv4-labeled-unicast', 'ipv4-multicast', 'ipv4-unicast', 'ipv4-vpn', + 'ipv6-labeled-unicast', 'ipv6-multicast', 'ipv6-unicast', 'ipv6-vpn']: + afi_neighbor_base = vrf_neighbor_base + [neighbor, 'address-family', afi] + afi_peer_group_base = vrf_peer_group_base + [peer_group, 'address-family', afi] + + # No need to change anything on an AFI not in use + if not config.exists(afi_neighbor_base) and not config.exists(afi_peer_group_base): + continue + + allowas_in_value = 0 + print(neighbor, afi) + # Check if there is any allowas-in definition for a peer-group + if peer_group and config.exists(afi_peer_group_base + allowas_in_numer): + allowas_in_value = int(config.return_value(afi_peer_group_base + allowas_in_numer)) + #print(f'peer-group {peer_group} allowas-in for {afi} is {allowas_in_value}') + + # Per neighbor "allowas-in" definition takes higher precendence + if config.exists(afi_neighbor_base + allowas_in_numer): + allowas_in_value = int(config.return_value(afi_neighbor_base + allowas_in_numer)) + #print(f'neighbor {neighbor} allowas-in for {afi} is {allowas_in_value}') + + # Increment allowas-in by 1 as we now have one more entry + allowas_in_value += 1 + # Clip allowas-in to 10 - max supported by FRR platform + if allowas_in_value > 10: allowas_in_value = 10 + + # Set per neighbor allowas-in which always takes precedence over the peer-group definition + config.set(afi_neighbor_base + allowas_in_numer, value=allowas_in_value, replace=True) From 85fe32f0e1a91a47fe4a6d4a5cdd6ac516dcc3b9 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Tue, 9 Sep 2025 17:57:44 +0200 Subject: [PATCH 2/2] bgp: T7760: remove per vrf instance system-as node VyOS 1.5 and onwards will no longer have the following CLI node available: set vrf name protocols bgp system-as --- .../include/version/bgp-version.xml.i | 2 +- interface-definitions/vrf.xml.in | 12 -------- src/conf_mode/protocols_bgp.py | 8 ----- src/migration-scripts/bgp/7-to-8 | 30 +++++++++++++++++++ 4 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 src/migration-scripts/bgp/7-to-8 diff --git a/interface-definitions/include/version/bgp-version.xml.i b/interface-definitions/include/version/bgp-version.xml.i index 21fddf9ae0..a283b9dd2c 100644 --- a/interface-definitions/include/version/bgp-version.xml.i +++ b/interface-definitions/include/version/bgp-version.xml.i @@ -1,3 +1,3 @@ - + diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index 0d1033e252..3fa95076ee 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -57,18 +57,6 @@ #include - - - Autonomous System Number (ASN) - DEPRECATED - - u32:1-4294967294 - Autonomous System Number - - - - - - diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index ab0e7e44a4..4e7f09d0e8 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -18,7 +18,6 @@ from sys import argv from vyos.base import Warning -from vyos.base import DeprecationWarning from vyos.config import Config from vyos.configverify import has_frr_protocol_in_dict from vyos.configverify import verify_prefix_list @@ -219,13 +218,6 @@ def verify(config_dict): if not system_as: raise ConfigError(ERR_MSG_GLOBAL_VRF_AS_MISSING) - if 'system_as' in bgp: - tmp_as = bgp['system_as'] - DeprecationWarning(f'CLI command "vrf name {vrf} protocols bgp system-as ' \ - f'{tmp_as}" is ignored and will be removed in VyOS 1.5! ' \ - f'\n\nGlobal "protocols bgp system-as {system_as}" option ' \ - 'applies, use per neighbor "local-as" option to override.') - elif 'system_as' not in bgp: raise ConfigError(ERR_MSG_GLOBAL_VRF_AS_MISSING) diff --git a/src/migration-scripts/bgp/7-to-8 b/src/migration-scripts/bgp/7-to-8 new file mode 100644 index 0000000000..ced4f837e5 --- /dev/null +++ b/src/migration-scripts/bgp/7-to-8 @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# +# Copyright VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# T7760: Remove per VRF setting for system-as option in VyOS 1.5 and onwards + +from vyos.configtree import ConfigTree + +def migrate(config: ConfigTree) -> None: + vrf_base = ['vrf', 'name'] + if not config.exists(vrf_base): + return + + for vrf in config.list_nodes(vrf_base): + # bail out early if there is no per VRF BGP instance defined + vrf_bgp_base = vrf_base + [vrf, 'protocols', 'bgp'] + if config.exists(vrf_bgp_base + ['system-as']): + config.delete(vrf_bgp_base + ['system-as'])