Skip to content

Commit e54e3db

Browse files
committed
Implement IPv6 RA attributes on EOS, FRR, and IOS
* Define new link/interface 'ra' dictionary * Implement selective merge of link 'ra' attributes with interface attributes. Link 'ra' attributes are propagated only to router nodes that support 'ra' (features.initial.ra) * Implement 'ra' attributes on EOS, FRR, and IOS
1 parent a8f60ea commit e54e3db

File tree

14 files changed

+332
-31
lines changed

14 files changed

+332
-31
lines changed

docs/links.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ A dictionary describing an individual link contains *node names* and *additional
7676
* **name** -- link name (used for interface description)
7777
* **pool** -- addressing pool used to assign a prefix to this link. The **pool** attribute is ignored on links with a **prefix** attribute.
7878
* **prefix** -- [prefix (or a set of prefixes)](links-static-addressing) used on the link. Setting **prefix** to *false* will give you a link without any IP configuration[^NOIP]
79+
* **ra** -- IPv6 Router Advertisement parameters ([more details](links-ra))
7980
* **role** -- The link *role* influences the behavior of several configuration modules. Typical link roles include *stub*, *passive*, and *external*. Please read [](module/routing.md) for more details.
8081
* **type** -- [link type](links-types) (lan, p2p, stub, loopback, tunnel)
8182

@@ -346,10 +347,24 @@ links:
346347
You can also use the **unnumbered** link attribute to get a single unnumbered link. Using an unnumbered pool is recommended when testing network-wide addressing changes.
347348
```
348349

350+
(links-ra)=
351+
## IPv6 Router Advertisement Parameters
352+
353+
_netlab_ configures [routers](node-role-router) to send IPv6 Router Advertisement messages and [hosts](node-role-host) to listen to them and use them to generate an IPv6 default route. Router Advertisement messages are disabled on [bridges](node-role-bridge). The default router advertisement interface is (when possible) set to a few seconds to speed up the IPv6 addressing of attached hosts using SLAAC.
354+
355+
You can use the **ra** link- or interface dictionary to control the contents of the Router Advertisement messages on [devices supporting fine-grained **ra** control](platform-initial-addresses):
356+
357+
* **ra.disable** -- do not send RA messages when set to True
358+
* **ra.slaac** -- set *autonomous* flag on the link prefix when set to True or missing. Set **ra.slaac** to False to disable SLAAC on attached hosts.
359+
* **ra.onlink** -- set *on-link* flag on the link prefix when set to True or missing. Set **ra.onlink** to False to disable direct host-to-host communication.
360+
* **ra.dhcp** -- set *other configuration* flag when set to **other** or *managed configuration* flag when set to **all**. No DHCPv6-related flag is set by default.
361+
362+
While you can set **ra** parameters on individual interfaces (node-to-link attachments), it's best to set them as link parameters to have a consistent set of parameters applied to all attached routers.
363+
349364
(links-mtu)=
350365
## Changing MTU
351366

352-
All devices supported by *netlab* are assumed to use ancient default layer-3 MTU value of 1500 bytes. Most VM-based network devices already use that default; container-based devices have their MTU set to 1500 through system settings.
367+
All devices supported by *netlab* are assumed to use the ancient default layer-3 MTU value of 1500 bytes. Most VM-based network devices already use that default; container-based devices have their MTU set to 1500 through system settings.
353368

354369
Please note that the **mtu** specified by *netlab* is always the layer-3 (IPv4 or IPv6) MTU. The peculiarities of individual device configuration commands are transparently (to the end-user) handled in the device configuration templates.
355370

docs/platforms.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -260,29 +260,29 @@ The following interface parameters are configured on supported network operating
260260
(platform-initial-addresses)=
261261
The following interface addresses are supported on various platforms:
262262

263-
| Operating system | IPv4<br />addresses | IPv6<br />addresses | Unnumbered<br />IPv4 interfaces |
264-
| --------------------- | :-: | :-: | :-: |
265-
| Arista EOS ||||
266-
| Aruba AOS-CX ||| |
267-
| Cisco ASAv ||||
268-
| Cisco IOSv/IOSvL2 ||||
269-
| Cisco IOS XE[^18v] ||||
270-
| Cisco IOS XRv ||||
271-
| Cisco Nexus OS ||||
272-
| Cumulus Linux ||||
273-
| Cumulus Linux 5.x (NVUE) ||||
274-
| Dell OS10 ||||
275-
| Fortinet FortiOS ||||
276-
| FRR ||||
277-
| Generic Linux ||||
278-
| Junos[^Junos] ||||
279-
| Mikrotik RouterOS 6 ||||
280-
| Mikrotik RouterOS 7 ||||
281-
| Nokia SR Linux ||||
282-
| Nokia SR OS[^SROS] ||||
283-
| OpenBSD ||||
284-
| Sonic ||||
285-
| VyOS ||||
263+
| Operating system | IPv4<br />addresses | IPv6<br />addresses | Unnumbered<br />IPv4 interfaces | Configurable<br>IPv6 RA |
264+
| --------------------- | :-: | :-: | :-: | :-: |
265+
| Arista EOS |||||
266+
| Aruba AOS-CX ||| | |
267+
| Cisco ASAv |||||
268+
| Cisco IOSv/IOSvL2 |||||
269+
| Cisco IOS XE[^18v] |||||
270+
| Cisco IOS XRv |||||
271+
| Cisco Nexus OS |||||
272+
| Cumulus Linux |||||
273+
| Cumulus Linux 5.x (NVUE) |||||
274+
| Dell OS10 |||||
275+
| Fortinet FortiOS |||||
276+
| FRR |||||
277+
| Generic Linux |||||
278+
| Junos[^Junos] |||||
279+
| Mikrotik RouterOS 6 |||||
280+
| Mikrotik RouterOS 7 |||||
281+
| Nokia SR Linux |||||
282+
| Nokia SR OS[^SROS] |||||
283+
| OpenBSD |||||
284+
| Sonic |||||
285+
| VyOS |||||
286286

287287
```{tip}
288288
* Use **‌netlab show modules -m initial** to display optional initial configuration features supported by individual devices

