diff --git a/docs/addressing.md b/docs/addressing.md
index 1316afed1..93f859c41 100644
--- a/docs/addressing.md
+++ b/docs/addressing.md
@@ -98,6 +98,7 @@ addressing:
* Management IP addresses are assigned from 192.168.121.0/24 CIDR block. The first IP address is 192.168.121.101 (*start* offset plus node ID)
* MAC addresses of management interfaces start with 08-4F-A9. The last byte of the MAC address is the node ID.
+(addressing-unnumbered)=
## Unnumbered Interface Support
*netlab* supports unnumbered IPv4 and IPv6 interfaces:
diff --git a/docs/caveats.md b/docs/caveats.md
index a95e96f66..6ed563fed 100644
--- a/docs/caveats.md
+++ b/docs/caveats.md
@@ -18,6 +18,28 @@
* *netlab* uses Cumulus VX containers created by Michael Kashin and downloaded from his Docker Hub account. Once Nvidia releases an official container image, change the container name with **defaults.devices.cumulus.clab.image** parameter (or by editing the `topology-defaults.yml` file included with *netlab*).
* The Cumulus VX 4.4.0 Vagrant box for VirtualBox is broken. *netlab* is using Cumulus VX 4.3.0 with *virtualbox* virtualization provider.
+(caveats-cumulus-nvue)=
+
+## Cumulus 5.0 with NVUE
+
+You could configure Cumulus Linux 5.0 with configuration templates developed for Cumulus Linux 4.0 (use device type **cumulus** and specify desired device image), or with NVUE.
+
+NVUE has several shortcomings that prevent *netlab* from configuring basic designs like IBGP on top of IGP. Don't be surprised if the labs that work with **cumulus** device don't work with **cumulus_nvue** device, and please create a GitHub issue whenever you find a glitch. We'd love to know (at least) what doesn't work as expected.
+
+To run Cumulus Linux 5.x with **cumulus** device type, add the following lines to your lab topology:
+
+```
+defaults.devices.cumulus.libvirt.image: CumulusCommunity/cumulus-vx:5.2.0
+defaults.devices.cumulus.libvirt.memory: 2048
+```
+
+Alternatively, you could add the following lines to your `~/.topology-defaults.yml` file:
+
+```
+devices.cumulus.libvirt.image: CumulusCommunity/cumulus-vx:5.2.0
+devices.cumulus.libvirt.memory: 2048
+```
+
## Fortinet FortiOS
* *FortiOS* VM images by default have a 15 day evaluation license. The VM has [limited capabilities](https://docs.fortinet.com/document/fortigate-private-cloud/6.0.0/fortigate-vm-on-kvm/504166/fortigate-vm-virtual-appliance-evaluation-license) without a license file. It will work for 15 days from first boot, at which point you must install a license file or recreate the vagrant box completely from scratch.
diff --git a/docs/dev/config/vlan.md b/docs/dev/config/vlan.md
index 8af8a18fc..da327c0f8 100644
--- a/docs/dev/config/vlan.md
+++ b/docs/dev/config/vlan.md
@@ -73,7 +73,7 @@ devices:
features:
vlan:
model: router
- svi_interface_name: "{ifname}.{vlan}"
+ svi_interface_name: "irb0.{vlan}"
subif_name: "{ifname}.{subif_index}"
vyos:
features:
diff --git a/docs/dev/guidelines.md b/docs/dev/guidelines.md
index 8d8970ccd..c8f3d5bbc 100644
--- a/docs/dev/guidelines.md
+++ b/docs/dev/guidelines.md
@@ -20,6 +20,7 @@ The easiest way to get started is to [add support for a new platform for an exis
device-box.md
device-features.md
devices.md
+ unnumbered.md
```
```eval_rst
diff --git a/docs/dev/unnumbered.md b/docs/dev/unnumbered.md
new file mode 100644
index 000000000..ab7023383
--- /dev/null
+++ b/docs/dev/unnumbered.md
@@ -0,0 +1,80 @@
+# Unnumbered Interfaces
+
+*netlab* supports unnumbered IPv4, IPv6 (LLA) or dual-stack interfaces. There are two ways to create an unnumbered interface (or link):
+
+* Set **unnumbered: true** on interface[^NLA] or link.
+* Set **ipv4** and/or **ipv6** attribute to *True*.
+
+**unnumbered** attribute is translated into **ipv4** and/or **ipv6** attributes set to *True* based on address families configured on node's loopback interface. With the default addressing setup, **unnumbered: True** results in **ipv4: True**, to enable dual-stack unnumbered interfaces with **unnumbered** attribute, add **ipv6** prefix to the **loopback** addressing pool. For more details, see [unnumbered interfaces](addressing-unnumbered) part of [addressing](../addressing.md) document.
+
+## Implementing unnumbered interfaces
+
+There is no standard way of implementing IPv4 unnumbered interfaces, and they might not be available on all platforms. IPv4 implementations of unnumbered interfaces usually use the loopback IPv4 address as the interface IPv4 address. For more details, read the [unnumbered interfaces](https://blog.ipspace.net/series/unnumbered-interfaces.html) series of blog posts on ipSpace.net.
+
+Unnumbered IPv6 interfaces should use link-local addresses, a standard IPv6 feature.
+
+If your device supports IPv6 LLA-only interface, set `topology-defaults.yml` attribute **devices._name_.features.initial.ipv6.lla** to *True*.
+
+If your device supports IPv4 unnumbered interfaces, set `topology-defaults.yml` attribute **devices._name_.features.initial.ipv4.unnumbered** to *True*.
+
+## Integration with IGP routing protocols
+
+OSPF and IS-IS implementations might support unnumbered IPv4 interfaces[^OSPFv3]. The routing protocol configuration modules detect unnumbered IPv4 interfaces by checking the **unnumbered** and **ipv4** attributes -- if either one of them is set to *True*, the interface is an unnumbered IPv4 interface.
+
+OSPFv2 can use unnumbered IPv4 interfaces on point-to-point links. If your device supports this functionality, set `topology-defaults.yml` attribute **devices._name_.features.ospf.unnumbered** to *True*. OSPFv2 cannot run over multi-access unnumbered IPv4 links.
+
+Some IS-IS implementations support unnumbered IPv4 P2P links. If your device supports this, set `topology-defaults.yml` attribute **devices._name_.features.isis.unnumbered.ipv4** to *True*.
+
+Fewer IS-IS implementations support unnumbered multi-access IPv4 links. To indicate your device can do that, set `topology-defaults.yml` attribute **devices._name_.features.isis.unnumbered.network** to *True*.
+
+```{note}
+If you're unsure what your device can do, set all three feature flags to *True*, start a lab, and check whether the adjacency- and routing tables are populated as expected.
+```
+
+## Unnumbered EBGP sessions
+
+Several vendors implemented EBGP sessions between well-known IPv6 LLA addresses[^EBGP_LLA]. *netlab* does not support this half-baked attempt and implements IPv6 LLA sessions only for those devices that can configure EBGP session *on an interface*.
+
+Devices supporting interface-level EBGP sessions between auto-generated IPv6 LLA can use these sessions to:
+
+* Transport IPv6 prefixes with LLA next hop over IPv6 AF
+* Transport IPv4 prefixes with IPv6 LLA next hop according to RFC 8950.
+
+*netlab* core and BGP configuration module do not support:
+
+* Running IPv4 AF with IPv4 next hops over IPv6 transport session
+* Running IPv4 AF with RFC 8950-style IPv6 next hops over numbered IPv6 interfaces or over IBGP sessions.
+* Creating IBGP sessions between IPv6 LLA addresses
+
+```{note}
+You can always extend *netlab* functionality with plugins and custom configuration modules.
+```
+
+*netlab* will create an IPv6 LLA EBGP session whenever it finds a pair of devices connected to the same link if the devices:
+
+* Belong to different autonomous systems
+* Have **ipv6** interface attribute set to *True*.
+
+Whenever *netlab* encounters an EBGP session between IPv6 LLA interfaces, it sets **local_if** attribute in the neighbor data structure to simplify the device configuration templates.
+
+If your device supports EBGP sessions between auto-generated IPv6 link-local addresses, set `topology-defaults.yml` attribute **devices._name_.features.bgp.ipv6_lla** to *True*.
+
+*netlab* device configuration templates will enable RFC 8950-style IPv4-over-IPv6 address family on IPv6 LLA sessions if the interface has **ipv6** interface attribute set to *True* (indicating IPv6 LLA EBGP session) AND **ipv4** interface attribute set to *True*.
+
+RFC 8950-style IPv4 address family [REALLY SHOULD NOT](https://www.rfc-editor.org/rfc/rfc6919#section-3) be enabled for:
+
+* EBGP sessions running on numbered IPv6 interfaces
+* Interfaces with IPv4 addresses regardless of the state of **ipv6** attribute.
+
+BGP configuration module simplifies the device configuration templates conforming to the above restriction with the **ipv4_rfc8950** neighbor attribute which is set:
+
+* when link or interface **unnumbered** attribute is set to *True* on both EBGP neighbors or
+* when both IPv4 and IPv6 interface attributes are set to *True* on both EBGP neighbors.
+
+If your device supports RFC 8950 (IPv4 with IPv6 next hops) on EBGP sessions between auto-generated IPv6 link-local addresses, set `topology-defaults.yml` attribute **devices._name_.features.bgp.rfc8950** to *True*.
+
+[^NLA]: node-to-link attachment
+
+[^OSPFv3]: OSPFv3 runs over IPv6 LLA. Decent IS-IS implementations should support IPv6 LLA-only segments. *netlab* therefore does not check whether an implementation supports IPv6 LLA-only segments.
+
+[^EBGP_LLA]: An EBGP neighbor has to be configured using remote IPv6 LLA address and an interface name.
diff --git a/docs/module/bgp.md b/docs/module/bgp.md
index ce0d8a38f..fa3d79c3e 100644
--- a/docs/module/bgp.md
+++ b/docs/module/bgp.md
@@ -5,6 +5,7 @@ This configuration module configures BGP routing process and BGP neighbors on mo
* EBGP sessions are established between directly-connected IP addresses on every link where the connected routers belong to different autonomous systems. Parallel sessions are established for all address families (IPv4, IPv6) configured on the link.
* IBGP sessions are established between loopback interfaces of routers in the same autonomous system. Parallel sessions are established for all address families configured on the loopback interfaces.
* IGBP sessions could form a full mesh (when no router reflectors are configured in the autonomous system) or a hubs-and-spokes topology with a single route reflector cluster and a full mesh of IBGP sessions between route reflectors.
+* Sessions (IBGP or EBGP) between directly-connected IP addresses are established whenever the real AS or the local AS of the devices differ, allowing you to build scenarios like IBGP-over-EBGP (EVPN design) or IBGP mesh across multiple autonomous systems (ISP migration scenario).
More interesting BGP topologies can be created with [custom plugins](../plugins.md).
@@ -18,25 +19,56 @@ More interesting BGP topologies can be created with [custom plugins](../plugins.
## Supported BGP Features
* Multiple autonomous systems
+* IPv4 and IPv6 address families
* Direct (single-hop) EBGP sessions
* IBGP sessions between loopback interfaces
+* EBGP sessions between auto-generated IPv6 link-local addresses
+* RFC8950-style IPv4 address family on EBGP IPv6 LLA sessions
* BGP route reflectors
* Next-hop-self control on IBGP sessions
* BGP community propagation
-* IPv4 and IPv6 address families
+* Configurable activation of default address families
* Configurable link prefix advertisement
* Additional (dummy) prefix advertisement
+* Changing local autonomous system for individual BGP sessions (*local-as*)
* Static **router-id** and **cluster-id**
* Interaction with OSPF or IS-IS (IGP is disabled on external links)
-You could use *global* or *per-node* parameters to configure BGP autonomous systems and route reflectors (you expected tons of nerd knobs in a BGP implementation, didn't you?):
+## Platform Support
+
+[Platforms supporting BGP configuration module](platform-routing-support) support most of the functionality mentioned above with the following exceptions:
+
+* Cumulus Linux 5.2.0 using NVUE cannot configure IBGP sessions between loopback interfaces. Please [see caveats for more details](caveats-cumulus-nvue).
+
+The following features are only supported on a subset of platforms:
+
+| Operating system | IPv6 LLA
EBGP sessions | Unnumbered IPv4
EBGP sessions | EBGP
local AS | IBGP
local AS | Configurable
default AF |
+| --------------------- | :-: | :-: | :-: | :-: | :-: |
+| Arista EOS | ❌ | ❌ | ✅ | ✅ | ✅ |
+| Cisco IOS/IOS XE | ❌ | ❌ | ✅ | ✅ | ✅ |
+| Cumulus Linux 4.x | ✅ | ✅ | ❌ | ❌ | ✅ |
+| Cumulus Linux 5.x | ✅ | ✅ | ❌ | ❌ | ✅ |
+| Dell OS10 | ❌ | ❌ | ✅ | ❌ | ❌ |
+| FRR 7.5.0 | ✅ | ❌ | ✅ | ✅ | ✅ |
+| Nokia SR Linux | ✅ | ❌ | ✅ | ✅ | ✅ |
-* Using a global **as_list**, specify members and route reflectors in an autonomous system.
-* Specify BGP AS and route reflector status of individual nodes with **bgp.as** and **bgp.rr** node parameters.
## Global BGP Configuration Parameters
-Use **bgp.as_list** global parameter to specify a dictionary of autonomous systems. Every autonomous system should have two elements:
+You could use *global* or *per-node* parameters to configure BGP autonomous systems and route reflectors:
+
+* Specify BGP AS and route reflector status of individual nodes with **bgp.as** and **bgp.rr** node parameters.
+* Using a global **as_list**, specify members and route reflectors in multiple autonomous systems in your lab.
+
+The simplest way to build a network with a single BGP autonomous system is to specify the BGP AS number in global **bgp.as** parameter and the list of route reflectors in the global **bgp.rr_list** parameter (See [IBGP-over-OSPF Data Center Fabric example](bgp_example/ibgp.md) for details):
+
+```
+bgp:
+ as: 65000
+ rr_list: [ s1, s2 ]
+```
+
+When building a more complex lab with multiple autonomous systems, you might want to use **bgp.as_list** global parameter to specify a dictionary of autonomous systems (although you could still set BGP AS numbers and RR status with node attributes). Every autonomous system in the **bgp.as_list** should have two elements:
* **members** -- list of nodes within the autonomous system
* **rr** -- list of route reflectors within the autonomous system.
@@ -55,24 +87,14 @@ bgp:
members: [ e2 ]
```
-When you're building a network with a single BGP autonomous system, it might be simpler to specify the default value of BGP AS number in **bgp.as** parameter instead of listing all nodes within that autonomous system. When using this approach, specify the list of route reflectors in **bgp.rr_list** parameter.
-
-```
-bgp:
- as: 65000
- rr_list: [ s1, s2 ]
-```
-
-See [IBGP-over-OSPF Data Center Fabric example](bgp_example/ibgp.md) for details.
-
## Advanced Global Configuration Parameters
Advanced global configuration parameters include:
-* **community** -- configure BGP community propagation. By defaults, standard and extended communities are propagated to IBGP neighbors, and standard communities are propagated to EBGP neighbors. See *[BGP Community Propagation](#bgp-communities-propagation)* for more details.
-* **advertise_roles** -- list of link types and roles. Links matching any element of the list will be advertised into BGP. See *[Advertised BGP Prefixes](#advertised-bgp-prefixes)* for details.
-* **ebgp_role** -- link role set on links connecting nodes from different autonomous systems. See *[Interaction with IGP](#interaction-with-igp)* for details.
-* **advertise_loopback** -- when set to `True` (default), the loopback IP address is advertised as a BGP prefix. Set it to `False` in global defaults or as a node setting to disable loopback prefix advertisements.
+* **bgp.community** -- configure BGP community propagation. By defaults, standard and extended communities are propagated to IBGP neighbors, and standard communities are propagated to EBGP neighbors. See *[BGP Community Propagation](#bgp-communities-propagation)* for more details.
+* **bgp.advertise_roles** -- list of link types and roles. Links matching any element of the list will be advertised into BGP. See *[Advertised BGP Prefixes](#advertised-bgp-prefixes)* for details.
+* **bgp.ebgp_role** -- link role set on links connecting nodes from different autonomous systems. See *[Interaction with IGP](#interaction-with-igp)* for details.
+* **bgp.advertise_loopback** -- when set to `True` (default), the loopback IP address is advertised as a BGP prefix. Set it to `False` in global defaults or as a node setting to disable loopback prefix advertisements.
## Node Configuration Parameters
@@ -80,22 +102,34 @@ Instead of using a global list of autonomous systems, you could specify a BGP au
* **bgp.as**: AS number -- specified on a node, or as default global value (propagated to all nodes without a specified AS number)
* **bgp.rr** -- the node is BGP route reflector within its autonomous system.
-* **bgp.next_hop_self** -- use *next-hop-self* on IBGP sessions. This parameter can also be specified as a global value; system default is **true**.
-* **bgp.router_id** -- set static router ID. Default **router_id** is taken from the IPv4 address of the loopback interface or from the **router_id** address pool if there's no usable IPv4 address on the loopback interface.
-* **bgp.rr_cluster_id** -- set static route reflector cluster ID. The default value is the lowest router ID of all route reflectors within the autonomous system.
-Specifying a BGP autonomous system on individual nodes makes sense when each node uses a different BGP AS. See [EBGP leaf-and-spine fabric example](bgp_example/ebgp.md) for details.
+```{note}
+* **bgp.as** parameter *must* be specified for every node using BGP configuration module.
+* The node AS number could be derived from the global **bgp.as_list**, from the default (global) value of **bgp.as** parameter, or specified on the node itself. Explore [simple BGP example](bgp_example/simple.md) to see how you can combine global AS number with node AS number.
+* Specifying a BGP autonomous system on individual nodes makes sense when each node uses a different BGP AS. See [EBGP leaf-and-spine fabric example](bgp_example/ebgp.md) for details.
+```
Additional per-node BGP configuration parameters include:
* **bgp.advertise_loopback** -- when set to `False`, the loopback IP prefix is not advertised in BGP. See also [*Advanced Global Configuration Parameters*](#advanced-global-configuration-parameters).
-* **bgp.originate** -- a list of additional prefixes to advertise. The advertised prefixes are supported with a static route pointing to *Null0*.
* **bgp.community** -- override global BGP community propagation defaults for this node. See *[BGP Community Propagation](#bgp-communities-propagation)* for more details.
+* **bgp.local_as** -- the autonomous system to use on all EBGP sessions.
+* **bgp.next_hop_self** -- use *next-hop-self* on IBGP sessions. This parameter can also be specified as a global value; system default is **true**.
+* **bgp.originate** -- a list of additional prefixes to advertise. The advertised prefixes are supported with a static route pointing to *Null0*.
+* **bgp.router_id** -- set static router ID. Default **router_id** is taken from the IPv4 address of the loopback interface or from the **router_id** address pool if there's no usable IPv4 address on the loopback interface.
-**Notes:**
-* **bgp.as** parameter *must* be specified for every node using BGP configuration module.
-* The node AS number could be derived from the global **bgp.as_list**, from the default (global) value of **bgp.as** parameter, or specified on the node itself. Explore [simple BGP example](bgp_example/simple.md) to see how you can combine global AS number with node AS number.
-* You could enable BGP configuration module globally using `module: [ bgp ]` as a top-level topology element, or for an individual node using `module: [ bgp ]` within node data. See [Segment Routing with BGP topology](https://github.com/ipspace/netlab-examples/blob/master/routing/sr-mpls-bgp/sr%2Bbgp.yml) for an example.
+Finally, BGP configuration module supports these advanced node parameters that you probably shouldn't touch without a very good reason:
+
+* **bgp.rr_cluster_id** -- set static route reflector cluster ID. The default value is the lowest router ID of all route reflectors within the autonomous system.
+* **bgp.replace_global_as** (default: True) -- the default implementation of **neighbor local-as** command replaces the real autonomous system (**bgp.as**) with the *local* autonomous system. Set this parameter to *false* to disable that functionality and include both autonomous systems in the AS path[^RAS_P].
+* **bgp.sessions** (node or global parameter) -- specifies which transport sessions (IPv4 and/or IPv6) should be created for each BGP session type (IBGP, EBGP, or IBGP created through *local-as*)[^SESS_DM]. See *[bgp-sessions](https://github.com/ipspace/netlab/blob/dev/tests/topology/input/bgp-sessions.yml)* test case for an example.
+* **bgp.activate** (node or global parameter) -- specifies which default address families (IPv4 AF on IPv4 session, IPv6 on IPv6 session) should be created for each BGP session type (IBGP, EBGP, or IBGP created through *local-as*)[^ACT_CFG]. See *[leaf-spine](https://github.com/ipspace/netlab/blob/dev/tests/integration/bgp/local-as/leaf-spine.yml)* local AS test case for an example.
+
+[^SESS_DM]: This parameter influences the data structures built during the data transformation phase and is thus available on all platforms supporting BGP configuration module.
+
+[^ACT_CFG]: This parameter has to be supported by the device configuration templates and is thus not available on all platforms.
+
+[^RAS_P]: This functionality might not be configurable on all platforms. For example, Arista EOS supports only the **neighbor local-as no-prepend replace-as** command.
## Link-Level Parameters
@@ -105,6 +139,18 @@ You can also use these link-level parameters to influence the BGP prefix adverti
See [examples](#more-examples) for sample usage guidelines.
+## Interface-Level Parameters
+
+You can specify **bgp.local_as** for individual node-to-link attachments, for example:
+
+```
+links:
+- r1:
+ bgp.local_as: 65100
+ r2:
+ bgp.local_as: 65101
+```
+
## Advertised BGP Prefixes
The following IPv4/IPv6 prefixes are configured with **network** statements within the BGP routing process:
@@ -169,41 +215,31 @@ The BGP transformation module builds a list of BGP neighbors for ever node. That
* Router reflectors have IBGP sessions to all other nodes in the same AS. When the remote node is not a router reflector, *route-reflector-client* is configured on the IBGP session.
* Route reflector clients have IBGP sessions with route reflectors (nodes within the same AS with **bgp.rr** set).
* IBGP sessions are established between loopback interfaces. You should combine IBGP deployment with an IGP configuration module like [OSPF](ospf.md).
-* Parallel IBGP sessions are established for all IP address families configured on loopback interfaces. See also [IPv6 support](#ipv6-support).
+* Parallel IBGP sessions are established for all IP address families configured on loopback interfaces[^BSESS]. See also [IPv6 support](#ipv6-support).
+
+[^BSESS]: If allowed by the **bgp.sessions** parameter
See the [IBGP Data Center Fabric](bgp_example/ibgp.md) example for more details.
**EBGP sessions**
* Whenever multiple nodes connected to the same link use different AS numbers, you'll get a full mesh of EBGP sessions between them.
-* Parallel EBGP sessions are established for all IP address families configured on the link. See also [IPv6 support](#ipv6-support).
+* Global (**bgp.as**) and local (**bgp.local_as**) autonomous systems are considered when deciding to create a session between two adjacent nodes, allowing you to create EBGP sessions between nodes belonging to the same AS, or IBGP sessions between nodes belonging to different AS.
+* Parallel EBGP sessions are established for all IP address families configured on the link[^BSESS]. See also [IPv6 support](#ipv6-support).
See the [Simple BGP Example](bgp_example/simple.md) and [EBGP Data Center Fabric](bgp_example/ebgp.md) example for more details.
### Notes on Unnumbered EBGP Sessions
-Unnumbered EBGP sessions are currently supported only on Cumulus VX. The transformed data model includes **unnumbered** and **ifindex** elements on EBGP neighbors reachable over unnumbered interfaces -- compare a regular EBGP neighbor (L2) with an unnumbered EBGP neighbor (L1):
+Unnumbered EBGP sessions are supported on a few platforms. *netlab* creates an IPv6 LLA EBGP session when the **unnumbered** link- or interface attribute is set, or when **ipv6** interface address or link prefix is set to *True* (IPv6 LLA).
-```
-- bgp:
- as: 65001
- neighbors:
- - as: 65100
- ifindex: 1
- name: l1
- type: ebgp
- unnumbered: true
- - as: 65101
- ipv4: 172.16.0.1
- name: l2
- type: ebgp
-```
+*netlab* can use an IPv6 LLA EBGP session to transport IPv4 address family with IPv6 next hops (RFC 8950) -- the functionality commonly used to implement *unnumbered EBGP sessions*. *netlab* will enable IPv4 AF over IPv6 LLA EBGP session when the **unnumbered** link- or interface attribute is set, or when **ipv4** interface address or link prefix is set to *True*.
## IPv6 Support
All BGP configuration templates include IPv4 and IPv6 address family configuration. Both address families are treated identically, allowing you to build IPv4-only, IPv6-only, or dual-stack networks:
* An address family (IPv4 or IPv6) is enabled within the BGP routing process as soon as the device has at least one interface with an address from that address family.
-* BGP configuration uses separate BGP sessions for IPv4 and IPv6 address families. Create your own configuration templates to enable IPv6 AF over IPv4 BGP sessions or IPv4 AF over IPv6 BGP sessions.
+* BGP configuration uses separate BGP sessions for IPv4 and IPv6 address families[^BSESS]. Create your own configuration templates to enable IPv6 AF over IPv4 BGP sessions or IPv4 AF over IPv6 BGP sessions.
* Whenever an IBGP neighbor has an IPv4/IPv6 address on its loopback interface, an IBGP sessions is configured between the IPv4/IPv6 addresses, and the IPv4/IPv6 address family is enabled for that session.
* An EBGP IPv4/IPv6 session is configured whenever a directly-connected router in another AS has an IPv4/IPv6 address on the directly-connected link.
diff --git a/docs/platforms.md b/docs/platforms.md
index e476087b5..90f8e827c 100644
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -8,8 +8,8 @@
| Cisco IOSv | iosv |
| Cisco CSR 1000v | csr |
| Cisco Nexus 9300v | nxos |
-| Cumulus Linux | cumulus |
-| Cumulus Linux 5.0 (NVUE) | cumulus_nvue |
+| Cumulus Linux 4.x/5.x | cumulus |
+| Cumulus Linux 5.0 (NVUE) | cumulus_nvue [❗](caveats.html#caveats-cumulus-nvue) |
| Fortinet FortiOS [❗](caveats.html#fortinet-fortios) | fortios |
| FRR 7.5.0 | frr |
| Generic Linux host | linux |
diff --git a/netsim/__init__.py b/netsim/__init__.py
index dca03a092..73f446e6c 100755
--- a/netsim/__init__.py
+++ b/netsim/__init__.py
@@ -1,3 +1,3 @@
#!/usr/bin/env python3
-__version__ = "1.3"
+__version__ = "1.3.1-dev1"
diff --git a/netsim/ansible/tasks/deploy-config/cumulus_nvue.yml b/netsim/ansible/tasks/deploy-config/cumulus_nvue.yml
index cdd88940e..59ff89dd0 100644
--- a/netsim/ansible/tasks/deploy-config/cumulus_nvue.yml
+++ b/netsim/ansible/tasks/deploy-config/cumulus_nvue.yml
@@ -5,9 +5,9 @@
mode: 0644
- block:
- - name: "Start NVUE daemon"
- command: systemctl start nvued
- become: true
+# - name: "Start NVUE daemon"
+# command: systemctl start nvued
+# become: true
- name: "Wait for nvued to start"
service_facts:
diff --git a/netsim/ansible/templates/bgp/cumulus.j2 b/netsim/ansible/templates/bgp/cumulus.j2
index ff7b1d6c6..0d142b3f7 100644
--- a/netsim/ansible/templates/bgp/cumulus.j2
+++ b/netsim/ansible/templates/bgp/cumulus.j2
@@ -14,28 +14,34 @@ router bgp {{ bgp.as }}
bgp cluster-id {{ bgp.rr_cluster_id }}
{% endif %}
!
-{% for n in bgp.neighbors %}
-{% if n.unnumbered is defined and n.unnumbered is sameas true %}
-{% if loop.first %}
+{#
+ Create external peer group if we have at least one IPv6 LLA EBGP neighbor
+#}
+{% for n in bgp.neighbors if n.local_if is defined %}
+{% if loop.first %}
neighbor external peer-group
neighbor external remote-as external
-{% endif %}
+{% endif %}
+{% endfor %}
+{#
+ Create neighbors
+#}
+{% for n in bgp.neighbors %}
+{% if n.local_if is defined %}
neighbor {{ n.local_if }} interface peer-group external
neighbor {{ n.local_if }} description {{ n.name }}
-{% else %}
-{% for af in ['ipv4','ipv6'] %}
-{% if n[af] is defined %}
+{% endif %}
+{% for af in ['ipv4','ipv6'] if n[af] is defined and n[af] is string %}
neighbor {{ n[af] }} remote-as {{ n.as }}
neighbor {{ n[af] }} description {{ n.name }}
-{% if n.type == 'ibgp' %}
+{% if n.type == 'ibgp' %}
neighbor {{ n[af] }} update-source lo
-{% endif %}
-{% endif %}
-{% endfor %}
- !
-{% endif %}
- !
+{% endif %}
+{% endfor %}
{% endfor %}
+{#
+ Activate neighbors, set AF attributes
+#}
{% for af in ['ipv4','ipv6'] if bgp[af] is defined %}
address-family {{ af }} unicast
!
@@ -51,29 +57,23 @@ router bgp {{ bgp.as }}
network {{ pfx|ipaddr('0') }}
{% endfor %}
!
-{# {% for n in bgp.neighbors if n[af] is defined %} #}
-{% for n in bgp.neighbors %}
-{% if n.unnumbered is defined and n.unnumbered is sameas true %}
- neighbor {{ n.local_if }} activate
-{% else %}
- neighbor {{ n[af] }} activate
-{% endif %}
-{% if n.type == 'ibgp' %}
-{% if bgp.next_hop_self is defined and bgp.next_hop_self %}
- neighbor {{ n[af] }} next-hop-self
-{% endif %}
-{% if bgp.rr|default('') and not n.rr|default('') %}
- neighbor {{ n[af] }} route-reflector-client
-{% endif %}
-{% if bgp.community.ibgp|default([]) %}
- neighbor {{ n[af] }} send-community {{ community(bgp.community.ibgp) }}
-{% endif %}
-{% else %}
-{% if bgp.community.ebgp|default([]) %}
-{% if n.unnumbered is defined and n.unnumbered is sameas true %}
- neighbor {{ n.local_if }} send-community {{ community(bgp.community.ebgp) }}
-{% else %}
- neighbor {{ n[af] }} send-community {{ community(bgp.community.ebgp) }}
+{% for n in bgp.neighbors if n.activate[af] is defined and n.activate[af] %}
+{% set peer = n[af] if n[af] is string else n.local_if|default('') %}
+{% if peer %}
+ neighbor {{ peer }} activate
+{% if n.type == 'ibgp' %}
+{% if bgp.next_hop_self is defined and bgp.next_hop_self %}
+ neighbor {{ peer }} next-hop-self
+{% endif %}
+{% if bgp.rr|default('') and not n.rr|default('') %}
+ neighbor {{ peer }} route-reflector-client
+{% endif %}
+{% if bgp.community.ibgp|default([]) %}
+ neighbor {{ peer }} send-community {{ community(bgp.community.ibgp) }}
+{% endif %}
+{% else %}
+{% if bgp.community.ebgp|default([]) %}
+ neighbor {{ peer }} send-community {{ community(bgp.community.ebgp) }}
{% endif %}
{% endif %}
{% endif %}
diff --git a/netsim/ansible/templates/bgp/cumulus_nvue.j2 b/netsim/ansible/templates/bgp/cumulus_nvue.j2
index 33b7e3060..d5e5ed199 100644
--- a/netsim/ansible/templates/bgp/cumulus_nvue.j2
+++ b/netsim/ansible/templates/bgp/cumulus_nvue.j2
@@ -26,50 +26,51 @@
autonomous-system: {{ bgp.as }}
neighbor:
{% for n in bgp.neighbors %}
-{% if n.unnumbered is defined and n.unnumbered is sameas true %}
- {{ n.local_if }}
+{% if n.local_if is defined %}
+ {{ n.local_if }}:
type: unnumbered
+ remote-as: {{ n.as }}
address-family:
ipv4-unicast:
- enable: on
+ enable: {{ 'on' if n.ipv4_rfc8950|default(False) else 'off' }}
community-advertise:
extended: on
ipv6-unicast:
- enable: on
+ enable: {{ 'on' if n.ipv6|default(False) and n.activate.ipv6|default(False) else 'off' }}
community-advertise:
extended: on
-{% else %}
- {{ n.ipv4 }}:
+{% endif %}
+{% for af in ('ipv4','ipv6') if af in n and n[af] is string %}
+ {{ n[af] }}:
remote-as: {{ n.as }}
-{% for af in ['ipv4','ipv6'] %}
-{% if loop.first %}
address-family:
-{% endif %}
-{% if n[af] is defined %}
+{# NVUE cannot turn off default IPv4 activation, so we have to implement a fix for IPv6 #}
+{% if af == 'ipv6' %}
+ ipv4-unicast:
+ enable: off
+{% endif %}
{{ af }}-unicast:
- enable: on
-{% if n.type == 'ibgp' %}
+ enable: {{ 'on' if n.activate[af]|default(False) else 'off' }}
+{% if n.type == 'ibgp' %}
nexthop-setting: self
-{% if bgp.rr|default('') and not n.rr|default('') %}
+{% if bgp.rr|default('') and not n.rr|default('') %}
route-reflector-client: on
-{% endif %}
-{% if bgp.community.ibgp|default([]) %}
+{% endif %}
+{% if bgp.community.ibgp|default([]) %}
community-advertise:
-{% for ibgp_community in bgp.community.ibgp %}
+{% for ibgp_community in bgp.community.ibgp %}
{{ ibgp_community.replace("standard","regular") }}: on
-{% endfor %}
-{% endif %}
-{% else %}
-{% if bgp.community.ebgp|default([]) %}
+{% endfor %}
+{% endif %}
+{% else %}
+{% if bgp.community.ebgp|default([]) %}
community-advertise:
-{% for ebgp_community in bgp.community.ebgp %}
+{% for ebgp_community in bgp.community.ebgp %}
{{ ebgp_community.replace("standard","regular") }}: on
-{% endfor %}
-{% endif %}
-{% endif %}
+{% endfor %}
{% endif %}
-{% endfor %}
-{% endif %}
+{% endif %}
+{% endfor %}
{% endfor %}
{% if 'router_id' in bgp %}
router-id: {{ bgp.router_id }}
diff --git a/netsim/ansible/templates/bgp/eos.j2 b/netsim/ansible/templates/bgp/eos.j2
index 0c4896dcc..d6b5d65e3 100644
--- a/netsim/ansible/templates/bgp/eos.j2
+++ b/netsim/ansible/templates/bgp/eos.j2
@@ -3,6 +3,8 @@
router bgp {{ bgp.as }}
bgp advertise-inactive
bgp log-neighbor-changes
+ no bgp default ipv4-unicast
+ no bgp default ipv6-unicast
{% if bgp.router_id|ipv4 %}
router-id {{ bgp.router_id }}
{% endif %}
@@ -36,7 +38,7 @@ router bgp {{ bgp.as }}
network {{ pfx|ipaddr('0') }}
{% endfor %}
!
-{% for n in bgp.neighbors if n[af] is defined %}
+{% for n in bgp.neighbors if n[af] is defined and n.activate[af] is defined and n.activate[af] %}
neighbor {{ n[af] }} activate
{% endfor %}
{% endif %}
diff --git a/netsim/ansible/templates/bgp/eos.macro.j2 b/netsim/ansible/templates/bgp/eos.macro.j2
index 07eb40d16..56c5eaf5d 100644
--- a/netsim/ansible/templates/bgp/eos.macro.j2
+++ b/netsim/ansible/templates/bgp/eos.macro.j2
@@ -13,13 +13,20 @@
{% if bgp.next_hop_self is defined and bgp.next_hop_self %}
neighbor {{ ip }} next-hop-self
{% endif %}
+{% endif %}
+{% if n.type == 'localas_ibgp' %}
+{% if bgp.next_hop_self is defined and bgp.next_hop_self %}
+ neighbor {{ ip }} next-hop-peer
+{% endif %}
+{% endif %}
+{% if 'ibgp' in n.type %}
{% if bgp.rr|default('') and not n.rr|default('') %}
neighbor {{ ip }} route-reflector-client
{% endif %}
{% if bgp.community.ibgp|default([]) %}
neighbor {{ ip }} send-community {{ bgp.community.ibgp|join(' ') }}
{% endif %}
-{% else %}
+{% else %}
{% if bgp.community.ebgp|default([]) %}
neighbor {{ ip }} send-community {{ bgp.community.ebgp|join(' ') }}
{% endif %}
diff --git a/netsim/ansible/templates/bgp/frr.j2 b/netsim/ansible/templates/bgp/frr.j2
index 842fdff50..6ce0172b7 100644
--- a/netsim/ansible/templates/bgp/frr.j2
+++ b/netsim/ansible/templates/bgp/frr.j2
@@ -16,12 +16,13 @@ router bgp {{ bgp.as }}
!
{% for af in ['ipv4','ipv6'] %}
{% for n in bgp.neighbors if n[af] is defined %}
- neighbor {{ n[af] }} remote-as {{ n.as }}
- neighbor {{ n[af] }} description {{ n.name }}
+{% set peer = n[af] if n[af] is string else n.local_if|default('?') %}
+ neighbor {{ peer }} {{ 'interface' if peer!=n[af] else '' }} remote-as {{ n.as }}
+ neighbor {{ peer }} description {{ n.name }}
{% if n.type == 'ibgp' %}
- neighbor {{ n[af] }} update-source lo0
+ neighbor {{ peer }} update-source lo0
{% elif n.local_as is defined %}
- neighbor {{ n[af] }} local-as {{ n.local_as }}
+ neighbor {{ peer }} local-as {{ n.local_as }} {{ 'no-prepend replace-as' if n.replace_global_as|default(True) else '' }}
{% endif %}
!
{% endfor %}
@@ -41,21 +42,22 @@ router bgp {{ bgp.as }}
network {{ pfx|ipaddr('0') }}
{% endfor %}
!
-{% for n in bgp.neighbors if n[af] is defined %}
- neighbor {{ n[af] }} activate
-{% if n.type == 'ibgp' %}
+{% for n in bgp.neighbors if n[af] is defined and n.activate[af] is defined and n.activate[af] %}
+{% set peer = n[af] if af in n and n[af] is string else n.local_if %}
+ neighbor {{ peer }} activate
+{% if 'ibgp' in n.type %}
{% if bgp.next_hop_self is defined and bgp.next_hop_self %}
- neighbor {{ n[af] }} next-hop-self
+ neighbor {{ peer }} next-hop-self
{% endif %}
{% if bgp.rr|default('') and not n.rr|default('') %}
- neighbor {{ n[af] }} route-reflector-client
+ neighbor {{ peer }} route-reflector-client
{% endif %}
{% if bgp.community.ibgp|default([]) %}
- neighbor {{ n[af] }} send-community {{ community(bgp.community.ibgp) }}
+ neighbor {{ peer }} send-community {{ community(bgp.community.ibgp) }}
{% endif %}
{% else %}
{% if bgp.community.ebgp|default([]) %}
- neighbor {{ n[af] }} send-community {{ community(bgp.community.ebgp) }}
+ neighbor {{ peer }} send-community {{ community(bgp.community.ebgp) }}
{% endif %}
{% endif %}
!
diff --git a/netsim/ansible/templates/bgp/ios.j2 b/netsim/ansible/templates/bgp/ios.j2
index 58d471161..0ac4c7b78 100644
--- a/netsim/ansible/templates/bgp/ios.j2
+++ b/netsim/ansible/templates/bgp/ios.j2
@@ -32,7 +32,7 @@ router bgp {{ bgp.as }}
{{ bgpcfg.bgp_network(af,pfx) }}
{% endfor %}
!
-{% for n in bgp.neighbors if n[af] is defined %}
+{% for n in bgp.neighbors if n[af] is defined and n.activate[af] is defined and n.activate[af] %}
{{ bgpcfg.neighbor_af(n,n[af],bgp) }}
{% endfor %}
{% endfor %}
diff --git a/netsim/ansible/templates/bgp/ios.macro.j2 b/netsim/ansible/templates/bgp/ios.macro.j2
index 5efa39b47..7d3787cc5 100644
--- a/netsim/ansible/templates/bgp/ios.macro.j2
+++ b/netsim/ansible/templates/bgp/ios.macro.j2
@@ -7,6 +7,10 @@
#}
{% macro neighbor_global(n,ip) %}
neighbor {{ ip }} remote-as {{ n.as }}
+{% if n.local_as is defined %}
+ neighbor {{ ip }} local-as {{ n.local_as }}{%
+ if n.replace_global_as|default(True) and n.type != 'localas_ibgp' %} no-prepend replace-as{% endif +%}
+{% endif %}
neighbor {{ ip }} description {{ n.name }}
{% if n.type == 'ibgp' %}
neighbor {{ ip }} update-source Loopback0
@@ -17,14 +21,14 @@
#}
{% macro neighbor_af(n,ip,bgp) %}
neighbor {{ ip }} activate
-{% if n.type == 'ibgp' %}
+{% if 'ibgp' in n.type %}
{% if bgp.community.ibgp|default([]) %}
neighbor {{ ip }} send-community {{ community(bgp.community.ibgp) }}
{% endif %}
{% if bgp.next_hop_self is defined and bgp.next_hop_self %}
- neighbor {{ ip }} next-hop-self
+ neighbor {{ ip }} next-hop-self{% if n.type == 'localas_ibgp' %} all{% endif +%}
{% endif %}
-{% if bgp.rr|default('') and not n.rr|default('') %}
+{% if bgp.rr|default('') and (not n.rr|default('') or n.type == 'localas_ibgp') %}
neighbor {{ ip }} route-reflector-client
{% endif %}
{% else %}{# EBGP IPv4 neighbor #}
diff --git a/netsim/ansible/templates/bgp/srlinux.j2 b/netsim/ansible/templates/bgp/srlinux.j2
index 85f0756e3..cbffb6ed9 100644
--- a/netsim/ansible/templates/bgp/srlinux.j2
+++ b/netsim/ansible/templates/bgp/srlinux.j2
@@ -9,5 +9,4 @@ updates:
accept: {}
{% from "srlinux.macro.j2" import bgp_config with context %}
-{% set _add_interfaces = bgp.update( { 'interfaces': interfaces } ) %}
{{ bgp_config('default',bgp.as,bgp.router_id,bgp,{}) }}
diff --git a/netsim/ansible/templates/bgp/srlinux.macro.j2 b/netsim/ansible/templates/bgp/srlinux.macro.j2
index 513419cc4..9ec4613e4 100644
--- a/netsim/ansible/templates/bgp/srlinux.macro.j2
+++ b/netsim/ansible/templates/bgp/srlinux.macro.j2
@@ -1,30 +1,60 @@
{% macro bgp_policy(vrf,is_import,communities) %}
{% set name = vrf + "_" + ('import' if is_import else 'export') %}
+
+{% if communities %}
- path: routing-policy/community-set[name={{name}}]
val:
member:
{% for c in communities %}
- "target:{{c}}"
{% endfor -%}
+{% endif %}
+
+{% macro accept() %}
+{% if is_import or not communities %}
+ accept: { }
+{% else %}
+ accept:
+ bgp:
+ communities:
+ add: "{{ name }}"
+{% endif %}
+{% endmacro %}
- path: routing-policy/policy[name={{name}}]
val:
+ default-action:
+ reject: { }
statement:
+{% if not is_import %}
+ - sequence-id: 5
+ match:
+ protocol: bgp
+ _annotate_protocol: "Accept and propagate prefixes received via BGP"
+ action:
+{{ accept() }}
+{% endif %}
- sequence-id: 10
{% if is_import %}
+{% if communities %}
match:
bgp:
community-set: "{{ name }}"
+{% endif %}
+{% else %}
+ match:
+ prefix-set: "{{ vrf }}_export"
{% endif %}
action:
-{% if is_import %}
- accept: { }
-{% else %}
- accept:
- bgp:
- communities:
- add: "{{ name }}"
-{% endif %}
+{{ accept() }}
+{% endmacro %}
+
+{% macro bgp_export_prefix(vrf,prefix) %}
+- path: routing-policy/prefix-set[name={{vrf}}_export]
+ val:
+ prefix:
+ - ip-prefix: {{ prefix }}
+ mask-length-range: exact
{% endmacro %}
{% macro bgp_config(vrf,_as,router_id,vrf_bgp,vrf_context) %}
@@ -36,150 +66,147 @@
{% set import_policy = "accept_all" %}
{% endif -%}
-{% if 'export' in vrf_context %}
{% set export_policy = vrf + "_export" %}
-{{ bgp_policy(vrf,0,vrf_context.export) }}
-{% else %}
-{% set export_policy = "accept_all" %}
-{% endif -%}
+{{ bgp_policy(vrf,0,vrf_context.export|default({})) }}
- path: network-instance[name={{vrf}}]/protocols/bgp
val:
+ admin-state: enable
autonomous-system: {{ _as }}
router-id: {{ router_id }}
ebgp-default-policy:
export-reject-all: False
import-reject-all: False
-{#
- Configure BGP Groups
-#}
- group:
-{% for g in ['ibgp-ipv4','ibgp-ipv6','ebgp','ebgp-unnumbered'] %}
-{% set _type = g[:4] %}
- - group-name: {{ g }}
- admin-state: enable
- import-policy: {{ import_policy if 'ebgp' in g else 'accept_all' }}
- export-policy: {{ export_policy if 'ebgp' in g else 'accept_all' }}
- timers:
- connect-retry: 10
- _annotate_connect-retry: "Reduce default 120s to 10s"
- minimum-advertisement-interval: 1
-
-{# BGP communities #}
-{% if bgp.community[ _type ]|default([]) %}
-{% set list = bgp.community[ _type ] %}
- send-community:
- standard: {{ 'standard' in list or 'both' in list }}
- large: {{ 'large' in list }}
-{% endif %}
-
-{# iBGP #}
-{% if g in ['ibgp-ipv4','ibgp-ipv6'] %}
- peer-as: {{ _as }}
- ipv4-unicast:
- admin-state: {{ 'enable' if g == 'ibgp-ipv4' and 'ipv4' in bgp.address_families['ibgp'] else 'disable' }}
- ipv6-unicast:
- admin-state: {{ 'enable' if g == 'ibgp-ipv6' or 'ipv6' in bgp.address_families['ibgp'] else 'disable' }}
- evpn:
- admin-state: {{ 'enable' if evpn is defined and 'ibgp' in evpn.session|default([]) else 'disable' }}
-{% if g[5:] in loopback %}
- transport:
- local-address: {{ loopback[ g[5:] ]|default('')|ipaddr('address') }}
-{% endif %}
-{% if vrf_bgp.rr|default(0) %}
- route-reflector:
- cluster-id: {{ vrf_bgp.rr_cluster_id|default(False) or router_id }}
- client: True
-{% endif %}
-{% else %}
-
-{# eBGP #}
- next-hop-self: {{ 'True' if bgp.next_hop_self|default(False) else 'False' }}
-{% if g == 'ebgp-unnumbered' %}
- local-as:
- - as-number: {{ bgp.underlay_as|default(_as) }}
- prepend-global-as: false
- ipv4-unicast:
- advertise-ipv6-next-hops: true
- receive-ipv6-next-hops: true
-{% else %}
- ipv4-unicast:
-{% endif %}
- admin-state: {{ 'enable' if 'ipv4' in bgp.address_families['ebgp'] else 'disable' }}
- ipv6-unicast:
- admin-state: {{ 'enable' if 'ipv6' in bgp.address_families['ebgp'] else 'disable' }}
-{% endif %}
-
-{% endfor %}
-
-{# Configure BGP address families #}
+{# Configure BGP address families globally #}
{% for af in ['ipv4','ipv6'] if vrf_bgp[af]|default(0) %}
{{ af }}-unicast:
admin-state: enable
multipath:
max-paths-level-1: 64
max-paths-level-2: 64 # indirect nexthops
-{% if loopback[af] is defined and bgp.advertise_loopback %}
-{# {{ bgp_network(af,loopback[af]) }} #}
-{% endif %}
+{% endfor %}
-{% for l in vrf_bgp.interfaces|default([]) if l.bgp.advertise|default("") and l[af] is defined and not 'vrf' in l %}
-{# {{ bgp_network(af,l[af]) }} #}
-{% endfor %}
-{% for pfx in bgp.originate|default([]) if af == 'ipv4' %}
-{# {{ bgp_network(af,pfx) }} #}
-{% endfor %}
+{# Create route export policies #}
+{% for af in ['ipv4','ipv6'] if vrf_bgp[af]|default(0) %}
+{% if loopback[af] is defined and bgp.advertise_loopback and vrf=='default' %}
+{{ bgp_export_prefix(vrf,loopback[af]) }}
+{% endif %}
+{% for l in interfaces|default([]) if l.bgp.advertise|default(0) and l[af]|default(False) is string and l.vrf|default('default')==vrf %}
+{{ bgp_export_prefix(vrf,l[af]) }}
{% endfor %}
+{% for pfx in vrf_bgp.originate|default([]) if af == 'ipv4' %}
+{{ bgp_export_prefix(vrf,pfx) }}
+{% endfor %}
+{% endfor %}
+
+{% macro bgp_peer_group(name,type,neighbor,transport_ip) %}
+- path: network-instance[name={{vrf}}]/protocols/bgp/group[group-name={{name}}]
+ val:
+ admin-state: enable
+ ipv4-unicast:
+ admin-state: {{ 'enable' if 'ipv4' in neighbor.activate and neighbor.activate['ipv4']
+ or 'ipv6' in neighbor.activate and not neighbor.activate['ipv6']
+ else 'disable' }}
+ ipv6-unicast:
+ admin-state: {{ 'enable' if 'ipv6' in neighbor.activate and neighbor.activate['ipv6'] else 'disable' }}
+{% if 'evpn' in neighbor and neighbor.evpn %}
+ evpn:
+ admin-state: enable # Must have at least 1 address family enabled
+{% endif %}
+ import-policy: {{ import_policy if 'ebgp' in type else 'accept_all' }}
+ export-policy: {{ export_policy if 'ebgp' in type else 'accept_all' }}
+ timers:
+ connect-retry: 10
+ _annotate_connect-retry: "Reduce default 120s to 10s"
+ minimum-advertisement-interval: 1
+{% if vrf_bgp.community[ type ]|default([]) %}
+{% set list = vrf_bgp.community[ type ] %}
+ send-community:
+ standard: {{ 'standard' in list }}
+ large: {{ 'extended' in list }}
+{% endif %}
+{% if transport_ip %}
+ transport:
+ local-address: {{ transport_ip }}
+{% endif %}
+{% if 'ibgp' in type %}
+ next-hop-self: {{ vrf_bgp.next_hop_self|default(False) }}
+ peer-as: {{ neighbor.as }}
+{% if vrf_bgp.rr|default(0) %}
+ route-reflector:
+ cluster-id: {{ vrf_bgp.rr_cluster_id|default(False) or router_id }}
+ client: True
+{% endif %}
+{% endif %}
+{% endmacro %}
+
{#
Define IPv4 and IPv6 BGP neighbors
#}
{% for n in vrf_bgp.neighbors %}
{% for af in ['ipv4','ipv6'] if n[af] is defined %}
+
{% if n[af] is string %}
+{# (Re)create peer group #}
+{% set peer_group = 'ebgp' if n.type=='ebgp' else 'ibgp-local-as' if n.type=='localas_ibgp' else ('ibgp-'+af) %}
+{% set transport_ip = loopback[af]|ipaddr('address') if af in loopback and n.type=='ibgp' else None %}
+{{ bgp_peer_group(peer_group,'ibgp' if 'ibgp' in n.type else 'ebgp',n,transport_ip) }}
+
- path: network-instance[name={{vrf}}]/protocols/bgp/neighbor[peer-address={{n[af]}}]
val:
description: {{ n.name }}
- peer-group: {{ 'ebgp' if n.type=='ebgp' else ('ibgp-'+af) }}
-{% if n.type != 'ibgp' %}
+ peer-group: {{ peer_group }}
+{% if 'ebgp' in n.type %}
peer-as: {{ n.as }}
+{% elif vrf_bgp.rr|default(False) and n.rr|default(False) %}
+ route-reflector:
+ client: False # Don't reflect routes between ibgp route reflectors
{% endif %}
{% if n.local_as is defined %}
local-as:
- as-number: {{ n.local_as }}
-{% if n.type == 'ebgp' %}
- prepend-global-as: False # Don't include iBGP AS in eBGP advertisements
-{% endif %}
+ prepend-global-as: {{ not n.replace_global_as|default(True) }} # Don't include iBGP global AS in eBGP advertisements
+{% endif %}
+
+{% elif n[af]==True and af=='ipv6' and n.type == 'ebgp' %}
+{# BGP unnumbered for IPv6 LLA, only supported for EBGP peers #}
+{% set peer_group = "ebgp-unnumbered" + (('-' + n.local_as|string()) if n.local_as is defined else '') %}
+{{ bgp_peer_group(peer_group,'ebgp',n,None) }}
+{% if n.local_as is defined %}
+ local-as:
+ - as-number: {{ n.local_as }}
+ prepend-global-as: {{ not n.replace_global_as|default(True) }} # Don't include iBGP AS in eBGP advertisements
{% endif %}
-{% elif n[af]==True and af=='ipv6' %}
-{# BGP unnumbered for IPv6 LLA #}
+{% if n.ipv4_rfc8950|default(False) %}
+ ipv4-unicast:
+ advertise-ipv6-next-hops: true
+ receive-ipv6-next-hops: true
- path: network-instance[name={{vrf}}]/ip-forwarding
val:
receive-ipv4-check: false
_annotate_receive-ipv4-check: "Allow IPv4 on IPv6 unnumbered interfaces"
+{% endif %}
-{% set ns = namespace(l=false) %}
-{% for i in vrf_bgp.interfaces|default([]) if i.ifindex == n.ifindex %}
-{% set ns.l = i %}
-{% endfor %}
-{% if ns.l %}
-{% set if_name_index = ns.l.ifname.split('.') %}
+{% for i in interfaces|default([]) if i.ifindex == n.ifindex %}
+{% set if_name_index = i.ifname.split('.') %}
{% set if_name = if_name_index[0] %}
{% set if_index = if_name_index[1] if if_name_index|length > 1 else '0' %}
- path: network-instance[name={{vrf}}]/protocols/bgp/dynamic-neighbors/interface[interface-name={{if_name}}.{{if_index}}]
val:
- peer-group: "ebgp-unnumbered"
+ peer-group: "{{ peer_group }}"
allowed-peer-as: [ {{ n.as }}..{{ n.as }} ]
-{% endif %}
+
+{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
{#
- Add extra IPv4 prefixes using static blackhole routes, add to export
+ Add extra IPv4 prefixes using static blackhole routes
#}
- path: network-instance[name={{vrf}}]/next-hop-groups/group[name=blackhole]
val:
@@ -190,11 +217,6 @@
- path: network-instance[name={{vrf}}]/static-routes/route[prefix={{pfx}}]
val:
next-hop-group: blackhole
-- path: routing-policy/prefix-set[name=external_routes]
- val:
- prefix:
- - ip-prefix: {{ pfx }}
- mask-length-range: exact
{% endfor %}
{% if 'sr' in module|default([]) %}
diff --git a/netsim/ansible/templates/evpn/srlinux.j2 b/netsim/ansible/templates/evpn/srlinux.j2
index a2851456b..5b87fb6f2 100644
--- a/netsim/ansible/templates/evpn/srlinux.j2
+++ b/netsim/ansible/templates/evpn/srlinux.j2
@@ -12,6 +12,9 @@ updates:
- group-name: {{ 'ibgp-ipv4' if type=='ibgp' else 'ebgp' }} # Could create a dedicated group for EVPN only?
evpn:
admin-state: enable
+{% if bgp.rr|default(0) %}
+ next-hop-self: False
+{% endif %}
evpn:
rapid-update: True
{% endfor %}
diff --git a/netsim/ansible/templates/initial/cumulus_nvue.j2 b/netsim/ansible/templates/initial/cumulus_nvue.j2
index 5b7b093f1..65fde37aa 100644
--- a/netsim/ansible/templates/initial/cumulus_nvue.j2
+++ b/netsim/ansible/templates/initial/cumulus_nvue.j2
@@ -32,11 +32,12 @@
{% elif l.type|default("") == "stub" %}
description: Stub interface
{% endif %}
-{% if l.ipv4 is defined or l.ipv6 is defined %}
+{% if (l.ipv4 is defined and l.ipv4) or l.ipv6 is string %}
ip:
address:
{% if l.ipv4 is defined %}
{% if l.ipv4 == True %}
+ {{ loopback.ipv4 }}: {}
{% else %}
{{ l.ipv4 }}: {}
{% endif %}
diff --git a/netsim/ansible/templates/initial/frr.j2 b/netsim/ansible/templates/initial/frr.j2
index e80dc9819..52924b2bd 100644
--- a/netsim/ansible/templates/initial/frr.j2
+++ b/netsim/ansible/templates/initial/frr.j2
@@ -68,11 +68,13 @@ interface {{ l.ifname }}
{% elif l.type|default("") == "stub" %}
description Stub interface
{% endif %}
-{% if l.ipv4 is defined %}
+{% if l.ipv4 is defined and l.ipv4 is string %}
ip address {{ l.ipv4 }}
{% endif %}
{% if l.ipv6 is defined %}
+{% if l.ipv6 is string %}
ipv6 address {{ l.ipv6 }}
+{% endif %}
ipv6 nd ra-interval 5
no ipv6 nd suppress-ra
{% endif %}
diff --git a/netsim/ansible/templates/initial/ios.j2 b/netsim/ansible/templates/initial/ios.j2
index 05ee309e7..0a031c1d7 100644
--- a/netsim/ansible/templates/initial/ios.j2
+++ b/netsim/ansible/templates/initial/ios.j2
@@ -38,6 +38,9 @@ interface {{ mgmt.ifname|default('GigabitEthernet0/0') }}
{% for l in interfaces|default([]) %}
interface {{ l.ifname }}
no shutdown
+{% if l.type == 'vlan_member' and l.vlan.access_id is defined %}
+ encapsulation dot1Q {{ l.vlan.access_id }}
+{% endif %}
{% if l.vrf is defined %}
vrf forwarding {{ l.vrf }}
{% endif %}
diff --git a/netsim/ansible/templates/initial/srlinux.j2 b/netsim/ansible/templates/initial/srlinux.j2
index 01aecdf59..1189659b3 100644
--- a/netsim/ansible/templates/initial/srlinux.j2
+++ b/netsim/ansible/templates/initial/srlinux.j2
@@ -1,5 +1,5 @@
{% macro ip_addresses(intf,ipv6_ra,is_system) %}
-{% if 'ipv4' in intf %}
+{% if 'ipv4' in intf and intf.ipv4 is string %}
ipv4:
address:
- ip-prefix: "{{ intf.ipv4 }}"
@@ -14,10 +14,13 @@
- ip-prefix: "{{ intf.ipv6 }}"
{% endif %}
{% if ipv6_ra %}
+ neighbor-discovery:
+ learn-unsolicited: link-local
router-advertisement:
router-role:
- admin-state: enable # no ipv6 nd suppress-ra
- max-advertisement-interval: 5 # ipv6 nd ra-interval 5
+ admin-state: enable # no ipv6 nd suppress-ra
+ # min-advertisement-interval: 5 # Leave at platform default 200..600
+ # max-advertisement-interval: 5
{% endif %}
{% endif %}
{% endmacro %}
@@ -40,7 +43,7 @@ updates:
val:
{{ ip_addresses(loopback,False,True) }}
-{% for l in interfaces|default([]) if (l.type in ['lan','vlan_member','svi'] or l.vlan is not defined) %}
+{% for l in interfaces|default([]) %}
{% set if_name_index = l.ifname.split('.') %}
{% set if_name = if_name_index[0] %}
{% set if_index = if_name_index[1] if if_name_index|length > 1 else l.vlan.access_id|default(0) if l.vlan is defined else '0' %}
@@ -83,25 +86,27 @@ updates:
{% set if_name_index = l.ifname.split('.') %}
{% set if_name = if_name_index[0] %}
{% set if_index = if_name_index[1] if if_name_index|length > 1 else '0' %}
+- path: network-instance[name={{ vrf }}]
+ val:
+ type: {{ 'default' if vrf=='default' else 'ip-vrf' }}
+ interface:
- name: {{ if_name }}.{{ if_index }}
{% endfor %}
{% endmacro %}
-{# Make sure the default VRF is called 'default' #}
+{# Make sure the default VRF is called 'default', and has system0.0 interface #}
- path: network-instance[name=default]
val:
type: default
interface:
- name: system0.0
+
{{ list_interfaces('default') }}
{% if vrfs is defined %}
{% for vname,vdata in vrfs.items() %}
-- path: network-instance[name={{ vname }}]
- val:
- type: ip-vrf
- # TODO: vdata.rd, vdata.import/export, vdata.af
- interface:
+
+# TODO: vdata.rd, vdata.import/export, vdata.af
{{ list_interfaces(vname) }}
{% endfor %}
diff --git a/netsim/ansible/templates/ospf/cumulus_nvue.j2 b/netsim/ansible/templates/ospf/cumulus_nvue.j2
index 64775feb3..8a10c7b76 100644
--- a/netsim/ansible/templates/ospf/cumulus_nvue.j2
+++ b/netsim/ansible/templates/ospf/cumulus_nvue.j2
@@ -13,12 +13,18 @@
{% if 'router_id' in ospf %}
router-id: {{ ospf.router_id }}
{% endif %}
+{% set node_area = ospf.area|default('0.0.0.0') %}
area:
- '{{ ospf.area | ipaddr('address') | default('0.0.0.0') }}':
+{% for o_area in interfaces|json_query('[*].ospf.area')|union([node_area])|unique %}
+ '{{ o_area }}':
network:
-{% if 'ipv4' in loopback %}
+{% if o_area == node_area and 'ipv4' in loopback %}
{{ loopback.ipv4 }}: {}
-{% endif %}
+{% endif %}
+{% for l in interfaces if 'ipv4' in l and l.ipv4 is string and l.ospf.area|default('') == o_area %}
+ {{ l.ipv4 }}: {}
+{% endfor %}
+{% endfor %}
{% for l in interfaces|default([]) if 'ospf' in l %}
{% if loop.first %}
interface:
@@ -26,14 +32,10 @@
{{ l.ifname }}:
router:
ospf:
- enable: on
- area: {{ l.ospf.area | ipaddr('address') | default('0.0.0.0') }}
{% if l.ospf.cost is defined %}
cost: {{ l.ospf.cost }}
{% endif %}
-{% if l.ospf.network_type is defined %}
- network-type: {{ l.ospf.network_type }}
-{% endif %}
+ network-type: {{ l.ospf.network_type|default('broadcast') }}
{% if l.ospf.passive | default(False) %}
passive: on
{% endif %}
diff --git a/netsim/ansible/templates/vrf/eos.bgp.j2 b/netsim/ansible/templates/vrf/eos.bgp.j2
index c0f7b851f..863445811 100644
--- a/netsim/ansible/templates/vrf/eos.bgp.j2
+++ b/netsim/ansible/templates/vrf/eos.bgp.j2
@@ -7,7 +7,7 @@ router bgp {{ bgp.as }}
!
vrf {{ vname }}
redistribute connected
- router-id {{ bgp.router_id }}
+ router-id {{ vdata.bgp.router_id|default(bgp.router_id) }}
rd {{ vdata.rd }}
{% if 'ospf' in vdata %}
redistribute ospf
diff --git a/netsim/ansible/templates/vrf/ios.bgp.j2 b/netsim/ansible/templates/vrf/ios.bgp.j2
index 63376e54a..ad61c60b5 100644
--- a/netsim/ansible/templates/vrf/ios.bgp.j2
+++ b/netsim/ansible/templates/vrf/ios.bgp.j2
@@ -4,6 +4,7 @@ router bgp {{ bgp.as }}
{% for vname,vdata in vrfs.items() %}
{% for af in ('ipv4','ipv6') if af in vdata.af|default({}) %}
address-family {{ af }} vrf {{ vname }}
+ bgp router-id {{ vdata.bgp.router_id|default(bgp.router_id) }}
redistribute connected
{% if af == 'ipv4' and 'ospf' in vdata %}
redistribute ospf {{ vdata.vrfidx }}
diff --git a/netsim/ansible/templates/vxlan/srlinux.j2 b/netsim/ansible/templates/vxlan/srlinux.j2
index 180be0865..feb475927 100644
--- a/netsim/ansible/templates/vxlan/srlinux.j2
+++ b/netsim/ansible/templates/vxlan/srlinux.j2
@@ -9,8 +9,10 @@ updates:
vni: {{ vlan.vni }}
egress:
source-ip: use-system-ipv4-address
+
- path: network-instance[name=vlan_{{vname}}]
val:
+ type: mac-vrf
vxlan-interface:
- name: vxlan0.{{ vlan.id }}
protocols:
diff --git a/netsim/augment/links.py b/netsim/augment/links.py
index dcf26dec4..51ab8a2ce 100644
--- a/netsim/augment/links.py
+++ b/netsim/augment/links.py
@@ -335,10 +335,15 @@ def augment_lan_link(link: Box, addr_pools: Box, ndict: dict, defaults: Box) ->
node_if['data'].neighbors = []
for remote_if in interfaces:
if remote_if['node'] != node_if['node'] or remote_if['data'].ifindex != node_if['data'].ifindex:
- ngh_data = { 'ifname': remote_if['data'].ifname, 'node': remote_if['node'] }
+ ngh_data = Box({ 'ifname': remote_if['data'].ifname, 'node': remote_if['node'] })
for af in ('ipv4','ipv6'):
if af in remote_if['data']:
ngh_data[af] = remote_if['data'][af]
+
+ # List enabled modules that have interface level attributes; copy those attributes too
+ mods_with_ifattr = Box({ m : True for m in ndict[remote_if['node']].get('module',[]) if defaults[m].attributes.get('interface',None) })
+ ifaddr_add_module(ngh_data,remote_if['data'],mods_with_ifattr)
+
node_if['data'].neighbors.append(ngh_data)
if common.DEBUG: # pragma: no cover (debugging)
@@ -418,6 +423,10 @@ def augment_p2p_link(link: Box, addr_pools: Box, ndict: dict, defaults: Box) ->
if af in interfaces[i]:
link[end_names[i]][af] = interfaces[i][af]
+ # JvB: copy module specific link attributes like bgp.local_as
+ mods_with_ifattr = Box({ m : True for m in ndict[remote].get('module',[]) if defaults[m].attributes.get('interface',None) })
+ ifaddr_add_module(interfaces[i]['neighbors'][0],interfaces[1-i],mods_with_ifattr)
+
return link
def check_link_attributes(data: Box, nodes: dict, valid: set) -> bool:
diff --git a/netsim/cli/__init__.py b/netsim/cli/__init__.py
index 5489cf039..e8d942c59 100755
--- a/netsim/cli/__init__.py
+++ b/netsim/cli/__init__.py
@@ -64,7 +64,7 @@ def fs_cleanup(filelist: typing.List[str], verbose: bool = False) -> None:
# Common topology loader (used by create and down)
-def load_topology(args: argparse.Namespace) -> Box:
+def load_topology(args: typing.Union[argparse.Namespace,Box]) -> Box:
common.set_logging_flags(args)
topology = read_topology.load(args.topology.name,args.defaults,"package:topology-defaults.yml")
@@ -77,7 +77,7 @@ def load_topology(args: argparse.Namespace) -> Box:
# Snapshot-or-topology loader (used by down)
-def load_snapshot_or_topology(args: argparse.Namespace) -> typing.Optional[Box]:
+def load_snapshot_or_topology(args: typing.Union[argparse.Namespace,Box]) -> typing.Optional[Box]:
common.set_logging_flags(args)
if args.device or args.provider or args.settings: # If we have -d, -p or -s flag
if not args.topology: # ... then the user wants to use the topology file
@@ -92,6 +92,22 @@ def load_snapshot_or_topology(args: argparse.Namespace) -> typing.Optional[Box]:
args.snapshot = args.snapshot or 'netlab.snapshot.yml'
return read_topology.read_yaml(filename=args.snapshot)
+# get_message: get action-specific message from topology file
+#
+
+def get_message(topology: Box, action: str, default_message: bool = False) -> typing.Optional[str]:
+ if not 'message' in topology:
+ return None
+
+ if isinstance(topology.message,str): # We have a single message
+ return topology.message if default_message else None # If the action is OK with getting the default message return it
+
+ if not isinstance(topology.message,Box): # Otherwise we should be dealing with a dict
+ common.fatal('topology message should be a string or a dict')
+ return None
+
+ return topology.message.get(action,None) # Return action-specific message if it exists
+
#
# Main command dispatcher
#
diff --git a/netsim/cli/initial.py b/netsim/cli/initial.py
index 3c39df77e..2fa997d99 100644
--- a/netsim/cli/initial.py
+++ b/netsim/cli/initial.py
@@ -7,8 +7,9 @@
import os
import argparse
-from . import common_parse_args
+from . import common_parse_args,get_message,load_snapshot_or_topology
from . import ansible
+from box import Box
#
# CLI parser for 'netlab initial' command
@@ -76,3 +77,8 @@ def run(cli_args: typing.List[str]) -> None:
print("\nInitial configurations have been created in the %s directory" % args.output)
else:
ansible.playbook('initial-config.ansible',rest)
+ topology = load_snapshot_or_topology(Box({},default_box=True,box_dots=True))
+ if topology:
+ message = get_message(topology,'initial',True)
+ if message:
+ print(f"\n\n{message}")
diff --git a/netsim/cli/up.py b/netsim/cli/up.py
index e527d532b..d639d7bc9 100644
--- a/netsim/cli/up.py
+++ b/netsim/cli/up.py
@@ -18,7 +18,7 @@
from .. import common
from . import create
from . import external_commands
-from . import common_parse_args, load_snapshot_or_topology
+from . import common_parse_args, load_snapshot_or_topology, get_message
from .. import providers
from .. import read_topology
@@ -87,5 +87,8 @@ def run(cli_args: typing.List[str]) -> None:
if not args.no_config:
external_commands.deploy_configs(4,"netlab up",args.fast_config)
+ message = get_message(topology,'up',False)
+ if message:
+ print(f"\n\n{message}")
else:
print("\nInitial configuration skipped, run 'netlab initial' to configure the devices")
diff --git a/netsim/common.py b/netsim/common.py
index 9321ece96..e78fdf213 100644
--- a/netsim/common.py
+++ b/netsim/common.py
@@ -125,7 +125,7 @@ def print_verbose(t: typing.Any) -> None:
#
# Internal debugging flags (RAISE_ON_ERROR, WARNING) cannot be set with this function
#
-def set_logging_flags(args: argparse.Namespace) -> None:
+def set_logging_flags(args: typing.Union[argparse.Namespace,Box]) -> None:
global VERBOSE, LOGGING, DEBUG, QUIET, WARNING, RAISE_ON_ERROR
if 'verbose' in args and args.verbose:
diff --git a/netsim/extra/ebgp-local_as.py b/netsim/extra/ebgp-local_as.py
deleted file mode 100644
index 03ee2d75b..000000000
--- a/netsim/extra/ebgp-local_as.py
+++ /dev/null
@@ -1,88 +0,0 @@
-#
-# BGP neighbors transformation module to support a different underlay AS per node
-#
-# The core bgp module only supports a single AS per node, as that is the more
-# common and simple way to operate BGP. Most vendors/devices can support
-# multiple AS, for example in a topology that uses iBGP for an EVPN overlay and
-# eBGP for the underlay.
-#
-# It is assumed the core bgp module provides the iBGP overlay peerings. This
-# module adds an 'underlay_as' per node, and builds additional eBGP peerings
-# based on that attribute.
-#
-# Changes:
-# * Creates additional bgp.neighbors of type 'ebgp' with a 'local_as' attribute
-#
-import typing, netaddr
-from box import Box
-from netsim import common
-
-"""
-Adds a custom bgp.underlay_as node attribute
-"""
-def init(topology: Box) -> None:
- # Allow users to specify a different AS to use for underlay peering (eBGP)
- topology.defaults.bgp.attributes.node.append('underlay_as')
-
- # Also allow the AS to be overriden on a per-link basis
- topology.defaults.bgp.attributes.link.append('underlay_as')
-
-def ebgp_neighbor(n: Box, asn: int, intf: Box, extra_data: dict) -> Box:
- ngb = Box(extra_data,default_box=True,box_dots=True)
- ngb.name = n.name
- ngb.type = 'ebgp'
- ngb["as"] = asn # 'as' for backwards compatibility, would prefer 'peer_as'
- for af in ["ipv4","ipv6"]:
- if af in intf:
- if "unnumbered" in ngb and ngb.unnumbered == True:
- ngb[af] = True
- elif isinstance( intf[af], bool ):
- ngb[af] = intf[af]
- else:
- ngb[af] = str(netaddr.IPNetwork(intf[af]).ip)
- return ngb
-
-
-"""
-build_ebgp_sessions: augment BGP neighbors with ebgp peers
-* EBGP sessions are established whenever two nodes on the same link have
- different underlay AS
-"""
-def build_ebgp_sessions(node: Box, topology: Box) -> None:
-
- #
- # eBGP sessions - iterate over all links, find adjacent nodes
- # in different AS numbers, and create eBGP neighbors; set 'local_as'
- ibgp_as = topology.bgp['as']
- for l in [ l for l in node.get("interfaces",[]) if l.type == 'p2p' ]:
- node_as = l.bgp.underlay_as if "bgp" in l and "underlay_as" in l.bgp else node.bgp.underlay_as
-
- for ngb_ifdata in l.get("neighbors",[]):
- ngb_name = ngb_ifdata.node
- neighbor = topology.nodes[ngb_name]
- if not "bgp" in neighbor:
- continue
-
- if "bgp" in ngb_ifdata and "underlay_as" in ngb_ifdata.bgp:
- peer_as = ngb_ifdata.bgp.underlay_as
- elif "underlay_as" in neighbor.bgp:
- peer_as = neighbor.bgp.underlay_as
- else:
- continue # No underlay_as defined for this neighbor
-
- if node_as!=peer_as:
- extra_data = Box({})
- extra_data.ifindex = l.ifindex
- if node_as != ibgp_as:
- extra_data.local_as = node_as
- if "unnumbered" in l:
- extra_data.unnumbered = True
- extra_data.local_if = l.ifname
- if common.DEBUG:
- print(f'ebgp-local_as: adding neighbor for node {node.name} peer {neighbor.name} peer_as={peer_as}')
- node.bgp.neighbors.append( ebgp_neighbor(neighbor,peer_as,ngb_ifdata,extra_data) )
-
-def post_transform(topology: Box) -> None:
- for node in topology.nodes.values():
- if "bgp" in node and "underlay_as" in node.bgp:
- build_ebgp_sessions(node,topology)
diff --git a/netsim/extra/ebgp.utils/srlinux.j2 b/netsim/extra/ebgp.utils/srlinux.j2
new file mode 100644
index 000000000..0ea95dd84
--- /dev/null
+++ b/netsim/extra/ebgp.utils/srlinux.j2
@@ -0,0 +1,51 @@
+{% macro ebgp_neighbor(n,af,vrf) -%}
+- path: network-instance[name={{vrf}}]/protocols/bgp
+ val:
+{% if n.type=='ebgp' and af=='ipv6' and n.ipv6|default(0) == True %}
+ group:
+ - group-name: "ebgp-unnumbered{{ ('-' + n.local_as|string()) if n.local_as is defined else '' }}"
+{% else %}
+ neighbor:
+ - peer-address: {{ n[af]|ipaddr('address') }}
+{% endif %}
+{% if n.default_originate|default(False) %}
+{% set activate = n.activate|default( {'ipv4':True,'ipv6':True} ) %}
+ send-default-route:
+ ipv4-unicast: {{ activate.ipv4|default(False) }}
+ ipv6-unicast: {{ activate.ipv6|default(False) }}
+{% endif %}
+ as-path-options:
+{% if n.allowas_in is defined %}
+ allow-own-as: {{ n.allowas_in|int }}
+{% endif %}
+ replace-peer-as: {{ True if n.as_override|default(False) else False }}
+{% if n.password is defined and (n.type!='ebgp' or n.ipv6|default('') is string) %}
+ authentication:
+ keychain: "peer-{{n.name}}"
+
+- path: system/authentication/keychain[name=peer-{{n.name}}]
+ val:
+ type: tcp-md5
+ key:
+ - index: 0
+ algorithm: md5
+ authentication-key: "{{ n.password }}"
+{% endif %}
+{%- endmacro %}
+
+updates:
+{% for af in ['ipv4','ipv6'] %}
+{% for n in bgp.neighbors if n[af] is defined and (n[af] is string or (af=='ipv6' and n.ipv6)) %}
+{{ ebgp_neighbor(n,af,'default') -}}
+{% endfor %}
+{% endfor %}
+
+{% if vrfs is defined %}
+{% for vname,vdata in vrfs.items() if vdata.bgp is defined %}
+{% for af in ['ipv4','ipv6'] %}
+{% for n in vdata.bgp.neighbors if n[af] is defined and (n[af] is string or (af=='ipv6' and n.ipv6)) %}
+{{ ebgp_neighbor(n,af,vname) -}}
+{% endfor %}
+{% endfor %}
+{% endfor %}
+{% endif %}
diff --git a/netsim/install/containerlab.sh b/netsim/install/containerlab.sh
index 67eba7794..5dc836225 100755
--- a/netsim/install/containerlab.sh
+++ b/netsim/install/containerlab.sh
@@ -79,7 +79,7 @@ echo "Install Docker Engine"
$SUDO apt-get update
$SUDO apt-get install -y $FLAG_QUIET docker-ce docker-ce-cli containerd.io
echo "Install containerlab"
-$SUDO bash -c "$(curl -sL https://get-clab.srlinux.dev)"
+$SUDO bash -c "$(curl -sL https://get.containerlab.dev)"
set +e
G="$(groups $USER|grep docker)"
set -e
diff --git a/netsim/install/grpc.sh b/netsim/install/grpc.sh
index 330d10ba0..5f547730c 100755
--- a/netsim/install/grpc.sh
+++ b/netsim/install/grpc.sh
@@ -1,5 +1,11 @@
#!/bin/bash
+# Check dependencies
+if [[ ! `which ansible-galaxy` ]]; then
+ echo "GRPC packages depend on Ansible (ansible-galaxy), aborting..."
+ exit 1
+fi
+
if [[ -z "$FLAG_YES" ]]; then
# Remove implied default of Y - ghostinthenet 20220418
read -p "Are you sure you want to proceed [y/n] " -n 1 -r
@@ -17,4 +23,4 @@ ansible-galaxy collection install git+https://github.com/nokia/ansible-networkin
# grpc sources contain generated pb2 file which is not compatible with newer versions of protobuf
python3 -m pip install grpcio protobuf==3.20.1 --upgrade
-exit $?
\ No newline at end of file
+exit $?
diff --git a/netsim/modules/bgp.py b/netsim/modules/bgp.py
index 8528adda3..a4ad51bb5 100644
--- a/netsim/modules/bgp.py
+++ b/netsim/modules/bgp.py
@@ -11,6 +11,7 @@
from .. import common
from .. import data
from ..augment.links import IFATTR
+from ..augment import devices
def check_bgp_parameters(node: Box) -> None:
if not "bgp" in node: # pragma: no cover (should have been tested and reported by the caller)
@@ -44,32 +45,295 @@ def check_bgp_parameters(node: Box) -> None:
valid_values=['standard','extended'],
module='bgp')
+BGP_VALID_AF: typing.Final[list] = ['ipv4','ipv6']
+BGP_VALID_SESSION_TYPE: typing.Final[list] = ['ibgp','ebgp','localas_ibgp']
+
+def validate_bgp_sessions(node: Box, sessions: Box, attribute: str) -> bool:
+ OK = True
+ for k in list(sessions.keys()):
+ if not k in BGP_VALID_AF:
+ common.error(
+ f'Invalid address family in bgp.{attribute} in node {node.name}',
+ common.IncorrectValue,
+ 'bgp')
+ OK = False
+ else:
+ if data.must_be_list(
+ parent=sessions,
+ key=k,
+ path=f'nodes.{node.name}.bgp.{attribute}',
+ true_value=BGP_VALID_SESSION_TYPE,
+ valid_values=BGP_VALID_SESSION_TYPE,
+ module='bgp') is None:
+ OK = False
+
+ return OK
+
+"""
+find_bgp_rr: find route reflectors in the specified autonomous system
+
+Given an autonomous system and lab topology, return a list of node names that are route reflectors in that AS
+"""
def find_bgp_rr(bgp_as: int, topology: Box) -> typing.List[Box]:
return [ n
for n in topology.nodes.values()
if 'bgp' in n and n.bgp["as"] == bgp_as and n.bgp.get("rr",None) ]
-def bgp_neighbor(n: Box, intf: Box, ctype: str, extra_data: typing.Optional[dict] = None) -> Box:
+"""
+bgp_neighbor: Create BGP neighbor data structure
+
+* n - neighbor node data
+* intf - neighbor interface data (could be addressing prefix or whatever is in the interface neighbor list)
+* ctype - session type (ibgp or ebgp)
+* extra_data - anything else we might want to pass to the neighbor data structure
+"""
+def bgp_neighbor(n: Box, intf: Box, ctype: str, sessions: Box, extra_data: typing.Optional[dict] = None) -> typing.Optional[Box]:
ngb = Box(extra_data or {},default_box=True,box_dots=True)
ngb.name = n.name
ngb["as"] = n.bgp.get("as")
ngb["type"] = ctype
+ af_count = 0
for af in ["ipv4","ipv6"]:
- if af in intf:
- if "unnumbered" in ngb and ngb.unnumbered == True:
- ngb[af] = True
- elif isinstance(intf[af],bool):
- ngb[af] = intf[af]
- else:
- ngb[af] = str(netaddr.IPNetwork(intf[af]).ip)
- return ngb
-
+ if ctype in sessions[af]:
+ if af in intf:
+ af_count = af_count + 1
+ if "unnumbered" in ngb and ngb.unnumbered == True:
+ ngb[af] = True
+ elif isinstance(intf[af],bool):
+ ngb[af] = intf[af]
+ else:
+ ngb[af] = str(netaddr.IPNetwork(intf[af]).ip)
+
+ return ngb if af_count > 0 else None
+
+"""
+get_neighbor_rr: create extra data for bgp_neighbor function
+
+Returns { rr: him } dict if the neighbor happens to be a RR. That dict will be merged with
+the rest of the neighbor data
+"""
def get_neighbor_rr(n: Box) -> typing.Optional[typing.Dict]:
if "rr" in n.get("bgp"):
return { "rr" : n.bgp.rr }
return {}
+"""
+build_ibgp_sessions: create IBGP session data structure
+
+* BGP route reflectors need IBGP session with all other nodes in the same AS
+* Other nodes need IBGP sessions with all RRs in the same AS
+"""
+def build_ibgp_sessions(node: Box, sessions: Box, topology: Box) -> None:
+ rrlist = find_bgp_rr(node.bgp.get("as"),topology)
+
+ # If we don't have route reflectors, or if the current node is a route
+ # reflector, we need BGP sessions to all other nodes in the same AS
+ if not(rrlist) or node.bgp.get("rr",None):
+ for name,n in topology.nodes.items():
+ if "bgp" in n:
+ if n.bgp.get("as") == node.bgp.get("as") and n.name != node.name:
+ neighbor_data = bgp_neighbor(n,n.loopback,'ibgp',sessions,get_neighbor_rr(n))
+ if not neighbor_data is None:
+ node.bgp.neighbors.append(neighbor_data)
+
+ #
+ # The node is not a route reflector, and we have a non-empty RR list
+ # We need BGP sessions with the route reflectors
+ else:
+ for n in rrlist:
+ if n.name != node.name:
+ neighbor_data = bgp_neighbor(n,n.loopback,'ibgp',sessions,get_neighbor_rr(n))
+ if not neighbor_data is None:
+ node.bgp.neighbors.append(neighbor_data)
+
+"""
+build_ebgp_sessions: create EBGP session data structure
+
+* EBGP sessions are established whenever two nodes on the same link have different AS
+* Links matching 'advertise_roles' get 'advertise' attribute set
+"""
+def build_ebgp_sessions(node: Box, sessions: Box, topology: Box) -> None:
+ features = devices.get_device_features(node,topology.defaults)
+
+ #
+ # Iterate over all links, find adjacent nodes
+ # in different AS numbers, and create BGP neighbors
+ for l in node.get("interfaces",[]):
+ node_as = node.bgp.get("as") # Get our real AS number and the AS number of the peering session
+ node_local_as = data.get_from_box(l,'bgp.local_as') or data.get_from_box(node,'bgp.local_as') or node_as
+
+ af_list = [ af for af in ('ipv4','ipv6','unnumbered') if af in l ]
+ if not af_list: # This interface has no usable address
+ continue
+
+ if node_as != node_local_as:
+ if not features.bgp.local_as:
+ common.error(
+ text=f'{node.name} (device {node.device}) does not support BGP local AS (interface {l.name})',
+ category=common.IncorrectValue,
+ module='bgp')
+ continue
+ if l.get('vrf',None) and not features.bgp.vrf_local_as:
+ common.error(
+ text=f'{node.name} (device {node.device}) does not support BGP local AS for EBGP sessions in VRF {l.vrf}',
+ category=common.IncorrectValue,
+ module='bgp')
+ continue
+
+ for ngb_ifdata in l.get("neighbors",[]):
+ af_list = [ af for af in ('ipv4','ipv6','unnumbered') if af in ngb_ifdata ]
+ if not af_list: # Neighbor has no usable address
+ continue
+ #
+ # Get neighbor node data and its true or interface-local AS
+ #
+ ngb_name = ngb_ifdata.node
+ neighbor = topology.nodes[ngb_name]
+ neighbor_real_as = data.get_from_box(neighbor,'bgp.as')
+ neighbor_local_as = data.get_from_box(ngb_ifdata,'bgp.local_as') or data.get_from_box(neighbor,'bgp.local_as') or neighbor_real_as
+
+ if not neighbor_local_as: # Neighbor has no usable BGP AS number, move on
+ continue
+
+ if node_as == neighbor_real_as and node_local_as == neighbor_local_as:
+ continue # Routers in the same AS + no local-as trickery => nothing to do here
+
+ extra_data = Box({})
+ extra_data.ifindex = l.ifindex
+
+ # Figure out whether both neighbors have IPv6 LLA and/or unnumbered IPv4 interfaces
+ #
+ ipv6_lla = l.get('ipv6',None) is True and ngb_ifdata.get('ipv6',None) is True
+ ipv6_num = isinstance(l.get('ipv6',None),str) and isinstance(ngb_ifdata.get('ipv6',None),str)
+ rfc8950 = l.get('unnumbered',None) is True and ngb_ifdata.get('unnumbered',None) is True
+ ipv4_unnum = l.get('ipv4',None) is True and ngb_ifdata.get('ipv4',None) is True
+# print(f'EBGP node {node.name} neighbor {ngb_name} lla {ipv6_lla} v6num {ipv6_num} v4unnum {ipv4_unnum} rfc8950 {unnumbered}')
+ if ipv4_unnum and not ipv6_num: # Unnumbered IPv4 w/o IPv6 ==> IPv6 LLA + RFC 8950 IPv4 AF
+ rfc8950 = True
+
+# print(f'... unnumbered {unnumbered}')
+ if ipv6_lla or rfc8950:
+ extra_data.local_if = l.ifname # Set local_if to indicate IPv6 LLA EBGP session
+ if not features.bgp.ipv6_lla: # IPv6_LLA feature flag has to be set even for IPv4 unnumbered EBGP
+ common.error(
+ text=f'{node.name} (device {node.device}) does not support EBGP sessions over auto-generated IPv6 LLA (interface {l.name})',
+ category=common.IncorrectValue,
+ module='bgp')
+ continue
+
+ if rfc8950:
+ extra_data.ipv4_rfc8950 = True # Set unnumbered indicate RFC 8950 IPv4 AF
+ if not features.bgp.rfc8950:
+ common.error(
+ text=f'{node.name} (device {node.device}) does not support IPv4 RFC 8950-style AF over IPv6 LLA EBGP sessions (interface {l.name})',
+ category=common.IncorrectValue,
+ module='bgp')
+ continue
+
+ for k in ('local_as','replace_global_as'):
+ local_as_data = data.get_from_box(l,f'bgp.{k}') or data.get_from_box(node,f'bgp.{k}')
+ if not local_as_data is None:
+ extra_data[k] = local_as_data
+
+ session_type = 'localas_ibgp' if neighbor_local_as == node_local_as else 'ebgp'
+ if session_type == 'localas_ibgp':
+ if not features.bgp.local_as_ibgp:
+ common.error(
+ text=f'You cannot use BGP local-as to create an IBGP session with {ngb_name} on {node.name} (device {node.device})',
+ category=common.IncorrectValue,
+ module='bgp')
+ continue
+
+ ebgp_data = bgp_neighbor(neighbor,ngb_ifdata,session_type,sessions,extra_data)
+ if not ebgp_data is None:
+ ebgp_data['as'] = neighbor_local_as
+ if 'vrf' in l: # VRF neighbor
+ if not node.vrfs[l.vrf].bgp.neighbors:
+ node.vrfs[l.vrf].bgp.neighbors = []
+ node.vrfs[l.vrf].bgp.neighbors.append(ebgp_data)
+ else: # Global neighbor
+ node.bgp.neighbors.append(ebgp_data)
+
+"""
+activate_bgp_default_af -- activate default AF on IPv4 and/or IPv6 transport sessions
+
+Based on transport session(s) with a BGP neighbor, local BGP AF, and BGP AF configuration
+parameters, set neighbor.activate.AF flags
+"""
+
+def activate_bgp_default_af(node: Box, activate: Box, topology: Box) -> None:
+ for ngb in node.bgp.neighbors:
+ for af in ('ipv4','ipv6'):
+ if af in ngb:
+ ngb.activate[af] = node.bgp.get(af) and af in activate and ngb.type in activate[af]
+
+"""
+build_bgp_sessions: create BGP session data structure
+
+* Create an empty list of BGP neighbors
+* Create IBGP sessions
+* Create EBGP sessions
+* Set BGP AF flags
+"""
+
+BGP_DEFAULT_SESSIONS: typing.Final[dict] = {
+ 'ipv4': [ 'ibgp', 'ebgp', 'localas_ibgp' ],
+ 'ipv6': [ 'ibgp', 'ebgp', 'localas_ibgp' ]
+}
+
+def build_bgp_sessions(node: Box, topology: Box) -> None:
+ if not data.get_from_box(node,'bgp.as'): # Sanity check: do we have usable AS number
+ common.fatal(f'build_bgp_sessions: node {node.name} has no usable BGP AS number, how did we get here???')
+ return # ... not really, get out of here
+
+ node.bgp.neighbors = []
+ bgp_sessions = node.bgp.get('sessions') or Box(BGP_DEFAULT_SESSIONS)
+ if not validate_bgp_sessions(node,bgp_sessions,'sessions'):
+ return
+
+ build_ibgp_sessions(node,bgp_sessions,topology)
+ build_ebgp_sessions(node,bgp_sessions,topology)
+
+ # Calculate BGP address families
+ #
+ for af in ['ipv4','ipv6']:
+ for n in node.bgp.neighbors:
+ if af in n:
+ node.bgp[af] = True
+ break
+
+ # Activate default BGP address families
+ features = devices.get_device_features(node,topology.defaults)
+ if 'activate' in node.bgp:
+ if not features.bgp.activate_af:
+ common.error(
+ f'node {node.name} (device {node.device}) does not support configurable activation of default BGP address families',
+ common.IncorrectValue,
+ 'bgp')
+ return
+
+ activate = node.bgp.get('activate') or Box(BGP_DEFAULT_SESSIONS)
+ if not validate_bgp_sessions(node,activate,'activate'):
+ return
+
+ activate_bgp_default_af(node,activate,topology)
+
+"""
+bgp_set_advertise: set bgp.advertise flag on stub links
+"""
+def bgp_set_advertise(node: Box, topology: Box) -> None:
+ stub_roles = data.get_global_parameter(topology,"bgp.advertise_roles")
+ if not stub_roles:
+ return
+
+ for l in node.get("interfaces",[]):
+ if "bgp" in l:
+ if "advertise" in l.bgp:
+ continue
+ if l.get("type",None) in stub_roles or l.get("role",None) in stub_roles:
+ l.bgp.advertise = True
+
class BGP(_Module):
'''
@@ -206,83 +470,6 @@ def link_pre_transform(self, link: Box, topology: Box) -> None:
if len(as_set) > 1 and not link.get("role"):
link.role = ebgp_role
- """
- build_bgp_sessions: create BGP session data structure
-
- * BGP route reflectors need IBGP session with all other nodes in the same AS
- * Other nodes need IBGP sessions with all RRs in the same AS
- * EBGP sessions are established whenever two nodes on the same link have different AS
- * Links matching 'advertise_roles' get 'advertise' attribute set
- """
- def build_bgp_sessions(self, node: Box, topology: Box) -> None:
- rrlist = find_bgp_rr(node.bgp.get("as"),topology)
- node.bgp.neighbors = []
-
- # If we don't have route reflectors, or if the current node is a route
- # reflector, we need BGP sessions to all other nodes in the same AS
- if not(rrlist) or node.bgp.get("rr",None):
- for name,n in topology.nodes.items():
- if "bgp" in n:
- if n.bgp.get("as") == node.bgp.get("as") and n.name != node.name:
- node.bgp.neighbors.append(bgp_neighbor(n,n.loopback,'ibgp',get_neighbor_rr(n)))
-
- #
- # The node is not a route reflector, and we have a non-empty RR list
- # We need BGP sessions with the route reflectors
- else:
- for n in rrlist:
- if n.name != node.name:
- node.bgp.neighbors.append(bgp_neighbor(n,n.loopback,'ibgp',get_neighbor_rr(n)))
-
- #
- # EBGP sessions - iterate over all links, find adjacent nodes
- # in different AS numbers, and create BGP neighbors
- for l in node.get("interfaces",[]):
- for ngb_ifdata in l.get("neighbors",[]):
- ngb_name = ngb_ifdata.node
- neighbor = topology.nodes[ngb_name]
- if not "bgp" in neighbor:
- continue
- if neighbor.bgp.get("as") and neighbor.bgp.get("as") != node.bgp.get("as"):
- extra_data = Box({})
- extra_data.ifindex = l.ifindex
- if "unnumbered" in l:
- extra_data.unnumbered = True
- extra_data.local_if = l.ifname
-
- ebgp_data = bgp_neighbor(neighbor,ngb_ifdata,'ebgp',extra_data)
- if 'vrf' in l: # VRF neighbor
- if not node.vrfs[l.vrf].bgp.neighbors:
- node.vrfs[l.vrf].bgp.neighbors = []
- node.vrfs[l.vrf].bgp.neighbors.append(ebgp_data)
- else: # Global neighbor
- node.bgp.neighbors.append(ebgp_data)
-
- # Calculate BGP address families
- #
- for af in ['ipv4','ipv6']:
- for n in node.bgp.neighbors:
- if af in n:
- node.bgp[af] = True
- break
-
- '''
- bgp_set_advertise: set bgp.advertise flag on stub links
- '''
- def bgp_set_advertise(self, node: Box, topology: Box) -> None:
- stub_roles = topology.defaults.bgp.get("advertise_roles",None)
- if 'advertise_roles' in topology.bgp:
- stub_roles = topology.bgp.get("advertise_roles",None)
- if stub_roles:
- for l in node.get("interfaces",[]):
- if "bgp" in l:
- if "advertise" in l.bgp:
- continue
- if l.get("type",None) in stub_roles or l.get("role",None) in stub_roles:
- if not 'bgp' in l:
- l.bgp = {}
- l.bgp.advertise = True
-
#
# Have to set BGP router IDs and cluster IDs before going into node_post_transform
#
@@ -325,5 +512,5 @@ def node_post_transform(self, node: Box, topology: Box) -> None:
common.fatal(f"Internal error: node {node.name} has BGP module enabled but no BGP parameters","bgp")
return
check_bgp_parameters(node)
- self.build_bgp_sessions(node,topology)
- self.bgp_set_advertise(node,topology)
+ build_bgp_sessions(node,topology)
+ bgp_set_advertise(node,topology)
diff --git a/netsim/modules/vlan.py b/netsim/modules/vlan.py
index 7a1bc74f5..ef9c66678 100644
--- a/netsim/modules/vlan.py
+++ b/netsim/modules/vlan.py
@@ -448,12 +448,21 @@ def create_vlan_links(link: Box, v_attr: Box, topology: Box) -> None:
if 'vlan' in intf and vname in intf.vlan.get('trunk',{}):
intf_data = Box(intf.vlan.trunk[vname] or {},default_box=True,box_dots=True)
intf_data.node = intf.node
+ intf_data.vlan.access = vname
+ intf_node = topology.nodes[intf.node]
+
if 'mode' in intf.vlan and not get_from_box(intf_data,'vlan.mode'):
intf_data.vlan.mode = intf.vlan.mode # vlan.mode is inherited from trunk dictionary or parent interface
- link_data.interfaces.append(intf_data)
- if not prefix: # Still no usable IP prefix? Try to get it from the node VLAN pool
- if vname in topology.nodes[intf.node].get('vlans',{}):
- prefix = topology.node[intf.node].vlans[vname].prefix
+
+ if interface_vlan_mode(intf_data,intf_node,topology) == 'bridge': # Is this VLAN interface in bridge mode?
+ intf_data.ipv4 = False # ... if so, disable addressing on this interface
+ intf_data.ipv6 = False
+ else:
+ if not prefix: # Still no usable IP prefix? Try to get it from the node VLAN pool
+ if vname in intf_node.get('vlans',{}):
+ prefix = topology.node[intf.node].vlans[vname].prefix
+
+ link_data.interfaces.append(intf_data) # Append the interface to vlan link
if routed_access_vlan(link_data,topology,vname):
link_data.vlan.mode = 'route'
@@ -814,6 +823,25 @@ def cleanup_vlan_name(topology: Box) -> None:
if 'vlan_name' in intf:
intf.pop('vlan_name',None)
+"""
+fix_vlan_gateways -- set VLAN-wide gateway IP
+
+The link augmentation code sets gateway IP for hosts connected to physical links. That approach does not work
+for VLAN subnets stretched across multiple physical links. We have to fix that here based on host neighbor list.
+
+Please note that we'll have to fix this code when we implement the first-hop gateway module
+"""
+def fix_vlan_gateways(topology: Box) -> None:
+ for node in topology.nodes.values():
+ if node.get('role') != 'host': # Fix first-hop gateways only for hosts
+ continue
+ for intf in node.get('interfaces',[]): # Iterate over all interfaces
+ if not get_from_box(intf,'gateway.ipv4'): # ... that don't have an IPv4 gateway
+ for neighbor in intf.get('neighbors',[]): # Iterate over all neighbors
+ if 'ipv4' in neighbor: # ... until we find one with a usable IPv4
+ intf.gateway.ipv4 = neighbor.ipv4 # Set that address as our gateway
+ break # ... and get out of here
+
class VLAN(_Module):
def module_pre_transform(self, topology: Box) -> None:
@@ -888,9 +916,19 @@ def link_pre_transform(self, link: Box, topology: Box) -> None:
# Merge link VLAN attributes into interface VLAN attributes to make subsequent steps easier
if 'vlan' in link:
- for intf in link.interfaces:
- if 'vlan' in topology.nodes[intf.node].get('module',[]):
- intf.vlan = link.vlan + intf.vlan
+ for intf in link.interfaces: # Iterate over all interfaces attached to the link
+ intf_node = topology.nodes[intf.node]
+ if 'vlan' in intf_node.get('module',[]): # ... is the node a VLAN-aware node?
+ intf.vlan = link.vlan + intf.vlan # ... merge link VLAN attributes with interface attributes
+
+ # Disable IP addressing on access VLAN ports on bridged VLANs
+ if link_vlan: # Are we dealing with an access VLAN?
+ for intf in link.interfaces: # Iterate over all interfaces attached to the link
+ intf_node = topology.nodes[intf.node]
+ if 'vlan' in intf_node.get('module',[]): # ... is the node a VLAN-aware node?
+ if interface_vlan_mode(intf,intf_node,topology) == 'bridge': # ... that is in bridge mode on the current access VLAN?
+ intf.ipv4 = False # ... if so, disable addressing on this interface
+ intf.ipv6 = False
if 'trunk' in v_attr:
create_vlan_links(link,v_attr,topology)
@@ -908,3 +946,4 @@ def module_post_transform(self, topology: Box) -> None:
topology.links = [ link for link in topology.links if link.type != 'vlan_member' ]
cleanup_vlan_name(topology)
+ fix_vlan_gateways(topology)
diff --git a/netsim/modules/vxlan.py b/netsim/modules/vxlan.py
index ea31c59fe..6bdcb7d7a 100644
--- a/netsim/modules/vxlan.py
+++ b/netsim/modules/vxlan.py
@@ -16,6 +16,7 @@
# Check VXLAN-enabled VLANs
#
def node_vlan_check(node: Box, topology: Box) -> bool:
+
if not node.vxlan.vlans: # Create a default list of VLANs if needed
node.vxlan.vlans = [ name for name,value in node.vlans.items() if 'vni' in value ]
@@ -35,15 +36,16 @@ def node_vlan_check(node: Box, topology: Box) -> bool:
# Skip VLAN names that are valid but not used on this node
if not vname in node.vlans:
continue
- if not 'vni' in node.vlans[vname]:
+ # 'None' can happen if vlan module logic hasn't run
+ if node.vlans[vname] is None or not 'vni' in node.vlans[vname]:
common.error(
- f'VXLAN-enabled VLAN {vname} in node {node.name} does not have a VNI',
+ f'VXLAN-enabled VLAN {vname} in node {node.name} does not have a VNI - was the "vlan" module applied?',
common.IncorrectValue,
'vxlan')
OK = False
else:
vlan_list.append(vname)
-
+
node.vxlan.vlans = vlan_list
return OK
@@ -94,6 +96,23 @@ def build_vtep_list(vlan: Box, node: str, nodes: typing.List[str], topology: Box
class VXLAN(_Module):
+ # Pulls in vlans and vrfs used in vxlan.vlans, runs before vlan.node_pre_transform
+ def node_pre_default(self, node: Box, topology: Box) -> None:
+ global_vlans = topology.get('vlans',[])
+ if global_vlans:
+ vxlan_vlans = get_from_box(node,'vxlan.vlans') or get_from_box(topology,'vxlan.vlans')
+ if vxlan_vlans:
+ node.vlans = node.vlans or {}
+ node.vrfs = node.vrfs or {}
+ for vname in vxlan_vlans:
+ if vname not in node.vlans and vname in global_vlans:
+ node.vlans[ vname ] = { }
+ if 'vrf' in global_vlans[vname] and global_vlans[vname].vrf not in node.vrfs:
+ node.vrfs[ global_vlans[vname].vrf ] = { }
+
+ if common.DEBUG:
+ print( f"vxlan: after node_pre_default for {node.name} -> node.vlans={node.vlans} node.vrfs={node.vrfs}" )
+
def node_pre_transform(self, node: Box, topology: Box) -> None:
flooding_values = ['static']
for m in ['evpn']:
diff --git a/netsim/read_topology.py b/netsim/read_topology.py
index a47af029f..e77f2e812 100644
--- a/netsim/read_topology.py
+++ b/netsim/read_topology.py
@@ -86,7 +86,7 @@ def load(fname: str , local_defaults: str, sys_defaults: str) -> Box:
return topology
-def add_cli_args(topo: Box, args: argparse.Namespace) -> None:
+def add_cli_args(topo: Box, args: typing.Union[argparse.Namespace,Box]) -> None:
if args.device:
topo.defaults.device = args.device
diff --git a/netsim/templates/provider/clab/clab.j2 b/netsim/templates/provider/clab/clab.j2
index 8dd946d81..39a651622 100644
--- a/netsim/templates/provider/clab/clab.j2
+++ b/netsim/templates/provider/clab/clab.j2
@@ -30,6 +30,9 @@ topology:
{% if 'runtime' in n %}
runtime: {{ n.runtime }}
{% endif %}
+{% if 'startup-config' in clab %}
+ startup-config: {{ clab['startup-config'] }}
+{% endif %}
{% if clab.license is defined %}
license: {{ clab.license }}
{% endif %}
diff --git a/netsim/templates/provider/libvirt/cumulus_nvue-domain.j2 b/netsim/templates/provider/libvirt/cumulus_nvue-domain.j2
index e18fec197..b49d673f6 100644
--- a/netsim/templates/provider/libvirt/cumulus_nvue-domain.j2
+++ b/netsim/templates/provider/libvirt/cumulus_nvue-domain.j2
@@ -6,7 +6,7 @@
{{ name }}.vm.provider :libvirt do |domain|
domain.cpus = 2
- domain.memory = 1024
+ domain.memory = 2048
end
# Run cumulus-specific Vagrant box configuration
diff --git a/netsim/topology-defaults.yml b/netsim/topology-defaults.yml
index abb3e83b4..8ce551d27 100644
--- a/netsim/topology-defaults.yml
+++ b/netsim/topology-defaults.yml
@@ -27,7 +27,7 @@ addressing:
# Global, node and link attributes
attributes:
global: [ addressing,defaults,groups,links,module,name,nodes,plugin,provider ]
- internal: [ input,includes,pools,Provider,Plugin ]
+ internal: [ input,includes,pools,Provider,Plugin,message ]
link: [ bandwidth,bridge,name,prefix,role,type,unnumbered,interfaces,mtu,gateway,vlan_name ]
link_internal: [ linkindex,parentindex ]
link_no_propagate: [ prefix,interfaces,gateway ]
@@ -47,11 +47,15 @@ bgp:
transform_after: [ vlan ]
next_hop_self: true
attributes:
- global: [ af, as, next_hop_self, rr_cluster_id, rr_list, ebgp_role, as_list, advertise_loopback, advertise_roles, community, address_families ]
- node: [ af, as, next_hop_self, rr, rr_cluster_id, originate, advertise_loopback, community, router_id, address_families, local_as ]
- node_copy: [ local_as ]
+ global: [
+ af, as, next_hop_self, rr_cluster_id, rr_list, ebgp_role, as_list, sessions, activate,
+ advertise_loopback, advertise_roles, community, address_families, replace_global_as ]
+ node: [
+ af, as, next_hop_self, rr, rr_cluster_id, originate, advertise_loopback, sessions, activate,
+ community, router_id, address_families, local_as, replace_global_as ]
+ node_copy: [ local_as, replace_global_as ]
link: [ advertise ]
- interface: [ local_as ]
+ interface: [ local_as, replace_global_as ]
isis:
supported_on: [ eos, frr, csr, iosv, nxos, vsrx, srlinux, sros, none, vyos ]
@@ -166,7 +170,7 @@ vxlan: # VXLAN support
no_propagate: [ use_v6_vtep ]
use_v6_vtep: false
-mpls: # LDP and BGP LU support
+mpls: # LDP, BGP LU, L3VPN and 6PE support
supported_on: [ eos, iosv, csr, routeros, vyos ]
config_after: [ vlan, ospf, isis, bgp ]
transform_after: [ vlan, bgp ]
@@ -287,6 +291,11 @@ devices:
virtualbox:
image: cisco/iosv
features:
+ bgp:
+ local_as: True
+ vrf_local_as: True
+ local_as_ibgp: True
+ activate_af: True
initial:
ipv4:
unnumbered: False
@@ -330,6 +339,11 @@ devices:
node:
min_mtu: 1500
features:
+ bgp:
+ local_as: True
+ vrf_local_as: True
+ local_as_ibgp: True
+ activate_af: True
initial:
ipv4:
unnumbered: True
@@ -407,6 +421,11 @@ devices:
ansible_network_os: eos
ansible_connection: network_cli
features:
+ bgp:
+ local_as: True
+ vrf_local_as: True
+ local_as_ibgp: True
+ activate_af: True
initial:
system_mtu: True
ipv4:
@@ -492,6 +511,19 @@ devices:
kind: linux
external:
image: none
+ features:
+ initial:
+ ipv4:
+ unnumbered: True
+ ipv6:
+ lla: True
+ bgp:
+ activate_af: True
+ ipv6_lla: True
+ rfc8950: True
+ local_as: True
+ vrf_local_as: True
+ local_as_ibgp: True
graphite.icon: router
linux:
@@ -592,6 +624,10 @@ devices:
lla: True
ospf:
unnumbered: True
+ bgp:
+ ipv6_lla: True
+ rfc8950: True
+ activate_af: True
clab:
mtu: 1500
runtime: docker
@@ -609,9 +645,9 @@ devices:
interface_name: swp%d
mgmt_if: eth0
libvirt:
- image: CumulusCommunity/cumulus-vx:5.0.1
+ image: CumulusCommunity/cumulus-vx:5.2.0
virtualbox:
- image: CumulusCommunity/cumulus-vx:5.0.1
+ image: CumulusCommunity/cumulus-vx:5.2.0
group_vars:
ansible_user: cumulus
ansible_ssh_pass: cumulus
@@ -624,6 +660,10 @@ devices:
unnumbered: True
ipv6:
lla: True
+ bgp:
+ ipv6_lla: True
+ rfc8950: True
+ activate_af: True
ospf:
unnumbered: True
clab:
@@ -652,10 +692,6 @@ devices:
srgb_range_start: 500000
srgb_range_size: 1000
ipv6_sid_offset: 100
- bgp:
- address_families:
- ibgp: [ ipv4, ipv6 ]
- ebgp: [ ipv4, ipv6 ]
bfd: # SR Linux supports lower BFD timers than the global default
min_tx: 100
min_rx: 100
@@ -672,7 +708,7 @@ devices:
initial:
system_mtu: True
ipv4:
- unnumbered: False
+ unnumbered: True
ipv6:
lla: True
vlan:
@@ -680,8 +716,15 @@ devices:
svi_interface_name: "irb0.{vlan}"
subif_name: "{ifname}.{vlan.access_id}"
mixed_trunk: True
+ bgp:
+ local_as: True
+ vrf_local_as: True
+ local_as_ibgp: True
+ activate_af: True
+ ipv6_lla: True
+ rfc8950: True
vxlan:
- requires: [ evpn ]
+ requires: [ evpn, vrf ]
ospf:
unnumbered: False
isis:
@@ -771,6 +814,9 @@ devices:
interface_name: ethernet1/1/%d
mgmt_if: mgmt1/1/1
features:
+ bgp:
+ local_as: True
+ vrf_local_as: True
vlan:
model: switch
svi_interface_name: virtual-network{vlan}
diff --git a/tests/integration/bgp/default-af/topology.yml b/tests/integration/bgp/default-af/topology.yml
new file mode 100644
index 000000000..db0e839ec
--- /dev/null
+++ b/tests/integration/bgp/default-af/topology.yml
@@ -0,0 +1,25 @@
+addressing:
+ loopback:
+ ipv6: 2001:db8:0::/48
+ lan:
+ ipv6: 2001:db8:1::/48
+ p2p:
+ ipv6: 2001:db8:2::/48
+
+module: [ ospf, bgp ]
+
+bgp.activate:
+ ipv4: [ ebgp ]
+ ipv6: [ ibgp, ebgp ]
+
+bgp.as: 65000
+
+nodes:
+ r1:
+ r2:
+ r3:
+ bgp.as: 65001
+
+links:
+- r1-r2
+- r2-r3
diff --git a/tests/integration/bgp/dual-stack-ibgp-ebgp.yml b/tests/integration/bgp/dual-stack-ibgp-ebgp.yml
new file mode 100644
index 000000000..6452ae9b0
--- /dev/null
+++ b/tests/integration/bgp/dual-stack-ibgp-ebgp.yml
@@ -0,0 +1,47 @@
+message: |
+ Use this topology to test dual-stack IPv4+IPv6 IBGP+EBGP implementation. The
+ topology requires full IPv6 support, including OSPFv3 implementation.
+
+ When all BGP and OSPF sessions are established, the IPv4 and IPv6 BGP tables on R1
+ should look similar to the one below (please also check the next hops)
+
+ Network Next Hop Metric AIGP LocPref Weight Path
+ * > 10.0.0.1/32 - - - - 0 i
+ * > 10.0.0.2/32 10.0.0.2 0 - 100 0 i Or-ID: 10.0.0.2 C-LST: 10.0.0.3
+ * > 10.0.0.3/32 10.0.0.3 0 - 100 0 i
+ * > 10.0.0.4/32 10.1.0.2 0 - 100 0 65001 i
+ * > 10.0.0.5/32 10.0.0.2 0 - 100 0 65002 i Or-ID: 10.0.0.2 C-LST: 10.0.0.3
+
+ Network Next Hop Metric AIGP LocPref Weight Path
+ * > 2001:db8:0:1::/64 - - - - 0 i
+ * > 2001:db8:0:2::/64 2001:db8:0:2::1 0 - 100 0 i Or-ID: 10.0.0.2 C-LST: 10.0.0.3
+ * > 2001:db8:0:3::/64 2001:db8:0:3::1 0 - 100 0 i
+ * > 2001:db8:0:4::/64 2001:db8:2::2 0 - 100 0 65001 i
+ * > 2001:db8:0:5::/64 2001:db8:0:2::1 0 - 100 0 65002 i Or-ID: 10.0.0.2 C-LST: 10.0.0.3
+addressing:
+ loopback:
+ ipv6: 2001:db8:0::/48
+ lan:
+ ipv6: 2001:db8:1::/48
+ p2p:
+ ipv6: 2001:db8:2::/48
+
+module: [ bgp, ospf ]
+bgp.as: 65000
+
+nodes:
+ r1:
+ r2:
+ rr:
+ bgp.rr: True
+ x1:
+ bgp.as: 65001
+ x2:
+ bgp.as: 65002
+
+links:
+- r1-x1
+- r2-x2
+- r1-rr
+- r2-rr
+- r1-r2
diff --git a/tests/integration/bgp/dual-stack-unnumbered.yml b/tests/integration/bgp/dual-stack-unnumbered.yml
new file mode 100644
index 000000000..48364a951
--- /dev/null
+++ b/tests/integration/bgp/dual-stack-unnumbered.yml
@@ -0,0 +1,82 @@
+message: |
+ Use this topology to test the dual stack unnumbered EBGP sessions.
+
+ When all EBGP sessions are established, the test device should have
+ four IPv4 AF sessions and five IPv6 AF sessions.
+
+ IPv4 Unicast Summary:
+
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt
+ x1(swp1) 4 65001 154 155 0 0 0 00:07:18 1 3
+ x1(swp2) 4 65001 151 152 0 0 0 00:07:18 1 3
+ x2(swp3) 4 65002 151 154 0 0 0 00:07:18 1 3
+ x4(172.31.1.2) 4 65004 151 151 0 0 0 00:07:18 1 3
+
+ Total number of neighbors 4
+
+ IPv6 Unicast Summary:
+
+ Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt
+ x1(swp1) 4 65001 154 155 0 0 0 00:07:18 1 2
+ x2(swp3) 4 65002 151 154 0 0 0 00:07:18 1 2
+ x3(swp4) 4 65003 150 151 0 0 0 00:07:18 1 2
+ x4(swp5) 4 65004 150 150 0 0 0 00:07:17 1 2
+ x4(2001:db8:1::2) 4 65004 150 150 0 0 0 00:07:18 1 2
+
+ Total number of neighbors 5
+
+module: [ bgp ]
+
+addressing:
+ loopback:
+ ipv6: 2001:db8:0::/48
+ p2p:
+ ipv4: False
+ ipv6: False
+
+nodes:
+ test:
+ bgp.as: 65000
+ x1:
+ bgp.as: 65001
+ x2:
+ bgp.as: 65002
+ x3:
+ bgp.as: 65003
+ x4:
+ bgp.as: 65004
+
+links:
+# interface 1 - dual-stack unnumbered
+- test:
+ x1:
+ unnumbered: True
+# interface 2 - activate only IPv4 AF over IPv6 LLA
+- test:
+ x1:
+ prefix:
+ ipv4: True
+# interface 3 - dual-stack unnumbered
+- test:
+ x2:
+ prefix:
+ ipv4: True
+ ipv6: True
+# interface 4 - IPv6 AF over IPv6 LLA
+- test:
+ x3:
+ prefix:
+ ipv6: True
+# interface 5 - IPv6 AF over IPv6 LLA + IPv4 AF over IPv4 numbered
+- test:
+ x4:
+ prefix:
+ ipv4: 172.31.1.0/24
+ ipv6: True
+# interface 6 - IPv6 AF over IPv6 numbered, no IPv4 session/AF
+- test:
+ x4:
+ prefix:
+ ipv4: True
+ ipv6: 2001:db8:1::/64
+
diff --git a/tests/integration/bgp/ibgp-ebgp.yml b/tests/integration/bgp/ibgp-ebgp.yml
new file mode 100644
index 000000000..00505bba4
--- /dev/null
+++ b/tests/integration/bgp/ibgp-ebgp.yml
@@ -0,0 +1,31 @@
+message: |
+ Use this topology to test the baseline IPv4 IBGP+EBGP implementation.
+
+ When all BGP and OSPF sessions are established, the BGP table on R1 should look
+ similar to the one below (please also check the next hops)
+
+ Network Next Hop Metric LocPrf Weight Path
+ *> 10.0.0.1/32 0.0.0.0 0 32768 i
+ *>i10.0.0.2/32 10.0.0.2 0 100 0 i
+ *>i10.0.0.3/32 10.0.0.3 0 100 0 i
+ *> 10.0.0.4/32 10.1.0.2 0 0 65001 i
+ *>i10.0.0.5/32 10.0.0.2 0 100 0 65002 i
+module: [ bgp, ospf ]
+bgp.as: 65000
+
+nodes:
+ r1:
+ r2:
+ rr:
+ bgp.rr: True
+ x1:
+ bgp.as: 65001
+ x2:
+ bgp.as: 65002
+
+links:
+- r1-x1
+- r2-x2
+- r1-rr
+- r2-rr
+- r1-r2
diff --git a/tests/integration/bgp/ipv4-unnumbered.yml b/tests/integration/bgp/ipv4-unnumbered.yml
new file mode 100644
index 000000000..8a50379e4
--- /dev/null
+++ b/tests/integration/bgp/ipv4-unnumbered.yml
@@ -0,0 +1,36 @@
+message: |
+ Use this topology to test the IPv4-only unnumbered EBGP sessions.
+
+ When all BGP and OSPF sessions are established, the BGP table on R1 should look
+ similar to the one below (please also check the next hops)
+
+ Network Next Hop Metric LocPrf Weight Path
+ *> 10.0.0.1/32 0.0.0.0 0 32768 i
+ *>i10.0.0.2/32 10.0.0.2 0 100 0 i
+ *>i10.0.0.3/32 10.0.0.3 0 100 0 i
+ *> 10.0.0.4/32 swp1 0 0 65001 i
+ *>i10.0.0.5/32 10.0.0.2 0 100 0 65002 i
+module: [ bgp, ospf ]
+bgp.as: 65000
+
+nodes:
+ r1:
+ r2:
+ rr:
+ bgp.rr: True
+ x1:
+ bgp.as: 65001
+ x2:
+ bgp.as: 65002
+
+links:
+- r1:
+ x1:
+ unnumbered: True
+- r2:
+ x2:
+ prefix:
+ ipv4: True
+- r1-rr
+- r2-rr
+- r1-r2
diff --git a/tests/integration/bgp/local-as/ibgp-local-as.yml b/tests/integration/bgp/local-as/ibgp-local-as.yml
new file mode 100644
index 000000000..de95bac78
--- /dev/null
+++ b/tests/integration/bgp/local-as/ibgp-local-as.yml
@@ -0,0 +1,31 @@
+message: |
+ This lab topology checks the IBGP local-as functionality -- creating BGP direct
+ session between two autonomous systems and using BGP local-as to make it a
+ fake IBGP session.
+
+ You should be able to see BGP routes from all four loopbacks on E1 and E2, and
+ ping between loopback interfaces of E1 and E2.
+defaults.device: iosv
+
+module: [ bgp,ospf ]
+
+nodes:
+ r1:
+ bgp.as: 65000
+ bgp.rr: True
+ r2:
+ bgp.as: 65001
+ bgp.rr: True
+ e1:
+ bgp.as: 65000
+ e2:
+ bgp.as: 65001
+
+links:
+- r1:
+ bgp.local_as: 666
+ r2:
+ bgp.local_as: 666
+ bgp.advertise: True
+- r1-e1
+- r2-e2
diff --git a/tests/integration/bgp/local-as/leaf-spine.yml b/tests/integration/bgp/local-as/leaf-spine.yml
new file mode 100644
index 000000000..0e5aec2aa
--- /dev/null
+++ b/tests/integration/bgp/local-as/leaf-spine.yml
@@ -0,0 +1,35 @@
+---
+message: |
+ This topology creates a leaf-and-spine topology with EBGP underlay sessions
+ and IBGP sessions between loopback interfaces.
+
+ You should be able to ping between all loopback interfaces. IPv4 IBGP
+ neighbor sessions are configured but will probably be idle (no AF configured
+ on the transport session).
+
+ All IPv4 BGP routes should be EBGP routes.
+defaults.device: eos
+
+module: [ bgp ]
+
+bgp.as: 65000
+bgp.activate:
+ ipv4: [ ebgp ]
+
+nodes:
+ s1:
+ bgp.local_as: 65101
+ bgp.rr: True
+ s2:
+ bgp.local_as: 65102
+ bgp.rr: True
+ l1:
+ bgp.local_as: 65201
+ l2:
+ bgp.local_as: 65202
+
+links:
+- s1-l1
+- s1-l2
+- s2-l1
+- s2-l2
diff --git a/tests/integration/bgp/local-as/simple.yml b/tests/integration/bgp/local-as/simple.yml
new file mode 100644
index 000000000..982d9ae78
--- /dev/null
+++ b/tests/integration/bgp/local-as/simple.yml
@@ -0,0 +1,15 @@
+defaults.device: iosv
+
+module: [ bgp ]
+
+nodes:
+ r1:
+ bgp.as: 65000
+ r2:
+ bgp.as: 65001
+
+links:
+- r1:
+ bgp.local_as: 65100
+ r2:
+ bgp.local_as: 65101
diff --git a/tests/integration/bgp/local-as/vrf-local-as-ebgp.yml b/tests/integration/bgp/local-as/vrf-local-as-ebgp.yml
new file mode 100644
index 000000000..3517e6d05
--- /dev/null
+++ b/tests/integration/bgp/local-as/vrf-local-as-ebgp.yml
@@ -0,0 +1,47 @@
+defaults.device: eos
+
+vrfs:
+ red:
+ blue:
+
+vlans:
+ red:
+ mode: route
+ vrf: red
+ blue:
+ mode: route
+ vrf: blue
+ vrf-leak:
+ mode: route
+
+module: [ vlan, vrf, bgp ]
+
+nodes:
+ r1:
+ bgp.as: 65100
+ r2:
+ bgp.as: 65000
+ vrfs:
+ red:
+ bgp.router_id: 172.31.0.1
+ blue:
+ bgp.router_id: 172.32.0.2
+ r3:
+ bgp.as: 65101
+
+links:
+- r1:
+ r2:
+ vlan.trunk: [ red, blue ]
+- r2:
+ r3:
+ vlan.trunk: [ red, blue ]
+- interfaces: # VRF route leaking between red and blue, using BGP peering
+ - node: r2
+ vrf: red
+ bgp.local_as: 65001
+ vlan.access: vrf-leak
+ - node: r2
+ vrf: blue
+ bgp.local_as: 65002
+ vlan.access: vrf-leak
diff --git a/tests/integration/multivendor-bgp-ipv6-only.yml b/tests/integration/bgp/multivendor-bgp-ipv6-only.yml
similarity index 100%
rename from tests/integration/multivendor-bgp-ipv6-only.yml
rename to tests/integration/bgp/multivendor-bgp-ipv6-only.yml
diff --git a/tests/integration/multivendor-bgp-ipv6.yml b/tests/integration/bgp/multivendor-bgp-ipv6.yml
similarity index 100%
rename from tests/integration/multivendor-bgp-ipv6.yml
rename to tests/integration/bgp/multivendor-bgp-ipv6.yml
diff --git a/tests/integration/multivendor-bgp.yml b/tests/integration/bgp/multivendor-bgp.yml
similarity index 100%
rename from tests/integration/multivendor-bgp.yml
rename to tests/integration/bgp/multivendor-bgp.yml
diff --git a/tests/integration/evpn/vxlan-bridging-ibgp-over-ebgp.yml b/tests/integration/evpn/vxlan-bridging-ibgp-over-ebgp.yml
index 0bbd06403..259967605 100644
--- a/tests/integration/evpn/vxlan-bridging-ibgp-over-ebgp.yml
+++ b/tests/integration/evpn/vxlan-bridging-ibgp-over-ebgp.yml
@@ -1,4 +1,17 @@
-## Testing topology for iBGP over eBGP with VyOS
+message: |
+ This test case builds a leaf-and-spine fabric with VLAN-over-VXLAN
+ bridging using IBGP-over-EBGP design.
+
+ The leaf switches are doing VLAN/VXLAN encap/decap, the
+ spine switches are IP routers running OSPF, BGP, and EVPN.
+
+ * h1 and h2 should be able to ping each other
+ * h3 and h4 should be able to ping each other
+
+ Please note it might take a while for the lab to work due to
+ STP learning phase
+
+ To change the devices under test, use netlab up -d parameter
groups:
hosts:
@@ -6,10 +19,10 @@ groups:
device: linux
switches:
members: [ spine, leaf1, leaf2 ]
- device: vyos
- module: [ vlan,vxlan,bgp,evpn ]
+ module: [ vlan, vxlan, bgp, evpn ]
bgp.as: 64999
+bgp.next_hop_self: False # Required by Arista implementation of IBGP-over-EBGP EVPN design
vlans:
red:
@@ -17,17 +30,18 @@ vlans:
blue:
mode: bridge
-plugin: [ ebgp-local_as ]
+bgp.activate:
+ ipv4: [ ebgp ]
nodes:
spine:
bgp:
- underlay_as: 65000
+ local_as: 65000
rr: True
leaf1:
- bgp.underlay_as: 65001
+ bgp.local_as: 65001
leaf2:
- bgp.underlay_as: 65002
+ bgp.local_as: 65002
h1:
h2:
h3:
diff --git a/tests/integration/evpn/vxlan-bridging-leaf-spine.yml b/tests/integration/evpn/vxlan-bridging-leaf-spine.yml
new file mode 100644
index 000000000..30fd85013
--- /dev/null
+++ b/tests/integration/evpn/vxlan-bridging-leaf-spine.yml
@@ -0,0 +1,58 @@
+message: |
+ This test case builds a leaf-and-spine fabric with VLAN-over-VXLAN
+ bridging. The leaf switches are doing VLAN/VXLAN encap/decap, the
+ spine switches are IP routers running OSPF, BGP, and EVPN.
+
+ * h1 and h2 should be able to ping each other
+ * h3 and h4 should be able to ping each other
+
+ Please note it might take a while for the lab to work due to
+ STP learning phase
+
+ To change the devices under test, use netlab up -d parameter
+groups:
+ hosts:
+ members: [ h1, h2, h3, h4 ]
+ device: linux
+ leafs:
+ members: [ l1, l2 ]
+ module: [ vlan,vxlan,ospf,bgp,evpn ]
+ spines:
+ members: [ s1, s2 ]
+ module: [ ospf,bgp,evpn ]
+
+bgp.as: 65000
+
+vlans:
+ red:
+ mode: bridge
+ blue:
+ mode: bridge
+
+nodes:
+ h1:
+ h2:
+ h3:
+ h4:
+ l1:
+ l2:
+ s1:
+ s2:
+
+links:
+- h1:
+ l1:
+ vlan.access: red
+- h2:
+ l2:
+ vlan.access: red
+- h3:
+ l1:
+ vlan.access: blue
+- h4:
+ l2:
+ vlan.access: blue
+- l1-s1
+- l1-s2
+- l2-s1
+- l2-s2
diff --git a/tests/integration/ios-bridging.yml b/tests/integration/ios-bridging.yml
deleted file mode 100644
index d1c78c4de..000000000
--- a/tests/integration/ios-bridging.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-defaults.device: iosv
-
-nodes:
- r1:
- h1:
- h2:
-
-links:
-- prefix: False
- r1:
- h1:
- ipv4: 10.42.42.1/24
-- prefix: False
- r1:
- h2:
- ipv4: 10.42.42.2/24
diff --git a/tests/integration/ospf/ospfv2/multi-area.yml b/tests/integration/ospf/ospfv2/multi-area.yml
new file mode 100644
index 000000000..bbf5feaaa
--- /dev/null
+++ b/tests/integration/ospf/ospfv2/multi-area.yml
@@ -0,0 +1,28 @@
+module: [ ospf ]
+
+ospf.area: 1
+
+nodes:
+ bb:
+ ospf.area: 0
+ abr:
+ ospf.area: 0
+ r1:
+ r2:
+ r3:
+
+links:
+- bb-abr
+- r1-r2-r3
+- r1:
+ abr:
+ ospf.area: 1
+- r2:
+ abr:
+ ospf.area: 1
+- r3:
+ abr:
+ ospf.area: 1
+- r1
+- r2
+- r3
diff --git a/tests/integration/vlan/vlan-vrf-route-leaking.yaml b/tests/integration/vlan/vlan-vrf-route-leaking.yaml
new file mode 100644
index 000000000..76001a6e8
--- /dev/null
+++ b/tests/integration/vlan/vlan-vrf-route-leaking.yaml
@@ -0,0 +1,82 @@
+#
+# VRF lite implementation with VLAN trunks, including ebgp peering between vrfs locally
+#
+# * h1 and h2 should be able to ping each other, as well as h3 and h4
+# * h3 and h4 should be able to ping each other, as well as h1 and h2
+#
+# A device has to support the following features to pass this test case:
+#
+# * Routed VLAN interfaces
+# * VRFs
+# * OSPF in VRFs
+# * BGP unnumbered using ipv6 lla
+#
+# Please note it might take a while for the lab to work due to
+# STP and OSPF setup phase
+#
+groups:
+ routers:
+ members: [ r1,r2,r3 ]
+ module: [ vlan,vrf,ospf ]
+ hosts:
+ device: linux
+ members: [ h1,h2,h3,h4 ]
+
+# plugin: [ ebgp-local_as ] old plugin, obsolete
+
+vrfs:
+ red:
+ blue:
+
+vlans:
+ red:
+ mode: route
+ vrf: red
+ blue:
+ mode: route
+ vrf: blue
+
+nodes:
+ r1:
+ r2:
+ module: [ vlan,vrf,ospf,bgp ]
+ bgp.as: 65000
+ r3:
+ h1:
+ h2:
+ h3:
+ h4:
+
+links:
+- r1:
+ r2:
+ vlan.trunk: [ red, blue ]
+- r2:
+ r3:
+ vlan.trunk: [ red, blue ]
+- interfaces: # VRF route leaking between red and blue on r2, using eBGP peering and rfc8950 ipv6 next hops with ipv4 prefixes
+ - node: r2
+ vrf: red
+ ipv6: True
+ ipv4: True
+ bgp.local_as: 65001
+ # vlan.access: vrf-leak
+ - node: r2
+ vrf: blue
+ ipv6: True
+ ipv4: True
+ bgp.local_as: 65002
+ # vlan.access: vrf-leak
+ role: external
+- h1:
+ r1:
+ vlan.access: red
+- h3:
+ r1:
+ vlan.access: blue
+- h2:
+ r3:
+ vlan.access: red
+- h4:
+ r3:
+ vlan.access: blue
diff --git a/tests/topology/expected/6pe.yml b/tests/topology/expected/6pe.yml
index d55d1c0b4..529ca41cd 100644
--- a/tests/topology/expected/6pe.yml
+++ b/tests/topology/expected/6pe.yml
@@ -170,7 +170,10 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ifindex: 1
ipv4: 172.31.0.2
ipv6: 2001:db8:2::2
@@ -221,7 +224,10 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ifindex: 1
ipv4: 172.31.1.2
ipv6: 2001:db8:2:1::2
@@ -331,11 +337,17 @@ nodes:
ipv6: true
neighbors:
- 6pe: true
+ activate:
+ ipv4: true
+ ipv6: true
as: 65000
ipv4: 10.0.0.2
name: pe2
type: ibgp
- - as: 65001
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65001
ifindex: 1
ipv4: 172.31.0.1
ipv6: 2001:db8:2::1
@@ -416,11 +428,17 @@ nodes:
ipv6: true
neighbors:
- 6pe: true
+ activate:
+ ipv4: true
+ ipv6: true
as: 65000
ipv4: 10.0.0.1
name: pe1
type: ibgp
- - as: 65002
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65002
ifindex: 1
ipv4: 172.31.1.1
ipv6: 2001:db8:2:1::1
diff --git a/tests/topology/expected/addressing-ipv6-only.yml b/tests/topology/expected/addressing-ipv6-only.yml
index e3db1046b..b1aa08f2e 100644
--- a/tests/topology/expected/addressing-ipv6-only.yml
+++ b/tests/topology/expected/addressing-ipv6-only.yml
@@ -119,15 +119,21 @@ nodes:
- extended
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:15::1
name: r2
type: ibgp
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:2a::1
name: r3
type: ibgp
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:1::1
name: r4
type: ibgp
@@ -227,15 +233,21 @@ nodes:
- extended
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:7::1
name: r1
type: ibgp
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:2a::1
name: r3
type: ibgp
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:1::1
name: r4
type: ibgp
@@ -332,15 +344,21 @@ nodes:
- extended
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:7::1
name: r1
type: ibgp
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:15::1
name: r2
type: ibgp
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:1::1
name: r4
type: ibgp
@@ -415,15 +433,21 @@ nodes:
- extended
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:7::1
name: r1
type: ibgp
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:15::1
name: r2
type: ibgp
- - as: 65000
+ - activate:
+ ipv6: true
+ as: 65000
ipv6: 2001:db8:0:2a::1
name: r3
type: ibgp
diff --git a/tests/topology/expected/bgp-anycast.yml b/tests/topology/expected/bgp-anycast.yml
index a415f5c17..bae49082f 100644
--- a/tests/topology/expected/bgp-anycast.yml
+++ b/tests/topology/expected/bgp-anycast.yml
@@ -57,7 +57,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: l2
type: ibgp
@@ -111,7 +113,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: l1
type: ibgp
diff --git a/tests/topology/expected/bgp-autogroup.yml b/tests/topology/expected/bgp-autogroup.yml
index 9d6632849..996dd4d04 100644
--- a/tests/topology/expected/bgp-autogroup.yml
+++ b/tests/topology/expected/bgp-autogroup.yml
@@ -190,15 +190,21 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65101
+ - activate:
+ ipv4: true
+ as: 65101
ipv4: 10.0.0.6
name: a2
type: ibgp
- - as: 65101
+ - activate:
+ ipv4: true
+ as: 65101
ipv4: 10.0.0.7
name: a3
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ifindex: 1
ipv4: 10.1.0.14
name: l2
@@ -243,15 +249,21 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65101
+ - activate:
+ ipv4: true
+ as: 65101
ipv4: 10.0.0.5
name: a1
type: ibgp
- - as: 65101
+ - activate:
+ ipv4: true
+ as: 65101
ipv4: 10.0.0.7
name: a3
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ifindex: 1
ipv4: 10.1.0.18
name: l2
@@ -296,15 +308,21 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65101
+ - activate:
+ ipv4: true
+ as: 65101
ipv4: 10.0.0.5
name: a1
type: ibgp
- - as: 65101
+ - activate:
+ ipv4: true
+ as: 65101
ipv4: 10.0.0.6
name: a2
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ifindex: 1
ipv4: 10.1.0.22
name: l3
@@ -349,7 +367,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.4
name: s1
rr: true
@@ -403,17 +423,23 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.4
name: s1
rr: true
type: ibgp
- - as: 65101
+ - activate:
+ ipv4: true
+ as: 65101
ifindex: 2
ipv4: 10.1.0.13
name: a1
type: ebgp
- - as: 65101
+ - activate:
+ ipv4: true
+ as: 65101
ifindex: 3
ipv4: 10.1.0.17
name: a2
@@ -489,12 +515,16 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.4
name: s1
rr: true
type: ibgp
- - as: 65101
+ - activate:
+ ipv4: true
+ as: 65101
ifindex: 2
ipv4: 10.1.0.21
name: a3
@@ -559,15 +589,21 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: l1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: l2
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: l3
type: ibgp
diff --git a/tests/topology/expected/bgp-community.yml b/tests/topology/expected/bgp-community.yml
index 813ba3a6b..b836bef77 100644
--- a/tests/topology/expected/bgp-community.yml
+++ b/tests/topology/expected/bgp-community.yml
@@ -79,14 +79,25 @@ nodes:
- standard
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: r2
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: r3
type: ibgp
+ - activate:
+ ipv4: true
+ as: 65001
+ ifindex: 1
+ ipv4: 10.1.0.2
+ name: r3
+ type: ebgp
next_hop_self: true
router_id: 10.0.0.1
box: cisco/iosv
@@ -137,14 +148,26 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: r1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: r3
type: ibgp
+ - activate:
+ ipv4: true
+ as: 65001
+ ifindex: 1
+ ipv4: 10.1.0.6
+ local_as: 65002
+ name: r3
+ type: ebgp
next_hop_self: true
router_id: 10.0.0.2
box: cisco/iosv
@@ -197,14 +220,34 @@ nodes:
ipv4: true
local_as: 65001
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: r1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: r2
type: ibgp
+ - activate:
+ ipv4: true
+ as: 65000
+ ifindex: 1
+ ipv4: 10.1.0.1
+ local_as: 65001
+ name: r1
+ type: ebgp
+ - activate:
+ ipv4: true
+ as: 65002
+ ifindex: 2
+ ipv4: 10.1.0.5
+ local_as: 65001
+ name: r2
+ type: ebgp
next_hop_self: true
router_id: 10.0.0.3
box: cisco/iosv
@@ -235,7 +278,9 @@ nodes:
linkindex: 2
name: r3 -> r2
neighbors:
- - ifname: GigabitEthernet0/1
+ - bgp:
+ local_as: 65002
+ ifname: GigabitEthernet0/1
ipv4: 10.1.0.5/30
node: r2
ospf:
diff --git a/tests/topology/expected/bgp-ibgp.yml b/tests/topology/expected/bgp-ibgp.yml
index 50e262030..848217bb1 100644
--- a/tests/topology/expected/bgp-ibgp.yml
+++ b/tests/topology/expected/bgp-ibgp.yml
@@ -122,12 +122,16 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: s1
rr: true
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.4
name: s2
rr: true
@@ -198,12 +202,16 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: s1
rr: true
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.4
name: s2
rr: true
@@ -274,15 +282,21 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: l1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: l2
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.4
name: s2
rr: true
@@ -355,15 +369,21 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: l1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: l2
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: s1
rr: true
diff --git a/tests/topology/expected/bgp-members.yml b/tests/topology/expected/bgp-members.yml
index 246ee4531..344af3e59 100644
--- a/tests/topology/expected/bgp-members.yml
+++ b/tests/topology/expected/bgp-members.yml
@@ -183,7 +183,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ifindex: 1
ipv4: 10.1.0.18
name: pe1
@@ -228,7 +230,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ifindex: 1
ipv4: 10.1.0.22
name: pe2
@@ -273,17 +277,23 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: rr1
rr: true
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: rr2
rr: true
type: ibgp
- - as: 65001
+ - activate:
+ ipv4: true
+ as: 65001
ifindex: 3
ipv4: 10.1.0.17
name: e1
@@ -348,17 +358,23 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: rr1
rr: true
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: rr2
rr: true
type: ibgp
- - as: 65002
+ - activate:
+ ipv4: true
+ as: 65002
ifindex: 3
ipv4: 10.1.0.21
name: e2
@@ -423,16 +439,22 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: rr2
rr: true
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: pe1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.4
name: pe2
type: ibgp
@@ -487,16 +509,22 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: rr1
rr: true
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: pe1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.4
name: pe2
type: ibgp
diff --git a/tests/topology/expected/bgp-sessions.yml b/tests/topology/expected/bgp-sessions.yml
new file mode 100644
index 000000000..1c80689b6
--- /dev/null
+++ b/tests/topology/expected/bgp-sessions.yml
@@ -0,0 +1,304 @@
+bgp:
+ advertise_loopback: true
+ as: 65000
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ next_hop_self: true
+ sessions:
+ ipv4:
+ - ebgp
+ ipv6:
+ - ibgp
+ - ebgp
+groups:
+ as65000:
+ members:
+ - r1
+ - r2
+ as65100:
+ members:
+ - x1
+input:
+- topology/input/bgp-sessions.yml
+- package:topology-defaults.yml
+links:
+- interfaces:
+ - ifindex: 1
+ ipv4: 10.1.0.1/30
+ ipv6: 2001:db8:1::1/64
+ node: r1
+ - ifindex: 1
+ ipv4: 10.1.0.2/30
+ ipv6: 2001:db8:1::2/64
+ node: x1
+ left:
+ ifname: Ethernet1
+ ipv4: 10.1.0.1/30
+ ipv6: 2001:db8:1::1/64
+ node: r1
+ linkindex: 1
+ name: r1 - x1
+ node_count: 2
+ prefix:
+ ipv4: 10.1.0.0/30
+ ipv6: 2001:db8:1::/64
+ right:
+ ifname: Ethernet1
+ ipv4: 10.1.0.2/30
+ ipv6: 2001:db8:1::2/64
+ node: x1
+ role: external
+ type: p2p
+- interfaces:
+ - ifindex: 2
+ ipv4: 10.1.0.5/30
+ ipv6: 2001:db8:1:1::1/64
+ node: r1
+ - ifindex: 1
+ ipv4: 10.1.0.6/30
+ ipv6: 2001:db8:1:1::2/64
+ node: r2
+ left:
+ ifname: Ethernet2
+ ipv4: 10.1.0.5/30
+ ipv6: 2001:db8:1:1::1/64
+ node: r1
+ linkindex: 2
+ name: r1 - r2
+ node_count: 2
+ prefix:
+ ipv4: 10.1.0.4/30
+ ipv6: 2001:db8:1:1::/64
+ right:
+ ifname: Ethernet1
+ ipv4: 10.1.0.6/30
+ ipv6: 2001:db8:1:1::2/64
+ node: r2
+ type: p2p
+module:
+- bgp
+- ospf
+name: input
+nodes:
+ r1:
+ af:
+ ipv4: true
+ ipv6: true
+ bgp:
+ advertise_loopback: true
+ as: 65000
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ ipv4: true
+ ipv6: true
+ neighbors:
+ - activate:
+ ipv6: true
+ as: 65000
+ ipv6: 2001:db8:0:3::1
+ name: r2
+ type: ibgp
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65100
+ ifindex: 1
+ ipv4: 10.1.0.2
+ ipv6: 2001:db8:1::2
+ name: x1
+ type: ebgp
+ next_hop_self: true
+ router_id: 10.0.0.2
+ sessions:
+ ipv4:
+ - ebgp
+ ipv6:
+ - ibgp
+ - ebgp
+ box: arista/veos
+ device: eos
+ id: 2
+ interfaces:
+ - ifindex: 1
+ ifname: Ethernet1
+ ipv4: 10.1.0.1/30
+ ipv6: 2001:db8:1::1/64
+ linkindex: 1
+ name: r1 -> x1
+ neighbors:
+ - ifname: Ethernet1
+ ipv4: 10.1.0.2/30
+ ipv6: 2001:db8:1::2/64
+ node: x1
+ role: external
+ type: p2p
+ - ifindex: 2
+ ifname: Ethernet2
+ ipv4: 10.1.0.5/30
+ ipv6: 2001:db8:1:1::1/64
+ linkindex: 2
+ name: r1 -> r2
+ neighbors:
+ - ifname: Ethernet1
+ ipv4: 10.1.0.6/30
+ ipv6: 2001:db8:1:1::2/64
+ node: r2
+ ospf:
+ area: 0.0.0.0
+ network_type: point-to-point
+ passive: false
+ type: p2p
+ loopback:
+ ipv4: 10.0.0.2/32
+ ipv6: 2001:db8:0:2::1/64
+ mgmt:
+ ifname: Management1
+ ipv4: 192.168.121.102
+ mac: 08-4F-A9-00-00-02
+ module:
+ - bgp
+ - ospf
+ name: r1
+ ospf:
+ af:
+ ipv4: true
+ ipv6: true
+ area: 0.0.0.0
+ router_id: 10.0.0.2
+ r2:
+ af:
+ ipv4: true
+ ipv6: true
+ bgp:
+ advertise_loopback: true
+ as: 65000
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ ipv6: true
+ neighbors:
+ - activate:
+ ipv6: true
+ as: 65000
+ ipv6: 2001:db8:0:2::1
+ name: r1
+ type: ibgp
+ next_hop_self: true
+ router_id: 10.0.0.3
+ sessions:
+ ipv4:
+ - ebgp
+ ipv6:
+ - ibgp
+ - ebgp
+ box: arista/veos
+ device: eos
+ id: 3
+ interfaces:
+ - ifindex: 1
+ ifname: Ethernet1
+ ipv4: 10.1.0.6/30
+ ipv6: 2001:db8:1:1::2/64
+ linkindex: 2
+ name: r2 -> r1
+ neighbors:
+ - ifname: Ethernet2
+ ipv4: 10.1.0.5/30
+ ipv6: 2001:db8:1:1::1/64
+ node: r1
+ ospf:
+ area: 0.0.0.0
+ network_type: point-to-point
+ passive: false
+ type: p2p
+ loopback:
+ ipv4: 10.0.0.3/32
+ ipv6: 2001:db8:0:3::1/64
+ mgmt:
+ ifname: Management1
+ ipv4: 192.168.121.103
+ mac: 08-4F-A9-00-00-03
+ module:
+ - bgp
+ - ospf
+ name: r2
+ ospf:
+ af:
+ ipv4: true
+ ipv6: true
+ area: 0.0.0.0
+ router_id: 10.0.0.3
+ x1:
+ af:
+ ipv4: true
+ ipv6: true
+ bgp:
+ advertise_loopback: true
+ as: 65100
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ ipv4: true
+ ipv6: true
+ neighbors:
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
+ ifindex: 1
+ ipv4: 10.1.0.1
+ ipv6: 2001:db8:1::1
+ name: r1
+ type: ebgp
+ next_hop_self: true
+ router_id: 10.0.0.1
+ sessions:
+ ipv4:
+ - ebgp
+ ipv6:
+ - ibgp
+ - ebgp
+ box: arista/veos
+ device: eos
+ id: 1
+ interfaces:
+ - ifindex: 1
+ ifname: Ethernet1
+ ipv4: 10.1.0.2/30
+ ipv6: 2001:db8:1::2/64
+ linkindex: 1
+ name: x1 -> r1
+ neighbors:
+ - ifname: Ethernet1
+ ipv4: 10.1.0.1/30
+ ipv6: 2001:db8:1::1/64
+ node: r1
+ role: external
+ type: p2p
+ loopback:
+ ipv4: 10.0.0.1/32
+ ipv6: 2001:db8:0:1::1/64
+ mgmt:
+ ifname: Management1
+ ipv4: 192.168.121.101
+ mac: 08-4F-A9-00-00-01
+ module:
+ - bgp
+ name: x1
+ospf:
+ area: 0.0.0.0
+provider: libvirt
diff --git a/tests/topology/expected/bgp-unnumbered-dual-stack.yml b/tests/topology/expected/bgp-unnumbered-dual-stack.yml
new file mode 100644
index 000000000..081896b93
--- /dev/null
+++ b/tests/topology/expected/bgp-unnumbered-dual-stack.yml
@@ -0,0 +1,609 @@
+bgp:
+ advertise_loopback: true
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ next_hop_self: true
+groups:
+ as65000:
+ members:
+ - test
+ as65001:
+ members:
+ - x1
+ as65002:
+ members:
+ - x2
+ as65003:
+ members:
+ - x3
+ as65004:
+ members:
+ - x4
+input:
+- topology/input/bgp-unnumbered-dual-stack.yml
+- package:topology-defaults.yml
+links:
+- interfaces:
+ - ifindex: 1
+ ipv4: true
+ ipv6: true
+ node: test
+ - ifindex: 1
+ ipv4: true
+ ipv6: true
+ node: x1
+ left:
+ ifname: swp1
+ ipv4: true
+ ipv6: true
+ node: test
+ linkindex: 1
+ name: test - x1
+ node_count: 2
+ right:
+ ifname: swp1
+ ipv4: true
+ ipv6: true
+ node: x1
+ role: external
+ type: p2p
+ unnumbered: true
+- interfaces:
+ - ifindex: 2
+ ipv4: true
+ node: test
+ - ifindex: 2
+ ipv4: true
+ node: x1
+ left:
+ ifname: swp2
+ ipv4: true
+ node: test
+ linkindex: 2
+ name: test - x1
+ node_count: 2
+ prefix:
+ ipv4: true
+ right:
+ ifname: swp2
+ ipv4: true
+ node: x1
+ role: external
+ type: p2p
+- interfaces:
+ - ifindex: 3
+ ipv4: true
+ ipv6: true
+ node: test
+ - ifindex: 1
+ ipv4: true
+ ipv6: true
+ node: x2
+ left:
+ ifname: swp3
+ ipv4: true
+ ipv6: true
+ node: test
+ linkindex: 3
+ name: test - x2
+ node_count: 2
+ prefix:
+ ipv4: true
+ ipv6: true
+ right:
+ ifname: swp1
+ ipv4: true
+ ipv6: true
+ node: x2
+ role: external
+ type: p2p
+- interfaces:
+ - ifindex: 4
+ ipv6: true
+ node: test
+ - ifindex: 1
+ ipv6: true
+ node: x3
+ left:
+ ifname: swp4
+ ipv6: true
+ node: test
+ linkindex: 4
+ name: test - x3
+ node_count: 2
+ prefix:
+ ipv6: true
+ right:
+ ifname: swp1
+ ipv6: true
+ node: x3
+ role: external
+ type: p2p
+- interfaces:
+ - ifindex: 5
+ ipv4: 172.31.1.1/24
+ ipv6: true
+ node: test
+ - ifindex: 1
+ ipv4: 172.31.1.2/24
+ ipv6: true
+ node: x4
+ left:
+ ifname: swp5
+ ipv4: 172.31.1.1/24
+ ipv6: true
+ node: test
+ linkindex: 5
+ name: test - x4
+ node_count: 2
+ prefix:
+ ipv4: 172.31.1.0/24
+ ipv6: true
+ right:
+ ifname: swp1
+ ipv4: 172.31.1.2/24
+ ipv6: true
+ node: x4
+ role: external
+ type: p2p
+- interfaces:
+ - ifindex: 6
+ ipv4: true
+ ipv6: 2001:db8:1::1/64
+ node: test
+ - ifindex: 2
+ ipv4: true
+ ipv6: 2001:db8:1::2/64
+ node: x4
+ left:
+ ifname: swp6
+ ipv4: true
+ ipv6: 2001:db8:1::1/64
+ node: test
+ linkindex: 6
+ name: test - x4
+ node_count: 2
+ prefix:
+ ipv4: true
+ ipv6: 2001:db8:1::/64
+ right:
+ ifname: swp2
+ ipv4: true
+ ipv6: 2001:db8:1::2/64
+ node: x4
+ role: external
+ type: p2p
+module:
+- bgp
+name: input
+nodes:
+ test:
+ af:
+ ipv4: true
+ ipv6: true
+ bgp:
+ advertise_loopback: true
+ as: 65000
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ ipv4: true
+ ipv6: true
+ neighbors:
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65001
+ ifindex: 1
+ ipv4: true
+ ipv4_rfc8950: true
+ ipv6: true
+ local_if: swp1
+ name: x1
+ type: ebgp
+ - activate:
+ ipv4: true
+ as: 65001
+ ifindex: 2
+ ipv4: true
+ ipv4_rfc8950: true
+ local_if: swp2
+ name: x1
+ type: ebgp
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65002
+ ifindex: 3
+ ipv4: true
+ ipv4_rfc8950: true
+ ipv6: true
+ local_if: swp3
+ name: x2
+ type: ebgp
+ - activate:
+ ipv6: true
+ as: 65003
+ ifindex: 4
+ ipv6: true
+ local_if: swp4
+ name: x3
+ type: ebgp
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65004
+ ifindex: 5
+ ipv4: 172.31.1.2
+ ipv6: true
+ local_if: swp5
+ name: x4
+ type: ebgp
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65004
+ ifindex: 6
+ ipv4: true
+ ipv6: 2001:db8:1::2
+ name: x4
+ type: ebgp
+ next_hop_self: true
+ router_id: 10.0.0.1
+ box: CumulusCommunity/cumulus-vx:4.4.0
+ device: cumulus
+ id: 1
+ interfaces:
+ - ifindex: 1
+ ifname: swp1
+ ipv4: true
+ ipv6: true
+ linkindex: 1
+ name: test -> x1
+ neighbors:
+ - ifname: swp1
+ ipv4: true
+ ipv6: true
+ node: x1
+ role: external
+ type: p2p
+ unnumbered: true
+ - ifindex: 2
+ ifname: swp2
+ ipv4: true
+ linkindex: 2
+ name: test -> x1
+ neighbors:
+ - ifname: swp2
+ ipv4: true
+ node: x1
+ role: external
+ type: p2p
+ - ifindex: 3
+ ifname: swp3
+ ipv4: true
+ ipv6: true
+ linkindex: 3
+ name: test -> x2
+ neighbors:
+ - ifname: swp1
+ ipv4: true
+ ipv6: true
+ node: x2
+ role: external
+ type: p2p
+ - ifindex: 4
+ ifname: swp4
+ ipv6: true
+ linkindex: 4
+ name: test -> x3
+ neighbors:
+ - ifname: swp1
+ ipv6: true
+ node: x3
+ role: external
+ type: p2p
+ - ifindex: 5
+ ifname: swp5
+ ipv4: 172.31.1.1/24
+ ipv6: true
+ linkindex: 5
+ name: test -> x4
+ neighbors:
+ - ifname: swp1
+ ipv4: 172.31.1.2/24
+ ipv6: true
+ node: x4
+ role: external
+ type: p2p
+ - ifindex: 6
+ ifname: swp6
+ ipv4: true
+ ipv6: 2001:db8:1::1/64
+ linkindex: 6
+ name: test -> x4
+ neighbors:
+ - ifname: swp2
+ ipv4: true
+ ipv6: 2001:db8:1::2/64
+ node: x4
+ role: external
+ type: p2p
+ loopback:
+ ipv4: 10.0.0.1/32
+ ipv6: 2001:db8:0:1::1/64
+ mgmt:
+ ifname: eth0
+ ipv4: 192.168.121.101
+ mac: 08-4F-A9-00-00-01
+ module:
+ - bgp
+ name: test
+ x1:
+ af:
+ ipv4: true
+ ipv6: true
+ bgp:
+ advertise_loopback: true
+ as: 65001
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ ipv4: true
+ ipv6: true
+ neighbors:
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
+ ifindex: 1
+ ipv4: true
+ ipv4_rfc8950: true
+ ipv6: true
+ local_if: swp1
+ name: test
+ type: ebgp
+ - activate:
+ ipv4: true
+ as: 65000
+ ifindex: 2
+ ipv4: true
+ ipv4_rfc8950: true
+ local_if: swp2
+ name: test
+ type: ebgp
+ next_hop_self: true
+ router_id: 10.0.0.2
+ box: CumulusCommunity/cumulus-vx:4.4.0
+ device: cumulus
+ id: 2
+ interfaces:
+ - ifindex: 1
+ ifname: swp1
+ ipv4: true
+ ipv6: true
+ linkindex: 1
+ name: x1 -> test
+ neighbors:
+ - ifname: swp1
+ ipv4: true
+ ipv6: true
+ node: test
+ role: external
+ type: p2p
+ unnumbered: true
+ - ifindex: 2
+ ifname: swp2
+ ipv4: true
+ linkindex: 2
+ name: x1 -> test
+ neighbors:
+ - ifname: swp2
+ ipv4: true
+ node: test
+ role: external
+ type: p2p
+ loopback:
+ ipv4: 10.0.0.2/32
+ ipv6: 2001:db8:0:2::1/64
+ mgmt:
+ ifname: eth0
+ ipv4: 192.168.121.102
+ mac: 08-4F-A9-00-00-02
+ module:
+ - bgp
+ name: x1
+ x2:
+ af:
+ ipv4: true
+ ipv6: true
+ bgp:
+ advertise_loopback: true
+ as: 65002
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ ipv4: true
+ ipv6: true
+ neighbors:
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
+ ifindex: 1
+ ipv4: true
+ ipv4_rfc8950: true
+ ipv6: true
+ local_if: swp1
+ name: test
+ type: ebgp
+ next_hop_self: true
+ router_id: 10.0.0.3
+ box: CumulusCommunity/cumulus-vx:4.4.0
+ device: cumulus
+ id: 3
+ interfaces:
+ - ifindex: 1
+ ifname: swp1
+ ipv4: true
+ ipv6: true
+ linkindex: 3
+ name: x2 -> test
+ neighbors:
+ - ifname: swp3
+ ipv4: true
+ ipv6: true
+ node: test
+ role: external
+ type: p2p
+ loopback:
+ ipv4: 10.0.0.3/32
+ ipv6: 2001:db8:0:3::1/64
+ mgmt:
+ ifname: eth0
+ ipv4: 192.168.121.103
+ mac: 08-4F-A9-00-00-03
+ module:
+ - bgp
+ name: x2
+ x3:
+ af:
+ ipv4: true
+ ipv6: true
+ bgp:
+ advertise_loopback: true
+ as: 65003
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ ipv6: true
+ neighbors:
+ - activate:
+ ipv6: true
+ as: 65000
+ ifindex: 1
+ ipv6: true
+ local_if: swp1
+ name: test
+ type: ebgp
+ next_hop_self: true
+ router_id: 10.0.0.4
+ box: CumulusCommunity/cumulus-vx:4.4.0
+ device: cumulus
+ id: 4
+ interfaces:
+ - ifindex: 1
+ ifname: swp1
+ ipv6: true
+ linkindex: 4
+ name: x3 -> test
+ neighbors:
+ - ifname: swp4
+ ipv6: true
+ node: test
+ role: external
+ type: p2p
+ loopback:
+ ipv4: 10.0.0.4/32
+ ipv6: 2001:db8:0:4::1/64
+ mgmt:
+ ifname: eth0
+ ipv4: 192.168.121.104
+ mac: 08-4F-A9-00-00-04
+ module:
+ - bgp
+ name: x3
+ x4:
+ af:
+ ipv4: true
+ ipv6: true
+ bgp:
+ advertise_loopback: true
+ as: 65004
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ ipv4: true
+ ipv6: true
+ neighbors:
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
+ ifindex: 1
+ ipv4: 172.31.1.1
+ ipv6: true
+ local_if: swp1
+ name: test
+ type: ebgp
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
+ ifindex: 2
+ ipv4: true
+ ipv6: 2001:db8:1::1
+ name: test
+ type: ebgp
+ next_hop_self: true
+ router_id: 10.0.0.5
+ box: CumulusCommunity/cumulus-vx:4.4.0
+ device: cumulus
+ id: 5
+ interfaces:
+ - ifindex: 1
+ ifname: swp1
+ ipv4: 172.31.1.2/24
+ ipv6: true
+ linkindex: 5
+ name: x4 -> test
+ neighbors:
+ - ifname: swp5
+ ipv4: 172.31.1.1/24
+ ipv6: true
+ node: test
+ role: external
+ type: p2p
+ - ifindex: 2
+ ifname: swp2
+ ipv4: true
+ ipv6: 2001:db8:1::2/64
+ linkindex: 6
+ name: x4 -> test
+ neighbors:
+ - ifname: swp6
+ ipv4: true
+ ipv6: 2001:db8:1::1/64
+ node: test
+ role: external
+ type: p2p
+ loopback:
+ ipv4: 10.0.0.5/32
+ ipv6: 2001:db8:0:5::1/64
+ mgmt:
+ ifname: eth0
+ ipv4: 192.168.121.105
+ mac: 08-4F-A9-00-00-05
+ module:
+ - bgp
+ name: x4
+provider: libvirt
diff --git a/tests/topology/expected/bgp-unnumbered.yml b/tests/topology/expected/bgp-unnumbered.yml
index cf4e72e55..e6cd24738 100644
--- a/tests/topology/expected/bgp-unnumbered.yml
+++ b/tests/topology/expected/bgp-unnumbered.yml
@@ -98,14 +98,17 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65100
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65100
ifindex: 1
ipv4: true
+ ipv4_rfc8950: true
ipv6: true
local_if: swp1
name: r2
type: ebgp
- unnumbered: true
next_hop_self: true
router_id: 10.0.0.1
box: CumulusCommunity/cumulus-vx:4.4.0
@@ -152,15 +155,20 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ifindex: 1
ipv4: true
+ ipv4_rfc8950: true
ipv6: true
local_if: swp1
name: r1
type: ebgp
- unnumbered: true
- - as: 65200
+ - activate:
+ ipv4: true
+ as: 65200
ifindex: 2
ipv4: 10.10.10.2
name: r3
@@ -221,7 +229,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65100
+ - activate:
+ ipv4: true
+ as: 65100
ifindex: 1
ipv4: 10.10.10.1
name: r2
diff --git a/tests/topology/expected/bgp-vrf-local-as.yml b/tests/topology/expected/bgp-vrf-local-as.yml
new file mode 100644
index 000000000..8c19ee915
--- /dev/null
+++ b/tests/topology/expected/bgp-vrf-local-as.yml
@@ -0,0 +1,680 @@
+bgp:
+ advertise_loopback: true
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ next_hop_self: true
+groups:
+ as65000:
+ members:
+ - r2
+ as65100:
+ members:
+ - r1
+ as65101:
+ members:
+ - r3
+input:
+- topology/input/bgp-vrf-local-as.yml
+- package:topology-defaults.yml
+links:
+- interfaces:
+ - ifindex: 1
+ node: r1
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+ - ifindex: 1
+ node: r2
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+ left:
+ ifname: Ethernet1
+ node: r1
+ linkindex: 1
+ name: r1 - r2
+ node_count: 2
+ prefix: {}
+ right:
+ ifname: Ethernet1
+ node: r2
+ role: external
+ type: p2p
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+- interfaces:
+ - ifindex: 2
+ node: r2
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+ - ifindex: 1
+ node: r3
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+ left:
+ ifname: Ethernet2
+ node: r2
+ linkindex: 2
+ name: r2 - r3
+ node_count: 2
+ prefix: {}
+ right:
+ ifname: Ethernet1
+ node: r3
+ role: external
+ type: p2p
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+- interfaces:
+ - bgp:
+ local_as: 65001
+ ifindex: 3
+ ipv4: 10.1.0.1/30
+ node: r2
+ vlan:
+ access: vrf-leak
+ mode: route
+ vrf: red
+ - bgp:
+ local_as: 65002
+ ifindex: 4
+ ipv4: 10.1.0.2/30
+ node: r2
+ vlan:
+ access: vrf-leak
+ mode: route
+ vrf: blue
+ left:
+ ifname: Ethernet3
+ ipv4: 10.1.0.1/30
+ node: r2
+ linkindex: 3
+ name: r2 - r2
+ node_count: 2
+ prefix:
+ ipv4: 10.1.0.0/30
+ right:
+ ifname: Ethernet4
+ ipv4: 10.1.0.2/30
+ node: r2
+ type: p2p
+ vlan:
+ mode: route
+module:
+- vlan
+- bgp
+- vrf
+name: input
+nodes:
+ r1:
+ af:
+ ipv4: true
+ vpnv4: true
+ bgp:
+ advertise_loopback: true
+ as: 65100
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ neighbors: []
+ next_hop_self: true
+ router_id: 10.0.0.1
+ box: arista/veos
+ device: eos
+ id: 1
+ interfaces:
+ - ifindex: 1
+ ifname: Ethernet1
+ linkindex: 1
+ name: r1 -> r2
+ neighbors:
+ - ifname: Ethernet1
+ node: r2
+ role: external
+ subif_index: 2
+ type: p2p
+ - bridge_group: 1
+ ifindex: 2
+ ifname: Ethernet1.1
+ ipv4: 172.16.3.1/24
+ name: r1 -> [r2]
+ neighbors:
+ - ifname: Ethernet1.1
+ ipv4: 172.16.3.2/24
+ node: r2
+ parent_ifindex: 1
+ parent_ifname: Ethernet1
+ role: external
+ type: vlan_member
+ virtual_interface: true
+ vlan:
+ access_id: 1001
+ mode: route
+ routed_link: true
+ vrf: blue
+ - bridge_group: 2
+ ifindex: 3
+ ifname: Ethernet1.2
+ ipv4: 172.16.4.1/24
+ name: r1 -> [r2]
+ neighbors:
+ - ifname: Ethernet1.2
+ ipv4: 172.16.4.2/24
+ node: r2
+ parent_ifindex: 1
+ parent_ifname: Ethernet1
+ role: external
+ type: vlan_member
+ virtual_interface: true
+ vlan:
+ access_id: 1000
+ mode: route
+ routed_link: true
+ vrf: red
+ loopback:
+ ipv4: 10.0.0.1/32
+ mgmt:
+ ifname: Management1
+ ipv4: 192.168.121.101
+ mac: 08-4F-A9-00-00-01
+ module:
+ - vlan
+ - bgp
+ - vrf
+ name: r1
+ vlan:
+ max_bridge_group: 2
+ mode: irb
+ vlans:
+ blue:
+ bridge_group: 1
+ id: 1001
+ mode: route
+ prefix:
+ ipv4: 172.16.1.0/24
+ vni: 101001
+ vrf: blue
+ red:
+ bridge_group: 2
+ id: 1000
+ mode: route
+ prefix:
+ ipv4: 172.16.0.0/24
+ vni: 101000
+ vrf: red
+ vrf:
+ as: 65000
+ vrfs:
+ blue:
+ af:
+ ipv4: true
+ bgp:
+ neighbors:
+ - as: 65000
+ ifindex: 2
+ ipv4: 172.16.3.2
+ name: r2
+ type: ebgp
+ export:
+ - '65000:2'
+ import:
+ - '65000:2'
+ rd: '65000:2'
+ vrfidx: 100
+ red:
+ af:
+ ipv4: true
+ bgp:
+ neighbors:
+ - as: 65000
+ ifindex: 3
+ ipv4: 172.16.4.2
+ name: r2
+ type: ebgp
+ export:
+ - '65000:1'
+ import:
+ - '65000:1'
+ rd: '65000:1'
+ vrfidx: 101
+ r2:
+ af:
+ ipv4: true
+ vpnv4: true
+ bgp:
+ advertise_loopback: true
+ as: 65000
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ neighbors: []
+ next_hop_self: true
+ router_id: 10.0.0.2
+ box: arista/veos
+ device: eos
+ id: 2
+ interfaces:
+ - ifindex: 1
+ ifname: Ethernet1
+ linkindex: 1
+ name: r2 -> r1
+ neighbors:
+ - ifname: Ethernet1
+ node: r1
+ role: external
+ subif_index: 2
+ type: p2p
+ - ifindex: 2
+ ifname: Ethernet2
+ linkindex: 2
+ name: r2 -> r3
+ neighbors:
+ - ifname: Ethernet1
+ node: r3
+ role: external
+ subif_index: 2
+ type: p2p
+ - bgp:
+ local_as: 65001
+ bridge_group: 1
+ ifindex: 3
+ ifname: Ethernet3
+ ipv4: 10.1.0.1/30
+ linkindex: 3
+ name: r2 -> r2
+ neighbors:
+ - bgp:
+ local_as: 65002
+ ifname: Ethernet4
+ ipv4: 10.1.0.2/30
+ node: r2
+ vrf: blue
+ type: p2p
+ vlan:
+ mode: route
+ vrf: red
+ - bgp:
+ local_as: 65002
+ bridge_group: 1
+ ifindex: 4
+ ifname: Ethernet4
+ ipv4: 10.1.0.2/30
+ linkindex: 3
+ name: r2 -> r2
+ neighbors:
+ - bgp:
+ local_as: 65001
+ ifname: Ethernet3
+ ipv4: 10.1.0.1/30
+ node: r2
+ vrf: red
+ type: p2p
+ vlan:
+ mode: route
+ vrf: blue
+ - bridge_group: 2
+ ifindex: 5
+ ifname: Ethernet1.1
+ ipv4: 172.16.3.2/24
+ name: r2 -> [r1]
+ neighbors:
+ - ifname: Ethernet1.1
+ ipv4: 172.16.3.1/24
+ node: r1
+ parent_ifindex: 1
+ parent_ifname: Ethernet1
+ role: external
+ type: vlan_member
+ virtual_interface: true
+ vlan:
+ access_id: 1001
+ mode: route
+ routed_link: true
+ vrf: blue
+ - bridge_group: 3
+ ifindex: 6
+ ifname: Ethernet1.2
+ ipv4: 172.16.4.2/24
+ name: r2 -> [r1]
+ neighbors:
+ - ifname: Ethernet1.2
+ ipv4: 172.16.4.1/24
+ node: r1
+ parent_ifindex: 1
+ parent_ifname: Ethernet1
+ role: external
+ type: vlan_member
+ virtual_interface: true
+ vlan:
+ access_id: 1000
+ mode: route
+ routed_link: true
+ vrf: red
+ - bridge_group: 2
+ ifindex: 7
+ ifname: Ethernet2.1
+ ipv4: 172.16.5.2/24
+ name: r2 -> [r3]
+ neighbors:
+ - ifname: Ethernet1.1
+ ipv4: 172.16.5.3/24
+ node: r3
+ parent_ifindex: 2
+ parent_ifname: Ethernet2
+ role: external
+ type: vlan_member
+ virtual_interface: true
+ vlan:
+ access_id: 1001
+ mode: route
+ routed_link: true
+ vrf: blue
+ - bridge_group: 3
+ ifindex: 8
+ ifname: Ethernet2.2
+ ipv4: 172.16.6.2/24
+ name: r2 -> [r3]
+ neighbors:
+ - ifname: Ethernet1.2
+ ipv4: 172.16.6.3/24
+ node: r3
+ parent_ifindex: 2
+ parent_ifname: Ethernet2
+ role: external
+ type: vlan_member
+ virtual_interface: true
+ vlan:
+ access_id: 1000
+ mode: route
+ routed_link: true
+ vrf: red
+ loopback:
+ ipv4: 10.0.0.2/32
+ mgmt:
+ ifname: Management1
+ ipv4: 192.168.121.102
+ mac: 08-4F-A9-00-00-02
+ module:
+ - vlan
+ - bgp
+ - vrf
+ name: r2
+ vlan:
+ max_bridge_group: 3
+ mode: irb
+ vlans:
+ blue:
+ bridge_group: 2
+ id: 1001
+ mode: route
+ prefix:
+ ipv4: 172.16.1.0/24
+ vni: 101001
+ vrf: blue
+ red:
+ bridge_group: 3
+ id: 1000
+ mode: route
+ prefix:
+ ipv4: 172.16.0.0/24
+ vni: 101000
+ vrf: red
+ vrf-leak:
+ bridge_group: 1
+ id: 1002
+ mode: route
+ prefix:
+ ipv4: 172.16.2.0/24
+ vni: 101002
+ vrf:
+ as: 65000
+ vrfs:
+ blue:
+ af:
+ ipv4: true
+ bgp:
+ neighbors:
+ - as: 65001
+ ifindex: 4
+ ipv4: 10.1.0.1
+ local_as: 65002
+ name: r2
+ type: ebgp
+ - as: 65100
+ ifindex: 5
+ ipv4: 172.16.3.1
+ name: r1
+ type: ebgp
+ - as: 65101
+ ifindex: 7
+ ipv4: 172.16.5.3
+ name: r3
+ type: ebgp
+ router_id: 172.32.0.2
+ export:
+ - '65000:2'
+ import:
+ - '65000:2'
+ rd: '65000:2'
+ vrfidx: 101
+ red:
+ af:
+ ipv4: true
+ bgp:
+ neighbors:
+ - as: 65002
+ ifindex: 3
+ ipv4: 10.1.0.2
+ local_as: 65001
+ name: r2
+ type: ebgp
+ - as: 65100
+ ifindex: 6
+ ipv4: 172.16.4.1
+ name: r1
+ type: ebgp
+ - as: 65101
+ ifindex: 8
+ ipv4: 172.16.6.3
+ name: r3
+ type: ebgp
+ router_id: 172.31.0.1
+ export:
+ - '65000:1'
+ import:
+ - '65000:1'
+ rd: '65000:1'
+ vrfidx: 100
+ r3:
+ af:
+ ipv4: true
+ vpnv4: true
+ bgp:
+ advertise_loopback: true
+ as: 65101
+ community:
+ ebgp:
+ - standard
+ ibgp:
+ - standard
+ - extended
+ neighbors: []
+ next_hop_self: true
+ router_id: 10.0.0.3
+ box: arista/veos
+ device: eos
+ id: 3
+ interfaces:
+ - ifindex: 1
+ ifname: Ethernet1
+ linkindex: 2
+ name: r3 -> r2
+ neighbors:
+ - ifname: Ethernet2
+ node: r2
+ role: external
+ subif_index: 2
+ type: p2p
+ - bridge_group: 1
+ ifindex: 2
+ ifname: Ethernet1.1
+ ipv4: 172.16.5.3/24
+ name: r3 -> [r2]
+ neighbors:
+ - ifname: Ethernet2.1
+ ipv4: 172.16.5.2/24
+ node: r2
+ parent_ifindex: 1
+ parent_ifname: Ethernet1
+ role: external
+ type: vlan_member
+ virtual_interface: true
+ vlan:
+ access_id: 1001
+ mode: route
+ routed_link: true
+ vrf: blue
+ - bridge_group: 2
+ ifindex: 3
+ ifname: Ethernet1.2
+ ipv4: 172.16.6.3/24
+ name: r3 -> [r2]
+ neighbors:
+ - ifname: Ethernet2.2
+ ipv4: 172.16.6.2/24
+ node: r2
+ parent_ifindex: 1
+ parent_ifname: Ethernet1
+ role: external
+ type: vlan_member
+ virtual_interface: true
+ vlan:
+ access_id: 1000
+ mode: route
+ routed_link: true
+ vrf: red
+ loopback:
+ ipv4: 10.0.0.3/32
+ mgmt:
+ ifname: Management1
+ ipv4: 192.168.121.103
+ mac: 08-4F-A9-00-00-03
+ module:
+ - vlan
+ - bgp
+ - vrf
+ name: r3
+ vlan:
+ max_bridge_group: 2
+ mode: irb
+ vlans:
+ blue:
+ bridge_group: 1
+ id: 1001
+ mode: route
+ prefix:
+ ipv4: 172.16.1.0/24
+ vni: 101001
+ vrf: blue
+ red:
+ bridge_group: 2
+ id: 1000
+ mode: route
+ prefix:
+ ipv4: 172.16.0.0/24
+ vni: 101000
+ vrf: red
+ vrf:
+ as: 65000
+ vrfs:
+ blue:
+ af:
+ ipv4: true
+ bgp:
+ neighbors:
+ - as: 65000
+ ifindex: 2
+ ipv4: 172.16.5.2
+ name: r2
+ type: ebgp
+ export:
+ - '65000:2'
+ import:
+ - '65000:2'
+ rd: '65000:2'
+ vrfidx: 100
+ red:
+ af:
+ ipv4: true
+ bgp:
+ neighbors:
+ - as: 65000
+ ifindex: 3
+ ipv4: 172.16.6.2
+ name: r2
+ type: ebgp
+ export:
+ - '65000:1'
+ import:
+ - '65000:1'
+ rd: '65000:1'
+ vrfidx: 101
+provider: libvirt
+vlan:
+ mode: irb
+vlans:
+ blue:
+ id: 1001
+ mode: route
+ prefix:
+ ipv4: 172.16.1.0/24
+ vni: 101001
+ vrf: blue
+ red:
+ id: 1000
+ mode: route
+ prefix:
+ ipv4: 172.16.0.0/24
+ vni: 101000
+ vrf: red
+ vrf-leak:
+ id: 1002
+ mode: route
+ prefix:
+ ipv4: 172.16.2.0/24
+ vni: 101002
+vrf:
+ as: 65000
+vrfs:
+ blue:
+ export:
+ - '65000:2'
+ import:
+ - '65000:2'
+ rd: '65000:2'
+ red:
+ export:
+ - '65000:1'
+ import:
+ - '65000:1'
+ rd: '65000:1'
diff --git a/tests/topology/expected/bgp.yml b/tests/topology/expected/bgp.yml
index 1c5e1b5a4..8186f26b2 100644
--- a/tests/topology/expected/bgp.yml
+++ b/tests/topology/expected/bgp.yml
@@ -241,7 +241,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ifindex: 1
ipv4: 172.31.0.2
name: pe1
@@ -307,7 +309,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ifindex: 1
ipv4: 172.31.0.6
name: pe2
@@ -419,19 +423,27 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.1
ipv6: 2001:db8:0:1::1
name: rr1
rr: true
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.2
ipv6: 2001:db8:0:2::1
name: rr2
rr: true
type: ibgp
- - as: 65001
+ - activate:
+ ipv4: true
+ as: 65001
ifindex: 3
ipv4: 172.31.0.1
name: e1
@@ -518,19 +530,27 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.1
ipv6: 2001:db8:0:1::1
name: rr1
rr: true
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.2
ipv6: 2001:db8:0:2::1
name: rr2
rr: true
type: ibgp
- - as: 65002
+ - activate:
+ ipv4: true
+ as: 65002
ifindex: 3
ipv4: 172.31.0.5
name: e2
@@ -617,18 +637,27 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.2
ipv6: 2001:db8:0:2::1
name: rr2
rr: true
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.3
ipv6: 2001:db8:0:3::1
name: pe1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.4
ipv6: 2001:db8:0:4::1
name: pe2
@@ -706,18 +735,27 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.1
ipv6: 2001:db8:0:1::1
name: rr1
rr: true
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.3
ipv6: 2001:db8:0:3::1
name: pe1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.4
ipv6: 2001:db8:0:4::1
name: pe2
diff --git a/tests/topology/expected/device-module-defaults.yml b/tests/topology/expected/device-module-defaults.yml
index c2e5fceb3..b94aad01a 100644
--- a/tests/topology/expected/device-module-defaults.yml
+++ b/tests/topology/expected/device-module-defaults.yml
@@ -54,9 +54,6 @@ nodes:
ipv4: true
bgp:
address_families:
- ebgp:
- - ipv4
- - ipv6
ibgp:
- ipv4
advertise_loopback: true
@@ -69,7 +66,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: l2
type: ibgp
@@ -133,7 +132,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: l1
type: ibgp
diff --git a/tests/topology/expected/groups-node-data.yml b/tests/topology/expected/groups-node-data.yml
index 80e2d67d6..14c1c19b7 100644
--- a/tests/topology/expected/groups-node-data.yml
+++ b/tests/topology/expected/groups-node-data.yml
@@ -49,11 +49,15 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: b
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: c
type: ibgp
@@ -84,11 +88,15 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: a
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: c
type: ibgp
@@ -119,11 +127,15 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: a
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: b
type: ibgp
@@ -154,11 +166,15 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65001
+ - activate:
+ ipv4: true
+ as: 65001
ipv4: 10.0.0.5
name: e
type: ibgp
- - as: 65001
+ - activate:
+ ipv4: true
+ as: 65001
ipv4: 10.0.0.6
name: f
type: ibgp
@@ -189,11 +205,15 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65001
+ - activate:
+ ipv4: true
+ as: 65001
ipv4: 10.0.0.4
name: d
type: ibgp
- - as: 65001
+ - activate:
+ ipv4: true
+ as: 65001
ipv4: 10.0.0.6
name: f
type: ibgp
@@ -224,11 +244,15 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65001
+ - activate:
+ ipv4: true
+ as: 65001
ipv4: 10.0.0.4
name: d
type: ibgp
- - as: 65001
+ - activate:
+ ipv4: true
+ as: 65001
ipv4: 10.0.0.5
name: e
type: ibgp
diff --git a/tests/topology/expected/module-reorder.yml b/tests/topology/expected/module-reorder.yml
index d46047002..b6068bbb7 100644
--- a/tests/topology/expected/module-reorder.yml
+++ b/tests/topology/expected/module-reorder.yml
@@ -124,11 +124,15 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: e1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.4
name: e2
type: ibgp
@@ -243,11 +247,15 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: c1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.4
name: e2
type: ibgp
@@ -314,11 +322,15 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: c1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.3
name: e1
type: ibgp
diff --git a/tests/topology/expected/mpls.yml b/tests/topology/expected/mpls.yml
index 8e022e404..7c78c4683 100644
--- a/tests/topology/expected/mpls.yml
+++ b/tests/topology/expected/mpls.yml
@@ -183,7 +183,10 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ifindex: 1
ipv4: 10.1.0.2
ipv4_label: true
@@ -245,7 +248,10 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ifindex: 1
ipv4: 10.1.0.6
ipv4_label: true
@@ -387,21 +393,30 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.2
ipv4_label: true
ipv6: 2001:db8:0:2::1
ipv6_label: true
name: pe2
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.4
ipv4_label: true
ipv6: 2001:db8:0:4::1
ipv6_label: true
name: rr
type: ibgp
- - as: 65101
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65101
ifindex: 1
ipv4: 10.1.0.1
ipv6: 2001:db8:1::1
@@ -488,21 +503,30 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.1
ipv4_label: true
ipv6: 2001:db8:0:1::1
ipv6_label: true
name: pe1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.4
ipv4_label: true
ipv6: 2001:db8:0:4::1
ipv6_label: true
name: rr
type: ibgp
- - as: 65102
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65102
ifindex: 1
ipv4: 10.1.0.5
ipv6: 2001:db8:1:1::1
@@ -587,13 +611,19 @@ nodes:
ipv4: true
ipv6: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.1
ipv4_label: true
ipv6: 2001:db8:0:1::1
name: pe1
type: ibgp
- - as: 65000
+ - activate:
+ ipv4: true
+ ipv6: true
+ as: 65000
ipv4: 10.0.0.2
ipv4_label: true
ipv6: 2001:db8:0:2::1
diff --git a/tests/topology/expected/vlan-bridge-trunk-router.yml b/tests/topology/expected/vlan-bridge-trunk-router.yml
new file mode 100644
index 000000000..04a3c6820
--- /dev/null
+++ b/tests/topology/expected/vlan-bridge-trunk-router.yml
@@ -0,0 +1,485 @@
+groups:
+ hosts:
+ device: linux
+ members:
+ - h1
+ - h2
+ routers:
+ device: iosv
+ members:
+ - r1
+ switches:
+ device: eos
+ members:
+ - s1
+ - s2
+ module:
+ - vlan
+input:
+- topology/input/vlan-bridge-trunk-router.yml
+- package:topology-defaults.yml
+links:
+- interfaces:
+ - ifindex: 1
+ node: s1
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+ - ifindex: 1
+ node: s2
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+ left:
+ ifname: Ethernet1
+ node: s1
+ linkindex: 1
+ name: s1 - s2
+ node_count: 2
+ prefix: {}
+ right:
+ ifname: Ethernet1
+ node: s2
+ type: p2p
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+- bridge: input_2
+ gateway:
+ ipv4: false
+ interfaces:
+ - ifindex: 1
+ ipv4: 172.16.0.2/24
+ node: h1
+ - ifindex: 2
+ ipv4: false
+ ipv6: false
+ node: s1
+ vlan:
+ access: red
+ linkindex: 2
+ node_count: 2
+ prefix:
+ ipv4: 172.16.0.0/24
+ type: lan
+- bridge: input_3
+ gateway:
+ ipv4: false
+ interfaces:
+ - ifindex: 1
+ ipv4: 172.16.1.3/24
+ node: h2
+ - ifindex: 3
+ ipv4: false
+ ipv6: false
+ node: s1
+ vlan:
+ access: blue
+ linkindex: 3
+ node_count: 2
+ prefix:
+ ipv4: 172.16.1.0/24
+ type: lan
+- bridge: input_4
+ interfaces:
+ - ifindex: 1
+ ipv4: 172.16.0.1/24
+ node: r1
+ - ifindex: 2
+ ipv4: false
+ ipv6: false
+ node: s2
+ vlan:
+ access: red
+ linkindex: 4
+ node_count: 2
+ prefix:
+ ipv4: 172.16.0.0/24
+ type: lan
+- bridge: input_5
+ interfaces:
+ - ifindex: 2
+ ipv4: 172.16.1.1/24
+ node: r1
+ - ifindex: 3
+ ipv4: false
+ ipv6: false
+ node: s2
+ vlan:
+ access: blue
+ linkindex: 5
+ node_count: 2
+ prefix:
+ ipv4: 172.16.1.0/24
+ type: lan
+module:
+- vlan
+name: input
+nodes:
+ h1:
+ af:
+ ipv4: true
+ box: generic/ubuntu2004
+ device: linux
+ id: 2
+ interfaces:
+ - bridge: input_2
+ gateway:
+ ipv4: 172.16.0.1/24
+ ifindex: 1
+ ifname: eth1
+ ipv4: 172.16.0.2/24
+ linkindex: 2
+ name: h1 -> [s1,s2,r1]
+ neighbors:
+ - ifname: Vlan1000
+ node: s1
+ - ifname: Vlan1000
+ node: s2
+ - ifname: GigabitEthernet0/1
+ ipv4: 172.16.0.1/24
+ node: r1
+ type: lan
+ mgmt:
+ ifname: eth0
+ ipv4: 192.168.121.102
+ mac: 08-4F-A9-00-00-02
+ name: h1
+ role: host
+ h2:
+ af:
+ ipv4: true
+ box: generic/ubuntu2004
+ device: linux
+ id: 3
+ interfaces:
+ - bridge: input_3
+ gateway:
+ ipv4: 172.16.1.1/24
+ ifindex: 1
+ ifname: eth1
+ ipv4: 172.16.1.3/24
+ linkindex: 3
+ name: h2 -> [s1,s2,r1]
+ neighbors:
+ - ifname: Vlan1001
+ node: s1
+ - ifname: Vlan1001
+ node: s2
+ - ifname: GigabitEthernet0/2
+ ipv4: 172.16.1.1/24
+ node: r1
+ type: lan
+ mgmt:
+ ifname: eth0
+ ipv4: 192.168.121.103
+ mac: 08-4F-A9-00-00-03
+ name: h2
+ role: host
+ r1:
+ af:
+ ipv4: true
+ box: cisco/iosv
+ device: iosv
+ id: 1
+ interfaces:
+ - bridge: input_4
+ ifindex: 1
+ ifname: GigabitEthernet0/1
+ ipv4: 172.16.0.1/24
+ linkindex: 4
+ name: r1 -> [h1,s1,s2]
+ neighbors:
+ - ifname: eth1
+ ipv4: 172.16.0.2/24
+ node: h1
+ - ifname: Vlan1000
+ node: s1
+ - ifname: Vlan1000
+ node: s2
+ type: lan
+ - bridge: input_5
+ ifindex: 2
+ ifname: GigabitEthernet0/2
+ ipv4: 172.16.1.1/24
+ linkindex: 5
+ name: r1 -> [h2,s1,s2]
+ neighbors:
+ - ifname: eth1
+ ipv4: 172.16.1.3/24
+ node: h2
+ - ifname: Vlan1001
+ node: s1
+ - ifname: Vlan1001
+ node: s2
+ type: lan
+ loopback:
+ ipv4: 10.0.0.1/32
+ mgmt:
+ ifname: GigabitEthernet0/0
+ ipv4: 192.168.121.101
+ mac: 08-4F-A9-00-00-01
+ name: r1
+ s1:
+ af:
+ ipv4: true
+ box: arista/veos
+ device: eos
+ id: 4
+ interfaces:
+ - ifindex: 1
+ ifname: Ethernet1
+ linkindex: 1
+ name: s1 -> s2
+ neighbors:
+ - ifname: Ethernet1
+ node: s2
+ type: p2p
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+ trunk_id:
+ - 1000
+ - 1001
+ - bridge: input_2
+ ifindex: 2
+ ifname: Ethernet2
+ linkindex: 2
+ type: lan
+ vlan:
+ access: red
+ access_id: 1000
+ - bridge: input_3
+ ifindex: 3
+ ifname: Ethernet3
+ linkindex: 3
+ type: lan
+ vlan:
+ access: blue
+ access_id: 1001
+ - bridge_group: 1
+ ifindex: 6
+ ifname: Vlan1000
+ name: VLAN red (1000) -> [h1,s2,r1]
+ neighbors:
+ - ifname: eth1
+ ipv4: 172.16.0.2/24
+ node: h1
+ - ifname: Vlan1000
+ node: s2
+ - ifname: GigabitEthernet0/1
+ ipv4: 172.16.0.1/24
+ node: r1
+ type: svi
+ virtual_interface: true
+ vlan:
+ mode: bridge
+ - bridge_group: 2
+ ifindex: 7
+ ifname: Vlan1001
+ name: VLAN blue (1001) -> [h2,s2,r1]
+ neighbors:
+ - ifname: eth1
+ ipv4: 172.16.1.3/24
+ node: h2
+ - ifname: Vlan1001
+ node: s2
+ - ifname: GigabitEthernet0/2
+ ipv4: 172.16.1.1/24
+ node: r1
+ type: svi
+ virtual_interface: true
+ vlan:
+ mode: bridge
+ loopback:
+ ipv4: 10.0.0.4/32
+ mgmt:
+ ifname: Management1
+ ipv4: 192.168.121.104
+ mac: 08-4F-A9-00-00-04
+ module:
+ - vlan
+ name: s1
+ vlan:
+ max_bridge_group: 2
+ mode: irb
+ vlans:
+ blue:
+ bridge_group: 2
+ id: 1001
+ mode: bridge
+ prefix:
+ ipv4: 172.16.1.0/24
+ vni: 101001
+ red:
+ bridge_group: 1
+ id: 1000
+ mode: bridge
+ prefix:
+ ipv4: 172.16.0.0/24
+ vni: 101000
+ s2:
+ af:
+ ipv4: true
+ box: arista/veos
+ device: eos
+ id: 5
+ interfaces:
+ - ifindex: 1
+ ifname: Ethernet1
+ linkindex: 1
+ name: s2 -> s1
+ neighbors:
+ - ifname: Ethernet1
+ node: s1
+ type: p2p
+ vlan:
+ trunk:
+ blue: {}
+ red: {}
+ trunk_id:
+ - 1000
+ - 1001
+ - bridge: input_4
+ ifindex: 2
+ ifname: Ethernet2
+ linkindex: 4
+ type: lan
+ vlan:
+ access: red
+ access_id: 1000
+ - bridge: input_5
+ ifindex: 3
+ ifname: Ethernet3
+ linkindex: 5
+ type: lan
+ vlan:
+ access: blue
+ access_id: 1001
+ - bridge_group: 1
+ ifindex: 6
+ ifname: Vlan1000
+ name: VLAN red (1000) -> [h1,s1,r1]
+ neighbors:
+ - ifname: eth1
+ ipv4: 172.16.0.2/24
+ node: h1
+ - ifname: Vlan1000
+ node: s1
+ - ifname: GigabitEthernet0/1
+ ipv4: 172.16.0.1/24
+ node: r1
+ type: svi
+ virtual_interface: true
+ vlan:
+ mode: bridge
+ - bridge_group: 2
+ ifindex: 7
+ ifname: Vlan1001
+ name: VLAN blue (1001) -> [h2,s1,r1]
+ neighbors:
+ - ifname: eth1
+ ipv4: 172.16.1.3/24
+ node: h2
+ - ifname: Vlan1001
+ node: s1
+ - ifname: GigabitEthernet0/2
+ ipv4: 172.16.1.1/24
+ node: r1
+ type: svi
+ virtual_interface: true
+ vlan:
+ mode: bridge
+ loopback:
+ ipv4: 10.0.0.5/32
+ mgmt:
+ ifname: Management1
+ ipv4: 192.168.121.105
+ mac: 08-4F-A9-00-00-05
+ module:
+ - vlan
+ name: s2
+ vlan:
+ max_bridge_group: 2
+ mode: irb
+ vlans:
+ blue:
+ bridge_group: 2
+ id: 1001
+ mode: bridge
+ neighbors:
+ - ifname: eth1
+ ipv4: 172.16.1.3/24
+ node: h2
+ - ifname: Vlan1001
+ node: s1
+ - ifname: Vlan1001
+ node: s2
+ - ifname: GigabitEthernet0/2
+ ipv4: 172.16.1.1/24
+ node: r1
+ prefix:
+ ipv4: 172.16.1.0/24
+ vni: 101001
+ red:
+ bridge_group: 1
+ id: 1000
+ mode: bridge
+ neighbors:
+ - ifname: eth1
+ ipv4: 172.16.0.2/24
+ node: h1
+ - ifname: Vlan1000
+ node: s1
+ - ifname: Vlan1000
+ node: s2
+ - ifname: GigabitEthernet0/1
+ ipv4: 172.16.0.1/24
+ node: r1
+ prefix:
+ ipv4: 172.16.0.0/24
+ vni: 101000
+provider: libvirt
+vlan:
+ mode: irb
+vlans:
+ blue:
+ host_count: 1
+ id: 1001
+ mode: bridge
+ neighbors:
+ - ifname: eth1
+ ipv4: 172.16.1.3/24
+ node: h2
+ - ifname: Vlan1001
+ node: s1
+ - ifname: Vlan1001
+ node: s2
+ - ifname: GigabitEthernet0/2
+ ipv4: 172.16.1.1/24
+ node: r1
+ prefix:
+ ipv4: 172.16.1.0/24
+ vni: 101001
+ red:
+ host_count: 1
+ id: 1000
+ mode: bridge
+ neighbors:
+ - ifname: eth1
+ ipv4: 172.16.0.2/24
+ node: h1
+ - ifname: Vlan1000
+ node: s1
+ - ifname: Vlan1000
+ node: s2
+ - ifname: GigabitEthernet0/1
+ ipv4: 172.16.0.1/24
+ node: r1
+ prefix:
+ ipv4: 172.16.0.0/24
+ vni: 101000
diff --git a/tests/topology/expected/vlan-coverage.yml b/tests/topology/expected/vlan-coverage.yml
index 37fedb944..d1662c04d 100644
--- a/tests/topology/expected/vlan-coverage.yml
+++ b/tests/topology/expected/vlan-coverage.yml
@@ -57,10 +57,11 @@ links:
type: lan
- bridge: input_4
gateway:
- ipv4: 172.16.1.3/24
+ ipv4: false
interfaces:
- ifindex: 3
- ipv4: 172.16.1.3/24
+ ipv4: false
+ ipv6: false
node: s2
vlan:
access: blue
@@ -163,7 +164,6 @@ nodes:
name: h2 -> [s2]
neighbors:
- ifname: GigabitEthernet0/3
- ipv4: 172.16.1.3/24
node: s2
type: lan
mgmt:
diff --git a/tests/topology/expected/vlan-routed-vrf.yml b/tests/topology/expected/vlan-routed-vrf.yml
index dcd4c5f02..9d388726d 100644
--- a/tests/topology/expected/vlan-routed-vrf.yml
+++ b/tests/topology/expected/vlan-routed-vrf.yml
@@ -48,10 +48,11 @@ links:
vrf: blue
- bridge: input_3
gateway:
- ipv4: 172.16.2.4/24
+ ipv4: false
interfaces:
- ifindex: 2
- ipv4: 172.16.2.4/24
+ ipv4: false
+ ipv6: false
node: s2
vlan:
access: green
@@ -246,7 +247,7 @@ nodes:
interfaces:
- bridge: input_3
gateway:
- ipv4: 172.16.2.4/24
+ ipv4: 172.16.2.1/24
ifindex: 1
ifname: eth1
ipv4: 172.16.2.7/24
diff --git a/tests/topology/expected/vlan-vrf-lite.yml b/tests/topology/expected/vlan-vrf-lite.yml
index 9091d67e4..0405268a6 100644
--- a/tests/topology/expected/vlan-vrf-lite.yml
+++ b/tests/topology/expected/vlan-vrf-lite.yml
@@ -215,6 +215,7 @@ nodes:
- ifname: Ethernet2
ipv4: 172.16.2.1/24
node: r1
+ vrf: red
role: stub
type: lan
mgmt:
@@ -242,6 +243,7 @@ nodes:
- ifname: Ethernet3
ipv4: 172.16.4.2/24
node: r2
+ vrf: red
role: stub
type: lan
mgmt:
@@ -269,6 +271,7 @@ nodes:
- ifname: Ethernet3
ipv4: 172.16.3.1/24
node: r1
+ vrf: blue
role: stub
type: lan
mgmt:
@@ -296,6 +299,7 @@ nodes:
- ifname: Ethernet4
ipv4: 172.16.5.2/24
node: r2
+ vrf: blue
role: stub
type: lan
mgmt:
@@ -320,6 +324,7 @@ nodes:
- ifname: Ethernet4
ipv4: 10.1.0.2/30
node: r1
+ vrf: red
type: p2p
loopback:
ipv4: 10.0.0.8/32
diff --git a/tests/topology/expected/vrf-igp.yml b/tests/topology/expected/vrf-igp.yml
index 27ff4660c..98a5a3f4a 100644
--- a/tests/topology/expected/vrf-igp.yml
+++ b/tests/topology/expected/vrf-igp.yml
@@ -134,7 +134,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.2
name: pe2
type: ibgp
@@ -228,7 +230,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ipv4: 10.0.0.1
name: pe1
type: ibgp
@@ -358,7 +362,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ifindex: 1
ipv4: 10.1.0.5
name: pe1
@@ -385,6 +391,7 @@ nodes:
- ifname: Ethernet2
ipv4: 10.1.0.5/30
node: pe1
+ vrf: red
role: external
type: p2p
loopback:
@@ -420,6 +427,7 @@ nodes:
- ifname: Ethernet2
ipv4: 10.1.0.9/30
node: pe2
+ vrf: blue
ospf:
area: 0.0.0.0
network_type: point-to-point
diff --git a/tests/topology/expected/vrf.yml b/tests/topology/expected/vrf.yml
index 370c6ef3f..bb7adc09c 100644
--- a/tests/topology/expected/vrf.yml
+++ b/tests/topology/expected/vrf.yml
@@ -252,7 +252,9 @@ nodes:
- extended
ipv4: true
neighbors:
- - as: 65000
+ - activate:
+ ipv4: true
+ as: 65000
ifindex: 1
ipv4: 10.1.0.1
name: r1
@@ -279,6 +281,7 @@ nodes:
- ifname: Ethernet1
ipv4: 10.1.0.1/30
node: r1
+ vrf: red
role: external
type: p2p
- bgp:
@@ -365,6 +368,7 @@ nodes:
- ifname: Ethernet2
ipv4: 10.1.0.5/30
node: r1
+ vrf: blue
ospf:
area: 0.0.0.0
network_type: point-to-point
diff --git a/tests/topology/input/bgp-sessions.yml b/tests/topology/input/bgp-sessions.yml
new file mode 100644
index 000000000..7ed16b7c7
--- /dev/null
+++ b/tests/topology/input/bgp-sessions.yml
@@ -0,0 +1,29 @@
+#
+# BGP sessions test case -- build EBGP IPv4 sessions but not IBGP IPv4 sessions.
+#
+# You might need a setup similar to this if you want to test IPv4-over-SRv6
+# configured with a custom configuration template
+#
+module: [ bgp,ospf ]
+
+addressing:
+ loopback:
+ ipv6: 2001:db8::/48
+ p2p:
+ ipv6: 2001:db8:1::/48
+
+defaults.device: eos
+bgp.as: 65000
+bgp.sessions:
+ ipv4: [ ebgp ]
+ ipv6: [ ibgp, ebgp ]
+
+nodes:
+ x1:
+ bgp.as: 65100
+ r1:
+ r2:
+
+links:
+- x1-r1
+- r1-r2
diff --git a/tests/topology/input/bgp-unnumbered-dual-stack.yml b/tests/topology/input/bgp-unnumbered-dual-stack.yml
new file mode 100644
index 000000000..603b21f6c
--- /dev/null
+++ b/tests/topology/input/bgp-unnumbered-dual-stack.yml
@@ -0,0 +1,56 @@
+module: [ bgp ]
+defaults.device: cumulus
+
+addressing:
+ loopback:
+ ipv6: 2001:db8:0::/48
+ p2p:
+ ipv4: False
+ ipv6: False
+
+nodes:
+ test:
+ bgp.as: 65000
+ x1:
+ bgp.as: 65001
+ x2:
+ bgp.as: 65002
+ x3:
+ bgp.as: 65003
+ x4:
+ bgp.as: 65004
+
+links:
+# interface 1 - dual-stack unnumbered
+- test:
+ x1:
+ unnumbered: True
+# interface 2 - activate only IPv4 AF over IPv6 LLA
+- test:
+ x1:
+ prefix:
+ ipv4: True
+# interface 3 - dual-stack unnumbered
+- test:
+ x2:
+ prefix:
+ ipv4: True
+ ipv6: True
+# interface 4 - IPv6 AF over IPv6 LLA
+- test:
+ x3:
+ prefix:
+ ipv6: True
+# interface 5 - IPv6 AF over IPv6 LLA + IPv4 AF over IPv4 numbered
+- test:
+ x4:
+ prefix:
+ ipv4: 172.31.1.0/24
+ ipv6: True
+# interface 6 - IPv6 AF over IPv6 numbered, no IPv4 session/AF
+- test:
+ x4:
+ prefix:
+ ipv4: True
+ ipv6: 2001:db8:1::/64
+
diff --git a/tests/topology/input/bgp-vrf-local-as.yml b/tests/topology/input/bgp-vrf-local-as.yml
new file mode 100644
index 000000000..3517e6d05
--- /dev/null
+++ b/tests/topology/input/bgp-vrf-local-as.yml
@@ -0,0 +1,47 @@
+defaults.device: eos
+
+vrfs:
+ red:
+ blue:
+
+vlans:
+ red:
+ mode: route
+ vrf: red
+ blue:
+ mode: route
+ vrf: blue
+ vrf-leak:
+ mode: route
+
+module: [ vlan, vrf, bgp ]
+
+nodes:
+ r1:
+ bgp.as: 65100
+ r2:
+ bgp.as: 65000
+ vrfs:
+ red:
+ bgp.router_id: 172.31.0.1
+ blue:
+ bgp.router_id: 172.32.0.2
+ r3:
+ bgp.as: 65101
+
+links:
+- r1:
+ r2:
+ vlan.trunk: [ red, blue ]
+- r2:
+ r3:
+ vlan.trunk: [ red, blue ]
+- interfaces: # VRF route leaking between red and blue, using BGP peering
+ - node: r2
+ vrf: red
+ bgp.local_as: 65001
+ vlan.access: vrf-leak
+ - node: r2
+ vrf: blue
+ bgp.local_as: 65002
+ vlan.access: vrf-leak
diff --git a/tests/topology/input/vlan-bridge-trunk-router.yml b/tests/topology/input/vlan-bridge-trunk-router.yml
new file mode 100644
index 000000000..f65d3cffd
--- /dev/null
+++ b/tests/topology/input/vlan-bridge-trunk-router.yml
@@ -0,0 +1,49 @@
+#
+# The devices under test are simple bridges with a VLAN trunk
+# between them. An external router is attached to one of them
+#
+# * h1 and h2 should be able to ping each other
+#
+# This test case requires #311 to be fixed to work correctly
+#
+groups:
+ hosts:
+ members: [ h1, h2 ]
+ device: linux
+ switches:
+ members: [ s1,s2 ]
+ module: [ vlan ]
+ device: eos
+ routers:
+ members: [ r1 ]
+ device: iosv
+
+vlans:
+ red:
+ mode: bridge
+ blue:
+ mode: bridge
+
+nodes:
+ r1:
+ h1:
+ h2:
+ s1:
+ s2:
+
+links:
+- s1:
+ s2:
+ vlan.trunk: [ red, blue ]
+- h1:
+ s1:
+ vlan.access: red
+- h2:
+ s1:
+ vlan.access: blue
+- r1:
+ s2:
+ vlan.access: red
+- r1:
+ s2:
+ vlan.access: blue