diff --git a/doc/source/configuration/reference/network.rst b/doc/source/configuration/reference/network.rst
index f3d37da74..f091c6f28 100644
--- a/doc/source/configuration/reference/network.rst
+++ b/doc/source/configuration/reference/network.rst
@@ -271,32 +271,16 @@ Configuring IP Routing Policy Rules
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 IP routing policy rules may be configured by setting the ``rules`` attribute
-for a network to a list of rules. The format of each rule currently differs
-between CentOS/Rocky and Ubuntu.
+for a network to a list of rules. Two formats are supported for defining rules:
+string format and dict format. String format rules are only supported on
+CentOS Stream and Rocky Linux systems.
 
-CentOS/Rocky
-""""""""""""
+Dict format rules
+"""""""""""""""""
 
-The format of a rule is the string which would be appended to ``ip rule
-<add|del>`` to create or delete the rule.
-
-To configure a network called ``example`` with an IP routing policy rule to
-handle traffic from the subnet ``10.1.0.0/24`` using the routing table
-``exampleroutetable``:
-
-.. code-block:: yaml
-   :caption: ``networks.yml``
-
-   example_rules:
-     - from 10.1.0.0/24 table exampleroutetable
-
-These rules will be configured on all hosts to which the network is mapped.
-
-Ubuntu
-""""""
-
-The format of a rule is a dictionary with optional items ``from``, ``to``,
-``priority``, and ``table``.
+The dict format of a rule is a dictionary with optional items ``from``, ``to``,
+``priority``, and ``table``. ``table`` should be the name of a route table
+defined in ``network_route_tables``.
 
 To configure a network called ``example`` with an IP routing policy rule to
 handle traffic from the subnet ``10.1.0.0/24`` using the routing table
@@ -311,6 +295,26 @@ handle traffic from the subnet ``10.1.0.0/24`` using the routing table
 
 These rules will be configured on all hosts to which the network is mapped.
 
+String format rules (CentOS Stream/Rocky Linux only)
+""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+The string format of a rule is the string which would be appended to ``ip rule
+<add|del>`` to create or delete the rule. Note that when using NetworkManager
+(the default since Zed and in Yoga when using Rocky Linux 9) the table must be
+specified by ID.
+
+To configure a network called ``example`` with an IP routing policy rule to
+handle traffic from the subnet ``10.1.0.0/24`` using the routing table with ID
+1:
+
+.. code-block:: yaml
+   :caption: ``networks.yml``
+
+   example_rules:
+     - from 10.1.0.0/24 table 1
+
+These rules will be configured on all hosts to which the network is mapped.
+
 Configuring IP Routes on Specific Tables
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/kayobe/plugins/filter/networks.py b/kayobe/plugins/filter/networks.py
index 6d47544b7..97d4fd7e7 100644
--- a/kayobe/plugins/filter/networks.py
+++ b/kayobe/plugins/filter/networks.py
@@ -367,10 +367,10 @@ def _validate_rules(rules):
     :raises: AnsibleFilterError if any rule is invalid.
     """
     for rule in rules or []:
-        if not isinstance(rule, str):
+        if not isinstance(rule, str) and not isinstance(rule, dict):
             raise errors.AnsibleFilterError(
-                "Routing policy rules must be defined in string format "
-                "for CentOS")
+                "Routing policy rules must be defined in string or dict "
+                "format for CentOS Stream and Rocky Linux")
 
 
 @jinja2.pass_context
diff --git a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2
index 01497d1e1..ae454d644 100644
--- a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2
+++ b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2
@@ -45,10 +45,14 @@ test_net_eth_vlan_routes:
     table: kayobe-test-route-table
 test_net_eth_vlan_rules:
 {% if ansible_facts.os_family == 'RedHat' %}
-  - from 192.168.35.0/24 table kayobe-test-route-table
+  - from 192.168.35.0/24 table 2
+  - to: 192.168.35.0/24
+    table: kayobe-test-route-table
 {% else %}
   - from: 192.168.35.0/24
     table: kayobe-test-route-table
+  - to: 192.168.35.0/24
+    table: kayobe-test-route-table
 {% endif %}
 test_net_eth_vlan_zone: test-zone1
 
diff --git a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py
index f0ed4721a..22cceb429 100644
--- a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py
+++ b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py
@@ -39,17 +39,15 @@ def test_network_ethernet_vlan(host):
     assert interface.exists
     assert '192.168.35.1' in interface.addresses
     assert host.file('/sys/class/net/dummy2.42/lower_dummy2').exists
-    # FIXME(bbezak): remove following IF after ansible-role-interfaces
-    # receive support for custom routes in NetworkManager
-    if not ('centos' in host.system_info.distribution.lower() or
-            'rocky' in host.system_info.distribution.lower()):
-       routes = host.check_output(
-           '/sbin/ip route show dev dummy2.42 table kayobe-test-route-table')
-       assert '192.168.40.0/24 via 192.168.35.254' in routes
-       rules = host.check_output(
-           '/sbin/ip rule show table kayobe-test-route-table')
-       expected = 'from 192.168.35.0/24 lookup kayobe-test-route-table'
-       assert expected in rules
+    routes = host.check_output(
+        '/sbin/ip route show dev dummy2.42 table kayobe-test-route-table')
+    assert '192.168.40.0/24 via 192.168.35.254' in routes
+    rules = host.check_output(
+        '/sbin/ip rule show table kayobe-test-route-table')
+    expected_from = 'from 192.168.35.0/24 lookup kayobe-test-route-table'
+    expected_to = 'to 192.168.35.0/24 lookup kayobe-test-route-table'
+    assert expected_from in rules
+    assert expected_to in rules
 
 
 def test_network_bridge(host):
diff --git a/releasenotes/notes/nm-rules-3f1f2c2a9e8f6ce3.yaml b/releasenotes/notes/nm-rules-3f1f2c2a9e8f6ce3.yaml
new file mode 100644
index 000000000..0c06720bd
--- /dev/null
+++ b/releasenotes/notes/nm-rules-3f1f2c2a9e8f6ce3.yaml
@@ -0,0 +1,11 @@
+---
+features:
+  - |
+    Adds support for specifying IP policy-based routing rules using the
+    dict-based format on CentOS Stream and Rocky Linux systems. The
+    string-based format is still supported on these systems.
+other:
+  - |
+    Kayobe networking documentation for IP rules on CentOS Stream/Rocky Linux
+    systems has been updated to reflect that routing tables must be specified
+    by ID rather than by name.