Skip to content

Commit c91fc1f

Browse files
committed
test: add dhcpv6 client tests
- Basic DHCPv6 client, request a lease and verify Linux installs a default route from the Router Advertisements (option not included in DHCPv6), hence the 'enable-ra' option to dnsmasq. Also verify DNS resolution over IPv6, including the resolvconf dance - Prefix delegation, verifies that the client can actually request and receives a prefix from a DHCPv6 server. We don't do anything with it today though Two new DHCP server implementations, the basic test relies on dnsmasq and the prefix delegation test requires an ISC based dhcp server with server side support for ipv6 prefix delegation. We could reuse the latter for both tests, but this way we get compatibility testing. Signed-off-by: Joachim Wiberg <[email protected]>
1 parent 987dd40 commit c91fc1f

File tree

8 files changed

+478
-0
lines changed

8 files changed

+478
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test.adoc
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env python3
2+
"""DHCPv6 Basic
3+
4+
Enable a DHCPv6 client and verify it requests an IPv6 lease from a
5+
DHCPv6 server that is then set on the interface.
6+
7+
"""
8+
9+
import infamy
10+
import infamy.dhcp
11+
import infamy.iface as iface
12+
import infamy.route as route
13+
from infamy.util import until
14+
15+
with infamy.Test() as test:
16+
SERVER = '2001:db8::1'
17+
CLIENT = '2001:db8::42'
18+
DOMAIN = 'example.com'
19+
VERIFY = 'server.' + DOMAIN
20+
21+
with test.step("Set up topology and attach to target DUT"):
22+
env = infamy.Env()
23+
client = env.attach("client", "mgmt")
24+
tgtssh = env.attach("client", "mgmt", "ssh")
25+
_, host = env.ltop.xlate("host", "data")
26+
_, port = env.ltop.xlate("client", "data")
27+
28+
with infamy.IsolatedMacVlan(host) as netns:
29+
netns.addip(SERVER, prefix_length=64, proto="ipv6")
30+
with infamy.dhcp.Server6Dnsmasq(netns,
31+
start=CLIENT,
32+
end=CLIENT,
33+
dns=SERVER,
34+
domain=DOMAIN,
35+
address=SERVER):
36+
37+
with test.step("Configure DHCPv6 client"):
38+
config = {
39+
"interfaces": {
40+
"interface": [{
41+
"name": f"{port}",
42+
"ipv6": {
43+
"enabled": True,
44+
"infix-dhcpv6-client:dhcp": {
45+
"option": [
46+
{"id": "dns-server"},
47+
{"id": "domain-search"}
48+
]
49+
}
50+
}
51+
}]
52+
}
53+
}
54+
client.put_config_dict("ietf-interfaces", config)
55+
56+
with test.step(f"Verify client lease for {CLIENT}"):
57+
until(lambda: iface.address_exist(client, port, CLIENT, prefix_length=128))
58+
59+
with test.step("Verify client default route ::/0"):
60+
until(lambda: route.ipv6_route_exist(client, "::/0"), attempts=20)
61+
62+
with test.step("Verify client domain name resolution"):
63+
rc = tgtssh.runsh(f"ping -6 -c1 -w20 {VERIFY}")
64+
assert rc.returncode == 0, f"Client failed: ping {VERIFY}"
65+
66+
test.succeed()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
graph "1x2" {
2+
layout="neato";
3+
overlap="false";
4+
esep="+100";
5+
6+
node [shape=record, fontname="DejaVu Sans Mono, Book"];
7+
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
8+
9+
host [
10+
label="host | { <mgmt> mgmt | <data> data }",
11+
pos="0,20!",
12+
requires="controller",
13+
];
14+
15+
client [
16+
label="{ <mgmt> mgmt | <data> data } | client",
17+
pos="200,20!",
18+
requires="infix",
19+
];
20+
21+
host:mgmt -- client:mgmt [requires="mgmt", color=lightgrey]
22+
host:data -- client:data [color=black, taillabel="2001:db8::1/64", headlabel="2001:db8::42/128"]
23+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test.adoc
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/env python3
2+
"""DHCPv6 Prefix Delegation
3+
4+
Verify DHCPv6 prefix delegation (IA_PD) where a client requests an IPv6
5+
prefix from a DHCPv6 server. This is commonly used on WAN interfaces of
6+
routers to obtain a prefix for distribution to downstream networks.
7+
8+
"""
9+
10+
import infamy, infamy.dhcp
11+
import infamy.iface as iface
12+
from infamy.util import until
13+
import time
14+
15+
16+
def checkrun(dut):
17+
"""Check DUT is running DHCPv6 client"""
18+
res = dut.runsh(f"pgrep -f 'udhcpc6.*{port}'")
19+
# print(f"Checking for udhcpc6: {res.stdout}")
20+
if res.stdout.strip() != "":
21+
return True
22+
return False
23+
24+
25+
def checklog(dut):
26+
"""Check syslog for prefix delegation message"""
27+
rc = dut.runsh("tail -10 /log/syslog | grep 'received delegated prefix'")
28+
# print(f"DHCPv6 client logs:\n{res.stdout}")
29+
if rc.stdout.strip() != "":
30+
return True
31+
return False
32+
33+
34+
with infamy.Test() as test:
35+
SERVER = '2001:db8::1'
36+
CLIENT = '2001:db8::42'
37+
PREFIX = '2001:db8:1::/48'
38+
39+
with test.step("Set up topology and attach to target DUT"):
40+
env = infamy.Env()
41+
client = env.attach("client", "mgmt")
42+
tgtssh = env.attach("client", "mgmt", "ssh")
43+
_, host = env.ltop.xlate("host", "data")
44+
_, port = env.ltop.xlate("client", "data")
45+
46+
with infamy.IsolatedMacVlan(host) as netns:
47+
netns.addip(SERVER, prefix_length=48, proto="ipv6")
48+
with infamy.dhcp.Server6Dhcpd(netns=netns,
49+
start="2001:db8::100",
50+
end="2001:db8::200",
51+
prefix="2001:db8:100::",
52+
prefix_len=64,
53+
dns="2001:db8::1",
54+
iface="iface",
55+
subnet="2001:db8::/48"):
56+
57+
with test.step("Configure DHCPv6 client w/ prefix delegation"):
58+
config = {
59+
"interfaces": {
60+
"interface": [{
61+
"name": f"{port}",
62+
"ipv6": {
63+
"enabled": True,
64+
"infix-dhcpv6-client:dhcp": {
65+
"option": [
66+
{"id": "dns-server"},
67+
{"id": "ia-pd"}
68+
]
69+
}
70+
}
71+
}]
72+
}
73+
}
74+
client.put_config_dict("ietf-interfaces", config)
75+
76+
with test.step("Verify DHCPv6 client is running"):
77+
until(lambda: checkrun(tgtssh))
78+
79+
with test.step("Verify prefix delegation in logs"):
80+
until(lambda: checklog(tgtssh))
81+
82+
test.succeed()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
graph "1x2" {
2+
layout="neato";
3+
overlap="false";
4+
esep="+100";
5+
6+
node [shape=record, fontname="DejaVu Sans Mono, Book"];
7+
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
8+
9+
host [
10+
label="host | { <mgmt> mgmt | <data> data }",
11+
pos="0,20!",
12+
requires="controller",
13+
];
14+
15+
client [
16+
label="{ <mgmt> mgmt | <data> data } | client",
17+
pos="200,20!",
18+
requires="infix",
19+
];
20+
21+
host:mgmt -- client:mgmt [requires="mgmt", color=lightgrey]
22+
host:data -- client:data [color=black, taillabel="2001:db8::1/64", headlabel="DHCPv6-PD: 2001:db8:1::/48"]
23+
}

test/case/infix_dhcp/dhcp_client.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@
1010

1111
- name: DHCP option 121 vs option 3
1212
case: client_routes/test.py
13+
14+
- name: DHCPv6 Basic
15+
case: client6_basic/test.py
16+
17+
- name: DHCPv6 Prefix Delegation
18+
case: client6_prefix_delegation/test.py

0 commit comments

Comments
 (0)