Merge ~slyon/netplan/+git/ubuntu:slyon/ubuntu/eoan into ~ubuntu-core-dev/netplan/+git/ubuntu:ubuntu/eoan

Proposed by Lukas Märdian
Status: Merged
Approved by: Łukasz Zemczak
Approved revision: 415bb4b36fecabbcfeee4f153ae07864dc6a15b4
Merged at revision: 415bb4b36fecabbcfeee4f153ae07864dc6a15b4
Proposed branch: ~slyon/netplan/+git/ubuntu:slyon/ubuntu/eoan
Merge into: ~ubuntu-core-dev/netplan/+git/ubuntu:ubuntu/eoan
Diff against target: 6685 lines (+3711/-538)
44 files modified
Makefile (+28/-6)
debian/changelog (+80/-0)
debian/control (+35/-0)
debian/copyright (+1/-1)
debian/libnetplan-dev.dirs (+1/-0)
debian/libnetplan-dev.install (+2/-0)
debian/libnetplan0.install (+1/-0)
debian/libnetplan0.symbols (+25/-0)
debian/netplan.io.install (+4/-0)
debian/patches/0001-Fix-LP-1874377-Not-connect-to-WiFi-after-netplan-app.patch (+175/-0)
debian/patches/series (+1/-0)
debian/rules (+4/-1)
debian/tests/control (+27/-0)
dev/null (+0/-11)
doc/netplan.md (+137/-5)
examples/dhcp_wired8021x.yaml (+11/-0)
examples/direct_connect_gateway_ipv6.yaml (+11/-0)
examples/modem.yaml (+15/-0)
netplan/cli/commands/apply.py (+24/-23)
netplan/cli/sriov.py (+320/-0)
netplan/cli/utils.py (+50/-1)
src/generate.c (+10/-9)
src/networkd.c (+240/-80)
src/networkd.h (+1/-1)
src/nm.c (+187/-62)
src/nm.h (+1/-1)
src/parse.c (+326/-156)
src/parse.h (+181/-106)
src/util.c (+62/-0)
src/util.h (+6/-0)
src/validation.c (+26/-25)
src/validation.h (+2/-2)
tests/cli.py (+30/-22)
tests/generator/base.py (+3/-0)
tests/generator/test_auth.py (+67/-6)
tests/generator/test_common.py (+56/-5)
tests/generator/test_errors.py (+58/-0)
tests/generator/test_ethernets.py (+124/-0)
tests/generator/test_modems.py (+374/-0)
tests/generator/test_vlans.py (+93/-0)
tests/generator/test_wifis.py (+283/-14)
tests/integration/base.py (+22/-1)
tests/integration/ethernets.py (+2/-0)
tests/test_sriov.py (+605/-0)
Reviewer Review Type Date Requested Status
Łukasz Zemczak Approve
Review via email: mp+383327@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Łukasz Zemczak (sil2100) :
review: Approve

Update scan failed

