#!/usr/bin/env python3 """DHCP option 121 vs option 3 Verify that DHCP option 121 (classless static routes) is used over the older option 3 (default gateway) in the DHCP client, when both are sent by the server, see RFC3442. Use the routing RIB in the operational datastore to verify. Installing routes from a DHCP server should not affect already existing (static) routes. To verify this, a static canary route (20.0.0.0/24 via 192.168.0.2) is installed before starting the DHCP client. As a twist, this canary route has a next-hop (192.168.0.2) which is not reachable until a DHCP lease has been acquired. The DHCP server is set up to hand out both a default router (option 3) via 192.168.0.1 and a default route (option 121) via 192.168.0.254. """ import infamy import infamy.dhcp import infamy.route as route from infamy.util import until with infamy.Test() as test: PREFIX = '10.0.0.0/24' ROUTER = '192.168.0.254' CANARY = '20.0.0.0/24' CANHOP = '192.168.0.2' with test.step("Setting up canary route, 20.0.0.0/24 via 192.168.0.2"): env = infamy.Env() client = env.attach("client", "mgmt") _, host = env.ltop.xlate("host", "data") # Install canary route to smoke out any regressions in # how the DHCP client installs routes in the kernel FIB client.put_config_dict("ietf-routing", { "routing": { "control-plane-protocols": { "control-plane-protocol": [{ "type": "infix-routing:static", "name": "default", "static-routes": { "ipv4": { "route": [{ "destination-prefix": CANARY, "next-hop": { "next-hop-address": CANHOP }, "infix-routing:route-preference": 250 }] } } }] } } }) with test.step("Enabling DHCP client, allow option 3 and 121"): _, port = env.ltop.xlate("client", "data") client.put_config_dict("ietf-interfaces", { "interfaces": { "interface": [{ "name": f"{port}", "ipv4": { "infix-dhcp-client:dhcp": { "option": [ {"id": "router"}, {"id": "classless-static-route"} ] } } }] } }) with infamy.IsolatedMacVlan(host) as netns: netns.addip("192.168.0.1") print(f"Start DHCP server {ROUTER} with option 3 and 121") with infamy.dhcp.Server(netns, prefix=PREFIX, router=ROUTER): with test.step("Verify client has route 10.0.0.0/24 via 192.168.0.254 (option 121)"): print("Verify client use classless routes, option 121") until(lambda: route.ipv4_route_exist(client, PREFIX, ROUTER)) with test.step("Verify client has default route via 192.168.0.254 (not use option 3)"): print("Verify client did *not* use option 3") if route.ipv4_route_exist(client, "0.0.0.0/0", ROUTER): test.fail() with test.step("Verify client still has canary route to 20.0.0.0/24 via 192.168.0.2"): until(lambda: route.ipv4_route_exist(client, CANARY, CANHOP, pref=250)) test.succeed()