Merge ~nicolasbock/netplan:tristate-jammy into ~ubuntu-core-dev/netplan/+git/ubuntu:ubuntu/jammy

Proposed by Nicolas Bock
Status: Rejected
Rejected by: Lukas Märdian
Proposed branch: ~nicolasbock/netplan:tristate-jammy
Merge into: ~ubuntu-core-dev/netplan/+git/ubuntu:ubuntu/jammy
Diff against target: 1067 lines (+779/-73)
12 files modified
debian/changelog (+7/-2)
debian/patches/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch (+534/-0)
debian/patches/series (+1/-0)
doc/netplan.md (+18/-18)
netplan/cli/commands/apply.py (+10/-1)
src/netplan.c (+14/-7)
src/networkd.c (+28/-21)
src/parse.c (+48/-7)
src/types.c (+8/-0)
src/types.h (+28/-7)
tests/generator/test_ethernets.py (+24/-10)
tests/integration/ethernets.py (+59/-0)
Reviewer Review Type Date Requested Status
Lukas Märdian Needs Fixing
Review via email: mp+424017@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Lukas Märdian (slyon) wrote :

Thank you very much Nicolas for preparing a debdiff/merge-proposal on top of the currently pending netplan Jammy SRU changeset!

The suggested patch is looking good so far, but I left a few inline comments (see below). Furthermore, there might be some misunderstanding of how to apply distro patches. We do not want to modify any files of the "upstream source" in this repository (e.g. src/ doc/ netplan/ ...), but ONLY files in the debian/ packaging subdirectory. Please revert those changes to the upstream source, the new "distro-patch" (quilt) you added in debian/patches/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch will apply those changes for us during the package build.

Another thing: You missed to add the "ethtool" test-dependency to the "ethernets" test in debian/tests/control, this needs to be added in order to make the new offloading autopkgtests PASS (as has been done here: http://launchpadlibrarian.net/601540356/netplan.io_0.104-0ubuntu2_0.104-0ubuntu3.diff.gz).

Some of my suggested changes/fixes:
https://paste.ubuntu.com/p/vKJR7yMN92/

Last but not least, please adopt the bug report in LP: #1956264 according to the SRU template in https://wiki.ubuntu.com/StableReleaseUpdates#SRU_Bug_Template, describing a proper test case/reporducer and impact analysis, to make it fit for SRU.

Keep up the good work!

review: Needs Fixing
Revision history for this message
Lukas Märdian (slyon) wrote :

I've integrated the changes and uploaded them for Focal & Jammy SRU.

Unmerged commits

4371541... by Nicolas Bock

Backport offloading tristate patches (dc3b335), LP: #1956264

- d/p/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch

Signed-off-by: Nicolas Bock <email address hidden>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/debian/changelog b/debian/changelog
index 0a35b63..3548933 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,9 +1,14 @@
1netplan.io (0.104-0ubuntu2.1) UNRELEASED; urgency=medium1netplan.io (0.104-0ubuntu2.2) UNRELEASED; urgency=medium
22
3 [ Lukas Märdian ]
3 * Cherry-pick fix for rendering WPA3 password (8934a1b), LP: #19755764 * Cherry-pick fix for rendering WPA3 password (8934a1b), LP: #1975576
4 + d/p/0010-nm-fix-rendering-of-password-for-unknown-passthrough.patch5 + d/p/0010-nm-fix-rendering-of-password-for-unknown-passthrough.patch
56
6 -- Lukas Märdian <slyon@ubuntu.com> Tue, 24 May 2022 11:15:29 +02007 [ Nicolas Bock ]
8 * Backport offloading tristate patches (dc3b335), LP: #1956264
9 + d/p/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch
10
11 -- Nicolas Bock <nicolas.bock@canonical.com> Mon, 06 Jun 2022 16:20:44 -0600
712
8netplan.io (0.104-0ubuntu2) jammy; urgency=medium13netplan.io (0.104-0ubuntu2) jammy; urgency=medium
914
diff --git a/debian/patches/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch b/debian/patches/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch
10new file mode 10064415new file mode 100644
index 0000000..62af06c
--- /dev/null
+++ b/debian/patches/0003-Add-tristate-type-for-offload-options-LP-1956264-270.patch
@@ -0,0 +1,534 @@
1From: Nicolas Bock <nicolas.bock@canonical.com>
2Date: Thu, 12 May 2022 01:18:36 -0600
3Subject: Add tristate type for offload options (LP: #1956264) (#270)
4MIME-Version: 1.0
5Content-Type: text/plain; charset="utf-8"
6Content-Transfer-Encoding: 8bit
7
8Closes: https://bugs.launchpad.net/netplan/+bug/1956264
9
10COMMITS:
11* Add tristate type for offload options
12* Clarify name matching issues for `networkd`
13* Fix tests for offload
14* cli: apply: properly change udev/.link offloading settings
15* Re-used existing offloading fields, they are ABI compatible
16Size:
17Both enum and gboolean reduce to integer, so they are of same size.
18Content:
19An old consumer looking at these will interpret UNSET as if it was TRUE,
20which is the kernel's default (=UNSET) value.
21* CI: quirk to add ethtool test dependency
22* tests: ethernets: link offloading validation
23* doc: be more specific with the offloading docs
24
25Co-authored-by: Lukas Märdian <slyon@ubuntu.com>
26---
27 doc/netplan.md | 36 ++++++++++++------------
28 netplan/cli/commands/apply.py | 11 +++++++-
29 src/netplan.c | 21 +++++++++-----
30 src/networkd.c | 49 ++++++++++++++++++--------------
31 src/parse.c | 55 +++++++++++++++++++++++++++++++-----
32 src/types.c | 8 ++++++
33 src/types.h | 35 ++++++++++++++++++-----
34 tests/generator/test_ethernets.py | 34 +++++++++++++++-------
35 tests/integration/ethernets.py | 59 +++++++++++++++++++++++++++++++++++++++
36 9 files changed, 237 insertions(+), 71 deletions(-)
37
38diff --git a/doc/netplan.md b/doc/netplan.md
39index e9b4e92..373e05d 100644
40--- a/doc/netplan.md
41+++ b/doc/netplan.md
42@@ -79,6 +79,10 @@ Virtual devices
43
44 ## Common properties for physical device types
45
46+**Note:** Some options will not work reliably for devices matched by name only
47+and rendered by networkd, due to interactions with device renaming in udev.
48+Match devices by MAC when setting options like: ``wakeonlan`` or ``*-offload``.
49+
50 ``match`` (mapping)
51
52 : This selects a subset of available physical devices by various hardware
53@@ -139,54 +143,50 @@ Virtual devices
54
55 : Enable wake on LAN. Off by default.
56
57- **Note:** This will not work reliably for devices matched by name
58- only and rendered by networkd, due to interactions with device
59- renaming in udev. Match devices by MAC when setting wake on LAN.
60-
61 ``emit-lldp`` (bool) – since **0.99**
62
63 : (networkd backend only) Whether to emit LLDP packets. Off by default.
64
65 ``receive-checksum-offload`` (bool) – since **0.104**
66
67-: (networkd backend only) If set to true, the hardware offload for
68- checksumming of ingress network packets is enabled. When unset,
69+: (networkd backend only) If set to true (false), the hardware offload for
70+ checksumming of ingress network packets is enabled (disabled). When unset,
71 the kernel's default will be used.
72
73 ``transmit-checksum-offload`` (bool) – since **0.104**
74
75-: (networkd backend only) If set to true, the hardware offload for
76- checksumming of egress network packets is enabled. When unset,
77+: (networkd backend only) If set to true (false), the hardware offload for
78+ checksumming of egress network packets is enabled (disabled). When unset,
79 the kernel's default will be used.
80
81 ``tcp-segmentation-offload`` (bool) – since **0.104**
82
83-: (networkd backend only) If set to true, the TCP Segmentation
84- Offload (TSO) is enabled. When unset, the kernel's default will
85+: (networkd backend only) If set to true (false), the TCP Segmentation
86+ Offload (TSO) is enabled (disabled). When unset, the kernel's default will
87 be used.
88
89 ``tcp6-segmentation-offload`` (bool) – since **0.104**
90
91-: (networkd backend only) If set to true, the TCP6 Segmentation
92- Offload (tx-tcp6-segmentation) is enabled. When unset, the
93+: (networkd backend only) If set to true (false), the TCP6 Segmentation
94+ Offload (tx-tcp6-segmentation) is enabled (disabled). When unset, the
95 kernel's default will be used.
96
97 ``generic-segmentation-offload`` (bool) – since **0.104**
98
99-: (networkd backend only) If set to true, the Generic Segmentation
100- Offload (GSO) is enabled. When unset, the kernel's default will
101+: (networkd backend only) If set to true (false), the Generic Segmentation
102+ Offload (GSO) is enabled (disabled). When unset, the kernel's default will
103 be used.
104
105 ``generic-receive-offload`` (bool) – since **0.104**
106
107-: (networkd backend only) If set to true, the Generic Receive
108- Offload (GRO) is enabled. When unset, the kernel's default will
109+: (networkd backend only) If set to true (false), the Generic Receive
110+ Offload (GRO) is enabled (disabled). When unset, the kernel's default will
111 be used.
112
113 ``large-receive-offload`` (bool) – since **0.104**
114
115-: (networkd backend only) If set to true, the Generic Receive
116- Offload (GRO) is enabled. When unset, the kernel's default will
117+: (networkd backend only) If set to true (false), the Large Receive Offload
118+ (LRO) is enabled (disabled). When unset, the kernel's default will
119 be used.
120
121 ``openvswitch`` (mapping) – since **0.100**
122diff --git a/netplan/cli/commands/apply.py b/netplan/cli/commands/apply.py
123index b36662a..9d4511f 100644
124--- a/netplan/cli/commands/apply.py
125+++ b/netplan/cli/commands/apply.py
126@@ -221,13 +221,22 @@ class NetplanApply(utils.NetplanCommand):
127 '/sys/class/net/' + device],
128 stdout=subprocess.DEVNULL,
129 stderr=subprocess.DEVNULL)
130+ subprocess.check_call(['udevadm', 'test',
131+ '/sys/class/net/' + device],
132+ stdout=subprocess.DEVNULL,
133+ stderr=subprocess.DEVNULL)
134 except subprocess.CalledProcessError:
135 logging.debug('Ignoring device without syspath: %s', device)
136
137+ devices_after_udev = netifaces.interfaces()
138 # apply some more changes manually
139 for iface, settings in changes.items():
140 # rename non-critical network interfaces
141- if settings.get('name'):
142+ new_name = settings.get('name')
143+ if new_name:
144+ if iface in devices and new_name in devices_after_udev:
145+ logging.debug('Interface rename {} -> {} already happened.'.format(iface, new_name))
146+ continue # re-name already happened via 'udevadm test'
147 # bring down the interface, using its current (matched) interface name
148 subprocess.check_call(['ip', 'link', 'set', 'dev', iface, 'down'],
149 stdout=subprocess.DEVNULL,
150diff --git a/src/netplan.c b/src/netplan.c
151index 7b387b4..d1a27a6 100644
152--- a/src/netplan.c
153+++ b/src/netplan.c
154@@ -752,13 +752,20 @@ _serialize_yaml(
155 YAML_BOOL_TRUE(def, event, emitter, "wakeonlan", def->wake_on_lan);
156
157 /* Offload options */
158- YAML_BOOL_TRUE(def, event, emitter, "receive-checksum-offload", def->receive_checksum_offload);
159- YAML_BOOL_TRUE(def, event, emitter, "transmit-checksum-offload", def->transmit_checksum_offload);
160- YAML_BOOL_TRUE(def, event, emitter, "tcp-segmentation-offload", def->tcp_segmentation_offload);
161- YAML_BOOL_TRUE(def, event, emitter, "tcp6-segmentation-offload", def->tcp6_segmentation_offload);
162- YAML_BOOL_TRUE(def, event, emitter, "generic-segmentation-offload", def->generic_segmentation_offload);
163- YAML_BOOL_TRUE(def, event, emitter, "generic-receive-offload", def->generic_receive_offload);
164- YAML_BOOL_TRUE(def, event, emitter, "large-receive-offload", def->large_receive_offload);
165+ if (def->receive_checksum_offload != NETPLAN_TRISTATE_UNSET)
166+ YAML_BOOL_TRUE(def, event, emitter, "receive-checksum-offload", def->receive_checksum_offload);
167+ if (def->transmit_checksum_offload != NETPLAN_TRISTATE_UNSET)
168+ YAML_BOOL_TRUE(def, event, emitter, "transmit-checksum-offload", def->transmit_checksum_offload);
169+ if (def->tcp_segmentation_offload != NETPLAN_TRISTATE_UNSET)
170+ YAML_BOOL_TRUE(def, event, emitter, "tcp-segmentation-offload", def->tcp_segmentation_offload);
171+ if (def->tcp6_segmentation_offload != NETPLAN_TRISTATE_UNSET)
172+ YAML_BOOL_TRUE(def, event, emitter, "tcp6-segmentation-offload", def->tcp6_segmentation_offload);
173+ if (def->generic_segmentation_offload != NETPLAN_TRISTATE_UNSET)
174+ YAML_BOOL_TRUE(def, event, emitter, "generic-segmentation-offload", def->generic_segmentation_offload);
175+ if (def->generic_receive_offload != NETPLAN_TRISTATE_UNSET)
176+ YAML_BOOL_TRUE(def, event, emitter, "generic-receive-offload", def->generic_receive_offload);
177+ if (def->large_receive_offload != NETPLAN_TRISTATE_UNSET)
178+ YAML_BOOL_TRUE(def, event, emitter, "large-receive-offload", def->large_receive_offload);
179
180 if (def->wowlan && def->wowlan != NETPLAN_WIFI_WOWLAN_DEFAULT) {
181 YAML_SCALAR_PLAIN(event, emitter, "wakeonwlan");
182diff --git a/src/networkd.c b/src/networkd.c
183index 6d26047..62c87ce 100644
184--- a/src/networkd.c
185+++ b/src/networkd.c
186@@ -243,13 +243,13 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char
187 if (!def->set_name &&
188 !def->wake_on_lan &&
189 !def->mtubytes &&
190- !def->receive_checksum_offload &&
191- !def->transmit_checksum_offload &&
192- !def->tcp_segmentation_offload &&
193- !def->tcp6_segmentation_offload &&
194- !def->generic_segmentation_offload &&
195- !def->generic_receive_offload &&
196- !def->large_receive_offload)
197+ (def->receive_checksum_offload == NETPLAN_TRISTATE_UNSET) &&
198+ (def->transmit_checksum_offload == NETPLAN_TRISTATE_UNSET) &&
199+ (def->tcp_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
200+ (def->tcp6_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
201+ (def->generic_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
202+ (def->generic_receive_offload == NETPLAN_TRISTATE_UNSET) &&
203+ (def->large_receive_offload == NETPLAN_TRISTATE_UNSET))
204 return;
205
206 /* build file contents */
207@@ -265,26 +265,33 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char
208 g_string_append_printf(s, "MTUBytes=%u\n", def->mtubytes);
209
210 /* Offload options */
211- if (def->receive_checksum_offload)
212- g_string_append_printf(s, "ReceiveChecksumOffload=%u\n", def->receive_checksum_offload);
213+ if (def->receive_checksum_offload != NETPLAN_TRISTATE_UNSET)
214+ g_string_append_printf(s, "ReceiveChecksumOffload=%s\n",
215+ (def->receive_checksum_offload ? "true" : "false"));
216
217- if (def->transmit_checksum_offload)
218- g_string_append_printf(s, "TransmitChecksumOffload=%u\n", def->transmit_checksum_offload);
219+ if (def->transmit_checksum_offload != NETPLAN_TRISTATE_UNSET)
220+ g_string_append_printf(s, "TransmitChecksumOffload=%s\n",
221+ (def->transmit_checksum_offload ? "true" : "false"));
222
223- if (def->tcp_segmentation_offload)
224- g_string_append_printf(s, "TCPSegmentationOffload=%u\n", def->tcp_segmentation_offload);
225+ if (def->tcp_segmentation_offload != NETPLAN_TRISTATE_UNSET)
226+ g_string_append_printf(s, "TCPSegmentationOffload=%s\n",
227+ (def->tcp_segmentation_offload ? "true" : "false"));
228
229- if (def->tcp6_segmentation_offload)
230- g_string_append_printf(s, "TCP6SegmentationOffload=%u\n", def->tcp6_segmentation_offload);
231+ if (def->tcp6_segmentation_offload != NETPLAN_TRISTATE_UNSET)
232+ g_string_append_printf(s, "TCP6SegmentationOffload=%s\n",
233+ (def->tcp6_segmentation_offload ? "true" : "false"));
234
235- if (def->generic_segmentation_offload)
236- g_string_append_printf(s, "GenericSegmentationOffload=%u\n", def->generic_segmentation_offload);
237+ if (def->generic_segmentation_offload != NETPLAN_TRISTATE_UNSET)
238+ g_string_append_printf(s, "GenericSegmentationOffload=%s\n",
239+ (def->generic_segmentation_offload ? "true" : "false"));
240
241- if (def->generic_receive_offload)
242- g_string_append_printf(s, "GenericReceiveOffload=%u\n", def->generic_receive_offload);
243+ if (def->generic_receive_offload != NETPLAN_TRISTATE_UNSET)
244+ g_string_append_printf(s, "GenericReceiveOffload=%s\n",
245+ (def->generic_receive_offload ? "true" : "false"));
246
247- if (def->large_receive_offload)
248- g_string_append_printf(s, "LargeReceiveOffload=%u\n", def->large_receive_offload);
249+ if (def->large_receive_offload != NETPLAN_TRISTATE_UNSET)
250+ g_string_append_printf(s, "LargeReceiveOffload=%s\n",
251+ (def->large_receive_offload ? "true" : "false"));
252
253 orig_umask = umask(022);
254 g_string_free_to_file(s, rootdir, path, ".link");
255diff --git a/src/parse.c b/src/parse.c
256index 350c508..0cb07d2 100644
257--- a/src/parse.c
258+++ b/src/parse.c
259@@ -370,6 +370,37 @@ handle_generic_bool(NetplanParser* npp, yaml_node_t* node, void* entryptr, const
260 return TRUE;
261 }
262
263+/*
264+ * Handler for setting a HashTable field from a mapping node, inside a given struct
265+ * @entryptr: pointer to the beginning of the to-be-modified data structure
266+ * @data: offset into entryptr struct where the boolean field to write is located
267+ */
268+static gboolean
269+handle_generic_tristate(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
270+{
271+ g_assert(entryptr);
272+ NetplanTristate v;
273+ guint offset = GPOINTER_TO_UINT(data);
274+ NetplanTristate* dest = ((void*) entryptr + offset);
275+
276+ if (g_ascii_strcasecmp(scalar(node), "true") == 0 ||
277+ g_ascii_strcasecmp(scalar(node), "on") == 0 ||
278+ g_ascii_strcasecmp(scalar(node), "yes") == 0 ||
279+ g_ascii_strcasecmp(scalar(node), "y") == 0)
280+ v = NETPLAN_TRISTATE_TRUE;
281+ else if (g_ascii_strcasecmp(scalar(node), "false") == 0 ||
282+ g_ascii_strcasecmp(scalar(node), "off") == 0 ||
283+ g_ascii_strcasecmp(scalar(node), "no") == 0 ||
284+ g_ascii_strcasecmp(scalar(node), "n") == 0)
285+ v = NETPLAN_TRISTATE_FALSE;
286+ else
287+ return yaml_error(npp, node, error, "invalid boolean value '%s'", scalar(node));
288+
289+ *dest = v;
290+ mark_data_as_dirty(npp, dest);
291+ return TRUE;
292+}
293+
294 /*
295 * Handler for setting a HashTable field from a mapping node, inside a given struct
296 * @entryptr: pointer to the beginning of the to-be-modified data structure
297@@ -516,6 +547,16 @@ handle_netdef_bool(NetplanParser* npp, yaml_node_t* node, const void* data, GErr
298 return handle_generic_bool(npp, node, npp->current.netdef, data, error);
299 }
300
301+/**
302+ * Generic handler for tri-state settings that can bei "UNSET", "TRUE", or "FALSE".
303+ * @data: offset into NetplanNetDefinition where the guint field to write is located
304+ */
305+static gboolean
306+handle_netdef_tristate(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
307+{
308+ return handle_generic_tristate(npp, node, npp->current.netdef, data, error);
309+}
310+
311 /**
312 * Generic handler for setting a npp->current.netdef guint field from a scalar node
313 * @data: offset into NetplanNetDefinition where the guint field to write is located
314@@ -2356,13 +2397,13 @@ static const mapping_entry_handler dhcp6_overrides_handlers[] = {
315 {"wakeonlan", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(wake_on_lan)}, \
316 {"wakeonwlan", YAML_SEQUENCE_NODE, {.generic=handle_wowlan}, netdef_offset(wowlan)}, \
317 {"emit-lldp", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(emit_lldp)}, \
318- {"receive-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(receive_checksum_offload)}, \
319- {"transmit-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(transmit_checksum_offload)}, \
320- {"tcp-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(tcp_segmentation_offload)}, \
321- {"tcp6-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(tcp6_segmentation_offload)}, \
322- {"generic-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(generic_segmentation_offload)}, \
323- {"generic-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(generic_receive_offload)}, \
324- {"large-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(large_receive_offload)}
325+ {"receive-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(receive_checksum_offload)}, \
326+ {"transmit-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(transmit_checksum_offload)}, \
327+ {"tcp-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(tcp_segmentation_offload)}, \
328+ {"tcp6-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(tcp6_segmentation_offload)}, \
329+ {"generic-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(generic_segmentation_offload)}, \
330+ {"generic-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(generic_receive_offload)}, \
331+ {"large-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(large_receive_offload)}
332
333 static const mapping_entry_handler ethernet_def_handlers[] = {
334 COMMON_LINK_HANDLERS,
335diff --git a/src/types.c b/src/types.c
336index eb9f780..00c2b0f 100644
337--- a/src/types.c
338+++ b/src/types.c
339@@ -335,6 +335,14 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
340
341 reset_private_netdef_data(netdef->_private);
342 FREE_AND_NULLIFY(netdef->_private);
343+
344+ netdef->receive_checksum_offload = NETPLAN_TRISTATE_UNSET;
345+ netdef->transmit_checksum_offload = NETPLAN_TRISTATE_UNSET;
346+ netdef->tcp_segmentation_offload = NETPLAN_TRISTATE_UNSET;
347+ netdef->tcp6_segmentation_offload = NETPLAN_TRISTATE_UNSET;
348+ netdef->generic_segmentation_offload = NETPLAN_TRISTATE_UNSET;
349+ netdef->generic_receive_offload = NETPLAN_TRISTATE_UNSET;
350+ netdef->large_receive_offload = NETPLAN_TRISTATE_UNSET;
351 }
352
353 static void
354diff --git a/src/types.h b/src/types.h
355index 27a23fc..710b1f1 100644
356--- a/src/types.h
357+++ b/src/types.h
358@@ -171,6 +171,27 @@ typedef union {
359 } networkd;
360 } NetplanBackendSettings;
361
362+typedef enum
363+{
364+ /**
365+ * @brief Tristate enum type
366+ *
367+ * This type defines a boolean which can be unset, i.e.
368+ * this type has three states. The enum is ordered so
369+ * that
370+ *
371+ * UNSET -> -1
372+ * FALSE -> 0
373+ * TRUE -> 1
374+ *
375+ * And the integer values can be used directly when
376+ * converting to string.
377+ */
378+ NETPLAN_TRISTATE_UNSET = -1, /* -1 */
379+ NETPLAN_TRISTATE_FALSE, /* 0 */
380+ NETPLAN_TRISTATE_TRUE, /* 1 */
381+} NetplanTristate;
382+
383 struct netplan_net_definition {
384 NetplanDefType type;
385 NetplanBackend backend;
386@@ -333,13 +354,13 @@ struct netplan_net_definition {
387 gboolean ignore_carrier;
388
389 /* offload options */
390- gboolean receive_checksum_offload;
391- gboolean transmit_checksum_offload;
392- gboolean tcp_segmentation_offload;
393- gboolean tcp6_segmentation_offload;
394- gboolean generic_segmentation_offload;
395- gboolean generic_receive_offload;
396- gboolean large_receive_offload;
397+ NetplanTristate receive_checksum_offload;
398+ NetplanTristate transmit_checksum_offload;
399+ NetplanTristate tcp_segmentation_offload;
400+ NetplanTristate tcp6_segmentation_offload;
401+ NetplanTristate generic_segmentation_offload;
402+ NetplanTristate generic_receive_offload;
403+ NetplanTristate large_receive_offload;
404
405 struct private_netdef_data* _private;
406
407diff --git a/tests/generator/test_ethernets.py b/tests/generator/test_ethernets.py
408index 46bf764..e81941b 100644
409--- a/tests/generator/test_ethernets.py
410+++ b/tests/generator/test_ethernets.py
411@@ -772,11 +772,11 @@ method=ignore
412 ethernets:
413 eth1:
414 receive-checksum-offload: true
415- transmit-checksum-offload: true
416+ transmit-checksum-offload: off
417 tcp-segmentation-offload: true
418- tcp6-segmentation-offload: true
419+ tcp6-segmentation-offload: false
420 generic-segmentation-offload: true
421- generic-receive-offload: true
422+ generic-receive-offload: no
423 large-receive-offload: true''')
424
425 self.assert_networkd({'eth1.link': '''[Match]
426@@ -784,13 +784,13 @@ OriginalName=eth1
427
428 [Link]
429 WakeOnLan=off
430-ReceiveChecksumOffload=1
431-TransmitChecksumOffload=1
432-TCPSegmentationOffload=1
433-TCP6SegmentationOffload=1
434-GenericSegmentationOffload=1
435-GenericReceiveOffload=1
436-LargeReceiveOffload=1
437+ReceiveChecksumOffload=true
438+TransmitChecksumOffload=false
439+TCPSegmentationOffload=true
440+TCP6SegmentationOffload=false
441+GenericSegmentationOffload=true
442+GenericReceiveOffload=false
443+LargeReceiveOffload=true
444 ''',
445 'eth1.network': '''[Match]
446 Name=eth1
447@@ -799,3 +799,17 @@ Name=eth1
448 LinkLocalAddressing=ipv6
449 '''})
450 self.assert_networkd_udev(None)
451+
452+ def test_offload_invalid(self):
453+ err = self.generate('''network:
454+ version: 2
455+ ethernets:
456+ eth1:
457+ generic-receive-offload: n
458+ receive-checksum-offload: true
459+ tcp-segmentation-offload: true
460+ tcp6-segmentation-offload: false
461+ generic-segmentation-offload: true
462+ transmit-checksum-offload: xx
463+ large-receive-offload: true''', expect_fail=True)
464+ self.assertIn('invalid boolean value \'xx\'', err)
465diff --git a/tests/integration/ethernets.py b/tests/integration/ethernets.py
466index 865c0d4..06ac069 100644
467--- a/tests/integration/ethernets.py
468+++ b/tests/integration/ethernets.py
469@@ -236,6 +236,65 @@ class _CommonTests():
470 self.assert_iface_up('iface1', ['inet 10.10.10.11'])
471 self.assert_iface_up('iface2', ['inet 10.10.10.22'])
472
473+ def test_link_offloading(self):
474+ self.setup_eth(None, False)
475+ # check kernel defaults
476+ out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
477+ self.assertIn(b'rx-checksumming: on', out)
478+ self.assertIn(b'tx-checksumming: on', out)
479+ self.assertIn(b'tcp-segmentation-offload: on', out)
480+ self.assertIn(b'tx-tcp6-segmentation: on', out)
481+ self.assertIn(b'generic-segmentation-offload: on', out)
482+ self.assertIn(b'generic-receive-offload: off', out) # off by default
483+ # validate turning off
484+ with open(self.config, 'w') as f:
485+ f.write('''network:
486+ renderer: %(r)s
487+ ethernets:
488+ %(ec)s:
489+ addresses: [10.10.10.22/24]
490+ receive-checksum-offload: off
491+ transmit-checksum-offload: off
492+ tcp-segmentation-offload: off
493+ tcp6-segmentation-offload: off
494+ generic-segmentation-offload: off
495+ generic-receive-offload: off
496+ #large-receive-offload: off # not possible on veth
497+''' % {'r': self.backend, 'ec': self.dev_e_client})
498+ self.generate_and_settle([self.dev_e_client])
499+ self.assert_iface_up(self.dev_e_client, ['inet 10.10.10.22'])
500+ out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
501+ self.assertIn(b'rx-checksumming: off', out)
502+ self.assertIn(b'tx-checksumming: off', out)
503+ self.assertIn(b'tcp-segmentation-offload: off', out)
504+ self.assertIn(b'tx-tcp6-segmentation: off', out)
505+ self.assertIn(b'generic-segmentation-offload: off', out)
506+ self.assertIn(b'generic-receive-offload: off', out)
507+ # validate turning on
508+ with open(self.config, 'w') as f:
509+ f.write('''network:
510+ renderer: %(r)s
511+ ethernets:
512+ %(ec)s:
513+ addresses: [10.10.10.22/24]
514+ receive-checksum-offload: true
515+ transmit-checksum-offload: true
516+ tcp-segmentation-offload: true
517+ tcp6-segmentation-offload: true
518+ generic-segmentation-offload: true
519+ generic-receive-offload: true
520+ #large-receive-offload: true # not possible on veth
521+''' % {'r': self.backend, 'ec': self.dev_e_client})
522+ self.generate_and_settle([self.dev_e_client])
523+ self.assert_iface_up(self.dev_e_client, ['inet 10.10.10.22'])
524+ out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
525+ self.assertIn(b'rx-checksumming: on', out)
526+ self.assertIn(b'tx-checksumming: on', out)
527+ self.assertIn(b'tcp-segmentation-offload: on', out)
528+ self.assertIn(b'tx-tcp6-segmentation: on', out)
529+ self.assertIn(b'generic-segmentation-offload: on', out)
530+ self.assertIn(b'generic-receive-offload: on', out)
531+
532
533 @unittest.skipIf("networkd" not in test_backends,
534 "skipping as networkd backend tests are disabled")
diff --git a/debian/patches/series b/debian/patches/series
index 27d8728..77cbaaa 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,3 +1,4 @@
10003-Add-tristate-type-for-offload-options-LP-1956264-270.patch
1autopkgtest-fixes.patch2autopkgtest-fixes.patch
20002-cli-apply-fix-potential-race-with-rename-creation-of.patch30002-cli-apply-fix-potential-race-with-rename-creation-of.patch
30010-nm-fix-rendering-of-password-for-unknown-passthrough.patch40010-nm-fix-rendering-of-password-for-unknown-passthrough.patch
diff --git a/doc/netplan.md b/doc/netplan.md
index e9b4e92..373e05d 100644
--- a/doc/netplan.md
+++ b/doc/netplan.md
@@ -79,6 +79,10 @@ Virtual devices
7979
80## Common properties for physical device types80## Common properties for physical device types
8181
82**Note:** Some options will not work reliably for devices matched by name only
83and rendered by networkd, due to interactions with device renaming in udev.
84Match devices by MAC when setting options like: ``wakeonlan`` or ``*-offload``.
85
82``match`` (mapping)86``match`` (mapping)
8387
84: This selects a subset of available physical devices by various hardware88: This selects a subset of available physical devices by various hardware
@@ -139,54 +143,50 @@ Virtual devices
139143
140: Enable wake on LAN. Off by default.144: Enable wake on LAN. Off by default.
141145
142 **Note:** This will not work reliably for devices matched by name
143 only and rendered by networkd, due to interactions with device
144 renaming in udev. Match devices by MAC when setting wake on LAN.
145
146``emit-lldp`` (bool) – since **0.99**146``emit-lldp`` (bool) – since **0.99**
147147
148: (networkd backend only) Whether to emit LLDP packets. Off by default.148: (networkd backend only) Whether to emit LLDP packets. Off by default.
149149
150``receive-checksum-offload`` (bool) – since **0.104**150``receive-checksum-offload`` (bool) – since **0.104**
151151
152: (networkd backend only) If set to true, the hardware offload for152: (networkd backend only) If set to true (false), the hardware offload for
153 checksumming of ingress network packets is enabled. When unset,153 checksumming of ingress network packets is enabled (disabled). When unset,
154 the kernel's default will be used.154 the kernel's default will be used.
155155
156``transmit-checksum-offload`` (bool) – since **0.104**156``transmit-checksum-offload`` (bool) – since **0.104**
157157
158: (networkd backend only) If set to true, the hardware offload for158: (networkd backend only) If set to true (false), the hardware offload for
159 checksumming of egress network packets is enabled. When unset,159 checksumming of egress network packets is enabled (disabled). When unset,
160 the kernel's default will be used.160 the kernel's default will be used.
161161
162``tcp-segmentation-offload`` (bool) – since **0.104**162``tcp-segmentation-offload`` (bool) – since **0.104**
163163
164: (networkd backend only) If set to true, the TCP Segmentation164: (networkd backend only) If set to true (false), the TCP Segmentation
165 Offload (TSO) is enabled. When unset, the kernel's default will165 Offload (TSO) is enabled (disabled). When unset, the kernel's default will
166 be used.166 be used.
167167
168``tcp6-segmentation-offload`` (bool) – since **0.104**168``tcp6-segmentation-offload`` (bool) – since **0.104**
169169
170: (networkd backend only) If set to true, the TCP6 Segmentation170: (networkd backend only) If set to true (false), the TCP6 Segmentation
171 Offload (tx-tcp6-segmentation) is enabled. When unset, the171 Offload (tx-tcp6-segmentation) is enabled (disabled). When unset, the
172 kernel's default will be used.172 kernel's default will be used.
173173
174``generic-segmentation-offload`` (bool) – since **0.104**174``generic-segmentation-offload`` (bool) – since **0.104**
175175
176: (networkd backend only) If set to true, the Generic Segmentation176: (networkd backend only) If set to true (false), the Generic Segmentation
177 Offload (GSO) is enabled. When unset, the kernel's default will177 Offload (GSO) is enabled (disabled). When unset, the kernel's default will
178 be used.178 be used.
179179
180``generic-receive-offload`` (bool) – since **0.104**180``generic-receive-offload`` (bool) – since **0.104**
181181
182: (networkd backend only) If set to true, the Generic Receive182: (networkd backend only) If set to true (false), the Generic Receive
183 Offload (GRO) is enabled. When unset, the kernel's default will183 Offload (GRO) is enabled (disabled). When unset, the kernel's default will
184 be used.184 be used.
185185
186``large-receive-offload`` (bool) – since **0.104**186``large-receive-offload`` (bool) – since **0.104**
187187
188: (networkd backend only) If set to true, the Generic Receive188: (networkd backend only) If set to true (false), the Large Receive Offload
189 Offload (GRO) is enabled. When unset, the kernel's default will189 (LRO) is enabled (disabled). When unset, the kernel's default will
190 be used.190 be used.
191191
192``openvswitch`` (mapping) – since **0.100**192``openvswitch`` (mapping) – since **0.100**
diff --git a/netplan/cli/commands/apply.py b/netplan/cli/commands/apply.py
index d481184..5bf4ae9 100644
--- a/netplan/cli/commands/apply.py
+++ b/netplan/cli/commands/apply.py
@@ -221,13 +221,22 @@ class NetplanApply(utils.NetplanCommand):
221 '/sys/class/net/' + device],221 '/sys/class/net/' + device],
222 stdout=subprocess.DEVNULL,222 stdout=subprocess.DEVNULL,
223 stderr=subprocess.DEVNULL)223 stderr=subprocess.DEVNULL)
224 subprocess.check_call(['udevadm', 'test',
225 '/sys/class/net/' + device],
226 stdout=subprocess.DEVNULL,
227 stderr=subprocess.DEVNULL)
224 except subprocess.CalledProcessError:228 except subprocess.CalledProcessError:
225 logging.debug('Ignoring device without syspath: %s', device)229 logging.debug('Ignoring device without syspath: %s', device)
226230
231 devices_after_udev = netifaces.interfaces()
227 # apply some more changes manually232 # apply some more changes manually
228 for iface, settings in changes.items():233 for iface, settings in changes.items():
229 # rename non-critical network interfaces234 # rename non-critical network interfaces
230 if settings.get('name'):235 new_name = settings.get('name')
236 if new_name:
237 if iface in devices and new_name in devices_after_udev:
238 logging.debug('Interface rename {} -> {} already happened.'.format(iface, new_name))
239 continue # re-name already happened via 'udevadm test'
231 # bring down the interface, using its current (matched) interface name240 # bring down the interface, using its current (matched) interface name
232 subprocess.check_call(['ip', 'link', 'set', 'dev', iface, 'down'],241 subprocess.check_call(['ip', 'link', 'set', 'dev', iface, 'down'],
233 stdout=subprocess.DEVNULL,242 stdout=subprocess.DEVNULL,
diff --git a/src/netplan.c b/src/netplan.c
index 7b387b4..d1a27a6 100644
--- a/src/netplan.c
+++ b/src/netplan.c
@@ -752,13 +752,20 @@ _serialize_yaml(
752 YAML_BOOL_TRUE(def, event, emitter, "wakeonlan", def->wake_on_lan);752 YAML_BOOL_TRUE(def, event, emitter, "wakeonlan", def->wake_on_lan);
753753
754 /* Offload options */754 /* Offload options */
755 YAML_BOOL_TRUE(def, event, emitter, "receive-checksum-offload", def->receive_checksum_offload);755 if (def->receive_checksum_offload != NETPLAN_TRISTATE_UNSET)
756 YAML_BOOL_TRUE(def, event, emitter, "transmit-checksum-offload", def->transmit_checksum_offload);756 YAML_BOOL_TRUE(def, event, emitter, "receive-checksum-offload", def->receive_checksum_offload);
757 YAML_BOOL_TRUE(def, event, emitter, "tcp-segmentation-offload", def->tcp_segmentation_offload);757 if (def->transmit_checksum_offload != NETPLAN_TRISTATE_UNSET)
758 YAML_BOOL_TRUE(def, event, emitter, "tcp6-segmentation-offload", def->tcp6_segmentation_offload);758 YAML_BOOL_TRUE(def, event, emitter, "transmit-checksum-offload", def->transmit_checksum_offload);
759 YAML_BOOL_TRUE(def, event, emitter, "generic-segmentation-offload", def->generic_segmentation_offload);759 if (def->tcp_segmentation_offload != NETPLAN_TRISTATE_UNSET)
760 YAML_BOOL_TRUE(def, event, emitter, "generic-receive-offload", def->generic_receive_offload);760 YAML_BOOL_TRUE(def, event, emitter, "tcp-segmentation-offload", def->tcp_segmentation_offload);
761 YAML_BOOL_TRUE(def, event, emitter, "large-receive-offload", def->large_receive_offload);761 if (def->tcp6_segmentation_offload != NETPLAN_TRISTATE_UNSET)
762 YAML_BOOL_TRUE(def, event, emitter, "tcp6-segmentation-offload", def->tcp6_segmentation_offload);
763 if (def->generic_segmentation_offload != NETPLAN_TRISTATE_UNSET)
764 YAML_BOOL_TRUE(def, event, emitter, "generic-segmentation-offload", def->generic_segmentation_offload);
765 if (def->generic_receive_offload != NETPLAN_TRISTATE_UNSET)
766 YAML_BOOL_TRUE(def, event, emitter, "generic-receive-offload", def->generic_receive_offload);
767 if (def->large_receive_offload != NETPLAN_TRISTATE_UNSET)
768 YAML_BOOL_TRUE(def, event, emitter, "large-receive-offload", def->large_receive_offload);
762769
763 if (def->wowlan && def->wowlan != NETPLAN_WIFI_WOWLAN_DEFAULT) {770 if (def->wowlan && def->wowlan != NETPLAN_WIFI_WOWLAN_DEFAULT) {
764 YAML_SCALAR_PLAIN(event, emitter, "wakeonwlan");771 YAML_SCALAR_PLAIN(event, emitter, "wakeonwlan");
diff --git a/src/networkd.c b/src/networkd.c
index 6d26047..62c87ce 100644
--- a/src/networkd.c
+++ b/src/networkd.c
@@ -243,13 +243,13 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char
243 if (!def->set_name &&243 if (!def->set_name &&
244 !def->wake_on_lan &&244 !def->wake_on_lan &&
245 !def->mtubytes &&245 !def->mtubytes &&
246 !def->receive_checksum_offload &&246 (def->receive_checksum_offload == NETPLAN_TRISTATE_UNSET) &&
247 !def->transmit_checksum_offload &&247 (def->transmit_checksum_offload == NETPLAN_TRISTATE_UNSET) &&
248 !def->tcp_segmentation_offload &&248 (def->tcp_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
249 !def->tcp6_segmentation_offload &&249 (def->tcp6_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
250 !def->generic_segmentation_offload &&250 (def->generic_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
251 !def->generic_receive_offload &&251 (def->generic_receive_offload == NETPLAN_TRISTATE_UNSET) &&
252 !def->large_receive_offload)252 (def->large_receive_offload == NETPLAN_TRISTATE_UNSET))
253 return;253 return;
254254
255 /* build file contents */255 /* build file contents */
@@ -265,26 +265,33 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char
265 g_string_append_printf(s, "MTUBytes=%u\n", def->mtubytes);265 g_string_append_printf(s, "MTUBytes=%u\n", def->mtubytes);
266266
267 /* Offload options */267 /* Offload options */
268 if (def->receive_checksum_offload)268 if (def->receive_checksum_offload != NETPLAN_TRISTATE_UNSET)
269 g_string_append_printf(s, "ReceiveChecksumOffload=%u\n", def->receive_checksum_offload);269 g_string_append_printf(s, "ReceiveChecksumOffload=%s\n",
270 (def->receive_checksum_offload ? "true" : "false"));
270271
271 if (def->transmit_checksum_offload)272 if (def->transmit_checksum_offload != NETPLAN_TRISTATE_UNSET)
272 g_string_append_printf(s, "TransmitChecksumOffload=%u\n", def->transmit_checksum_offload);273 g_string_append_printf(s, "TransmitChecksumOffload=%s\n",
274 (def->transmit_checksum_offload ? "true" : "false"));
273275
274 if (def->tcp_segmentation_offload)276 if (def->tcp_segmentation_offload != NETPLAN_TRISTATE_UNSET)
275 g_string_append_printf(s, "TCPSegmentationOffload=%u\n", def->tcp_segmentation_offload);277 g_string_append_printf(s, "TCPSegmentationOffload=%s\n",
278 (def->tcp_segmentation_offload ? "true" : "false"));
276279
277 if (def->tcp6_segmentation_offload)280 if (def->tcp6_segmentation_offload != NETPLAN_TRISTATE_UNSET)
278 g_string_append_printf(s, "TCP6SegmentationOffload=%u\n", def->tcp6_segmentation_offload);281 g_string_append_printf(s, "TCP6SegmentationOffload=%s\n",
282 (def->tcp6_segmentation_offload ? "true" : "false"));
279283
280 if (def->generic_segmentation_offload)284 if (def->generic_segmentation_offload != NETPLAN_TRISTATE_UNSET)
281 g_string_append_printf(s, "GenericSegmentationOffload=%u\n", def->generic_segmentation_offload);285 g_string_append_printf(s, "GenericSegmentationOffload=%s\n",
286 (def->generic_segmentation_offload ? "true" : "false"));
282287
283 if (def->generic_receive_offload)288 if (def->generic_receive_offload != NETPLAN_TRISTATE_UNSET)
284 g_string_append_printf(s, "GenericReceiveOffload=%u\n", def->generic_receive_offload);289 g_string_append_printf(s, "GenericReceiveOffload=%s\n",
290 (def->generic_receive_offload ? "true" : "false"));
285291
286 if (def->large_receive_offload)292 if (def->large_receive_offload != NETPLAN_TRISTATE_UNSET)
287 g_string_append_printf(s, "LargeReceiveOffload=%u\n", def->large_receive_offload);293 g_string_append_printf(s, "LargeReceiveOffload=%s\n",
294 (def->large_receive_offload ? "true" : "false"));
288295
289 orig_umask = umask(022);296 orig_umask = umask(022);
290 g_string_free_to_file(s, rootdir, path, ".link");297 g_string_free_to_file(s, rootdir, path, ".link");
diff --git a/src/parse.c b/src/parse.c
index 350c508..0cb07d2 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -374,6 +374,37 @@ handle_generic_bool(NetplanParser* npp, yaml_node_t* node, void* entryptr, const
374 * Handler for setting a HashTable field from a mapping node, inside a given struct374 * Handler for setting a HashTable field from a mapping node, inside a given struct
375 * @entryptr: pointer to the beginning of the to-be-modified data structure375 * @entryptr: pointer to the beginning of the to-be-modified data structure
376 * @data: offset into entryptr struct where the boolean field to write is located376 * @data: offset into entryptr struct where the boolean field to write is located
377 */
378static gboolean
379handle_generic_tristate(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
380{
381 g_assert(entryptr);
382 NetplanTristate v;
383 guint offset = GPOINTER_TO_UINT(data);
384 NetplanTristate* dest = ((void*) entryptr + offset);
385
386 if (g_ascii_strcasecmp(scalar(node), "true") == 0 ||
387 g_ascii_strcasecmp(scalar(node), "on") == 0 ||
388 g_ascii_strcasecmp(scalar(node), "yes") == 0 ||
389 g_ascii_strcasecmp(scalar(node), "y") == 0)
390 v = NETPLAN_TRISTATE_TRUE;
391 else if (g_ascii_strcasecmp(scalar(node), "false") == 0 ||
392 g_ascii_strcasecmp(scalar(node), "off") == 0 ||
393 g_ascii_strcasecmp(scalar(node), "no") == 0 ||
394 g_ascii_strcasecmp(scalar(node), "n") == 0)
395 v = NETPLAN_TRISTATE_FALSE;
396 else
397 return yaml_error(npp, node, error, "invalid boolean value '%s'", scalar(node));
398
399 *dest = v;
400 mark_data_as_dirty(npp, dest);
401 return TRUE;
402}
403
404/*
405 * Handler for setting a HashTable field from a mapping node, inside a given struct
406 * @entryptr: pointer to the beginning of the to-be-modified data structure
407 * @data: offset into entryptr struct where the boolean field to write is located
377*/408*/
378static gboolean409static gboolean
379handle_generic_map(NetplanParser *npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)410handle_generic_map(NetplanParser *npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
@@ -517,6 +548,16 @@ handle_netdef_bool(NetplanParser* npp, yaml_node_t* node, const void* data, GErr
517}548}
518549
519/**550/**
551 * Generic handler for tri-state settings that can bei "UNSET", "TRUE", or "FALSE".
552 * @data: offset into NetplanNetDefinition where the guint field to write is located
553 */
554static gboolean
555handle_netdef_tristate(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
556{
557 return handle_generic_tristate(npp, node, npp->current.netdef, data, error);
558}
559
560/**
520 * Generic handler for setting a npp->current.netdef guint field from a scalar node561 * Generic handler for setting a npp->current.netdef guint field from a scalar node
521 * @data: offset into NetplanNetDefinition where the guint field to write is located562 * @data: offset into NetplanNetDefinition where the guint field to write is located
522 */563 */
@@ -2356,13 +2397,13 @@ static const mapping_entry_handler dhcp6_overrides_handlers[] = {
2356 {"wakeonlan", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(wake_on_lan)}, \2397 {"wakeonlan", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(wake_on_lan)}, \
2357 {"wakeonwlan", YAML_SEQUENCE_NODE, {.generic=handle_wowlan}, netdef_offset(wowlan)}, \2398 {"wakeonwlan", YAML_SEQUENCE_NODE, {.generic=handle_wowlan}, netdef_offset(wowlan)}, \
2358 {"emit-lldp", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(emit_lldp)}, \2399 {"emit-lldp", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(emit_lldp)}, \
2359 {"receive-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(receive_checksum_offload)}, \2400 {"receive-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(receive_checksum_offload)}, \
2360 {"transmit-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(transmit_checksum_offload)}, \2401 {"transmit-checksum-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(transmit_checksum_offload)}, \
2361 {"tcp-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(tcp_segmentation_offload)}, \2402 {"tcp-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(tcp_segmentation_offload)}, \
2362 {"tcp6-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(tcp6_segmentation_offload)}, \2403 {"tcp6-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(tcp6_segmentation_offload)}, \
2363 {"generic-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(generic_segmentation_offload)}, \2404 {"generic-segmentation-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(generic_segmentation_offload)}, \
2364 {"generic-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(generic_receive_offload)}, \2405 {"generic-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(generic_receive_offload)}, \
2365 {"large-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(large_receive_offload)}2406 {"large-receive-offload", YAML_SCALAR_NODE, {.generic=handle_netdef_tristate}, netdef_offset(large_receive_offload)}
23662407
2367static const mapping_entry_handler ethernet_def_handlers[] = {2408static const mapping_entry_handler ethernet_def_handlers[] = {
2368 COMMON_LINK_HANDLERS,2409 COMMON_LINK_HANDLERS,
diff --git a/src/types.c b/src/types.c
index eb9f780..00c2b0f 100644
--- a/src/types.c
+++ b/src/types.c
@@ -335,6 +335,14 @@ reset_netdef(NetplanNetDefinition* netdef, NetplanDefType new_type, NetplanBacke
335335
336 reset_private_netdef_data(netdef->_private);336 reset_private_netdef_data(netdef->_private);
337 FREE_AND_NULLIFY(netdef->_private);337 FREE_AND_NULLIFY(netdef->_private);
338
339 netdef->receive_checksum_offload = NETPLAN_TRISTATE_UNSET;
340 netdef->transmit_checksum_offload = NETPLAN_TRISTATE_UNSET;
341 netdef->tcp_segmentation_offload = NETPLAN_TRISTATE_UNSET;
342 netdef->tcp6_segmentation_offload = NETPLAN_TRISTATE_UNSET;
343 netdef->generic_segmentation_offload = NETPLAN_TRISTATE_UNSET;
344 netdef->generic_receive_offload = NETPLAN_TRISTATE_UNSET;
345 netdef->large_receive_offload = NETPLAN_TRISTATE_UNSET;
338}346}
339347
340static void348static void
diff --git a/src/types.h b/src/types.h
index 27a23fc..710b1f1 100644
--- a/src/types.h
+++ b/src/types.h
@@ -171,6 +171,27 @@ typedef union {
171 } networkd;171 } networkd;
172} NetplanBackendSettings;172} NetplanBackendSettings;
173173
174typedef enum
175{
176 /**
177 * @brief Tristate enum type
178 *
179 * This type defines a boolean which can be unset, i.e.
180 * this type has three states. The enum is ordered so
181 * that
182 *
183 * UNSET -> -1
184 * FALSE -> 0
185 * TRUE -> 1
186 *
187 * And the integer values can be used directly when
188 * converting to string.
189 */
190 NETPLAN_TRISTATE_UNSET = -1, /* -1 */
191 NETPLAN_TRISTATE_FALSE, /* 0 */
192 NETPLAN_TRISTATE_TRUE, /* 1 */
193} NetplanTristate;
194
174struct netplan_net_definition {195struct netplan_net_definition {
175 NetplanDefType type;196 NetplanDefType type;
176 NetplanBackend backend;197 NetplanBackend backend;
@@ -333,13 +354,13 @@ struct netplan_net_definition {
333 gboolean ignore_carrier;354 gboolean ignore_carrier;
334355
335 /* offload options */356 /* offload options */
336 gboolean receive_checksum_offload;357 NetplanTristate receive_checksum_offload;
337 gboolean transmit_checksum_offload;358 NetplanTristate transmit_checksum_offload;
338 gboolean tcp_segmentation_offload;359 NetplanTristate tcp_segmentation_offload;
339 gboolean tcp6_segmentation_offload;360 NetplanTristate tcp6_segmentation_offload;
340 gboolean generic_segmentation_offload;361 NetplanTristate generic_segmentation_offload;
341 gboolean generic_receive_offload;362 NetplanTristate generic_receive_offload;
342 gboolean large_receive_offload;363 NetplanTristate large_receive_offload;
343364
344 struct private_netdef_data* _private;365 struct private_netdef_data* _private;
345366
diff --git a/tests/generator/test_ethernets.py b/tests/generator/test_ethernets.py
index 46bf764..e81941b 100644
--- a/tests/generator/test_ethernets.py
+++ b/tests/generator/test_ethernets.py
@@ -772,11 +772,11 @@ method=ignore
772 ethernets:772 ethernets:
773 eth1:773 eth1:
774 receive-checksum-offload: true774 receive-checksum-offload: true
775 transmit-checksum-offload: true775 transmit-checksum-offload: off
776 tcp-segmentation-offload: true776 tcp-segmentation-offload: true
777 tcp6-segmentation-offload: true777 tcp6-segmentation-offload: false
778 generic-segmentation-offload: true778 generic-segmentation-offload: true
779 generic-receive-offload: true779 generic-receive-offload: no
780 large-receive-offload: true''')780 large-receive-offload: true''')
781781
782 self.assert_networkd({'eth1.link': '''[Match]782 self.assert_networkd({'eth1.link': '''[Match]
@@ -784,13 +784,13 @@ OriginalName=eth1
784784
785[Link]785[Link]
786WakeOnLan=off786WakeOnLan=off
787ReceiveChecksumOffload=1787ReceiveChecksumOffload=true
788TransmitChecksumOffload=1788TransmitChecksumOffload=false
789TCPSegmentationOffload=1789TCPSegmentationOffload=true
790TCP6SegmentationOffload=1790TCP6SegmentationOffload=false
791GenericSegmentationOffload=1791GenericSegmentationOffload=true
792GenericReceiveOffload=1792GenericReceiveOffload=false
793LargeReceiveOffload=1793LargeReceiveOffload=true
794''',794''',
795 'eth1.network': '''[Match]795 'eth1.network': '''[Match]
796Name=eth1796Name=eth1
@@ -799,3 +799,17 @@ Name=eth1
799LinkLocalAddressing=ipv6799LinkLocalAddressing=ipv6
800'''})800'''})
801 self.assert_networkd_udev(None)801 self.assert_networkd_udev(None)
802
803 def test_offload_invalid(self):
804 err = self.generate('''network:
805 version: 2
806 ethernets:
807 eth1:
808 generic-receive-offload: n
809 receive-checksum-offload: true
810 tcp-segmentation-offload: true
811 tcp6-segmentation-offload: false
812 generic-segmentation-offload: true
813 transmit-checksum-offload: xx
814 large-receive-offload: true''', expect_fail=True)
815 self.assertIn('invalid boolean value \'xx\'', err)
diff --git a/tests/integration/ethernets.py b/tests/integration/ethernets.py
index 865c0d4..06ac069 100644
--- a/tests/integration/ethernets.py
+++ b/tests/integration/ethernets.py
@@ -236,6 +236,65 @@ class _CommonTests():
236 self.assert_iface_up('iface1', ['inet 10.10.10.11'])236 self.assert_iface_up('iface1', ['inet 10.10.10.11'])
237 self.assert_iface_up('iface2', ['inet 10.10.10.22'])237 self.assert_iface_up('iface2', ['inet 10.10.10.22'])
238238
239 def test_link_offloading(self):
240 self.setup_eth(None, False)
241 # check kernel defaults
242 out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
243 self.assertIn(b'rx-checksumming: on', out)
244 self.assertIn(b'tx-checksumming: on', out)
245 self.assertIn(b'tcp-segmentation-offload: on', out)
246 self.assertIn(b'tx-tcp6-segmentation: on', out)
247 self.assertIn(b'generic-segmentation-offload: on', out)
248 self.assertIn(b'generic-receive-offload: off', out) # off by default
249 # validate turning off
250 with open(self.config, 'w') as f:
251 f.write('''network:
252 renderer: %(r)s
253 ethernets:
254 %(ec)s:
255 addresses: [10.10.10.22/24]
256 receive-checksum-offload: off
257 transmit-checksum-offload: off
258 tcp-segmentation-offload: off
259 tcp6-segmentation-offload: off
260 generic-segmentation-offload: off
261 generic-receive-offload: off
262 #large-receive-offload: off # not possible on veth
263''' % {'r': self.backend, 'ec': self.dev_e_client})
264 self.generate_and_settle([self.dev_e_client])
265 self.assert_iface_up(self.dev_e_client, ['inet 10.10.10.22'])
266 out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
267 self.assertIn(b'rx-checksumming: off', out)
268 self.assertIn(b'tx-checksumming: off', out)
269 self.assertIn(b'tcp-segmentation-offload: off', out)
270 self.assertIn(b'tx-tcp6-segmentation: off', out)
271 self.assertIn(b'generic-segmentation-offload: off', out)
272 self.assertIn(b'generic-receive-offload: off', out)
273 # validate turning on
274 with open(self.config, 'w') as f:
275 f.write('''network:
276 renderer: %(r)s
277 ethernets:
278 %(ec)s:
279 addresses: [10.10.10.22/24]
280 receive-checksum-offload: true
281 transmit-checksum-offload: true
282 tcp-segmentation-offload: true
283 tcp6-segmentation-offload: true
284 generic-segmentation-offload: true
285 generic-receive-offload: true
286 #large-receive-offload: true # not possible on veth
287''' % {'r': self.backend, 'ec': self.dev_e_client})
288 self.generate_and_settle([self.dev_e_client])
289 self.assert_iface_up(self.dev_e_client, ['inet 10.10.10.22'])
290 out = subprocess.check_output(['ethtool', '-k', self.dev_e_client])
291 self.assertIn(b'rx-checksumming: on', out)
292 self.assertIn(b'tx-checksumming: on', out)
293 self.assertIn(b'tcp-segmentation-offload: on', out)
294 self.assertIn(b'tx-tcp6-segmentation: on', out)
295 self.assertIn(b'generic-segmentation-offload: on', out)
296 self.assertIn(b'generic-receive-offload: on', out)
297
239298
240@unittest.skipIf("networkd" not in test_backends,299@unittest.skipIf("networkd" not in test_backends,
241 "skipping as networkd backend tests are disabled")300 "skipping as networkd backend tests are disabled")

Subscribers

People subscribed via source and target branches