Files
infix/test/case/infix_dhcp/server_host/test.py
T
Joachim Wiberg 340330b75b confd: refactor dhcp client/server modeling of client-id
Instead of encoding hex data in regular strings, using an 'id:' prefix,
or guessing based on pattern in C code, we have decided for a breaking
change in the DHCP client model.

This commit introduces string and hex values for client-id in the models
for both DHCP client and server.  The *breaking* part of the changes are
strictly related to how a user pass binary data as colon-separated hex
digits.  The dedicated leaf node `client-id` is now reserved for string
values and the generic `list option` can be used to input user defined
string or hex values.  Even non-conformant client-id options can now be
encoded.  All described in detail in infix-dhcp-client.yang

The server model has been updated to be able to send generic options in
hex format, and the static host lease matcher for client-id now require
a 'str' or 'hex' modifier.

Note: dnsmasq is RFC compliant and will not match non-conforming option
      61 (client-id) even though the client can send such payload.

To increase test coverage, the server_host test has been updated to
check both string and hex client-id.  Matching on hostname is already
covered by other test(s).

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-01-31 13:56:40 +01:00

171 lines
6.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""DHCP Server Static Host
Verify DHCP server can hand out static host leases based on client-id,
both hexadecimal and a very long string, ensuring no pool address is
handed out instead.
"""
import infamy
import infamy.iface as iface
import infamy.route as route
from infamy.util import until
with infamy.Test() as test:
POOL1 = '192.168.1.100'
ADDRESS1 = '192.168.1.11'
GW1 = '192.168.1.2'
POOL2 = '192.168.2.100'
ADDRESS2 = '192.168.2.22'
GW2 = '192.168.2.1'
HOSTNM1 = 'foo'
HOSTCID1 = '00:c0:ff:ee' # Infix DHCP server is RFC compliant
HOSTNM11 = 'client1'
HOSTCID2 = 'xyzzydissiegillespiefoobarterrawinklesouponastick'
HOSTNM2 = 'bar'
HOSTNM22 = 'client2'
with test.step("Set up topology and attach to client and server DUTs"):
env = infamy.Env()
server = env.attach("server", "mgmt")
client1 = env.attach("client1", "mgmt")
client2 = env.attach("client2", "mgmt")
with test.step("Configure DHCP client and server DUTs"):
server.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": server["link1"],
"ipv4": {
"address": [{
"ip": "192.168.1.1",
"prefix-length": 24
}]
}
}, {
"name": server["link2"],
"ipv4": {
"address": [{
"ip": "192.168.2.1",
"prefix-length": 24
}]
}
},
]
}
},
"infix-dhcp-server": {
"dhcp-server": {
"option": [{
"id": "router", "address": "auto"
}],
"subnet": [
{
"subnet": "192.168.1.0/24",
"pool": {
"start-address": POOL1,
"end-address": POOL1
},
"host": [{
"address": ADDRESS1,
"match": {
"client-id": {"hex": HOSTCID1}
},
"option": [
{
"id": "hostname",
"name": HOSTNM11
}, {
"id": "classless-static-route",
"static-route": [{
"destination": "0.0.0.0/0",
"next-hop": GW1
}]
}
]
}]
}, {
"subnet": "192.168.2.0/24",
"pool": {
"start-address": POOL2,
"end-address": POOL2
},
"host": [{
"address": ADDRESS2,
"match": {
"client-id": {"str": HOSTCID2}
},
"option": [
{
"id": "hostname",
"name": HOSTNM22
}
],
"lease-time": "infinite"
}]
},
]
}
}})
# We request hostname option just to ensure we don't get it.
client1.put_config_dicts({
"ietf-system": {
"system": {"hostname": HOSTNM1}
},
"infix-dhcp-client": {
"dhcp-client": {
"client-if": [{
"if-name": client1["link"],
"option": [
{"id": "router"},
{"id": "client-id", "hex": HOSTCID1},
{"id": 121}
]
}]
}
},
})
client2.put_config_dicts({
"ietf-system": {
"system": {"hostname": HOSTNM2}
},
"infix-dhcp-client": {
"dhcp-client": {
"client-if": [{
"if-name": client2["link"],
"client-id": HOSTCID2,
"option": [
{"id": "router"},
{"id": "hostname"},
{"id": 121}
]
}]
}
},
})
with test.step("Verify DHCP client1 static lease"):
until(lambda: iface.address_exist(client1, client1["link"], ADDRESS1))
with test.step("Verify client1 hostname has *not* changed"):
until(lambda: client1.get_data("/ietf-system:system")["system"]["hostname"] == HOSTNM1)
with test.step("Verify DHCP client1 has default route via option 121"):
until(lambda: route.ipv4_route_exist(client1, "0.0.0.0/0", nexthop=GW1))
with test.step("Verify DHCP client2 static lease"):
until(lambda: iface.address_exist(client2, client2["link"], ADDRESS2))
with test.step("Verify client2 hostname has changed"):
until(lambda: client2.get_data("/ietf-system:system")["system"]["hostname"] == HOSTNM22)
with test.step("Verify DHCP client2 has default gateway"):
until(lambda: route.ipv4_route_exist(client2, "0.0.0.0/0", nexthop=GW2))
test.succeed()