netsim/ansible/templates/initial/eos.j2

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,27 @@ interface {{ l.ifname }}
7070
Set interface IPv6 addresses
7171
#}
7272
{% if 'ipv6' in l %}
73-
{% if role != 'host' %}
73+
{% if role == 'router' %}
74+
{% if l.ra.disable|default(false) is true %}
75+
ipv6 nd ra disabled all
76+
{% else %}
7477
ipv6 nd ra interval 5
75-
{% else %}
78+
{% if 'ra' in l and l.ipv6 is string %}
79+
{% if l.ra.slaac|default(true) is false %}
80+
ipv6 nd prefix {{ l.ipv6|ipaddr(0) }} no-autoconfig
81+
{% endif %}
82+
{% if l.ra.onlink|default(true) is false %}
83+
ipv6 nd prefix {{ l.ipv6|ipaddr(0) }} no-onlink
84+
{% endif %}
85+
{% endif %}
86+
{% if l.ra.dhcp|default(false) == 'all' %}
87+
ipv6 nd managed-config-flag
88+
{% endif %}
89+
{% if l.ra.dhcp|default(false) == 'other' %}
90+
ipv6 nd other-config-flag
91+
{% endif %}
92+
{% endif %}
93+
{% elif role == 'host' %}
7694
ipv6 nd ra rx accept default-route
7795
{% endif %}
7896
{% if l.ipv6 is sameas True %}

