confd: rename dhcp-client option name -> id

For consistency with the new dhcp-server model, which has an option
value 'name', options are now referred to as id's or identifiers.

This means bumping the confd syntax version 1.3 -> 1.4 and adding a
migration script for existing dhcp-client configuration files.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2025-01-31 13:55:35 +01:00
parent 60f459687c
commit f86f184783
13 changed files with 72 additions and 49 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
#
################################################################################
CONFD_VERSION = 1.3
CONFD_VERSION = 1.4
CONFD_SITE_METHOD = local
CONFD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/confd
CONFD_LICENSE = BSD-3-Clause
+2 -1
View File
@@ -1,6 +1,6 @@
AC_PREREQ(2.61)
# confd version is same as system YANG model version, step on breaking changes
AC_INIT([confd], [1.3], [https://github.com/kernelkit/infix/issues])
AC_INIT([confd], [1.4], [https://github.com/kernelkit/infix/issues])
AM_INIT_AUTOMAKE(1.11 foreign subdir-objects)
AM_SILENT_RULES(yes)
@@ -17,6 +17,7 @@ AC_CONFIG_FILES([
share/migrate/1.1/Makefile
share/migrate/1.2/Makefile
share/migrate/1.3/Makefile
share/migrate/1.4/Makefile
src/Makefile
yang/Makefile
])
+15
View File
@@ -0,0 +1,15 @@
#!/bin/sh
# Rename DHCP client option attribute 'name' -> 'id' to make DHCP server.
file=$1
temp=${file}.tmp
jq '(
.["infix-dhcp-client:dhcp-client"]?."client-if"[]?.option[]? |=
if has("name") then
{ "id": .name } + . | del(.name)
else
.
end
)' "$file" > "$temp" &&
mv "$temp" "$file"
+2
View File
@@ -0,0 +1,2 @@
migratedir = $(pkgdatadir)/migrate/1.4
dist_migrate_DATA = 10-dhcp-client-option-name.sh
+1 -1
View File
@@ -1,2 +1,2 @@
SUBDIRS = 1.0 1.1 1.2 1.3
SUBDIRS = 1.0 1.1 1.2 1.3 1.4
migratedir = $(pkgdatadir)/migrate
+18 -18
View File
@@ -102,12 +102,12 @@ static char *os_name_version(char *str, size_t len)
return str;
}
static char *compose_option(const char *ifname, const char *name, const char *value,
static char *compose_option(const char *ifname, const char *id, const char *value,
char *option, size_t len)
{
if (value) {
if (isdigit(name[0])) {
unsigned long opt = strtoul(name, NULL, 0);
if (isdigit(id[0])) {
unsigned long opt = strtoul(id, NULL, 0);
switch (opt) {
case 81:
@@ -116,25 +116,25 @@ static char *compose_option(const char *ifname, const char *name, const char *va
break;
}
snprintf(option, len, "-x %s:%s ", name, value);
snprintf(option, len, "-x %s:%s ", id, value);
} else {
if (!strcmp(name, "fqdn"))
if (!strcmp(id, "fqdn"))
fqdn(value, option, len);
else if (!strcmp(name, "hostname"))
snprintf(option, len, "-x %s:%s ", name, value);
else if (!strcmp(id, "hostname"))
snprintf(option, len, "-x %s:%s ", id, value);
else
snprintf(option, len, "-x %s:'\"%s\"' ", name, value);
snprintf(option, len, "-x %s:'\"%s\"' ", id, value);
}
} else {
struct { char *name; char *(*cb)(const char *, char *, size_t); } opt[] = {
struct { char *id; char *(*cb)(const char *, char *, size_t); } opt[] = {
{ "hostname", hostname },
{ "address", ip_cache },
{ "fqdn", NULL },
{ NULL, NULL }
};
for (size_t i = 0; opt[i].name; i++) {
if (strcmp(name, opt[i].name))
for (size_t i = 0; opt[i].id; i++) {
if (strcmp(id, opt[i].id))
continue;
if (!opt[i].cb || !opt[i].cb(ifname, option, len))
@@ -143,17 +143,17 @@ static char *compose_option(const char *ifname, const char *name, const char *va
return option;
}
snprintf(option, len, "-O %s ", name);
snprintf(option, len, "-O %s ", id);
}
return option;
}
static char *compose_options(const char *ifname, char **options, const char *name, const char *value)
static char *compose_options(const char *ifname, char **options, const char *id, const char *value)
{
char opt[300];
compose_option(ifname, name, value, opt, sizeof(opt));
compose_option(ifname, id, value, opt, sizeof(opt));
if (*options) {
char *opts;
@@ -177,10 +177,10 @@ static char *dhcp_options(const char *ifname, struct lyd_node *cfg)
char *options = NULL;
LYX_LIST_FOR_EACH(lyd_child(cfg), option, "option") {
const char *value = lydx_get_cattr(option, "value");
const char *name = lydx_get_cattr(option, "name");
const char *id = lydx_get_cattr(option, "id");
const char *val = lydx_get_cattr(option, "value");
options = compose_options(ifname, &options, name, value);
options = compose_options(ifname, &options, id, val);
}
if (!options) {
@@ -339,7 +339,7 @@ static void infer_options(sr_session_ctx_t *session, const char *xpath)
size_t i;
for (i = 0; i < NELEMS(opt); i++)
srx_set_item(session, NULL, 0, "%s/option[name='%s']", xpath, opt[i]);
srx_set_item(session, NULL, 0, "%s/option[id='%s']", xpath, opt[i]);
}
static int cand(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
+1 -1
View File
@@ -30,7 +30,7 @@ MODULES=(
"infix-routing@2024-11-27.yang"
"ieee802-dot1ab-lldp@2022-03-15.yang"
"infix-lldp@2025-01-08.yang"
"infix-dhcp-client@2024-09-20.yang"
"infix-dhcp-client@2025-01-20.yang"
"infix-dhcp-server@2024-08-18.yang"
"infix-meta@2024-10-18.yang"
"infix-system@2024-11-27.yang"
+7 -3
View File
@@ -10,6 +10,10 @@ module infix-dhcp-client {
contact "kernelkit@googlegroups.com";
description "This module implements an IPv4 DHCP client";
revision 2025-01-20 {
description "Rename option 'name' -> 'id' and rename internal type.";
reference "internal";
}
revision 2024-09-20 {
description "Routes are installed in Frr (staticd), clarify preference
vs metric and adjust default preference 100 -> 5.";
@@ -133,13 +137,13 @@ module infix-dhcp-client {
description "ARP for lease to check for IP address collisions (slow).";
}
list option {
key "name";
key "id";
description
"List of DHCP options to request (and accept). The default is an
empty list, meaning all supported options. To restrict the
client to only get IP address and default route, set this to:
'subnet router'";
leaf name {
leaf id {
type dhcp-client-options;
description "DHCP option to request from, or inform server of.";
}
@@ -149,7 +153,7 @@ module infix-dhcp-client {
Example: option:hostname, value:xyzzy
option:clientid, value:01:02:03:04:05:06:07:08:09:0a
option:0x51, value:xyzzy.example.com";
must "../name != 'hostname' or re-match(., '[a-zA-Z0-9\\-_]{1,64}')";
must "../id != 'hostname' or re-match(., '[a-zA-Z0-9\\-_]{1,64}')";
}
}
leaf route-preference {
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
Route preference: DHCP vs Static
Route preference: DHCP vs Static
This test configures a device with both a DHCP-acquired route on a
dedicated interface and a static route to the same destination on
@@ -70,13 +70,13 @@ def config_target1(target, data1, data2, link):
"if-name": data1,
"enabled": True,
"option": [
{"name": "broadcast"},
{"name": "dns"},
{"name": "domain"},
{"name": "hostname"},
{"name": "ntpsrv"},
{"name": "router"},
{"name": "subnet"}
{"id": "broadcast"},
{"id": "dns"},
{"id": "domain"},
{"id": "hostname"},
{"id": "ntpsrv"},
{"id": "router"},
{"id": "subnet"}
],
"route-preference": 5
}
+4 -3
View File
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
"""
DHCP Basic
"""DHCP Basic
This is a very basic DHCP test that requests an IPv4 lease
from a DHCP server and checks that the lease is set on the
interface.
"""
import infamy, infamy.dhcp
@@ -13,6 +13,7 @@ from infamy.util import until
with infamy.Test() as test:
ADDRESS = '10.0.0.42'
with test.step("Initialize"):
env = infamy.Env()
client = env.attach("client", "mgmt")
@@ -31,7 +32,7 @@ with infamy.Test() as test:
}
client.put_config_dict("infix-dhcp-client", config)
with test.step("Verify client get DHCP lease for 10.0.0.42 on client:data"):
with test.step("Verify client lease for 10.0.0.42"):
until(lambda: iface.address_exist(client, port, ADDRESS))
test.succeed()
+7 -7
View File
@@ -1,9 +1,8 @@
#!/usr/bin/env python3
"""
DHCP router
"""DHCP router
Verify that the DHCP client receives default gateway (DHCP option 3, router)
and that route exists in operational datastore.
Verify that the DHCP client receives default gateway (DHCP option 3,
router) and that route exists in operational datastore.
"""
import infamy, infamy.dhcp
@@ -11,7 +10,8 @@ import infamy.route as route
from infamy.util import until
with infamy.Test() as test:
ROUTER = '192.168.0.254'
ROUTER = '192.168.0.254'
with test.step("Initialize"):
env = infamy.Env()
client = env.attach("client", "mgmt")
@@ -26,14 +26,14 @@ with infamy.Test() as test:
"client-if": [{
"if-name": f"{port}",
"option": [
{ "name": "router" }
{"id": "router"}
]
}]
}
}
client.put_config_dict("infix-dhcp-client", config)
with test.step("Verify client to set up default route via 192.168.0.254"):
with test.step("Verify client has default route via 192.168.0.254"):
until(lambda: route.ipv4_route_exist(client, "0.0.0.0/0", ROUTER))
test.succeed()
+6 -6
View File
@@ -64,8 +64,8 @@ with infamy.Test() as test:
"client-if": [{
"if-name": f"{port}",
"option": [
{"name": "router"},
{"name": "staticroutes"}
{"id": "router"},
{"id": "staticroutes"}
]
}]
}
@@ -75,17 +75,17 @@ with infamy.Test() as test:
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 a route 10.0.0.0/24 via 192.168.0.254 (option 121)"):
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)"):
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"):
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))
CANHOP, pref=250))
test.succeed()