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 @@ + + + + + + +ospf_auto_cost + +IPv4 addresses shown, the IPv6 run uses the matching 2001:db8: prefixes + + +PC + +PC + +mgmt1 + +mgmt2 + + + +R1 + +mgmt + +link + +R1 + router-id 1.1.1.1 + + + +PC:mgmt1--R1:mgmt + + + + +R2 + +link + +mgmt + +R2 + router-id 2.2.2.2 + + + +PC:mgmt2--R2:mgmt + + + + +R1:link--R2:link + +192.168.50.2/24 +192.168.50.1/24 + + + 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":