At least one of the branches involved have failed to scan. You can manually schedule a rescan if required.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/Makefile b/Makefile
2index d89eb26..3ce1347 100644
3--- a/Makefile
4+++ b/Makefile
5@@ -1,4 +1,8 @@
6+NETPLAN_SOVER=0.0
7+
8 BUILDFLAGS = \
9+ -g \
10+ -fPIC \
11 -std=c99 \
12 -D_XOPEN_SOURCE=500 \
13 -DSBINDIR=\"$(SBINDIR)\" \
14@@ -13,12 +17,14 @@ BASH_COMPLETIONS_DIR=$(shell pkg-config --variable=completionsdir bash-completio
15 GCOV ?= gcov
16 ROOTPREFIX ?=
17 PREFIX ?= /usr
18+LIBDIR ?= $(PREFIX)/lib
19 ROOTLIBEXECDIR ?= $(ROOTPREFIX)/lib
20 LIBEXECDIR ?= $(PREFIX)/lib
21 SBINDIR ?= $(PREFIX)/sbin
22 DATADIR ?= $(PREFIX)/share
23 DOCDIR ?= $(DATADIR)/doc
24 MANDIR ?= $(DATADIR)/man
25+INCLUDEDIR ?= $(PREFIX)/include
26
27 PYCODE = netplan/ $(wildcard src/*.py) $(wildcard tests/*.py) $(wildcard tests/generator/*.py) $(wildcard tests/dbus/*.py)
28
29@@ -29,14 +35,22 @@ NOSETESTS3 ?= $(shell which nosetests-3 || which nosetests3 || echo true)
30
31 default: netplan/_features.py generate netplan-dbus dbus/io.netplan.Netplan.service doc/netplan.html doc/netplan.5 doc/netplan-generate.8 doc/netplan-apply.8 doc/netplan-try.8
32
33-generate: src/generate.[hc] src/parse.[hc] src/util.[hc] src/networkd.[hc] src/nm.[hc] src/validation.[hc] src/error.[hc]
34- $(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(filter %.c, $^) `pkg-config --cflags --libs glib-2.0 gio-2.0 yaml-0.1 uuid`
35+%.o: src/%.c
36+ $(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -c $^ `pkg-config --cflags --libs glib-2.0 gio-2.0 yaml-0.1 uuid`
37+
38+libnetplan.so.$(NETPLAN_SOVER): parse.o util.o validation.o error.o
39+ $(CC) -shared -Wl,-soname,libnetplan.so.$(NETPLAN_SOVER) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $^ `pkg-config --libs yaml-0.1`
40+ ln -snf libnetplan.so.$(NETPLAN_SOVER) libnetplan.so
41+
42+#generate: src/generate.[hc] src/parse.[hc] src/util.[hc] src/networkd.[hc] src/nm.[hc] src/validation.[hc] src/error.[hc]
43+generate: libnetplan.so.$(NETPLAN_SOVER) nm.o networkd.o generate.o
44+ $(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $^ -L. -lnetplan `pkg-config --cflags --libs glib-2.0 gio-2.0 yaml-0.1 uuid`
45
46 netplan-dbus: src/dbus.c src/_features.h
47- $(CC) $(BUILDFLAGS) $(CFLAGS) -o $@ $^ `pkg-config --cflags --libs libsystemd glib-2.0`
48+ $(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $^ `pkg-config --cflags --libs libsystemd glib-2.0`
49
50 src/_features.h: src/[^_]*.[hc]
51- echo "#include <stddef.h>\nstatic const char *feature_flags[] __attribute__((__unused__)) = {" > $@
52+ printf "#include <stddef.h>\nstatic const char *feature_flags[] __attribute__((__unused__)) = {\n" > $@
53 awk 'match ($$0, /netplan-feature:.*/ ) { $$0=substr($$0, RSTART, RLENGTH); print "\""$$2"\"," }' $^ >> $@
54 echo "NULL, };" >> $@
55
56@@ -49,6 +63,7 @@ netplan/_features.py: src/[^_]*.[hc]
57 clean:
58 rm -f netplan/_features.py src/_features.h
59 rm -f generate doc/*.html doc/*.[1-9]
60+ rm -f *.o *.so*
61 rm -f netplan-dbus dbus/*.service
62 rm -f *.gcda *.gcno generate.info
63 rm -rf test-coverage .coverage
64@@ -85,20 +100,27 @@ python-coverage:
65 python3-coverage xml --omit=/usr* || true
66
67 install: default
68- mkdir -p $(DESTDIR)/$(SBINDIR) $(DESTDIR)/$(ROOTLIBEXECDIR)/netplan $(DESTDIR)/$(SYSTEMD_GENERATOR_DIR)
69+ mkdir -p $(DESTDIR)/$(SBINDIR) $(DESTDIR)/$(ROOTLIBEXECDIR)/netplan $(DESTDIR)/$(SYSTEMD_GENERATOR_DIR) $(DESTDIR)/$(LIBDIR)
70 mkdir -p $(DESTDIR)/$(MANDIR)/man5 $(DESTDIR)/$(MANDIR)/man8
71 mkdir -p $(DESTDIR)/$(DOCDIR)/netplan/examples
72 mkdir -p $(DESTDIR)/$(DATADIR)/netplan/netplan
73+ mkdir -p $(DESTDIR)/$(INCLUDEDIR)/netplan
74 install -m 755 generate $(DESTDIR)/$(ROOTLIBEXECDIR)/netplan/
75 find netplan/ -name '*.py' -exec install -Dm 644 "{}" "$(DESTDIR)/$(DATADIR)/netplan/{}" \;
76 install -m 755 src/netplan.script $(DESTDIR)/$(DATADIR)/netplan/
77 ln -srf $(DESTDIR)/$(DATADIR)/netplan/netplan.script $(DESTDIR)/$(SBINDIR)/netplan
78 ln -srf $(DESTDIR)/$(ROOTLIBEXECDIR)/netplan/generate $(DESTDIR)/$(SYSTEMD_GENERATOR_DIR)/netplan
79+ # lib
80+ install -m 644 *.so.* $(DESTDIR)/$(LIBDIR)/
81+ ln -snf libnetplan.so.$(NETPLAN_SOVER) $(DESTDIR)/$(LIBDIR)/libnetplan.so
82+ # headers, dev data
83+ install -m 644 src/*.h $(DESTDIR)/$(INCLUDEDIR)/netplan/
84+ # TODO: install pkg-config once available
85+ # docs, data
86 install -m 644 doc/*.html $(DESTDIR)/$(DOCDIR)/netplan/
87 install -m 644 examples/*.yaml $(DESTDIR)/$(DOCDIR)/netplan/examples/
88 install -m 644 doc/*.5 $(DESTDIR)/$(MANDIR)/man5/
89 install -m 644 doc/*.8 $(DESTDIR)/$(MANDIR)/man8/
90- install -D -m 644 src/netplan-wpa@.service $(DESTDIR)/$(SYSTEMD_UNIT_DIR)/netplan-wpa@.service
91 install -T -D -m 644 netplan.completions $(DESTDIR)/$(BASH_COMPLETIONS_DIR)/netplan
92 # dbus
93 mkdir -p $(DESTDIR)/$(DATADIR)/dbus-1/system.d $(DESTDIR)/$(DATADIR)/dbus-1/system-services
94diff --git a/debian/changelog b/debian/changelog
95index ad35a3b..2e8084d 100644
96--- a/debian/changelog
97+++ b/debian/changelog
98@@ -1,3 +1,83 @@
99+netplan.io (0.99-0ubuntu3~19.10.1) UNRELEASED; urgency=medium
100+
101+ * Backport netplan.io 0.99 to 19.10. (LP: #1871825)
102+ * Include proper upstream fix for "Not connect to WiFi after 'netplan apply'
103+ (LP: #1874377)
104+
105+ -- Lukas Märdian <lukas.maerdian@canonical.com> Mon, 04 May 2020 11:48:04 +0200
106+
107+netplan.io (0.99-0ubuntu3) groovy; urgency=medium
108+
109+ * Drop d/p/0001-Not-connect-to-WiFi-after-netplan-apply.patch
110+ - Replaced by upstream fix
111+ * Add d/p/0001-Fix-LP-1874377-Not-connect-to-WiFi-after-netplan-app.patch:
112+ - Proper upstream fix, which handles edge cases better and contains tests
113+
114+ -- Lukas Märdian <lukas.maerdian@canonical.com> Thu, 30 Apr 2020 12:51:36 +0200
115+
116+netplan.io (0.99-0ubuntu2) focal; urgency=medium
117+
118+ [ Lukas Märdian ]
119+ * debian/patches/0001-Not-connect-to-WiFi-after-netplan-apply.patch:
120+ - Seems like the 'netplan apply' command was not properly adopted when
121+ wired wpa_supplicant support was introduced.
122+
123+ -- Łukasz 'sil2100' Zemczak <lukasz.zemczak@ubuntu.com> Thu, 23 Apr 2020 15:22:07 +0200
124+
125+netplan.io (0.99-0ubuntu1) focal; urgency=medium
126+
127+ [ Łukasz 'sil2100' Zemczak ]
128+ * New upstream release: 0.99 (LP: #1871825)
129+ - Fixed setting MTUBytes= in .network files as well
130+ - Added "phase2" keyword to "auth" section
131+ - Allowing "critical" to be used without "dhcp4"/"dhcp6" enabled
132+ - Added support for GSM modems in the NetworkManager backend (with the
133+ "modems" keyword)
134+ - Added "emit-lldp" option for networkd backend (LP: #1862607)
135+ - Fixed netplan incorrectly generating WPA PSK hex (LP: #1867690)
136+ - Split out the netplan parser into a separate libnetplan library
137+ - Added "ipv6-address-generation" field for NM backend
138+ - Added WiFi flags for "bssid"/"band"/"channel"
139+ - Added support for SR-IOV network devices
140+ * debian/copyright: Change contact address as Matt is no longer available
141+ via the previous e-mail.
142+ * debian/control: Add new libnetplan packages
143+ * Drop d/p/0002-Adopt-integration-tests-for-NetworkManager-v1.22-foc.patch:
144+ included in upstream release
145+
146+ [ Lukas Märdian ]
147+ * Drop d/p/workaround_tests_issues.patch:
148+ The problem was solved upstream and is integrated via
149+ d/p/0002-Adopt-integration-tests-for-NetworkManager-v1.22-foc.patch
150+
151+ -- Łukasz 'sil2100' Zemczak <lukasz.zemczak@ubuntu.com> Thu, 16 Apr 2020 09:13:50 +0200
152+
153+netplan.io (0.98-0ubuntu4) focal; urgency=medium
154+
155+ [ Lukas Märdian ]
156+ * d/p/0002-Adopt-integration-tests-for-NetworkManager-v1.22-foc.patch:
157+ Adopt integration tests for NetworkManager v1.22 (focal)
158+ * debian/tests/control: add new autopkgtest dependencies for the new
159+ integration tests.
160+
161+ -- Łukasz 'sil2100' Zemczak <lukasz.zemczak@ubuntu.com> Mon, 23 Mar 2020 09:24:39 +0100
162+
163+netplan.io (0.98-0ubuntu3) focal; urgency=medium
164+
165+ * No change rebuild to get Testsuite-Triggers restored, the previous
166+ upload was built on an older serie/dpkg version which lead to the
167+ dsc to be missing the reference
168+
169+ -- Sebastien Bacher <seb128@ubuntu.com> Tue, 03 Mar 2020 11:31:26 +0100
170+
171+netplan.io (0.98-0ubuntu2) focal; urgency=medium
172+
173+ * debian/patches/workaround_tests_issues.patch:
174+ - workaround a test issue, the default route seems to take a bit of
175+ time to be applied in n-m, wait for it before erroring out
176+
177+ -- Sebastien Bacher <seb128@ubuntu.com> Wed, 29 Jan 2020 23:33:03 +0100
178+
179 netplan.io (0.98-0ubuntu1) eoan; urgency=medium
180
181 * New upstream release: 0.98 (LP: #1840832)
182diff --git a/debian/control b/debian/control
183index 533e943..d6a69b1 100644
184--- a/debian/control
185+++ b/debian/control
186@@ -34,6 +34,7 @@ Multi-Arch: foreign
187 Depends: ${shlibs:Depends},
188 ${misc:Depends},
189 iproute2,
190+ libnetplan0 (>= ${binary:Version}),
191 python3,
192 python3-yaml,
193 python3-netifaces,
194@@ -51,3 +52,37 @@ Description: YAML network configuration abstraction for various backends
195 networking daemon.
196 .
197 Currently supported backends are networkd and NetworkManager.
198+
199+Package: libnetplan0
200+Architecture: any
201+Multi-Arch: same
202+Depends: ${shlibs:Depends},
203+ ${misc:Depends},
204+Description: YAML network configuration abstraction runtime library
205+ netplan reads YAML network configuration files which are written
206+ by administrators, installers, cloud image instantiations, or other OS
207+ deployments. During early boot it then generates backend specific
208+ configuration files in /run to hand off control of devices to a particular
209+ networking daemon.
210+ .
211+ Currently supported backends are networkd and NetworkManager.
212+ .
213+ This package contains the necessary runtime library files.
214+
215+Package: libnetplan-dev
216+Architecture: any
217+Multi-Arch: same
218+Depends: ${misc:Depends},
219+ libnetplan0 (= ${binary:Version}),
220+Description: Development files for netplan's libnetplan runtime library
221+ netplan reads YAML network configuration files which are written
222+ by administrators, installers, cloud image instantiations, or other OS
223+ deployments. During early boot it then generates backend specific
224+ configuration files in /run to hand off control of devices to a particular
225+ networking daemon.
226+ .
227+ Currently supported backends are networkd and NetworkManager.
228+ .
229+ This package contains development files for developers wanting to use
230+ libnetplan in their applications.
231+
232diff --git a/debian/copyright b/debian/copyright
233index 5af515c..556c410 100644
234--- a/debian/copyright
235+++ b/debian/copyright
236@@ -1,6 +1,6 @@
237 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
238 Upstream-Name: netplan.io
239-Upstream-Contact: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
240+Upstream-Contact: Łukasz 'sil2100' Zemczak <lukasz.zemczak@canonical.com>
241 Source: https://github.com/CanonicalLtd/netplan
242
243 Files: *
244diff --git a/debian/libnetplan-dev.dirs b/debian/libnetplan-dev.dirs
245new file mode 100644
246index 0000000..99379cd
247--- /dev/null
248+++ b/debian/libnetplan-dev.dirs
249@@ -0,0 +1 @@
250+usr/include/netplan
251diff --git a/debian/libnetplan-dev.install b/debian/libnetplan-dev.install
252new file mode 100644
253index 0000000..911f5de
254--- /dev/null
255+++ b/debian/libnetplan-dev.install
256@@ -0,0 +1,2 @@
257+usr/include/netplan/*.h
258+usr/lib/*/libnetplan*.so
259diff --git a/debian/libnetplan0.install b/debian/libnetplan0.install
260new file mode 100644
261index 0000000..514ba93
262--- /dev/null
263+++ b/debian/libnetplan0.install
264@@ -0,0 +1 @@
265+usr/lib/*/libnetplan*.so.*
266diff --git a/debian/libnetplan0.symbols b/debian/libnetplan0.symbols
267new file mode 100644
268index 0000000..62adadd
269--- /dev/null
270+++ b/debian/libnetplan0.symbols
271@@ -0,0 +1,25 @@
272+libnetplan.so.0.0 libnetplan0 #MINVER#
273+ NETPLAN_OPTIONAL_ADDRESS_TYPES@Base 0.99
274+ NETPLAN_WIFI_WOWLAN_TYPES@Base 0.99
275+ current_file@Base 0.99
276+ g_string_free_to_file@Base 0.99
277+ is_ip4_address@Base 0.99
278+ is_ip6_address@Base 0.99
279+ missing_id@Base 0.99
280+ missing_ids_found@Base 0.99
281+ netdefs@Base 0.99
282+ netdefs_ordered@Base 0.99
283+ netplan_finish_parse@Base 0.99
284+ netplan_get_global_backend@Base 0.99
285+ netplan_parse_yaml@Base 0.99
286+ parser_error@Base 0.99
287+ safe_mkdir_p_dir@Base 0.99
288+ tunnel_mode_to_string@Base 0.99
289+ unlink_glob@Base 0.99
290+ validate_backend_rules@Base 0.99
291+ validate_netdef_grammar@Base 0.99
292+ wifi_frequency_24@Base 0.99
293+ wifi_frequency_5@Base 0.99
294+ wifi_get_freq24@Base 0.99
295+ wifi_get_freq5@Base 0.99
296+ yaml_error@Base 0.99
297diff --git a/debian/dirs b/debian/netplan.io.dirs
298similarity index 100%
299rename from debian/dirs
300rename to debian/netplan.io.dirs
301diff --git a/debian/netplan.io.install b/debian/netplan.io.install
302new file mode 100644
303index 0000000..88c78f1
304--- /dev/null
305+++ b/debian/netplan.io.install
306@@ -0,0 +1,4 @@
307+lib/netplan/*
308+lib/systemd/system-generators/netplan
309+usr/share/*
310+usr/sbin/netplan
311diff --git a/debian/patches/0001-Fix-LP-1874377-Not-connect-to-WiFi-after-netplan-app.patch b/debian/patches/0001-Fix-LP-1874377-Not-connect-to-WiFi-after-netplan-app.patch
312new file mode 100644
313index 0000000..d401e97
314--- /dev/null
315+++ b/debian/patches/0001-Fix-LP-1874377-Not-connect-to-WiFi-after-netplan-app.patch
316@@ -0,0 +1,175 @@
317+From: Lukas Maerdian <lukas.maerdian@canonical.com>
318+Date: Tue, 28 Apr 2020 14:35:36 +0200
319+Subject: Fix LP#1874377: Not connect to WiFi after 'netplan apply' (#133)
320+
321+* Fix LP#1874377: Not connect to WiFi after 'netplan apply'
322+
323+Seems like the 'netplan apply' command was not properly adopted in #109 when wired wpa_supplicant support was introduced.
324+
325+Fixes: https://bugs.launchpad.net/ubuntu/+source/netplan.io/+bug/1874377
326+---
327+ netplan/cli/commands/apply.py | 10 ++++--
328+ netplan/cli/utils.py | 7 +++++
329+ src/networkd.c | 4 +++
330+ tests/generator/test_wifis.py | 71 +++++++++++++++++++++++++++++++++++++++++++
331+ tests/integration/base.py | 2 +-
332+ 5 files changed, 91 insertions(+), 3 deletions(-)
333+
334+diff --git a/netplan/cli/commands/apply.py b/netplan/cli/commands/apply.py
335+index 0ec95f5..cf9f122 100644
336+--- a/netplan/cli/commands/apply.py
337++++ b/netplan/cli/commands/apply.py
338+@@ -108,7 +108,13 @@ class NetplanApply(utils.NetplanCommand):
339+ # stop backends
340+ if restart_networkd:
341+ logging.debug('netplan generated networkd configuration changed, restarting networkd')
342+- utils.systemctl_networkd('stop', sync=sync, extra_services=['netplan-wpa@*.service'])
343++ wpa_services = ['netplan-wpa-*.service']
344++ # Historically (up to v0.98) we had netplan-wpa@*.service files, in case of an
345++ # upgraded system, we need to make sure to stop those.
346++ if utils.systemctl_is_active('netplan-wpa@*.service'):
347++ wpa_services.insert(0, 'netplan-wpa@*.service')
348++ utils.systemctl_networkd('stop', sync=sync, extra_services=wpa_services)
349++
350+ else:
351+ logging.debug('no netplan generated networkd configuration exists')
352+
353+@@ -169,7 +175,7 @@ class NetplanApply(utils.NetplanCommand):
354+
355+ # (re)start backends
356+ if restart_networkd:
357+- netplan_wpa = [os.path.basename(f) for f in glob.glob('/run/systemd/system/*.wants/netplan-wpa@*.service')]
358++ netplan_wpa = [os.path.basename(f) for f in glob.glob('/run/systemd/system/*.wants/netplan-wpa-*.service')]
359+ utils.systemctl_networkd('start', sync=sync, extra_services=netplan_wpa)
360+ if restart_nm:
361+ utils.systemctl_network_manager('start', sync=sync)
362+diff --git a/netplan/cli/utils.py b/netplan/cli/utils.py
363+index 5f54b1a..c0eee03 100644
364+--- a/netplan/cli/utils.py
365++++ b/netplan/cli/utils.py
366+@@ -86,6 +86,13 @@ def systemctl_networkd(action, sync=False, extra_services=[]): # pragma: nocove
367+ subprocess.check_call(command)
368+
369+
370++def systemctl_is_active(unit_pattern): # pragma: nocover (covered in autopkgtest)
371++ '''Return True if at least one matching unit is running'''
372++ if subprocess.call(['systemctl', '--quiet', 'is-active', unit_pattern]) == 0:
373++ return True
374++ return False
375++
376++
377+ def get_interface_driver_name(interface, only_down=False): # pragma: nocover (covered in autopkgtest)
378+ devdir = os.path.join('/sys/class/net', interface)
379+ if only_down:
380+diff --git a/src/networkd.c b/src/networkd.c
381+index e2bb111..6f6173a 100644
382+--- a/src/networkd.c
383++++ b/src/networkd.c
384+@@ -990,8 +990,12 @@ cleanup_networkd_conf(const char* rootdir)
385+ {
386+ unlink_glob(rootdir, "/run/systemd/network/10-netplan-*");
387+ unlink_glob(rootdir, "/run/netplan/wpa-*.conf");
388++ unlink_glob(rootdir, "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa-*.service");
389+ unlink_glob(rootdir, "/run/systemd/system/netplan-wpa-*.service");
390+ unlink_glob(rootdir, "/run/udev/rules.d/99-netplan-*");
391++ /* Historically (up to v0.98) we had netplan-wpa@*.service files, in case of an
392++ * upgraded system, we need to make sure to clean those up. */
393++ unlink_glob(rootdir, "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa@*.service");
394+ }
395+
396+ /**
397+diff --git a/tests/generator/test_wifis.py b/tests/generator/test_wifis.py
398+index 8eb804e..d5b79cf 100644
399+--- a/tests/generator/test_wifis.py
400++++ b/tests/generator/test_wifis.py
401+@@ -116,6 +116,77 @@ network={
402+ self.assertTrue(os.path.islink(os.path.join(
403+ self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl0.service')))
404+
405++ def test_wifi_upgrade(self):
406++ # pretend an old 'netplan-wpa@*.service' link still exists on an upgraded system
407++ os.makedirs(os.path.join(self.workdir.name, 'lib/systemd/system'))
408++ os.makedirs(os.path.join(self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants'))
409++ with open(os.path.join(self.workdir.name, 'lib/systemd/system/netplan-wpa@.service'), 'w') as out:
410++ out.write('''[Unit]
411++Description=WPA supplicant for netplan %I
412++DefaultDependencies=no
413++Requires=sys-subsystem-net-devices-%i.device
414++After=sys-subsystem-net-devices-%i.device
415++Before=network.target
416++Wants=network.target
417++
418++[Service]
419++Type=simple
420++ExecStart=/sbin/wpa_supplicant -c /run/netplan/wpa-%I.conf -i%I''')
421++ os.symlink(os.path.join(self.workdir.name, 'lib/systemd/system/netplan-wpa@.service'),
422++ os.path.join(self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa@wl0.service'))
423++
424++ # run generate, which should cleanup the old files/symlinks
425++ self.generate('''network:
426++ version: 2
427++ wifis:
428++ wl0:
429++ access-points:
430++ "Joe's Home":
431++ password: "s0s3kr1t"
432++ dhcp4: yes''')
433++
434++ # verify new files/links exist, while old have been removed
435++ self.assertTrue(os.path.isfile(os.path.join(
436++ self.workdir.name, 'run/systemd/system/netplan-wpa-wl0.service')))
437++ self.assertTrue(os.path.islink(os.path.join(
438++ self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl0.service')))
439++ # old files/links
440++ self.assertTrue(os.path.isfile(os.path.join(
441++ self.workdir.name, 'lib/systemd/system/netplan-wpa@.service')))
442++ self.assertFalse(os.path.islink(os.path.join(
443++ self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa@wl0.service')))
444++
445++ # pretend another old systemd service file exists for wl1
446++ os.symlink(os.path.join(self.workdir.name, 'lib/systemd/system/netplan-wpa@.service'),
447++ os.path.join(self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa@wl1.service'))
448++
449++ # run generate again, to verify the historical netplan-wpa@.service links and wl0 links are gone
450++ self.generate('''network:
451++ version: 2
452++ wifis:
453++ wl1:
454++ access-points:
455++ "Other Home":
456++ password: "s0s3kr1t"
457++ dhcp4: yes''')
458++
459++ # verify new files/links exist, while old have been removed
460++ self.assertTrue(os.path.isfile(os.path.join(
461++ self.workdir.name, 'run/systemd/system/netplan-wpa-wl1.service')))
462++ self.assertTrue(os.path.islink(os.path.join(
463++ self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl1.service')))
464++ # old files/links
465++ self.assertTrue(os.path.isfile(os.path.join(
466++ self.workdir.name, 'lib/systemd/system/netplan-wpa@.service')))
467++ self.assertFalse(os.path.islink(os.path.join(
468++ self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa@wl1.service')))
469++ self.assertFalse(os.path.islink(os.path.join(
470++ self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa@wl0.service')))
471++ self.assertFalse(os.path.isfile(os.path.join(
472++ self.workdir.name, 'run/systemd/system/netplan-wpa-wl0.service')))
473++ self.assertFalse(os.path.islink(os.path.join(
474++ self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl0.service')))
475++
476+ def test_wifi_route(self):
477+ self.generate('''network:
478+ version: 2
479+diff --git a/tests/integration/base.py b/tests/integration/base.py
480+index ed7100e..16fd2ee 100644
481+--- a/tests/integration/base.py
482++++ b/tests/integration/base.py
483+@@ -93,7 +93,7 @@ class IntegrationTestsBase(unittest.TestCase):
484+ pass
485+
486+ def tearDown(self):
487+- subprocess.call(['systemctl', 'stop', 'NetworkManager', 'systemd-networkd', 'netplan-wpa@*',
488++ subprocess.call(['systemctl', 'stop', 'NetworkManager', 'systemd-networkd', 'netplan-wpa-*',
489+ 'systemd-networkd.socket'])
490+ # NM has KillMode=process and leaks dhclient processes
491+ subprocess.call(['systemctl', 'kill', 'NetworkManager'])
492diff --git a/debian/patches/series b/debian/patches/series
493index e69de29..817f04c 100644
494--- a/debian/patches/series
495+++ b/debian/patches/series
496@@ -0,0 +1 @@
497+0001-Fix-LP-1874377-Not-connect-to-WiFi-after-netplan-app.patch
498diff --git a/debian/rules b/debian/rules
499index 2d33f6a..f64d5f7 100755
500--- a/debian/rules
501+++ b/debian/rules
502@@ -1,4 +1,7 @@
503 #!/usr/bin/make -f
504
505 %:
506- dh $@
507+ dh $@ --fail-missing
508+
509+override_dh_auto_install:
510+ dh_auto_install -- LIBDIR=/usr/lib/${DEB_HOST_MULTIARCH}/
511diff --git a/debian/tests/control b/debian/tests/control
512index 6202b41..727e3da 100644
513--- a/debian/tests/control
514+++ b/debian/tests/control
515@@ -5,6 +5,9 @@ Depends: @,
516 network-manager,
517 hostapd,
518 dnsmasq-base,
519+ libnm0,
520+ python3-gi,
521+ gir1.2-nm-1.0,
522 Restrictions: allow-stderr, needs-root, isolation-machine
523 Features: test-name=ethernets
524
525@@ -15,6 +18,9 @@ Depends: @,
526 network-manager,
527 hostapd,
528 dnsmasq-base,
529+ libnm0,
530+ python3-gi,
531+ gir1.2-nm-1.0,
532 Restrictions: allow-stderr, needs-root, isolation-machine
533 Features: test-name=bridges
534
535@@ -25,6 +31,9 @@ Depends: @,
536 network-manager,
537 hostapd,
538 dnsmasq-base,
539+ libnm0,
540+ python3-gi,
541+ gir1.2-nm-1.0,
542 Restrictions: allow-stderr, needs-root, isolation-machine
543 Features: test-name=bonds
544
545@@ -35,6 +44,9 @@ Depends: @,
546 network-manager,
547 hostapd,
548 dnsmasq-base,
549+ libnm0,
550+ python3-gi,
551+ gir1.2-nm-1.0,
552 Restrictions: allow-stderr, needs-root, isolation-machine
553 Features: test-name=routing
554
555@@ -45,6 +57,9 @@ Depends: @,
556 network-manager,
557 hostapd,
558 dnsmasq-base,
559+ libnm0,
560+ python3-gi,
561+ gir1.2-nm-1.0,
562 Restrictions: allow-stderr, needs-root, isolation-machine
563 Features: test-name=vlans
564
565@@ -55,6 +70,9 @@ Depends: @,
566 network-manager,
567 hostapd,
568 dnsmasq-base,
569+ libnm0,
570+ python3-gi,
571+ gir1.2-nm-1.0,
572 Restrictions: allow-stderr, needs-root, isolation-machine, flaky
573 Features: test-name=wifi
574
575@@ -65,6 +83,9 @@ Depends: @,
576 network-manager,
577 hostapd,
578 dnsmasq-base,
579+ libnm0,
580+ python3-gi,
581+ gir1.2-nm-1.0,
582 Restrictions: allow-stderr, needs-root, isolation-machine
583 Features: test-name=tunnels
584
585@@ -75,6 +96,9 @@ Depends: @,
586 network-manager,
587 hostapd,
588 dnsmasq-base,
589+ libnm0,
590+ python3-gi,
591+ gir1.2-nm-1.0,
592 Restrictions: allow-stderr, needs-root, isolation-machine
593 Features: test-name=scenarios
594
595@@ -85,6 +109,9 @@ Depends: @,
596 network-manager,
597 hostapd,
598 dnsmasq-base,
599+ libnm0,
600+ python3-gi,
601+ gir1.2-nm-1.0,
602 Restrictions: allow-stderr, needs-root, isolation-machine
603 Features: test-name=regressions
604
605diff --git a/doc/netplan.md b/doc/netplan.md
606index 1cf8ded..c4b0ba3 100644
607--- a/doc/netplan.md
608+++ b/doc/netplan.md
609@@ -30,7 +30,7 @@ either of those directories shadows a file with the same name in
610 The top-level node in a netplan configuration file is a ``network:`` mapping
611 that contains ``version: 2`` (the YAML currently being used by curtin, MaaS,
612 etc. is version 1), and then device definitions grouped by their type, such as
613-``ethernets:``, ``wifis:``, or ``bridges:``. These are the types that our
614+``ethernets:``, ``modems:``, ``wifis:``, or ``bridges:``. These are the types that our
615 renderer can understand and are supported by our backends.
616
617 Each type block contains device definitions as a map where the keys (called
618@@ -52,7 +52,7 @@ and the ID field has a different interpretation for each:
619
620 Physical devices
621
622-: (Examples: ethernet, wifi) These can dynamically come and go between
623+: (Examples: ethernet, modem, wifi) These can dynamically come and go between
624 reboots and even during runtime (hotplugging). In the generic case, they
625 can be selected by ``match:`` rules on desired properties, such as name/name
626 pattern, MAC address, driver, or device paths. In general these will match
627@@ -133,6 +133,10 @@ Virtual devices
628
629 : Enable wake on LAN. Off by default.
630
631+``emit-lldp`` (bool)
632+
633+: (networkd backend only) Whether to emit LLDP packets. Off by default.
634+
635
636 ## Common properties for all device types
637
638@@ -143,6 +147,11 @@ Virtual devices
639 in ``networks:``, for a device type (in e. g. ``ethernets:``) or
640 for a particular device definition. Default is ``networkd``.
641
642+ The ``renderer`` property has one additional acceptable value for vlan objects
643+ (i. e. defined in ``vlans:``): ``sriov``. If a vlan is defined with the ``sriov``
644+ renderer for an SR-IOV Virtual Function interface, this causes netplan to set
645+ up a hardware VLAN filter for it. There can be only one defined per VF.
646+
647 ``dhcp4`` (bool)
648
649 : Enable DHCP for IPv4. Off by default.
650@@ -198,7 +207,7 @@ Virtual devices
651
652 : (networkd backend only) Designate the connection as "critical to the
653 system", meaning that special care will be taken by systemd-networkd to
654- not release the IP from DHCP when the daemon is restarted.
655+ not release the assigned IP when the daemon is restarted.
656
657 ``dhcp-identifier`` (scalar)
658
659@@ -235,6 +244,12 @@ Virtual devices
660
661 Example: ``addresses: [192.168.14.2/24, "2001:1::1/64"]``
662
663+``ipv6-address-generation`` (scalar)
664+
665+: Configure method for creating the address for use with RFC4862 IPv6
666+ Stateless Address Autoconfiguration. Possible values are ``eui64``
667+ or ``stable-privacy``.
668+
669 ``gateway4``, ``gateway6`` (scalar)
670
671 : Set default gateway for IPv4/6, for manual address configuration. This
672@@ -540,10 +555,77 @@ interfaces, as well as individual wifi networks, by means of the ``auth`` block.
673 : Password to use to decrypt the private key specified in
674 ``client-key`` if it is encrypted.
675
676+ ``phase2-auth`` (scalar)
677+ : Phase 2 authentication mechanism.
678+
679
680 ## Properties for device type ``ethernets:``
681-Ethernet device definitions do not support any specific properties beyond the
682-common ones described above.
683+Ethernet device definitions, beyond common ones described above, also support
684+some additional properties that can be used for SR-IOV devices.
685+
686+``link`` (scalar)
687+
688+: (SR-IOV devices only) The ``link`` property declares the device as a
689+ Virtual Function of the selected Physical Function device, as identified
690+ by the given netplan id.
691+
692+Example:
693+
694+ ethernets:
695+ enp1: {...}
696+ enp1s16f1:
697+ link: enp1
698+
699+``virtual-function-count`` (scalar)
700+
701+: (SR-IOV devices only) In certain special cases VFs might need to be
702+ configured outside of netplan. For such configurations ``virtual-function-count``
703+ can be optionally used to set an explicit number of Virtual Functions for
704+ the given Physical Function. If unset, the default is to create only as many
705+ VFs as are defined in the netplan configuration. This should be used for special
706+ cases only.
707+
708+## Properties for device type ``modems:``
709+GSM/CDMA modem configuration is only supported for the ``NetworkManager`` backend. ``systemd-networkd`` does
710+not support modems.
711+
712+``apn`` (scalar)
713+: Set the carrier APN (Access Point Name). This can be omitted if ``auto-config`` is enabled.
714+
715+``auto-config`` (bool)
716+: Specify whether to try and autoconfigure the modem by doing a lookup of the carrier
717+ against the Mobile Broadband Provider database. This may not work for all carriers.
718+
719+``device-id`` (scalar)
720+: Specify the device ID (as given by the WWAN management service) of the modem to match.
721+ This can be found using ``mmcli``.
722+
723+``network-id`` (scalar)
724+: Specify the Network ID (GSM LAI format). If this is specified, the device will not roam networks.
725+
726+``number`` (scalar)
727+: The number to dial to establish the connection to the mobile broadband network. (Deprecated for GSM)
728+
729+``password`` (scalar)
730+: Specify the password used to authenticate with the carrier network. This can be omitted
731+ if ``auto-config`` is enabled.
732+
733+``pin`` (scalar)
734+: Specify the SIM PIN to allow it to operate if a PIN is set.
735+
736+``sim-id`` (scalar)
737+: Specify the SIM unique identifier (as given by the WWAN management service) which this
738+ connection applies to. If given, the connection will apply to any device also allowed by
739+ ``device-id`` which contains a SIM card matching the given identifier.
740+
741+``sim-operator-id`` (scalar)
742+: Specify the MCC/MNC string (such as "310260" or "21601") which identifies the carrier that
743+ this connection should apply to. If given, the connection will apply to any device also
744+ allowed by ``device-id`` and ``sim-id`` which contains a SIM card provisioned by the given operator.
745+
746+``username`` (scalar)
747+: Specify the username used to authentiate with the carrier network. This can be omitted if
748+ ``auto-config`` is enabled.
749
750 ## Properties for device type ``wifis:``
751 Note that ``systemd-networkd`` does not natively support wifi, so you need
752@@ -575,6 +657,28 @@ wpasupplicant installed if you let the ``networkd`` renderer handle wifi.
753 and ``adhoc`` (peer to peer networks without a central access point).
754 ``ap`` is only supported with NetworkManager.
755
756+ ``bssid`` (scalar)
757+ : If specified, directs the device to only associate with the given
758+ access point.
759+
760+ ``band`` (scalar)
761+ : Possible bands are ``5GHz`` (for 5GHz 802.11a) and ``2.4GHz``
762+ (for 2.4GHz 802.11), do not restrict the 802.11 frequency band of the
763+ network if unset (the default).
764+
765+ ``channel`` (scalar)
766+ : Wireless channel to use for the Wi-Fi connection. Because channel
767+ numbers overlap between bands, this property takes effect only if
768+ the ``band`` property is also set.
769+
770+``wakeonwlan`` (sequence of scalars)
771+
772+: This enables WakeOnWLan on supported devices. Not all drivers support all
773+ options. May be any combination of ``any``, ``disconnect``, ``magic_pkt``,
774+ ``gtk_rekey_failure``, ``eap_identity_req``, ``four_way_handshake``,
775+ ``rfkill_release`` or ``tcp`` (NetworkManager only). Or the exclusive
776+ ``default`` flag (the default).
777+
778 ## Properties for device type ``bridges:``
779
780 ``interfaces`` (sequence of scalars)
781@@ -898,6 +1002,34 @@ Example:
782 addresses: ...
783
784
785+## Backend-specific configuration parameters
786+
787+In addition to the other fields available to configure interfaces, some
788+backends may require to record some of their own parameters in netplan,
789+especially if the netplan definitions are generated automatically by the
790+consumer of that backend. Currently, this is only used with ``NetworkManager``.
791+
792+``networkmanager`` (mapping)
793+
794+: Keeps the NetworkManager-specific configuration parameters used by the
795+ daemon to recognize connections.
796+
797+ ``name`` (scalar)
798+ : Set the display name for the connection.
799+
800+ ``uuid`` (scalar)
801+ : Defines the UUID (unique identifier) for this connection, as
802+ generated by NetworkManager itself.
803+
804+ ``stable-id`` (scalar)
805+ : Defines the stable ID (a different form of a connection name) used
806+ by NetworkManager in case the name of the connection might otherwise
807+ change, such as when sharing connections between users.
808+
809+ ``device`` (scalar)
810+ : Defines the interface name for which this connection applies.
811+
812+
813 ## Examples
814 Configure an ethernet device with networkd, identified by its name, and enable
815 DHCP:
816diff --git a/examples/dhcp_wired8021x.yaml b/examples/dhcp_wired8021x.yaml
817new file mode 100644
818index 0000000..9f401dd
819--- /dev/null
820+++ b/examples/dhcp_wired8021x.yaml
821@@ -0,0 +1,11 @@
822+network:
823+ version: 2
824+ renderer: networkd
825+ ethernets:
826+ enp3s0:
827+ dhcp4: true
828+ auth:
829+ key-management: 802.1x
830+ method: ttls
831+ identity: fluffy@cisco.com
832+ password: hash:83...11
833diff --git a/examples/direct_connect_gateway_ipv6.yaml b/examples/direct_connect_gateway_ipv6.yaml
834new file mode 100644
835index 0000000..3f821d3
836--- /dev/null
837+++ b/examples/direct_connect_gateway_ipv6.yaml
838@@ -0,0 +1,11 @@
839+network:
840+ version: 2
841+ renderer: networkd
842+ ethernets:
843+ addresses: [ "2001:cafe:face:beef::dead:dead/64" ]
844+ routes:
845+ - to: "2001:cafe:face::1/128"
846+ scope: link
847+ - to: "::/0"
848+ via: "2001:cafe:face::1"
849+ on-link: true
850diff --git a/examples/modem.yaml b/examples/modem.yaml
851new file mode 100644
852index 0000000..043d74a
853--- /dev/null
854+++ b/examples/modem.yaml
855@@ -0,0 +1,15 @@
856+network:
857+ version: 2
858+ renderer: NetworkManager
859+ modems:
860+ cdc-wdm1:
861+ mtu: 1600
862+ apn: ISP.CINGULAR
863+ username: ISP@CINGULARGPRS.COM
864+ password: CINGULAR1
865+ number: "*99#"
866+ network-id: 24005
867+ device-id: da812de91eec16620b06cd0ca5cbc7ea25245222
868+ pin: 2345
869+ sim-id: 89148000000060671234
870+ sim-operator-id: 310260
871diff --git a/netplan/cli/commands/apply.py b/netplan/cli/commands/apply.py
872index 3e7019d..0ec95f5 100644
873--- a/netplan/cli/commands/apply.py
874+++ b/netplan/cli/commands/apply.py
875@@ -1,7 +1,8 @@
876 #!/usr/bin/python3
877 #
878-# Copyright (C) 2018 Canonical, Ltd.
879+# Copyright (C) 2018-2020 Canonical, Ltd.
880 # Author: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
881+# Author: Łukasz 'sil2100' Zemczak <lukasz.zemczak@canonical.com>
882 #
883 # This program is free software; you can redistribute it and/or modify
884 # it under the terms of the GNU General Public License as published by
885@@ -26,6 +27,7 @@ import shutil
886
887 import netplan.cli.utils as utils
888 from netplan.configmanager import ConfigManager, ConfigurationError
889+from netplan.cli.sriov import apply_sriov_config
890
891 import netifaces
892
893@@ -74,7 +76,14 @@ class NetplanApply(utils.NetplanCommand):
894 old_files_networkd = bool(glob.glob('/run/systemd/network/*netplan-*'))
895 old_files_nm = bool(glob.glob('/run/NetworkManager/system-connections/netplan-*'))
896
897- if run_generate and subprocess.call([utils.get_generator_path()]) != 0:
898+ generator_call = []
899+ generate_out = None
900+ if 'NETPLAN_PROFILE' in os.environ:
901+ generator_call.extend(['valgrind', '--leak-check=full'])
902+ generate_out = subprocess.STDOUT
903+
904+ generator_call.append(utils.get_generator_path())
905+ if run_generate and subprocess.call(generator_call, stderr=generate_out) != 0:
906 if exit_on_error:
907 sys.exit(os.EX_CONFIG)
908 else:
909@@ -118,11 +127,22 @@ class NetplanApply(utils.NetplanCommand):
910 else:
911 logging.debug('no netplan generated NM configuration exists')
912
913+ # Refresh devices now; restarting a backend might have made something appear.
914+ devices = netifaces.interfaces()
915+
916 # evaluate config for extra steps we need to take (like renaming)
917 # for now, only applies to non-virtual (real) devices.
918 config_manager.parse()
919 changes = NetplanApply.process_link_changes(devices, config_manager)
920
921+ # apply any SR-IOV related changes, if applicable
922+ try:
923+ apply_sriov_config(devices, config_manager)
924+ except (ConfigurationError, RuntimeError) as e:
925+ logging.error(str(e))
926+ if exit_on_error:
927+ sys.exit(1)
928+
929 # if the interface is up, we can still apply some .link file changes
930 devices = netifaces.interfaces()
931 for device in devices:
932@@ -216,28 +236,9 @@ class NetplanApply(utils.NetplanCommand):
933 # do not rename members of virtual devices. MAC addresses
934 # may be the same for all interface members.
935 continue
936- # try to get the device's driver for matching.
937- devdir = os.path.join('/sys/class/net', interface)
938- try:
939- with open(os.path.join(devdir, 'operstate')) as f:
940- state = f.read().strip()
941- if state != 'down':
942- logging.debug('device %s operstate is %s, not changing', interface, state)
943- continue
944- except IOError as e:
945- logging.error('Cannot determine operstate of %s: %s', interface, str(e))
946- continue
947
948- try:
949- driver = os.path.realpath(os.path.join(devdir, 'device', 'driver'))
950- driver_name = os.path.basename(driver)
951- except IOError as e:
952- logging.debug('Cannot replug %s: cannot read link %s/device: %s', interface, devdir, str(e))
953- driver_name = None
954- pass
955-
956- link = netifaces.ifaddresses(interface)[netifaces.AF_LINK][0]
957- macaddress = link.get('addr')
958+ driver_name = utils.get_interface_driver_name(interface, only_down=True)
959+ macaddress = utils.get_interface_macaddress(interface)
960 if driver_name in matches['by-driver']:
961 new_name = matches['by-driver'][driver_name]
962 logging.debug(new_name)
963diff --git a/netplan/cli/sriov.py b/netplan/cli/sriov.py
964new file mode 100644
965index 0000000..8feacf1
966--- /dev/null
967+++ b/netplan/cli/sriov.py
968@@ -0,0 +1,320 @@
969+#!/usr/bin/python3
970+#
971+# Copyright (C) 2020 Canonical, Ltd.
972+# Author: Łukasz 'sil2100' Zemczak <lukasz.zemczak@canonical.com>
973+#
974+# This program is free software; you can redistribute it and/or modify
975+# it under the terms of the GNU General Public License as published by
976+# the Free Software Foundation; version 3.
977+#
978+# This program is distributed in the hope that it will be useful,
979+# but WITHOUT ANY WARRANTY; without even the implied warranty of
980+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
981+# GNU General Public License for more details.
982+#
983+# You should have received a copy of the GNU General Public License
984+# along with this program. If not, see <http://www.gnu.org/licenses/>.
985+
986+import logging
987+import os
988+import subprocess
989+
990+from collections import defaultdict
991+
992+import netplan.cli.utils as utils
993+from netplan.configmanager import ConfigurationError
994+
995+import netifaces
996+
997+
998+def _get_target_interface(interfaces, config_manager, pf_link, pfs):
999+ if pf_link not in pfs:
1000+ # handle the match: syntax, get the actual device name
1001+ pf_match = config_manager.ethernets[pf_link].get('match')
1002+ if pf_match:
1003+ by_name = pf_match.get('name')
1004+ by_mac = pf_match.get('macaddress')
1005+ by_driver = pf_match.get('driver')
1006+
1007+ for interface in interfaces:
1008+ if ((by_name and not utils.is_interface_matching_name(interface, by_name)) or
1009+ (by_mac and not utils.is_interface_matching_macaddress(interface, by_mac)) or
1010+ (by_driver and not utils.is_interface_matching_driver_name(interface, by_driver))):
1011+ continue
1012+ # we have a matching PF
1013+ # store the matching interface in the dictionary of
1014+ # active PFs, but error out if we matched more than one
1015+ if pf_link in pfs:
1016+ raise ConfigurationError('matched more than one interface for a PF device: %s' % pf_link)
1017+ pfs[pf_link] = interface
1018+ else:
1019+ # no match field, assume entry name is interface name
1020+ if pf_link in interfaces:
1021+ pfs[pf_link] = pf_link
1022+
1023+ return pfs.get(pf_link, None)
1024+
1025+
1026+def get_vf_count_and_functions(interfaces, config_manager,
1027+ vf_counts, vfs, pfs):
1028+ """
1029+ Go through the list of netplan ethernet devices and identify which are
1030+ PFs and VFs, matching the former with actual networking interfaces.
1031+ Count how many VFs each PF will need.
1032+ """
1033+ explicit_counts = {}
1034+ for ethernet, settings in config_manager.ethernets.items():
1035+ if not settings:
1036+ continue
1037+ if ethernet == 'renderer':
1038+ continue
1039+
1040+ # we now also support explicitly stating how many VFs should be
1041+ # allocated for a PF
1042+ explicit_num = settings.get('virtual-function-count')
1043+ if explicit_num:
1044+ pf = _get_target_interface(interfaces, config_manager, ethernet, pfs)
1045+ if pf:
1046+ explicit_counts[pf] = explicit_num
1047+ continue
1048+
1049+ pf_link = settings.get('link')
1050+ if pf_link and pf_link in config_manager.ethernets:
1051+ _get_target_interface(interfaces, config_manager, pf_link, pfs)
1052+
1053+ if pf_link in pfs:
1054+ vf_counts[pfs[pf_link]] += 1
1055+ else:
1056+ logging.warning('could not match physical interface for the defined PF: %s' % pf_link)
1057+ # continue looking for other VFs
1058+ continue
1059+
1060+ # we can't yet perform matching on VFs as those are only
1061+ # created later - but store, for convenience, all the valid
1062+ # VFs that we encounter so far
1063+ vfs[ethernet] = None
1064+
1065+ # sanity check: since we can explicitly state the VF count, make sure
1066+ # that this number isn't smaller than the actual number of VFs declared
1067+ # the explicit number also overrides the number of actual VFs
1068+ for pf, count in explicit_counts.items():
1069+ if pf in vf_counts and vf_counts[pf] > count:
1070+ raise ConfigurationError(
1071+ 'more VFs allocated than the explicit size declared: %s > %s' % (vf_counts[pf], count))
1072+ vf_counts[pf] = count
1073+
1074+
1075+def set_numvfs_for_pf(pf, vf_count):
1076+ """
1077+ Allocate the required number of VFs for the selected PF.
1078+ """
1079+ if vf_count > 256:
1080+ raise ConfigurationError(
1081+ 'cannot allocate more VFs for PF %s than the SR-IOV maximum: %s > 256' % (pf, vf_count))
1082+
1083+ devdir = os.path.join('/sys/class/net', pf, 'device')
1084+ numvfs_path = os.path.join(devdir, 'sriov_numvfs')
1085+ totalvfs_path = os.path.join(devdir, 'sriov_totalvfs')
1086+ try:
1087+ with open(totalvfs_path) as f:
1088+ vf_max = int(f.read().strip())
1089+ except IOError as e:
1090+ raise RuntimeError('failed parsing sriov_totalvfs for %s: %s' % (pf, str(e)))
1091+ except ValueError:
1092+ raise RuntimeError('invalid sriov_totalvfs value for %s' % pf)
1093+
1094+ if vf_count > vf_max:
1095+ raise ConfigurationError(
1096+ 'cannot allocate more VFs for PF %s than supported: %s > %s (sriov_totalvfs)' % (pf, vf_count, vf_max))
1097+
1098+ try:
1099+ with open(numvfs_path, 'w') as f:
1100+ f.write(str(vf_count))
1101+ except IOError as e:
1102+ bail = True
1103+ if e.errno == 16: # device or resource busy
1104+ logging.warning('device or resource busy while setting sriov_numvfs for %s, trying workaround' % pf)
1105+ try:
1106+ # doing this in two open/close sequences so that
1107+ # it's as close to writing via shell as possible
1108+ with open(numvfs_path, 'w') as f:
1109+ f.write('0')
1110+ with open(numvfs_path, 'w') as f:
1111+ f.write(str(vf_count))
1112+ except IOError as e_inner:
1113+ e = e_inner
1114+ else:
1115+ bail = False
1116+ if bail:
1117+ raise RuntimeError('failed setting sriov_numvfs to %s for %s: %s' % (vf_count, pf, str(e)))
1118+
1119+ return True
1120+
1121+
1122+def perform_hardware_specific_quirks(pf):
1123+ """
1124+ Perform any hardware-specific quirks for the given SR-IOV device to make
1125+ sure all the VF-count changes are applied.
1126+ """
1127+ devdir = os.path.join('/sys/class/net', pf, 'device')
1128+ try:
1129+ with open(os.path.join(devdir, 'vendor')) as f:
1130+ device_id = f.read().strip()[2:]
1131+ with open(os.path.join(devdir, 'device')) as f:
1132+ vendor_id = f.read().strip()[2:]
1133+ except IOError as e:
1134+ raise RuntimeError('could not determine vendor and device ID of %s: %s' % (pf, str(e)))
1135+
1136+ combined_id = ':'.join([vendor_id, device_id])
1137+ quirk_devices = () # TODO: add entries to the list
1138+ if combined_id in quirk_devices:
1139+ # some devices need special handling, so this is the place
1140+
1141+ # Currently this part is empty, but has been added as a preemptive
1142+ # measure, as apparently a lot of SR-IOV cards have issues with
1143+ # dynamically allocating VFs. Some cards seem to require a full
1144+ # kernel module reload cycle after changing the sriov_numvfs value
1145+ # for the changes to come into effect.
1146+ # Any identified card/vendor can then be special-cased here, if
1147+ # needed.
1148+ pass
1149+
1150+
1151+def apply_vlan_filter_for_vf(pf, vf, vlan_name, vlan_id, prefix='/'):
1152+ """
1153+ Apply the hardware VLAN filtering for the selected VF.
1154+ """
1155+
1156+ # this is more complicated, because to do this, we actually need to have
1157+ # the vf index - just knowing the vf interface name is not enough
1158+ vf_index = None
1159+ # the prefix argument is here only for unit testing purposes
1160+ vf_devdir = os.path.join(prefix, 'sys/class/net', vf, 'device')
1161+ vf_dev_id = os.path.basename(os.readlink(vf_devdir))
1162+ pf_devdir = os.path.join(prefix, 'sys/class/net', pf, 'device')
1163+ for f in os.listdir(pf_devdir):
1164+ if 'virtfn' in f:
1165+ dev_path = os.path.join(pf_devdir, f)
1166+ dev_id = os.path.basename(os.readlink(dev_path))
1167+ if dev_id == vf_dev_id:
1168+ vf_index = f[6:]
1169+ break
1170+
1171+ if not vf_index:
1172+ raise RuntimeError(
1173+ 'could not determine the VF index for %s while configuring vlan %s' % (vf, vlan_name))
1174+
1175+ # now, create the VLAN filter
1176+ # TODO: would be best if we did this directl via python, without calling
1177+ # the iproute tooling
1178+ try:
1179+ subprocess.check_call(['ip', 'link', 'set',
1180+ 'dev', pf,
1181+ 'vf', vf_index,
1182+ 'vlan', str(vlan_id)],
1183+ stdout=subprocess.DEVNULL,
1184+ stderr=subprocess.DEVNULL)
1185+ except subprocess.CalledProcessError:
1186+ raise RuntimeError(
1187+ 'failed setting SR-IOV VLAN filter for vlan %s (ip link set command failed)' % vlan_name)
1188+
1189+
1190+def apply_sriov_config(interfaces, config_manager):
1191+ """
1192+ Go through all interfaces, identify which ones are SR-IOV VFs, create
1193+ them and perform all other necessary setup.
1194+ """
1195+
1196+ # for sr-iov devices, we identify VFs by them having a link: field
1197+ # pointing to an PF. So let's browse through all ethernet devices,
1198+ # find all that are VFs and count how many of those are linked to
1199+ # particular PFs, as we need to then set the numvfs for each.
1200+ vf_counts = defaultdict(int)
1201+ # we also store all matches between VF/PF netplan entry names and
1202+ # interface that they're currently matching to
1203+ vfs = {}
1204+ pfs = {}
1205+
1206+ get_vf_count_and_functions(
1207+ interfaces, config_manager, vf_counts, vfs, pfs)
1208+
1209+ # setup the required number of VFs per PF
1210+ # at the same time store which PFs got changed in case the NICs
1211+ # require some special quirks for the VF number to change
1212+ vf_count_changed = []
1213+ if vf_counts:
1214+ for pf, vf_count in vf_counts.items():
1215+ if not set_numvfs_for_pf(pf, vf_count):
1216+ continue
1217+
1218+ vf_count_changed.append(pf)
1219+
1220+ if vf_count_changed:
1221+ # some cards need special treatment when we want to change the
1222+ # number of enabled VFs
1223+ for pf in vf_count_changed:
1224+ perform_hardware_specific_quirks(pf)
1225+
1226+ # also, since the VF number changed, the interfaces list also
1227+ # changed, so we need to refresh it
1228+ interfaces = netifaces.interfaces()
1229+
1230+ # now in theory we should have all the new VFs set up and existing;
1231+ # this is needed because we will have to now match the defined VF
1232+ # entries to existing interfaces, otherwise we won't be able to set
1233+ # filtered VLANs for those.
1234+ # XXX: does matching those even make sense?
1235+ for vf in vfs:
1236+ settings = config_manager.ethernets.get(vf)
1237+ match = settings.get('match')
1238+ if match:
1239+ # right now we only match by name, as I don't think matching per
1240+ # driver and/or macaddress makes sense
1241+ by_name = match.get('name')
1242+ # by_mac = match.get('macaddress')
1243+ # by_driver = match.get('driver')
1244+ # TODO: print warning if other matches are provided
1245+
1246+ for interface in interfaces:
1247+ if by_name and not utils.is_interface_matching_name(interface, by_name):
1248+ continue
1249+ if vf in vfs and vfs[vf]:
1250+ raise ConfigurationError('matched more than one interface for a VF device: %s' % vf)
1251+ vfs[vf] = interface
1252+ else:
1253+ if vf in interfaces:
1254+ vfs[vf] = vf
1255+
1256+ filtered_vlans_set = set()
1257+ for vlan, settings in config_manager.vlans.items():
1258+ # there is a special sriov vlan renderer that one can use to mark
1259+ # a selected vlan to be done in hardware (VLAN filtering)
1260+ if settings.get('renderer') == 'sriov':
1261+ # this only works for SR-IOV VF interfaces
1262+ link = settings.get('link')
1263+ vlan_id = settings.get('id')
1264+ if not vlan_id:
1265+ raise ConfigurationError(
1266+ 'no id property defined for SR-IOV vlan %s' % vlan)
1267+
1268+ vf = vfs.get(link)
1269+ if not vf:
1270+ # it is possible this is not an error, for instance when
1271+ # the configuration has been defined 'for the future'
1272+ # XXX: but maybe we should error out here as well?
1273+ logging.warning(
1274+ 'SR-IOV vlan defined for %s but link %s is either not a VF or has no matches' % (vlan, link))
1275+ continue
1276+
1277+ # get the parent pf interface
1278+ # first we fetch the related vf netplan entry
1279+ vf_parent_entry = config_manager.ethernets.get(link).get('link')
1280+ # and finally, get the matched pf interface
1281+ pf = pfs.get(vf_parent_entry)
1282+
1283+ if vf in filtered_vlans_set:
1284+ raise ConfigurationError(
1285+ 'interface %s for netplan device %s (%s) already has an SR-IOV vlan defined' % (vf, link, vlan))
1286+
1287+ apply_vlan_filter_for_vf(pf, vf, vlan, vlan_id)
1288+ filtered_vlans_set.add(vf)
1289diff --git a/netplan/cli/utils.py b/netplan/cli/utils.py
1290index 0d1b53f..5f54b1a 100644
1291--- a/netplan/cli/utils.py
1292+++ b/netplan/cli/utils.py
1293@@ -1,7 +1,8 @@
1294 #!/usr/bin/python3
1295 #
1296-# Copyright (C) 2018 Canonical, Ltd.
1297+# Copyright (C) 2018-2020 Canonical, Ltd.
1298 # Author: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
1299+# Author: Łukasz 'sil2100' Zemczak <lukasz.zemczak@canonical.com>
1300 #
1301 # This program is free software; you can redistribute it and/or modify
1302 # it under the terms of the GNU General Public License as published by
1303@@ -17,8 +18,11 @@
1304
1305 import sys
1306 import os
1307+import logging
1308+import fnmatch
1309 import argparse
1310 import subprocess
1311+import netifaces
1312
1313 NM_SERVICE_NAME = 'NetworkManager.service'
1314 NM_SNAP_SERVICE_NAME = 'snap.network-manager.networkmanager.service'
1315@@ -82,6 +86,51 @@ def systemctl_networkd(action, sync=False, extra_services=[]): # pragma: nocove
1316 subprocess.check_call(command)
1317
1318
1319+def get_interface_driver_name(interface, only_down=False): # pragma: nocover (covered in autopkgtest)
1320+ devdir = os.path.join('/sys/class/net', interface)
1321+ if only_down:
1322+ try:
1323+ with open(os.path.join(devdir, 'operstate')) as f:
1324+ state = f.read().strip()
1325+ if state != 'down':
1326+ logging.debug('device %s operstate is %s, not changing', interface, state)
1327+ return None
1328+ except IOError as e:
1329+ logging.error('Cannot determine operstate of %s: %s', interface, str(e))
1330+ return None
1331+
1332+ try:
1333+ driver = os.path.realpath(os.path.join(devdir, 'device', 'driver'))
1334+ driver_name = os.path.basename(driver)
1335+ except IOError as e:
1336+ logging.debug('Cannot replug %s: cannot read link %s/device: %s', interface, devdir, str(e))
1337+ return None
1338+
1339+ return driver_name
1340+
1341+
1342+def get_interface_macaddress(interface): # pragma: nocover (covered in autopkgtest)
1343+ link = netifaces.ifaddresses(interface)[netifaces.AF_LINK][0]
1344+
1345+ return link.get('addr')
1346+
1347+
1348+def is_interface_matching_name(interface, match_driver):
1349+ return fnmatch.fnmatchcase(interface, match_driver)
1350+
1351+
1352+def is_interface_matching_driver_name(interface, match_driver):
1353+ driver_name = get_interface_driver_name(interface)
1354+
1355+ return match_driver == driver_name
1356+
1357+
1358+def is_interface_matching_macaddress(interface, match_mac):
1359+ macaddress = get_interface_macaddress(interface)
1360+
1361+ return match_mac == macaddress
1362+
1363+
1364 class NetplanCommand(argparse.Namespace):
1365
1366 def __init__(self, command_id, description, leaf=True, testing=False):
1367diff --git a/src/generate.c b/src/generate.c
1368index 2106994..c020d8f 100644
1369--- a/src/generate.c
1370+++ b/src/generate.c
1371@@ -53,9 +53,9 @@ reload_udevd(void)
1372 static void
1373 nd_iterator_list(gpointer value, gpointer user_data)
1374 {
1375- if (write_networkd_conf((net_definition*) value, (const char*) user_data))
1376+ if (write_networkd_conf((NetplanNetDefinition*) value, (const char*) user_data))
1377 any_networkd = TRUE;
1378- write_nm_conf((net_definition*) value, (const char*) user_data);
1379+ write_nm_conf((NetplanNetDefinition*) value, (const char*) user_data);
1380 }
1381
1382
1383@@ -91,7 +91,7 @@ find_interface(gchar* interface)
1384
1385 g_hash_table_iter_init (&iter, netdefs);
1386 while (g_hash_table_iter_next (&iter, &key, &value)) {
1387- net_definition *nd = (net_definition *) value;
1388+ NetplanNetDefinition *nd = (NetplanNetDefinition *) value;
1389 if (!g_strcmp0(nd->set_name, interface))
1390 g_ptr_array_add (found, (gpointer) nd);
1391 else if (!g_strcmp0(nd->id, interface))
1392@@ -104,7 +104,7 @@ find_interface(gchar* interface)
1393 // LCOV_EXCL_START
1394 g_hash_table_iter_init (&iter, netdefs);
1395 while (g_hash_table_iter_next (&iter, &key, &value)) {
1396- net_definition *nd = (net_definition *) value;
1397+ NetplanNetDefinition *nd = (NetplanNetDefinition *) value;
1398 if (!g_strcmp0(nd->match.driver, driver))
1399 g_ptr_array_add (found, (gpointer) nd);
1400 }
1401@@ -118,10 +118,10 @@ find_interface(gchar* interface)
1402 goto exit_find;
1403 }
1404 else {
1405- net_definition *nd = (net_definition *)g_ptr_array_index (found, 0);
1406+ const NetplanNetDefinition *nd = (NetplanNetDefinition *)g_ptr_array_index (found, 0);
1407 g_printf("id=%s, backend=%s, set_name=%s, match_name=%s, match_mac=%s, match_driver=%s\n",
1408 nd->id,
1409- netdef_backend_to_name[nd->backend],
1410+ netplan_backend_to_name[nd->backend],
1411 nd->set_name,
1412 nd->match.original_name,
1413 nd->match.mac,
1414@@ -141,7 +141,7 @@ process_input_file(const char* f)
1415 GError* error = NULL;
1416
1417 g_debug("Processing input file %s..", f);
1418- if (!parse_yaml(f, &error)) {
1419+ if (!netplan_parse_yaml(f, &error)) {
1420 g_fprintf(stderr, "%s\n", error->message);
1421 exit(1);
1422 }
1423@@ -237,7 +237,8 @@ int main(int argc, char** argv)
1424 process_input_file(g_hash_table_lookup(configs, i->data));
1425 }
1426
1427- if (!finish_parse(&error)) {
1428+ netdefs = netplan_finish_parse(&error);
1429+ if (error) {
1430 g_fprintf(stderr, "%s\n", error->message);
1431 exit(1);
1432 }
1433@@ -266,7 +267,7 @@ int main(int argc, char** argv)
1434
1435 /* Disable /usr/lib/NetworkManager/conf.d/10-globally-managed-devices.conf
1436 * (which restricts NM to wifi and wwan) if global renderer is NM */
1437- if (get_global_backend() == BACKEND_NM)
1438+ if (netplan_get_global_backend() == NETPLAN_BACKEND_NM)
1439 g_string_free_to_file(g_string_new(NULL), rootdir, "/run/NetworkManager/conf.d/10-globally-managed-devices.conf", NULL);
1440
1441 if (called_as_generator) {
1442diff --git a/src/netplan-wpa@.service b/src/netplan-wpa@.service
1443deleted file mode 100644
1444index d2670d9..0000000
1445--- a/src/netplan-wpa@.service
1446+++ /dev/null
1447@@ -1,11 +0,0 @@
1448-[Unit]
1449-Description=WPA supplicant for netplan %I
1450-DefaultDependencies=no
1451-Requires=sys-subsystem-net-devices-%i.device
1452-After=sys-subsystem-net-devices-%i.device
1453-Before=network.target
1454-Wants=network.target
1455-
1456-[Service]
1457-Type=simple
1458-ExecStart=/sbin/wpa_supplicant -c /run/netplan/wpa-%I.conf -i%I
1459diff --git a/src/networkd.c b/src/networkd.c
1460index b6d276f..e2bb111 100644
1461--- a/src/networkd.c
1462+++ b/src/networkd.c
1463@@ -16,7 +16,9 @@
1464 */
1465
1466 #include <stdlib.h>
1467+#include <string.h>
1468 #include <unistd.h>
1469+#include <ctype.h>
1470 #include <errno.h>
1471 #include <sys/stat.h>
1472
1473@@ -27,12 +29,39 @@
1474 #include "parse.h"
1475 #include "util.h"
1476
1477+/**
1478+ * Append WiFi frequencies to wpa_supplicant's freq_list=
1479+ */
1480+static void
1481+wifi_append_freq(gpointer key, gpointer value, gpointer user_data)
1482+{
1483+ GString* s = user_data;
1484+ g_string_append_printf(s, "%d ", GPOINTER_TO_INT(value));
1485+}
1486+
1487+/**
1488+ * append wowlan_triggers= string for wpa_supplicant.conf
1489+ */
1490+static void
1491+append_wifi_wowlan_flags(NetplanWifiWowlanFlag flag, GString* str) {
1492+ if (flag & NETPLAN_WIFI_WOWLAN_TYPES[0].flag || flag >= NETPLAN_WIFI_WOWLAN_TCP) {
1493+ g_fprintf(stderr, "ERROR: unsupported wowlan_triggers mask: 0x%x\n", flag);
1494+ exit(1);
1495+ }
1496+ for (unsigned i = 0; NETPLAN_WIFI_WOWLAN_TYPES[i].name != NULL; ++i) {
1497+ if (flag & NETPLAN_WIFI_WOWLAN_TYPES[i].flag) {
1498+ g_string_append_printf(str, "%s ", NETPLAN_WIFI_WOWLAN_TYPES[i].name);
1499+ }
1500+ }
1501+ /* replace trailing space with newline */
1502+ str = g_string_overwrite(str, str->len-1, "\n");
1503+}
1504
1505 /**
1506 * Append [Match] section of @def to @s.
1507 */
1508 static void
1509-append_match_section(net_definition* def, GString* s, gboolean match_rename)
1510+append_match_section(const NetplanNetDefinition* def, GString* s, gboolean match_rename)
1511 {
1512 /* Note: an empty [Match] section is interpreted as matching all devices,
1513 * which is what we want for the simple case that you only have one device
1514@@ -48,7 +77,7 @@ append_match_section(net_definition* def, GString* s, gboolean match_rename)
1515 if (!match_rename && def->match.original_name)
1516 g_string_append_printf(s, "OriginalName=%s\n", def->match.original_name);
1517 if (match_rename) {
1518- if (def->type >= ND_VIRTUAL)
1519+ if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL)
1520 g_string_append_printf(s, "Name=%s\n", def->id);
1521 else if (def->set_name)
1522 g_string_append_printf(s, "Name=%s\n", def->set_name);
1523@@ -76,7 +105,7 @@ append_match_section(net_definition* def, GString* s, gboolean match_rename)
1524 }
1525
1526 static void
1527-write_bridge_params(GString* s, net_definition* def)
1528+write_bridge_params(GString* s, const NetplanNetDefinition* def)
1529 {
1530 GString *params = NULL;
1531
1532@@ -102,14 +131,14 @@ write_bridge_params(GString* s, net_definition* def)
1533 }
1534
1535 static void
1536-write_tunnel_params(GString* s, net_definition* def)
1537+write_tunnel_params(GString* s, const NetplanNetDefinition* def)
1538 {
1539 GString *params = NULL;
1540
1541 params = g_string_sized_new(200);
1542
1543 g_string_printf(params, "Independent=true\n");
1544- if (def->tunnel.mode == TUNNEL_MODE_IPIP6 || def->tunnel.mode == TUNNEL_MODE_IP6IP6)
1545+ if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_IPIP6 || def->tunnel.mode == NETPLAN_TUNNEL_MODE_IP6IP6)
1546 g_string_append_printf(params, "Mode=%s\n", tunnel_mode_to_string(def->tunnel.mode));
1547 g_string_append_printf(params, "Local=%s\n", def->tunnel.local_ip);
1548 g_string_append_printf(params, "Remote=%s\n", def->tunnel.remote_ip);
1549@@ -123,13 +152,15 @@ write_tunnel_params(GString* s, net_definition* def)
1550 }
1551
1552 static void
1553-write_link_file(net_definition* def, const char* rootdir, const char* path)
1554+write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char* path)
1555 {
1556 GString* s = NULL;
1557 mode_t orig_umask;
1558
1559- /* Don't write .link files for virtual devices; they use .netdev instead */
1560- if (def->type >= ND_VIRTUAL)
1561+ /* Don't write .link files for virtual devices; they use .netdev instead.
1562+ * Don't write .link files for MODEM devices, as they aren't supported by networkd.
1563+ */
1564+ if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL || def->type == NETPLAN_DEF_TYPE_MODEM)
1565 return;
1566
1567 /* do we need to write a .link file? */
1568@@ -170,7 +201,7 @@ interval_has_suffix(const char* param) {
1569
1570
1571 static void
1572-write_bond_parameters(net_definition* def, GString* s)
1573+write_bond_parameters(const NetplanNetDefinition* def, GString* s)
1574 {
1575 GString* params = NULL;
1576
1577@@ -249,12 +280,17 @@ write_bond_parameters(net_definition* def, GString* s)
1578 }
1579
1580 static void
1581-write_netdev_file(net_definition* def, const char* rootdir, const char* path)
1582+write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const char* path)
1583 {
1584 GString* s = NULL;
1585 mode_t orig_umask;
1586
1587- g_assert(def->type >= ND_VIRTUAL);
1588+ g_assert(def->type >= NETPLAN_DEF_TYPE_VIRTUAL);
1589+
1590+ if (def->type == NETPLAN_DEF_TYPE_VLAN && def->sriov_vlan_filter) {
1591+ g_debug("%s is defined as a hardware SR-IOV filtered VLAN, postponing creation", def->id);
1592+ return;
1593+ }
1594
1595 /* build file contents */
1596 s = g_string_sized_new(200);
1597@@ -266,37 +302,37 @@ write_netdev_file(net_definition* def, const char* rootdir, const char* path)
1598 g_string_append_printf(s, "MTUBytes=%u\n", def->mtubytes);
1599
1600 switch (def->type) {
1601- case ND_BRIDGE:
1602+ case NETPLAN_DEF_TYPE_BRIDGE:
1603 g_string_append(s, "Kind=bridge\n");
1604 write_bridge_params(s, def);
1605 break;
1606
1607- case ND_BOND:
1608+ case NETPLAN_DEF_TYPE_BOND:
1609 g_string_append(s, "Kind=bond\n");
1610 write_bond_parameters(def, s);
1611 break;
1612
1613- case ND_VLAN:
1614+ case NETPLAN_DEF_TYPE_VLAN:
1615 g_string_append_printf(s, "Kind=vlan\n\n[VLAN]\nId=%u\n", def->vlan_id);
1616 break;
1617
1618- case ND_TUNNEL:
1619+ case NETPLAN_DEF_TYPE_TUNNEL:
1620 switch(def->tunnel.mode) {
1621- case TUNNEL_MODE_GRE:
1622- case TUNNEL_MODE_GRETAP:
1623- case TUNNEL_MODE_IPIP:
1624- case TUNNEL_MODE_IP6GRE:
1625- case TUNNEL_MODE_IP6GRETAP:
1626- case TUNNEL_MODE_SIT:
1627- case TUNNEL_MODE_VTI:
1628- case TUNNEL_MODE_VTI6:
1629+ case NETPLAN_TUNNEL_MODE_GRE:
1630+ case NETPLAN_TUNNEL_MODE_GRETAP:
1631+ case NETPLAN_TUNNEL_MODE_IPIP:
1632+ case NETPLAN_TUNNEL_MODE_IP6GRE:
1633+ case NETPLAN_TUNNEL_MODE_IP6GRETAP:
1634+ case NETPLAN_TUNNEL_MODE_SIT:
1635+ case NETPLAN_TUNNEL_MODE_VTI:
1636+ case NETPLAN_TUNNEL_MODE_VTI6:
1637 g_string_append_printf(s,
1638 "Kind=%s\n",
1639 tunnel_mode_to_string(def->tunnel.mode));
1640 break;
1641
1642- case TUNNEL_MODE_IP6IP6:
1643- case TUNNEL_MODE_IPIP6:
1644+ case NETPLAN_TUNNEL_MODE_IP6IP6:
1645+ case NETPLAN_TUNNEL_MODE_IPIP6:
1646 g_string_append(s, "Kind=ip6tnl\n");
1647 break;
1648
1649@@ -323,7 +359,7 @@ write_netdev_file(net_definition* def, const char* rootdir, const char* path)
1650 }
1651
1652 static void
1653-write_route(ip_route* r, GString* s)
1654+write_route(NetplanIPRoute* r, GString* s)
1655 {
1656 g_string_append_printf(s, "\n[Route]\n");
1657
1658@@ -340,14 +376,14 @@ write_route(ip_route* r, GString* s)
1659 g_string_append_printf(s, "Type=%s\n", r->type);
1660 if (r->onlink)
1661 g_string_append_printf(s, "GatewayOnlink=true\n");
1662- if (r->metric != METRIC_UNSPEC)
1663+ if (r->metric != NETPLAN_METRIC_UNSPEC)
1664 g_string_append_printf(s, "Metric=%d\n", r->metric);
1665- if (r->table != ROUTE_TABLE_UNSPEC)
1666+ if (r->table != NETPLAN_ROUTE_TABLE_UNSPEC)
1667 g_string_append_printf(s, "Table=%d\n", r->table);
1668 }
1669
1670 static void
1671-write_ip_rule(ip_rule* r, GString* s)
1672+write_ip_rule(NetplanIPRule* r, GString* s)
1673 {
1674 g_string_append_printf(s, "\n[RoutingPolicyRule]\n");
1675
1676@@ -356,13 +392,13 @@ write_ip_rule(ip_rule* r, GString* s)
1677 if (r->to)
1678 g_string_append_printf(s, "To=%s\n", r->to);
1679
1680- if (r->table != ROUTE_TABLE_UNSPEC)
1681+ if (r->table != NETPLAN_ROUTE_TABLE_UNSPEC)
1682 g_string_append_printf(s, "Table=%d\n", r->table);
1683- if (r->priority != IP_RULE_PRIO_UNSPEC)
1684+ if (r->priority != NETPLAN_IP_RULE_PRIO_UNSPEC)
1685 g_string_append_printf(s, "Priority=%d\n", r->priority);
1686- if (r->fwmark != IP_RULE_FW_MARK_UNSPEC)
1687+ if (r->fwmark != NETPLAN_IP_RULE_FW_MARK_UNSPEC)
1688 g_string_append_printf(s, "FirewallMark=%d\n", r->fwmark);
1689- if (r->tos != IP_RULE_TOS_UNSPEC)
1690+ if (r->tos != NETPLAN_IP_RULE_TOS_UNSPEC)
1691 g_string_append_printf(s, "TypeOfService=%d\n", r->tos);
1692 }
1693
1694@@ -371,7 +407,7 @@ write_ip_rule(ip_rule* r, GString* s)
1695 "dhcp4_overrides and dhcp6_overrides\n"
1696
1697 static void
1698-combine_dhcp_overrides(net_definition* def, dhcp_overrides* combined_dhcp_overrides)
1699+combine_dhcp_overrides(const NetplanNetDefinition* def, NetplanDHCPOverrides* combined_dhcp_overrides)
1700 {
1701 /* if only one of dhcp4 or dhcp6 is enabled, those overrides are used */
1702 if (def->dhcp4 && !def->dhcp6) {
1703@@ -424,13 +460,18 @@ combine_dhcp_overrides(net_definition* def, dhcp_overrides* combined_dhcp_overri
1704 }
1705
1706 static void
1707-write_network_file(net_definition* def, const char* rootdir, const char* path)
1708+write_network_file(const NetplanNetDefinition* def, const char* rootdir, const char* path)
1709 {
1710 GString* network = NULL;
1711 GString* link = NULL;
1712 GString* s = NULL;
1713 mode_t orig_umask;
1714
1715+ if (def->type == NETPLAN_DEF_TYPE_VLAN && def->sriov_vlan_filter) {
1716+ g_debug("%s is defined as a hardware SR-IOV filtered VLAN, postponing creation", def->id);
1717+ return;
1718+ }
1719+
1720 /* Prepare the [Link] section of the .network file. */
1721 link = g_string_sized_new(200);
1722
1723@@ -441,13 +482,20 @@ write_network_file(net_definition* def, const char* rootdir, const char* path)
1724 if (def->optional) {
1725 g_string_append(link, "RequiredForOnline=no\n");
1726 }
1727- for (unsigned i = 0; optional_address_options[i].name != NULL; ++i) {
1728- if (def->optional_addresses & optional_address_options[i].flag) {
1729- g_string_append_printf(link, "OptionalAddresses=%s\n", optional_address_options[i].name);
1730+ for (unsigned i = 0; NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name != NULL; ++i) {
1731+ if (def->optional_addresses & NETPLAN_OPTIONAL_ADDRESS_TYPES[i].flag) {
1732+ g_string_append_printf(link, "OptionalAddresses=%s\n", NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name);
1733 }
1734 }
1735 }
1736
1737+ if (def->mtubytes) {
1738+ g_string_append_printf(link, "MTUBytes=%d\n", def->mtubytes);
1739+ }
1740+
1741+ if (def->emit_lldp) {
1742+ g_string_append(network, "EmitLLDP=true\n");
1743+ }
1744
1745 if (def->dhcp4 && def->dhcp6)
1746 g_string_append(network, "DHCP=yes\n");
1747@@ -476,9 +524,18 @@ write_network_file(net_definition* def, const char* rootdir, const char* path)
1748 if (def->ip6_addresses)
1749 for (unsigned i = 0; i < def->ip6_addresses->len; ++i)
1750 g_string_append_printf(network, "Address=%s\n", g_array_index(def->ip6_addresses, char*, i));
1751- if (def->accept_ra == ACCEPT_RA_ENABLED)
1752+ if (def->ip6_addr_gen_mode) {
1753+ /* TODO: Figure out how we can configure ipv6-address-generation for networkd.
1754+ * IPv6Token= seems to be the corresponding option, but it doesn't do
1755+ * exactly what we need and has quite some restrictions, c.f.:
1756+ * https://github.com/systemd/systemd/issues/4625
1757+ * https://github.com/systemd/systemd/pull/14415 */
1758+ g_fprintf(stderr, "ERROR: %s: ipv6-address-generation is not supported by networkd\n", def->id);
1759+ exit(1);
1760+ }
1761+ if (def->accept_ra == NETPLAN_RA_MODE_ENABLED)
1762 g_string_append_printf(network, "IPv6AcceptRA=yes\n");
1763- else if (def->accept_ra == ACCEPT_RA_DISABLED)
1764+ else if (def->accept_ra == NETPLAN_RA_MODE_DISABLED)
1765 g_string_append_printf(network, "IPv6AcceptRA=no\n");
1766 if (def->ip6_privacy)
1767 g_string_append(network, "IPv6PrivacyExtensions=yes\n");
1768@@ -503,7 +560,7 @@ write_network_file(net_definition* def, const char* rootdir, const char* path)
1769 g_string_append_printf(network, "IPv6MTUBytes=%d\n", def->ipv6_mtubytes);
1770 }
1771
1772- if (def->type >= ND_VIRTUAL)
1773+ if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL)
1774 g_string_append(network, "ConfigureWithoutCarrier=yes\n");
1775
1776 if (def->bridge) {
1777@@ -526,41 +583,44 @@ write_network_file(net_definition* def, const char* rootdir, const char* path)
1778 if (def->has_vlans) {
1779 /* iterate over all netdefs to find VLANs attached to us */
1780 GList *l = netdefs_ordered;
1781- net_definition* nd;
1782+ const NetplanNetDefinition* nd;
1783 for (; l != NULL; l = l->next) {
1784 nd = l->data;
1785- if (nd->vlan_link == def)
1786+ if (nd->vlan_link == def && !nd->sriov_vlan_filter)
1787 g_string_append_printf(network, "VLAN=%s\n", nd->id);
1788 }
1789 }
1790
1791 if (def->routes != NULL) {
1792 for (unsigned i = 0; i < def->routes->len; ++i) {
1793- ip_route* cur_route = g_array_index (def->routes, ip_route*, i);
1794+ NetplanIPRoute* cur_route = g_array_index (def->routes, NetplanIPRoute*, i);
1795 write_route(cur_route, network);
1796 }
1797 }
1798 if (def->ip_rules != NULL) {
1799 for (unsigned i = 0; i < def->ip_rules->len; ++i) {
1800- ip_rule* cur_rule = g_array_index (def->ip_rules, ip_rule*, i);
1801+ NetplanIPRule* cur_rule = g_array_index (def->ip_rules, NetplanIPRule*, i);
1802 write_ip_rule(cur_rule, network);
1803 }
1804 }
1805
1806- if (def->dhcp4 || def->dhcp6) {
1807+ if (def->dhcp4 || def->dhcp6 || def->critical) {
1808 /* NetworkManager compatible route metrics */
1809 g_string_append(network, "\n[DHCP]\n");
1810+ }
1811+
1812+ if (def->critical)
1813+ g_string_append_printf(network, "CriticalConnection=true\n");
1814
1815+ if (def->dhcp4 || def->dhcp6) {
1816 if (g_strcmp0(def->dhcp_identifier, "duid") != 0)
1817 g_string_append_printf(network, "ClientIdentifier=%s\n", def->dhcp_identifier);
1818- if (def->critical)
1819- g_string_append_printf(network, "CriticalConnection=true\n");
1820
1821- dhcp_overrides combined_dhcp_overrides;
1822+ NetplanDHCPOverrides combined_dhcp_overrides;
1823 combine_dhcp_overrides(def, &combined_dhcp_overrides);
1824
1825- if (combined_dhcp_overrides.metric == METRIC_UNSPEC) {
1826- g_string_append_printf(network, "RouteMetric=%i\n", (def->type == ND_WIFI ? 600 : 100));
1827+ if (combined_dhcp_overrides.metric == NETPLAN_METRIC_UNSPEC) {
1828+ g_string_append_printf(network, "RouteMetric=%i\n", (def->type == NETPLAN_DEF_TYPE_WIFI ? 600 : 100));
1829 } else {
1830 g_string_append_printf(network, "RouteMetric=%u\n",
1831 combined_dhcp_overrides.metric);
1832@@ -613,7 +673,7 @@ write_network_file(net_definition* def, const char* rootdir, const char* path)
1833 }
1834
1835 static void
1836-write_rules_file(net_definition* def, const char* rootdir)
1837+write_rules_file(const NetplanNetDefinition* def, const char* rootdir)
1838 {
1839 GString* s = NULL;
1840 g_autofree char* path = g_strjoin(NULL, "run/udev/rules.d/99-netplan-", def->id, ".rules", NULL);
1841@@ -622,7 +682,7 @@ write_rules_file(net_definition* def, const char* rootdir)
1842 /* do we need to write a .rules file?
1843 * It's only required for reliably setting the name of a physical device
1844 * until systemd issue #9006 is resolved. */
1845- if (def->type >= ND_VIRTUAL)
1846+ if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL)
1847 return;
1848
1849 /* Matching by name does not work.
1850@@ -657,39 +717,39 @@ write_rules_file(net_definition* def, const char* rootdir)
1851 }
1852
1853 static void
1854-append_wpa_auth_conf(GString* s, const authentication_settings* auth)
1855+append_wpa_auth_conf(GString* s, const NetplanAuthenticationSettings* auth, const char* id)
1856 {
1857 switch (auth->key_management) {
1858- case KEY_MANAGEMENT_NONE:
1859+ case NETPLAN_AUTH_KEY_MANAGEMENT_NONE:
1860 g_string_append(s, " key_mgmt=NONE\n");
1861 break;
1862
1863- case KEY_MANAGEMENT_WPA_PSK:
1864+ case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK:
1865 g_string_append(s, " key_mgmt=WPA-PSK\n");
1866 break;
1867
1868- case KEY_MANAGEMENT_WPA_EAP:
1869+ case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP:
1870 g_string_append(s, " key_mgmt=WPA-EAP\n");
1871 break;
1872
1873- case KEY_MANAGEMENT_8021X:
1874+ case NETPLAN_AUTH_KEY_MANAGEMENT_8021X:
1875 g_string_append(s, " key_mgmt=IEEE8021X\n");
1876 break;
1877 }
1878
1879 switch (auth->eap_method) {
1880- case EAP_NONE:
1881+ case NETPLAN_AUTH_EAP_NONE:
1882 break;
1883
1884- case EAP_TLS:
1885+ case NETPLAN_AUTH_EAP_TLS:
1886 g_string_append(s, " eap=TLS\n");
1887 break;
1888
1889- case EAP_PEAP:
1890+ case NETPLAN_AUTH_EAP_PEAP:
1891 g_string_append(s, " eap=PEAP\n");
1892 break;
1893
1894- case EAP_TTLS:
1895+ case NETPLAN_AUTH_EAP_TTLS:
1896 g_string_append(s, " eap=TTLS\n");
1897 break;
1898 }
1899@@ -701,8 +761,25 @@ append_wpa_auth_conf(GString* s, const authentication_settings* auth)
1900 g_string_append_printf(s, " anonymous_identity=\"%s\"\n", auth->anonymous_identity);
1901 }
1902 if (auth->password) {
1903- if (auth->key_management == KEY_MANAGEMENT_WPA_PSK) {
1904- g_string_append_printf(s, " psk=\"%s\"\n", auth->password);
1905+ if (auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK) {
1906+ size_t len = strlen(auth->password);
1907+ if (len == 64) {
1908+ /* must be a hex-digit key representation */
1909+ for (unsigned i = 0; i < 64; ++i)
1910+ if (!isxdigit(auth->password[i])) {
1911+ g_fprintf(stderr, "ERROR: %s: PSK length of 64 is only supported for hex-digit representation\n", id);
1912+ exit(1);
1913+ }
1914+ /* this is required to be unquoted */
1915+ g_string_append_printf(s, " psk=%s\n", auth->password);
1916+ } else if (len < 8 || len > 63) {
1917+ /* per wpa_supplicant spec, passphrase needs to be between 8
1918+ and 63 characters */
1919+ g_fprintf(stderr, "ERROR: %s: ASCII passphrase must be between 8 and 63 characters (inclusive)\n", id);
1920+ exit(1);
1921+ } else {
1922+ g_string_append_printf(s, " psk=\"%s\"\n", auth->password);
1923+ }
1924 } else {
1925 if (strncmp(auth->password, "hash:", 5) == 0) {
1926 g_string_append_printf(s, " password=%s\n", auth->password);
1927@@ -723,10 +800,50 @@ append_wpa_auth_conf(GString* s, const authentication_settings* auth)
1928 if (auth->client_key_password) {
1929 g_string_append_printf(s, " private_key_passwd=\"%s\"\n", auth->client_key_password);
1930 }
1931+ if (auth->phase2_auth) {
1932+ g_string_append_printf(s, " phase2=\"auth=%s\"\n", auth->phase2_auth);
1933+ }
1934+
1935 }
1936
1937+/* netplan-feature: generated-supplicant */
1938 static void
1939-write_wpa_conf(net_definition* def, const char* rootdir)
1940+write_wpa_unit(const NetplanNetDefinition* def, const char* rootdir)
1941+{
1942+ g_autoptr(GError) err = NULL;
1943+ g_autofree gchar *stdouth = NULL;
1944+ g_autofree gchar *stderrh = NULL;
1945+ gint exit_status = 0;
1946+
1947+ gchar *argv[] = {"bin" "/" "systemd-escape", def->id, NULL};
1948+ g_spawn_sync("/", argv, NULL, 0, NULL, NULL, &stdouth, &stderrh, &exit_status, &err);
1949+ g_spawn_check_exit_status(exit_status, &err);
1950+ if (err != NULL) {
1951+ // LCOV_EXCL_START
1952+ g_fprintf(stderr, "failed to ask systemd to escape %s; exit %d\nstdout: '%s'\nstderr: '%s'", def->id, exit_status, stdouth, stderrh);
1953+ exit(1);
1954+ // LCOV_EXCL_STOP
1955+ }
1956+ g_strstrip(stdouth);
1957+
1958+ GString* s = g_string_new("[Unit]\n");
1959+ g_autofree char* path = g_strjoin(NULL, "/run/systemd/system/netplan-wpa-", stdouth, ".service", NULL);
1960+ g_string_append_printf(s, "Description=WPA supplicant for netplan %s\n", stdouth);
1961+ g_string_append(s, "DefaultDependencies=no\n");
1962+ g_string_append_printf(s, "Requires=sys-subsystem-net-devices-%s.device\n", stdouth);
1963+ g_string_append_printf(s, "After=sys-subsystem-net-devices-%s.device\n", stdouth);
1964+ g_string_append(s, "Before=network.target\nWants=network.target\n\n");
1965+ g_string_append(s, "[Service]\nType=simple\n");
1966+ g_string_append_printf(s, "ExecStart=/sbin/wpa_supplicant -c /run/netplan/wpa-%s.conf -i%s", stdouth, stdouth);
1967+
1968+ if (def->type != NETPLAN_DEF_TYPE_WIFI) {
1969+ g_string_append(s, " -Dwired\n");
1970+ }
1971+ g_string_free_to_file(s, rootdir, path, NULL);
1972+}
1973+
1974+static void
1975+write_wpa_conf(const NetplanNetDefinition* def, const char* rootdir)
1976 {
1977 GHashTableIter iter;
1978 GString* s = g_string_new("ctrl_interface=/run/wpa_supplicant\n\n");
1979@@ -734,26 +851,58 @@ write_wpa_conf(net_definition* def, const char* rootdir)
1980 mode_t orig_umask;
1981
1982 g_debug("%s: Creating wpa_supplicant configuration file %s", def->id, path);
1983- if (def->type == ND_WIFI) {
1984- wifi_access_point* ap;
1985+ if (def->type == NETPLAN_DEF_TYPE_WIFI) {
1986+ if (def->wowlan && def->wowlan > NETPLAN_WIFI_WOWLAN_DEFAULT) {
1987+ g_string_append(s, "wowlan_triggers=");
1988+ append_wifi_wowlan_flags(def->wowlan, s);
1989+ }
1990+ NetplanWifiAccessPoint* ap;
1991 g_hash_table_iter_init(&iter, def->access_points);
1992 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &ap)) {
1993 g_string_append_printf(s, "network={\n ssid=\"%s\"\n", ap->ssid);
1994+ if (ap->bssid) {
1995+ g_string_append_printf(s, " bssid=%s\n", ap->bssid);
1996+ }
1997+ if (ap->band == NETPLAN_WIFI_BAND_24) {
1998+ // initialize 2.4GHz frequency hashtable
1999+ if(!wifi_frequency_24)
2000+ wifi_get_freq24(1);
2001+ if (ap->channel) {
2002+ g_string_append_printf(s, " freq_list=%d\n", wifi_get_freq24(ap->channel));
2003+ } else {
2004+ g_string_append_printf(s, " freq_list=");
2005+ g_hash_table_foreach(wifi_frequency_24, wifi_append_freq, s);
2006+ // overwrite last whitespace with newline
2007+ s = g_string_overwrite(s, s->len-1, "\n");
2008+ }
2009+ } else if (ap->band == NETPLAN_WIFI_BAND_5) {
2010+ // initialize 5GHz frequency hashtable
2011+ if(!wifi_frequency_5)
2012+ wifi_get_freq5(7);
2013+ if (ap->channel) {
2014+ g_string_append_printf(s, " freq_list=%d\n", wifi_get_freq5(ap->channel));
2015+ } else {
2016+ g_string_append_printf(s, " freq_list=");
2017+ g_hash_table_foreach(wifi_frequency_5, wifi_append_freq, s);
2018+ // overwrite last whitespace with newline
2019+ s = g_string_overwrite(s, s->len-1, "\n");
2020+ }
2021+ }
2022 switch (ap->mode) {
2023- case WIFI_MODE_INFRASTRUCTURE:
2024+ case NETPLAN_WIFI_MODE_INFRASTRUCTURE:
2025 /* default in wpasupplicant */
2026 break;
2027- case WIFI_MODE_ADHOC:
2028+ case NETPLAN_WIFI_MODE_ADHOC:
2029 g_string_append(s, " mode=1\n");
2030 break;
2031- case WIFI_MODE_AP:
2032+ case NETPLAN_WIFI_MODE_AP:
2033 g_fprintf(stderr, "ERROR: %s: networkd does not support wifi in access point mode\n", def->id);
2034 exit(1);
2035 }
2036
2037 /* wifi auth trumps netdef auth */
2038 if (ap->has_auth) {
2039- append_wpa_auth_conf(s, &ap->auth);
2040+ append_wpa_auth_conf(s, &ap->auth, ap->ssid);
2041 }
2042 else {
2043 g_string_append(s, " key_mgmt=NONE\n");
2044@@ -764,7 +913,7 @@ write_wpa_conf(net_definition* def, const char* rootdir)
2045 else {
2046 /* wired 802.1x auth or similar */
2047 g_string_append(s, "network={\n");
2048- append_wpa_auth_conf(s, &def->auth);
2049+ append_wpa_auth_conf(s, &def->auth, def->id);
2050 g_string_append(s, "}\n");
2051 }
2052
2053@@ -782,7 +931,7 @@ write_wpa_conf(net_definition* def, const char* rootdir)
2054 * Returns: TRUE if @def applies to networkd, FALSE otherwise.
2055 */
2056 gboolean
2057-write_networkd_conf(net_definition* def, const char* rootdir)
2058+write_networkd_conf(const NetplanNetDefinition* def, const char* rootdir)
2059 {
2060 g_autofree char* path_base = g_strjoin(NULL, "run/systemd/network/10-netplan-", def->id, NULL);
2061
2062@@ -791,23 +940,34 @@ write_networkd_conf(net_definition* def, const char* rootdir)
2063 write_link_file(def, rootdir, path_base);
2064 write_rules_file(def, rootdir);
2065
2066- if (def->backend != BACKEND_NETWORKD) {
2067+ if (def->backend != NETPLAN_BACKEND_NETWORKD) {
2068 g_debug("networkd: definition %s is not for us (backend %i)", def->id, def->backend);
2069 return FALSE;
2070 }
2071
2072- if (def->type == ND_WIFI || def->has_auth) {
2073- g_autofree char* link = g_strjoin(NULL, rootdir ?: "", "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa@", def->id, ".service", NULL);
2074- if (def->type == ND_WIFI && def->has_match) {
2075+ if (def->type == NETPLAN_DEF_TYPE_MODEM) {
2076+ g_fprintf(stderr, "ERROR: %s: networkd backend does not support GSM/CDMA modem configuration\n", def->id);
2077+ exit(1);
2078+ }
2079+
2080+ if (def->type == NETPLAN_DEF_TYPE_WIFI || def->has_auth) {
2081+ g_autofree char* link = g_strjoin(NULL, rootdir ?: "", "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa-", def->id, ".service", NULL);
2082+ g_autofree char* slink = g_strjoin(NULL, "/run/systemd/system/netplan-wpa-", def->id, ".service", NULL);
2083+ if (def->type == NETPLAN_DEF_TYPE_WIFI && def->has_match) {
2084 g_fprintf(stderr, "ERROR: %s: networkd backend does not support wifi with match:, only by interface name\n", def->id);
2085 exit(1);
2086 }
2087
2088+ g_debug("Creating wpa_supplicant config");
2089 write_wpa_conf(def, rootdir);
2090
2091+ g_debug("Creating wpa_supplicant unit %s", slink);
2092+ write_wpa_unit(def, rootdir);
2093+
2094 g_debug("Creating wpa_supplicant service enablement link %s", link);
2095 safe_mkdir_p_dir(link);
2096- if (symlink("/lib/systemd/system/netplan-wpa@.service", link) < 0 && errno != EEXIST) {
2097+
2098+ if (symlink(slink, link) < 0 && errno != EEXIST) {
2099 // LCOV_EXCL_START
2100 g_fprintf(stderr, "failed to create enablement symlink: %m\n");
2101 exit(1);
2102@@ -816,7 +976,7 @@ write_networkd_conf(net_definition* def, const char* rootdir)
2103
2104 }
2105
2106- if (def->type >= ND_VIRTUAL)
2107+ if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL)
2108 write_netdev_file(def, rootdir, path_base);
2109 write_network_file(def, rootdir, path_base);
2110 return TRUE;
2111@@ -830,7 +990,7 @@ cleanup_networkd_conf(const char* rootdir)
2112 {
2113 unlink_glob(rootdir, "/run/systemd/network/10-netplan-*");
2114 unlink_glob(rootdir, "/run/netplan/wpa-*.conf");
2115- unlink_glob(rootdir, "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa@*.service");
2116+ unlink_glob(rootdir, "/run/systemd/system/netplan-wpa-*.service");
2117 unlink_glob(rootdir, "/run/udev/rules.d/99-netplan-*");
2118 }
2119
2120diff --git a/src/networkd.h b/src/networkd.h
2121index 6660f9c..5629a04 100644
2122--- a/src/networkd.h
2123+++ b/src/networkd.h
2124@@ -19,6 +19,6 @@
2125
2126 #include "parse.h"
2127
2128-gboolean write_networkd_conf(net_definition* def, const char* rootdir);
2129+gboolean write_networkd_conf(const NetplanNetDefinition* def, const char* rootdir);
2130 void cleanup_networkd_conf(const char* rootdir);
2131 void enable_networkd(const char* generator_dir);
2132diff --git a/src/nm.c b/src/nm.c
2133index 804037f..2cd1d52 100644
2134--- a/src/nm.c
2135+++ b/src/nm.c
2136@@ -36,20 +36,20 @@ GString* udev_rules;
2137 * Append NM device specifier of @def to @s.
2138 */
2139 static void
2140-g_string_append_netdef_match(GString* s, const net_definition* def)
2141+g_string_append_netdef_match(GString* s, const NetplanNetDefinition* def)
2142 {
2143 g_assert(!def->match.driver || def->set_name);
2144 if (def->match.mac) {
2145 g_string_append_printf(s, "mac:%s", def->match.mac);
2146- } else if (def->match.original_name || def->set_name || def->type >= ND_VIRTUAL) {
2147+ } else if (def->match.original_name || def->set_name || def->type >= NETPLAN_DEF_TYPE_VIRTUAL) {
2148 /* we always have the renamed name here */
2149 g_string_append_printf(s, "interface-name:%s",
2150- (def->type >= ND_VIRTUAL) ? def->id
2151+ (def->type >= NETPLAN_DEF_TYPE_VIRTUAL) ? def->id
2152 : (def->set_name ?: def->match.original_name));
2153 } else {
2154 /* no matches → match all devices of that type */
2155 switch (def->type) {
2156- case ND_ETHERNET:
2157+ case NETPLAN_DEF_TYPE_ETHERNET:
2158 g_string_append(s, "type:ethernet");
2159 break;
2160 /* This cannot be reached with just NM and networkd backends, as
2161@@ -57,7 +57,7 @@ g_string_append_netdef_match(GString* s, const net_definition* def)
2162 * wifi device from NM. This would become relevant with another
2163 * wifi-supporting backend, but until then this just spoils 100%
2164 * code coverage.
2165- case ND_WIFI:
2166+ case NETPLAN_DEF_TYPE_WIFI:
2167 g_string_append(s, "type:wifi");
2168 break;
2169 */
2170@@ -71,23 +71,46 @@ g_string_append_netdef_match(GString* s, const net_definition* def)
2171 }
2172
2173 /**
2174+ * Infer if this is a modem netdef of type GSM.
2175+ * This is done by checking for certain modem_params, which are only
2176+ * applicable to GSM connections.
2177+ */
2178+static const gboolean
2179+modem_is_gsm(const NetplanNetDefinition* def)
2180+{
2181+ if (def->type == NETPLAN_DEF_TYPE_MODEM && (def->modem_params.apn ||
2182+ def->modem_params.auto_config || def->modem_params.device_id ||
2183+ def->modem_params.network_id || def->modem_params.pin ||
2184+ def->modem_params.sim_id || def->modem_params.sim_operator_id))
2185+ return TRUE;
2186+
2187+ return FALSE;
2188+}
2189+
2190+/**
2191 * Return NM "type=" string.
2192 */
2193 static const char*
2194-type_str(netdef_type type)
2195+type_str(const NetplanNetDefinition* def)
2196 {
2197+ const NetplanDefType type = def->type;
2198 switch (type) {
2199- case ND_ETHERNET:
2200+ case NETPLAN_DEF_TYPE_ETHERNET:
2201 return "ethernet";
2202- case ND_WIFI:
2203+ case NETPLAN_DEF_TYPE_MODEM:
2204+ if (modem_is_gsm(def))
2205+ return "gsm";
2206+ else
2207+ return "cdma";
2208+ case NETPLAN_DEF_TYPE_WIFI:
2209 return "wifi";
2210- case ND_BRIDGE:
2211+ case NETPLAN_DEF_TYPE_BRIDGE:
2212 return "bridge";
2213- case ND_BOND:
2214+ case NETPLAN_DEF_TYPE_BOND:
2215 return "bond";
2216- case ND_VLAN:
2217+ case NETPLAN_DEF_TYPE_VLAN:
2218 return "vlan";
2219- case ND_TUNNEL:
2220+ case NETPLAN_DEF_TYPE_TUNNEL:
2221 return "ip-tunnel";
2222 // LCOV_EXCL_START
2223 default:
2224@@ -100,14 +123,14 @@ type_str(netdef_type type)
2225 * Return NM wifi "mode=" string.
2226 */
2227 static const char*
2228-wifi_mode_str(wifi_mode mode)
2229+wifi_mode_str(const NetplanWifiMode mode)
2230 {
2231 switch (mode) {
2232- case WIFI_MODE_INFRASTRUCTURE:
2233+ case NETPLAN_WIFI_MODE_INFRASTRUCTURE:
2234 return "infrastructure";
2235- case WIFI_MODE_ADHOC:
2236+ case NETPLAN_WIFI_MODE_ADHOC:
2237 return "adhoc";
2238- case WIFI_MODE_AP:
2239+ case NETPLAN_WIFI_MODE_AP:
2240 return "ap";
2241 // LCOV_EXCL_START
2242 default:
2243@@ -116,8 +139,44 @@ wifi_mode_str(wifi_mode mode)
2244 }
2245 }
2246
2247+/**
2248+ * Return NM wifi "band=" string.
2249+ */
2250+static const char*
2251+wifi_band_str(const NetplanWifiBand band)
2252+{
2253+ switch (band) {
2254+ case NETPLAN_WIFI_BAND_5:
2255+ return "a";
2256+ case NETPLAN_WIFI_BAND_24:
2257+ return "bg";
2258+ // LCOV_EXCL_START
2259+ default:
2260+ g_assert_not_reached();
2261+ // LCOV_EXCL_STOP
2262+ }
2263+}
2264+
2265+/**
2266+ * Return NM addr-gen-mode string.
2267+ */
2268+static const char*
2269+addr_gen_mode_str(const NetplanAddrGenMode mode)
2270+{
2271+ switch (mode) {
2272+ case NETPLAN_ADDRGEN_EUI64:
2273+ return "0";
2274+ case NETPLAN_ADDRGEN_STABLEPRIVACY:
2275+ return "1";
2276+ // LCOV_EXCL_START
2277+ default:
2278+ g_assert_not_reached();
2279+ // LCOV_EXCL_STOP
2280+ }
2281+}
2282+
2283 static void
2284-write_search_domains(const net_definition* def, GString *s)
2285+write_search_domains(const NetplanNetDefinition* def, GString *s)
2286 {
2287 if (def->search_domains) {
2288 g_string_append(s, "dns-search=");
2289@@ -128,11 +187,11 @@ write_search_domains(const net_definition* def, GString *s)
2290 }
2291
2292 static void
2293-write_routes(const net_definition* def, GString *s, int family)
2294+write_routes(const NetplanNetDefinition* def, GString *s, int family)
2295 {
2296 if (def->routes != NULL) {
2297 for (unsigned i = 0, j = 1; i < def->routes->len; ++i) {
2298- ip_route *cur_route = g_array_index(def->routes, ip_route*, i);
2299+ const NetplanIPRoute *cur_route = g_array_index(def->routes, NetplanIPRoute*, i);
2300
2301 if (cur_route->family != family)
2302 continue;
2303@@ -147,7 +206,7 @@ write_routes(const net_definition* def, GString *s, int family)
2304 exit(1);
2305 }
2306
2307- if (cur_route->table != ROUTE_TABLE_UNSPEC) {
2308+ if (cur_route->table != NETPLAN_ROUTE_TABLE_UNSPEC) {
2309 g_fprintf(stderr, "ERROR: %s: NetworkManager does not support non-default routing tables\n", def->id);
2310 exit(1);
2311 }
2312@@ -164,7 +223,7 @@ write_routes(const net_definition* def, GString *s, int family)
2313
2314 g_string_append_printf(s, "route%d=%s,%s",
2315 j, cur_route->to, cur_route->via);
2316- if (cur_route->metric != METRIC_UNSPEC)
2317+ if (cur_route->metric != NETPLAN_METRIC_UNSPEC)
2318 g_string_append_printf(s, ",%d", cur_route->metric);
2319 g_string_append(s, "\n");
2320 j++;
2321@@ -173,7 +232,7 @@ write_routes(const net_definition* def, GString *s, int family)
2322 }
2323
2324 static void
2325-write_bond_parameters(const net_definition* def, GString *s)
2326+write_bond_parameters(const NetplanNetDefinition* def, GString *s)
2327 {
2328 GString* params = NULL;
2329
2330@@ -237,7 +296,7 @@ write_bond_parameters(const net_definition* def, GString *s)
2331 }
2332
2333 static void
2334-write_bridge_params(const net_definition* def, GString *s)
2335+write_bridge_params(const NetplanNetDefinition* def, GString *s)
2336 {
2337 GString* params = NULL;
2338
2339@@ -263,7 +322,7 @@ write_bridge_params(const net_definition* def, GString *s)
2340 }
2341
2342 static void
2343-write_tunnel_params(const net_definition* def, GString *s)
2344+write_tunnel_params(const NetplanNetDefinition* def, GString *s)
2345 {
2346 g_string_append(s, "\n[ip-tunnel]\n");
2347
2348@@ -278,23 +337,23 @@ write_tunnel_params(const net_definition* def, GString *s)
2349 }
2350
2351 static void
2352-write_dot1x_auth_parameters(const authentication_settings* auth, GString *s)
2353+write_dot1x_auth_parameters(const NetplanAuthenticationSettings* auth, GString *s)
2354 {
2355- if (auth->eap_method == EAP_NONE) {
2356+ if (auth->eap_method == NETPLAN_AUTH_EAP_NONE) {
2357 return;
2358 }
2359
2360 g_string_append_printf(s, "\n[802-1x]\n");
2361
2362 switch (auth->eap_method) {
2363- case EAP_NONE: break; // LCOV_EXCL_LINE
2364- case EAP_TLS:
2365+ case NETPLAN_AUTH_EAP_NONE: break; // LCOV_EXCL_LINE
2366+ case NETPLAN_AUTH_EAP_TLS:
2367 g_string_append(s, "eap=tls\n");
2368 break;
2369- case EAP_PEAP:
2370+ case NETPLAN_AUTH_EAP_PEAP:
2371 g_string_append(s, "eap=peap\n");
2372 break;
2373- case EAP_TTLS:
2374+ case NETPLAN_AUTH_EAP_TTLS:
2375 g_string_append(s, "eap=ttls\n");
2376 break;
2377 }
2378@@ -305,7 +364,7 @@ write_dot1x_auth_parameters(const authentication_settings* auth, GString *s)
2379 if (auth->anonymous_identity) {
2380 g_string_append_printf(s, "anonymous-identity=%s\n", auth->anonymous_identity);
2381 }
2382- if (auth->password && auth->key_management != KEY_MANAGEMENT_WPA_PSK) {
2383+ if (auth->password && auth->key_management != NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK) {
2384 g_string_append_printf(s, "password=%s\n", auth->password);
2385 }
2386 if (auth->ca_certificate) {
2387@@ -320,29 +379,33 @@ write_dot1x_auth_parameters(const authentication_settings* auth, GString *s)
2388 if (auth->client_key_password) {
2389 g_string_append_printf(s, "private-key-password=%s\n", auth->client_key_password);
2390 }
2391+ if (auth->phase2_auth) {
2392+ g_string_append_printf(s, "phase2-auth=%s\n", auth->phase2_auth);
2393+ }
2394+
2395 }
2396
2397 static void
2398-write_wifi_auth_parameters(const authentication_settings* auth, GString *s)
2399+write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GString *s)
2400 {
2401- if (auth->key_management == KEY_MANAGEMENT_NONE) {
2402+ if (auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_NONE) {
2403 return;
2404 }
2405
2406 g_string_append(s, "\n[wifi-security]\n");
2407
2408 switch (auth->key_management) {
2409- case KEY_MANAGEMENT_NONE: break; // LCOV_EXCL_LINE
2410- case KEY_MANAGEMENT_WPA_PSK:
2411+ case NETPLAN_AUTH_KEY_MANAGEMENT_NONE: break; // LCOV_EXCL_LINE
2412+ case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK:
2413 g_string_append(s, "key-mgmt=wpa-psk\n");
2414 if (auth->password) {
2415 g_string_append_printf(s, "psk=%s\n", auth->password);
2416 }
2417 break;
2418- case KEY_MANAGEMENT_WPA_EAP:
2419+ case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP:
2420 g_string_append(s, "key-mgmt=wpa-eap\n");
2421 break;
2422- case KEY_MANAGEMENT_8021X:
2423+ case NETPLAN_AUTH_KEY_MANAGEMENT_8021X:
2424 g_string_append(s, "key-mgmt=ieee8021x\n");
2425 break;
2426 }
2427@@ -351,7 +414,7 @@ write_wifi_auth_parameters(const authentication_settings* auth, GString *s)
2428 }
2429
2430 static void
2431-maybe_generate_uuid(net_definition* def)
2432+maybe_generate_uuid(NetplanNetDefinition* def)
2433 {
2434 if (uuid_is_null(def->uuid))
2435 uuid_generate(def->uuid);
2436@@ -359,32 +422,37 @@ maybe_generate_uuid(net_definition* def)
2437
2438 /**
2439 * Generate NetworkManager configuration in @rootdir/run/NetworkManager/ for a
2440- * particular net_definition and wifi_access_point, as NM requires a separate
2441+ * particular NetplanNetDefinition and NetplanWifiAccessPoint, as NM requires a separate
2442 * connection file for each SSID.
2443- * @def: The net_definition for which to create a connection
2444+ * @def: The NetplanNetDefinition for which to create a connection
2445 * @rootdir: If not %NULL, generate configuration in this root directory
2446 * (useful for testing).
2447 * @ap: The access point for which to create a connection. Must be %NULL for
2448 * non-wifi types.
2449 */
2450 static void
2451-write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_access_point* ap)
2452+write_nm_conf_access_point(NetplanNetDefinition* def, const char* rootdir, const NetplanWifiAccessPoint* ap)
2453 {
2454 GString *s = NULL;
2455 g_autofree char* conf_path = NULL;
2456 mode_t orig_umask;
2457 char uuidstr[37];
2458
2459- if (def->type == ND_WIFI)
2460+ if (def->type == NETPLAN_DEF_TYPE_WIFI)
2461 g_assert(ap);
2462 else
2463 g_assert(ap == NULL);
2464
2465+ if (def->type == NETPLAN_DEF_TYPE_VLAN && def->sriov_vlan_filter) {
2466+ g_debug("%s is defined as a hardware SR-IOV filtered VLAN, postponing creation", def->id);
2467+ return;
2468+ }
2469+
2470 s = g_string_new(NULL);
2471 g_string_append_printf(s, "[connection]\nid=netplan-%s", def->id);
2472 if (ap)
2473 g_string_append_printf(s, "-%s", ap->ssid);
2474- g_string_append_printf(s, "\ntype=%s\n", type_str(def->type));
2475+ g_string_append_printf(s, "\ntype=%s\n", type_str(def));
2476
2477 /* VLAN devices refer to us as their parent; if our ID is not a name but we
2478 * have matches, parent= must be the connection UUID, so put it into the
2479@@ -395,7 +463,7 @@ write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_
2480 g_string_append_printf(s, "uuid=%s\n", uuidstr);
2481 }
2482
2483- if (def->type < ND_VIRTUAL) {
2484+ if (def->type < NETPLAN_DEF_TYPE_VIRTUAL) {
2485 /* physical (existing) devices use matching; driver matching is not
2486 * supported, MAC matching is done below (different keyfile section),
2487 * so only match names here */
2488@@ -416,9 +484,43 @@ write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_
2489 /* virtual (created) devices set a name */
2490 g_string_append_printf(s, "interface-name=%s\n", def->id);
2491
2492- if (def->type == ND_BRIDGE)
2493+ if (def->type == NETPLAN_DEF_TYPE_BRIDGE)
2494 write_bridge_params(def, s);
2495 }
2496+ if (def->type == NETPLAN_DEF_TYPE_MODEM) {
2497+ if (modem_is_gsm(def))
2498+ g_string_append_printf(s, "\n[gsm]\n");
2499+ else
2500+ g_string_append_printf(s, "\n[cdma]\n");
2501+
2502+ /* Use NetworkManager's auto configuration feature if no APN, username, or password is specified */
2503+ if (def->modem_params.auto_config || (!def->modem_params.apn &&
2504+ !def->modem_params.username && !def->modem_params.password)) {
2505+ g_string_append_printf(s, "auto-config=true\n");
2506+ } else {
2507+ if (def->modem_params.apn)
2508+ g_string_append_printf(s, "apn=%s\n", def->modem_params.apn);
2509+ if (def->modem_params.password)
2510+ g_string_append_printf(s, "password=%s\n", def->modem_params.password);
2511+ if (def->modem_params.username)
2512+ g_string_append_printf(s, "username=%s\n", def->modem_params.username);
2513+ }
2514+
2515+ if (def->modem_params.device_id)
2516+ g_string_append_printf(s, "device-id=%s\n", def->modem_params.device_id);
2517+ if (def->mtubytes)
2518+ g_string_append_printf(s, "mtu=%u\n", def->mtubytes);
2519+ if (def->modem_params.network_id)
2520+ g_string_append_printf(s, "network-id=%s\n", def->modem_params.network_id);
2521+ if (def->modem_params.number)
2522+ g_string_append_printf(s, "number=%s\n", def->modem_params.number);
2523+ if (def->modem_params.pin)
2524+ g_string_append_printf(s, "pin=%s\n", def->modem_params.pin);
2525+ if (def->modem_params.sim_id)
2526+ g_string_append_printf(s, "sim-id=%s\n", def->modem_params.sim_id);
2527+ if (def->modem_params.sim_operator_id)
2528+ g_string_append_printf(s, "sim-operator-id=%s\n", def->modem_params.sim_operator_id);
2529+ }
2530 if (def->bridge) {
2531 g_string_append_printf(s, "slave-type=bridge\nmaster=%s\n", def->bridge);
2532
2533@@ -437,7 +539,7 @@ write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_
2534 exit(1);
2535 }
2536
2537- if (def->type < ND_VIRTUAL) {
2538+ if (def->type < NETPLAN_DEF_TYPE_VIRTUAL) {
2539 GString *link_str = NULL;
2540
2541 link_str = g_string_new(NULL);
2542@@ -453,11 +555,16 @@ write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_
2543 if (def->mtubytes) {
2544 g_string_append_printf(link_str, "mtu=%d\n", def->mtubytes);
2545 }
2546+ if (def->wowlan && def->wowlan > NETPLAN_WIFI_WOWLAN_DEFAULT)
2547+ g_string_append_printf(link_str, "wake-on-wlan=%u\n", def->wowlan);
2548
2549 if (link_str->len > 0) {
2550 switch (def->type) {
2551- case ND_WIFI:
2552+ case NETPLAN_DEF_TYPE_WIFI:
2553 g_string_append_printf(s, "\n[802-11-wireless]\n%s", link_str->str); break;
2554+ case NETPLAN_DEF_TYPE_MODEM:
2555+ /* Avoid adding an [ethernet] section into the [gsm/cdma] description. */
2556+ break;
2557 default:
2558 g_string_append_printf(s, "\n[802-3-ethernet]\n%s", link_str->str); break;
2559 }
2560@@ -483,7 +590,7 @@ write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_
2561 g_string_free(link_str, TRUE);
2562 }
2563
2564- if (def->type == ND_VLAN) {
2565+ if (def->type == NETPLAN_DEF_TYPE_VLAN) {
2566 g_assert(def->vlan_id < G_MAXUINT);
2567 g_assert(def->vlan_link != NULL);
2568 g_string_append_printf(s, "\n[vlan]\nid=%u\nparent=", def->vlan_id);
2569@@ -499,22 +606,22 @@ write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_
2570 }
2571 }
2572
2573- if (def->type == ND_BOND)
2574+ if (def->type == NETPLAN_DEF_TYPE_BOND)
2575 write_bond_parameters(def, s);
2576
2577- if (def->type == ND_TUNNEL)
2578+ if (def->type == NETPLAN_DEF_TYPE_TUNNEL)
2579 write_tunnel_params(def, s);
2580
2581 g_string_append(s, "\n[ipv4]\n");
2582
2583- if (ap && ap->mode == WIFI_MODE_AP)
2584+ if (ap && ap->mode == NETPLAN_WIFI_MODE_AP)
2585 g_string_append(s, "method=shared\n");
2586 else if (def->dhcp4)
2587 g_string_append(s, "method=auto\n");
2588 else if (def->ip4_addresses)
2589 /* This requires adding at least one address (done below) */
2590 g_string_append(s, "method=manual\n");
2591- else if (def->type == ND_TUNNEL)
2592+ else if (def->type == NETPLAN_DEF_TYPE_TUNNEL)
2593 /* sit tunnels will not start in link-local apparently */
2594 g_string_append(s, "method=disabled\n");
2595 else
2596@@ -544,15 +651,18 @@ write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_
2597 g_string_append(s, "never-default=true\n");
2598 }
2599
2600- if (def->dhcp4 && def->dhcp4_overrides.metric != METRIC_UNSPEC)
2601+ if (def->dhcp4 && def->dhcp4_overrides.metric != NETPLAN_METRIC_UNSPEC)
2602 g_string_append_printf(s, "route-metric=%u\n", def->dhcp4_overrides.metric);
2603
2604- if (def->dhcp6 || def->ip6_addresses || def->gateway6 || def->ip6_nameservers) {
2605+ if (def->dhcp6 || def->ip6_addresses || def->gateway6 || def->ip6_nameservers || def->ip6_addr_gen_mode) {
2606 g_string_append(s, "\n[ipv6]\n");
2607 g_string_append(s, def->dhcp6 ? "method=auto\n" : "method=manual\n");
2608 if (def->ip6_addresses)
2609 for (unsigned i = 0; i < def->ip6_addresses->len; ++i)
2610 g_string_append_printf(s, "address%i=%s\n", i+1, g_array_index(def->ip6_addresses, char*, i));
2611+ if (def->ip6_addr_gen_mode) {
2612+ g_string_append_printf(s, "addr-gen-mode=%s\n", addr_gen_mode_str(def->ip6_addr_gen_mode));
2613+ }
2614 if (def->ip6_privacy)
2615 g_string_append(s, "ip6-privacy=2\n");
2616 if (def->gateway6)
2617@@ -575,7 +685,7 @@ write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_
2618 g_string_append(s, "never-default=true\n");
2619 }
2620
2621- if (def->dhcp6_overrides.metric != METRIC_UNSPEC)
2622+ if (def->dhcp6_overrides.metric != NETPLAN_METRIC_UNSPEC)
2623 g_string_append_printf(s, "route-metric=%u\n", def->dhcp6_overrides.metric);
2624 }
2625 else {
2626@@ -587,6 +697,21 @@ write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_
2627 conf_path = g_strjoin(NULL, "run/NetworkManager/system-connections/netplan-", def->id, "-", escaped_ssid, ".nmconnection", NULL);
2628
2629 g_string_append_printf(s, "\n[wifi]\nssid=%s\nmode=%s\n", ap->ssid, wifi_mode_str(ap->mode));
2630+ if (ap->bssid) {
2631+ g_string_append_printf(s, "bssid=%s\n", ap->bssid);
2632+ }
2633+ if (ap->band == NETPLAN_WIFI_BAND_5 || ap->band == NETPLAN_WIFI_BAND_24) {
2634+ g_string_append_printf(s, "band=%s\n", wifi_band_str(ap->band));
2635+ /* Channel is only unambiguous, if band is set. */
2636+ if (ap->channel) {
2637+ /* Validate WiFi channel */
2638+ if (ap->band == NETPLAN_WIFI_BAND_5)
2639+ wifi_get_freq5(ap->channel);
2640+ else
2641+ wifi_get_freq24(ap->channel);
2642+ g_string_append_printf(s, "channel=%u\n", ap->channel);
2643+ }
2644+ }
2645 if (ap->has_auth) {
2646 write_wifi_auth_parameters(&ap->auth, s);
2647 }
2648@@ -605,14 +730,14 @@ write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_
2649
2650 /**
2651 * Generate NetworkManager configuration in @rootdir/run/NetworkManager/ for a
2652- * particular net_definition.
2653+ * particular NetplanNetDefinition.
2654 * @rootdir: If not %NULL, generate configuration in this root directory
2655 * (useful for testing).
2656 */
2657 void
2658-write_nm_conf(net_definition* def, const char* rootdir)
2659+write_nm_conf(NetplanNetDefinition* def, const char* rootdir)
2660 {
2661- if (def->backend != BACKEND_NM) {
2662+ if (def->backend != NETPLAN_BACKEND_NM) {
2663 g_debug("NetworkManager: definition %s is not for us (backend %i)", def->id, def->backend);
2664 return;
2665 }
2666@@ -623,10 +748,10 @@ write_nm_conf(net_definition* def, const char* rootdir)
2667 }
2668
2669 /* for wifi we need to create a separate connection file for every SSID */
2670- if (def->type == ND_WIFI) {
2671+ if (def->type == NETPLAN_DEF_TYPE_WIFI) {
2672 GHashTableIter iter;
2673 gpointer key;
2674- wifi_access_point* ap;
2675+ const NetplanWifiAccessPoint* ap;
2676 g_assert(def->access_points);
2677 g_hash_table_iter_init(&iter, def->access_points);
2678 while (g_hash_table_iter_next(&iter, &key, (gpointer) &ap))
2679@@ -640,9 +765,9 @@ write_nm_conf(net_definition* def, const char* rootdir)
2680 static void
2681 nd_append_non_nm_ids(gpointer data, gpointer str)
2682 {
2683- net_definition* nd = data;
2684+ const NetplanNetDefinition* nd = data;
2685
2686- if (nd->backend != BACKEND_NM) {
2687+ if (nd->backend != NETPLAN_BACKEND_NM) {
2688 if (nd->match.driver) {
2689 /* NM cannot match on drivers, so ignore these via udev rules */
2690 if (!udev_rules)
2691diff --git a/src/nm.h b/src/nm.h
2692index f25c506..9f8f9ca 100644
2693--- a/src/nm.h
2694+++ b/src/nm.h
2695@@ -19,6 +19,6 @@
2696
2697 #include "parse.h"
2698
2699-void write_nm_conf(net_definition* def, const char* rootdir);
2700+void write_nm_conf(NetplanNetDefinition* def, const char* rootdir);
2701 void write_nm_conf_finish(const char* rootdir);
2702 void cleanup_nm_conf(const char* rootdir);
2703diff --git a/src/parse.c b/src/parse.c
2704index c72813f..c1dc175 100644
2705--- a/src/parse.c
2706+++ b/src/parse.c
2707@@ -30,27 +30,28 @@
2708 #include "error.h"
2709 #include "validation.h"
2710
2711-/* convenience macro to put the offset of a net_definition field into "void* data" */
2712-#define netdef_offset(field) GUINT_TO_POINTER(offsetof(net_definition, field))
2713-#define route_offset(field) GUINT_TO_POINTER(offsetof(ip_route, field))
2714-#define ip_rule_offset(field) GUINT_TO_POINTER(offsetof(ip_rule, field))
2715-#define auth_offset(field) GUINT_TO_POINTER(offsetof(authentication_settings, field))
2716+/* convenience macro to put the offset of a NetplanNetDefinition field into "void* data" */
2717+#define netdef_offset(field) GUINT_TO_POINTER(offsetof(NetplanNetDefinition, field))
2718+#define route_offset(field) GUINT_TO_POINTER(offsetof(NetplanIPRoute, field))
2719+#define ip_rule_offset(field) GUINT_TO_POINTER(offsetof(NetplanIPRule, field))
2720+#define auth_offset(field) GUINT_TO_POINTER(offsetof(NetplanAuthenticationSettings, field))
2721+#define access_point_offset(field) GUINT_TO_POINTER(offsetof(NetplanWifiAccessPoint, field))
2722
2723-/* net_definition that is currently being processed */
2724-net_definition* cur_netdef;
2725+/* NetplanNetDefinition that is currently being processed */
2726+static NetplanNetDefinition* cur_netdef;
2727
2728-/* wifi AP that is currently being processed */
2729-wifi_access_point* cur_access_point;
2730+/* NetplanWifiAccessPoint that is currently being processed */
2731+static NetplanWifiAccessPoint* cur_access_point;
2732
2733 /* authentication options that are currently being processed */
2734-authentication_settings* cur_auth;
2735+static NetplanAuthenticationSettings* cur_auth;
2736
2737-ip_route* cur_route;
2738-ip_rule* cur_ip_rule;
2739+static NetplanIPRoute* cur_route;
2740+static NetplanIPRule* cur_ip_rule;
2741
2742-netdef_backend backend_global, backend_cur_type;
2743+static NetplanBackend backend_global, backend_cur_type;
2744
2745-/* Global ID → net_definition* map for all parsed config files */
2746+/* Global ID → NetplanNetDefinition* map for all parsed config files */
2747 GHashTable* netdefs;
2748
2749 /* Contains the same objects as 'netdefs' but ordered by dependency */
2750@@ -59,7 +60,7 @@ GList* netdefs_ordered;
2751 /* Set of IDs in currently parsed YAML file, for being able to detect
2752 * "duplicate ID within one file" vs. allowing a drop-in to override/amend an
2753 * existing definition */
2754-GHashTable* ids_in_file;
2755+static GHashTable* ids_in_file;
2756
2757 /**
2758 * Load YAML file name into a yaml_document_t.
2759@@ -87,6 +88,7 @@ load_yaml(const char* yaml, yaml_document_t* doc, GError** error)
2760 ret = parser_error(&parser, yaml, error);
2761 }
2762
2763+ yaml_parser_delete(&parser);
2764 fclose(fyaml);
2765 return ret;
2766 }
2767@@ -136,13 +138,13 @@ scalar(const yaml_node_t* node)
2768 static void
2769 add_missing_node(const yaml_node_t* node)
2770 {
2771- missing_node* missing;
2772+ NetplanMissingNode* missing;
2773
2774 /* Let's capture the current netdef we were playing with along with the
2775 * actual yaml_node_t that errors (that is an identifier not previously
2776 * seen by the compiler). We can use it later to write an sensible error
2777 * message and point the user in the right direction. */
2778- missing = g_new0(missing_node, 1);
2779+ missing = g_new0(NetplanMissingNode, 1);
2780 missing->netdef_id = cur_netdef->id;
2781 missing->node = node;
2782
2783@@ -247,24 +249,88 @@ process_mapping(yaml_document_t* doc, yaml_node_t* node, const mapping_entry_han
2784 return TRUE;
2785 }
2786
2787+/*************************************************************
2788+ * Generic helper functions to extract data from scalar nodes.
2789+ *************************************************************/
2790+
2791 /**
2792- * Generic handler for setting a cur_netdef string field from a scalar node
2793- * @data: offset into net_definition where the const char* field to write is
2794+ * Handler for setting a guint field from a scalar node, inside a given struct
2795+ * @entryptr: pointer to the begining of the to-be-modified data structure
2796+ * @data: offset into entryptr struct where the guint field to write is located
2797+ */
2798+static gboolean
2799+handle_generic_guint(yaml_document_t* doc, yaml_node_t* node, const void* entryptr, const void* data, GError** error)
2800+{
2801+ g_assert(entryptr);
2802+ guint offset = GPOINTER_TO_UINT(data);
2803+ guint64 v;
2804+ gchar* endptr;
2805+
2806+ v = g_ascii_strtoull(scalar(node), &endptr, 10);
2807+ if (*endptr != '\0' || v > G_MAXUINT)
2808+ return yaml_error(node, error, "invalid unsigned int value '%s'", scalar(node));
2809+
2810+ *((guint*) ((void*) entryptr + offset)) = (guint) v;
2811+ return TRUE;
2812+}
2813+
2814+/**
2815+ * Handler for setting a string field from a scalar node, inside a given struct
2816+ * @entryptr: pointer to the beginning of the to-be-modified data structure
2817+ * @data: offset into entryptr struct where the const char* field to write is
2818 * located
2819 */
2820 static gboolean
2821-handle_netdef_str(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
2822+handle_generic_str(yaml_document_t* doc, yaml_node_t* node, void* entryptr, const void* data, GError** error)
2823 {
2824+ g_assert(entryptr);
2825 guint offset = GPOINTER_TO_UINT(data);
2826- char** dest = (char**) ((void*) cur_netdef + offset);
2827+ char** dest = (char**) ((void*) entryptr + offset);
2828 g_free(*dest);
2829 *dest = g_strdup(scalar(node));
2830 return TRUE;
2831 }
2832
2833+/*
2834+ * Handler for setting a MAC address field from a scalar node, inside a given struct
2835+ * @entryptr: pointer to the beginning of the to-be-modified data structure
2836+ * @data: offset into entryptr struct where the const char* field to write is
2837+ * located
2838+ */
2839+static gboolean
2840+handle_generic_mac(yaml_document_t* doc, yaml_node_t* node, void* entryptr, const void* data, GError** error)
2841+{
2842+ g_assert(entryptr);
2843+ static regex_t re;
2844+ static gboolean re_inited = FALSE;
2845+
2846+ g_assert(node->type == YAML_SCALAR_NODE);
2847+
2848+ if (!re_inited) {
2849+ g_assert(regcomp(&re, "^[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]$", REG_EXTENDED|REG_NOSUB) == 0);
2850+ re_inited = TRUE;
2851+ }
2852+
2853+ if (regexec(&re, scalar(node), 0, NULL, 0) != 0)
2854+ return yaml_error(node, error, "Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX", scalar(node));
2855+
2856+ return handle_generic_str(doc, node, entryptr, data, error);
2857+}
2858+
2859+/**
2860+ * Generic handler for setting a cur_netdef string field from a scalar node
2861+ * @data: offset into NetplanNetDefinition where the const char* field to write is
2862+ * located
2863+ */
2864+static gboolean
2865+handle_netdef_str(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
2866+{
2867+ return handle_generic_str(doc, node, cur_netdef, data, error);
2868+}
2869+
2870 /**
2871 * Generic handler for setting a cur_netdef ID/iface name field from a scalar node
2872- * @data: offset into net_definition where the const char* field to write is
2873+ * @data: offset into NetplanNetDefinition where the const char* field to write is
2874 * located
2875 */
2876 static gboolean
2877@@ -278,20 +344,20 @@ handle_netdef_id(yaml_document_t* doc, yaml_node_t* node, const void* data, GErr
2878 /**
2879 * Generic handler for setting a cur_netdef ID/iface name field referring to an
2880 * existing ID from a scalar node
2881- * @data: offset into net_definition where the net_definition* field to write is
2882+ * @data: offset into NetplanNetDefinition where the NetplanNetDefinition* field to write is
2883 * located
2884 */
2885 static gboolean
2886 handle_netdef_id_ref(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
2887 {
2888 guint offset = GPOINTER_TO_UINT(data);
2889- net_definition* ref = NULL;
2890+ NetplanNetDefinition* ref = NULL;
2891
2892 ref = g_hash_table_lookup(netdefs, scalar(node));
2893 if (!ref) {
2894 add_missing_node(node);
2895 } else {
2896- *((net_definition**) ((void*) cur_netdef + offset)) = ref;
2897+ *((NetplanNetDefinition**) ((void*) cur_netdef + offset)) = ref;
2898 }
2899 return TRUE;
2900 }
2901@@ -299,31 +365,18 @@ handle_netdef_id_ref(yaml_document_t* doc, yaml_node_t* node, const void* data,
2902
2903 /**
2904 * Generic handler for setting a cur_netdef MAC address field from a scalar node
2905- * @data: offset into net_definition where the const char* field to write is
2906+ * @data: offset into NetplanNetDefinition where the const char* field to write is
2907 * located
2908 */
2909 static gboolean
2910 handle_netdef_mac(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
2911 {
2912- static regex_t re;
2913- static gboolean re_inited = FALSE;
2914-
2915- g_assert(node->type == YAML_SCALAR_NODE);
2916-
2917- if (!re_inited) {
2918- g_assert(regcomp(&re, "^[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]$", REG_EXTENDED|REG_NOSUB) == 0);
2919- re_inited = TRUE;
2920- }
2921-
2922- if (regexec(&re, scalar(node), 0, NULL, 0) != 0)
2923- return yaml_error(node, error, "Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX", scalar(node));
2924-
2925- return handle_netdef_str(doc, node, data, error);
2926+ return handle_generic_mac(doc, node, cur_netdef, data, error);
2927 }
2928
2929 /**
2930 * Generic handler for setting a cur_netdef gboolean field from a scalar node
2931- * @data: offset into net_definition where the gboolean field to write is located
2932+ * @data: offset into NetplanNetDefinition where the gboolean field to write is located
2933 */
2934 static gboolean
2935 handle_netdef_bool(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
2936@@ -350,21 +403,12 @@ handle_netdef_bool(yaml_document_t* doc, yaml_node_t* node, const void* data, GE
2937
2938 /**
2939 * Generic handler for setting a cur_netdef guint field from a scalar node
2940- * @data: offset into net_definition where the guint field to write is located
2941+ * @data: offset into NetplanNetDefinition where the guint field to write is located
2942 */
2943 static gboolean
2944 handle_netdef_guint(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
2945 {
2946- guint offset = GPOINTER_TO_UINT(data);
2947- guint64 v;
2948- gchar* endptr;
2949-
2950- v = g_ascii_strtoull(scalar(node), &endptr, 10);
2951- if (*endptr != '\0' || v > G_MAXUINT)
2952- return yaml_error(node, error, "invalid unsigned int value '%s'", scalar(node));
2953-
2954- *((guint*) ((void*) cur_netdef + offset)) = (guint) v;
2955- return TRUE;
2956+ return handle_generic_guint(doc, node, cur_netdef, data, error);
2957 }
2958
2959 static gboolean
2960@@ -427,12 +471,25 @@ handle_netdef_ip6(yaml_document_t* doc, yaml_node_t* node, const void* data, GEr
2961 return TRUE;
2962 }
2963
2964+static gboolean
2965+handle_netdef_addrgen(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
2966+{
2967+ g_assert(cur_netdef);
2968+ if (strcmp(scalar(node), "eui64") == 0)
2969+ cur_netdef->ip6_addr_gen_mode = NETPLAN_ADDRGEN_EUI64;
2970+ else if (strcmp(scalar(node), "stable-privacy") == 0)
2971+ cur_netdef->ip6_addr_gen_mode = NETPLAN_ADDRGEN_STABLEPRIVACY;
2972+ else
2973+ return yaml_error(node, error, "unknown ipv6-address-generation '%s'", scalar(node));
2974+ return TRUE;
2975+}
2976+
2977
2978 /****************************************************
2979 * Grammar and handlers for network config "match" entry
2980 ****************************************************/
2981
2982-const mapping_entry_handler match_handlers[] = {
2983+static const mapping_entry_handler match_handlers[] = {
2984 {"driver", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(match.driver)},
2985 {"macaddress", YAML_SCALAR_NODE, handle_netdef_mac, NULL, netdef_offset(match.mac)},
2986 {"name", YAML_SCALAR_NODE, handle_netdef_id, NULL, netdef_offset(match.original_name)},
2987@@ -459,13 +516,13 @@ handle_auth_key_management(yaml_document_t* doc, yaml_node_t* node, const void*
2988 {
2989 g_assert(cur_auth);
2990 if (strcmp(scalar(node), "none") == 0)
2991- cur_auth->key_management = KEY_MANAGEMENT_NONE;
2992+ cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_NONE;
2993 else if (strcmp(scalar(node), "psk") == 0)
2994- cur_auth->key_management = KEY_MANAGEMENT_WPA_PSK;
2995+ cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK;
2996 else if (strcmp(scalar(node), "eap") == 0)
2997- cur_auth->key_management = KEY_MANAGEMENT_WPA_EAP;
2998+ cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP;
2999 else if (strcmp(scalar(node), "802.1x") == 0)
3000- cur_auth->key_management = KEY_MANAGEMENT_8021X;
3001+ cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_8021X;
3002 else
3003 return yaml_error(node, error, "unknown key management type '%s'", scalar(node));
3004 return TRUE;
3005@@ -476,17 +533,17 @@ handle_auth_method(yaml_document_t* doc, yaml_node_t* node, const void* _, GErro
3006 {
3007 g_assert(cur_auth);
3008 if (strcmp(scalar(node), "tls") == 0)
3009- cur_auth->eap_method = EAP_TLS;
3010+ cur_auth->eap_method = NETPLAN_AUTH_EAP_TLS;
3011 else if (strcmp(scalar(node), "peap") == 0)
3012- cur_auth->eap_method = EAP_PEAP;
3013+ cur_auth->eap_method = NETPLAN_AUTH_EAP_PEAP;
3014 else if (strcmp(scalar(node), "ttls") == 0)
3015- cur_auth->eap_method = EAP_TTLS;
3016+ cur_auth->eap_method = NETPLAN_AUTH_EAP_TTLS;
3017 else
3018 return yaml_error(node, error, "unknown EAP method '%s'", scalar(node));
3019 return TRUE;
3020 }
3021
3022-const mapping_entry_handler auth_handlers[] = {
3023+static const mapping_entry_handler auth_handlers[] = {
3024 {"key-management", YAML_SCALAR_NODE, handle_auth_key_management},
3025 {"method", YAML_SCALAR_NODE, handle_auth_method},
3026 {"identity", YAML_SCALAR_NODE, handle_auth_str, NULL, auth_offset(identity)},
3027@@ -496,6 +553,7 @@ const mapping_entry_handler auth_handlers[] = {
3028 {"client-certificate", YAML_SCALAR_NODE, handle_auth_str, NULL, auth_offset(client_certificate)},
3029 {"client-key", YAML_SCALAR_NODE, handle_auth_str, NULL, auth_offset(client_key)},
3030 {"client-key-password", YAML_SCALAR_NODE, handle_auth_str, NULL, auth_offset(client_key_password)},
3031+ {"phase2-auth", YAML_SCALAR_NODE, handle_auth_str, NULL, auth_offset(phase2_auth)},
3032 {NULL}
3033 };
3034
3035@@ -503,15 +561,27 @@ const mapping_entry_handler auth_handlers[] = {
3036 * Grammar and handlers for network device definition
3037 ****************************************************/
3038
3039-static netdef_backend
3040-get_default_backend_for_type(netdef_type type)
3041+static NetplanBackend
3042+get_default_backend_for_type(NetplanDefType type)
3043 {
3044- if (backend_global != BACKEND_NONE)
3045+ if (backend_global != NETPLAN_BACKEND_NONE)
3046 return backend_global;
3047
3048 /* networkd can handle all device types at the moment, so nothing
3049 * type-specific */
3050- return BACKEND_NETWORKD;
3051+ return NETPLAN_BACKEND_NETWORKD;
3052+}
3053+
3054+static gboolean
3055+handle_access_point_guint(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
3056+{
3057+ return handle_generic_guint(doc, node, cur_access_point, data, error);
3058+}
3059+
3060+static gboolean
3061+handle_access_point_mac(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
3062+{
3063+ return handle_generic_mac(doc, node, cur_access_point, data, error);
3064 }
3065
3066 static gboolean
3067@@ -520,7 +590,7 @@ handle_access_point_password(yaml_document_t* doc, yaml_node_t* node, const void
3068 g_assert(cur_access_point);
3069 /* shortcut for WPA-PSK */
3070 cur_access_point->has_auth = TRUE;
3071- cur_access_point->auth.key_management = KEY_MANAGEMENT_WPA_PSK;
3072+ cur_access_point->auth.key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK;
3073 g_free(cur_access_point->auth.password);
3074 cur_access_point->auth.password = g_strdup(scalar(node));
3075 return TRUE;
3076@@ -546,17 +616,33 @@ handle_access_point_mode(yaml_document_t* doc, yaml_node_t* node, const void* _,
3077 {
3078 g_assert(cur_access_point);
3079 if (strcmp(scalar(node), "infrastructure") == 0)
3080- cur_access_point->mode = WIFI_MODE_INFRASTRUCTURE;
3081+ cur_access_point->mode = NETPLAN_WIFI_MODE_INFRASTRUCTURE;
3082 else if (strcmp(scalar(node), "adhoc") == 0)
3083- cur_access_point->mode = WIFI_MODE_ADHOC;
3084+ cur_access_point->mode = NETPLAN_WIFI_MODE_ADHOC;
3085 else if (strcmp(scalar(node), "ap") == 0)
3086- cur_access_point->mode = WIFI_MODE_AP;
3087+ cur_access_point->mode = NETPLAN_WIFI_MODE_AP;
3088 else
3089 return yaml_error(node, error, "unknown wifi mode '%s'", scalar(node));
3090 return TRUE;
3091 }
3092
3093-const mapping_entry_handler wifi_access_point_handlers[] = {
3094+static gboolean
3095+handle_access_point_band(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
3096+{
3097+ g_assert(cur_access_point);
3098+ if (strcmp(scalar(node), "5GHz") == 0 || strcmp(scalar(node), "5G") == 0)
3099+ cur_access_point->band = NETPLAN_WIFI_BAND_5;
3100+ else if (strcmp(scalar(node), "2.4GHz") == 0 || strcmp(scalar(node), "2.4G") == 0)
3101+ cur_access_point->band = NETPLAN_WIFI_BAND_24;
3102+ else
3103+ return yaml_error(node, error, "unknown wifi band '%s'", scalar(node));
3104+ return TRUE;
3105+}
3106+
3107+static const mapping_entry_handler wifi_access_point_handlers[] = {
3108+ {"band", YAML_SCALAR_NODE, handle_access_point_band},
3109+ {"bssid", YAML_SCALAR_NODE, handle_access_point_mac, NULL, access_point_offset(bssid)},
3110+ {"channel", YAML_SCALAR_NODE, handle_access_point_guint, NULL, access_point_offset(channel)},
3111 {"mode", YAML_SCALAR_NODE, handle_access_point_mode},
3112 {"password", YAML_SCALAR_NODE, handle_access_point_password},
3113 {"auth", YAML_MAPPING_NODE, handle_access_point_auth},
3114@@ -567,12 +653,12 @@ const mapping_entry_handler wifi_access_point_handlers[] = {
3115 * Parse scalar node's string into a netdef_backend.
3116 */
3117 static gboolean
3118-parse_renderer(yaml_node_t* node, netdef_backend* backend, GError** error)
3119+parse_renderer(yaml_node_t* node, NetplanBackend* backend, GError** error)
3120 {
3121 if (strcmp(scalar(node), "networkd") == 0)
3122- *backend = BACKEND_NETWORKD;
3123+ *backend = NETPLAN_BACKEND_NETWORKD;
3124 else if (strcmp(scalar(node), "NetworkManager") == 0)
3125- *backend = BACKEND_NM;
3126+ *backend = NETPLAN_BACKEND_NM;
3127 else
3128 return yaml_error(node, error, "unknown renderer '%s'", scalar(node));
3129 return TRUE;
3130@@ -581,6 +667,13 @@ parse_renderer(yaml_node_t* node, netdef_backend* backend, GError** error)
3131 static gboolean
3132 handle_netdef_renderer(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
3133 {
3134+ if (cur_netdef->type == NETPLAN_DEF_TYPE_VLAN) {
3135+ if (strcmp(scalar(node), "sriov") == 0) {
3136+ cur_netdef->sriov_vlan_filter = TRUE;
3137+ return TRUE;
3138+ }
3139+ }
3140+
3141 return parse_renderer(node, &cur_netdef->backend, error);
3142 }
3143
3144@@ -591,12 +684,12 @@ handle_accept_ra(yaml_document_t* doc, yaml_node_t* node, const void* data, GErr
3145 g_ascii_strcasecmp(scalar(node), "on") == 0 ||
3146 g_ascii_strcasecmp(scalar(node), "yes") == 0 ||
3147 g_ascii_strcasecmp(scalar(node), "y") == 0)
3148- cur_netdef->accept_ra = ACCEPT_RA_ENABLED;
3149+ cur_netdef->accept_ra = NETPLAN_RA_MODE_ENABLED;
3150 else if (g_ascii_strcasecmp(scalar(node), "false") == 0 ||
3151 g_ascii_strcasecmp(scalar(node), "off") == 0 ||
3152 g_ascii_strcasecmp(scalar(node), "no") == 0 ||
3153 g_ascii_strcasecmp(scalar(node), "n") == 0)
3154- cur_netdef->accept_ra = ACCEPT_RA_DISABLED;
3155+ cur_netdef->accept_ra = NETPLAN_RA_MODE_DISABLED;
3156 else
3157 return yaml_error(node, error, "invalid boolean value '%s'", scalar(node));
3158
3159@@ -610,6 +703,42 @@ handle_match(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** er
3160 return process_mapping(doc, node, match_handlers, error);
3161 }
3162
3163+struct NetplanWifiWowlanType NETPLAN_WIFI_WOWLAN_TYPES[] = {
3164+ {"default", NETPLAN_WIFI_WOWLAN_DEFAULT},
3165+ {"any", NETPLAN_WIFI_WOWLAN_ANY},
3166+ {"disconnect", NETPLAN_WIFI_WOWLAN_DISCONNECT},
3167+ {"magic_pkt", NETPLAN_WIFI_WOWLAN_MAGIC},
3168+ {"gtk_rekey_failure", NETPLAN_WIFI_WOWLAN_GTK_REKEY_FAILURE},
3169+ {"eap_identity_req", NETPLAN_WIFI_WOWLAN_EAP_IDENTITY_REQ},
3170+ {"four_way_handshake", NETPLAN_WIFI_WOWLAN_4WAY_HANDSHAKE},
3171+ {"rfkill_release", NETPLAN_WIFI_WOWLAN_RFKILL_RELEASE},
3172+ {"tcp", NETPLAN_WIFI_WOWLAN_TCP},
3173+ {NULL},
3174+};
3175+
3176+static gboolean
3177+handle_wowlan(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
3178+{
3179+ for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
3180+ yaml_node_t *entry = yaml_document_get_node(doc, *i);
3181+ assert_type(entry, YAML_SCALAR_NODE);
3182+ int found = FALSE;
3183+
3184+ for (unsigned i = 0; NETPLAN_WIFI_WOWLAN_TYPES[i].name != NULL; ++i) {
3185+ if (g_ascii_strcasecmp(scalar(entry), NETPLAN_WIFI_WOWLAN_TYPES[i].name) == 0) {
3186+ cur_netdef->wowlan |= NETPLAN_WIFI_WOWLAN_TYPES[i].flag;
3187+ found = TRUE;
3188+ break;
3189+ }
3190+ }
3191+ if (!found)
3192+ return yaml_error(node, error, "invalid value for wakeonwlan: '%s'", scalar(entry));
3193+ }
3194+ if (cur_netdef->wowlan > NETPLAN_WIFI_WOWLAN_DEFAULT && cur_netdef->wowlan & NETPLAN_WIFI_WOWLAN_TYPES[0].flag)
3195+ return yaml_error(node, error, "'default' is an exclusive flag for wakeonwlan");
3196+ return TRUE;
3197+}
3198+
3199 static gboolean
3200 handle_auth(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
3201 {
3202@@ -702,7 +831,7 @@ handle_wifi_access_points(yaml_document_t* doc, yaml_node_t* node, const void* d
3203 assert_type(value, YAML_MAPPING_NODE);
3204
3205 g_assert(cur_access_point == NULL);
3206- cur_access_point = g_new0(wifi_access_point, 1);
3207+ cur_access_point = g_new0(NetplanWifiAccessPoint, 1);
3208 cur_access_point->ssid = g_strdup(scalar(key));
3209 g_debug("%s: adding wifi AP '%s'", cur_netdef->id, cur_access_point->ssid);
3210
3211@@ -739,7 +868,7 @@ handle_bridge_interfaces(yaml_document_t* doc, yaml_node_t* node, const void* da
3212 /* all entries must refer to already defined IDs */
3213 for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
3214 yaml_node_t *entry = yaml_document_get_node(doc, *i);
3215- net_definition *component;
3216+ NetplanNetDefinition *component;
3217
3218 assert_type(entry, YAML_SCALAR_NODE);
3219 component = g_hash_table_lookup(netdefs, scalar(entry));
3220@@ -761,7 +890,7 @@ handle_bridge_interfaces(yaml_document_t* doc, yaml_node_t* node, const void* da
3221
3222 /**
3223 * Handler for bond "mode" types.
3224- * @data: offset into net_definition where the const char* field to write is
3225+ * @data: offset into NetplanNetDefinition where the const char* field to write is
3226 * located
3227 */
3228 static gboolean
3229@@ -789,7 +918,7 @@ handle_bond_interfaces(yaml_document_t* doc, yaml_node_t* node, const void* data
3230 /* all entries must refer to already defined IDs */
3231 for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
3232 yaml_node_t *entry = yaml_document_get_node(doc, *i);
3233- net_definition *component;
3234+ NetplanNetDefinition *component;
3235
3236 assert_type(entry, YAML_SCALAR_NODE);
3237 component = g_hash_table_lookup(netdefs, scalar(entry));
3238@@ -880,12 +1009,12 @@ handle_link_local(yaml_document_t* doc, yaml_node_t* node, const void* _, GError
3239 return TRUE;
3240 }
3241
3242-struct optional_address_option optional_address_options[] = {
3243- {"ipv4-ll", OPTIONAL_IPV4_LL},
3244- {"ipv6-ra", OPTIONAL_IPV6_RA},
3245- {"dhcp4", OPTIONAL_DHCP4},
3246- {"dhcp6", OPTIONAL_DHCP6},
3247- {"static", OPTIONAL_STATIC},
3248+struct NetplanOptionalAddressType NETPLAN_OPTIONAL_ADDRESS_TYPES[] = {
3249+ {"ipv4-ll", NETPLAN_OPTIONAL_IPV4_LL},
3250+ {"ipv6-ra", NETPLAN_OPTIONAL_IPV6_RA},
3251+ {"dhcp4", NETPLAN_OPTIONAL_DHCP4},
3252+ {"dhcp6", NETPLAN_OPTIONAL_DHCP6},
3253+ {"static", NETPLAN_OPTIONAL_STATIC},
3254 {NULL},
3255 };
3256
3257@@ -897,9 +1026,9 @@ handle_optional_addresses(yaml_document_t* doc, yaml_node_t* node, const void* _
3258 assert_type(entry, YAML_SCALAR_NODE);
3259 int found = FALSE;
3260
3261- for (unsigned i = 0; optional_address_options[i].name != NULL; ++i) {
3262- if (g_ascii_strcasecmp(scalar(entry), optional_address_options[i].name) == 0) {
3263- cur_netdef->optional_addresses |= optional_address_options[i].flag;
3264+ for (unsigned i = 0; NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name != NULL; ++i) {
3265+ if (g_ascii_strcasecmp(scalar(entry), NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name) == 0) {
3266+ cur_netdef->optional_addresses |= NETPLAN_OPTIONAL_ADDRESS_TYPES[i].flag;
3267 found = TRUE;
3268 break;
3269 }
3270@@ -1131,7 +1260,7 @@ handle_bridge_path_cost(yaml_document_t* doc, yaml_node_t* node, const void* dat
3271 yaml_node_t* key, *value;
3272 guint v;
3273 gchar* endptr;
3274- net_definition *component;
3275+ NetplanNetDefinition *component;
3276 guint* ref_ptr;
3277
3278 key = yaml_document_get_node(doc, entry->key);
3279@@ -1167,7 +1296,7 @@ handle_bridge_port_priority(yaml_document_t* doc, yaml_node_t* node, const void*
3280 yaml_node_t* key, *value;
3281 guint v;
3282 gchar* endptr;
3283- net_definition *component;
3284+ NetplanNetDefinition *component;
3285 guint* ref_ptr;
3286
3287 key = yaml_document_get_node(doc, entry->key);
3288@@ -1197,7 +1326,7 @@ handle_bridge_port_priority(yaml_document_t* doc, yaml_node_t* node, const void*
3289 return TRUE;
3290 }
3291
3292-const mapping_entry_handler bridge_params_handlers[] = {
3293+static const mapping_entry_handler bridge_params_handlers[] = {
3294 {"ageing-time", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(bridge_params.ageing_time)},
3295 {"forward-delay", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(bridge_params.forward_delay)},
3296 {"hello-time", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(bridge_params.hello_time)},
3297@@ -1221,7 +1350,7 @@ handle_bridge(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** e
3298 * Grammar and handlers for network config "routes" entry
3299 ****************************************************/
3300
3301-const mapping_entry_handler routes_handlers[] = {
3302+static const mapping_entry_handler routes_handlers[] = {
3303 {"from", YAML_SCALAR_NODE, handle_routes_ip, NULL, route_offset(from)},
3304 {"on-link", YAML_SCALAR_NODE, handle_routes_bool, NULL, route_offset(onlink)},
3305 {"scope", YAML_SCALAR_NODE, handle_routes_scope},
3306@@ -1239,15 +1368,15 @@ handle_routes(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** e
3307 for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
3308 yaml_node_t *entry = yaml_document_get_node(doc, *i);
3309
3310- cur_route = g_new0(ip_route, 1);
3311+ cur_route = g_new0(NetplanIPRoute, 1);
3312 cur_route->type = g_strdup("unicast");
3313 cur_route->scope = g_strdup("global");
3314 cur_route->family = G_MAXUINT; /* 0 is a valid family ID */
3315- cur_route->metric = METRIC_UNSPEC; /* 0 is a valid metric */
3316+ cur_route->metric = NETPLAN_METRIC_UNSPEC; /* 0 is a valid metric */
3317
3318 if (process_mapping(doc, entry, routes_handlers, error)) {
3319 if (!cur_netdef->routes) {
3320- cur_netdef->routes = g_array_new(FALSE, FALSE, sizeof(ip_route*));
3321+ cur_netdef->routes = g_array_new(FALSE, FALSE, sizeof(NetplanIPRoute*));
3322 }
3323
3324 g_array_append_val(cur_netdef->routes, cur_route);
3325@@ -1272,7 +1401,7 @@ handle_routes(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** e
3326 return TRUE;
3327 }
3328
3329-const mapping_entry_handler ip_rules_handlers[] = {
3330+static const mapping_entry_handler ip_rules_handlers[] = {
3331 {"from", YAML_SCALAR_NODE, handle_ip_rule_ip, NULL, ip_rule_offset(from)},
3332 {"mark", YAML_SCALAR_NODE, handle_ip_rule_fwmark},
3333 {"priority", YAML_SCALAR_NODE, handle_ip_rule_prio},
3334@@ -1288,16 +1417,16 @@ handle_ip_rules(yaml_document_t* doc, yaml_node_t* node, const void* _, GError**
3335 for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
3336 yaml_node_t *entry = yaml_document_get_node(doc, *i);
3337
3338- cur_ip_rule = g_new0(ip_rule, 1);
3339+ cur_ip_rule = g_new0(NetplanIPRule, 1);
3340 cur_ip_rule->family = G_MAXUINT; /* 0 is a valid family ID */
3341- cur_ip_rule->priority = IP_RULE_PRIO_UNSPEC;
3342- cur_ip_rule->table = ROUTE_TABLE_UNSPEC;
3343- cur_ip_rule->tos = IP_RULE_TOS_UNSPEC;
3344- cur_ip_rule->fwmark = IP_RULE_FW_MARK_UNSPEC;
3345+ cur_ip_rule->priority = NETPLAN_IP_RULE_PRIO_UNSPEC;
3346+ cur_ip_rule->table = NETPLAN_ROUTE_TABLE_UNSPEC;
3347+ cur_ip_rule->tos = NETPLAN_IP_RULE_TOS_UNSPEC;
3348+ cur_ip_rule->fwmark = NETPLAN_IP_RULE_FW_MARK_UNSPEC;
3349
3350 if (process_mapping(doc, entry, ip_rules_handlers, error)) {
3351 if (!cur_netdef->ip_rules) {
3352- cur_netdef->ip_rules = g_array_new(FALSE, FALSE, sizeof(ip_rule*));
3353+ cur_netdef->ip_rules = g_array_new(FALSE, FALSE, sizeof(NetplanIPRule*));
3354 }
3355
3356 g_array_append_val(cur_netdef->ip_rules, cur_ip_rule);
3357@@ -1346,7 +1475,7 @@ handle_arp_ip_targets(yaml_document_t* doc, yaml_node_t* node, const void* _, GE
3358 static gboolean
3359 handle_bond_primary_slave(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
3360 {
3361- net_definition *component;
3362+ NetplanNetDefinition *component;
3363 char** ref_ptr;
3364
3365 component = g_hash_table_lookup(netdefs, scalar(node));
3366@@ -1365,7 +1494,7 @@ handle_bond_primary_slave(yaml_document_t* doc, yaml_node_t* node, const void* d
3367 return TRUE;
3368 }
3369
3370-const mapping_entry_handler bond_params_handlers[] = {
3371+static const mapping_entry_handler bond_params_handlers[] = {
3372 {"mode", YAML_SCALAR_NODE, handle_bond_mode, NULL, netdef_offset(bond_params.mode)},
3373 {"lacp-rate", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(bond_params.lacp_rate)},
3374 {"mii-monitor-interval", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(bond_params.monitor_interval)},
3375@@ -1418,9 +1547,9 @@ handle_dhcp_identifier(yaml_document_t* doc, yaml_node_t* node, const void* data
3376 ****************************************************/
3377
3378 const char*
3379-tunnel_mode_to_string(tunnel_mode mode)
3380+tunnel_mode_to_string(NetplanTunnelMode mode)
3381 {
3382- return tunnel_mode_table[mode];
3383+ return netplan_tunnel_mode_table[mode];
3384 }
3385
3386 static gboolean
3387@@ -1450,11 +1579,11 @@ static gboolean
3388 handle_tunnel_mode(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
3389 {
3390 const char *key = scalar(node);
3391- tunnel_mode i;
3392+ NetplanTunnelMode i;
3393
3394 // Skip over unknown (0) tunnel mode.
3395- for (i = 1; i < _TUNNEL_MODE_MAX; ++i) {
3396- if (g_strcmp0(tunnel_mode_table[i], key) == 0) {
3397+ for (i = 1; i < NETPLAN_TUNNEL_MODE_MAX_; ++i) {
3398+ if (g_strcmp0(netplan_tunnel_mode_table[i], key) == 0) {
3399 cur_netdef->tunnel.mode = i;
3400 return TRUE;
3401 }
3402@@ -1485,7 +1614,7 @@ handle_tunnel_key(yaml_document_t* doc, yaml_node_t* node, const void* data, GEr
3403 return TRUE;
3404 }
3405
3406-const mapping_entry_handler tunnel_keys_handlers[] = {
3407+static const mapping_entry_handler tunnel_keys_handlers[] = {
3408 {"input", YAML_SCALAR_NODE, handle_tunnel_key, NULL, netdef_offset(tunnel.input_key)},
3409 {"output", YAML_SCALAR_NODE, handle_tunnel_key, NULL, netdef_offset(tunnel.output_key)},
3410 {NULL}
3411@@ -1519,7 +1648,15 @@ handle_tunnel_key_mapping(yaml_document_t* doc, yaml_node_t* node, const void* _
3412 * Grammar and handlers for network devices
3413 ****************************************************/
3414
3415-const mapping_entry_handler nameservers_handlers[] = {
3416+static const mapping_entry_handler nm_backend_settings_handlers[] = {
3417+ {"name", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(backend_settings.nm.name)},
3418+ {"uuid", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(backend_settings.nm.uuid)},
3419+ {"stable-id", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(backend_settings.nm.stable_id)},
3420+ {"device", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(backend_settings.nm.device)},
3421+ {NULL}
3422+};
3423+
3424+static const mapping_entry_handler nameservers_handlers[] = {
3425 {"search", YAML_SEQUENCE_NODE, handle_nameservers_search},
3426 {"addresses", YAML_SEQUENCE_NODE, handle_nameservers_addresses},
3427 {NULL}
3428@@ -1537,12 +1674,12 @@ const mapping_entry_handler nameservers_handlers[] = {
3429 {"use-ntp", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(overrides.use_ntp)}, \
3430 {"use-routes", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(overrides.use_routes)}
3431
3432-const mapping_entry_handler dhcp4_overrides_handlers[] = {
3433+static const mapping_entry_handler dhcp4_overrides_handlers[] = {
3434 COMMON_DHCP_OVERRIDES_HANDLERS(dhcp4_overrides),
3435 {NULL},
3436 };
3437
3438-const mapping_entry_handler dhcp6_overrides_handlers[] = {
3439+static const mapping_entry_handler dhcp6_overrides_handlers[] = {
3440 COMMON_DHCP_OVERRIDES_HANDLERS(dhcp6_overrides),
3441 {NULL},
3442 };
3443@@ -1559,6 +1696,7 @@ const mapping_entry_handler dhcp6_overrides_handlers[] = {
3444 {"dhcp6-overrides", YAML_MAPPING_NODE, NULL, dhcp6_overrides_handlers}, \
3445 {"gateway4", YAML_SCALAR_NODE, handle_gateway4}, \
3446 {"gateway6", YAML_SCALAR_NODE, handle_gateway6}, \
3447+ {"ipv6-address-generation", YAML_SCALAR_NODE, handle_netdef_addrgen}, \
3448 {"ipv6-mtu", YAML_SCALAR_NODE, handle_netdef_guint, NULL, netdef_offset(ipv6_mtubytes)}, \
3449 {"ipv6-privacy", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(ip6_privacy)}, \
3450 {"link-local", YAML_SEQUENCE_NODE, handle_link_local}, \
3451@@ -1571,50 +1709,77 @@ const mapping_entry_handler dhcp6_overrides_handlers[] = {
3452 {"routes", YAML_SEQUENCE_NODE, handle_routes}, \
3453 {"routing-policy", YAML_SEQUENCE_NODE, handle_ip_rules}
3454
3455+#define COMMON_BACKEND_HANDLERS \
3456+ {"networkmanager", YAML_MAPPING_NODE, NULL, nm_backend_settings_handlers}
3457+
3458 /* Handlers for physical links */
3459 #define PHYSICAL_LINK_HANDLERS \
3460 {"match", YAML_MAPPING_NODE, handle_match}, \
3461 {"set-name", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(set_name)}, \
3462- {"wakeonlan", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(wake_on_lan)}
3463+ {"wakeonlan", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(wake_on_lan)}, \
3464+ {"wakeonwlan", YAML_SEQUENCE_NODE, handle_wowlan, NULL, netdef_offset(wowlan)}, \
3465+ {"emit-lldp", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(emit_lldp)}
3466
3467-const mapping_entry_handler ethernet_def_handlers[] = {
3468+static const mapping_entry_handler ethernet_def_handlers[] = {
3469 COMMON_LINK_HANDLERS,
3470+ COMMON_BACKEND_HANDLERS,
3471 PHYSICAL_LINK_HANDLERS,
3472 {"auth", YAML_MAPPING_NODE, handle_auth},
3473+ {"link", YAML_SCALAR_NODE, handle_netdef_id_ref, NULL, netdef_offset(sriov_link)},
3474+ {"virtual-function-count", YAML_SCALAR_NODE, handle_netdef_guint, NULL, netdef_offset(sriov_explicit_vf_count)},
3475 {NULL}
3476 };
3477
3478-const mapping_entry_handler wifi_def_handlers[] = {
3479+static const mapping_entry_handler wifi_def_handlers[] = {
3480 COMMON_LINK_HANDLERS,
3481+ COMMON_BACKEND_HANDLERS,
3482 PHYSICAL_LINK_HANDLERS,
3483 {"access-points", YAML_MAPPING_NODE, handle_wifi_access_points},
3484 {"auth", YAML_MAPPING_NODE, handle_auth},
3485 {NULL}
3486 };
3487
3488-const mapping_entry_handler bridge_def_handlers[] = {
3489+static const mapping_entry_handler bridge_def_handlers[] = {
3490 COMMON_LINK_HANDLERS,
3491+ COMMON_BACKEND_HANDLERS,
3492 {"interfaces", YAML_SEQUENCE_NODE, handle_bridge_interfaces, NULL, NULL},
3493 {"parameters", YAML_MAPPING_NODE, handle_bridge},
3494 {NULL}
3495 };
3496
3497-const mapping_entry_handler bond_def_handlers[] = {
3498+static const mapping_entry_handler bond_def_handlers[] = {
3499 COMMON_LINK_HANDLERS,
3500+ COMMON_BACKEND_HANDLERS,
3501 {"interfaces", YAML_SEQUENCE_NODE, handle_bond_interfaces, NULL, NULL},
3502 {"parameters", YAML_MAPPING_NODE, handle_bonding},
3503 {NULL}
3504 };
3505
3506-const mapping_entry_handler vlan_def_handlers[] = {
3507+static const mapping_entry_handler vlan_def_handlers[] = {
3508 COMMON_LINK_HANDLERS,
3509+ COMMON_BACKEND_HANDLERS,
3510 {"id", YAML_SCALAR_NODE, handle_netdef_guint, NULL, netdef_offset(vlan_id)},
3511 {"link", YAML_SCALAR_NODE, handle_netdef_id_ref, NULL, netdef_offset(vlan_link)},
3512 {NULL}
3513 };
3514
3515-const mapping_entry_handler tunnel_def_handlers[] = {
3516+static const mapping_entry_handler modem_def_handlers[] = {
3517+ COMMON_LINK_HANDLERS,
3518+ {"apn", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.apn)},
3519+ {"auto-config", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(modem_params.auto_config)},
3520+ {"device-id", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.device_id)},
3521+ {"network-id", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.network_id)},
3522+ {"number", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.number)},
3523+ {"password", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.password)},
3524+ {"pin", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.pin)},
3525+ {"sim-id", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.sim_id)},
3526+ {"sim-operator-id", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.sim_operator_id)},
3527+ {"username", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.username)},
3528+};
3529+
3530+static const mapping_entry_handler tunnel_def_handlers[] = {
3531 COMMON_LINK_HANDLERS,
3532+ COMMON_BACKEND_HANDLERS,
3533 {"mode", YAML_SCALAR_NODE, handle_tunnel_mode},
3534 {"local", YAML_SCALAR_NODE, handle_tunnel_addr, NULL, netdef_offset(tunnel.local_ip)},
3535 {"remote", YAML_SCALAR_NODE, handle_tunnel_addr, NULL, netdef_offset(tunnel.remote_ip)},
3536@@ -1650,7 +1815,7 @@ handle_network_renderer(yaml_document_t* doc, yaml_node_t* node, const void* _,
3537 }
3538
3539 static void
3540-initialize_dhcp_overrides(dhcp_overrides* overrides)
3541+initialize_dhcp_overrides(NetplanDHCPOverrides* overrides)
3542 {
3543 overrides->use_dns = TRUE;
3544 overrides->use_domains = NULL;
3545@@ -1660,7 +1825,7 @@ initialize_dhcp_overrides(dhcp_overrides* overrides)
3546 overrides->use_mtu = TRUE;
3547 overrides->use_routes = TRUE;
3548 overrides->hostname = NULL;
3549- overrides->metric = METRIC_UNSPEC;
3550+ overrides->metric = NETPLAN_METRIC_UNSPEC;
3551 }
3552
3553 /**
3554@@ -1705,19 +1870,20 @@ handle_network_type(yaml_document_t* doc, yaml_node_t* node, const void* data, G
3555 return yaml_error(key, error, "Updated definition '%s' changes device type", scalar(key));
3556 } else {
3557 /* create new network definition */
3558- cur_netdef = g_new0(net_definition, 1);
3559+ cur_netdef = g_new0(NetplanNetDefinition, 1);
3560 cur_netdef->type = GPOINTER_TO_UINT(data);
3561- cur_netdef->backend = backend_cur_type ?: BACKEND_NONE;
3562+ cur_netdef->backend = backend_cur_type ?: NETPLAN_BACKEND_NONE;
3563 cur_netdef->id = g_strdup(scalar(key));
3564
3565 /* Set some default values */
3566 cur_netdef->vlan_id = G_MAXUINT; /* 0 is a valid ID */
3567- cur_netdef->tunnel.mode = TUNNEL_MODE_UNKNOWN;
3568+ cur_netdef->tunnel.mode = NETPLAN_TUNNEL_MODE_UNKNOWN;
3569 cur_netdef->dhcp_identifier = g_strdup("duid"); /* keep networkd's default */
3570 /* systemd-networkd defaults to IPv6 LL enabled; keep that default */
3571 cur_netdef->linklocal.ipv6 = TRUE;
3572 g_hash_table_insert(netdefs, cur_netdef->id, cur_netdef);
3573 netdefs_ordered = g_list_append(netdefs_ordered, cur_netdef);
3574+ cur_netdef->sriov_vlan_filter = FALSE;
3575
3576 /* DHCP override defaults */
3577 initialize_dhcp_overrides(&cur_netdef->dhcp4_overrides);
3578@@ -1732,12 +1898,13 @@ handle_network_type(yaml_document_t* doc, yaml_node_t* node, const void* data, G
3579
3580 /* and fill it with definitions */
3581 switch (cur_netdef->type) {
3582- case ND_BOND: handlers = bond_def_handlers; break;
3583- case ND_BRIDGE: handlers = bridge_def_handlers; break;
3584- case ND_ETHERNET: handlers = ethernet_def_handlers; break;
3585- case ND_TUNNEL: handlers = tunnel_def_handlers; break;
3586- case ND_VLAN: handlers = vlan_def_handlers; break;
3587- case ND_WIFI: handlers = wifi_def_handlers; break;
3588+ case NETPLAN_DEF_TYPE_BOND: handlers = bond_def_handlers; break;
3589+ case NETPLAN_DEF_TYPE_BRIDGE: handlers = bridge_def_handlers; break;
3590+ case NETPLAN_DEF_TYPE_ETHERNET: handlers = ethernet_def_handlers; break;
3591+ case NETPLAN_DEF_TYPE_MODEM: handlers = modem_def_handlers; break;
3592+ case NETPLAN_DEF_TYPE_TUNNEL: handlers = tunnel_def_handlers; break;
3593+ case NETPLAN_DEF_TYPE_VLAN: handlers = vlan_def_handlers; break;
3594+ case NETPLAN_DEF_TYPE_WIFI: handlers = wifi_def_handlers; break;
3595 default: g_assert_not_reached(); // LCOV_EXCL_LINE
3596 }
3597 if (!process_mapping(doc, value, handlers, error))
3598@@ -1749,22 +1916,23 @@ handle_network_type(yaml_document_t* doc, yaml_node_t* node, const void* data, G
3599
3600 /* convenience shortcut: physical device without match: means match
3601 * name on ID */
3602- if (cur_netdef->type < ND_VIRTUAL && !cur_netdef->has_match)
3603+ if (cur_netdef->type < NETPLAN_DEF_TYPE_VIRTUAL && !cur_netdef->has_match)
3604 cur_netdef->match.original_name = cur_netdef->id;
3605 }
3606- backend_cur_type = BACKEND_NONE;
3607+ backend_cur_type = NETPLAN_BACKEND_NONE;
3608 return TRUE;
3609 }
3610
3611-const mapping_entry_handler network_handlers[] = {
3612- {"bonds", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_BOND)},
3613- {"bridges", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_BRIDGE)},
3614- {"ethernets", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_ETHERNET)},
3615+static const mapping_entry_handler network_handlers[] = {
3616+ {"bonds", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_BOND)},
3617+ {"bridges", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_BRIDGE)},
3618+ {"ethernets", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_ETHERNET)},
3619 {"renderer", YAML_SCALAR_NODE, handle_network_renderer},
3620- {"tunnels", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_TUNNEL)},
3621+ {"tunnels", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_TUNNEL)},
3622 {"version", YAML_SCALAR_NODE, handle_network_version},
3623- {"vlans", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_VLAN)},
3624- {"wifis", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_WIFI)},
3625+ {"vlans", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_VLAN)},
3626+ {"wifis", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_WIFI)},
3627+ {"modems", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_MODEM)},
3628 {NULL}
3629 };
3630
3631@@ -1772,7 +1940,7 @@ const mapping_entry_handler network_handlers[] = {
3632 * Grammar and handlers for root node
3633 ****************************************************/
3634
3635-const mapping_entry_handler root_handlers[] = {
3636+static const mapping_entry_handler root_handlers[] = {
3637 {"network", YAML_MAPPING_NODE, NULL, network_handlers},
3638 {NULL}
3639 };
3640@@ -1809,7 +1977,7 @@ process_document(yaml_document_t* doc, GError** error)
3641 if (g_hash_table_size(missing_id) > 0) {
3642 GHashTableIter iter;
3643 gpointer key, value;
3644- missing_node *missing;
3645+ NetplanMissingNode *missing;
3646
3647 g_clear_error(error);
3648
3649@@ -1817,7 +1985,7 @@ process_document(yaml_document_t* doc, GError** error)
3650 * approximate early failure and give the user a meaningful error. */
3651 g_hash_table_iter_init (&iter, missing_id);
3652 g_hash_table_iter_next (&iter, &key, &value);
3653- missing = (missing_node*) value;
3654+ missing = (NetplanMissingNode*) value;
3655
3656 return yaml_error(missing->node, error, "%s: interface '%s' is not defined",
3657 missing->netdef_id,
3658@@ -1833,7 +2001,7 @@ process_document(yaml_document_t* doc, GError** error)
3659 * Parse given YAML file and create/update global "netdefs" list.
3660 */
3661 gboolean
3662-parse_yaml(const char* filename, GError** error)
3663+netplan_parse_yaml(const char* filename, GError** error)
3664 {
3665 yaml_document_t doc;
3666 gboolean ret;
3667@@ -1841,13 +2009,13 @@ parse_yaml(const char* filename, GError** error)
3668 if (!load_yaml(filename, &doc, error))
3669 return FALSE;
3670
3671+ if (!netdefs)
3672+ netdefs = g_hash_table_new(g_str_hash, g_str_equal);
3673+
3674 /* empty file? */
3675 if (yaml_document_get_root_node(&doc) == NULL)
3676 return TRUE;
3677
3678- if (!netdefs)
3679- netdefs = g_hash_table_new(g_str_hash, g_str_equal);
3680-
3681 g_assert(ids_in_file == NULL);
3682 ids_in_file = g_hash_table_new(g_str_hash, NULL);
3683
3684@@ -1864,10 +2032,10 @@ static void
3685 finish_iterator(gpointer key, gpointer value, gpointer user_data)
3686 {
3687 GError **error = (GError **)user_data;
3688- net_definition* nd = value;
3689+ NetplanNetDefinition* nd = value;
3690
3691 /* Take more steps to make sure we always have a backend set for netdefs */
3692- if (nd->backend == BACKEND_NONE) {
3693+ if (nd->backend == NETPLAN_BACKEND_NONE) {
3694 nd->backend = get_default_backend_for_type(nd->type);
3695 g_debug("%s: setting default backend to %i", nd->id, nd->backend);
3696 }
3697@@ -1880,23 +2048,25 @@ finish_iterator(gpointer key, gpointer value, gpointer user_data)
3698 /**
3699 * Post-processing after parsing all config files
3700 */
3701-gboolean
3702-finish_parse(GError** error)
3703+GHashTable *
3704+netplan_finish_parse(GError** error)
3705 {
3706- if (netdefs)
3707+ if (netdefs) {
3708+ g_debug("We have some netdefs, pass them through a final round of validation");
3709 g_hash_table_foreach(netdefs, finish_iterator, error);
3710+ }
3711
3712 if (error && *error)
3713- return FALSE;
3714+ return NULL;
3715
3716- return TRUE;
3717+ return netdefs;
3718 }
3719
3720 /**
3721 * Return current global backend.
3722 */
3723-netdef_backend
3724-get_global_backend()
3725+NetplanBackend
3726+netplan_get_global_backend()
3727 {
3728 return backend_global;
3729 }
3730diff --git a/src/parse.h b/src/parse.h
3731index 381d450..b84ff4c 100644
3732--- a/src/parse.h
3733+++ b/src/parse.h
3734@@ -39,114 +39,140 @@ int missing_ids_found;
3735 ****************************************************/
3736
3737 typedef enum {
3738- ND_NONE,
3739+ NETPLAN_DEF_TYPE_NONE,
3740 /* physical devices */
3741- ND_ETHERNET,
3742- ND_WIFI,
3743+ NETPLAN_DEF_TYPE_ETHERNET,
3744+ NETPLAN_DEF_TYPE_WIFI,
3745+ NETPLAN_DEF_TYPE_MODEM,
3746 /* virtual devices */
3747- ND_VIRTUAL,
3748- ND_BRIDGE = ND_VIRTUAL,
3749- ND_BOND,
3750- ND_VLAN,
3751- ND_TUNNEL,
3752-} netdef_type;
3753+ NETPLAN_DEF_TYPE_VIRTUAL,
3754+ NETPLAN_DEF_TYPE_BRIDGE = NETPLAN_DEF_TYPE_VIRTUAL,
3755+ NETPLAN_DEF_TYPE_BOND,
3756+ NETPLAN_DEF_TYPE_VLAN,
3757+ NETPLAN_DEF_TYPE_TUNNEL,
3758+} NetplanDefType;
3759
3760 typedef enum {
3761- BACKEND_NONE,
3762- BACKEND_NETWORKD,
3763- BACKEND_NM,
3764- _BACKEND_MAX,
3765-} netdef_backend;
3766-
3767-static const char* const netdef_backend_to_name[_BACKEND_MAX] = {
3768- [BACKEND_NONE] = "none",
3769- [BACKEND_NETWORKD] = "networkd",
3770- [BACKEND_NM] = "NetworkManager",
3771+ NETPLAN_BACKEND_NONE,
3772+ NETPLAN_BACKEND_NETWORKD,
3773+ NETPLAN_BACKEND_NM,
3774+ NETPLAN_BACKEND_MAX_,
3775+} NetplanBackend;
3776+
3777+static const char* const netplan_backend_to_name[NETPLAN_BACKEND_MAX_] = {
3778+ [NETPLAN_BACKEND_NONE] = "none",
3779+ [NETPLAN_BACKEND_NETWORKD] = "networkd",
3780+ [NETPLAN_BACKEND_NM] = "NetworkManager",
3781 };
3782
3783 typedef enum {
3784- ACCEPT_RA_KERNEL,
3785- ACCEPT_RA_ENABLED,
3786- ACCEPT_RA_DISABLED,
3787-} ra_mode;
3788+ NETPLAN_RA_MODE_KERNEL,
3789+ NETPLAN_RA_MODE_ENABLED,
3790+ NETPLAN_RA_MODE_DISABLED,
3791+} NetplanRAMode;
3792
3793 typedef enum {
3794- OPTIONAL_IPV4_LL = 1<<0,
3795- OPTIONAL_IPV6_RA = 1<<1,
3796- OPTIONAL_DHCP4 = 1<<2,
3797- OPTIONAL_DHCP6 = 1<<3,
3798- OPTIONAL_STATIC = 1<<4,
3799-} optional_addr;
3800+ NETPLAN_OPTIONAL_IPV4_LL = 1<<0,
3801+ NETPLAN_OPTIONAL_IPV6_RA = 1<<1,
3802+ NETPLAN_OPTIONAL_DHCP4 = 1<<2,
3803+ NETPLAN_OPTIONAL_DHCP6 = 1<<3,
3804+ NETPLAN_OPTIONAL_STATIC = 1<<4,
3805+} NetplanOptionalAddressFlag;
3806+
3807+typedef enum {
3808+ NETPLAN_ADDRGEN_DEFAULT,
3809+ NETPLAN_ADDRGEN_EUI64,
3810+ NETPLAN_ADDRGEN_STABLEPRIVACY,
3811+} NetplanAddrGenMode;
3812+
3813+struct NetplanOptionalAddressType {
3814+ char* name;
3815+ NetplanOptionalAddressFlag flag;
3816+};
3817+
3818+extern struct NetplanOptionalAddressType NETPLAN_OPTIONAL_ADDRESS_TYPES[];
3819
3820 /* Tunnel mode enum; sync with NetworkManager's DBUS API */
3821 /* TODO: figure out whether networkd's GRETAP and NM's ISATAP
3822 * are the same thing.
3823 */
3824 typedef enum {
3825- TUNNEL_MODE_UNKNOWN = 0,
3826- TUNNEL_MODE_IPIP = 1,
3827- TUNNEL_MODE_GRE = 2,
3828- TUNNEL_MODE_SIT = 3,
3829- TUNNEL_MODE_ISATAP = 4, // NM only.
3830- TUNNEL_MODE_VTI = 5,
3831- TUNNEL_MODE_IP6IP6 = 6,
3832- TUNNEL_MODE_IPIP6 = 7,
3833- TUNNEL_MODE_IP6GRE = 8,
3834- TUNNEL_MODE_VTI6 = 9,
3835+ NETPLAN_TUNNEL_MODE_UNKNOWN = 0,
3836+ NETPLAN_TUNNEL_MODE_IPIP = 1,
3837+ NETPLAN_TUNNEL_MODE_GRE = 2,
3838+ NETPLAN_TUNNEL_MODE_SIT = 3,
3839+ NETPLAN_TUNNEL_MODE_ISATAP = 4, // NM only.
3840+ NETPLAN_TUNNEL_MODE_VTI = 5,
3841+ NETPLAN_TUNNEL_MODE_IP6IP6 = 6,
3842+ NETPLAN_TUNNEL_MODE_IPIP6 = 7,
3843+ NETPLAN_TUNNEL_MODE_IP6GRE = 8,
3844+ NETPLAN_TUNNEL_MODE_VTI6 = 9,
3845
3846 /* systemd-only, apparently? */
3847- TUNNEL_MODE_GRETAP = 101,
3848- TUNNEL_MODE_IP6GRETAP = 102,
3849+ NETPLAN_TUNNEL_MODE_GRETAP = 101,
3850+ NETPLAN_TUNNEL_MODE_IP6GRETAP = 102,
3851
3852- _TUNNEL_MODE_MAX,
3853-} tunnel_mode;
3854+ NETPLAN_TUNNEL_MODE_MAX_,
3855+} NetplanTunnelMode;
3856
3857 static const char* const
3858-tunnel_mode_table[_TUNNEL_MODE_MAX] = {
3859- [TUNNEL_MODE_UNKNOWN] = "unknown",
3860- [TUNNEL_MODE_IPIP] = "ipip",
3861- [TUNNEL_MODE_GRE] = "gre",
3862- [TUNNEL_MODE_SIT] = "sit",
3863- [TUNNEL_MODE_ISATAP] = "isatap",
3864- [TUNNEL_MODE_VTI] = "vti",
3865- [TUNNEL_MODE_IP6IP6] = "ip6ip6",
3866- [TUNNEL_MODE_IPIP6] = "ipip6",
3867- [TUNNEL_MODE_IP6GRE] = "ip6gre",
3868- [TUNNEL_MODE_VTI6] = "vti6",
3869-
3870- [TUNNEL_MODE_GRETAP] = "gretap",
3871- [TUNNEL_MODE_IP6GRETAP] = "ip6gretap",
3872+netplan_tunnel_mode_table[NETPLAN_TUNNEL_MODE_MAX_] = {
3873+ [NETPLAN_TUNNEL_MODE_UNKNOWN] = "unknown",
3874+ [NETPLAN_TUNNEL_MODE_IPIP] = "ipip",
3875+ [NETPLAN_TUNNEL_MODE_GRE] = "gre",
3876+ [NETPLAN_TUNNEL_MODE_SIT] = "sit",
3877+ [NETPLAN_TUNNEL_MODE_ISATAP] = "isatap",
3878+ [NETPLAN_TUNNEL_MODE_VTI] = "vti",
3879+ [NETPLAN_TUNNEL_MODE_IP6IP6] = "ip6ip6",
3880+ [NETPLAN_TUNNEL_MODE_IPIP6] = "ipip6",
3881+ [NETPLAN_TUNNEL_MODE_IP6GRE] = "ip6gre",
3882+ [NETPLAN_TUNNEL_MODE_VTI6] = "vti6",
3883+
3884+ [NETPLAN_TUNNEL_MODE_GRETAP] = "gretap",
3885+ [NETPLAN_TUNNEL_MODE_IP6GRETAP] = "ip6gretap",
3886 };
3887
3888-struct optional_address_option {
3889+typedef enum {
3890+ NETPLAN_WIFI_WOWLAN_DEFAULT = 1<<0,
3891+ NETPLAN_WIFI_WOWLAN_ANY = 1<<1,
3892+ NETPLAN_WIFI_WOWLAN_DISCONNECT = 1<<2,
3893+ NETPLAN_WIFI_WOWLAN_MAGIC = 1<<3,
3894+ NETPLAN_WIFI_WOWLAN_GTK_REKEY_FAILURE = 1<<4,
3895+ NETPLAN_WIFI_WOWLAN_EAP_IDENTITY_REQ = 1<<5,
3896+ NETPLAN_WIFI_WOWLAN_4WAY_HANDSHAKE = 1<<6,
3897+ NETPLAN_WIFI_WOWLAN_RFKILL_RELEASE = 1<<7,
3898+ NETPLAN_WIFI_WOWLAN_TCP = 1<<8,
3899+} NetplanWifiWowlanFlag;
3900+
3901+struct NetplanWifiWowlanType {
3902 char* name;
3903- optional_addr flag;
3904+ NetplanWifiWowlanFlag flag;
3905 };
3906
3907-extern struct optional_address_option optional_address_options[];
3908+extern struct NetplanWifiWowlanType NETPLAN_WIFI_WOWLAN_TYPES[];
3909
3910 typedef enum {
3911- KEY_MANAGEMENT_NONE,
3912- KEY_MANAGEMENT_WPA_PSK,
3913- KEY_MANAGEMENT_WPA_EAP,
3914- KEY_MANAGEMENT_8021X,
3915-} auth_key_management_type;
3916+ NETPLAN_AUTH_KEY_MANAGEMENT_NONE,
3917+ NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK,
3918+ NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP,
3919+ NETPLAN_AUTH_KEY_MANAGEMENT_8021X,
3920+} NetplanAuthKeyManagementType;
3921
3922 typedef enum {
3923- EAP_NONE,
3924- EAP_TLS,
3925- EAP_PEAP,
3926- EAP_TTLS,
3927-} auth_eap_method;
3928+ NETPLAN_AUTH_EAP_NONE,
3929+ NETPLAN_AUTH_EAP_TLS,
3930+ NETPLAN_AUTH_EAP_PEAP,
3931+ NETPLAN_AUTH_EAP_TTLS,
3932+} NetplanAuthEAPMethod;
3933
3934 typedef struct missing_node {
3935 char* netdef_id;
3936 const yaml_node_t* node;
3937-} missing_node;
3938+} NetplanMissingNode;
3939
3940 typedef struct authentication_settings {
3941- auth_key_management_type key_management;
3942- auth_eap_method eap_method;
3943+ NetplanAuthKeyManagementType key_management;
3944+ NetplanAuthEAPMethod eap_method;
3945 char* identity;
3946 char* anonymous_identity;
3947 char* password;
3948@@ -154,7 +180,8 @@ typedef struct authentication_settings {
3949 char* client_certificate;
3950 char* client_key;
3951 char* client_key_password;
3952-} authentication_settings;
3953+ char* phase2_auth; /* netplan-feature: auth-phase2 */
3954+} NetplanAuthenticationSettings;
3955
3956 /* Fields below are valid for dhcp4 and dhcp6 unless otherwise noted. */
3957 typedef struct dhcp_overrides {
3958@@ -167,33 +194,39 @@ typedef struct dhcp_overrides {
3959 char* use_domains; /* netplan-feature: dhcp-use-domains */
3960 char* hostname;
3961 guint metric;
3962-} dhcp_overrides;
3963+} NetplanDHCPOverrides;
3964
3965 /**
3966 * Represent a configuration stanza
3967 */
3968-typedef struct net_definition {
3969- netdef_type type;
3970- netdef_backend backend;
3971+
3972+struct net_definition;
3973+
3974+typedef struct net_definition NetplanNetDefinition;
3975+
3976+struct net_definition {
3977+ NetplanDefType type;
3978+ NetplanBackend backend;
3979 char* id;
3980 /* only necessary for NetworkManager connection UUIDs in some cases */
3981 uuid_t uuid;
3982
3983 /* status options */
3984 gboolean optional;
3985- optional_addr optional_addresses;
3986+ NetplanOptionalAddressFlag optional_addresses;
3987 gboolean critical;
3988
3989 /* addresses */
3990 gboolean dhcp4;
3991 gboolean dhcp6;
3992 char* dhcp_identifier;
3993- dhcp_overrides dhcp4_overrides;
3994- dhcp_overrides dhcp6_overrides;
3995- ra_mode accept_ra;
3996+ NetplanDHCPOverrides dhcp4_overrides;
3997+ NetplanDHCPOverrides dhcp6_overrides;
3998+ NetplanRAMode accept_ra;
3999 GArray* ip4_addresses;
4000 GArray* ip6_addresses;
4001 gboolean ip6_privacy;
4002+ guint ip6_addr_gen_mode;
4003 char* gateway4;
4004 char* gateway6;
4005 GArray* ip4_nameservers;
4006@@ -212,7 +245,7 @@ typedef struct net_definition {
4007
4008 /* vlan */
4009 guint vlan_id;
4010- struct net_definition* vlan_link;
4011+ NetplanNetDefinition* vlan_link;
4012 gboolean has_vlans;
4013
4014 /* Configured custom MAC address */
4015@@ -233,9 +266,12 @@ typedef struct net_definition {
4016 } match;
4017 gboolean has_match;
4018 gboolean wake_on_lan;
4019+ NetplanWifiWowlanFlag wowlan;
4020+ gboolean emit_lldp;
4021+
4022
4023- /* these properties are only valid for ND_WIFI */
4024- GHashTable* access_points; /* SSID → wifi_access_point* */
4025+ /* these properties are only valid for NETPLAN_DEF_TYPE_WIFI */
4026+ GHashTable* access_points; /* SSID → NetplanWifiAccessPoint* */
4027
4028 struct {
4029 char* mode;
4030@@ -262,6 +298,19 @@ typedef struct net_definition {
4031 } bond_params;
4032
4033 struct {
4034+ char* apn;
4035+ gboolean auto_config;
4036+ char* device_id;
4037+ char* network_id;
4038+ char* number;
4039+ char* password;
4040+ char* pin;
4041+ char* sim_id;
4042+ char* sim_operator_id;
4043+ char* username;
4044+ } modem_params;
4045+
4046+ struct {
4047 char* ageing_time;
4048 guint priority;
4049 guint port_priority;
4050@@ -274,36 +323,62 @@ typedef struct net_definition {
4051 gboolean custom_bridging;
4052
4053 struct {
4054- tunnel_mode mode;
4055+ NetplanTunnelMode mode;
4056 char *local_ip;
4057 char *remote_ip;
4058 char *input_key;
4059 char *output_key;
4060 } tunnel;
4061
4062- authentication_settings auth;
4063+ NetplanAuthenticationSettings auth;
4064 gboolean has_auth;
4065-} net_definition;
4066+
4067+ /* these properties are only valid for SR-IOV NICs */
4068+ struct net_definition* sriov_link;
4069+ gboolean sriov_vlan_filter;
4070+ guint sriov_explicit_vf_count;
4071+
4072+ union {
4073+ struct NetplanNMSettings {
4074+ char *name;
4075+ char *uuid;
4076+ char *stable_id;
4077+ char *device;
4078+ } nm;
4079+ struct NetplanNetworkdSettings {
4080+ char *unit;
4081+ } networkd;
4082+ } backend_settings;
4083+};
4084+
4085+typedef enum {
4086+ NETPLAN_WIFI_MODE_INFRASTRUCTURE,
4087+ NETPLAN_WIFI_MODE_ADHOC,
4088+ NETPLAN_WIFI_MODE_AP
4089+} NetplanWifiMode;
4090
4091 typedef enum {
4092- WIFI_MODE_INFRASTRUCTURE,
4093- WIFI_MODE_ADHOC,
4094- WIFI_MODE_AP
4095-} wifi_mode;
4096+ NETPLAN_WIFI_BAND_DEFAULT,
4097+ NETPLAN_WIFI_BAND_5,
4098+ NETPLAN_WIFI_BAND_24
4099+} NetplanWifiBand;
4100
4101 typedef struct {
4102- wifi_mode mode;
4103+ NetplanWifiMode mode;
4104 char* ssid;
4105+ NetplanWifiBand band;
4106+ char* bssid;
4107+ guint channel;
4108
4109- authentication_settings auth;
4110+ NetplanAuthenticationSettings auth;
4111 gboolean has_auth;
4112-} wifi_access_point;
4113+} NetplanWifiAccessPoint;
4114
4115-#define METRIC_UNSPEC G_MAXUINT
4116-#define ROUTE_TABLE_UNSPEC 0
4117-#define IP_RULE_PRIO_UNSPEC G_MAXUINT
4118-#define IP_RULE_FW_MARK_UNSPEC 0
4119-#define IP_RULE_TOS_UNSPEC G_MAXUINT
4120+#define NETPLAN_METRIC_UNSPEC G_MAXUINT
4121+#define NETPLAN_ROUTE_TABLE_UNSPEC 0
4122+#define NETPLAN_IP_RULE_PRIO_UNSPEC G_MAXUINT
4123+#define NETPLAN_IP_RULE_FW_MARK_UNSPEC 0
4124+#define NETPLAN_IP_RULE_TOS_UNSPEC G_MAXUINT
4125
4126 typedef struct {
4127 guint family;
4128@@ -320,7 +395,7 @@ typedef struct {
4129 /* valid metrics are valid positive integers.
4130 * invalid metrics are represented by METRIC_UNSPEC */
4131 guint metric;
4132-} ip_route;
4133+} NetplanIPRoute;
4134
4135 typedef struct {
4136 guint family;
4137@@ -335,7 +410,7 @@ typedef struct {
4138 guint fwmark;
4139 /* type-of-service: between 0 and 255 */
4140 guint tos;
4141-} ip_rule;
4142+} NetplanIPRule;
4143
4144 /* Written/updated by parse_yaml(): char* id → net_definition */
4145 extern GHashTable* netdefs;
4146@@ -345,7 +420,7 @@ extern GList* netdefs_ordered;
4147 * Functions
4148 ****************************************************/
4149
4150-gboolean parse_yaml(const char* filename, GError** error);
4151-gboolean finish_parse(GError** error);
4152-netdef_backend get_global_backend();
4153-const char* tunnel_mode_to_string(tunnel_mode mode);
4154+gboolean netplan_parse_yaml(const char* filename, GError** error);
4155+GHashTable* netplan_finish_parse(GError** error);
4156+NetplanBackend netplan_get_global_backend();
4157+const char* tunnel_mode_to_string(NetplanTunnelMode mode);
4158diff --git a/src/util.c b/src/util.c
4159index e3441d5..4b71ef1 100644
4160--- a/src/util.c
4161+++ b/src/util.c
4162@@ -86,3 +86,65 @@ unlink_glob(const char* rootdir, const char* _glob)
4163 for (size_t i = 0; i < gl.gl_pathc; ++i)
4164 unlink(gl.gl_pathv[i]);
4165 }
4166+
4167+/**
4168+ * Get the frequency of a given 2.4GHz WiFi channel
4169+ */
4170+int
4171+wifi_get_freq24(int channel)
4172+{
4173+ if (channel < 1 || channel > 14) {
4174+ g_fprintf(stderr, "ERROR: invalid 2.4GHz WiFi channel: %d\n", channel);
4175+ exit(1);
4176+ }
4177+
4178+ if (!wifi_frequency_24) {
4179+ wifi_frequency_24 = g_hash_table_new(g_direct_hash, g_direct_equal);
4180+ /* Initialize 2.4GHz frequencies, as of:
4181+ * https://en.wikipedia.org/wiki/List_of_WLAN_channels#2.4_GHz_(802.11b/g/n/ax) */
4182+ for (unsigned i = 0; i < 13; i++) {
4183+ g_hash_table_insert(wifi_frequency_24, GINT_TO_POINTER(i+1),
4184+ GINT_TO_POINTER(2412+i*5));
4185+ }
4186+ g_hash_table_insert(wifi_frequency_24, GINT_TO_POINTER(14),
4187+ GINT_TO_POINTER(2484));
4188+ }
4189+ return GPOINTER_TO_INT(g_hash_table_lookup(wifi_frequency_24,
4190+ GINT_TO_POINTER(channel)));
4191+}
4192+
4193+/**
4194+ * Get the frequency of a given 5GHz WiFi channel
4195+ */
4196+int
4197+wifi_get_freq5(int channel)
4198+{
4199+ int channels[] = { 7, 8, 9, 11, 12, 16, 32, 34, 36, 38, 40, 42, 44, 46, 48,
4200+ 50, 52, 54, 56, 58, 60, 62, 64, 68, 96, 100, 102, 104,
4201+ 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126,
4202+ 128, 132, 134, 136, 138, 140, 142, 144, 149, 151, 153,
4203+ 155, 157, 159, 161, 165, 169, 173 };
4204+ gboolean found = FALSE;
4205+ for (unsigned i = 0; i < sizeof(channels) / sizeof(int); i++) {
4206+ if (channel == channels[i]) {
4207+ found = TRUE;
4208+ break;
4209+ }
4210+ }
4211+ if (!found) {
4212+ g_fprintf(stderr, "ERROR: invalid 5GHz WiFi channel: %d\n", channel);
4213+ exit(1);
4214+ }
4215+ if (!wifi_frequency_5) {
4216+ wifi_frequency_5 = g_hash_table_new(g_direct_hash, g_direct_equal);
4217+ /* Initialize 5GHz frequencies, as of:
4218+ * https://en.wikipedia.org/wiki/List_of_WLAN_channels#5.0_GHz_(802.11j)_WLAN
4219+ * Skipping channels 183-196. They are valid only in Japan with registration needed */
4220+ for (unsigned i = 0; i < sizeof(channels) / sizeof(int); i++) {
4221+ g_hash_table_insert(wifi_frequency_5, GINT_TO_POINTER(channels[i]),
4222+ GINT_TO_POINTER(5000+channels[i]*5));
4223+ }
4224+ }
4225+ return GPOINTER_TO_INT(g_hash_table_lookup(wifi_frequency_5,
4226+ GINT_TO_POINTER(channel)));
4227+}
4228diff --git a/src/util.h b/src/util.h
4229index 4e85a98..4ed1a60 100644
4230--- a/src/util.h
4231+++ b/src/util.h
4232@@ -17,6 +17,12 @@
4233
4234 #pragma once
4235
4236+GHashTable* wifi_frequency_24;
4237+GHashTable* wifi_frequency_5;
4238+
4239 void safe_mkdir_p_dir(const char* file_path);
4240 void g_string_free_to_file(GString* s, const char* rootdir, const char* path, const char* suffix);
4241 void unlink_glob(const char* rootdir, const char* _glob);
4242+
4243+int wifi_get_freq24(int channel);
4244+int wifi_get_freq5(int channel);
4245diff --git a/src/validation.c b/src/validation.c
4246index 7731be4..ad08988 100644
4247--- a/src/validation.c
4248+++ b/src/validation.c
4249@@ -19,6 +19,7 @@
4250 #include <glib/gstdio.h>
4251 #include <gio/gio.h>
4252 #include <arpa/inet.h>
4253+#include <regex.h>
4254
4255 #include <yaml.h>
4256
4257@@ -60,9 +61,9 @@ is_ip6_address(const char* address)
4258 * Validation for grammar and backend rules.
4259 ************************************************/
4260 static gboolean
4261-validate_tunnel_grammar(net_definition* nd, yaml_node_t* node, GError** error)
4262+validate_tunnel_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** error)
4263 {
4264- if (nd->tunnel.mode == TUNNEL_MODE_UNKNOWN)
4265+ if (nd->tunnel.mode == NETPLAN_TUNNEL_MODE_UNKNOWN)
4266 return yaml_error(node, error, "%s: missing 'mode' property for tunnel", nd->id);
4267
4268 /* Validate local/remote IPs */
4269@@ -72,11 +73,11 @@ validate_tunnel_grammar(net_definition* nd, yaml_node_t* node, GError** error)
4270 return yaml_error(node, error, "%s: missing 'remote' property for tunnel", nd->id);
4271
4272 switch(nd->tunnel.mode) {
4273- case TUNNEL_MODE_IPIP6:
4274- case TUNNEL_MODE_IP6IP6:
4275- case TUNNEL_MODE_IP6GRE:
4276- case TUNNEL_MODE_IP6GRETAP:
4277- case TUNNEL_MODE_VTI6:
4278+ case NETPLAN_TUNNEL_MODE_IPIP6:
4279+ case NETPLAN_TUNNEL_MODE_IP6IP6:
4280+ case NETPLAN_TUNNEL_MODE_IP6GRE:
4281+ case NETPLAN_TUNNEL_MODE_IP6GRETAP:
4282+ case NETPLAN_TUNNEL_MODE_VTI6:
4283 if (!is_ip6_address(nd->tunnel.local_ip))
4284 return yaml_error(node, error, "%s: 'local' must be a valid IPv6 address for this tunnel type", nd->id);
4285 if (!is_ip6_address(nd->tunnel.remote_ip))
4286@@ -95,21 +96,21 @@ validate_tunnel_grammar(net_definition* nd, yaml_node_t* node, GError** error)
4287 }
4288
4289 static gboolean
4290-validate_tunnel_backend_rules(net_definition* nd, yaml_node_t* node, GError** error)
4291+validate_tunnel_backend_rules(NetplanNetDefinition* nd, yaml_node_t* node, GError** error)
4292 {
4293 /* Backend-specific validation rules for tunnels */
4294 switch (nd->backend) {
4295- case BACKEND_NETWORKD:
4296+ case NETPLAN_BACKEND_NETWORKD:
4297 switch (nd->tunnel.mode) {
4298- case TUNNEL_MODE_VTI:
4299- case TUNNEL_MODE_VTI6:
4300+ case NETPLAN_TUNNEL_MODE_VTI:
4301+ case NETPLAN_TUNNEL_MODE_VTI6:
4302 break;
4303
4304 /* TODO: Remove this exception and fix ISATAP handling with the
4305 * networkd backend.
4306 * systemd-networkd has grown ISATAP support in 918049a.
4307 */
4308- case TUNNEL_MODE_ISATAP:
4309+ case NETPLAN_TUNNEL_MODE_ISATAP:
4310 return yaml_error(node, error,
4311 "%s: %s tunnel mode is not supported by networkd",
4312 nd->id,
4313@@ -125,14 +126,14 @@ validate_tunnel_backend_rules(net_definition* nd, yaml_node_t* node, GError** er
4314 }
4315 break;
4316
4317- case BACKEND_NM:
4318+ case NETPLAN_BACKEND_NM:
4319 switch (nd->tunnel.mode) {
4320- case TUNNEL_MODE_GRE:
4321- case TUNNEL_MODE_IP6GRE:
4322+ case NETPLAN_TUNNEL_MODE_GRE:
4323+ case NETPLAN_TUNNEL_MODE_IP6GRE:
4324 break;
4325
4326- case TUNNEL_MODE_GRETAP:
4327- case TUNNEL_MODE_IP6GRETAP:
4328+ case NETPLAN_TUNNEL_MODE_GRETAP:
4329+ case NETPLAN_TUNNEL_MODE_IP6GRETAP:
4330 return yaml_error(node, error,
4331 "%s: %s tunnel mode is not supported by NetworkManager",
4332 nd->id,
4333@@ -158,12 +159,12 @@ validate_tunnel_backend_rules(net_definition* nd, yaml_node_t* node, GError** er
4334 }
4335
4336 gboolean
4337-validate_netdef_grammar(net_definition* nd, yaml_node_t* node, GError** error)
4338+validate_netdef_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** error)
4339 {
4340 int missing_id_count = g_hash_table_size(missing_id);
4341 gboolean valid = FALSE;
4342
4343- g_assert(nd->type != ND_NONE);
4344+ g_assert(nd->type != NETPLAN_DEF_TYPE_NONE);
4345
4346 /* Skip all validation if we're missing some definition IDs (devices).
4347 * The ones we have yet to see may be necessary for validation to succeed,
4348@@ -175,10 +176,10 @@ validate_netdef_grammar(net_definition* nd, yaml_node_t* node, GError** error)
4349 if (nd->set_name && !nd->has_match)
4350 return yaml_error(node, error, "%s: 'set-name:' requires 'match:' properties", nd->id);
4351
4352- if (nd->type == ND_WIFI && nd->access_points == NULL)
4353+ if (nd->type == NETPLAN_DEF_TYPE_WIFI && nd->access_points == NULL)
4354 return yaml_error(node, error, "%s: No access points defined", nd->id);
4355
4356- if (nd->type == ND_VLAN) {
4357+ if (nd->type == NETPLAN_DEF_TYPE_VLAN) {
4358 if (!nd->vlan_link)
4359 return yaml_error(node, error, "%s: missing 'link' property", nd->id);
4360 nd->vlan_link->has_vlans = TRUE;
4361@@ -188,7 +189,7 @@ validate_netdef_grammar(net_definition* nd, yaml_node_t* node, GError** error)
4362 return yaml_error(node, error, "%s: invalid id '%u' (allowed values are 0 to 4094)", nd->id, nd->vlan_id);
4363 }
4364
4365- if (nd->type == ND_TUNNEL) {
4366+ if (nd->type == NETPLAN_DEF_TYPE_TUNNEL) {
4367 valid = validate_tunnel_grammar(nd, node, error);
4368 if (!valid)
4369 goto netdef_grammar_error;
4370@@ -201,15 +202,15 @@ netdef_grammar_error:
4371 }
4372
4373 gboolean
4374-validate_backend_rules(net_definition* nd, GError** error)
4375+validate_backend_rules(NetplanNetDefinition* nd, GError** error)
4376 {
4377 gboolean valid = FALSE;
4378 /* Set a dummy, NULL yaml_node_t for error reporting */
4379 yaml_node_t* node = NULL;
4380
4381- g_assert(nd->type != ND_NONE);
4382+ g_assert(nd->type != NETPLAN_DEF_TYPE_NONE);
4383
4384- if (nd->type == ND_TUNNEL) {
4385+ if (nd->type == NETPLAN_DEF_TYPE_TUNNEL) {
4386 valid = validate_tunnel_backend_rules(nd, node, error);
4387 if (!valid)
4388 goto backend_rules_error;
4389diff --git a/src/validation.h b/src/validation.h
4390index a30305f..0383a33 100644
4391--- a/src/validation.h
4392+++ b/src/validation.h
4393@@ -24,7 +24,7 @@ gboolean is_ip4_address(const char* address);
4394 gboolean is_ip6_address(const char* address);
4395
4396 gboolean
4397-validate_netdef_grammar(net_definition* nd, yaml_node_t* node, GError** error);
4398+validate_netdef_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** error);
4399
4400 gboolean
4401-validate_backend_rules(net_definition* nd, GError** error);
4402+validate_backend_rules(NetplanNetDefinition* nd, GError** error);
4403diff --git a/tests/cli.py b/tests/cli.py
4404index 6eb2a85..21c8a3b 100755
4405--- a/tests/cli.py
4406+++ b/tests/cli.py
4407@@ -33,6 +33,11 @@ if shutil.which('python3-coverage'):
4408
4409 # Make sure we can import our development netplan.
4410 os.environ.update({'PYTHONPATH': '.'})
4411+os.environ.update({'LD_LIBRARY_PATH': '.:{}'.format(os.environ.get('LD_LIBRARY_PATH'))})
4412+
4413+
4414+def _load_yaml(text):
4415+ return yaml.load(text, Loader=yaml.SafeLoader)
4416
4417
4418 class TestArgs(unittest.TestCase):
4419@@ -63,7 +68,9 @@ class TestGenerate(unittest.TestCase):
4420 self.workdir = tempfile.TemporaryDirectory()
4421
4422 def test_no_config(self):
4423- out = subprocess.check_output(exe_cli + ['generate', '--root-dir', self.workdir.name])
4424+ p = subprocess.Popen(exe_cli + ['generate', '--root-dir', self.workdir.name], stdout=subprocess.PIPE,
4425+ stderr=subprocess.PIPE)
4426+ (out, err) = p.communicate()
4427 self.assertEqual(out, b'')
4428 self.assertEqual(os.listdir(self.workdir.name), [])
4429
4430@@ -145,6 +152,7 @@ class TestGenerate(unittest.TestCase):
4431
4432
4433 class TestIfupdownMigrate(unittest.TestCase):
4434+
4435 def setUp(self):
4436 self.workdir = tempfile.TemporaryDirectory()
4437 self.ifaces_path = os.path.join(self.workdir.name, 'etc/network/interfaces')
4438@@ -203,20 +211,20 @@ source-directory /etc/network/interfaces.d''')[0]
4439
4440 def test_dhcp4(self):
4441 out = self.do_test('auto en1\niface en1 inet dhcp')[0]
4442- self.assertEqual(yaml.load(out), {'network': {
4443+ self.assertEqual(_load_yaml(out), {'network': {
4444 'version': 2,
4445 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
4446
4447 def test_dhcp6(self):
4448 out = self.do_test('auto en1\niface en1 inet6 dhcp')[0]
4449- self.assertEqual(yaml.load(out), {'network': {
4450+ self.assertEqual(_load_yaml(out), {'network': {
4451 'version': 2,
4452 'ethernets': {'en1': {'dhcp6': True}}}}, out.decode())
4453
4454 def test_dhcp4_and_6(self):
4455 out = self.do_test('auto lo\niface lo inet loopback\n\n'
4456 'auto en1\niface en1 inet dhcp\niface en1 inet6 dhcp')[0]
4457- self.assertEqual(yaml.load(out), {'network': {
4458+ self.assertEqual(_load_yaml(out), {'network': {
4459 'version': 2,
4460 'ethernets': {'en1': {'dhcp4': True, 'dhcp6': True}}}}, out.decode())
4461
4462@@ -224,7 +232,7 @@ source-directory /etc/network/interfaces.d''')[0]
4463 out = self.do_test('iface lo inet loopback\nauto lo\nsource-directory interfaces.d',
4464 dropins={'interfaces.d/std': 'auto en1\niface en1 inet dhcp',
4465 'interfaces.d/std.bak': 'some_bogus dontreadme'})[0]
4466- self.assertEqual(yaml.load(out), {'network': {
4467+ self.assertEqual(_load_yaml(out), {'network': {
4468 'version': 2,
4469 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
4470
4471@@ -232,7 +240,7 @@ source-directory /etc/network/interfaces.d''')[0]
4472 out = self.do_test('iface lo inet loopback\nauto lo\nsource-directory /etc/network/defs/my',
4473 dropins={'defs/my/std': 'auto en1\niface en1 inet dhcp',
4474 'defs/my/std.bak': 'some_bogus dontreadme'})[0]
4475- self.assertEqual(yaml.load(out), {'network': {
4476+ self.assertEqual(_load_yaml(out), {'network': {
4477 'version': 2,
4478 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
4479
4480@@ -240,7 +248,7 @@ source-directory /etc/network/interfaces.d''')[0]
4481 out = self.do_test('iface lo inet loopback\nauto lo\nsource interfaces.d/*.cfg',
4482 dropins={'interfaces.d/std.cfg': 'auto en1\niface en1 inet dhcp',
4483 'interfaces.d/std.cfgold': 'some_bogus dontreadme'})[0]
4484- self.assertEqual(yaml.load(out), {'network': {
4485+ self.assertEqual(_load_yaml(out), {'network': {
4486 'version': 2,
4487 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
4488
4489@@ -248,21 +256,21 @@ source-directory /etc/network/interfaces.d''')[0]
4490 out = self.do_test('iface lo inet loopback\nauto lo\nsource /etc/network/*.cfg',
4491 dropins={'std.cfg': 'auto en1\niface en1 inet dhcp',
4492 'std.cfgold': 'some_bogus dontreadme'})[0]
4493- self.assertEqual(yaml.load(out), {'network': {
4494+ self.assertEqual(_load_yaml(out), {'network': {
4495 'version': 2,
4496 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
4497
4498 def test_allow(self):
4499 out = self.do_test('allow-hotplug en1\niface en1 inet dhcp\n'
4500 'allow-auto en2\niface en2 inet dhcp')[0]
4501- self.assertEqual(yaml.load(out), {'network': {
4502+ self.assertEqual(_load_yaml(out), {'network': {
4503 'version': 2,
4504 'ethernets': {'en1': {'dhcp4': True},
4505 'en2': {'dhcp4': True}}}}, out.decode())
4506
4507 def test_no_scripts(self):
4508 out = self.do_test('auto en1\niface en1 inet dhcp\nno-scripts en1')[0]
4509- self.assertEqual(yaml.load(out), {'network': {
4510+ self.assertEqual(_load_yaml(out), {'network': {
4511 'version': 2,
4512 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
4513
4514@@ -276,7 +284,7 @@ source-directory /etc/network/interfaces.d''')[0]
4515 def test_write_file_haveconfig(self):
4516 (out, err) = self.do_test('auto en1\niface en1 inet dhcp', dry_run=False)
4517 with open(self.converted_path) as f:
4518- config = yaml.load(f)
4519+ config = _load_yaml(f)
4520 self.assertEqual(config, {'network': {
4521 'version': 2,
4522 'ethernets': {'en1': {'dhcp4': True}}}})
4523@@ -302,13 +310,13 @@ source-directory /etc/network/interfaces.d''')[0]
4524
4525 def test_static_ipv4_prefix(self):
4526 out = self.do_test('auto en1\niface en1 inet static\naddress 1.2.3.4/8', dry_run=True)[0]
4527- self.assertEqual(yaml.load(out), {'network': {
4528+ self.assertEqual(_load_yaml(out), {'network': {
4529 'version': 2,
4530 'ethernets': {'en1': {'addresses': ["1.2.3.4/8"]}}}}, out.decode())
4531
4532 def test_static_ipv4_netmask(self):
4533 out = self.do_test('auto en1\niface en1 inet static\naddress 1.2.3.4\nnetmask 255.0.0.0', dry_run=True)[0]
4534- self.assertEqual(yaml.load(out), {'network': {
4535+ self.assertEqual(_load_yaml(out), {'network': {
4536 'version': 2,
4537 'ethernets': {'en1': {'addresses': ["1.2.3.4/8"]}}}}, out.decode())
4538
4539@@ -349,14 +357,14 @@ source-directory /etc/network/interfaces.d''')[0]
4540
4541 def test_static_ipv6_prefix(self):
4542 out = self.do_test('auto en1\niface en1 inet6 static\naddress fc00:0123:4567:89ab:cdef::1234/64', dry_run=True)[0]
4543- self.assertEqual(yaml.load(out), {'network': {
4544+ self.assertEqual(_load_yaml(out), {'network': {
4545 'version': 2,
4546 'ethernets': {'en1': {'addresses': ["fc00:123:4567:89ab:cdef::1234/64"]}}}}, out.decode())
4547
4548 def test_static_ipv6_netmask(self):
4549 out = self.do_test('auto en1\niface en1 inet6 static\n'
4550 'address fc00:0123:4567:89ab:cdef::1234\nnetmask 64', dry_run=True)[0]
4551- self.assertEqual(yaml.load(out), {'network': {
4552+ self.assertEqual(_load_yaml(out), {'network': {
4553 'version': 2,
4554 'ethernets': {'en1': {'addresses': ["fc00:123:4567:89ab:cdef::1234/64"]}}}}, out.decode())
4555
4556@@ -404,7 +412,7 @@ source-directory /etc/network/interfaces.d''')[0]
4557 def test_static_ipv6_accept_ra_0(self):
4558 out = self.do_test('auto en1\niface en1 inet6 static\n'
4559 'address fc00:0123:4567:89ab:cdef::1234/64\naccept_ra 0', dry_run=True)[0]
4560- self.assertEqual(yaml.load(out), {'network': {
4561+ self.assertEqual(_load_yaml(out), {'network': {
4562 'version': 2,
4563 'ethernets': {'en1': {'addresses': ["fc00:123:4567:89ab:cdef::1234/64"],
4564 'accept_ra': False}}}}, out.decode())
4565@@ -412,7 +420,7 @@ source-directory /etc/network/interfaces.d''')[0]
4566 def test_static_ipv6_accept_ra_1(self):
4567 out = self.do_test('auto en1\niface en1 inet6 static\n'
4568 'address fc00:0123:4567:89ab:cdef::1234/64\naccept_ra 1', dry_run=True)[0]
4569- self.assertEqual(yaml.load(out), {'network': {
4570+ self.assertEqual(_load_yaml(out), {'network': {
4571 'version': 2,
4572 'ethernets': {'en1': {'addresses': ["fc00:123:4567:89ab:cdef::1234/64"],
4573 'accept_ra': True}}}}, out.decode())
4574@@ -438,7 +446,7 @@ iface en1 inet static
4575 iface en1 inet6 static
4576 address fc00:0123:4567:89ab:cdef::1234/64
4577 gateway fc00:0123:4567:89ab::1""", dry_run=True)[0]
4578- self.assertEqual(yaml.load(out), {'network': {
4579+ self.assertEqual(_load_yaml(out), {'network': {
4580 'version': 2,
4581 'ethernets': {'en1':
4582 {'addresses': ["1.2.3.4/8", "fc00:123:4567:89ab:cdef::1234/64"],
4583@@ -455,7 +463,7 @@ iface en1 inet static
4584 iface en1 inet6 static
4585 address fc00:0123:4567:89ab:cdef::1234/64
4586 dns-nameservers fc00:0123:4567:89ab:1::1 fc00:0123:4567:89ab:2::1""", dry_run=True)[0]
4587- self.assertEqual(yaml.load(out), {'network': {
4588+ self.assertEqual(_load_yaml(out), {'network': {
4589 'version': 2,
4590 'ethernets': {'en1':
4591 {'addresses': ["1.2.3.4/8", "fc00:123:4567:89ab:cdef::1234/64"],
4592@@ -467,7 +475,7 @@ iface en1 inet6 static
4593
4594 def test_static_dns2(self):
4595 out = self.do_test('auto en1\niface en1 inet static\naddress 1.2.3.4/8\ndns-search foo foo.bar', dry_run=True)[0]
4596- self.assertEqual(yaml.load(out), {'network': {
4597+ self.assertEqual(_load_yaml(out), {'network': {
4598 'version': 2,
4599 'ethernets': {'en1': {'addresses': ["1.2.3.4/8"],
4600 'nameservers': {
4601@@ -476,7 +484,7 @@ iface en1 inet6 static
4602
4603 def test_static_mtu(self):
4604 out = self.do_test('auto en1\niface en1 inet static\naddress 1.2.3.4/8\nmtu 1280', dry_run=True)[0]
4605- self.assertEqual(yaml.load(out), {'network': {
4606+ self.assertEqual(_load_yaml(out), {'network': {
4607 'version': 2,
4608 'ethernets': {'en1': {'addresses': ["1.2.3.4/8"],
4609 'mtu': 1280}}}}, out.decode())
4610@@ -494,7 +502,7 @@ iface en1 inet6 static
4611
4612 def test_static_hwaddress(self):
4613 out = self.do_test('auto en1\niface en1 inet static\naddress 1.2.3.4/8\nhwaddress 52:54:00:6b:3c:59', dry_run=True)[0]
4614- self.assertEqual(yaml.load(out), {'network': {
4615+ self.assertEqual(_load_yaml(out), {'network': {
4616 'version': 2,
4617 'ethernets': {'en1': {'addresses': ["1.2.3.4/8"],
4618 'macaddress': '52:54:00:6b:3c:59'}}}}, out.decode())
4619diff --git a/tests/generator/base.py b/tests/generator/base.py
4620index e5eb18a..2cdbcfd 100644
4621--- a/tests/generator/base.py
4622+++ b/tests/generator/base.py
4623@@ -29,6 +29,9 @@ import unittest
4624 exe_generate = os.path.join(os.path.dirname(os.path.dirname(
4625 os.path.dirname(os.path.abspath(__file__)))), 'generate')
4626
4627+# make sure we point to libnetplan properly.
4628+os.environ.update({'LD_LIBRARY_PATH': '.:{}'.format(os.environ.get('LD_LIBRARY_PATH'))})
4629+
4630 # make sure we fail on criticals
4631 os.environ['G_DEBUG'] = 'fatal-criticals'
4632
4633diff --git a/tests/generator/test_auth.py b/tests/generator/test_auth.py
4634index 89e9f30..f35f3f8 100644
4635--- a/tests/generator/test_auth.py
4636+++ b/tests/generator/test_auth.py
4637@@ -31,11 +31,17 @@ class TestNetworkd(TestBase):
4638 wl0:
4639 access-points:
4640 "Joe's Home":
4641- password: "s3kr1t"
4642+ password: "s0s3kr1t"
4643 "Luke's Home":
4644 auth:
4645 key-management: psk
4646 password: "4lsos3kr1t"
4647+ "BobsHome":
4648+ password: "e03ce667c87bc81ca968d9120ca37f89eb09aec3c55b80386e5d772efd6b926e"
4649+ "BillsHome":
4650+ auth:
4651+ key-management: psk
4652+ password: "db3b0acf5653aeaddd5fe034fb9f07175b2864f847b005aaa2f09182d9411b04"
4653 workplace:
4654 auth:
4655 key-management: eap
4656@@ -102,6 +108,20 @@ network={
4657 ''', new_config)
4658 self.assertIn('''
4659 network={
4660+ ssid="BobsHome"
4661+ key_mgmt=WPA-PSK
4662+ psk=e03ce667c87bc81ca968d9120ca37f89eb09aec3c55b80386e5d772efd6b926e
4663+}
4664+''', new_config)
4665+ self.assertIn('''
4666+network={
4667+ ssid="BillsHome"
4668+ key_mgmt=WPA-PSK
4669+ psk=db3b0acf5653aeaddd5fe034fb9f07175b2864f847b005aaa2f09182d9411b04
4670+}
4671+''', new_config)
4672+ self.assertIn('''
4673+network={
4674 ssid="workplace2"
4675 key_mgmt=WPA-EAP
4676 eap=PEAP
4677@@ -153,12 +173,14 @@ network={
4678 network={
4679 ssid="Joe's Home"
4680 key_mgmt=WPA-PSK
4681- psk="s3kr1t"
4682+ psk="s0s3kr1t"
4683 }
4684 ''', new_config)
4685 self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o600)
4686+ self.assertTrue(os.path.isfile(os.path.join(
4687+ self.workdir.name, 'run/systemd/system/netplan-wpa-wl0.service')))
4688 self.assertTrue(os.path.islink(os.path.join(
4689- self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa@wl0.service')))
4690+ self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl0.service')))
4691
4692 def test_auth_wired(self):
4693 self.generate('''network:
4694@@ -174,6 +196,7 @@ network={
4695 client-certificate: /etc/ssl/cust-crt.pem
4696 client-key: /etc/ssl/cust-key.pem
4697 client-key-password: "d3cryptPr1v4t3K3y"
4698+ phase2-auth: MSCHAPV2
4699 dhcp4: yes
4700 ''')
4701
4702@@ -196,11 +219,14 @@ network={
4703 client_cert="/etc/ssl/cust-crt.pem"
4704 private_key="/etc/ssl/cust-key.pem"
4705 private_key_passwd="d3cryptPr1v4t3K3y"
4706+ phase2="auth=MSCHAPV2"
4707 }
4708 ''')
4709 self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o600)
4710+ self.assertTrue(os.path.isfile(os.path.join(
4711+ self.workdir.name, 'run/systemd/system/netplan-wpa-eth0.service')))
4712 self.assertTrue(os.path.islink(os.path.join(
4713- self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa@eth0.service')))
4714+ self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-eth0.service')))
4715
4716
4717 class TestNetworkManager(TestBase):
4718@@ -213,7 +239,7 @@ class TestNetworkManager(TestBase):
4719 wl0:
4720 access-points:
4721 "Joe's Home":
4722- password: "s3kr1t"
4723+ password: "s0s3kr1t"
4724 "Luke's Home":
4725 auth:
4726 key-management: psk
4727@@ -249,6 +275,7 @@ class TestNetworkManager(TestBase):
4728 client-certificate: /etc/ssl/cust-crt.pem
4729 client-key: /etc/ssl/cust-key.pem
4730 client-key-password: "d3cryptPr1v4t3K3y"
4731+ phase2-auth: MSCHAPV2
4732 opennet:
4733 auth:
4734 key-management: none
4735@@ -279,7 +306,7 @@ mode=infrastructure
4736
4737 [wifi-security]
4738 key-mgmt=wpa-psk
4739-psk=s3kr1t
4740+psk=s0s3kr1t
4741 ''',
4742 'wl0-Luke%27s%20Home': '''[connection]
4743 id=netplan-wl0-Luke's Home
4744@@ -413,6 +440,7 @@ ca-cert=/etc/ssl/cust-cacrt.pem
4745 client-cert=/etc/ssl/cust-crt.pem
4746 private-key=/etc/ssl/cust-key.pem
4747 private-key-password=d3cryptPr1v4t3K3y
4748+phase2-auth=MSCHAPV2
4749 ''',
4750 'wl0-opennet': '''[connection]
4751 id=netplan-wl0-opennet
4752@@ -516,3 +544,36 @@ class TestConfigErrors(TestBase):
4753 auth:
4754 method: bogus''', expect_fail=True)
4755 self.assertIn("unknown EAP method 'bogus'", err)
4756+
4757+ def test_auth_networkd_wifi_psk_too_big(self):
4758+ err = self.generate('''network:
4759+ version: 2
4760+ wifis:
4761+ wl0:
4762+ access-points:
4763+ "Joe's Home":
4764+ password: "LoremipsumdolorsitametconsecteturadipiscingelitCrastemporvelitnunc"
4765+ dhcp4: yes''', expect_fail=True)
4766+ self.assertIn("ASCII passphrase must be between 8 and 63 characters (inclusive)", err)
4767+
4768+ def test_auth_networkd_wifi_psk_too_small(self):
4769+ err = self.generate('''network:
4770+ version: 2
4771+ wifis:
4772+ wl0:
4773+ access-points:
4774+ "Joe's Home":
4775+ password: "p4ss"
4776+ dhcp4: yes''', expect_fail=True)
4777+ self.assertIn("ASCII passphrase must be between 8 and 63 characters (inclusive)", err)
4778+
4779+ def test_auth_networkd_wifi_psk_64_non_hexdigit(self):
4780+ err = self.generate('''network:
4781+ version: 2
4782+ wifis:
4783+ wl0:
4784+ access-points:
4785+ "Joe's Home":
4786+ password: "LoremipsumdolorsitametconsecteturadipiscingelitCrastemporvelitnu"
4787+ dhcp4: yes''', expect_fail=True)
4788+ self.assertIn("PSK length of 64 is only supported for hex-digit representation", err)
4789diff --git a/tests/generator/test_common.py b/tests/generator/test_common.py
4790index 8fb91bd..abbc368 100644
4791--- a/tests/generator/test_common.py
4792+++ b/tests/generator/test_common.py
4793@@ -104,13 +104,26 @@ ConfigureWithoutCarrier=yes
4794 'bond0.network': '''[Match]
4795 Name=bond0
4796
4797+[Link]
4798+MTUBytes=9000
4799+
4800 [Network]
4801 LinkLocalAddressing=ipv6
4802 ConfigureWithoutCarrier=yes
4803 VLAN=bond0.108
4804 ''',
4805 'eth1.link': '[Match]\nOriginalName=eth1\n\n[Link]\nWakeOnLan=off\nMTUBytes=9000\n',
4806- 'eth1.network': '[Match]\nName=eth1\n\n[Network]\nLinkLocalAddressing=no\nIPv6MTUBytes=2000\nBond=bond0\n'
4807+ 'eth1.network': '''[Match]
4808+Name=eth1
4809+
4810+[Link]
4811+MTUBytes=9000
4812+
4813+[Network]
4814+LinkLocalAddressing=no
4815+IPv6MTUBytes=2000
4816+Bond=bond0
4817+'''
4818 })
4819 self.assert_networkd_udev(None)
4820
4821@@ -283,7 +296,6 @@ UseMTU=true
4822 version: 2
4823 ethernets:
4824 engreen:
4825- dhcp4: yes
4826 critical: yes
4827 ''')
4828
4829@@ -291,13 +303,10 @@ UseMTU=true
4830 Name=engreen
4831
4832 [Network]
4833-DHCP=ipv4
4834 LinkLocalAddressing=ipv6
4835
4836 [DHCP]
4837 CriticalConnection=true
4838-RouteMetric=100
4839-UseMTU=true
4840 '''})
4841
4842 def test_dhcp_identifier_mac(self):
4843@@ -715,6 +724,48 @@ method=auto
4844 method=auto
4845 '''})
4846
4847+ def test_ip6_addr_gen_mode(self):
4848+ self.generate('''network:
4849+ version: 2
4850+ renderer: NetworkManager
4851+ ethernets:
4852+ engreen:
4853+ dhcp6: yes
4854+ ipv6-address-generation: stable-privacy
4855+ enblue:
4856+ dhcp6: yes
4857+ ipv6-address-generation: eui64''')
4858+ self.assert_nm({'engreen': '''[connection]
4859+id=netplan-engreen
4860+type=ethernet
4861+interface-name=engreen
4862+
4863+[ethernet]
4864+wake-on-lan=0
4865+
4866+[ipv4]
4867+method=link-local
4868+
4869+[ipv6]
4870+method=auto
4871+addr-gen-mode=1
4872+''',
4873+ 'enblue': '''[connection]
4874+id=netplan-enblue
4875+type=ethernet
4876+interface-name=enblue
4877+
4878+[ethernet]
4879+wake-on-lan=0
4880+
4881+[ipv4]
4882+method=link-local
4883+
4884+[ipv6]
4885+method=auto
4886+addr-gen-mode=0
4887+'''})
4888+
4889 def test_eth_manual_addresses(self):
4890 self.generate('''network:
4891 version: 2
4892diff --git a/tests/generator/test_errors.py b/tests/generator/test_errors.py
4893index 5f3def8..afe487b 100644
4894--- a/tests/generator/test_errors.py
4895+++ b/tests/generator/test_errors.py
4896@@ -210,6 +210,39 @@ class TestConfigErrors(TestBase):
4897 mode: bogus''', expect_fail=True)
4898 self.assertIn("unknown wifi mode 'bogus'", err)
4899
4900+ def test_wifi_ap_unknown_band(self):
4901+ err = self.generate('''network:
4902+ version: 2
4903+ wifis:
4904+ wl0:
4905+ access-points:
4906+ workplace:
4907+ band: bogus''', expect_fail=True)
4908+ self.assertIn("unknown wifi band 'bogus'", err)
4909+
4910+ def test_wifi_ap_invalid_freq24(self):
4911+ err = self.generate('''network:
4912+ version: 2
4913+ renderer: NetworkManager
4914+ wifis:
4915+ wl0:
4916+ access-points:
4917+ workplace:
4918+ band: 2.4GHz
4919+ channel: 15''', expect_fail=True)
4920+ self.assertIn("ERROR: invalid 2.4GHz WiFi channel: 15", err)
4921+
4922+ def test_wifi_ap_invalid_freq5(self):
4923+ err = self.generate('''network:
4924+ version: 2
4925+ wifis:
4926+ wl0:
4927+ access-points:
4928+ workplace:
4929+ band: 5GHz
4930+ channel: 14''', expect_fail=True)
4931+ self.assertIn("ERROR: invalid 5GHz WiFi channel: 14", err)
4932+
4933 def test_invalid_ipv4_address(self):
4934 err = self.generate('''network:
4935 version: 2
4936@@ -288,6 +321,23 @@ class TestConfigErrors(TestBase):
4937 - 2001::1/''', expect_fail=True)
4938 self.assertIn("invalid prefix length in address '2001::1/'", err)
4939
4940+ def test_invalid_addr_gen_mode(self):
4941+ err = self.generate('''network:
4942+ version: 2
4943+ renderer: NetworkManager
4944+ ethernets:
4945+ engreen:
4946+ ipv6-address-generation: 0''', expect_fail=True)
4947+ self.assertIn("unknown ipv6-address-generation '0'", err)
4948+
4949+ def test_addr_gen_mode_not_supported(self):
4950+ err = self.generate('''network:
4951+ version: 2
4952+ ethernets:
4953+ engreen:
4954+ ipv6-address-generation: eui64''', expect_fail=True)
4955+ self.assertIn("ERROR: engreen: ipv6-address-generation is not supported by networkd", err)
4956+
4957 def test_invalid_gateway4(self):
4958 for a in ['300.400.1.1', '1.2.3', '192.168.14.1/24']:
4959 err = self.generate('''network:
4960@@ -363,6 +413,14 @@ class TestConfigErrors(TestBase):
4961 ena: {id: 1, link: en1}''', expect_fail=True)
4962 self.assertIn("ena: interface 'en1' is not defined", err)
4963
4964+ def test_vlan_unknown_renderer(self):
4965+ err = self.generate('''network:
4966+ version: 2
4967+ ethernets: {en1: {}}
4968+ vlans:
4969+ ena: {id: 1, link: en1, renderer: foo}''', expect_fail=True)
4970+ self.assertIn("unknown renderer 'foo'", err)
4971+
4972 def test_device_bad_route_to(self):
4973 self.generate('''network:
4974 version: 2
4975diff --git a/tests/generator/test_ethernets.py b/tests/generator/test_ethernets.py
4976index 22e6d1e..7a451f7 100644
4977--- a/tests/generator/test_ethernets.py
4978+++ b/tests/generator/test_ethernets.py
4979@@ -46,6 +46,22 @@ unmanaged-devices+=interface-name:eth0,''')
4980 # should not allow NM to manage everything
4981 self.assertFalse(os.path.exists(self.nm_enable_all_conf))
4982
4983+ def test_eth_lldp(self):
4984+ self.generate('''network:
4985+ version: 2
4986+ ethernets:
4987+ eth0:
4988+ dhcp4: n
4989+ emit-lldp: true''')
4990+
4991+ self.assert_networkd({'eth0.network': '''[Match]
4992+Name=eth0
4993+
4994+[Network]
4995+EmitLLDP=true
4996+LinkLocalAddressing=ipv6
4997+'''})
4998+
4999 def test_eth_mtu(self):
5000 self.generate('''network:
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches