Skip to content

Commit c570445

Browse files
committed
Add DuplicateAddressDetection settings for systemd-networkd
* implement configuration key duplicate-address-detection * documentation of the parameter * adding a few tests
1 parent 83a8d8c commit c570445

File tree

13 files changed

+83
-3
lines changed

13 files changed

+83
-3
lines changed

doc/netplan-yaml.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,12 @@ Match devices by MAC when setting options like: `wakeonlan` or `*-offload`.
422422
> In addition to the addresses themselves one can specify configuration
423423
> parameters as mappings. Current supported options are:
424424

425+
- **`duplicate-address-detection`** (scalar)
426+
427+
> Configure the duplicate address detection (DAD). Valid options
428+
> are `ipv4`, `ipv6`, `both` or `none`. Currently supported on the
429+
> networkd back end only.
430+
425431
- **`lifetime`** (scalar) – since 0.100
426432

427433
> Default: `forever`. This can be `forever` or `0` and corresponds
@@ -445,6 +451,8 @@ Match devices by MAC when setting options like: `wakeonlan` or `*-offload`.
445451
- "10.0.0.15/24":
446452
lifetime: 0
447453
label: "maas"
454+
- "169.254.10.1/24":
455+
duplicate-address-detection: "none"
448456
- "2001:1::1/64"
449457
```
450458

python-cffi/netplan/_build_cffi.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
char* address;
4141
char* lifetime;
4242
char* label;
43+
char* duplicate_address_detection;
4344
} NetplanAddressOptions;
4445
struct address_iter { ...; };
4546
struct nameserver_iter { ...; };

python-cffi/netplan/netdef.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,11 @@ def __next__(self):
211211

212212

213213
class NetplanAddress:
214-
def __init__(self, address: str, lifetime: str, label: str):
214+
def __init__(self, address: str, lifetime: str, label: str, duplicate_address_detection: str):
215215
self.address = address
216216
self.lifetime = lifetime
217217
self.label = label
218+
self.duplicate_address_detection = duplicate_address_detection
218219

219220
def __str__(self) -> str:
220221
return self.address
@@ -241,7 +242,9 @@ def __next__(self):
241242
address = ffi.string(content.address).decode('utf-8') if content.address else None
242243
lifetime = ffi.string(content.lifetime).decode('utf-8') if content.lifetime else None
243244
label = ffi.string(content.label).decode('utf-8') if content.label else None
244-
return NetplanAddress(address, lifetime, label)
245+
duplicate_address_detection = ffi.string(content.duplicate_address_detection).decode('utf-8') \
246+
if content.duplicate_address_detection else None
247+
return NetplanAddress(address, lifetime, label, duplicate_address_detection)
245248

246249

247250
class _NetdefNameserverIterator:

src/netplan.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ write_addresses(yaml_event_t* event, yaml_emitter_t* emitter, const NetplanNetDe
420420
YAML_MAPPING_OPEN(event, emitter);
421421
YAML_NONNULL_STRING(event, emitter, "label", opts->label);
422422
YAML_NONNULL_STRING(event, emitter, "lifetime", opts->lifetime);
423+
YAML_NONNULL_STRING(event, emitter, "duplicate-address-detection", opts->duplicate_address_detection);
423424
YAML_MAPPING_CLOSE(event, emitter);
424425
YAML_MAPPING_CLOSE(event, emitter);
425426
}

src/networkd.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,8 @@ write_addr_option(NetplanAddressOptions* o, GString* s)
767767
g_assert(o->address != NULL);
768768
g_string_append_printf(s, "Address=%s\n", o->address);
769769

770+
if (o->duplicate_address_detection)
771+
g_string_append_printf(s, "DuplicateAddressDetection=%s\n", o->duplicate_address_detection);
770772
if (o->lifetime)
771773
g_string_append_printf(s, "PreferredLifetime=%s\n", o->lifetime);
772774
if (o->label)

src/parse.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,9 +1393,22 @@ handle_address_option_label(NetplanParser* npp, yaml_node_t* node, const void* d
13931393
return handle_generic_str(npp, node, npp->current.addr_options, data, error);
13941394
}
13951395

1396+
STATIC gboolean
1397+
handle_address_option_duplicate_address_detection(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
1398+
{
1399+
if (g_ascii_strcasecmp(scalar(node), "ipv4") != 0 &&
1400+
g_ascii_strcasecmp(scalar(node), "ipv6") != 0 &&
1401+
g_ascii_strcasecmp(scalar(node), "both") != 0 &&
1402+
g_ascii_strcasecmp(scalar(node), "none") != 0) {
1403+
return yaml_error(npp, node, error, "invalid duplicate-address-detection value '%s'", scalar(node));
1404+
}
1405+
return handle_generic_str(npp, node, npp->current.addr_options, data, error);
1406+
}
1407+
13961408
const mapping_entry_handler address_option_handlers[] = {
13971409
{"lifetime", YAML_SCALAR_NODE, {.generic=handle_address_option_lifetime}, addr_option_offset(lifetime)},
13981410
{"label", YAML_SCALAR_NODE, {.generic=handle_address_option_label}, addr_option_offset(label)},
1411+
{"duplicate-address-detection", YAML_SCALAR_NODE, {.generic=handle_address_option_duplicate_address_detection}, addr_option_offset(duplicate_address_detection)},
13991412
{NULL}
14001413
};
14011414

src/types-internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ typedef struct {
9797
char* address;
9898
char* lifetime;
9999
char* label;
100+
char* duplicate_address_detection;
100101
} NetplanAddressOptions;
101102

102103
struct address_iter {

src/types.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ free_address_options(void* ptr)
6666
g_free(opts->address);
6767
g_free(opts->label);
6868
g_free(opts->lifetime);
69+
g_free(opts->duplicate_address_detection);
6970
g_free(opts);
7071
}
7172

src/util.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ _netplan_address_iter_next(struct address_iter* it)
802802
options->address = g_strdup(netdef_options->address);
803803
options->lifetime = g_strdup(netdef_options->lifetime);
804804
options->label = g_strdup(netdef_options->label);
805+
options->duplicate_address_detection = g_strdup(netdef_options->duplicate_address_detection);
805806
it->last_address = options;
806807
return options;
807808
}

tests/config_fuzzer/schemas/common.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ export const common_properties = {
147147
label: {
148148
type: "string",
149149
maxLength: 15,
150+
},
151+
"duplicate-address-detection": {
152+
{
153+
type: "string",
154+
enum: ["ipv4", "ipv6", "both", "none"],
150155
}
151156
}
152157
}

tests/generator/test_common.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,34 @@ def test_eth_address_option_label(self):
392392
[Address]
393393
Address=192.168.14.2/24
394394
Label=test-label
395+
'''})
396+
397+
def test_eth_address_option_duplicate_address_detection(self):
398+
self.generate('''network:
399+
version: 2
400+
ethernets:
401+
engreen:
402+
addresses:
403+
- 192.168.14.2/24:
404+
duplicate-address-detection: ipv4
405+
- 2001:FFfe::1/64:
406+
duplicate-address-detection: none
407+
- 10.0.0.1/24''')
408+
409+
self.assert_networkd({'engreen.network': '''[Match]
410+
Name=engreen
411+
412+
[Network]
413+
LinkLocalAddressing=ipv6
414+
Address=10.0.0.1/24
415+
416+
[Address]
417+
Address=192.168.14.2/24
418+
DuplicateAddressDetection=ipv4
419+
420+
[Address]
421+
Address=2001:FFfe::1/64
422+
DuplicateAddressDetection=none
395423
'''})
396424

397425
def test_eth_address_option_multi_pass(self):

tests/generator/test_errors.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,16 @@ def test_invalid_address_option_lifetime(self):
408408
lifetime: 1''', expect_fail=True)
409409
self.assertIn("invalid lifetime value '1'", err)
410410

411+
def test_invalid_address_option_duplicate_address_detection(self):
412+
err = self.generate('''network:
413+
version: 2
414+
ethernets:
415+
engreen:
416+
addresses:
417+
- 192.168.1.15/24:
418+
duplicate-address-detection: a''', expect_fail=True)
419+
self.assertIn("invalid duplicate-address-detection value 'a'", err)
420+
411421
def test_invalid_nm_options(self):
412422
err = self.generate('''network:
413423
version: 2

tests/test_libnetplan.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,17 +200,23 @@ def test_iter_ethernets_with_options(self):
200200
- 172.16.0.1/24:
201201
lifetime: 0
202202
label: label1
203+
duplicate-address-detection: none
203204
- 1234:4321:abcd::cdef/96:
204205
lifetime: forever
205-
label: label2''')
206+
label: label2
207+
duplicate-address-detection: both''')
206208

207209
expected_ips = set(["1234:4321:abcd::cdef/96", "192.168.0.1/24", "172.16.0.1/24"])
208210
expected_lifetime_options = set([None, "0", "forever"])
209211
expected_label_options = set([None, "label1", "label2"])
212+
expected_duplicate_address_detection_options = set([None, "none", "both"])
210213
netdef = next(netplan.netdef.NetDefinitionIterator(state, "ethernets"))
211214
self.assertSetEqual(expected_ips, set(ip.address for ip in netdef.addresses))
212215
self.assertSetEqual(expected_lifetime_options, set(ip.lifetime for ip in netdef.addresses))
213216
self.assertSetEqual(expected_label_options, set(ip.label for ip in netdef.addresses))
217+
self.assertSetEqual(
218+
expected_duplicate_address_detection_options, set(ip.duplicate_address_detection for ip in netdef.addresses)
219+
)
214220

215221
def test_drop_iterator_before_finishing(self):
216222
state = state_from_yaml(self.confdir, '''network:

0 commit comments

Comments
 (0)