Skip to content

Commit c0662e5

Browse files
committed
Implement IPv6 RA attributes
* 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)
1 parent a8f60ea commit c0662e5

File tree

8 files changed

+225
-0
lines changed

8 files changed

+225
-0
lines changed

netsim/ansible/templates/initial/eos.j2

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,25 @@ interface {{ l.ifname }}
7171
#}
7272
{% if 'ipv6' in l %}
7373
{% if role != 'host' %}
74+
{% if l.ra.disable|default(false) is true %}
75+
ipv6 nd ra disabled all
76+
{% else %}
7477
ipv6 nd ra interval 5
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 %}
7593
{% else %}
7694
ipv6 nd ra rx accept default-route
7795
{% endif %}

netsim/augment/links.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,67 @@ 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+
if 'ra' not in link and not link.get('prefix.ipv6',None): # Processing RAs makes sense only if
1122+
return # the link contains RA attribute(s) or uses IPv6
1123+
1124+
# Get a list of routers and hosts
1125+
rtr_list = [ intf for intf in link.interfaces if nodes[intf.node].get('role','router') == 'router' ]
1126+
host_list = [ intf for intf in link.interfaces if nodes[intf.node].get('role','') == 'host' ]
1127+
1128+
if 'ra' in link and not rtr_list: # Link RA attribute with no hosts attached
1129+
log.warning(
1130+
text='Cannot use link IPv6 RA attributes on link {link._linkname} with no routers',
1131+
module='links',flag='ra_useless')
1132+
return
1133+
1134+
if 'ra' not in link and not host_list: # No link RA attributes, no attached hosts
1135+
return # ... there's nothing for us to do
1136+
1137+
if not rtr_list: # No hosts, no routers... what are we doing here?
1138+
return
1139+
1140+
ra_supported = False # Does at least one router support RA attributes?
1141+
link_ra = link.get('ra',{})
1142+
for intf in rtr_list: # Now iterate over routers attached to the link
1143+
n_data = nodes[intf.node]
1144+
d_features = devices.get_device_features(n_data,defaults)
1145+
if not d_features.initial.ra: # The router does not support the RA attributes
1146+
if 'ra' in intf:
1147+
log.error(
1148+
f'We did not implement configurable IPv6 RA parameters for {n_data.device} yet',
1149+
category=log.IncorrectAttr,
1150+
module='links',
1151+
more_data='Node {intf.node}, link {link._linkname}')
1152+
continue
1153+
if 'ra' in intf or link_ra:
1154+
intf.ra = link_ra + intf.ra # Merge link attributes with interface attributes
1155+
elif host_list:
1156+
intf.ra.slaac = True
1157+
ra_supported = True
1158+
1159+
if link_ra and not ra_supported: # At least one node on the link supports RA
1160+
log.warning(
1161+
text='None of the routers connected to link {link._linkname} supports netlab IPv6 RA attributes',
1162+
module='links',flag='ra_unsupported')
1163+
1164+
return
1165+
11051166
"""
11061167
Set node.af flags to indicate that the node has IPv4 and/or IPv6 address family configured on its interfaces
11071168
"""
@@ -1250,6 +1311,7 @@ def transform(link_list: typing.Optional[Box], defaults: Box, nodes: Box, pools:
12501311
link_default_pools.append('lan')
12511312
assign_link_prefix(link,link_default_pools,pools,nodes,link._linkname)
12521313
copy_link_gateway(link,nodes)
1314+
process_link_ra(link,nodes,defaults)
12531315
assign_interface_addresses(link,pools,nodes,defaults)
12541316
create_node_interfaces(link,pools,nodes,defaults=defaults)
12551317

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/none.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ features:
6767
ipv6:
6868
lla: True
6969
mgmt_vrf: True
70+
ra: True
7071
roles: [ router, bridge, host ]
7172
isis:
7273
import: [ bgp, ospf, ripv2, connected, vrf ]
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
---
2+
message:
3+
This scenario tests IPv6 RA behavior
4+
5+
defaults.sources.extra: [ ../wait_times.yml, ../warnings.yml ]
6+
7+
addressing:
8+
loopback:
9+
ipv4: False
10+
ipv6: 2001:db8:0::/48
11+
lan:
12+
ipv4: False
13+
ipv6: 2001:db8:1::/48
14+
15+
groups:
16+
_auto_create: True
17+
hosts:
18+
members: [ h0, h1, h2, h3, h4 ]
19+
device: frr
20+
role: host
21+
provider: clab
22+
23+
nodes:
24+
dut:
25+
module: []
26+
id: 132
27+
loopback: True
28+
h4:
29+
device: linux
30+
provider: libvirt
31+
dhsrv:
32+
module: [ dhcp ]
33+
device: dnsmasq
34+
dhcp.server: true
35+
provider: clab
36+
37+
links:
38+
- name: Regular link (default RA behavior)
39+
dut:
40+
h0:
41+
ipv6: True
42+
- name: no RA
43+
dut:
44+
h1:
45+
ipv6: True
46+
ra.disable: True # No RA on this link -> no SLAAC, no default route
47+
- name: No SLAAC
48+
dut:
49+
h2:
50+
ipv6: True
51+
ra.slaac: False # No SLAAC on this link, default route should be present
52+
- name: No onlink
53+
dut:
54+
h3:
55+
ipv6: True
56+
ra.onlink: False # The prefix should not be in the IPv6 routing table
57+
- name: DHCPv6 managed config
58+
dut:
59+
h4:
60+
ipv6: dhcp
61+
dhsrv:
62+
ra.dhcp: all # The host should use DHCP to get an IPv6 address
63+
ra.slaac: False
64+
65+
# Expected results
66+
#
67+
# H0: SLAAC address, default route, prefix in routing table
68+
# H1: No SLAAC address, no default route, no prefix
69+
# H2: Default route, prefix, no SLAAC address
70+
# H3: Default route, SLAAC address, no prefix
71+
# H4: Default route, SLAAC address, prefix, DHCPv6 address (maybe)
72+
73+
validate:
74+
ra:
75+
description: Check RA-generated default route
76+
wait: ra_send
77+
wait_msg: Waiting for RA message to generate the default route
78+
nodes: [ h0, h2, h3, h4 ]
79+
plugin: default6()
80+
stop_on_error: True
81+
slaac:
82+
description: Check for SLAAC IPv6 address
83+
wait: ra_send
84+
devices: [ linux ]
85+
nodes: [ h0, h3, h4 ]
86+
exec: ip addr show dev {{ interfaces[0].ifname }}
87+
valid: >-
88+
'2001:db8' in stdout
89+
onlink:
90+
description: Check for onlink prefix
91+
wait: ra_send
92+
devices: [ linux ]
93+
nodes: [ h0, h2, h4 ]
94+
exec: ip -6 route
95+
valid: >-
96+
'2001:db8' in stdout
97+
dhcpv6:
98+
description: Check for DHCPv6 IPv6 address
99+
wait: ra_send
100+
devices: [ linux ]
101+
nodes: [ h4 ]
102+
exec: ip addr show dev {{ interfaces[0].ifname }}
103+
valid: >-
104+
'2001:db8' in stdout and '/128' in stdout
105+
w3:
106+
description: Waiting a few seconds just in case...
107+
wait: 3
108+
no_slaac:
109+
description: Check for lack of SLAAC address
110+
devices: [ linux ]
111+
nodes: [ h1, h2 ]
112+
exec: ip addr show dev {{ interfaces[0].ifname }}
113+
valid: >-
114+
'2001:db8' not in stdout
115+
no_link:
116+
description: Check for lack of onlink prefix
117+
devices: [ linux ]
118+
nodes: [ h1, h3 ]
119+
exec: ip -6 route
120+
valid: >-
121+
'2001:db8' not in stdout
122+
no_def:
123+
description: Check for lack of IPv6 default route
124+
devices: [ linux ]
125+
nodes: [ h1 ]
126+
exec: ip -6 route
127+
valid: >-
128+
'default' not in stdout

tests/topology/expected/anycast-gateway.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ links:
261261
ipv4: 172.31.31.3/24
262262
ipv6: 2001:db8:cafe:1::3/64
263263
node: r2
264+
ra:
265+
slaac: true
264266
- gateway:
265267
anycast:
266268
mac: 0200.cafe.00ff
@@ -1036,6 +1038,8 @@ nodes:
10361038
area: 0.0.0.0
10371039
network_type: point-to-point
10381040
passive: true
1041+
ra:
1042+
slaac: true
10391043
role: stub
10401044
type: lan
10411045
- ifindex: 6

tests/topology/expected/dhcp-vlan.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ links:
4444
ipv4: 192.168.42.2/24
4545
ipv6: 2001:db8:cafe:d001::2/64
4646
node: s1
47+
ra:
48+
slaac: true
4749
- ifindex: 1
4850
ifname: eth1
4951
ipv4: 192.168.42.7/24
@@ -83,6 +85,8 @@ links:
8385
ifname: eth2
8486
ipv6: 2001:db8:cafe:1::2/64
8587
node: s1
88+
ra:
89+
slaac: true
8690
vlan:
8791
access: blue
8892
- dhcp:
@@ -529,6 +533,8 @@ nodes:
529533
ipv4: 192.168.42.7/24
530534
ipv6: 2001:db8:cafe:d001::7/64
531535
node: dhs
536+
ra:
537+
slaac: true
532538
type: lan
533539
- bridge: input_2
534540
ifindex: 2

0 commit comments

Comments
 (0)