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

Proposed by Lukas Märdian
Status: Merged
Approved by: Łukasz Zemczak
Approved revision: be01bd4e47cbbcde37ed5d13a71dfa7ace1bf1a4
Merged at revision: be01bd4e47cbbcde37ed5d13a71dfa7ace1bf1a4
Proposed branch: ~slyon/netplan/+git/ubuntu:slyon/ubuntu/bionic
Merge into: ~ubuntu-core-dev/netplan/+git/ubuntu:ubuntu/bionic
Diff against target: 6693 lines (+3713/-544)
44 files modified
Makefile (+28/-6)
debian/changelog (+82/-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/-6)
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+383323@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Łukasz Zemczak (sil2100) :
review: Approve

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

Subscribers

People subscribed via source and target branches