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/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 0f19fce8f..e0ad89129 100644
--- a/docs/module/bgp.md
+++ b/docs/module/bgp.md
@@ -19,12 +19,14 @@ 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
@@ -36,14 +38,15 @@ More interesting BGP topologies can be created with [custom plugins](../plugins.
[Platforms supporting BGP configuration module](platform-routing-support) support most of the functionality mentioned above. The following features are only supported on a subset of platforms:
-| Operating system | Unnumbered
interfaces | local AS | IBGP
local AS | Configurable
default AF |
-| --------------------- | :-: | :-: | :-: | :-: |
-| Arista EOS | ❌ | ✅ | ✅ | ✅ |
-| Cisco IOS/IOS XE | ❌ | ✅ | ✅ | ✅ |
-| Cumulus Linux | ✅ | ❌ | ❌ | ✅ |
-| Dell OS10 | ❌ | ✅ | ❌ | ❌ |
-| FRR 7.5.0 | ❌ | ❌ | ❌ | ✅ |
-| Nokia SR Linux | ✅ | ✅ | ❌ | ❌ |
+| Operating system | IPv6 LLA
EBGP sessions | Unnumbered
IPv4 EBGP sessions | 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 | ✅ | ✅ | ✅ | ❌ | ❌ |
## Global BGP Configuration Parameters
@@ -222,24 +225,9 @@ See the [Simple BGP Example](bgp_example/simple.md) and [EBGP Data Center Fabric
### Notes on Unnumbered EBGP Sessions
-Unnumbered EBGP sessions are supported on a few platforms. The transformed data model includes **unnumbered** and **ifindex** elements on EBGP neighbors reachable over unnumbered interfaces -- compare a regular EBGP neighbor (R4) with an unnumbered EBGP neighbor (R1):
+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).
-```
-neighbors:
-- as: 65000
- ifindex: 1
- ipv4: true
- ipv6: true
- local_if: swp1
- name: r1
- type: ebgp
- unnumbered: true
-- as: 65200
- ifindex: 2
- ipv4: 10.10.10.2
- name: r3
- 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
diff --git a/netsim/ansible/templates/bgp/cumulus.j2 b/netsim/ansible/templates/bgp/cumulus.j2
index f495db896..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.activate[af] is defined and n.activate[af] %}
-{% 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) }}
+{% 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/modules/bgp.py b/netsim/modules/bgp.py
index 56512c0e3..a4ad51bb5 100644
--- a/netsim/modules/bgp.py
+++ b/netsim/modules/bgp.py
@@ -201,9 +201,35 @@ def build_ebgp_sessions(node: Box, sessions: Box, topology: Box) -> None:
extra_data = Box({})
extra_data.ifindex = l.ifindex
- if "unnumbered" in l:
- extra_data.unnumbered = True
- extra_data.local_if = l.ifname
+
+ # 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}')
diff --git a/netsim/topology-defaults.yml b/netsim/topology-defaults.yml
index 1137e511b..8edbb0b78 100644
--- a/netsim/topology-defaults.yml
+++ b/netsim/topology-defaults.yml
@@ -514,6 +514,8 @@ devices:
features:
bgp:
activate_af: True
+ ipv6_lla: True
+ rfc8950: True
graphite.icon: router
linux:
@@ -614,6 +616,10 @@ devices:
lla: True
ospf:
unnumbered: True
+ bgp:
+ ipv6_lla: True
+ rfc8950: True
+ activate_af: True
clab:
mtu: 1500
runtime: docker
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/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 a343d3ec0..e6cd24738 100644
--- a/tests/topology/expected/bgp-unnumbered.yml
+++ b/tests/topology/expected/bgp-unnumbered.yml
@@ -104,11 +104,11 @@ nodes:
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
@@ -161,11 +161,11 @@ nodes:
as: 65000
ifindex: 1
ipv4: true
+ ipv4_rfc8950: true
ipv6: true
local_if: swp1
name: r1
type: ebgp
- unnumbered: true
- activate:
ipv4: true
as: 65200
diff --git a/tests/topology/expected/bgp-vrf-local-as.yml b/tests/topology/expected/bgp-vrf-local-as.yml
index de1f570e9..7ec58f026 100644
--- a/tests/topology/expected/bgp-vrf-local-as.yml
+++ b/tests/topology/expected/bgp-vrf-local-as.yml
@@ -84,8 +84,7 @@ links:
- bgp:
local_as: 65001
ifindex: 3
- ipv4: false
- ipv6: true
+ ipv4: 10.1.0.1/30
node: r2
vlan:
access: vrf-leak
@@ -94,8 +93,7 @@ links:
- bgp:
local_as: 65002
ifindex: 4
- ipv4: false
- ipv6: true
+ ipv4: 10.1.0.2/30
node: r2
vlan:
access: vrf-leak
@@ -103,7 +101,7 @@ links:
vrf: blue
left:
ifname: Ethernet3
- ipv6: true
+ ipv4: 10.1.0.1/30
node: r2
linkindex: 3
name: r2 - r2
@@ -112,7 +110,7 @@ links:
ipv4: 10.1.0.0/30
right:
ifname: Ethernet4
- ipv6: true
+ ipv4: 10.1.0.2/30
node: r2
type: p2p
vlan:
@@ -260,9 +258,7 @@ nodes:
r2:
af:
ipv4: true
- ipv6: true
vpnv4: true
- vpnv6: true
bgp:
advertise_loopback: true
as: 65000
@@ -304,14 +300,14 @@ nodes:
bridge_group: 1
ifindex: 3
ifname: Ethernet3
- ipv6: true
+ ipv4: 10.1.0.1/30
linkindex: 3
name: r2 -> r2
neighbors:
- bgp:
local_as: 65002
ifname: Ethernet4
- ipv6: true
+ ipv4: 10.1.0.2/30
node: r2
vrf: blue
type: p2p
@@ -323,14 +319,14 @@ nodes:
bridge_group: 1
ifindex: 4
ifname: Ethernet4
- ipv6: true
+ ipv4: 10.1.0.2/30
linkindex: 3
name: r2 -> r2
neighbors:
- bgp:
local_as: 65001
ifname: Ethernet3
- ipv6: true
+ ipv4: 10.1.0.1/30
node: r2
vrf: red
type: p2p
@@ -457,12 +453,11 @@ nodes:
blue:
af:
ipv4: true
- ipv6: true
bgp:
neighbors:
- as: 65001
ifindex: 4
- ipv6: true
+ ipv4: 10.1.0.1
local_as: 65002
name: r2
type: ebgp
@@ -486,12 +481,11 @@ nodes:
red:
af:
ipv4: true
- ipv6: true
bgp:
neighbors:
- as: 65002
ifindex: 3
- ipv6: true
+ ipv4: 10.1.0.2
local_as: 65001
name: r2
type: ebgp
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
index dff6b937b..365e9f5b5 100644
--- a/tests/topology/input/bgp-vrf-local-as.yml
+++ b/tests/topology/input/bgp-vrf-local-as.yml
@@ -39,13 +39,9 @@ links:
- interfaces: # VRF route leaking between red and blue, using BGP peering
- node: r2
vrf: red
- ipv6: True
- ipv4: False
bgp.local_as: 65001
vlan.access: vrf-leak
- node: r2
vrf: blue
- ipv6: True
- ipv4: False
bgp.local_as: 65002
vlan.access: vrf-leak