netsim/ansible/templates/initial/frr.j2

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,31 @@ interface {{ i.ifname }}
171171
! no ip address
172172
{% endif %}
173173
{% if i.ipv6 is defined %}
174-
{% if i.ipv6 is string and i.ipv6|ipv6 %}
174+
{% if i.ipv6 is string and i.ipv6|ipv6 %}
175175
ipv6 address {{ i.ipv6 }}
176-
{% endif %}
177-
{% if i.type != 'loopback' and role == 'router' %}
176+
{% endif %}
177+
{% if i.type != 'loopback' and role == 'router' %}
178+
{% if i.ra.disable|default(false) is true %}
179+
ipv6 nd suppress-ra
180+
{% else %}
178181
ipv6 nd ra-interval 5
179182
no ipv6 nd suppress-ra
180-
{% endif %}
183+
{% endif %}
184+
{% if 'ra' in i and i.ipv6 is string %}
185+
{% if i.ra.slaac|default(true) is false %}
186+
ipv6 nd prefix {{ i.ipv6|ipaddr(0) }} no-autoconfig
187+
{% endif %}
188+
{% if i.ra.onlink|default(true) is false %}
189+
ipv6 nd prefix {{ i.ipv6|ipaddr(0) }} off-link
190+
{% endif %}
191+
{% endif %}
192+
{% if i.ra.dhcp|default(false) == 'all' %}
193+
ipv6 nd managed-config-flag
194+
{% endif %}
195+
{% if i.ra.dhcp|default(false) == 'other' %}
196+
ipv6 nd other-config-flag
197+
{% endif %}
198+
{% endif %}
181199
{% endif %}
182200
{% if i.bandwidth is defined %}
183201
bandwidth {{ i.bandwidth }}

