Skip to content

Commit

Permalink
Implement configurable RIPv2/RIPng protocol timers (#1315)
Browse files Browse the repository at this point in the history
  • Loading branch information
ipspace authored Sep 23, 2024
1 parent 98affa5 commit fc64894
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 8 deletions.
31 changes: 23 additions & 8 deletions docs/module/ripv2.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ This configuration module configures the RIPv2 and RIPng. The module supports th

The following table describes per-platform support of individual RIPv2/RIPng features:

| Operating system | IPv4<br>(RIPv2) | IPv6<br>(RIPng) | Passive<br>interfaces | Route<br>import | VRF<br>instances |
| ------------------ | :-: | :-: | :-: | :-: | :-: |
| Arista EOS |||||
| Cisco IOSv/IOSvL2 |||[](caveats-iosv) |[](caveats-iosv) ||
| Cisco IOS XE[^18v] |||[](caveats-iosv) |[](caveats-iosv) ||
| Cumulus Linux ||||||
| FRR ||||||
| Operating system | IPv4<br>(RIPv2) | IPv6<br>(RIPng) | Passive<br>interfaces | Route<br>import | VRF<br>instances | RIP<br>timers |
| ------------------ | :-: | :-: | :-: | :-: | :-: | :-: |
| Arista EOS |||||||
| Cisco IOSv/IOSvL2 |||[](caveats-iosv) |[](caveats-iosv) |||
| Cisco IOS XE[^18v] |||[](caveats-iosv) |[](caveats-iosv) |||
| Cumulus Linux |||||||
| FRR |||||||
| VyOS |||||

```{tip}
Expand All @@ -35,16 +35,31 @@ See [RIP Integration Tests Results](https://release.netlab.tools/_html/coverage.

## Lab Topology Parameters

RIPv2/RIPng module does not have global parameters. The only relevant node parameter is the **ripv2.import** parameter specifying the [import (redistribution) of routes](routing_import) into the global RIP instance (default: no route import).
You can change the RIPv2/RIPng timers with the global **rip.timers** dictionary ([more details](rip-timers)).

The RIPv2/RIPng configuration module supports these node parameters:

* **rip.timers**: Change RIP timers for a single node
* **ripv2.import**: Specify the [import (redistribution) of routes](routing_import) into the global RIP instance (default: no route import).

RIPv2 also supports [](routing_passive) and [](routing_external).

## VRF Parameters

* By default, _netlab_ redistributes BGP- and connected routes into VRF RIPv2/RIPng instances on all network devices. You can change that on devices supporting configurable route import with the **[ripv2.import](routing_import)** VRF parameter.
* Use **rip.timers** VRF parameter to change RIP timers for a single VRF instance
* Set **ripv2.active** to *True* to force a VRF to use RIPv2/RIPng even when no routers are attached to the VRF interfaces.
* To disable RIPv2/RIPng in a VRF set **ripv2** to *False* (see also [](routing_disable_vrf)).

(rip-timers)=
## Changing RIP Timers

You can change the RIP protocol timers with the **rip.timers** global/node/VRF dictionary. The dictionary has these elements:

* **update**: periodic RIP update timer. Default: 30 seconds, minimum: 5 seconds
* **timeout**: route expiration timer. Default: six times the **update** timer, minimum: 5 seconds)
* **garbage**: garbage collection timer (the time after which an invalid route is removed from the routing table). Default: four times the **update** timer, minimum: 5 seconds

## Example

We want to create a simple two-router RIPv2 network using Cumulus Linux:
Expand Down
3 changes: 3 additions & 0 deletions netsim/ansible/templates/ripv2/eos.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
{% if ripv2.af.ipv4|default(False) %}
router rip
no shutdown
{% if 'timers' in ripv2 %}
timers {{ ripv2.timers['update'] }} {{ ripv2.timers.timeout }} {{ ripv2.timers.garbage }}
{% endif %}
{% for intf in netlab_interfaces if 'ripv2' in intf and intf.ipv4|default('')|ipaddr() %}
network {{ intf.ipv4|ipaddr(0) }}
{% endfor %}
Expand Down
6 changes: 6 additions & 0 deletions netsim/ansible/templates/ripv2/frr.macro.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
{% if ripv2.af.ipv4|default(False) %}
router rip{% if vrf %} vrf {{ vrf }}{% endif +%}
version 2
{% if 'timers' in ripv2 %}
timers basic {{ ripv2.timers['update'] }} {{ ripv2.timers.timeout }} {{ ripv2.timers.garbage }}
{% endif %}
{{ redistribute.config(ripv2,af='ipv4') }}
{% for intf in ripv2.interfaces|default(netlab_interfaces) if 'ripv2' in intf and 'ipv4' in intf %}
network {{ intf.ifname }}
Expand All @@ -15,6 +18,9 @@ router rip{% if vrf %} vrf {{ vrf }}{% endif +%}
!
{% if ripv2.af.ipv6|default(False) %}
router ripng{% if vrf %} vrf {{ vrf }}{% endif +%}
{% if 'timers' in ripv2 %}
timers basic {{ ripv2.timers['update'] }} {{ ripv2.timers.timeout }} {{ ripv2.timers.garbage }}
{% endif %}
{{ redistribute.config(ripv2,af='ipv6') }}
{% for intf in ripv2.interfaces|default(netlab_interfaces) if 'ripv2' in intf and 'ipv6' in intf %}
network {{ intf.ifname }}
Expand Down
6 changes: 6 additions & 0 deletions netsim/ansible/templates/ripv2/ios.macro.j2
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ router rip
{% if 'import' in ripv2 %}
default-metric {{ netlab_ripv2_default_metric|default(5) }}
{% endif %}
{% if 'timers' in ripv2 %}
timers basic {{ ripv2.timers['update'] }} {{ ripv2.timers.timeout }} {{ ripv2.timers.garbage }} 1
{% endif %}
{{ redistribute.config(ripv2,af='ipv4',ospf_pid=ospf_pid)|indent(1,first=True) }}
{% for intf in ripv2.interfaces|default(netlab_interfaces) %}
{% if 'ripv2' in intf and intf.ipv4|default(False) is string %}
Expand All @@ -30,6 +33,9 @@ ipv6 router rip default
{% if vrf %}
address-family ipv6 vrf {{ vrf }}
{% endif %}
{% if 'timers' in ripv2 %}
timers {{ ripv2.timers['update'] }} {{ ripv2.timers.timeout }} {{ ripv2.timers.garbage }} 1
{% endif %}
{{ redistribute.config(ripv2,af='ipv6',ospf_pid=ospf_pid)|indent(1,first=True) }}
!
{% for intf in ripv2.interfaces|default(netlab_interfaces) if 'ripv2' in intf and 'ipv6' in intf %}
Expand Down
15 changes: 15 additions & 0 deletions netsim/modules/_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,3 +700,18 @@ def igp_post_transform(

process_imports(node,proto,topology,['bgp','connected'])
process_default_route(node,proto,topology)

"""
routing_protocol_data: Given a node and a protocol name, returns the global protocol data
and any VRF instances
You can use this generator to iterate over all routing protocol instances without going through
the "global first, then VRFs" logic on your own
"""
def routing_protocol_data(node: Box, proto: str) -> typing.Generator:
if isinstance(node.get(proto,None),Box):
yield node[proto]

for vname,vdata in node.get('vrfs',{}).items():
if isinstance(vdata.get(proto,None),Box):
yield vdata[proto]
26 changes: 26 additions & 0 deletions netsim/modules/ripv2.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,29 @@
from ..utils import log
from ..augment import devices

"""
adjust_rip_timers: Make sure all three timers are set if at least one of the 'ripv2.timers'
attributes are set (data validation makes sure we're not dealing with bogus attributes)
Having consistent set of three timers makes the configuration templates cleaner and avoids
dealing with device-specific defaults (some devices accept less than three timers)
Finally: it seems like 'update' might be a valid method for the dictionary object,
so we're using traditional dict expression to access the update timer.
"""
def adjust_rip_timers(rip_data: Box) -> None:
if 'timers' not in rip_data:
return

if 'update' not in rip_data.timers: # If we want to set timers, we need the update timer
rip_data.timers['update'] = 30 # ... if it's missing, we'll use the default value

if 'timeout' not in rip_data.timers: # RFC says the default timeout value is 6 times the update timer
rip_data.timers.timeout = rip_data.timers['update'] * 6

if 'garbage' not in rip_data.timers: # ... and the default GC value is 4 times the update timer
rip_data.timers.garbage = rip_data.timers['update'] * 4

class RIPv2(_Module):

def node_post_transform(self, node: Box, topology: Box) -> None:
Expand All @@ -28,5 +51,8 @@ def node_post_transform(self, node: Box, topology: Box) -> None:
_routing.igp_post_transform(node,topology,proto='ripv2',vrf_aware=True)
_routing.check_vrf_protocol_support(node,proto='ripv2',af='ipv4',feature='ripv2',topology=topology)
_routing.check_vrf_protocol_support(node,proto='ripv2',af='ipv6',feature='ripng',topology=topology)
for rip_data in _routing.routing_protocol_data(node,'ripv2'):
adjust_rip_timers(rip_data)

if 'ripv2' in node and 'loopback' in node:
node.loopback.ripv2.passive = False
9 changes: 9 additions & 0 deletions netsim/modules/ripv2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ config_after: [ vlan, dhcp, routing ]
attributes:
global:
version: { type: int, min_value: 2, max_value: 2 }
timers:
update: { type: int, min_value: 5, max_value: 60 }
timeout: { type: int, min_value: 5, max_value: 3600 }
garbage: { type: int, min_value: 5, max_value: 600 }
af:
_list_to_dict: True
_alt_types: [ NoneType ]
Expand All @@ -20,8 +24,13 @@ attributes:
af:
version:
import: _r_import
timers:
copy: global
vrf:
active: bool
timers:
copy: global
vrf_copy: [ timers ]
link:
passive: bool
warnings:
Expand Down
5 changes: 5 additions & 0 deletions tests/integration/ripv2/topology-defaults.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
ripv2.timers:
update: 5 # Change the RIP update timer to speed up the tests
timeout: 180 # ... but leave the other timers intact to avoid route expiration failures
garbage: 120 # ... with devices that do not support fast timers

0 comments on commit fc64894

Please sign in to comment.