Skip to content

Commit

Permalink
feat: Add support for the route_type
Browse files Browse the repository at this point in the history
When users have BGP routing setups, it is common practice to blackhole
some less-specific routes in order to avoid routing loops, and the BGP
router might insert a more specific route dynamically afterwards.

Signed-off-by: Wen Liang <[email protected]>
  • Loading branch information
liangwen12year committed Dec 19, 2023
1 parent e213dc9 commit 06ddc26
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 7 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -586,13 +586,14 @@ The IP configuration supports the following options:

Static route configuration can be specified via a list of routes given in the
`route` option. The default value is an empty list. Each route is a dictionary with
the following entries: `network`, `prefix`, `gateway`, `metric` and `table`.
`network` and `prefix` specify the destination network. `table` supports both the
numeric table and named table. In order to specify the named table, the users have
to ensure the named table is properly defined in `/etc/iproute2/rt_tables` or
`/etc/iproute2/rt_tables.d/*.conf`.
Note that Classless inter-domain routing (CIDR) notation or network mask notation
are not supported yet.
the following entries: `network`, `prefix`, `gateway`, `metric`, `table` and
`route_type`. `network` and `prefix` specify the destination network. `table`
supports both the numeric table and named table. In order to specify the named
table, the users have to ensure the named table is properly defined in
`/etc/iproute2/rt_tables` or `/etc/iproute2/rt_tables.d/*.conf`. And the supported
`route_type` are `blackhole`, `prohibit`, `unreachable` and these `route_type` routes
can not have next hop (gateway) specified. Note that Classless inter-domain routing
(CIDR) notation or network mask notation are not supported yet.

- `routing_rule`

Expand Down
15 changes: 15 additions & 0 deletions examples/route_table_support.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@
gateway: 198.51.100.1
metric: 2
table: 30400
- network: 198.53.100.18
prefix: 32
metric: 20
route_type: blackhole
table: 30200
- network: 198.53.100.12
prefix: 32
metric: 24
route_type: unreachable
table: 30200
- network: 198.53.100.10
prefix: 32
metric: 30
route_type: prohibit
table: 30200
- network: 198.51.100.64
prefix: 26
gateway: 198.51.100.6
Expand Down
4 changes: 4 additions & 0 deletions library/network_connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,10 @@ def connection_create(self, connections, idx, connection_current=None):
new_route = NM.IPRoute.new(
r["family"], r["network"], r["prefix"], r["gateway"], r["metric"]
)
if r["route_type"]:
NM.IPRoute.set_attribute(

Check warning on line 1224 in library/network_connections.py

View check run for this annotation

Codecov / codecov/patch

library/network_connections.py#L1223-L1224

Added lines #L1223 - L1224 were not covered by tests
new_route, "type", Util.GLib().Variant("s", r["route_type"])
)
if r["table"]:
NM.IPRoute.set_attribute(
new_route, "table", Util.GLib().Variant.new_uint32(r["table"])
Expand Down
20 changes: 20 additions & 0 deletions module_utils/network_lsr/argument_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,11 @@ def __init__(self, name, family=None, required=False):
ArgValidatorNum(
"metric", default_value=-1, val_min=-1, val_max=UINT32_MAX
),
ArgValidatorStr(
"route_type",
default_value=None,
enum_values=["blackhole", "prohibit", "unreachable"],
),
ArgValidatorRouteTable("table"),
],
default_value=None,
Expand All @@ -688,13 +693,20 @@ def _validate_post(self, value, name, result):
result["family"] = family

gateway = result["gateway"]
route_type = result["route_type"]
if gateway is not None:
if family != gateway["family"]:
raise ValidationError(
name,
"conflicting address family between network and gateway '%s'"
% (gateway["address"]),
)
if route_type is not None:
raise ValidationError(
name,
"a %s route can not have a next hop '%s'"
% (route_type, gateway["address"]),
)
result["gateway"] = gateway["address"]

prefix = result["prefix"]
Expand Down Expand Up @@ -2607,6 +2619,14 @@ def _ipv6_is_not_configured(connection):
"NetworkManger until NM 1.30",
)

if connection["ip"]["route"]:
if mode == self.VALIDATE_ONE_MODE_INITSCRIPTS:
for route in connection["ip"]["route"]:
if route["route_type"] is not None:
raise ValidationError.from_connection(

Check warning on line 2626 in module_utils/network_lsr/argument_validator.py

View check run for this annotation

Codecov / codecov/patch

module_utils/network_lsr/argument_validator.py#L2626

Added line #L2626 was not covered by tests
idx,
"route_type is not supported by initscripts",
)
if connection["ip"]["routing_rule"]:
if mode == self.VALIDATE_ONE_MODE_INITSCRIPTS:
raise ValidationError.from_connection(
Expand Down
21 changes: 21 additions & 0 deletions tests/playbooks/tests_route_table.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@
gateway: 198.51.100.6
metric: 4
table: 30200
- network: 198.53.100.18
prefix: 32
metric: 20
route_type: blackhole
table: 30200
- network: 198.53.100.12
prefix: 32
metric: 24
route_type: unreachable
table: 30200
- network: 198.53.100.10
prefix: 32
metric: 30
route_type: prohibit
table: 30200

- name: Get the routes from the route table 30200
command: ip route show table 30200
Expand All @@ -65,6 +80,12 @@
that:
- route_table_30200.stdout is search("198.51.100.64/26 via
198.51.100.6 dev ethtest0 proto static metric 4")
- route_table_30200.stdout is search("blackhole 198.53.100.18
proto static scope link metric 20")
- route_table_30200.stdout is search("unreachable 198.53.100.12
proto static scope link metric 24")
- route_table_30200.stdout is search("prohibit 198.53.100.10
proto static scope link metric 30")
msg: "the route table 30200 does not exist or does not contain the
specified route"

Expand Down
34 changes: 34 additions & 0 deletions tests/unit/test_network_connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,12 @@ def do_connections_validate_nm(self, input_connections, **kwargs):
r["gateway"],
r["metric"],
)
if r["route_type"]:
NM.IPRoute.set_attribute(
new_route,
"type",
Util.GLib().Variant("s", r["route_type"]),
)
if r["table"]:
NM.IPRoute.set_attribute(
new_route,
Expand Down Expand Up @@ -1135,6 +1141,7 @@ def test_routes(self):
"prefix": 24,
"gateway": None,
"metric": -1,
"route_type": None,
"table": None,
}
],
Expand Down Expand Up @@ -1475,6 +1482,7 @@ def test_vlan(self):
"prefix": 24,
"gateway": None,
"metric": -1,
"route_type": None,
"table": None,
}
],
Expand Down Expand Up @@ -1624,6 +1632,7 @@ def test_macvlan(self):
"prefix": 24,
"gateway": None,
"metric": -1,
"route_type": None,
"table": None,
}
],
Expand Down Expand Up @@ -1686,6 +1695,7 @@ def test_macvlan(self):
"prefix": 24,
"gateway": None,
"metric": -1,
"route_type": None,
"table": None,
}
],
Expand Down Expand Up @@ -2648,6 +2658,7 @@ def test_route_metric_prefix(self):
"prefix": 24,
"gateway": None,
"metric": 545,
"route_type": None,
"table": None,
},
{
Expand All @@ -2656,6 +2667,7 @@ def test_route_metric_prefix(self):
"prefix": 30,
"gateway": None,
"metric": -1,
"route_type": None,
"table": None,
},
],
Expand Down Expand Up @@ -2752,6 +2764,7 @@ def test_route_v6(self):
"prefix": 24,
"gateway": None,
"metric": 545,
"route_type": None,
"table": None,
},
{
Expand All @@ -2760,6 +2773,7 @@ def test_route_v6(self):
"prefix": 30,
"gateway": None,
"metric": -1,
"route_type": None,
"table": None,
},
{
Expand All @@ -2768,6 +2782,7 @@ def test_route_v6(self):
"prefix": 64,
"gateway": None,
"metric": -1,
"route_type": None,
"table": None,
},
],
Expand Down Expand Up @@ -2905,6 +2920,7 @@ def test_route_without_interface_name(self):
"prefix": 24,
"gateway": None,
"metric": 545,
"route_type": None,
"table": None,
},
{
Expand All @@ -2913,6 +2929,7 @@ def test_route_without_interface_name(self):
"prefix": 30,
"gateway": None,
"metric": -1,
"route_type": None,
"table": None,
},
{
Expand All @@ -2921,6 +2938,7 @@ def test_route_without_interface_name(self):
"prefix": 64,
"gateway": None,
"metric": -1,
"route_type": None,
"table": None,
},
],
Expand Down Expand Up @@ -4966,6 +4984,22 @@ def test_table_not_found_when_validate_route_tables(self):
self.connection_index,
)

def test_route_type_route_with_next_hop(self):
"""
Test that the route type route can not have the next hop
"""

self.test_connections[0]["ip"]["route"][0]["route_type"] = "blackhole"
self.assertRaisesRegex(
ValidationError,
"a {0} route can not have a next hop '{1}'".format(
self.test_connections[0]["ip"]["route"][0]["route_type"],
self.test_connections[0]["ip"]["route"][0]["gateway"],
),
self.validator.validate,
self.test_connections,
)


class TestValidatorRoutingRules(Python26CompatTestCase):
def setUp(self):
Expand Down

0 comments on commit 06ddc26

Please sign in to comment.