netsim/ansible/templates/initial/ios.j2

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,31 @@ interface {{ l.ifname }}
9797
Set interface addresses: IPv6
9898
#}
9999
{% if 'ipv6' in l %}
100-
{% if role == 'host' %}
100+
{% if role != 'router' %}
101101
ipv6 nd ra suppress all
102+
{% if role == 'host' %}
102103
ipv6 nd autoconfig default-route
104+
{% endif %}
105+
{% else %}{# router #}
106+
{% if l.ra.disable|default(false) is true %}
107+
ipv6 nd ra suppress all
108+
{% else %}
109+
ipv6 nd ra interval 5
110+
{% if 'ra' in l and l.ipv6 is string %}
111+
{% if l.ra.slaac|default(true) is false %}
112+
ipv6 nd prefix {{ l.ipv6|ipaddr(0) }} 30 30 no-autoconfig
113+
{% endif %}
114+
{% if l.ra.onlink|default(true) is false %}
115+
ipv6 nd prefix {{ l.ipv6|ipaddr(0) }} 30 30 no-onlink
116+
{% endif %}
117+
{% endif %}
118+
{% if l.ra.dhcp|default(false) == 'all' %}
119+
ipv6 nd managed-config-flag
120+
{% endif %}
121+
{% if l.ra.dhcp|default(false) == 'other' %}
122+
ipv6 nd other-config-flag
123+
{% endif %}
124+
{% endif %}
103125
{% endif %}
104126
{% if l.ipv6 == True %}
105127
ipv6 enable

netsim/augment/links.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,86 @@ def copy_link_gateway(link: Box, nodes: Box) -> None:
11021102
if af in link.gateway:
11031103
intf.gateway[af] = link.gateway[af]
11041104

1105+
"""
1106+
process_link_ra -- copy link IPv6 RA parameters to node-on-link (future interface) data
1107+
1108+
The link.ra attributes are copied only into the router nodes that support RA
1109+
(features.initial.ra). _netlab_ creates a warning message if a link has an 'ra'
1110+
dictionary but no routers attached, or if no routers attached to the link supports
1111+
'ra' attribute.
1112+
1113+
Furthermore, when there are no link/intf RA settings, all routers supporting 'ra'
1114+
attributes get the default RA settings when there are hosts attached to the link,
1115+
hopefully simplifying the configuration templates.
1116+
1117+
TODO: If a router is a DHCP relay and supports 'ra', 'ra.dhcp' should be set to 'all'
1118+
"""
1119+
1120+
def process_link_ra(link: Box, nodes: Box, defaults: Box) -> None:
1121+
rtr_list = []
1122+
host_list = []
1123+
1124+
for intf in link.interfaces: # First figure out what we're dealing with
1125+
role = nodes[intf.node].get('role','router')
1126+
if role == 'router':
1127+
rtr_list.append(intf)
1128+
if 'ra' in intf and 'ipv6' not in intf:
1129+
log.warning(
1130+
text='Applying RA attributes to an interface without an IPv6 address makes no sense',
1131+
more_data='Node {intf.node} link {link._linkname}',
1132+
module='links',
1133+
flag='ra_no_ipv6')
1134+
continue
1135+
elif role == 'host':
1136+
host_list.append(intf)
1137+
if 'ra' in intf:
1138+
log.warning(
1139+
text='Applying RA attributes to a device that is not a router makes no sense',
1140+
more_data='Node {intf.node} link {link._linkname}',
1141+
module='links',
1142+
flag='ra_not_router')
1143+
1144+
if 'ra' in link and not rtr_list: # Link RA attribute with no hosts attached
1145+
log.warning(
1146+
text='Cannot use link IPv6 RA attributes on link {link._linkname} with no routers',
1147+
module='links',flag='ra_useless')
1148+
return
1149+
1150+
if 'ra' not in link and not link.get('prefix.ipv6',None): # Processing RAs makes sense only if
1151+
return # the link contains RA attribute(s) or uses IPv6
1152+
1153+
if 'ra' not in link and not host_list: # No link RA attributes, no attached hosts
1154+
return # ... there's nothing for us to do
1155+
1156+
if not rtr_list: # No hosts, no routers... what are we doing here?
1157+
return
1158+
1159+
ra_supported = False # Does at least one router support RA attributes?
1160+
link_ra = link.get('ra',{})
1161+
for intf in rtr_list: # Now iterate over routers attached to the link
1162+
n_data = nodes[intf.node]
1163+
d_features = devices.get_device_features(n_data,defaults)
1164+
if not d_features.initial.ra: # The router does not support the RA attributes
1165+
if 'ra' in intf:
1166+
log.error(
1167+
f'We did not implement configurable IPv6 RA parameters for {n_data.device} yet',
1168+
category=log.IncorrectAttr,
1169+
module='links',
1170+
more_data='Node {intf.node}, link {link._linkname}')
1171+
continue
1172+
if 'ra' in intf or link_ra:
1173+
intf.ra = link_ra + intf.ra # Merge link attributes with interface attributes
1174+
elif host_list:
1175+
intf.ra.slaac = True
1176+
ra_supported = True
1177+
1178+
if link_ra and not ra_supported: # At least one node on the link supports RA
1179+
log.warning(
1180+
text='None of the routers connected to link {link._linkname} supports netlab IPv6 RA attributes',
1181+
module='links',flag='ra_unsupported')
1182+
1183+
return
1184+
11051185
"""
11061186
Set node.af flags to indicate that the node has IPv4 and/or IPv6 address family configured on its interfaces
11071187
"""
@@ -1250,6 +1330,7 @@ def transform(link_list: typing.Optional[Box], defaults: Box, nodes: Box, pools:
12501330
link_default_pools.append('lan')
12511331
assign_link_prefix(link,link_default_pools,pools,nodes,link._linkname)
12521332
copy_link_gateway(link,nodes)
1333+
process_link_ra(link,nodes,defaults)
12531334
assign_interface_addresses(link,pools,nodes,defaults)
12541335
create_node_interfaces(link,pools,nodes,defaults=defaults)
12551336

netsim/defaults/attributes.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ link: # Global link attributes
5858
allocation: { type: str, valid_values: [ p2p, sequential, id_based ] }
5959
_name: str
6060
_alt_types: [ bool_false, prefix_str, named_pfx ]
61+
ra:
62+
disable: bool
63+
slaac: bool
64+
dhcp: { type: str, valid_values: [ all, other ] }
65+
onlink: bool
6166
role: id
6267
pool: id
6368
type: { type: str, valid_values: [ lan, p2p, stub, loopback, tunnel, vlan_member ] }

netsim/devices/eos.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ features:
2626
use_ra: true
2727
max_mtu: 9194
2828
min_mtu: 68
29+
ra: true
2930
roles: [ host, router, bridge ]
3031
mgmt_vrf: true
3132
bfd: true

netsim/devices/frr.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ features:
5757
ipv6:
5858
lla: true
5959
use_ra: true
60+
ra: true
6061
roles: [ host, router ]
6162
bfd: True
6263
bgp:

netsim/devices/ios.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ features:
5252
use_ra: true
5353
roles: [ host, router, bridge ]
5454
mgmt_vrf: true
55+
ra: true
5556
isis:
5657
circuit_type: true
5758
import: [ bgp, ospf, ripv2, connected, static ]

0 commit comments

Comments
 (0)