ospf: add point-to-multipoint and hybrid interface type support

This commit is contained in:
Mattias Walström
2026-03-10 12:44:47 +01:00
parent 5bb21e5221
commit 312b856fba
22 changed files with 1242 additions and 40 deletions
+10
View File
@@ -10,6 +10,8 @@ Tests verifying standard routing protocols and configuration:
- OSPF with BFD (Bidirectional Forwarding Detection)
- OSPF default route advertisement and propagation
- OSPF debug logging configuration and verification
- OSPF point-to-multipoint hybrid (broadcast) interface type
- OSPF point-to-multipoint (non-broadcast) interface type with static neighbors
- RIP basic neighbor discovery and route exchange
- RIP passive interface configuration
- RIP route redistribution (connected, static, and OSPF)
@@ -46,6 +48,14 @@ include::ospf_debug/Readme.adoc[]
<<<
include::ospf_point_to_multipoint_hybrid/Readme.adoc[]
<<<
include::ospf_point_to_multipoint/Readme.adoc[]
<<<
include::rip_basic/Readme.adoc[]
<<<
+6
View File
@@ -29,6 +29,12 @@
- name: OSPF Debug Logging
case: ospf_debug/test.py
- name: OSPF Point-to-Multipoint Hybrid
case: ospf_point_to_multipoint_hybrid/test.py
- name: OSPF Point-to-Multipoint
case: ospf_point_to_multipoint/test.py
- name: RIP Basic
case: rip_basic/test.py
@@ -0,0 +1 @@
test.adoc
@@ -0,0 +1,49 @@
=== OSPF Point-to-Multipoint
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/routing/ospf_point_to_multipoint]
==== Description
Verify OSPF point-to-multipoint (non-broadcast) interface type by
configuring three routers on a shared multi-access network with the
ietf-ospf 'point-to-multipoint' interface type and static neighbors.
This maps to FRR's 'point-to-multipoint non-broadcast' network type,
which requires manual neighbor configuration since there is no
multicast neighbor discovery.
R2 acts as the hub, bridging two physical links (link1, link2) into a
single broadcast domain (br0). R1 and R3 each connect to one of R2's
ports. The test verifies that all routers form OSPF adjacencies via
unicast, exchange routes, and that the interface type is correctly
reported as point-to-multipoint.
....
+------------------+ +------------------+
| R1 | | R3 |
| 10.0.1.1/32 | | 10.0.3.1/32 |
| (lo) | | (lo) |
+--------+---------+ +--------+---------+
| .1 | .3
| +------------------+ |
+----link1------+ R2 +------link2--------+
| 10.0.2.1/32 |
| (lo) |
| br0: 10.0.123.2 |
+------------------+
10.0.123.0/24
(P2MP non-broadcast / shared segment)
....
==== Topology
image::topology.svg[OSPF Point-to-Multipoint topology, align=center, scaledwidth=75%]
==== Sequence
. Set up topology and attach to target DUTs
. Configure targets
. Wait for OSPF routes
. Verify interface type is point-to-multipoint
. Verify connectivity between all DUTs
+341
View File
@@ -0,0 +1,341 @@
#!/usr/bin/env python3
"""OSPF Point-to-Multipoint
Verify OSPF point-to-multipoint (non-broadcast) interface type by
configuring three routers on a shared multi-access network with the
ietf-ospf 'point-to-multipoint' interface type and static neighbors.
This maps to FRR's 'point-to-multipoint non-broadcast' network type,
which requires manual neighbor configuration since there is no
multicast neighbor discovery.
R2 acts as the hub, bridging two physical links (link1, link2) into a
single broadcast domain (br0). R1 and R3 each connect to one of R2's
ports. The test verifies that all routers form OSPF adjacencies via
unicast, exchange routes, and that the interface type is correctly
reported as point-to-multipoint.
....
+------------------+ +------------------+
| R1 | | R3 |
| 10.0.1.1/32 | | 10.0.3.1/32 |
| (lo) | | (lo) |
+--------+---------+ +--------+---------+
| .1 | .3
| +------------------+ |
+----link1------+ R2 +------link2--------+
| 10.0.2.1/32 |
| (lo) |
| br0: 10.0.123.2 |
+------------------+
10.0.123.0/24
(P2MP non-broadcast / shared segment)
....
"""
import infamy
import infamy.route as route
from infamy.util import until, parallel
def config_target1(target, link, data):
target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": link,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.123.1",
"prefix-length": 24
}]
}
},
{
"name": data,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.10.1",
"prefix-length": 24
}]
}
},
{
"name": "lo",
"enabled": True,
"ipv4": {
"address": [{
"ip": "10.0.1.1",
"prefix-length": 32
}]
}
}
]
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"redistribute": {
"redistribute": [{
"protocol": "connected"
}]
},
"areas": {
"area": [{
"area-id": "0.0.0.0",
"interfaces": {
"interface": [{
"name": link,
"enabled": True,
"hello-interval": 1,
"dead-interval": 3,
"interface-type": "point-to-multipoint",
"static-neighbors": {
"neighbor": [
{"identifier": "10.0.123.2"},
{"identifier": "10.0.123.3"}
]
}
}, {
"name": "lo",
"enabled": True
}]
}
}]
}
}
}]
}
}
}
})
def config_target2(target, link1, link2):
target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": "br0",
"type": "infix-if-type:bridge",
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.123.2",
"prefix-length": 24
}]
}
},
{
"name": link1,
"enabled": True,
"infix-interfaces:bridge-port": {
"bridge": "br0"
}
},
{
"name": link2,
"enabled": True,
"infix-interfaces:bridge-port": {
"bridge": "br0"
}
},
{
"name": "lo",
"enabled": True,
"ipv4": {
"address": [{
"ip": "10.0.2.1",
"prefix-length": 32
}]
}
}
]
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"redistribute": {
"redistribute": [{
"protocol": "connected"
}]
},
"areas": {
"area": [{
"area-id": "0.0.0.0",
"interfaces": {
"interface": [{
"name": "br0",
"enabled": True,
"hello-interval": 1,
"dead-interval": 3,
"interface-type": "point-to-multipoint",
"static-neighbors": {
"neighbor": [
{"identifier": "10.0.123.1"},
{"identifier": "10.0.123.3"}
]
}
}, {
"name": "lo",
"enabled": True
}]
}
}]
}
}
}]
}
}
}
})
def config_target3(target, link, data):
target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": link,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.123.3",
"prefix-length": 24
}]
}
},
{
"name": data,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.30.1",
"prefix-length": 24
}]
}
},
{
"name": "lo",
"enabled": True,
"ipv4": {
"address": [{
"ip": "10.0.3.1",
"prefix-length": 32
}]
}
}
]
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"redistribute": {
"redistribute": [{
"protocol": "connected"
}]
},
"areas": {
"area": [{
"area-id": "0.0.0.0",
"interfaces": {
"interface": [{
"name": link,
"enabled": True,
"hello-interval": 1,
"dead-interval": 3,
"interface-type": "point-to-multipoint",
"static-neighbors": {
"neighbor": [
{"identifier": "10.0.123.1"},
{"identifier": "10.0.123.2"}
]
}
}, {
"name": "lo",
"enabled": True
}]
}
}]
}
}
}]
}
}
}
})
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUTs"):
env = infamy.Env()
R1 = env.attach("R1", "mgmt")
R2 = env.attach("R2", "mgmt")
R3 = env.attach("R3", "mgmt")
with test.step("Configure targets"):
_, R1link = env.ltop.xlate("R1", "link")
_, R1data = env.ltop.xlate("R1", "data")
_, R2link1 = env.ltop.xlate("R2", "link1")
_, R2link2 = env.ltop.xlate("R2", "link2")
_, R3link = env.ltop.xlate("R3", "link")
_, R3data = env.ltop.xlate("R3", "data")
parallel(config_target1(R1, R1link, R1data),
config_target2(R2, R2link1, R2link2),
config_target3(R3, R3link, R3data))
with test.step("Wait for OSPF routes"):
print("Waiting for OSPF routes from all routers")
until(lambda: route.ipv4_route_exist(R1, "10.0.2.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R1, "10.0.3.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "10.0.1.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "10.0.3.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R3, "10.0.1.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R3, "10.0.2.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
with test.step("Verify interface type is point-to-multipoint"):
print("Checking OSPF interface type on all routers")
assert route.ospf_get_interface_type(R1, "0.0.0.0", R1link) == "point-to-multipoint"
assert route.ospf_get_interface_type(R2, "0.0.0.0", "br0") == "point-to-multipoint"
assert route.ospf_get_interface_type(R3, "0.0.0.0", R3link) == "point-to-multipoint"
with test.step("Verify connectivity between all DUTs"):
_, hport1 = env.ltop.xlate("PC", "data1")
_, hport2 = env.ltop.xlate("PC", "data2")
with infamy.IsolatedMacVlan(hport1) as ns1, \
infamy.IsolatedMacVlan(hport2) as ns2:
ns1.addip("10.0.10.2")
ns2.addip("10.0.30.2")
ns1.addroute("10.0.3.1/32", "10.0.10.1")
ns2.addroute("10.0.1.1/32", "10.0.30.1")
parallel(
lambda: ns1.must_reach("10.0.3.1"),
lambda: ns2.must_reach("10.0.1.1"),
)
test.succeed()
@@ -0,0 +1,39 @@
graph "3x1" {
layout="neato";
overlap="false";
esep="+20";
size=10
node [shape=record, fontname="DejaVu Sans Mono, Book"];
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
PC [
label="PC | { <mgmt1> mgmt1 | <data1> data1 | \n\n\n\n | <mgmt2> mgmt2 | <mgmt3> mgmt3 | <data2> data2 }",
pos="20,30!",
requires="controller",
];
R1 [
label="{ <mgmt> mgmt | <data> data | <link> link } | R1 \n 10.0.1.1/32 \n(lo)",
pos="160,60!",
requires="infix",
];
R2 [
label="{ <link1> link1 | <mgmt> mgmt | <link2> link2 } | R2 \n 10.0.2.1/32 \n(lo) \n(br0: 10.0.123.2/24)",
pos="160,30!",
requires="infix",
];
R3 [
label="{ <link> link | <mgmt> mgmt | <data> data } | R3 \n 10.0.3.1/32 \n(lo)",
pos="160,0!",
requires="infix",
];
PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
PC:mgmt3 -- R3:mgmt [requires="mgmt", color="lightgray"]
PC:data1 -- R1:data [taillabel="10.0.10.2/24", headlabel="10.0.10.1/24"]
R3:data -- PC:data2 [taillabel="10.0.30.1/24", headlabel="10.0.30.2/24"]
R1:link -- R2:link1 [taillabel="10.0.123.1/24"]
R3:link -- R2:link2 [taillabel="10.0.123.3/24"]
}
@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: 3x1 Pages: 1 -->
<svg width="720pt" height="312pt"
viewBox="0.00 0.00 720.00 312.15" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(0.98 0.98) rotate(0) translate(4 314.02)">
<title>3x1</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-314.02 729.56,-314.02 729.56,4 -4,4"/>
<!-- PC -->
<g id="node1" class="node">
<title>PC</title>
<polygon fill="none" stroke="black" points="0,-61.51 0,-248.51 91,-248.51 91,-61.51 0,-61.51"/>
<text text-anchor="middle" x="16.5" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">PC</text>
<polyline fill="none" stroke="black" points="33,-61.51 33,-248.51 "/>
<text text-anchor="middle" x="62" y="-233.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
<polyline fill="none" stroke="black" points="33,-225.51 91,-225.51 "/>
<text text-anchor="middle" x="62" y="-210.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
<polyline fill="none" stroke="black" points="33,-202.51 91,-202.51 "/>
<polyline fill="none" stroke="black" points="33,-130.51 91,-130.51 "/>
<text text-anchor="middle" x="62" y="-115.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
<polyline fill="none" stroke="black" points="33,-107.51 91,-107.51 "/>
<text text-anchor="middle" x="62" y="-92.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt3</text>
<polyline fill="none" stroke="black" points="33,-84.51 91,-84.51 "/>
<text text-anchor="middle" x="62" y="-69.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
</g>
<!-- R1 -->
<g id="node2" class="node">
<title>R1</title>
<polygon fill="none" stroke="black" points="518.56,-240.52 518.56,-309.52 692.56,-309.52 692.56,-240.52 518.56,-240.52"/>
<text text-anchor="middle" x="543.56" y="-294.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="518.56,-286.52 568.56,-286.52 "/>
<text text-anchor="middle" x="543.56" y="-271.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="518.56,-263.52 568.56,-263.52 "/>
<text text-anchor="middle" x="543.56" y="-248.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="568.56,-240.52 568.56,-309.52 "/>
<text text-anchor="middle" x="630.56" y="-286.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">R1 </text>
<text text-anchor="middle" x="630.56" y="-271.32" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 10.0.1.1/32 </text>
<text text-anchor="middle" x="630.56" y="-256.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
</g>
<!-- PC&#45;&#45;R1 -->
<g id="edge1" class="edge">
<title>PC:mgmt1&#45;&#45;R1:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-237.01C91.5,-237.01 518.56,-298.02 518.56,-298.02"/>
</g>
<!-- PC&#45;&#45;R1 -->
<g id="edge4" class="edge">
<title>PC:data1&#45;&#45;R1:data</title>
<path fill="none" stroke="cornflowerblue" stroke-width="2" d="M91.5,-214.01C91.5,-214.01 518.56,-275.02 518.56,-275.02"/>
<text text-anchor="middle" x="473.06" y="-278.82" font-family="DejaVu Serif, Book" font-size="14.00">10.0.10.1/24</text>
<text text-anchor="middle" x="137" y="-217.81" font-family="DejaVu Serif, Book" font-size="14.00">10.0.10.2/24</text>
</g>
<!-- R2 -->
<g id="node3" class="node">
<title>R2</title>
<polygon fill="none" stroke="black" points="485.56,-120.51 485.56,-189.51 725.56,-189.51 725.56,-120.51 485.56,-120.51"/>
<text text-anchor="middle" x="514.56" y="-174.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">link1</text>
<polyline fill="none" stroke="black" points="485.56,-166.51 543.56,-166.51 "/>
<text text-anchor="middle" x="514.56" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="485.56,-143.51 543.56,-143.51 "/>
<text text-anchor="middle" x="514.56" y="-128.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">link2</text>
<polyline fill="none" stroke="black" points="543.56,-120.51 543.56,-189.51 "/>
<text text-anchor="middle" x="634.56" y="-173.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">R2 </text>
<text text-anchor="middle" x="634.56" y="-158.81" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 10.0.2.1/32 </text>
<text text-anchor="middle" x="634.56" y="-143.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo) </text>
<text text-anchor="middle" x="634.56" y="-128.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">(br0: 10.0.123.2/24)</text>
</g>
<!-- PC&#45;&#45;R2 -->
<g id="edge2" class="edge">
<title>PC:mgmt2&#45;&#45;R2:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-119.01C91.5,-119.01 485.56,-155.01 485.56,-155.01"/>
</g>
<!-- R3 -->
<g id="node4" class="node">
<title>R3</title>
<polygon fill="none" stroke="black" points="518.56,-0.5 518.56,-69.5 692.56,-69.5 692.56,-0.5 518.56,-0.5"/>
<text text-anchor="middle" x="543.56" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="518.56,-46.5 568.56,-46.5 "/>
<text text-anchor="middle" x="543.56" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="518.56,-23.5 568.56,-23.5 "/>
<text text-anchor="middle" x="543.56" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="568.56,-0.5 568.56,-69.5 "/>
<text text-anchor="middle" x="630.56" y="-46.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">R3 </text>
<text text-anchor="middle" x="630.56" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 10.0.3.1/32 </text>
<text text-anchor="middle" x="630.56" y="-16.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
</g>
<!-- PC&#45;&#45;R3 -->
<g id="edge3" class="edge">
<title>PC:mgmt3&#45;&#45;R3:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-96.01C91.5,-96.01 518.56,-35 518.56,-35"/>
</g>
<!-- R1&#45;&#45;R2 -->
<g id="edge6" class="edge">
<title>R1:link&#45;&#45;R2:link1</title>
<path fill="none" stroke="cornflowerblue" stroke-width="2" d="M543.56,-240.02C543.56,-240.02 514.56,-190.01 514.56,-190.01"/>
<text text-anchor="middle" x="493.56" y="-228.82" font-family="DejaVu Serif, Book" font-size="14.00">10.0.123.1/24</text>
</g>
<!-- R3&#45;&#45;PC -->
<g id="edge5" class="edge">
<title>R3:data&#45;&#45;PC:data2</title>
<path fill="none" stroke="cornflowerblue" stroke-width="2" d="M518.56,-12C518.56,-12 91.5,-73.01 91.5,-73.01"/>
<text text-anchor="middle" x="137" y="-76.81" font-family="DejaVu Serif, Book" font-size="14.00">10.0.30.2/24</text>
<text text-anchor="middle" x="473.06" y="-15.8" font-family="DejaVu Serif, Book" font-size="14.00">10.0.30.1/24</text>
</g>
<!-- R3&#45;&#45;R2 -->
<g id="edge7" class="edge">
<title>R3:link&#45;&#45;R2:link2</title>
<path fill="none" stroke="cornflowerblue" stroke-width="2" d="M543.56,-70C543.56,-70 514.56,-120.01 514.56,-120.01"/>
<text text-anchor="middle" x="493.56" y="-73.8" font-family="DejaVu Serif, Book" font-size="14.00">10.0.123.3/24</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

@@ -0,0 +1 @@
test.adoc
@@ -0,0 +1,46 @@
=== OSPF Point-to-Multipoint Hybrid
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/routing/ospf_point_to_multipoint_hybrid]
==== Description
Verify OSPF point-to-multipoint hybrid (broadcast) interface type by
configuring three routers on a shared multi-access network with the
ietf-ospf 'hybrid' interface type. This maps to FRR's 'point-to-multipoint'
network type, which uses multicast for neighbor discovery.
R2 acts as the hub, bridging two physical links (link1, link2) into a
single broadcast domain (br0). R1 and R3 each connect to one of R2's
ports. The test verifies that all routers form OSPF adjacencies, exchange
routes, and that the interface type is correctly reported as hybrid.
....
+------------------+ +------------------+
| R1 | | R3 |
| 10.0.1.1/32 | | 10.0.3.1/32 |
| (lo) | | (lo) |
+--------+---------+ +--------+---------+
| .1 | .3
| +------------------+ |
+----link1------+ R2 +------link2--------+
| 10.0.2.1/32 |
| (lo) |
| br0: 10.0.123.2 |
+------------------+
10.0.123.0/24
(P2MP hybrid / shared segment)
....
==== Topology
image::topology.svg[OSPF Point-to-Multipoint Hybrid topology, align=center, scaledwidth=75%]
==== Sequence
. Set up topology and attach to target DUTs
. Configure targets
. Wait for OSPF routes
. Verify interface type is hybrid
. Verify connectivity between all DUTs
+334
View File
@@ -0,0 +1,334 @@
#!/usr/bin/env python3
"""OSPF Point-to-Multipoint Hybrid
Verify OSPF point-to-multipoint hybrid (broadcast) interface type by
configuring three routers on a shared multi-access network with the
ietf-ospf 'hybrid' interface type. This maps to FRR's 'point-to-multipoint'
network type, which uses multicast for neighbor discovery.
R2 acts as the hub, bridging two physical links (link1, link2) into a
single broadcast domain (br0). R1 and R3 each connect to one of R2's
ports. The test verifies that all routers form OSPF adjacencies, exchange
routes, and that the interface type is correctly reported as hybrid.
....
+------------------+ +------------------+
| R1 | | R3 |
| 10.0.1.1/32 | | 10.0.3.1/32 |
| (lo) | | (lo) |
+--------+---------+ +--------+---------+
| .1 | .3
| +------------------+ |
+----link1------+ R2 +------link2--------+
| 10.0.2.1/32 |
| (lo) |
| br0: 10.0.123.2 |
+------------------+
10.0.123.0/24
(P2MP hybrid / shared segment)
....
"""
import infamy
import infamy.route as route
from infamy.util import until, parallel
def config_target1(target, link, data):
target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": link,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.123.1",
"prefix-length": 24
}]
}
},
{
"name": data,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.10.1",
"prefix-length": 24
}]
}
},
{
"name": "lo",
"enabled": True,
"ipv4": {
"address": [{
"ip": "10.0.1.1",
"prefix-length": 32
}]
}
}
]
}
},
"ietf-system": {
"system": {
"hostname": "R1"
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"redistribute": {
"redistribute": [{
"protocol": "connected"
}]
},
"areas": {
"area": [{
"area-id": "0.0.0.0",
"interfaces": {
"interface": [{
"name": link,
"enabled": True,
"interface-type": "hybrid",
"hello-interval": 1,
"dead-interval": 3
}, {
"name": "lo",
"enabled": True
}]
}
}]
}
}
}]
}
}
}
})
def config_target2(target, link1, link2):
target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": "br0",
"type": "infix-if-type:bridge",
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.123.2",
"prefix-length": 24
}]
}
},
{
"name": link1,
"enabled": True,
"infix-interfaces:bridge-port": {
"bridge": "br0"
}
},
{
"name": link2,
"enabled": True,
"infix-interfaces:bridge-port": {
"bridge": "br0"
}
},
{
"name": "lo",
"enabled": True,
"ipv4": {
"address": [{
"ip": "10.0.2.1",
"prefix-length": 32
}]
}
}
]
}
},
"ietf-system": {
"system": {
"hostname": "R2"
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"redistribute": {
"redistribute": [{
"protocol": "connected"
}]
},
"areas": {
"area": [{
"area-id": "0.0.0.0",
"interfaces": {
"interface": [{
"name": "br0",
"enabled": True,
"interface-type": "hybrid",
"hello-interval": 1,
"dead-interval": 3
}, {
"name": "lo",
"enabled": True
}]
}
}]
}
}
}]
}
}
}
})
def config_target3(target, link, data):
target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": link,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.123.3",
"prefix-length": 24
}]
}
},
{
"name": data,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.30.1",
"prefix-length": 24
}]
}
},
{
"name": "lo",
"enabled": True,
"ipv4": {
"address": [{
"ip": "10.0.3.1",
"prefix-length": 32
}]
}
}
]
}
},
"ietf-system": {
"system": {
"hostname": "R3"
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"redistribute": {
"redistribute": [{
"protocol": "connected"
}]
},
"areas": {
"area": [{
"area-id": "0.0.0.0",
"interfaces": {
"interface": [{
"name": link,
"enabled": True,
"interface-type": "hybrid",
"hello-interval": 1,
"dead-interval": 3
}, {
"name": "lo",
"enabled": True
}]
}
}]
}
}
}]
}
}
}
})
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUTs"):
env = infamy.Env()
R1 = env.attach("R1", "mgmt")
R2 = env.attach("R2", "mgmt")
R3 = env.attach("R3", "mgmt")
with test.step("Configure targets"):
_, R1link = env.ltop.xlate("R1", "link")
_, R1data = env.ltop.xlate("R1", "data")
_, R2link1 = env.ltop.xlate("R2", "link1")
_, R2link2 = env.ltop.xlate("R2", "link2")
_, R3link = env.ltop.xlate("R3", "link")
_, R3data = env.ltop.xlate("R3", "data")
parallel(config_target1(R1, R1link, R1data),
config_target2(R2, R2link1, R2link2),
config_target3(R3, R3link, R3data))
with test.step("Wait for OSPF routes"):
print("Waiting for OSPF routes to converge")
until(lambda: route.ipv4_route_exist(R1, "10.0.2.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R1, "10.0.3.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "10.0.1.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "10.0.3.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R3, "10.0.1.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R3, "10.0.2.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
with test.step("Verify interface type is hybrid"):
print("Checking OSPF interface type on all routers")
assert route.ospf_get_interface_type(R1, "0.0.0.0", R1link) == "hybrid"
assert route.ospf_get_interface_type(R2, "0.0.0.0", "br0") == "hybrid"
assert route.ospf_get_interface_type(R3, "0.0.0.0", R3link) == "hybrid"
with test.step("Verify connectivity between all DUTs"):
_, hport1 = env.ltop.xlate("PC", "data1")
_, hport2 = env.ltop.xlate("PC", "data2")
with infamy.IsolatedMacVlan(hport1) as ns1, \
infamy.IsolatedMacVlan(hport2) as ns2:
ns1.addip("10.0.10.2")
ns2.addip("10.0.30.2")
ns1.addroute("10.0.3.1/32", "10.0.10.1")
ns2.addroute("10.0.1.1/32", "10.0.30.1")
parallel(
lambda: ns1.must_reach("10.0.3.1"),
lambda: ns2.must_reach("10.0.1.1"),
)
test.succeed()
@@ -0,0 +1,39 @@
graph "3r-p2mp" {
layout="neato";
overlap="false";
esep="+20";
size=10
node [shape=record, fontname="DejaVu Sans Mono, Book"];
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
PC [
label="PC | { <mgmt1> mgmt1 | <data1> data1 | \n\n\n\n | <mgmt2> mgmt2 | <mgmt3> mgmt3 | <data2> data2 }",
pos="20,30!",
requires="controller",
];
R1 [
label="{ <mgmt> mgmt | <data> data | <link> link } | R1 \n 10.0.1.1/32 \n(lo)",
pos="160,60!",
requires="infix",
];
R2 [
label="{ <link1> link1 | <mgmt> mgmt | <link2> link2 } | R2 \n 10.0.2.1/32 \n(lo) \n(br0: 10.0.123.2/24)",
pos="160,30!",
requires="infix",
];
R3 [
label="{ <link> link | <mgmt> mgmt | <data> data } | R3 \n 10.0.3.1/32 \n(lo)",
pos="160,0!",
requires="infix",
];
PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
PC:mgmt3 -- R3:mgmt [requires="mgmt", color="lightgray"]
PC:data1 -- R1:data [taillabel="10.0.10.2/24", headlabel="10.0.10.1/24"]
R3:data -- PC:data2 [taillabel="10.0.30.1/24", headlabel="10.0.30.2/24"]
R1:link -- R2:link1 [taillabel="10.0.123.1/24"]
R3:link -- R2:link2 [taillabel="10.0.123.3/24"]
}
@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: 3r&#45;p2mp Pages: 1 -->
<svg width="720pt" height="312pt"
viewBox="0.00 0.00 720.00 312.15" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(0.98 0.98) rotate(0) translate(4 314.02)">
<title>3r&#45;p2mp</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-314.02 729.56,-314.02 729.56,4 -4,4"/>
<!-- PC -->
<g id="node1" class="node">
<title>PC</title>
<polygon fill="none" stroke="black" points="0,-61.51 0,-248.51 91,-248.51 91,-61.51 0,-61.51"/>
<text text-anchor="middle" x="16.5" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">PC</text>
<polyline fill="none" stroke="black" points="33,-61.51 33,-248.51 "/>
<text text-anchor="middle" x="62" y="-233.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
<polyline fill="none" stroke="black" points="33,-225.51 91,-225.51 "/>
<text text-anchor="middle" x="62" y="-210.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
<polyline fill="none" stroke="black" points="33,-202.51 91,-202.51 "/>
<polyline fill="none" stroke="black" points="33,-130.51 91,-130.51 "/>
<text text-anchor="middle" x="62" y="-115.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
<polyline fill="none" stroke="black" points="33,-107.51 91,-107.51 "/>
<text text-anchor="middle" x="62" y="-92.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt3</text>
<polyline fill="none" stroke="black" points="33,-84.51 91,-84.51 "/>
<text text-anchor="middle" x="62" y="-69.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
</g>
<!-- R1 -->
<g id="node2" class="node">
<title>R1</title>
<polygon fill="none" stroke="black" points="518.56,-240.52 518.56,-309.52 692.56,-309.52 692.56,-240.52 518.56,-240.52"/>
<text text-anchor="middle" x="543.56" y="-294.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="518.56,-286.52 568.56,-286.52 "/>
<text text-anchor="middle" x="543.56" y="-271.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="518.56,-263.52 568.56,-263.52 "/>
<text text-anchor="middle" x="543.56" y="-248.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="568.56,-240.52 568.56,-309.52 "/>
<text text-anchor="middle" x="630.56" y="-286.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">R1 </text>
<text text-anchor="middle" x="630.56" y="-271.32" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 10.0.1.1/32 </text>
<text text-anchor="middle" x="630.56" y="-256.32" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
</g>
<!-- PC&#45;&#45;R1 -->
<g id="edge1" class="edge">
<title>PC:mgmt1&#45;&#45;R1:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-237.01C91.5,-237.01 518.56,-298.02 518.56,-298.02"/>
</g>
<!-- PC&#45;&#45;R1 -->
<g id="edge4" class="edge">
<title>PC:data1&#45;&#45;R1:data</title>
<path fill="none" stroke="cornflowerblue" stroke-width="2" d="M91.5,-214.01C91.5,-214.01 518.56,-275.02 518.56,-275.02"/>
<text text-anchor="middle" x="473.06" y="-278.82" font-family="DejaVu Serif, Book" font-size="14.00">10.0.10.1/24</text>
<text text-anchor="middle" x="137" y="-217.81" font-family="DejaVu Serif, Book" font-size="14.00">10.0.10.2/24</text>
</g>
<!-- R2 -->
<g id="node3" class="node">
<title>R2</title>
<polygon fill="none" stroke="black" points="485.56,-120.51 485.56,-189.51 725.56,-189.51 725.56,-120.51 485.56,-120.51"/>
<text text-anchor="middle" x="514.56" y="-174.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">link1</text>
<polyline fill="none" stroke="black" points="485.56,-166.51 543.56,-166.51 "/>
<text text-anchor="middle" x="514.56" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="485.56,-143.51 543.56,-143.51 "/>
<text text-anchor="middle" x="514.56" y="-128.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">link2</text>
<polyline fill="none" stroke="black" points="543.56,-120.51 543.56,-189.51 "/>
<text text-anchor="middle" x="634.56" y="-173.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">R2 </text>
<text text-anchor="middle" x="634.56" y="-158.81" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 10.0.2.1/32 </text>
<text text-anchor="middle" x="634.56" y="-143.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo) </text>
<text text-anchor="middle" x="634.56" y="-128.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">(br0: 10.0.123.2/24)</text>
</g>
<!-- PC&#45;&#45;R2 -->
<g id="edge2" class="edge">
<title>PC:mgmt2&#45;&#45;R2:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-119.01C91.5,-119.01 485.56,-155.01 485.56,-155.01"/>
</g>
<!-- R3 -->
<g id="node4" class="node">
<title>R3</title>
<polygon fill="none" stroke="black" points="518.56,-0.5 518.56,-69.5 692.56,-69.5 692.56,-0.5 518.56,-0.5"/>
<text text-anchor="middle" x="543.56" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="518.56,-46.5 568.56,-46.5 "/>
<text text-anchor="middle" x="543.56" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="518.56,-23.5 568.56,-23.5 "/>
<text text-anchor="middle" x="543.56" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="568.56,-0.5 568.56,-69.5 "/>
<text text-anchor="middle" x="630.56" y="-46.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">R3 </text>
<text text-anchor="middle" x="630.56" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 10.0.3.1/32 </text>
<text text-anchor="middle" x="630.56" y="-16.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
</g>
<!-- PC&#45;&#45;R3 -->
<g id="edge3" class="edge">
<title>PC:mgmt3&#45;&#45;R3:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-96.01C91.5,-96.01 518.56,-35 518.56,-35"/>
</g>
<!-- R1&#45;&#45;R2 -->
<g id="edge6" class="edge">
<title>R1:link&#45;&#45;R2:link1</title>
<path fill="none" stroke="cornflowerblue" stroke-width="2" d="M543.56,-240.02C543.56,-240.02 514.56,-190.01 514.56,-190.01"/>
<text text-anchor="middle" x="493.56" y="-228.82" font-family="DejaVu Serif, Book" font-size="14.00">10.0.123.1/24</text>
</g>
<!-- R3&#45;&#45;PC -->
<g id="edge5" class="edge">
<title>R3:data&#45;&#45;PC:data2</title>
<path fill="none" stroke="cornflowerblue" stroke-width="2" d="M518.56,-12C518.56,-12 91.5,-73.01 91.5,-73.01"/>
<text text-anchor="middle" x="137" y="-76.81" font-family="DejaVu Serif, Book" font-size="14.00">10.0.30.2/24</text>
<text text-anchor="middle" x="473.06" y="-15.8" font-family="DejaVu Serif, Book" font-size="14.00">10.0.30.1/24</text>
</g>
<!-- R3&#45;&#45;R2 -->
<g id="edge7" class="edge">
<title>R3:link&#45;&#45;R2:link2</title>
<path fill="none" stroke="cornflowerblue" stroke-width="2" d="M543.56,-70C543.56,-70 514.56,-120.01 514.56,-120.01"/>
<text text-anchor="middle" x="493.56" y="-73.8" font-family="DejaVu Serif, Book" font-size="14.00">10.0.123.3/24</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB