diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md index 89de004c4..a473a12c4 100644 --- a/doc/ChangeLog.md +++ b/doc/ChangeLog.md @@ -14,6 +14,9 @@ All notable changes to the project are documented in this file. `ripng`/`ospfv3` type and the IPv6 address-family - Support building without Frr, which also drops bash. The minimal defconfigs are now built this way, with static routing only, issue #1670 +- Configurable OSPF auto-cost reference bandwidth, for OSPFv2 and OSPFv3, + issue #1576. The default, 100000 Mbit/s, is the value used before, so + link costs do not change on upgrade. The CLI shows it in `show ip ospf` - Check for a new software release on demand: `upgrade` without a bundle in the CLI, the Check now button on the WebUI dashboard, or the `check-update` RPC, see [Unattended Software Updates][unattended] diff --git a/doc/routing.md b/doc/routing.md index b4af8e5bf..d0516a3b0 100644 --- a/doc/routing.md +++ b/doc/routing.md @@ -215,11 +215,13 @@ admin@example:/> ### OSPF global settings In addition to *area* and *interface* specific settings, OSPF provides -global settings for route redistribution and OSPF router identifier. +global settings for route redistribution, OSPF router identifier, and +the reference bandwidth used to calculate interface costs.
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf
admin@example:/config/routing/…/ospf/> set ?
area List of OSPF areas.
+ auto-cost Interface auto-cost configuration state.
default-route-advertise Distribute default route to network
explicit-router-id Defined in RFC 2328. A 32-bit number
redistribute Redistribute protocols into OSPF
@@ -242,6 +244,24 @@ admin@example:/config/routing/…/ospf/> set
router will distribute a default route even when it lacks a default
route. The default route will be distributed as an *external type-2*
(E2) route.
+- Reference bandwidth: Routers from different vendors only agree on
+ the shortest path if they use the same reference bandwidth. The cost
+ of an interface without an explicit `cost` is the reference bandwidth
+ divided by the link speed, rounded, with 1 as the lowest cost. The
+ default is 100000 Mbit/s, which gives a 1 Gbit/s link cost 100. Use
+ `set auto-cost reference-bandwidth 10000` (Mbit/s) to change it. A
+ link of unknown speed, e.g., a virtual link, counts as 10000 Mbit/s.
+ Auto-cost can not be disabled.
+
+`show ip ospf` shows the reference bandwidth in use, and `show ipv6
+ospf` does the same for OSPFv3.
+
+admin@example:/> show ip ospf
+ OSPF Routing Process, Router ID: 192.168.10.1
+ Reference bandwidth: 100000 Mbit/s
+ Number of areas attached to this router: 1
+ ...
+
### Debug OSPFv2
diff --git a/src/confd/src/routing.c b/src/confd/src/routing.c
index 2a5a33cf9..328013849 100644
--- a/src/confd/src/routing.c
+++ b/src/confd/src/routing.c
@@ -315,7 +315,7 @@ int parse_ospf_areas(sr_session_ctx_t *session, struct lyd_node *areas, FILE *fp
int parse_ospf(sr_session_ctx_t *session, struct lyd_node *ospf)
{
struct lyd_node *areas, *default_route, *debug;
- const char *router_id;
+ const char *router_id, *refbw;
int bfd_enabled = 0;
int num_areas = 0;
FILE *fp;
@@ -381,6 +381,9 @@ int parse_ospf(sr_session_ctx_t *session, struct lyd_node *ospf)
}
}
+ refbw = lydx_get_cattr(lydx_get_child(ospf, "auto-cost"), "reference-bandwidth");
+ if (refbw)
+ fprintf(fp, " auto-cost reference-bandwidth %s\n", refbw);
if (router_id)
fprintf(fp, " ospf router-id %s\n", router_id);
fclose(fp);
@@ -429,7 +432,7 @@ static int parse_ospf6_areas(struct lyd_node *areas, FILE *fp)
int parse_ospf6(sr_session_ctx_t *session, struct lyd_node *ospf)
{
struct lyd_node *areas, *default_route, *debug;
- const char *router_id;
+ const char *router_id, *refbw;
int bfd_enabled = 0;
int num_areas = 0;
FILE *fp;
@@ -489,6 +492,9 @@ int parse_ospf6(sr_session_ctx_t *session, struct lyd_node *ospf)
}
}
+ refbw = lydx_get_cattr(lydx_get_child(ospf, "auto-cost"), "reference-bandwidth");
+ if (refbw)
+ fprintf(fp, " auto-cost reference-bandwidth %s\n", refbw);
if (router_id)
fprintf(fp, " ospf6 router-id %s\n", router_id);
fclose(fp);
diff --git a/src/confd/yang/confd.inc b/src/confd/yang/confd.inc
index 054dca82b..d00f50617 100644
--- a/src/confd/yang/confd.inc
+++ b/src/confd/yang/confd.inc
@@ -14,7 +14,7 @@ MODULES=(
"ietf-routing@2018-03-13.yang"
"ietf-ipv6-unicast-routing@2018-03-13.yang"
"ietf-ipv4-unicast-routing@2018-03-13.yang"
- "ietf-ospf@2022-10-19.yang -e bfd -e explicit-router-id -e hybrid-interface"
+ "ietf-ospf@2022-10-19.yang -e auto-cost -e bfd -e explicit-router-id -e hybrid-interface"
"ietf-rip@2020-02-20.yang"
"iana-bfd-types@2021-10-21.yang"
"ietf-bfd-types@2022-09-22.yang"
@@ -31,7 +31,7 @@ MODULES=(
"ieee802-dot1q-types@2022-10-29.yang"
"infix-ip@2026-04-28.yang"
"infix-if-type@2026-01-07.yang"
- "infix-routing@2026-09-28.yang"
+ "infix-routing@2026-10-07.yang"
"ieee802-dot1ab-lldp@2022-03-15.yang"
"infix-lldp@2025-05-05.yang"
"infix-dhcp-common@2025-12-21.yang"
diff --git a/src/confd/yang/confd/infix-routing.yang b/src/confd/yang/confd/infix-routing.yang
index 25b495898..8e492bf2a 100644
--- a/src/confd/yang/confd/infix-routing.yang
+++ b/src/confd/yang/confd/infix-routing.yang
@@ -26,12 +26,19 @@ module infix-routing {
contact "kernelkit@googlegroups.com";
description "Deviations and augments for ietf-routing, ietf-ospf, and ietf-rip.";
+ revision 2026-10-07 {
+ description "Add OSPF auto-cost reference-bandwidth, for OSPFv2 and
+ OSPFv3. The default 100000 Mbit/s matches the value
+ previously used, and auto-cost can not be disabled.";
+ reference "internal";
+ }
+
revision 2026-09-28 {
description "Add ospf, rip, and bfd features, where ospf covers OSPFv2
and OSPFv3, and rip covers RIPv2 and RIPng. A build without
FRR leaves them off, so only static routes can be configured
and the device advertises exactly what it supports.";
- reference "Issue #1670";
+ reference "internal";
}
revision 2026-07-22 {
@@ -67,19 +74,19 @@ module infix-routing {
Allows displaying which area a route was learned from,
how long neighbors have been up, their DR/BDR role, and
the interface with local address.";
- reference "Issue #1190";
+ reference "internal";
}
revision 2025-11-11 {
description "Add BFD IP single-hop support (RFC 9127).
Deviate session-statistics (not implemented yet).";
- reference "RFC 9127, Issue #1190";
+ reference "RFC 9127";
}
revision 2025-11-10 {
description "Add must constraint to prevent backbone area as stub/NSSA.
Enable OSPF operational data for CLI: un-deviate priority,
hello-timer, wait-timer, route metric and route-tag fields.";
- reference "RFC 2328, RFC 3101, Issue #1190";
+ reference "RFC 2328, RFC 3101";
}
revision 2024-11-27 {
description "Deviate address-family in OSPF";
@@ -423,8 +430,21 @@ module infix-routing {
}
}
- deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:auto-cost" {
- deviate not-supported;
+ /* The reference bandwidth must be the same on all routers in the OSPF
+ domain. Frr always derives the interface cost from it, unless a cost
+ is set on the interface, so auto-cost can not be disabled. */
+ deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:auto-cost/ospf:enabled" {
+ deviate add {
+ default "true";
+ must ". = 'true'" {
+ error-message "Disabling OSPF auto-cost is not supported.";
+ }
+ }
+ }
+ deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:auto-cost/ospf:reference-bandwidth" {
+ deviate add {
+ default "100000";
+ }
}
deviation "/rt:routing/rt:control-plane-protocols/rt:control-plane-protocol/ospf:ospf/ospf:database" {
deviate not-supported;
diff --git a/src/confd/yang/confd/infix-routing@2026-09-28.yang b/src/confd/yang/confd/infix-routing@2026-10-07.yang
similarity index 100%
rename from src/confd/yang/confd/infix-routing@2026-09-28.yang
rename to src/confd/yang/confd/infix-routing@2026-10-07.yang
diff --git a/src/statd/python/cli_pretty/cli_pretty.py b/src/statd/python/cli_pretty/cli_pretty.py
index ad661b556..f538aa573 100755
--- a/src/statd/python/cli_pretty/cli_pretty.py
+++ b/src/statd/python/cli_pretty/cli_pretty.py
@@ -5250,6 +5250,9 @@ def show_ospf(json_data):
# OSPF Process header
print(f" OSPF Routing Process, Router ID: {router_id}")
+ refbw = ospf.get('auto-cost', {}).get('reference-bandwidth')
+ if refbw is not None:
+ print(f" Reference bandwidth: {refbw} Mbit/s")
print(f" Number of areas attached to this router: {len(areas)}")
print()
diff --git a/src/statd/python/yanger/ietf_ospf.py b/src/statd/python/yanger/ietf_ospf.py
index 8accac9c0..5e9bd4c89 100644
--- a/src/statd/python/yanger/ietf_ospf.py
+++ b/src/statd/python/yanger/ietf_ospf.py
@@ -162,6 +162,27 @@ def add_routes(ospf, ipv6=False):
insert(ospf, "ietf-ospf:local-rib", "ietf-ospf:route", routes)
+def add_auto_cost(ospf, daemon):
+ """Report the auto-cost reference bandwidth (Mbit/s) of an OSPF daemon.
+
+ Frr has no JSON for it, and its running-config only lists the value
+ when it differs from the default, 100000 Mbit/s in both ospfd and
+ ospf6d. Auto-cost is always enabled in Frr."""
+ refbw = 100000
+
+ cmd = ("vtysh", "-c", f"show running-config {daemon}")
+ for line in HOST.run(cmd, default="").splitlines():
+ words = line.split()
+ if words[:2] == ["auto-cost", "reference-bandwidth"]:
+ refbw = int(words[2])
+ break
+
+ ospf["ietf-ospf:auto-cost"] = {
+ "enabled": True,
+ "reference-bandwidth": refbw,
+ }
+
+
def add_areas(control_protocols):
"""Populate OSPF status"""
cmd = ['/usr/libexec/statd/ospf-status']
@@ -308,6 +329,7 @@ def add_areas(control_protocols):
area["ietf-ospf:interfaces"]["ietf-ospf:interface"] = interfaces
areas.append(area)
+ add_auto_cost(control_protocol["ietf-ospf:ospf"], "ospfd")
add_routes(control_protocol["ietf-ospf:ospf"])
control_protocol["ietf-ospf:ospf"]["ietf-ospf:areas"]["ietf-ospf:area"] = areas
insert(control_protocols, "control-plane-protocol", [control_protocol])
@@ -389,6 +411,7 @@ def add_areas6(control_protocols):
area["ietf-ospf:interfaces"]["ietf-ospf:interface"] = interfaces
areas.append(area)
+ add_auto_cost(control_protocol["ietf-ospf:ospf"], "ospf6d")
add_routes(control_protocol["ietf-ospf:ospf"], ipv6=True)
control_protocol["ietf-ospf:ospf"]["ietf-ospf:areas"]["ietf-ospf:area"] = areas
insert(control_protocols, "control-plane-protocol", [control_protocol])
diff --git a/test/case/routing/Readme.adoc b/test/case/routing/Readme.adoc
index 6152bc0ad..4b2df2077 100644
--- a/test/case/routing/Readme.adoc
+++ b/test/case/routing/Readme.adoc
@@ -10,6 +10,7 @@ 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 auto-cost reference bandwidth and interface cost
- 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
@@ -48,6 +49,10 @@ include::ospf_debug/Readme.adoc[]
<<<
+include::ospf_auto_cost/Readme.adoc[]
+
+<<<
+
include::ospf_point_to_multipoint_hybrid/Readme.adoc[]
<<<
diff --git a/test/case/routing/all.yaml b/test/case/routing/all.yaml
index cbd53ccd7..c240cc709 100644
--- a/test/case/routing/all.yaml
+++ b/test/case/routing/all.yaml
@@ -29,6 +29,9 @@
- name: OSPF Debug Logging
case: ospf_debug/test.py
+- name: OSPF Auto-Cost Reference Bandwidth
+ suite: ospf_auto_cost/test.yaml
+
- name: OSPF Point-to-Multipoint Hybrid
suite: ospf_point_to_multipoint_hybrid/test.yaml
diff --git a/test/case/routing/ospf_auto_cost/Readme.adoc b/test/case/routing/ospf_auto_cost/Readme.adoc
new file mode 100644
index 000000000..2322b7ef7
--- /dev/null
+++ b/test/case/routing/ospf_auto_cost/Readme.adoc
@@ -0,0 +1,6 @@
+include::ospfv2.adoc[]
+
+<<<
+
+include::ospfv3.adoc[]
+
diff --git a/test/case/routing/ospf_auto_cost/ospfv2.adoc b/test/case/routing/ospf_auto_cost/ospfv2.adoc
new file mode 100644
index 000000000..7110cc19c
--- /dev/null
+++ b/test/case/routing/ospf_auto_cost/ospfv2.adoc
@@ -0,0 +1,41 @@
+=== OSPFv2 Auto-Cost Reference Bandwidth
+
+ifdef::topdoc[:imagesdir: {topdoc}../../test/case/routing/ospf_auto_cost]
+
+==== Description
+
+Verifies that the OSPFv2 interface cost follows the configured auto-cost
+reference bandwidth. The reference bandwidth must be the same on all
+routers in an OSPF domain, so it is changed on both R1 and R2.
+
+The cost of an interface is the reference bandwidth divided by the link
+speed, with 1 as the lowest cost. A link of unknown speed, e.g., a virtual
+link, counts as 10000 Mbit/s. On a 1 Gbit/s link the cost is:
+
+|===
+| Reference bandwidth | Cost
+| 100000 Mbit/s (default) | 100
+| 10000 Mbit/s | 10
+| 1000 Mbit/s | 1
+|===
+
+The test reads the link speed on each router to calculate the expected
+cost, and reads the reference bandwidth back from the operational
+datastore.
+
+==== Topology
+
+image::topology.svg[OSPFv2 Auto-Cost Reference Bandwidth topology, align=center, scaledwidth=75%]
+
+==== Sequence
+
+. Set up topology and attach to target DUTs
+. Configure OSPFv2 on R1 and R2
+. Wait for OSPFv2 adjacency between R1 and R2
+. Verify default reference bandwidth 100000 Mbit/s and link cost
+. Set reference bandwidth 10000 Mbit/s on R1 and R2
+. Verify reference bandwidth 10000 Mbit/s and link cost
+. Set reference bandwidth 1000 Mbit/s on R1 and R2
+. Verify reference bandwidth 1000 Mbit/s and link cost
+
+
diff --git a/test/case/routing/ospf_auto_cost/ospfv2.py b/test/case/routing/ospf_auto_cost/ospfv2.py
new file mode 120000
index 000000000..946566431
--- /dev/null
+++ b/test/case/routing/ospf_auto_cost/ospfv2.py
@@ -0,0 +1 @@
+test.py
\ No newline at end of file
diff --git a/test/case/routing/ospf_auto_cost/ospfv3.adoc b/test/case/routing/ospf_auto_cost/ospfv3.adoc
new file mode 100644
index 000000000..cf9cec253
--- /dev/null
+++ b/test/case/routing/ospf_auto_cost/ospfv3.adoc
@@ -0,0 +1,41 @@
+=== OSPFv3 Auto-Cost Reference Bandwidth
+
+ifdef::topdoc[:imagesdir: {topdoc}../../test/case/routing/ospf_auto_cost]
+
+==== Description
+
+Verifies that the OSPFv3 interface cost follows the configured auto-cost
+reference bandwidth. The reference bandwidth must be the same on all
+routers in an OSPF domain, so it is changed on both R1 and R2.
+
+The cost of an interface is the reference bandwidth divided by the link
+speed, with 1 as the lowest cost. A link of unknown speed, e.g., a virtual
+link, counts as 10000 Mbit/s. On a 1 Gbit/s link the cost is:
+
+|===
+| Reference bandwidth | Cost
+| 100000 Mbit/s (default) | 100
+| 10000 Mbit/s | 10
+| 1000 Mbit/s | 1
+|===
+
+The test reads the link speed on each router to calculate the expected
+cost, and reads the reference bandwidth back from the operational
+datastore.
+
+==== Topology
+
+image::topology.svg[OSPFv3 Auto-Cost Reference Bandwidth topology, align=center, scaledwidth=75%]
+
+==== Sequence
+
+. Set up topology and attach to target DUTs
+. Configure OSPFv3 on R1 and R2
+. Wait for OSPFv3 adjacency between R1 and R2
+. Verify default reference bandwidth 100000 Mbit/s and link cost
+. Set reference bandwidth 10000 Mbit/s on R1 and R2
+. Verify reference bandwidth 10000 Mbit/s and link cost
+. Set reference bandwidth 1000 Mbit/s on R1 and R2
+. Verify reference bandwidth 1000 Mbit/s and link cost
+
+
diff --git a/test/case/routing/ospf_auto_cost/ospfv3.py b/test/case/routing/ospf_auto_cost/ospfv3.py
new file mode 120000
index 000000000..946566431
--- /dev/null
+++ b/test/case/routing/ospf_auto_cost/ospfv3.py
@@ -0,0 +1 @@
+test.py
\ No newline at end of file
diff --git a/test/case/routing/ospf_auto_cost/test.py b/test/case/routing/ospf_auto_cost/test.py
new file mode 100755
index 000000000..73e70434f
--- /dev/null
+++ b/test/case/routing/ospf_auto_cost/test.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python3
+"""{version} Auto-Cost Reference Bandwidth
+
+Verifies that the {version} interface cost follows the configured auto-cost
+reference bandwidth. The reference bandwidth must be the same on all
+routers in an OSPF domain, so it is changed on both R1 and R2.
+
+The cost of an interface is the reference bandwidth divided by the link
+speed, with 1 as the lowest cost. A link of unknown speed, e.g., a virtual
+link, counts as 10000 Mbit/s. On a 1 Gbit/s link the cost is:
+
+|===
+| Reference bandwidth | Cost
+| 100000 Mbit/s (default) | 100
+| 10000 Mbit/s | 10
+| 1000 Mbit/s | 1
+|===
+
+The test reads the link speed on each router to calculate the expected
+cost, and reads the reference bandwidth back from the operational
+datastore.
+"""
+
+import infamy
+import infamy.route as route
+from infamy.util import until, parallel
+
+
+class ArgumentParser(infamy.ArgumentParser):
+ def __init__(self):
+ super().__init__()
+ self.add_argument("--version", type=str.lower, choices=["ospfv2", "ospfv3"])
+
+
+PARAM = {
+ "ospfv2": {
+ "af": "ipv4",
+ "len": 24,
+ "R1link": "192.168.50.1",
+ "R2link": "192.168.50.2",
+ },
+ "ospfv3": {
+ "af": "ipv6",
+ "len": 64,
+ "R1link": "2001:db8:50::1",
+ "R2link": "2001:db8:50::2",
+ },
+}
+
+ROUTER_ID = {"R1": "1.1.1.1", "R2": "2.2.2.2"}
+
+# Frr uses this link speed (Mbit/s) when the speed is unknown
+UNKNOWN_SPEED = 10000
+
+
+def config_target(target, name, link, p, proto):
+ target.put_config_dicts({
+ "ietf-interfaces": {
+ "interfaces": {
+ "interface": [{
+ "name": link,
+ "enabled": True,
+ p["af"]: {
+ "forwarding": True,
+ "address": [{
+ "ip": p[f"{name}link"],
+ "prefix-length": p["len"]
+ }]
+ }
+ }]
+ }
+ },
+ "ietf-routing": {
+ "routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [{
+ "type": proto,
+ "name": "default",
+ "ospf": {
+ "explicit-router-id": ROUTER_ID[name],
+ "areas": {
+ "area": [{
+ "area-id": "0.0.0.0",
+ "interfaces": {
+ "interface": [{
+ "enabled": True,
+ "name": link,
+ "hello-interval": 1,
+ "dead-interval": 3
+ }]
+ }
+ }]
+ }
+ }
+ }]
+ }
+ }
+ }
+ })
+
+
+def set_reference_bandwidth(target, refbw, proto):
+ target.put_config_dicts({
+ "ietf-routing": {
+ "routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [{
+ "type": proto,
+ "name": "default",
+ "ospf": {
+ "auto-cost": {
+ "reference-bandwidth": refbw
+ }
+ }
+ }]
+ }
+ }
+ }
+ })
+
+
+def link_speed(target, link):
+ """Link speed in Mbit/s, as used by Frr to calculate the cost"""
+ speed = target.get_iface(link).get("speed")
+ if not speed:
+ return UNKNOWN_SPEED
+
+ return int(speed) // 1000000
+
+
+def expected_cost(refbw, speed):
+ """Same rounding as Frr, with 1 as the lowest cost"""
+ return max(1, int(refbw / speed + 0.5))
+
+
+def wait_adjacency(target, link, neighbor, proto):
+ until(lambda: route.ospf_get_neighbor(target, "0.0.0.0", link, ROUTER_ID[neighbor],
+ proto=proto), attempts=200)
+
+
+def verify(target, link, refbw, proto):
+ speed = link_speed(target, link)
+ cost = expected_cost(refbw, speed)
+ print(f"Expecting reference bandwidth {refbw} Mbit/s and cost {cost} "
+ f"on {link} ({speed} Mbit/s)")
+
+ until(lambda: route.ospf_get_reference_bandwidth(target, proto) == refbw and
+ route.ospf_get_interface_cost(target, "0.0.0.0", link, proto) == cost)
+
+
+with infamy.Test() as test:
+ with test.step("Set up topology and attach to target DUTs"):
+ env = infamy.Env(args=ArgumentParser())
+ version = env.args.version
+ param = PARAM[version]
+ proto = f"infix-routing:{version}"
+
+ R1, R2 = parallel(lambda: env.attach("R1", "mgmt"),
+ lambda: env.attach("R2", "mgmt"))
+ route.skip_unless_supported(test, "ospf", R1, R2)
+
+ _, R1link = env.ltop.xlate("R1", "link")
+ _, R2link = env.ltop.xlate("R2", "link")
+
+ def set_both(refbw):
+ parallel(lambda: set_reference_bandwidth(R1, refbw, proto),
+ lambda: set_reference_bandwidth(R2, refbw, proto))
+
+ def verify_both(refbw):
+ parallel(lambda: verify(R1, R1link, refbw, proto),
+ lambda: verify(R2, R2link, refbw, proto))
+
+ with test.step(f"Configure {version} on R1 and R2"):
+ parallel(lambda: config_target(R1, "R1", R1link, param, proto),
+ lambda: config_target(R2, "R2", R2link, param, proto))
+
+ with test.step(f"Wait for {version} adjacency between R1 and R2"):
+ parallel(lambda: wait_adjacency(R1, R1link, "R2", proto),
+ lambda: wait_adjacency(R2, R2link, "R1", proto))
+
+ with test.step("Verify default reference bandwidth 100000 Mbit/s and link cost"):
+ verify_both(100000)
+
+ with test.step("Set reference bandwidth 10000 Mbit/s on R1 and R2"):
+ set_both(10000)
+
+ with test.step("Verify reference bandwidth 10000 Mbit/s and link cost"):
+ verify_both(10000)
+
+ with test.step("Set reference bandwidth 1000 Mbit/s on R1 and R2"):
+ set_both(1000)
+
+ with test.step("Verify reference bandwidth 1000 Mbit/s and link cost"):
+ verify_both(1000)
+
+ test.succeed()
diff --git a/test/case/routing/ospf_auto_cost/test.yaml b/test/case/routing/ospf_auto_cost/test.yaml
new file mode 100644
index 000000000..f069ddb9e
--- /dev/null
+++ b/test/case/routing/ospf_auto_cost/test.yaml
@@ -0,0 +1,11 @@
+---
+- settings:
+ test-spec: .adoc
+
+- name: OSPFv2 Auto-Cost Reference Bandwidth
+ case: ospfv2.py
+ opts: ["--version", "OSPFv2"]
+
+- name: OSPFv3 Auto-Cost Reference Bandwidth
+ case: ospfv3.py
+ opts: ["--version", "OSPFv3"]
diff --git a/test/case/routing/ospf_auto_cost/topology.dot b/test/case/routing/ospf_auto_cost/topology.dot
new file mode 100644
index 000000000..b9fb38909
--- /dev/null
+++ b/test/case/routing/ospf_auto_cost/topology.dot
@@ -0,0 +1,35 @@
+graph "ospf_auto_cost" {
+ labelloc="b";
+ label="IPv4 addresses shown, the IPv6 run uses the matching 2001:db8: prefixes";
+ fontname="DejaVu Serif, Book";
+ 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 | mgmt2 }",
+ pos="20,30!",
+ requires="controller",
+ ];
+
+ R1 [
+ label="{ mgmt | link } | R1 \n router-id 1.1.1.1",
+ pos="160,60!",
+
+ requires="infix",
+ ];
+ R2 [
+ label="{ link | mgmt } | R2 \n router-id 2.2.2.2",
+ pos="160,30!",
+
+ requires="infix",
+ ];
+
+ PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
+ PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
+ R1:link -- R2:link [headlabel="192.168.50.2/24", taillabel="192.168.50.1/24", labeldistance=1, fontcolor="black", color="black"]
+}
diff --git a/test/case/routing/ospf_auto_cost/topology.svg b/test/case/routing/ospf_auto_cost/topology.svg
new file mode 100644
index 000000000..2a7b3e606
--- /dev/null
+++ b/test/case/routing/ospf_auto_cost/topology.svg
@@ -0,0 +1,62 @@
+
+
+
+
+
diff --git a/test/case/statd/containers/ietf-ospf.json b/test/case/statd/containers/ietf-ospf.json
index d9129e242..889fe05e7 100644
--- a/test/case/statd/containers/ietf-ospf.json
+++ b/test/case/statd/containers/ietf-ospf.json
@@ -113,6 +113,10 @@
},
"ietf-ospf:router-id": "10.1.1.1",
"ietf-ospf:address-family": "ipv4",
+ "ietf-ospf:auto-cost": {
+ "enabled": true,
+ "reference-bandwidth": 100000
+ },
"ietf-ospf:local-rib": {
"ietf-ospf:route": [
{
diff --git a/test/case/statd/containers/operational.json b/test/case/statd/containers/operational.json
index c210c8a8f..15892196b 100644
--- a/test/case/statd/containers/operational.json
+++ b/test/case/statd/containers/operational.json
@@ -790,6 +790,10 @@
}
]
},
+ "ietf-ospf:auto-cost": {
+ "enabled": true,
+ "reference-bandwidth": 100000
+ },
"ietf-ospf:local-rib": {
"ietf-ospf:route": [
{
diff --git a/test/infamy/route.py b/test/infamy/route.py
index 093e8bd4e..232086461 100644
--- a/test/infamy/route.py
+++ b/test/infamy/route.py
@@ -132,6 +132,16 @@ def ospf_get_interface_passive(target, area_id, ifname, proto="infix-routing:osp
return ospf_interface.get("passive", False)
+def ospf_get_interface_cost(target, area_id, ifname, proto="infix-routing:ospfv2"):
+ ospf_interface = _get_ospf_status_area_interface(target, area_id, ifname, proto)
+ return ospf_interface.get("cost")
+
+
+def ospf_get_reference_bandwidth(target, proto="infix-routing:ospfv2"):
+ ospf = _get_ospf_status(target, proto)
+ return ospf.get("auto-cost", {}).get("reference-bandwidth")
+
+
def ospf_is_area_nssa(target, area_id, proto="infix-routing:ospfv2"):
area = _get_ospf_status_area(target, area_id, proto)
if area.get("area-type", "") == "ietf-ospf:nssa-area":