Codebase list netplan.io / dcdb6c5
Import upstream version 0.99, md5 b9a732fca6f3505ab67f385ea6c6ef31 Debian Janitor 4 years ago
32 changed file(s) with 3405 addition(s) and 586 deletion(s). Raw diff Collapse all Expand all
0 NETPLAN_SOVER=0.0
1
02 BUILDFLAGS = \
3 -g \
4 -fPIC \
15 -std=c99 \
26 -D_XOPEN_SOURCE=500 \
37 -DSBINDIR=\"$(SBINDIR)\" \
1216 GCOV ?= gcov
1317 ROOTPREFIX ?=
1418 PREFIX ?= /usr
19 LIBDIR ?= $(PREFIX)/lib
1520 ROOTLIBEXECDIR ?= $(ROOTPREFIX)/lib
1621 LIBEXECDIR ?= $(PREFIX)/lib
1722 SBINDIR ?= $(PREFIX)/sbin
1823 DATADIR ?= $(PREFIX)/share
1924 DOCDIR ?= $(DATADIR)/doc
2025 MANDIR ?= $(DATADIR)/man
26 INCLUDEDIR ?= $(PREFIX)/include
2127
2228 PYCODE = netplan/ $(wildcard src/*.py) $(wildcard tests/*.py) $(wildcard tests/generator/*.py) $(wildcard tests/dbus/*.py)
2329
2834
2935 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
3036
31 generate: src/generate.[hc] src/parse.[hc] src/util.[hc] src/networkd.[hc] src/nm.[hc] src/validation.[hc] src/error.[hc]
32 $(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(filter %.c, $^) `pkg-config --cflags --libs glib-2.0 gio-2.0 yaml-0.1 uuid`
37 %.o: src/%.c
38 $(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -c $^ `pkg-config --cflags --libs glib-2.0 gio-2.0 yaml-0.1 uuid`
39
40 libnetplan.so.$(NETPLAN_SOVER): parse.o util.o validation.o error.o
41 $(CC) -shared -Wl,-soname,libnetplan.so.$(NETPLAN_SOVER) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $^ `pkg-config --libs yaml-0.1`
42 ln -snf libnetplan.so.$(NETPLAN_SOVER) libnetplan.so
43
44 #generate: src/generate.[hc] src/parse.[hc] src/util.[hc] src/networkd.[hc] src/nm.[hc] src/validation.[hc] src/error.[hc]
45 generate: libnetplan.so.$(NETPLAN_SOVER) nm.o networkd.o generate.o
46 $(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $^ -L. -lnetplan `pkg-config --cflags --libs glib-2.0 gio-2.0 yaml-0.1 uuid`
3347
3448 netplan-dbus: src/dbus.c src/_features.h
35 $(CC) $(BUILDFLAGS) $(CFLAGS) -o $@ $^ `pkg-config --cflags --libs libsystemd glib-2.0`
49 $(CC) $(BUILDFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $^ `pkg-config --cflags --libs libsystemd glib-2.0`
3650
3751 src/_features.h: src/[^_]*.[hc]
38 echo "#include <stddef.h>\nstatic const char *feature_flags[] __attribute__((__unused__)) = {" > $@
52 printf "#include <stddef.h>\nstatic const char *feature_flags[] __attribute__((__unused__)) = {\n" > $@
3953 awk 'match ($$0, /netplan-feature:.*/ ) { $$0=substr($$0, RSTART, RLENGTH); print "\""$$2"\"," }' $^ >> $@
4054 echo "NULL, };" >> $@
4155
4862 clean:
4963 rm -f netplan/_features.py src/_features.h
5064 rm -f generate doc/*.html doc/*.[1-9]
65 rm -f *.o *.so*
5166 rm -f netplan-dbus dbus/*.service
5267 rm -f *.gcda *.gcno generate.info
5368 rm -rf test-coverage .coverage
8499 python3-coverage xml --omit=/usr* || true
85100
86101 install: default
87 mkdir -p $(DESTDIR)/$(SBINDIR) $(DESTDIR)/$(ROOTLIBEXECDIR)/netplan $(DESTDIR)/$(SYSTEMD_GENERATOR_DIR)
102 mkdir -p $(DESTDIR)/$(SBINDIR) $(DESTDIR)/$(ROOTLIBEXECDIR)/netplan $(DESTDIR)/$(SYSTEMD_GENERATOR_DIR) $(DESTDIR)/$(LIBDIR)
88103 mkdir -p $(DESTDIR)/$(MANDIR)/man5 $(DESTDIR)/$(MANDIR)/man8
89104 mkdir -p $(DESTDIR)/$(DOCDIR)/netplan/examples
90105 mkdir -p $(DESTDIR)/$(DATADIR)/netplan/netplan
106 mkdir -p $(DESTDIR)/$(INCLUDEDIR)/netplan
91107 install -m 755 generate $(DESTDIR)/$(ROOTLIBEXECDIR)/netplan/
92108 find netplan/ -name '*.py' -exec install -Dm 644 "{}" "$(DESTDIR)/$(DATADIR)/netplan/{}" \;
93109 install -m 755 src/netplan.script $(DESTDIR)/$(DATADIR)/netplan/
94110 ln -srf $(DESTDIR)/$(DATADIR)/netplan/netplan.script $(DESTDIR)/$(SBINDIR)/netplan
95111 ln -srf $(DESTDIR)/$(ROOTLIBEXECDIR)/netplan/generate $(DESTDIR)/$(SYSTEMD_GENERATOR_DIR)/netplan
112 # lib
113 install -m 644 *.so.* $(DESTDIR)/$(LIBDIR)/
114 ln -snf libnetplan.so.$(NETPLAN_SOVER) $(DESTDIR)/$(LIBDIR)/libnetplan.so
115 # headers, dev data
116 install -m 644 src/*.h $(DESTDIR)/$(INCLUDEDIR)/netplan/
117 # TODO: install pkg-config once available
118 # docs, data
96119 install -m 644 doc/*.html $(DESTDIR)/$(DOCDIR)/netplan/
97120 install -m 644 examples/*.yaml $(DESTDIR)/$(DOCDIR)/netplan/examples/
98121 install -m 644 doc/*.5 $(DESTDIR)/$(MANDIR)/man5/
99122 install -m 644 doc/*.8 $(DESTDIR)/$(MANDIR)/man8/
100 install -D -m 644 src/netplan-wpa@.service $(DESTDIR)/$(SYSTEMD_UNIT_DIR)/netplan-wpa@.service
101123 install -T -D -m 644 netplan.completions $(DESTDIR)/$(BASH_COMPLETIONS_DIR)/netplan
102124 # dbus
103125 mkdir -p $(DESTDIR)/$(DATADIR)/dbus-1/system.d $(DESTDIR)/$(DATADIR)/dbus-1/system-services
2929 The top-level node in a netplan configuration file is a ``network:`` mapping
3030 that contains ``version: 2`` (the YAML currently being used by curtin, MaaS,
3131 etc. is version 1), and then device definitions grouped by their type, such as
32 ``ethernets:``, ``wifis:``, or ``bridges:``. These are the types that our
32 ``ethernets:``, ``modems:``, ``wifis:``, or ``bridges:``. These are the types that our
3333 renderer can understand and are supported by our backends.
3434
3535 Each type block contains device definitions as a map where the keys (called
5151
5252 Physical devices
5353
54 : (Examples: ethernet, wifi) These can dynamically come and go between
54 : (Examples: ethernet, modem, wifi) These can dynamically come and go between
5555 reboots and even during runtime (hotplugging). In the generic case, they
5656 can be selected by ``match:`` rules on desired properties, such as name/name
5757 pattern, MAC address, driver, or device paths. In general these will match
132132
133133 : Enable wake on LAN. Off by default.
134134
135 ``emit-lldp`` (bool)
136
137 : (networkd backend only) Whether to emit LLDP packets. Off by default.
138
135139
136140 ## Common properties for all device types
137141
141145 ``networkd`` and ``NetworkManager``. This property can be specified globally
142146 in ``networks:``, for a device type (in e. g. ``ethernets:``) or
143147 for a particular device definition. Default is ``networkd``.
148
149 The ``renderer`` property has one additional acceptable value for vlan objects
150 (i. e. defined in ``vlans:``): ``sriov``. If a vlan is defined with the ``sriov``
151 renderer for an SR-IOV Virtual Function interface, this causes netplan to set
152 up a hardware VLAN filter for it. There can be only one defined per VF.
144153
145154 ``dhcp4`` (bool)
146155
197206
198207 : (networkd backend only) Designate the connection as "critical to the
199208 system", meaning that special care will be taken by systemd-networkd to
200 not release the IP from DHCP when the daemon is restarted.
209 not release the assigned IP when the daemon is restarted.
201210
202211 ``dhcp-identifier`` (scalar)
203212
233242 but will not be addressable from the network.
234243
235244 Example: ``addresses: [192.168.14.2/24, "2001:1::1/64"]``
245
246 ``ipv6-address-generation`` (scalar)
247
248 : Configure method for creating the address for use with RFC4862 IPv6
249 Stateless Address Autoconfiguration. Possible values are ``eui64``
250 or ``stable-privacy``.
236251
237252 ``gateway4``, ``gateway6`` (scalar)
238253
539554 : Password to use to decrypt the private key specified in
540555 ``client-key`` if it is encrypted.
541556
557 ``phase2-auth`` (scalar)
558 : Phase 2 authentication mechanism.
559
542560
543561 ## Properties for device type ``ethernets:``
544 Ethernet device definitions do not support any specific properties beyond the
545 common ones described above.
562 Ethernet device definitions, beyond common ones described above, also support
563 some additional properties that can be used for SR-IOV devices.
564
565 ``link`` (scalar)
566
567 : (SR-IOV devices only) The ``link`` property declares the device as a
568 Virtual Function of the selected Physical Function device, as identified
569 by the given netplan id.
570
571 Example:
572
573 ethernets:
574 enp1: {...}
575 enp1s16f1:
576 link: enp1
577
578 ``virtual-function-count`` (scalar)
579
580 : (SR-IOV devices only) In certain special cases VFs might need to be
581 configured outside of netplan. For such configurations ``virtual-function-count``
582 can be optionally used to set an explicit number of Virtual Functions for
583 the given Physical Function. If unset, the default is to create only as many
584 VFs as are defined in the netplan configuration. This should be used for special
585 cases only.
586
587 ## Properties for device type ``modems:``
588 GSM/CDMA modem configuration is only supported for the ``NetworkManager`` backend. ``systemd-networkd`` does
589 not support modems.
590
591 ``apn`` (scalar)
592 : Set the carrier APN (Access Point Name). This can be omitted if ``auto-config`` is enabled.
593
594 ``auto-config`` (bool)
595 : Specify whether to try and autoconfigure the modem by doing a lookup of the carrier
596 against the Mobile Broadband Provider database. This may not work for all carriers.
597
598 ``device-id`` (scalar)
599 : Specify the device ID (as given by the WWAN management service) of the modem to match.
600 This can be found using ``mmcli``.
601
602 ``network-id`` (scalar)
603 : Specify the Network ID (GSM LAI format). If this is specified, the device will not roam networks.
604
605 ``number`` (scalar)
606 : The number to dial to establish the connection to the mobile broadband network. (Deprecated for GSM)
607
608 ``password`` (scalar)
609 : Specify the password used to authenticate with the carrier network. This can be omitted
610 if ``auto-config`` is enabled.
611
612 ``pin`` (scalar)
613 : Specify the SIM PIN to allow it to operate if a PIN is set.
614
615 ``sim-id`` (scalar)
616 : Specify the SIM unique identifier (as given by the WWAN management service) which this
617 connection applies to. If given, the connection will apply to any device also allowed by
618 ``device-id`` which contains a SIM card matching the given identifier.
619
620 ``sim-operator-id`` (scalar)
621 : Specify the MCC/MNC string (such as "310260" or "21601") which identifies the carrier that
622 this connection should apply to. If given, the connection will apply to any device also
623 allowed by ``device-id`` and ``sim-id`` which contains a SIM card provisioned by the given operator.
624
625 ``username`` (scalar)
626 : Specify the username used to authentiate with the carrier network. This can be omitted if
627 ``auto-config`` is enabled.
546628
547629 ## Properties for device type ``wifis:``
548630 Note that ``systemd-networkd`` does not natively support wifi, so you need
573655 ``ap`` (create an access point to which other devices can connect),
574656 and ``adhoc`` (peer to peer networks without a central access point).
575657 ``ap`` is only supported with NetworkManager.
658
659 ``bssid`` (scalar)
660 : If specified, directs the device to only associate with the given
661 access point.
662
663 ``band`` (scalar)
664 : Possible bands are ``5GHz`` (for 5GHz 802.11a) and ``2.4GHz``
665 (for 2.4GHz 802.11), do not restrict the 802.11 frequency band of the
666 network if unset (the default).
667
668 ``channel`` (scalar)
669 : Wireless channel to use for the Wi-Fi connection. Because channel
670 numbers overlap between bands, this property takes effect only if
671 the ``band`` property is also set.
672
673 ``wakeonwlan`` (sequence of scalars)
674
675 : This enables WakeOnWLan on supported devices. Not all drivers support all
676 options. May be any combination of ``any``, ``disconnect``, ``magic_pkt``,
677 ``gtk_rekey_failure``, ``eap_identity_req``, ``four_way_handshake``,
678 ``rfkill_release`` or ``tcp`` (NetworkManager only). Or the exclusive
679 ``default`` flag (the default).
576680
577681 ## Properties for device type ``bridges:``
578682
895999 id: 2
8961000 link: eno1
8971001 addresses: ...
1002
1003
1004 ## Backend-specific configuration parameters
1005
1006 In addition to the other fields available to configure interfaces, some
1007 backends may require to record some of their own parameters in netplan,
1008 especially if the netplan definitions are generated automatically by the
1009 consumer of that backend. Currently, this is only used with ``NetworkManager``.
1010
1011 ``networkmanager`` (mapping)
1012
1013 : Keeps the NetworkManager-specific configuration parameters used by the
1014 daemon to recognize connections.
1015
1016 ``name`` (scalar)
1017 : Set the display name for the connection.
1018
1019 ``uuid`` (scalar)
1020 : Defines the UUID (unique identifier) for this connection, as
1021 generated by NetworkManager itself.
1022
1023 ``stable-id`` (scalar)
1024 : Defines the stable ID (a different form of a connection name) used
1025 by NetworkManager in case the name of the connection might otherwise
1026 change, such as when sharing connections between users.
1027
1028 ``device`` (scalar)
1029 : Defines the interface name for which this connection applies.
8981030
8991031
9001032 ## Examples
0 network:
1 version: 2
2 renderer: networkd
3 ethernets:
4 enp3s0:
5 dhcp4: true
6 auth:
7 key-management: 802.1x
8 method: ttls
9 identity: fluffy@cisco.com
10 password: hash:83...11
0 network:
1 version: 2
2 renderer: networkd
3 ethernets:
4 addresses: [ "2001:cafe:face:beef::dead:dead/64" ]
5 routes:
6 - to: "2001:cafe:face::1/128"
7 scope: link
8 - to: "::/0"
9 via: "2001:cafe:face::1"
10 on-link: true
0 network:
1 version: 2
2 renderer: NetworkManager
3 modems:
4 cdc-wdm1:
5 mtu: 1600
6 apn: ISP.CINGULAR
7 username: ISP@CINGULARGPRS.COM
8 password: CINGULAR1
9 number: "*99#"
10 network-id: 24005
11 device-id: da812de91eec16620b06cd0ca5cbc7ea25245222
12 pin: 2345
13 sim-id: 89148000000060671234
14 sim-operator-id: 310260
00 #!/usr/bin/python3
11 #
2 # Copyright (C) 2018 Canonical, Ltd.
2 # Copyright (C) 2018-2020 Canonical, Ltd.
33 # Author: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
4 # Author: Łukasz 'sil2100' Zemczak <lukasz.zemczak@canonical.com>
45 #
56 # This program is free software; you can redistribute it and/or modify
67 # it under the terms of the GNU General Public License as published by
2526
2627 import netplan.cli.utils as utils
2728 from netplan.configmanager import ConfigManager, ConfigurationError
29 from netplan.cli.sriov import apply_sriov_config
2830
2931 import netifaces
3032
7375 old_files_networkd = bool(glob.glob('/run/systemd/network/*netplan-*'))
7476 old_files_nm = bool(glob.glob('/run/NetworkManager/system-connections/netplan-*'))
7577
76 if run_generate and subprocess.call([utils.get_generator_path()]) != 0:
78 generator_call = []
79 generate_out = None
80 if 'NETPLAN_PROFILE' in os.environ:
81 generator_call.extend(['valgrind', '--leak-check=full'])
82 generate_out = subprocess.STDOUT
83
84 generator_call.append(utils.get_generator_path())
85 if run_generate and subprocess.call(generator_call, stderr=generate_out) != 0:
7786 if exit_on_error:
7887 sys.exit(os.EX_CONFIG)
7988 else:
117126 else:
118127 logging.debug('no netplan generated NM configuration exists')
119128
129 # Refresh devices now; restarting a backend might have made something appear.
130 devices = netifaces.interfaces()
131
120132 # evaluate config for extra steps we need to take (like renaming)
121133 # for now, only applies to non-virtual (real) devices.
122134 config_manager.parse()
123135 changes = NetplanApply.process_link_changes(devices, config_manager)
136
137 # apply any SR-IOV related changes, if applicable
138 try:
139 apply_sriov_config(devices, config_manager)
140 except (ConfigurationError, RuntimeError) as e:
141 logging.error(str(e))
142 if exit_on_error:
143 sys.exit(1)
124144
125145 # if the interface is up, we can still apply some .link file changes
126146 devices = netifaces.interfaces()
215235 # do not rename members of virtual devices. MAC addresses
216236 # may be the same for all interface members.
217237 continue
218 # try to get the device's driver for matching.
219 devdir = os.path.join('/sys/class/net', interface)
220 try:
221 with open(os.path.join(devdir, 'operstate')) as f:
222 state = f.read().strip()
223 if state != 'down':
224 logging.debug('device %s operstate is %s, not changing', interface, state)
225 continue
226 except IOError as e:
227 logging.error('Cannot determine operstate of %s: %s', interface, str(e))
228 continue
229
230 try:
231 driver = os.path.realpath(os.path.join(devdir, 'device', 'driver'))
232 driver_name = os.path.basename(driver)
233 except IOError as e:
234 logging.debug('Cannot replug %s: cannot read link %s/device: %s', interface, devdir, str(e))
235 driver_name = None
236 pass
237
238 link = netifaces.ifaddresses(interface)[netifaces.AF_LINK][0]
239 macaddress = link.get('addr')
238
239 driver_name = utils.get_interface_driver_name(interface, only_down=True)
240 macaddress = utils.get_interface_macaddress(interface)
240241 if driver_name in matches['by-driver']:
241242 new_name = matches['by-driver'][driver_name]
242243 logging.debug(new_name)
0 #!/usr/bin/python3
1 #
2 # Copyright (C) 2020 Canonical, Ltd.
3 # Author: Łukasz 'sil2100' Zemczak <lukasz.zemczak@canonical.com>
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; version 3.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 import logging
18 import os
19 import subprocess
20
21 from collections import defaultdict
22
23 import netplan.cli.utils as utils
24 from netplan.configmanager import ConfigurationError
25
26 import netifaces
27
28
29 def _get_target_interface(interfaces, config_manager, pf_link, pfs):
30 if pf_link not in pfs:
31 # handle the match: syntax, get the actual device name
32 pf_match = config_manager.ethernets[pf_link].get('match')
33 if pf_match:
34 by_name = pf_match.get('name')
35 by_mac = pf_match.get('macaddress')
36 by_driver = pf_match.get('driver')
37
38 for interface in interfaces:
39 if ((by_name and not utils.is_interface_matching_name(interface, by_name)) or
40 (by_mac and not utils.is_interface_matching_macaddress(interface, by_mac)) or
41 (by_driver and not utils.is_interface_matching_driver_name(interface, by_driver))):
42 continue
43 # we have a matching PF
44 # store the matching interface in the dictionary of
45 # active PFs, but error out if we matched more than one
46 if pf_link in pfs:
47 raise ConfigurationError('matched more than one interface for a PF device: %s' % pf_link)
48 pfs[pf_link] = interface
49 else:
50 # no match field, assume entry name is interface name
51 if pf_link in interfaces:
52 pfs[pf_link] = pf_link
53
54 return pfs.get(pf_link, None)
55
56
57 def get_vf_count_and_functions(interfaces, config_manager,
58 vf_counts, vfs, pfs):
59 """
60 Go through the list of netplan ethernet devices and identify which are
61 PFs and VFs, matching the former with actual networking interfaces.
62 Count how many VFs each PF will need.
63 """
64 explicit_counts = {}
65 for ethernet, settings in config_manager.ethernets.items():
66 if not settings:
67 continue
68 if ethernet == 'renderer':
69 continue
70
71 # we now also support explicitly stating how many VFs should be
72 # allocated for a PF
73 explicit_num = settings.get('virtual-function-count')
74 if explicit_num:
75 pf = _get_target_interface(interfaces, config_manager, ethernet, pfs)
76 if pf:
77 explicit_counts[pf] = explicit_num
78 continue
79
80 pf_link = settings.get('link')
81 if pf_link and pf_link in config_manager.ethernets:
82 _get_target_interface(interfaces, config_manager, pf_link, pfs)
83
84 if pf_link in pfs:
85 vf_counts[pfs[pf_link]] += 1
86 else:
87 logging.warning('could not match physical interface for the defined PF: %s' % pf_link)
88 # continue looking for other VFs
89 continue
90
91 # we can't yet perform matching on VFs as those are only
92 # created later - but store, for convenience, all the valid
93 # VFs that we encounter so far
94 vfs[ethernet] = None
95
96 # sanity check: since we can explicitly state the VF count, make sure
97 # that this number isn't smaller than the actual number of VFs declared
98 # the explicit number also overrides the number of actual VFs
99 for pf, count in explicit_counts.items():
100 if pf in vf_counts and vf_counts[pf] > count:
101 raise ConfigurationError(
102 'more VFs allocated than the explicit size declared: %s > %s' % (vf_counts[pf], count))
103 vf_counts[pf] = count
104
105
106 def set_numvfs_for_pf(pf, vf_count):
107 """
108 Allocate the required number of VFs for the selected PF.
109 """
110 if vf_count > 256:
111 raise ConfigurationError(
112 'cannot allocate more VFs for PF %s than the SR-IOV maximum: %s > 256' % (pf, vf_count))
113
114 devdir = os.path.join('/sys/class/net', pf, 'device')
115 numvfs_path = os.path.join(devdir, 'sriov_numvfs')
116 totalvfs_path = os.path.join(devdir, 'sriov_totalvfs')
117 try:
118 with open(totalvfs_path) as f:
119 vf_max = int(f.read().strip())
120 except IOError as e:
121 raise RuntimeError('failed parsing sriov_totalvfs for %s: %s' % (pf, str(e)))
122 except ValueError:
123 raise RuntimeError('invalid sriov_totalvfs value for %s' % pf)
124
125 if vf_count > vf_max:
126 raise ConfigurationError(
127 'cannot allocate more VFs for PF %s than supported: %s > %s (sriov_totalvfs)' % (pf, vf_count, vf_max))
128
129 try:
130 with open(numvfs_path, 'w') as f:
131 f.write(str(vf_count))
132 except IOError as e:
133 bail = True
134 if e.errno == 16: # device or resource busy
135 logging.warning('device or resource busy while setting sriov_numvfs for %s, trying workaround' % pf)
136 try:
137 # doing this in two open/close sequences so that
138 # it's as close to writing via shell as possible
139 with open(numvfs_path, 'w') as f:
140 f.write('0')
141 with open(numvfs_path, 'w') as f:
142 f.write(str(vf_count))
143 except IOError as e_inner:
144 e = e_inner
145 else:
146 bail = False
147 if bail:
148 raise RuntimeError('failed setting sriov_numvfs to %s for %s: %s' % (vf_count, pf, str(e)))
149
150 return True
151
152
153 def perform_hardware_specific_quirks(pf):
154 """
155 Perform any hardware-specific quirks for the given SR-IOV device to make
156 sure all the VF-count changes are applied.
157 """
158 devdir = os.path.join('/sys/class/net', pf, 'device')
159 try:
160 with open(os.path.join(devdir, 'vendor')) as f:
161 device_id = f.read().strip()[2:]
162 with open(os.path.join(devdir, 'device')) as f:
163 vendor_id = f.read().strip()[2:]
164 except IOError as e:
165 raise RuntimeError('could not determine vendor and device ID of %s: %s' % (pf, str(e)))
166
167 combined_id = ':'.join([vendor_id, device_id])
168 quirk_devices = () # TODO: add entries to the list
169 if combined_id in quirk_devices:
170 # some devices need special handling, so this is the place
171
172 # Currently this part is empty, but has been added as a preemptive
173 # measure, as apparently a lot of SR-IOV cards have issues with
174 # dynamically allocating VFs. Some cards seem to require a full
175 # kernel module reload cycle after changing the sriov_numvfs value
176 # for the changes to come into effect.
177 # Any identified card/vendor can then be special-cased here, if
178 # needed.
179 pass
180
181
182 def apply_vlan_filter_for_vf(pf, vf, vlan_name, vlan_id, prefix='/'):
183 """
184 Apply the hardware VLAN filtering for the selected VF.
185 """
186
187 # this is more complicated, because to do this, we actually need to have
188 # the vf index - just knowing the vf interface name is not enough
189 vf_index = None
190 # the prefix argument is here only for unit testing purposes
191 vf_devdir = os.path.join(prefix, 'sys/class/net', vf, 'device')
192 vf_dev_id = os.path.basename(os.readlink(vf_devdir))
193 pf_devdir = os.path.join(prefix, 'sys/class/net', pf, 'device')
194 for f in os.listdir(pf_devdir):
195 if 'virtfn' in f:
196 dev_path = os.path.join(pf_devdir, f)
197 dev_id = os.path.basename(os.readlink(dev_path))
198 if dev_id == vf_dev_id:
199 vf_index = f[6:]
200 break
201
202 if not vf_index:
203 raise RuntimeError(
204 'could not determine the VF index for %s while configuring vlan %s' % (vf, vlan_name))
205
206 # now, create the VLAN filter
207 # TODO: would be best if we did this directl via python, without calling
208 # the iproute tooling
209 try:
210 subprocess.check_call(['ip', 'link', 'set',
211 'dev', pf,
212 'vf', vf_index,
213 'vlan', str(vlan_id)],
214 stdout=subprocess.DEVNULL,
215 stderr=subprocess.DEVNULL)
216 except subprocess.CalledProcessError:
217 raise RuntimeError(
218 'failed setting SR-IOV VLAN filter for vlan %s (ip link set command failed)' % vlan_name)
219
220
221 def apply_sriov_config(interfaces, config_manager):
222 """
223 Go through all interfaces, identify which ones are SR-IOV VFs, create
224 them and perform all other necessary setup.
225 """
226
227 # for sr-iov devices, we identify VFs by them having a link: field
228 # pointing to an PF. So let's browse through all ethernet devices,
229 # find all that are VFs and count how many of those are linked to
230 # particular PFs, as we need to then set the numvfs for each.
231 vf_counts = defaultdict(int)
232 # we also store all matches between VF/PF netplan entry names and
233 # interface that they're currently matching to
234 vfs = {}
235 pfs = {}
236
237 get_vf_count_and_functions(
238 interfaces, config_manager, vf_counts, vfs, pfs)
239
240 # setup the required number of VFs per PF
241 # at the same time store which PFs got changed in case the NICs
242 # require some special quirks for the VF number to change
243 vf_count_changed = []
244 if vf_counts:
245 for pf, vf_count in vf_counts.items():
246 if not set_numvfs_for_pf(pf, vf_count):
247 continue
248
249 vf_count_changed.append(pf)
250
251 if vf_count_changed:
252 # some cards need special treatment when we want to change the
253 # number of enabled VFs
254 for pf in vf_count_changed:
255 perform_hardware_specific_quirks(pf)
256
257 # also, since the VF number changed, the interfaces list also
258 # changed, so we need to refresh it
259 interfaces = netifaces.interfaces()
260
261 # now in theory we should have all the new VFs set up and existing;
262 # this is needed because we will have to now match the defined VF
263 # entries to existing interfaces, otherwise we won't be able to set
264 # filtered VLANs for those.
265 # XXX: does matching those even make sense?
266 for vf in vfs:
267 settings = config_manager.ethernets.get(vf)
268 match = settings.get('match')
269 if match:
270 # right now we only match by name, as I don't think matching per
271 # driver and/or macaddress makes sense
272 by_name = match.get('name')
273 # by_mac = match.get('macaddress')
274 # by_driver = match.get('driver')
275 # TODO: print warning if other matches are provided
276
277 for interface in interfaces:
278 if by_name and not utils.is_interface_matching_name(interface, by_name):
279 continue
280 if vf in vfs and vfs[vf]:
281 raise ConfigurationError('matched more than one interface for a VF device: %s' % vf)
282 vfs[vf] = interface
283 else:
284 if vf in interfaces:
285 vfs[vf] = vf
286
287 filtered_vlans_set = set()
288 for vlan, settings in config_manager.vlans.items():
289 # there is a special sriov vlan renderer that one can use to mark
290 # a selected vlan to be done in hardware (VLAN filtering)
291 if settings.get('renderer') == 'sriov':
292 # this only works for SR-IOV VF interfaces
293 link = settings.get('link')
294 vlan_id = settings.get('id')
295 if not vlan_id:
296 raise ConfigurationError(
297 'no id property defined for SR-IOV vlan %s' % vlan)
298
299 vf = vfs.get(link)
300 if not vf:
301 # it is possible this is not an error, for instance when
302 # the configuration has been defined 'for the future'
303 # XXX: but maybe we should error out here as well?
304 logging.warning(
305 'SR-IOV vlan defined for %s but link %s is either not a VF or has no matches' % (vlan, link))
306 continue
307
308 # get the parent pf interface
309 # first we fetch the related vf netplan entry
310 vf_parent_entry = config_manager.ethernets.get(link).get('link')
311 # and finally, get the matched pf interface
312 pf = pfs.get(vf_parent_entry)
313
314 if vf in filtered_vlans_set:
315 raise ConfigurationError(
316 'interface %s for netplan device %s (%s) already has an SR-IOV vlan defined' % (vf, link, vlan))
317
318 apply_vlan_filter_for_vf(pf, vf, vlan, vlan_id)
319 filtered_vlans_set.add(vf)
00 #!/usr/bin/python3
11 #
2 # Copyright (C) 2018 Canonical, Ltd.
2 # Copyright (C) 2018-2020 Canonical, Ltd.
33 # Author: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
4 # Author: Łukasz 'sil2100' Zemczak <lukasz.zemczak@canonical.com>
45 #
56 # This program is free software; you can redistribute it and/or modify
67 # it under the terms of the GNU General Public License as published by
1617
1718 import sys
1819 import os
20 import logging
21 import fnmatch
1922 import argparse
2023 import subprocess
24 import netifaces
2125
2226 NM_SERVICE_NAME = 'NetworkManager.service'
2327 NM_SNAP_SERVICE_NAME = 'snap.network-manager.networkmanager.service'
7983 command.append(service)
8084
8185 subprocess.check_call(command)
86
87
88 def get_interface_driver_name(interface, only_down=False): # pragma: nocover (covered in autopkgtest)
89 devdir = os.path.join('/sys/class/net', interface)
90 if only_down:
91 try:
92 with open(os.path.join(devdir, 'operstate')) as f:
93 state = f.read().strip()
94 if state != 'down':
95 logging.debug('device %s operstate is %s, not changing', interface, state)
96 return None
97 except IOError as e:
98 logging.error('Cannot determine operstate of %s: %s', interface, str(e))
99 return None
100
101 try:
102 driver = os.path.realpath(os.path.join(devdir, 'device', 'driver'))
103 driver_name = os.path.basename(driver)
104 except IOError as e:
105 logging.debug('Cannot replug %s: cannot read link %s/device: %s', interface, devdir, str(e))
106 return None
107
108 return driver_name
109
110
111 def get_interface_macaddress(interface): # pragma: nocover (covered in autopkgtest)
112 link = netifaces.ifaddresses(interface)[netifaces.AF_LINK][0]
113
114 return link.get('addr')
115
116
117 def is_interface_matching_name(interface, match_driver):
118 return fnmatch.fnmatchcase(interface, match_driver)
119
120
121 def is_interface_matching_driver_name(interface, match_driver):
122 driver_name = get_interface_driver_name(interface)
123
124 return match_driver == driver_name
125
126
127 def is_interface_matching_macaddress(interface, match_mac):
128 macaddress = get_interface_macaddress(interface)
129
130 return match_mac == macaddress
82131
83132
84133 class NetplanCommand(argparse.Namespace):
5252 static void
5353 nd_iterator_list(gpointer value, gpointer user_data)
5454 {
55 if (write_networkd_conf((net_definition*) value, (const char*) user_data))
55 if (write_networkd_conf((NetplanNetDefinition*) value, (const char*) user_data))
5656 any_networkd = TRUE;
57 write_nm_conf((net_definition*) value, (const char*) user_data);
57 write_nm_conf((NetplanNetDefinition*) value, (const char*) user_data);
5858 }
5959
6060
9090
9191 g_hash_table_iter_init (&iter, netdefs);
9292 while (g_hash_table_iter_next (&iter, &key, &value)) {
93 net_definition *nd = (net_definition *) value;
93 NetplanNetDefinition *nd = (NetplanNetDefinition *) value;
9494 if (!g_strcmp0(nd->set_name, interface))
9595 g_ptr_array_add (found, (gpointer) nd);
9696 else if (!g_strcmp0(nd->id, interface))
103103 // LCOV_EXCL_START
104104 g_hash_table_iter_init (&iter, netdefs);
105105 while (g_hash_table_iter_next (&iter, &key, &value)) {
106 net_definition *nd = (net_definition *) value;
106 NetplanNetDefinition *nd = (NetplanNetDefinition *) value;
107107 if (!g_strcmp0(nd->match.driver, driver))
108108 g_ptr_array_add (found, (gpointer) nd);
109109 }
117117 goto exit_find;
118118 }
119119 else {
120 net_definition *nd = (net_definition *)g_ptr_array_index (found, 0);
120 const NetplanNetDefinition *nd = (NetplanNetDefinition *)g_ptr_array_index (found, 0);
121121 g_printf("id=%s, backend=%s, set_name=%s, match_name=%s, match_mac=%s, match_driver=%s\n",
122122 nd->id,
123 netdef_backend_to_name[nd->backend],
123 netplan_backend_to_name[nd->backend],
124124 nd->set_name,
125125 nd->match.original_name,
126126 nd->match.mac,
140140 GError* error = NULL;
141141
142142 g_debug("Processing input file %s..", f);
143 if (!parse_yaml(f, &error)) {
143 if (!netplan_parse_yaml(f, &error)) {
144144 g_fprintf(stderr, "%s\n", error->message);
145145 exit(1);
146146 }
236236 process_input_file(g_hash_table_lookup(configs, i->data));
237237 }
238238
239 if (!finish_parse(&error)) {
239 netdefs = netplan_finish_parse(&error);
240 if (error) {
240241 g_fprintf(stderr, "%s\n", error->message);
241242 exit(1);
242243 }
265266
266267 /* Disable /usr/lib/NetworkManager/conf.d/10-globally-managed-devices.conf
267268 * (which restricts NM to wifi and wwan) if global renderer is NM */
268 if (get_global_backend() == BACKEND_NM)
269 if (netplan_get_global_backend() == NETPLAN_BACKEND_NM)
269270 g_string_free_to_file(g_string_new(NULL), rootdir, "/run/NetworkManager/conf.d/10-globally-managed-devices.conf", NULL);
270271
271272 if (called_as_generator) {
+0
-11
src/netplan-wpa@.service less more
0 [Unit]
1 Description=WPA supplicant for netplan %I
2 DefaultDependencies=no
3 Requires=sys-subsystem-net-devices-%i.device
4 After=sys-subsystem-net-devices-%i.device
5 Before=network.target
6 Wants=network.target
7
8 [Service]
9 Type=simple
10 ExecStart=/sbin/wpa_supplicant -c /run/netplan/wpa-%I.conf -i%I
1515 */
1616
1717 #include <stdlib.h>
18 #include <string.h>
1819 #include <unistd.h>
20 #include <ctype.h>
1921 #include <errno.h>
2022 #include <sys/stat.h>
2123
2628 #include "parse.h"
2729 #include "util.h"
2830
31 /**
32 * Append WiFi frequencies to wpa_supplicant's freq_list=
33 */
34 static void
35 wifi_append_freq(gpointer key, gpointer value, gpointer user_data)
36 {
37 GString* s = user_data;
38 g_string_append_printf(s, "%d ", GPOINTER_TO_INT(value));
39 }
40
41 /**
42 * append wowlan_triggers= string for wpa_supplicant.conf
43 */
44 static void
45 append_wifi_wowlan_flags(NetplanWifiWowlanFlag flag, GString* str) {
46 if (flag & NETPLAN_WIFI_WOWLAN_TYPES[0].flag || flag >= NETPLAN_WIFI_WOWLAN_TCP) {
47 g_fprintf(stderr, "ERROR: unsupported wowlan_triggers mask: 0x%x\n", flag);
48 exit(1);
49 }
50 for (unsigned i = 0; NETPLAN_WIFI_WOWLAN_TYPES[i].name != NULL; ++i) {
51 if (flag & NETPLAN_WIFI_WOWLAN_TYPES[i].flag) {
52 g_string_append_printf(str, "%s ", NETPLAN_WIFI_WOWLAN_TYPES[i].name);
53 }
54 }
55 /* replace trailing space with newline */
56 str = g_string_overwrite(str, str->len-1, "\n");
57 }
2958
3059 /**
3160 * Append [Match] section of @def to @s.
3261 */
3362 static void
34 append_match_section(net_definition* def, GString* s, gboolean match_rename)
63 append_match_section(const NetplanNetDefinition* def, GString* s, gboolean match_rename)
3564 {
3665 /* Note: an empty [Match] section is interpreted as matching all devices,
3766 * which is what we want for the simple case that you only have one device
4776 if (!match_rename && def->match.original_name)
4877 g_string_append_printf(s, "OriginalName=%s\n", def->match.original_name);
4978 if (match_rename) {
50 if (def->type >= ND_VIRTUAL)
79 if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL)
5180 g_string_append_printf(s, "Name=%s\n", def->id);
5281 else if (def->set_name)
5382 g_string_append_printf(s, "Name=%s\n", def->set_name);
75104 }
76105
77106 static void
78 write_bridge_params(GString* s, net_definition* def)
107 write_bridge_params(GString* s, const NetplanNetDefinition* def)
79108 {
80109 GString *params = NULL;
81110
101130 }
102131
103132 static void
104 write_tunnel_params(GString* s, net_definition* def)
133 write_tunnel_params(GString* s, const NetplanNetDefinition* def)
105134 {
106135 GString *params = NULL;
107136
108137 params = g_string_sized_new(200);
109138
110139 g_string_printf(params, "Independent=true\n");
111 if (def->tunnel.mode == TUNNEL_MODE_IPIP6 || def->tunnel.mode == TUNNEL_MODE_IP6IP6)
140 if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_IPIP6 || def->tunnel.mode == NETPLAN_TUNNEL_MODE_IP6IP6)
112141 g_string_append_printf(params, "Mode=%s\n", tunnel_mode_to_string(def->tunnel.mode));
113142 g_string_append_printf(params, "Local=%s\n", def->tunnel.local_ip);
114143 g_string_append_printf(params, "Remote=%s\n", def->tunnel.remote_ip);
122151 }
123152
124153 static void
125 write_link_file(net_definition* def, const char* rootdir, const char* path)
154 write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char* path)
126155 {
127156 GString* s = NULL;
128157 mode_t orig_umask;
129158
130 /* Don't write .link files for virtual devices; they use .netdev instead */
131 if (def->type >= ND_VIRTUAL)
159 /* Don't write .link files for virtual devices; they use .netdev instead.
160 * Don't write .link files for MODEM devices, as they aren't supported by networkd.
161 */
162 if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL || def->type == NETPLAN_DEF_TYPE_MODEM)
132163 return;
133164
134165 /* do we need to write a .link file? */
169200
170201
171202 static void
172 write_bond_parameters(net_definition* def, GString* s)
203 write_bond_parameters(const NetplanNetDefinition* def, GString* s)
173204 {
174205 GString* params = NULL;
175206
248279 }
249280
250281 static void
251 write_netdev_file(net_definition* def, const char* rootdir, const char* path)
282 write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const char* path)
252283 {
253284 GString* s = NULL;
254285 mode_t orig_umask;
255286
256 g_assert(def->type >= ND_VIRTUAL);
287 g_assert(def->type >= NETPLAN_DEF_TYPE_VIRTUAL);
288
289 if (def->type == NETPLAN_DEF_TYPE_VLAN && def->sriov_vlan_filter) {
290 g_debug("%s is defined as a hardware SR-IOV filtered VLAN, postponing creation", def->id);
291 return;
292 }
257293
258294 /* build file contents */
259295 s = g_string_sized_new(200);
265301 g_string_append_printf(s, "MTUBytes=%u\n", def->mtubytes);
266302
267303 switch (def->type) {
268 case ND_BRIDGE:
304 case NETPLAN_DEF_TYPE_BRIDGE:
269305 g_string_append(s, "Kind=bridge\n");
270306 write_bridge_params(s, def);
271307 break;
272308
273 case ND_BOND:
309 case NETPLAN_DEF_TYPE_BOND:
274310 g_string_append(s, "Kind=bond\n");
275311 write_bond_parameters(def, s);
276312 break;
277313
278 case ND_VLAN:
314 case NETPLAN_DEF_TYPE_VLAN:
279315 g_string_append_printf(s, "Kind=vlan\n\n[VLAN]\nId=%u\n", def->vlan_id);
280316 break;
281317
282 case ND_TUNNEL:
318 case NETPLAN_DEF_TYPE_TUNNEL:
283319 switch(def->tunnel.mode) {
284 case TUNNEL_MODE_GRE:
285 case TUNNEL_MODE_GRETAP:
286 case TUNNEL_MODE_IPIP:
287 case TUNNEL_MODE_IP6GRE:
288 case TUNNEL_MODE_IP6GRETAP:
289 case TUNNEL_MODE_SIT:
290 case TUNNEL_MODE_VTI:
291 case TUNNEL_MODE_VTI6:
320 case NETPLAN_TUNNEL_MODE_GRE:
321 case NETPLAN_TUNNEL_MODE_GRETAP:
322 case NETPLAN_TUNNEL_MODE_IPIP:
323 case NETPLAN_TUNNEL_MODE_IP6GRE:
324 case NETPLAN_TUNNEL_MODE_IP6GRETAP:
325 case NETPLAN_TUNNEL_MODE_SIT:
326 case NETPLAN_TUNNEL_MODE_VTI:
327 case NETPLAN_TUNNEL_MODE_VTI6:
292328 g_string_append_printf(s,
293329 "Kind=%s\n",
294330 tunnel_mode_to_string(def->tunnel.mode));
295331 break;
296332
297 case TUNNEL_MODE_IP6IP6:
298 case TUNNEL_MODE_IPIP6:
333 case NETPLAN_TUNNEL_MODE_IP6IP6:
334 case NETPLAN_TUNNEL_MODE_IPIP6:
299335 g_string_append(s, "Kind=ip6tnl\n");
300336 break;
301337
322358 }
323359
324360 static void
325 write_route(ip_route* r, GString* s)
361 write_route(NetplanIPRoute* r, GString* s)
326362 {
327363 g_string_append_printf(s, "\n[Route]\n");
328364
339375 g_string_append_printf(s, "Type=%s\n", r->type);
340376 if (r->onlink)
341377 g_string_append_printf(s, "GatewayOnlink=true\n");
342 if (r->metric != METRIC_UNSPEC)
378 if (r->metric != NETPLAN_METRIC_UNSPEC)
343379 g_string_append_printf(s, "Metric=%d\n", r->metric);
344 if (r->table != ROUTE_TABLE_UNSPEC)
380 if (r->table != NETPLAN_ROUTE_TABLE_UNSPEC)
345381 g_string_append_printf(s, "Table=%d\n", r->table);
346382 }
347383
348384 static void
349 write_ip_rule(ip_rule* r, GString* s)
385 write_ip_rule(NetplanIPRule* r, GString* s)
350386 {
351387 g_string_append_printf(s, "\n[RoutingPolicyRule]\n");
352388
355391 if (r->to)
356392 g_string_append_printf(s, "To=%s\n", r->to);
357393
358 if (r->table != ROUTE_TABLE_UNSPEC)
394 if (r->table != NETPLAN_ROUTE_TABLE_UNSPEC)
359395 g_string_append_printf(s, "Table=%d\n", r->table);
360 if (r->priority != IP_RULE_PRIO_UNSPEC)
396 if (r->priority != NETPLAN_IP_RULE_PRIO_UNSPEC)
361397 g_string_append_printf(s, "Priority=%d\n", r->priority);
362 if (r->fwmark != IP_RULE_FW_MARK_UNSPEC)
398 if (r->fwmark != NETPLAN_IP_RULE_FW_MARK_UNSPEC)
363399 g_string_append_printf(s, "FirewallMark=%d\n", r->fwmark);
364 if (r->tos != IP_RULE_TOS_UNSPEC)
400 if (r->tos != NETPLAN_IP_RULE_TOS_UNSPEC)
365401 g_string_append_printf(s, "TypeOfService=%d\n", r->tos);
366402 }
367403
370406 "dhcp4_overrides and dhcp6_overrides\n"
371407
372408 static void
373 combine_dhcp_overrides(net_definition* def, dhcp_overrides* combined_dhcp_overrides)
409 combine_dhcp_overrides(const NetplanNetDefinition* def, NetplanDHCPOverrides* combined_dhcp_overrides)
374410 {
375411 /* if only one of dhcp4 or dhcp6 is enabled, those overrides are used */
376412 if (def->dhcp4 && !def->dhcp6) {
423459 }
424460
425461 static void
426 write_network_file(net_definition* def, const char* rootdir, const char* path)
462 write_network_file(const NetplanNetDefinition* def, const char* rootdir, const char* path)
427463 {
428464 GString* network = NULL;
429465 GString* link = NULL;
430466 GString* s = NULL;
431467 mode_t orig_umask;
432468
469 if (def->type == NETPLAN_DEF_TYPE_VLAN && def->sriov_vlan_filter) {
470 g_debug("%s is defined as a hardware SR-IOV filtered VLAN, postponing creation", def->id);
471 return;
472 }
473
433474 /* Prepare the [Link] section of the .network file. */
434475 link = g_string_sized_new(200);
435476
440481 if (def->optional) {
441482 g_string_append(link, "RequiredForOnline=no\n");
442483 }
443 for (unsigned i = 0; optional_address_options[i].name != NULL; ++i) {
444 if (def->optional_addresses & optional_address_options[i].flag) {
445 g_string_append_printf(link, "OptionalAddresses=%s\n", optional_address_options[i].name);
484 for (unsigned i = 0; NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name != NULL; ++i) {
485 if (def->optional_addresses & NETPLAN_OPTIONAL_ADDRESS_TYPES[i].flag) {
486 g_string_append_printf(link, "OptionalAddresses=%s\n", NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name);
446487 }
447488 }
448489 }
449490
491 if (def->mtubytes) {
492 g_string_append_printf(link, "MTUBytes=%d\n", def->mtubytes);
493 }
494
495 if (def->emit_lldp) {
496 g_string_append(network, "EmitLLDP=true\n");
497 }
450498
451499 if (def->dhcp4 && def->dhcp6)
452500 g_string_append(network, "DHCP=yes\n");
475523 if (def->ip6_addresses)
476524 for (unsigned i = 0; i < def->ip6_addresses->len; ++i)
477525 g_string_append_printf(network, "Address=%s\n", g_array_index(def->ip6_addresses, char*, i));
478 if (def->accept_ra == ACCEPT_RA_ENABLED)
526 if (def->ip6_addr_gen_mode) {
527 /* TODO: Figure out how we can configure ipv6-address-generation for networkd.
528 * IPv6Token= seems to be the corresponding option, but it doesn't do
529 * exactly what we need and has quite some restrictions, c.f.:
530 * https://github.com/systemd/systemd/issues/4625
531 * https://github.com/systemd/systemd/pull/14415 */
532 g_fprintf(stderr, "ERROR: %s: ipv6-address-generation is not supported by networkd\n", def->id);
533 exit(1);
534 }
535 if (def->accept_ra == NETPLAN_RA_MODE_ENABLED)
479536 g_string_append_printf(network, "IPv6AcceptRA=yes\n");
480 else if (def->accept_ra == ACCEPT_RA_DISABLED)
537 else if (def->accept_ra == NETPLAN_RA_MODE_DISABLED)
481538 g_string_append_printf(network, "IPv6AcceptRA=no\n");
482539 if (def->ip6_privacy)
483540 g_string_append(network, "IPv6PrivacyExtensions=yes\n");
502559 g_string_append_printf(network, "IPv6MTUBytes=%d\n", def->ipv6_mtubytes);
503560 }
504561
505 if (def->type >= ND_VIRTUAL)
562 if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL)
506563 g_string_append(network, "ConfigureWithoutCarrier=yes\n");
507564
508565 if (def->bridge) {
525582 if (def->has_vlans) {
526583 /* iterate over all netdefs to find VLANs attached to us */
527584 GList *l = netdefs_ordered;
528 net_definition* nd;
585 const NetplanNetDefinition* nd;
529586 for (; l != NULL; l = l->next) {
530587 nd = l->data;
531 if (nd->vlan_link == def)
588 if (nd->vlan_link == def && !nd->sriov_vlan_filter)
532589 g_string_append_printf(network, "VLAN=%s\n", nd->id);
533590 }
534591 }
535592
536593 if (def->routes != NULL) {
537594 for (unsigned i = 0; i < def->routes->len; ++i) {
538 ip_route* cur_route = g_array_index (def->routes, ip_route*, i);
595 NetplanIPRoute* cur_route = g_array_index (def->routes, NetplanIPRoute*, i);
539596 write_route(cur_route, network);
540597 }
541598 }
542599 if (def->ip_rules != NULL) {
543600 for (unsigned i = 0; i < def->ip_rules->len; ++i) {
544 ip_rule* cur_rule = g_array_index (def->ip_rules, ip_rule*, i);
601 NetplanIPRule* cur_rule = g_array_index (def->ip_rules, NetplanIPRule*, i);
545602 write_ip_rule(cur_rule, network);
546603 }
547604 }
548605
549 if (def->dhcp4 || def->dhcp6) {
606 if (def->dhcp4 || def->dhcp6 || def->critical) {
550607 /* NetworkManager compatible route metrics */
551608 g_string_append(network, "\n[DHCP]\n");
552
609 }
610
611 if (def->critical)
612 g_string_append_printf(network, "CriticalConnection=true\n");
613
614 if (def->dhcp4 || def->dhcp6) {
553615 if (g_strcmp0(def->dhcp_identifier, "duid") != 0)
554616 g_string_append_printf(network, "ClientIdentifier=%s\n", def->dhcp_identifier);
555 if (def->critical)
556 g_string_append_printf(network, "CriticalConnection=true\n");
557
558 dhcp_overrides combined_dhcp_overrides;
617
618 NetplanDHCPOverrides combined_dhcp_overrides;
559619 combine_dhcp_overrides(def, &combined_dhcp_overrides);
560620
561 if (combined_dhcp_overrides.metric == METRIC_UNSPEC) {
562 g_string_append_printf(network, "RouteMetric=%i\n", (def->type == ND_WIFI ? 600 : 100));
621 if (combined_dhcp_overrides.metric == NETPLAN_METRIC_UNSPEC) {
622 g_string_append_printf(network, "RouteMetric=%i\n", (def->type == NETPLAN_DEF_TYPE_WIFI ? 600 : 100));
563623 } else {
564624 g_string_append_printf(network, "RouteMetric=%u\n",
565625 combined_dhcp_overrides.metric);
612672 }
613673
614674 static void
615 write_rules_file(net_definition* def, const char* rootdir)
675 write_rules_file(const NetplanNetDefinition* def, const char* rootdir)
616676 {
617677 GString* s = NULL;
618678 g_autofree char* path = g_strjoin(NULL, "run/udev/rules.d/99-netplan-", def->id, ".rules", NULL);
621681 /* do we need to write a .rules file?
622682 * It's only required for reliably setting the name of a physical device
623683 * until systemd issue #9006 is resolved. */
624 if (def->type >= ND_VIRTUAL)
684 if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL)
625685 return;
626686
627687 /* Matching by name does not work.
656716 }
657717
658718 static void
659 append_wpa_auth_conf(GString* s, const authentication_settings* auth)
719 append_wpa_auth_conf(GString* s, const NetplanAuthenticationSettings* auth, const char* id)
660720 {
661721 switch (auth->key_management) {
662 case KEY_MANAGEMENT_NONE:
722 case NETPLAN_AUTH_KEY_MANAGEMENT_NONE:
663723 g_string_append(s, " key_mgmt=NONE\n");
664724 break;
665725
666 case KEY_MANAGEMENT_WPA_PSK:
726 case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK:
667727 g_string_append(s, " key_mgmt=WPA-PSK\n");
668728 break;
669729
670 case KEY_MANAGEMENT_WPA_EAP:
730 case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP:
671731 g_string_append(s, " key_mgmt=WPA-EAP\n");
672732 break;
673733
674 case KEY_MANAGEMENT_8021X:
734 case NETPLAN_AUTH_KEY_MANAGEMENT_8021X:
675735 g_string_append(s, " key_mgmt=IEEE8021X\n");
676736 break;
677737 }
678738
679739 switch (auth->eap_method) {
680 case EAP_NONE:
681 break;
682
683 case EAP_TLS:
740 case NETPLAN_AUTH_EAP_NONE:
741 break;
742
743 case NETPLAN_AUTH_EAP_TLS:
684744 g_string_append(s, " eap=TLS\n");
685745 break;
686746
687 case EAP_PEAP:
747 case NETPLAN_AUTH_EAP_PEAP:
688748 g_string_append(s, " eap=PEAP\n");
689749 break;
690750
691 case EAP_TTLS:
751 case NETPLAN_AUTH_EAP_TTLS:
692752 g_string_append(s, " eap=TTLS\n");
693753 break;
694754 }
700760 g_string_append_printf(s, " anonymous_identity=\"%s\"\n", auth->anonymous_identity);
701761 }
702762 if (auth->password) {
703 if (auth->key_management == KEY_MANAGEMENT_WPA_PSK) {
704 g_string_append_printf(s, " psk=\"%s\"\n", auth->password);
763 if (auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK) {
764 size_t len = strlen(auth->password);
765 if (len == 64) {
766 /* must be a hex-digit key representation */
767 for (unsigned i = 0; i < 64; ++i)
768 if (!isxdigit(auth->password[i])) {
769 g_fprintf(stderr, "ERROR: %s: PSK length of 64 is only supported for hex-digit representation\n", id);
770 exit(1);
771 }
772 /* this is required to be unquoted */
773 g_string_append_printf(s, " psk=%s\n", auth->password);
774 } else if (len < 8 || len > 63) {
775 /* per wpa_supplicant spec, passphrase needs to be between 8
776 and 63 characters */
777 g_fprintf(stderr, "ERROR: %s: ASCII passphrase must be between 8 and 63 characters (inclusive)\n", id);
778 exit(1);
779 } else {
780 g_string_append_printf(s, " psk=\"%s\"\n", auth->password);
781 }
705782 } else {
706783 if (strncmp(auth->password, "hash:", 5) == 0) {
707784 g_string_append_printf(s, " password=%s\n", auth->password);
722799 if (auth->client_key_password) {
723800 g_string_append_printf(s, " private_key_passwd=\"%s\"\n", auth->client_key_password);
724801 }
725 }
726
727 static void
728 write_wpa_conf(net_definition* def, const char* rootdir)
802 if (auth->phase2_auth) {
803 g_string_append_printf(s, " phase2=\"auth=%s\"\n", auth->phase2_auth);
804 }
805
806 }
807
808 /* netplan-feature: generated-supplicant */
809 static void
810 write_wpa_unit(const NetplanNetDefinition* def, const char* rootdir)
811 {
812 g_autoptr(GError) err = NULL;
813 g_autofree gchar *stdouth = NULL;
814 g_autofree gchar *stderrh = NULL;
815 gint exit_status = 0;
816
817 gchar *argv[] = {"bin" "/" "systemd-escape", def->id, NULL};
818 g_spawn_sync("/", argv, NULL, 0, NULL, NULL, &stdouth, &stderrh, &exit_status, &err);
819 g_spawn_check_exit_status(exit_status, &err);
820 if (err != NULL) {
821 // LCOV_EXCL_START
822 g_fprintf(stderr, "failed to ask systemd to escape %s; exit %d\nstdout: '%s'\nstderr: '%s'", def->id, exit_status, stdouth, stderrh);
823 exit(1);
824 // LCOV_EXCL_STOP
825 }
826 g_strstrip(stdouth);
827
828 GString* s = g_string_new("[Unit]\n");
829 g_autofree char* path = g_strjoin(NULL, "/run/systemd/system/netplan-wpa-", stdouth, ".service", NULL);
830 g_string_append_printf(s, "Description=WPA supplicant for netplan %s\n", stdouth);
831 g_string_append(s, "DefaultDependencies=no\n");
832 g_string_append_printf(s, "Requires=sys-subsystem-net-devices-%s.device\n", stdouth);
833 g_string_append_printf(s, "After=sys-subsystem-net-devices-%s.device\n", stdouth);
834 g_string_append(s, "Before=network.target\nWants=network.target\n\n");
835 g_string_append(s, "[Service]\nType=simple\n");
836 g_string_append_printf(s, "ExecStart=/sbin/wpa_supplicant -c /run/netplan/wpa-%s.conf -i%s", stdouth, stdouth);
837
838 if (def->type != NETPLAN_DEF_TYPE_WIFI) {
839 g_string_append(s, " -Dwired\n");
840 }
841 g_string_free_to_file(s, rootdir, path, NULL);
842 }
843
844 static void
845 write_wpa_conf(const NetplanNetDefinition* def, const char* rootdir)
729846 {
730847 GHashTableIter iter;
731848 GString* s = g_string_new("ctrl_interface=/run/wpa_supplicant\n\n");
733850 mode_t orig_umask;
734851
735852 g_debug("%s: Creating wpa_supplicant configuration file %s", def->id, path);
736 if (def->type == ND_WIFI) {
737 wifi_access_point* ap;
853 if (def->type == NETPLAN_DEF_TYPE_WIFI) {
854 if (def->wowlan && def->wowlan > NETPLAN_WIFI_WOWLAN_DEFAULT) {
855 g_string_append(s, "wowlan_triggers=");
856 append_wifi_wowlan_flags(def->wowlan, s);
857 }
858 NetplanWifiAccessPoint* ap;
738859 g_hash_table_iter_init(&iter, def->access_points);
739860 while (g_hash_table_iter_next(&iter, NULL, (gpointer) &ap)) {
740861 g_string_append_printf(s, "network={\n ssid=\"%s\"\n", ap->ssid);
862 if (ap->bssid) {
863 g_string_append_printf(s, " bssid=%s\n", ap->bssid);
864 }
865 if (ap->band == NETPLAN_WIFI_BAND_24) {
866 // initialize 2.4GHz frequency hashtable
867 if(!wifi_frequency_24)
868 wifi_get_freq24(1);
869 if (ap->channel) {
870 g_string_append_printf(s, " freq_list=%d\n", wifi_get_freq24(ap->channel));
871 } else {
872 g_string_append_printf(s, " freq_list=");
873 g_hash_table_foreach(wifi_frequency_24, wifi_append_freq, s);
874 // overwrite last whitespace with newline
875 s = g_string_overwrite(s, s->len-1, "\n");
876 }
877 } else if (ap->band == NETPLAN_WIFI_BAND_5) {
878 // initialize 5GHz frequency hashtable
879 if(!wifi_frequency_5)
880 wifi_get_freq5(7);
881 if (ap->channel) {
882 g_string_append_printf(s, " freq_list=%d\n", wifi_get_freq5(ap->channel));
883 } else {
884 g_string_append_printf(s, " freq_list=");
885 g_hash_table_foreach(wifi_frequency_5, wifi_append_freq, s);
886 // overwrite last whitespace with newline
887 s = g_string_overwrite(s, s->len-1, "\n");
888 }
889 }
741890 switch (ap->mode) {
742 case WIFI_MODE_INFRASTRUCTURE:
891 case NETPLAN_WIFI_MODE_INFRASTRUCTURE:
743892 /* default in wpasupplicant */
744893 break;
745 case WIFI_MODE_ADHOC:
894 case NETPLAN_WIFI_MODE_ADHOC:
746895 g_string_append(s, " mode=1\n");
747896 break;
748 case WIFI_MODE_AP:
897 case NETPLAN_WIFI_MODE_AP:
749898 g_fprintf(stderr, "ERROR: %s: networkd does not support wifi in access point mode\n", def->id);
750899 exit(1);
751900 }
752901
753902 /* wifi auth trumps netdef auth */
754903 if (ap->has_auth) {
755 append_wpa_auth_conf(s, &ap->auth);
904 append_wpa_auth_conf(s, &ap->auth, ap->ssid);
756905 }
757906 else {
758907 g_string_append(s, " key_mgmt=NONE\n");
763912 else {
764913 /* wired 802.1x auth or similar */
765914 g_string_append(s, "network={\n");
766 append_wpa_auth_conf(s, &def->auth);
915 append_wpa_auth_conf(s, &def->auth, def->id);
767916 g_string_append(s, "}\n");
768917 }
769918
781930 * Returns: TRUE if @def applies to networkd, FALSE otherwise.
782931 */
783932 gboolean
784 write_networkd_conf(net_definition* def, const char* rootdir)
933 write_networkd_conf(const NetplanNetDefinition* def, const char* rootdir)
785934 {
786935 g_autofree char* path_base = g_strjoin(NULL, "run/systemd/network/10-netplan-", def->id, NULL);
787936
790939 write_link_file(def, rootdir, path_base);
791940 write_rules_file(def, rootdir);
792941
793 if (def->backend != BACKEND_NETWORKD) {
942 if (def->backend != NETPLAN_BACKEND_NETWORKD) {
794943 g_debug("networkd: definition %s is not for us (backend %i)", def->id, def->backend);
795944 return FALSE;
796945 }
797946
798 if (def->type == ND_WIFI || def->has_auth) {
799 g_autofree char* link = g_strjoin(NULL, rootdir ?: "", "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa@", def->id, ".service", NULL);
800 if (def->type == ND_WIFI && def->has_match) {
947 if (def->type == NETPLAN_DEF_TYPE_MODEM) {
948 g_fprintf(stderr, "ERROR: %s: networkd backend does not support GSM/CDMA modem configuration\n", def->id);
949 exit(1);
950 }
951
952 if (def->type == NETPLAN_DEF_TYPE_WIFI || def->has_auth) {
953 g_autofree char* link = g_strjoin(NULL, rootdir ?: "", "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa-", def->id, ".service", NULL);
954 g_autofree char* slink = g_strjoin(NULL, "/run/systemd/system/netplan-wpa-", def->id, ".service", NULL);
955 if (def->type == NETPLAN_DEF_TYPE_WIFI && def->has_match) {
801956 g_fprintf(stderr, "ERROR: %s: networkd backend does not support wifi with match:, only by interface name\n", def->id);
802957 exit(1);
803958 }
804959
960 g_debug("Creating wpa_supplicant config");
805961 write_wpa_conf(def, rootdir);
962
963 g_debug("Creating wpa_supplicant unit %s", slink);
964 write_wpa_unit(def, rootdir);
806965
807966 g_debug("Creating wpa_supplicant service enablement link %s", link);
808967 safe_mkdir_p_dir(link);
809 if (symlink("/lib/systemd/system/netplan-wpa@.service", link) < 0 && errno != EEXIST) {
968
969 if (symlink(slink, link) < 0 && errno != EEXIST) {
810970 // LCOV_EXCL_START
811971 g_fprintf(stderr, "failed to create enablement symlink: %m\n");
812972 exit(1);
815975
816976 }
817977
818 if (def->type >= ND_VIRTUAL)
978 if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL)
819979 write_netdev_file(def, rootdir, path_base);
820980 write_network_file(def, rootdir, path_base);
821981 return TRUE;
829989 {
830990 unlink_glob(rootdir, "/run/systemd/network/10-netplan-*");
831991 unlink_glob(rootdir, "/run/netplan/wpa-*.conf");
832 unlink_glob(rootdir, "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa@*.service");
992 unlink_glob(rootdir, "/run/systemd/system/netplan-wpa-*.service");
833993 unlink_glob(rootdir, "/run/udev/rules.d/99-netplan-*");
834994 }
835995
1818
1919 #include "parse.h"
2020
21 gboolean write_networkd_conf(net_definition* def, const char* rootdir);
21 gboolean write_networkd_conf(const NetplanNetDefinition* def, const char* rootdir);
2222 void cleanup_networkd_conf(const char* rootdir);
2323 void enable_networkd(const char* generator_dir);
3535 * Append NM device specifier of @def to @s.
3636 */
3737 static void
38 g_string_append_netdef_match(GString* s, const net_definition* def)
38 g_string_append_netdef_match(GString* s, const NetplanNetDefinition* def)
3939 {
4040 g_assert(!def->match.driver || def->set_name);
4141 if (def->match.mac) {
4242 g_string_append_printf(s, "mac:%s", def->match.mac);
43 } else if (def->match.original_name || def->set_name || def->type >= ND_VIRTUAL) {
43 } else if (def->match.original_name || def->set_name || def->type >= NETPLAN_DEF_TYPE_VIRTUAL) {
4444 /* we always have the renamed name here */
4545 g_string_append_printf(s, "interface-name:%s",
46 (def->type >= ND_VIRTUAL) ? def->id
46 (def->type >= NETPLAN_DEF_TYPE_VIRTUAL) ? def->id
4747 : (def->set_name ?: def->match.original_name));
4848 } else {
4949 /* no matches → match all devices of that type */
5050 switch (def->type) {
51 case ND_ETHERNET:
51 case NETPLAN_DEF_TYPE_ETHERNET:
5252 g_string_append(s, "type:ethernet");
5353 break;
5454 /* This cannot be reached with just NM and networkd backends, as
5656 * wifi device from NM. This would become relevant with another
5757 * wifi-supporting backend, but until then this just spoils 100%
5858 * code coverage.
59 case ND_WIFI:
59 case NETPLAN_DEF_TYPE_WIFI:
6060 g_string_append(s, "type:wifi");
6161 break;
6262 */
7070 }
7171
7272 /**
73 * Infer if this is a modem netdef of type GSM.
74 * This is done by checking for certain modem_params, which are only
75 * applicable to GSM connections.
76 */
77 static const gboolean
78 modem_is_gsm(const NetplanNetDefinition* def)
79 {
80 if (def->type == NETPLAN_DEF_TYPE_MODEM && (def->modem_params.apn ||
81 def->modem_params.auto_config || def->modem_params.device_id ||
82 def->modem_params.network_id || def->modem_params.pin ||
83 def->modem_params.sim_id || def->modem_params.sim_operator_id))
84 return TRUE;
85
86 return FALSE;
87 }
88
89 /**
7390 * Return NM "type=" string.
7491 */
7592 static const char*
76 type_str(netdef_type type)
77 {
93 type_str(const NetplanNetDefinition* def)
94 {
95 const NetplanDefType type = def->type;
7896 switch (type) {
79 case ND_ETHERNET:
97 case NETPLAN_DEF_TYPE_ETHERNET:
8098 return "ethernet";
81 case ND_WIFI:
99 case NETPLAN_DEF_TYPE_MODEM:
100 if (modem_is_gsm(def))
101 return "gsm";
102 else
103 return "cdma";
104 case NETPLAN_DEF_TYPE_WIFI:
82105 return "wifi";
83 case ND_BRIDGE:
106 case NETPLAN_DEF_TYPE_BRIDGE:
84107 return "bridge";
85 case ND_BOND:
108 case NETPLAN_DEF_TYPE_BOND:
86109 return "bond";
87 case ND_VLAN:
110 case NETPLAN_DEF_TYPE_VLAN:
88111 return "vlan";
89 case ND_TUNNEL:
112 case NETPLAN_DEF_TYPE_TUNNEL:
90113 return "ip-tunnel";
91114 // LCOV_EXCL_START
92115 default:
99122 * Return NM wifi "mode=" string.
100123 */
101124 static const char*
102 wifi_mode_str(wifi_mode mode)
125 wifi_mode_str(const NetplanWifiMode mode)
103126 {
104127 switch (mode) {
105 case WIFI_MODE_INFRASTRUCTURE:
128 case NETPLAN_WIFI_MODE_INFRASTRUCTURE:
106129 return "infrastructure";
107 case WIFI_MODE_ADHOC:
130 case NETPLAN_WIFI_MODE_ADHOC:
108131 return "adhoc";
109 case WIFI_MODE_AP:
132 case NETPLAN_WIFI_MODE_AP:
110133 return "ap";
111134 // LCOV_EXCL_START
112135 default:
115138 }
116139 }
117140
118 static void
119 write_search_domains(const net_definition* def, GString *s)
141 /**
142 * Return NM wifi "band=" string.
143 */
144 static const char*
145 wifi_band_str(const NetplanWifiBand band)
146 {
147 switch (band) {
148 case NETPLAN_WIFI_BAND_5:
149 return "a";
150 case NETPLAN_WIFI_BAND_24:
151 return "bg";
152 // LCOV_EXCL_START
153 default:
154 g_assert_not_reached();
155 // LCOV_EXCL_STOP
156 }
157 }
158
159 /**
160 * Return NM addr-gen-mode string.
161 */
162 static const char*
163 addr_gen_mode_str(const NetplanAddrGenMode mode)
164 {
165 switch (mode) {
166 case NETPLAN_ADDRGEN_EUI64:
167 return "0";
168 case NETPLAN_ADDRGEN_STABLEPRIVACY:
169 return "1";
170 // LCOV_EXCL_START
171 default:
172 g_assert_not_reached();
173 // LCOV_EXCL_STOP
174 }
175 }
176
177 static void
178 write_search_domains(const NetplanNetDefinition* def, GString *s)
120179 {
121180 if (def->search_domains) {
122181 g_string_append(s, "dns-search=");
127186 }
128187
129188 static void
130 write_routes(const net_definition* def, GString *s, int family)
189 write_routes(const NetplanNetDefinition* def, GString *s, int family)
131190 {
132191 if (def->routes != NULL) {
133192 for (unsigned i = 0, j = 1; i < def->routes->len; ++i) {
134 ip_route *cur_route = g_array_index(def->routes, ip_route*, i);
193 const NetplanIPRoute *cur_route = g_array_index(def->routes, NetplanIPRoute*, i);
135194
136195 if (cur_route->family != family)
137196 continue;
146205 exit(1);
147206 }
148207
149 if (cur_route->table != ROUTE_TABLE_UNSPEC) {
208 if (cur_route->table != NETPLAN_ROUTE_TABLE_UNSPEC) {
150209 g_fprintf(stderr, "ERROR: %s: NetworkManager does not support non-default routing tables\n", def->id);
151210 exit(1);
152211 }
163222
164223 g_string_append_printf(s, "route%d=%s,%s",
165224 j, cur_route->to, cur_route->via);
166 if (cur_route->metric != METRIC_UNSPEC)
225 if (cur_route->metric != NETPLAN_METRIC_UNSPEC)
167226 g_string_append_printf(s, ",%d", cur_route->metric);
168227 g_string_append(s, "\n");
169228 j++;
172231 }
173232
174233 static void
175 write_bond_parameters(const net_definition* def, GString *s)
234 write_bond_parameters(const NetplanNetDefinition* def, GString *s)
176235 {
177236 GString* params = NULL;
178237
236295 }
237296
238297 static void
239 write_bridge_params(const net_definition* def, GString *s)
298 write_bridge_params(const NetplanNetDefinition* def, GString *s)
240299 {
241300 GString* params = NULL;
242301
262321 }
263322
264323 static void
265 write_tunnel_params(const net_definition* def, GString *s)
324 write_tunnel_params(const NetplanNetDefinition* def, GString *s)
266325 {
267326 g_string_append(s, "\n[ip-tunnel]\n");
268327
277336 }
278337
279338 static void
280 write_dot1x_auth_parameters(const authentication_settings* auth, GString *s)
281 {
282 if (auth->eap_method == EAP_NONE) {
339 write_dot1x_auth_parameters(const NetplanAuthenticationSettings* auth, GString *s)
340 {
341 if (auth->eap_method == NETPLAN_AUTH_EAP_NONE) {
283342 return;
284343 }
285344
286345 g_string_append_printf(s, "\n[802-1x]\n");
287346
288347 switch (auth->eap_method) {
289 case EAP_NONE: break; // LCOV_EXCL_LINE
290 case EAP_TLS:
348 case NETPLAN_AUTH_EAP_NONE: break; // LCOV_EXCL_LINE
349 case NETPLAN_AUTH_EAP_TLS:
291350 g_string_append(s, "eap=tls\n");
292351 break;
293 case EAP_PEAP:
352 case NETPLAN_AUTH_EAP_PEAP:
294353 g_string_append(s, "eap=peap\n");
295354 break;
296 case EAP_TTLS:
355 case NETPLAN_AUTH_EAP_TTLS:
297356 g_string_append(s, "eap=ttls\n");
298357 break;
299358 }
304363 if (auth->anonymous_identity) {
305364 g_string_append_printf(s, "anonymous-identity=%s\n", auth->anonymous_identity);
306365 }
307 if (auth->password && auth->key_management != KEY_MANAGEMENT_WPA_PSK) {
366 if (auth->password && auth->key_management != NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK) {
308367 g_string_append_printf(s, "password=%s\n", auth->password);
309368 }
310369 if (auth->ca_certificate) {
319378 if (auth->client_key_password) {
320379 g_string_append_printf(s, "private-key-password=%s\n", auth->client_key_password);
321380 }
322 }
323
324 static void
325 write_wifi_auth_parameters(const authentication_settings* auth, GString *s)
326 {
327 if (auth->key_management == KEY_MANAGEMENT_NONE) {
381 if (auth->phase2_auth) {
382 g_string_append_printf(s, "phase2-auth=%s\n", auth->phase2_auth);
383 }
384
385 }
386
387 static void
388 write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GString *s)
389 {
390 if (auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_NONE) {
328391 return;
329392 }
330393
331394 g_string_append(s, "\n[wifi-security]\n");
332395
333396 switch (auth->key_management) {
334 case KEY_MANAGEMENT_NONE: break; // LCOV_EXCL_LINE
335 case KEY_MANAGEMENT_WPA_PSK:
397 case NETPLAN_AUTH_KEY_MANAGEMENT_NONE: break; // LCOV_EXCL_LINE
398 case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK:
336399 g_string_append(s, "key-mgmt=wpa-psk\n");
337400 if (auth->password) {
338401 g_string_append_printf(s, "psk=%s\n", auth->password);
339402 }
340403 break;
341 case KEY_MANAGEMENT_WPA_EAP:
404 case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP:
342405 g_string_append(s, "key-mgmt=wpa-eap\n");
343406 break;
344 case KEY_MANAGEMENT_8021X:
407 case NETPLAN_AUTH_KEY_MANAGEMENT_8021X:
345408 g_string_append(s, "key-mgmt=ieee8021x\n");
346409 break;
347410 }
350413 }
351414
352415 static void
353 maybe_generate_uuid(net_definition* def)
416 maybe_generate_uuid(NetplanNetDefinition* def)
354417 {
355418 if (uuid_is_null(def->uuid))
356419 uuid_generate(def->uuid);
358421
359422 /**
360423 * Generate NetworkManager configuration in @rootdir/run/NetworkManager/ for a
361 * particular net_definition and wifi_access_point, as NM requires a separate
424 * particular NetplanNetDefinition and NetplanWifiAccessPoint, as NM requires a separate
362425 * connection file for each SSID.
363 * @def: The net_definition for which to create a connection
426 * @def: The NetplanNetDefinition for which to create a connection
364427 * @rootdir: If not %NULL, generate configuration in this root directory
365428 * (useful for testing).
366429 * @ap: The access point for which to create a connection. Must be %NULL for
367430 * non-wifi types.
368431 */
369432 static void
370 write_nm_conf_access_point(net_definition* def, const char* rootdir, const wifi_access_point* ap)
433 write_nm_conf_access_point(NetplanNetDefinition* def, const char* rootdir, const NetplanWifiAccessPoint* ap)
371434 {
372435 GString *s = NULL;
373436 g_autofree char* conf_path = NULL;
374437 mode_t orig_umask;
375438 char uuidstr[37];
376439
377 if (def->type == ND_WIFI)
440 if (def->type == NETPLAN_DEF_TYPE_WIFI)
378441 g_assert(ap);
379442 else
380443 g_assert(ap == NULL);
444
445 if (def->type == NETPLAN_DEF_TYPE_VLAN && def->sriov_vlan_filter) {
446 g_debug("%s is defined as a hardware SR-IOV filtered VLAN, postponing creation", def->id);
447 return;
448 }
381449
382450 s = g_string_new(NULL);
383451 g_string_append_printf(s, "[connection]\nid=netplan-%s", def->id);
384452 if (ap)
385453 g_string_append_printf(s, "-%s", ap->ssid);
386 g_string_append_printf(s, "\ntype=%s\n", type_str(def->type));
454 g_string_append_printf(s, "\ntype=%s\n", type_str(def));
387455
388456 /* VLAN devices refer to us as their parent; if our ID is not a name but we
389457 * have matches, parent= must be the connection UUID, so put it into the
394462 g_string_append_printf(s, "uuid=%s\n", uuidstr);
395463 }
396464
397 if (def->type < ND_VIRTUAL) {
465 if (def->type < NETPLAN_DEF_TYPE_VIRTUAL) {
398466 /* physical (existing) devices use matching; driver matching is not
399467 * supported, MAC matching is done below (different keyfile section),
400468 * so only match names here */
415483 /* virtual (created) devices set a name */
416484 g_string_append_printf(s, "interface-name=%s\n", def->id);
417485
418 if (def->type == ND_BRIDGE)
486 if (def->type == NETPLAN_DEF_TYPE_BRIDGE)
419487 write_bridge_params(def, s);
488 }
489 if (def->type == NETPLAN_DEF_TYPE_MODEM) {
490 if (modem_is_gsm(def))
491 g_string_append_printf(s, "\n[gsm]\n");
492 else
493 g_string_append_printf(s, "\n[cdma]\n");
494
495 /* Use NetworkManager's auto configuration feature if no APN, username, or password is specified */
496 if (def->modem_params.auto_config || (!def->modem_params.apn &&
497 !def->modem_params.username && !def->modem_params.password)) {
498 g_string_append_printf(s, "auto-config=true\n");
499 } else {
500 if (def->modem_params.apn)
501 g_string_append_printf(s, "apn=%s\n", def->modem_params.apn);
502 if (def->modem_params.password)
503 g_string_append_printf(s, "password=%s\n", def->modem_params.password);
504 if (def->modem_params.username)
505 g_string_append_printf(s, "username=%s\n", def->modem_params.username);
506 }
507
508 if (def->modem_params.device_id)
509 g_string_append_printf(s, "device-id=%s\n", def->modem_params.device_id);
510 if (def->mtubytes)
511 g_string_append_printf(s, "mtu=%u\n", def->mtubytes);
512 if (def->modem_params.network_id)
513 g_string_append_printf(s, "network-id=%s\n", def->modem_params.network_id);
514 if (def->modem_params.number)
515 g_string_append_printf(s, "number=%s\n", def->modem_params.number);
516 if (def->modem_params.pin)
517 g_string_append_printf(s, "pin=%s\n", def->modem_params.pin);
518 if (def->modem_params.sim_id)
519 g_string_append_printf(s, "sim-id=%s\n", def->modem_params.sim_id);
520 if (def->modem_params.sim_operator_id)
521 g_string_append_printf(s, "sim-operator-id=%s\n", def->modem_params.sim_operator_id);
420522 }
421523 if (def->bridge) {
422524 g_string_append_printf(s, "slave-type=bridge\nmaster=%s\n", def->bridge);
436538 exit(1);
437539 }
438540
439 if (def->type < ND_VIRTUAL) {
541 if (def->type < NETPLAN_DEF_TYPE_VIRTUAL) {
440542 GString *link_str = NULL;
441543
442544 link_str = g_string_new(NULL);
452554 if (def->mtubytes) {
453555 g_string_append_printf(link_str, "mtu=%d\n", def->mtubytes);
454556 }
557 if (def->wowlan && def->wowlan > NETPLAN_WIFI_WOWLAN_DEFAULT)
558 g_string_append_printf(link_str, "wake-on-wlan=%u\n", def->wowlan);
455559
456560 if (link_str->len > 0) {
457561 switch (def->type) {
458 case ND_WIFI:
562 case NETPLAN_DEF_TYPE_WIFI:
459563 g_string_append_printf(s, "\n[802-11-wireless]\n%s", link_str->str); break;
564 case NETPLAN_DEF_TYPE_MODEM:
565 /* Avoid adding an [ethernet] section into the [gsm/cdma] description. */
566 break;
460567 default:
461568 g_string_append_printf(s, "\n[802-3-ethernet]\n%s", link_str->str); break;
462569 }
482589 g_string_free(link_str, TRUE);
483590 }
484591
485 if (def->type == ND_VLAN) {
592 if (def->type == NETPLAN_DEF_TYPE_VLAN) {
486593 g_assert(def->vlan_id < G_MAXUINT);
487594 g_assert(def->vlan_link != NULL);
488595 g_string_append_printf(s, "\n[vlan]\nid=%u\nparent=", def->vlan_id);
498605 }
499606 }
500607
501 if (def->type == ND_BOND)
608 if (def->type == NETPLAN_DEF_TYPE_BOND)
502609 write_bond_parameters(def, s);
503610
504 if (def->type == ND_TUNNEL)
611 if (def->type == NETPLAN_DEF_TYPE_TUNNEL)
505612 write_tunnel_params(def, s);
506613
507614 g_string_append(s, "\n[ipv4]\n");
508615
509 if (ap && ap->mode == WIFI_MODE_AP)
616 if (ap && ap->mode == NETPLAN_WIFI_MODE_AP)
510617 g_string_append(s, "method=shared\n");
511618 else if (def->dhcp4)
512619 g_string_append(s, "method=auto\n");
513620 else if (def->ip4_addresses)
514621 /* This requires adding at least one address (done below) */
515622 g_string_append(s, "method=manual\n");
516 else if (def->type == ND_TUNNEL)
623 else if (def->type == NETPLAN_DEF_TYPE_TUNNEL)
517624 /* sit tunnels will not start in link-local apparently */
518625 g_string_append(s, "method=disabled\n");
519626 else
543650 g_string_append(s, "never-default=true\n");
544651 }
545652
546 if (def->dhcp4 && def->dhcp4_overrides.metric != METRIC_UNSPEC)
653 if (def->dhcp4 && def->dhcp4_overrides.metric != NETPLAN_METRIC_UNSPEC)
547654 g_string_append_printf(s, "route-metric=%u\n", def->dhcp4_overrides.metric);
548655
549 if (def->dhcp6 || def->ip6_addresses || def->gateway6 || def->ip6_nameservers) {
656 if (def->dhcp6 || def->ip6_addresses || def->gateway6 || def->ip6_nameservers || def->ip6_addr_gen_mode) {
550657 g_string_append(s, "\n[ipv6]\n");
551658 g_string_append(s, def->dhcp6 ? "method=auto\n" : "method=manual\n");
552659 if (def->ip6_addresses)
553660 for (unsigned i = 0; i < def->ip6_addresses->len; ++i)
554661 g_string_append_printf(s, "address%i=%s\n", i+1, g_array_index(def->ip6_addresses, char*, i));
662 if (def->ip6_addr_gen_mode) {
663 g_string_append_printf(s, "addr-gen-mode=%s\n", addr_gen_mode_str(def->ip6_addr_gen_mode));
664 }
555665 if (def->ip6_privacy)
556666 g_string_append(s, "ip6-privacy=2\n");
557667 if (def->gateway6)
574684 g_string_append(s, "never-default=true\n");
575685 }
576686
577 if (def->dhcp6_overrides.metric != METRIC_UNSPEC)
687 if (def->dhcp6_overrides.metric != NETPLAN_METRIC_UNSPEC)
578688 g_string_append_printf(s, "route-metric=%u\n", def->dhcp6_overrides.metric);
579689 }
580690 else {
586696 conf_path = g_strjoin(NULL, "run/NetworkManager/system-connections/netplan-", def->id, "-", escaped_ssid, ".nmconnection", NULL);
587697
588698 g_string_append_printf(s, "\n[wifi]\nssid=%s\nmode=%s\n", ap->ssid, wifi_mode_str(ap->mode));
699 if (ap->bssid) {
700 g_string_append_printf(s, "bssid=%s\n", ap->bssid);
701 }
702 if (ap->band == NETPLAN_WIFI_BAND_5 || ap->band == NETPLAN_WIFI_BAND_24) {
703 g_string_append_printf(s, "band=%s\n", wifi_band_str(ap->band));
704 /* Channel is only unambiguous, if band is set. */
705 if (ap->channel) {
706 /* Validate WiFi channel */
707 if (ap->band == NETPLAN_WIFI_BAND_5)
708 wifi_get_freq5(ap->channel);
709 else
710 wifi_get_freq24(ap->channel);
711 g_string_append_printf(s, "channel=%u\n", ap->channel);
712 }
713 }
589714 if (ap->has_auth) {
590715 write_wifi_auth_parameters(&ap->auth, s);
591716 }
604729
605730 /**
606731 * Generate NetworkManager configuration in @rootdir/run/NetworkManager/ for a
607 * particular net_definition.
732 * particular NetplanNetDefinition.
608733 * @rootdir: If not %NULL, generate configuration in this root directory
609734 * (useful for testing).
610735 */
611736 void
612 write_nm_conf(net_definition* def, const char* rootdir)
613 {
614 if (def->backend != BACKEND_NM) {
737 write_nm_conf(NetplanNetDefinition* def, const char* rootdir)
738 {
739 if (def->backend != NETPLAN_BACKEND_NM) {
615740 g_debug("NetworkManager: definition %s is not for us (backend %i)", def->id, def->backend);
616741 return;
617742 }
622747 }
623748
624749 /* for wifi we need to create a separate connection file for every SSID */
625 if (def->type == ND_WIFI) {
750 if (def->type == NETPLAN_DEF_TYPE_WIFI) {
626751 GHashTableIter iter;
627752 gpointer key;
628 wifi_access_point* ap;
753 const NetplanWifiAccessPoint* ap;
629754 g_assert(def->access_points);
630755 g_hash_table_iter_init(&iter, def->access_points);
631756 while (g_hash_table_iter_next(&iter, &key, (gpointer) &ap))
639764 static void
640765 nd_append_non_nm_ids(gpointer data, gpointer str)
641766 {
642 net_definition* nd = data;
643
644 if (nd->backend != BACKEND_NM) {
767 const NetplanNetDefinition* nd = data;
768
769 if (nd->backend != NETPLAN_BACKEND_NM) {
645770 if (nd->match.driver) {
646771 /* NM cannot match on drivers, so ignore these via udev rules */
647772 if (!udev_rules)
1818
1919 #include "parse.h"
2020
21 void write_nm_conf(net_definition* def, const char* rootdir);
21 void write_nm_conf(NetplanNetDefinition* def, const char* rootdir);
2222 void write_nm_conf_finish(const char* rootdir);
2323 void cleanup_nm_conf(const char* rootdir);
2929 #include "error.h"
3030 #include "validation.h"
3131
32 /* convenience macro to put the offset of a net_definition field into "void* data" */
33 #define netdef_offset(field) GUINT_TO_POINTER(offsetof(net_definition, field))
34 #define route_offset(field) GUINT_TO_POINTER(offsetof(ip_route, field))
35 #define ip_rule_offset(field) GUINT_TO_POINTER(offsetof(ip_rule, field))
36 #define auth_offset(field) GUINT_TO_POINTER(offsetof(authentication_settings, field))
37
38 /* net_definition that is currently being processed */
39 net_definition* cur_netdef;
40
41 /* wifi AP that is currently being processed */
42 wifi_access_point* cur_access_point;
32 /* convenience macro to put the offset of a NetplanNetDefinition field into "void* data" */
33 #define netdef_offset(field) GUINT_TO_POINTER(offsetof(NetplanNetDefinition, field))
34 #define route_offset(field) GUINT_TO_POINTER(offsetof(NetplanIPRoute, field))
35 #define ip_rule_offset(field) GUINT_TO_POINTER(offsetof(NetplanIPRule, field))
36 #define auth_offset(field) GUINT_TO_POINTER(offsetof(NetplanAuthenticationSettings, field))
37 #define access_point_offset(field) GUINT_TO_POINTER(offsetof(NetplanWifiAccessPoint, field))
38
39 /* NetplanNetDefinition that is currently being processed */
40 static NetplanNetDefinition* cur_netdef;
41
42 /* NetplanWifiAccessPoint that is currently being processed */
43 static NetplanWifiAccessPoint* cur_access_point;
4344
4445 /* authentication options that are currently being processed */
45 authentication_settings* cur_auth;
46
47 ip_route* cur_route;
48 ip_rule* cur_ip_rule;
49
50 netdef_backend backend_global, backend_cur_type;
51
52 /* Global ID → net_definition* map for all parsed config files */
46 static NetplanAuthenticationSettings* cur_auth;
47
48 static NetplanIPRoute* cur_route;
49 static NetplanIPRule* cur_ip_rule;
50
51 static NetplanBackend backend_global, backend_cur_type;
52
53 /* Global ID → NetplanNetDefinition* map for all parsed config files */
5354 GHashTable* netdefs;
5455
5556 /* Contains the same objects as 'netdefs' but ordered by dependency */
5859 /* Set of IDs in currently parsed YAML file, for being able to detect
5960 * "duplicate ID within one file" vs. allowing a drop-in to override/amend an
6061 * existing definition */
61 GHashTable* ids_in_file;
62 static GHashTable* ids_in_file;
6263
6364 /**
6465 * Load YAML file name into a yaml_document_t.
8687 ret = parser_error(&parser, yaml, error);
8788 }
8889
90 yaml_parser_delete(&parser);
8991 fclose(fyaml);
9092 return ret;
9193 }
135137 static void
136138 add_missing_node(const yaml_node_t* node)
137139 {
138 missing_node* missing;
140 NetplanMissingNode* missing;
139141
140142 /* Let's capture the current netdef we were playing with along with the
141143 * actual yaml_node_t that errors (that is an identifier not previously
142144 * seen by the compiler). We can use it later to write an sensible error
143145 * message and point the user in the right direction. */
144 missing = g_new0(missing_node, 1);
146 missing = g_new0(NetplanMissingNode, 1);
145147 missing->netdef_id = cur_netdef->id;
146148 missing->node = node;
147149
246248 return TRUE;
247249 }
248250
249 /**
250 * Generic handler for setting a cur_netdef string field from a scalar node
251 * @data: offset into net_definition where the const char* field to write is
251 /*************************************************************
252 * Generic helper functions to extract data from scalar nodes.
253 *************************************************************/
254
255 /**
256 * Handler for setting a guint field from a scalar node, inside a given struct
257 * @entryptr: pointer to the begining of the to-be-modified data structure
258 * @data: offset into entryptr struct where the guint field to write is located
259 */
260 static gboolean
261 handle_generic_guint(yaml_document_t* doc, yaml_node_t* node, const void* entryptr, const void* data, GError** error)
262 {
263 g_assert(entryptr);
264 guint offset = GPOINTER_TO_UINT(data);
265 guint64 v;
266 gchar* endptr;
267
268 v = g_ascii_strtoull(scalar(node), &endptr, 10);
269 if (*endptr != '\0' || v > G_MAXUINT)
270 return yaml_error(node, error, "invalid unsigned int value '%s'", scalar(node));
271
272 *((guint*) ((void*) entryptr + offset)) = (guint) v;
273 return TRUE;
274 }
275
276 /**
277 * Handler for setting a string field from a scalar node, inside a given struct
278 * @entryptr: pointer to the beginning of the to-be-modified data structure
279 * @data: offset into entryptr struct where the const char* field to write is
252280 * located
253281 */
254282 static gboolean
255 handle_netdef_str(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
256 {
283 handle_generic_str(yaml_document_t* doc, yaml_node_t* node, void* entryptr, const void* data, GError** error)
284 {
285 g_assert(entryptr);
257286 guint offset = GPOINTER_TO_UINT(data);
258 char** dest = (char**) ((void*) cur_netdef + offset);
287 char** dest = (char**) ((void*) entryptr + offset);
259288 g_free(*dest);
260289 *dest = g_strdup(scalar(node));
261290 return TRUE;
262291 }
263292
293 /*
294 * Handler for setting a MAC address field from a scalar node, inside a given struct
295 * @entryptr: pointer to the beginning of the to-be-modified data structure
296 * @data: offset into entryptr struct where the const char* field to write is
297 * located
298 */
299 static gboolean
300 handle_generic_mac(yaml_document_t* doc, yaml_node_t* node, void* entryptr, const void* data, GError** error)
301 {
302 g_assert(entryptr);
303 static regex_t re;
304 static gboolean re_inited = FALSE;
305
306 g_assert(node->type == YAML_SCALAR_NODE);
307
308 if (!re_inited) {
309 g_assert(regcomp(&re, "^[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]$", REG_EXTENDED|REG_NOSUB) == 0);
310 re_inited = TRUE;
311 }
312
313 if (regexec(&re, scalar(node), 0, NULL, 0) != 0)
314 return yaml_error(node, error, "Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX", scalar(node));
315
316 return handle_generic_str(doc, node, entryptr, data, error);
317 }
318
319 /**
320 * Generic handler for setting a cur_netdef string field from a scalar node
321 * @data: offset into NetplanNetDefinition where the const char* field to write is
322 * located
323 */
324 static gboolean
325 handle_netdef_str(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
326 {
327 return handle_generic_str(doc, node, cur_netdef, data, error);
328 }
329
264330 /**
265331 * Generic handler for setting a cur_netdef ID/iface name field from a scalar node
266 * @data: offset into net_definition where the const char* field to write is
332 * @data: offset into NetplanNetDefinition where the const char* field to write is
267333 * located
268334 */
269335 static gboolean
277343 /**
278344 * Generic handler for setting a cur_netdef ID/iface name field referring to an
279345 * existing ID from a scalar node
280 * @data: offset into net_definition where the net_definition* field to write is
346 * @data: offset into NetplanNetDefinition where the NetplanNetDefinition* field to write is
281347 * located
282348 */
283349 static gboolean
284350 handle_netdef_id_ref(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
285351 {
286352 guint offset = GPOINTER_TO_UINT(data);
287 net_definition* ref = NULL;
353 NetplanNetDefinition* ref = NULL;
288354
289355 ref = g_hash_table_lookup(netdefs, scalar(node));
290356 if (!ref) {
291357 add_missing_node(node);
292358 } else {
293 *((net_definition**) ((void*) cur_netdef + offset)) = ref;
359 *((NetplanNetDefinition**) ((void*) cur_netdef + offset)) = ref;
294360 }
295361 return TRUE;
296362 }
298364
299365 /**
300366 * Generic handler for setting a cur_netdef MAC address field from a scalar node
301 * @data: offset into net_definition where the const char* field to write is
367 * @data: offset into NetplanNetDefinition where the const char* field to write is
302368 * located
303369 */
304370 static gboolean
305371 handle_netdef_mac(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
306372 {
307 static regex_t re;
308 static gboolean re_inited = FALSE;
309
310 g_assert(node->type == YAML_SCALAR_NODE);
311
312 if (!re_inited) {
313 g_assert(regcomp(&re, "^[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]$", REG_EXTENDED|REG_NOSUB) == 0);
314 re_inited = TRUE;
315 }
316
317 if (regexec(&re, scalar(node), 0, NULL, 0) != 0)
318 return yaml_error(node, error, "Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX", scalar(node));
319
320 return handle_netdef_str(doc, node, data, error);
373 return handle_generic_mac(doc, node, cur_netdef, data, error);
321374 }
322375
323376 /**
324377 * Generic handler for setting a cur_netdef gboolean field from a scalar node
325 * @data: offset into net_definition where the gboolean field to write is located
378 * @data: offset into NetplanNetDefinition where the gboolean field to write is located
326379 */
327380 static gboolean
328381 handle_netdef_bool(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
349402
350403 /**
351404 * Generic handler for setting a cur_netdef guint field from a scalar node
352 * @data: offset into net_definition where the guint field to write is located
405 * @data: offset into NetplanNetDefinition where the guint field to write is located
353406 */
354407 static gboolean
355408 handle_netdef_guint(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
356409 {
357 guint offset = GPOINTER_TO_UINT(data);
358 guint64 v;
359 gchar* endptr;
360
361 v = g_ascii_strtoull(scalar(node), &endptr, 10);
362 if (*endptr != '\0' || v > G_MAXUINT)
363 return yaml_error(node, error, "invalid unsigned int value '%s'", scalar(node));
364
365 *((guint*) ((void*) cur_netdef + offset)) = (guint) v;
366 return TRUE;
410 return handle_generic_guint(doc, node, cur_netdef, data, error);
367411 }
368412
369413 static gboolean
426470 return TRUE;
427471 }
428472
473 static gboolean
474 handle_netdef_addrgen(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
475 {
476 g_assert(cur_netdef);
477 if (strcmp(scalar(node), "eui64") == 0)
478 cur_netdef->ip6_addr_gen_mode = NETPLAN_ADDRGEN_EUI64;
479 else if (strcmp(scalar(node), "stable-privacy") == 0)
480 cur_netdef->ip6_addr_gen_mode = NETPLAN_ADDRGEN_STABLEPRIVACY;
481 else
482 return yaml_error(node, error, "unknown ipv6-address-generation '%s'", scalar(node));
483 return TRUE;
484 }
485
429486
430487 /****************************************************
431488 * Grammar and handlers for network config "match" entry
432489 ****************************************************/
433490
434 const mapping_entry_handler match_handlers[] = {
491 static const mapping_entry_handler match_handlers[] = {
435492 {"driver", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(match.driver)},
436493 {"macaddress", YAML_SCALAR_NODE, handle_netdef_mac, NULL, netdef_offset(match.mac)},
437494 {"name", YAML_SCALAR_NODE, handle_netdef_id, NULL, netdef_offset(match.original_name)},
458515 {
459516 g_assert(cur_auth);
460517 if (strcmp(scalar(node), "none") == 0)
461 cur_auth->key_management = KEY_MANAGEMENT_NONE;
518 cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_NONE;
462519 else if (strcmp(scalar(node), "psk") == 0)
463 cur_auth->key_management = KEY_MANAGEMENT_WPA_PSK;
520 cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK;
464521 else if (strcmp(scalar(node), "eap") == 0)
465 cur_auth->key_management = KEY_MANAGEMENT_WPA_EAP;
522 cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP;
466523 else if (strcmp(scalar(node), "802.1x") == 0)
467 cur_auth->key_management = KEY_MANAGEMENT_8021X;
524 cur_auth->key_management = NETPLAN_AUTH_KEY_MANAGEMENT_8021X;
468525 else
469526 return yaml_error(node, error, "unknown key management type '%s'", scalar(node));
470527 return TRUE;
475532 {
476533 g_assert(cur_auth);
477534 if (strcmp(scalar(node), "tls") == 0)
478 cur_auth->eap_method = EAP_TLS;
535 cur_auth->eap_method = NETPLAN_AUTH_EAP_TLS;
479536 else if (strcmp(scalar(node), "peap") == 0)
480 cur_auth->eap_method = EAP_PEAP;
537 cur_auth->eap_method = NETPLAN_AUTH_EAP_PEAP;
481538 else if (strcmp(scalar(node), "ttls") == 0)
482 cur_auth->eap_method = EAP_TTLS;
539 cur_auth->eap_method = NETPLAN_AUTH_EAP_TTLS;
483540 else
484541 return yaml_error(node, error, "unknown EAP method '%s'", scalar(node));
485542 return TRUE;
486543 }
487544
488 const mapping_entry_handler auth_handlers[] = {
545 static const mapping_entry_handler auth_handlers[] = {
489546 {"key-management", YAML_SCALAR_NODE, handle_auth_key_management},
490547 {"method", YAML_SCALAR_NODE, handle_auth_method},
491548 {"identity", YAML_SCALAR_NODE, handle_auth_str, NULL, auth_offset(identity)},
495552 {"client-certificate", YAML_SCALAR_NODE, handle_auth_str, NULL, auth_offset(client_certificate)},
496553 {"client-key", YAML_SCALAR_NODE, handle_auth_str, NULL, auth_offset(client_key)},
497554 {"client-key-password", YAML_SCALAR_NODE, handle_auth_str, NULL, auth_offset(client_key_password)},
555 {"phase2-auth", YAML_SCALAR_NODE, handle_auth_str, NULL, auth_offset(phase2_auth)},
498556 {NULL}
499557 };
500558
502560 * Grammar and handlers for network device definition
503561 ****************************************************/
504562
505 static netdef_backend
506 get_default_backend_for_type(netdef_type type)
507 {
508 if (backend_global != BACKEND_NONE)
563 static NetplanBackend
564 get_default_backend_for_type(NetplanDefType type)
565 {
566 if (backend_global != NETPLAN_BACKEND_NONE)
509567 return backend_global;
510568
511569 /* networkd can handle all device types at the moment, so nothing
512570 * type-specific */
513 return BACKEND_NETWORKD;
571 return NETPLAN_BACKEND_NETWORKD;
572 }
573
574 static gboolean
575 handle_access_point_guint(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
576 {
577 return handle_generic_guint(doc, node, cur_access_point, data, error);
578 }
579
580 static gboolean
581 handle_access_point_mac(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
582 {
583 return handle_generic_mac(doc, node, cur_access_point, data, error);
514584 }
515585
516586 static gboolean
519589 g_assert(cur_access_point);
520590 /* shortcut for WPA-PSK */
521591 cur_access_point->has_auth = TRUE;
522 cur_access_point->auth.key_management = KEY_MANAGEMENT_WPA_PSK;
592 cur_access_point->auth.key_management = NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK;
523593 g_free(cur_access_point->auth.password);
524594 cur_access_point->auth.password = g_strdup(scalar(node));
525595 return TRUE;
545615 {
546616 g_assert(cur_access_point);
547617 if (strcmp(scalar(node), "infrastructure") == 0)
548 cur_access_point->mode = WIFI_MODE_INFRASTRUCTURE;
618 cur_access_point->mode = NETPLAN_WIFI_MODE_INFRASTRUCTURE;
549619 else if (strcmp(scalar(node), "adhoc") == 0)
550 cur_access_point->mode = WIFI_MODE_ADHOC;
620 cur_access_point->mode = NETPLAN_WIFI_MODE_ADHOC;
551621 else if (strcmp(scalar(node), "ap") == 0)
552 cur_access_point->mode = WIFI_MODE_AP;
622 cur_access_point->mode = NETPLAN_WIFI_MODE_AP;
553623 else
554624 return yaml_error(node, error, "unknown wifi mode '%s'", scalar(node));
555625 return TRUE;
556626 }
557627
558 const mapping_entry_handler wifi_access_point_handlers[] = {
628 static gboolean
629 handle_access_point_band(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
630 {
631 g_assert(cur_access_point);
632 if (strcmp(scalar(node), "5GHz") == 0 || strcmp(scalar(node), "5G") == 0)
633 cur_access_point->band = NETPLAN_WIFI_BAND_5;
634 else if (strcmp(scalar(node), "2.4GHz") == 0 || strcmp(scalar(node), "2.4G") == 0)
635 cur_access_point->band = NETPLAN_WIFI_BAND_24;
636 else
637 return yaml_error(node, error, "unknown wifi band '%s'", scalar(node));
638 return TRUE;
639 }
640
641 static const mapping_entry_handler wifi_access_point_handlers[] = {
642 {"band", YAML_SCALAR_NODE, handle_access_point_band},
643 {"bssid", YAML_SCALAR_NODE, handle_access_point_mac, NULL, access_point_offset(bssid)},
644 {"channel", YAML_SCALAR_NODE, handle_access_point_guint, NULL, access_point_offset(channel)},
559645 {"mode", YAML_SCALAR_NODE, handle_access_point_mode},
560646 {"password", YAML_SCALAR_NODE, handle_access_point_password},
561647 {"auth", YAML_MAPPING_NODE, handle_access_point_auth},
566652 * Parse scalar node's string into a netdef_backend.
567653 */
568654 static gboolean
569 parse_renderer(yaml_node_t* node, netdef_backend* backend, GError** error)
655 parse_renderer(yaml_node_t* node, NetplanBackend* backend, GError** error)
570656 {
571657 if (strcmp(scalar(node), "networkd") == 0)
572 *backend = BACKEND_NETWORKD;
658 *backend = NETPLAN_BACKEND_NETWORKD;
573659 else if (strcmp(scalar(node), "NetworkManager") == 0)
574 *backend = BACKEND_NM;
660 *backend = NETPLAN_BACKEND_NM;
575661 else
576662 return yaml_error(node, error, "unknown renderer '%s'", scalar(node));
577663 return TRUE;
580666 static gboolean
581667 handle_netdef_renderer(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
582668 {
669 if (cur_netdef->type == NETPLAN_DEF_TYPE_VLAN) {
670 if (strcmp(scalar(node), "sriov") == 0) {
671 cur_netdef->sriov_vlan_filter = TRUE;
672 return TRUE;
673 }
674 }
675
583676 return parse_renderer(node, &cur_netdef->backend, error);
584677 }
585678
590683 g_ascii_strcasecmp(scalar(node), "on") == 0 ||
591684 g_ascii_strcasecmp(scalar(node), "yes") == 0 ||
592685 g_ascii_strcasecmp(scalar(node), "y") == 0)
593 cur_netdef->accept_ra = ACCEPT_RA_ENABLED;
686 cur_netdef->accept_ra = NETPLAN_RA_MODE_ENABLED;
594687 else if (g_ascii_strcasecmp(scalar(node), "false") == 0 ||
595688 g_ascii_strcasecmp(scalar(node), "off") == 0 ||
596689 g_ascii_strcasecmp(scalar(node), "no") == 0 ||
597690 g_ascii_strcasecmp(scalar(node), "n") == 0)
598 cur_netdef->accept_ra = ACCEPT_RA_DISABLED;
691 cur_netdef->accept_ra = NETPLAN_RA_MODE_DISABLED;
599692 else
600693 return yaml_error(node, error, "invalid boolean value '%s'", scalar(node));
601694
607700 {
608701 cur_netdef->has_match = TRUE;
609702 return process_mapping(doc, node, match_handlers, error);
703 }
704
705 struct NetplanWifiWowlanType NETPLAN_WIFI_WOWLAN_TYPES[] = {
706 {"default", NETPLAN_WIFI_WOWLAN_DEFAULT},
707 {"any", NETPLAN_WIFI_WOWLAN_ANY},
708 {"disconnect", NETPLAN_WIFI_WOWLAN_DISCONNECT},
709 {"magic_pkt", NETPLAN_WIFI_WOWLAN_MAGIC},
710 {"gtk_rekey_failure", NETPLAN_WIFI_WOWLAN_GTK_REKEY_FAILURE},
711 {"eap_identity_req", NETPLAN_WIFI_WOWLAN_EAP_IDENTITY_REQ},
712 {"four_way_handshake", NETPLAN_WIFI_WOWLAN_4WAY_HANDSHAKE},
713 {"rfkill_release", NETPLAN_WIFI_WOWLAN_RFKILL_RELEASE},
714 {"tcp", NETPLAN_WIFI_WOWLAN_TCP},
715 {NULL},
716 };
717
718 static gboolean
719 handle_wowlan(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
720 {
721 for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
722 yaml_node_t *entry = yaml_document_get_node(doc, *i);
723 assert_type(entry, YAML_SCALAR_NODE);
724 int found = FALSE;
725
726 for (unsigned i = 0; NETPLAN_WIFI_WOWLAN_TYPES[i].name != NULL; ++i) {
727 if (g_ascii_strcasecmp(scalar(entry), NETPLAN_WIFI_WOWLAN_TYPES[i].name) == 0) {
728 cur_netdef->wowlan |= NETPLAN_WIFI_WOWLAN_TYPES[i].flag;
729 found = TRUE;
730 break;
731 }
732 }
733 if (!found)
734 return yaml_error(node, error, "invalid value for wakeonwlan: '%s'", scalar(entry));
735 }
736 if (cur_netdef->wowlan > NETPLAN_WIFI_WOWLAN_DEFAULT && cur_netdef->wowlan & NETPLAN_WIFI_WOWLAN_TYPES[0].flag)
737 return yaml_error(node, error, "'default' is an exclusive flag for wakeonwlan");
738 return TRUE;
610739 }
611740
612741 static gboolean
701830 assert_type(value, YAML_MAPPING_NODE);
702831
703832 g_assert(cur_access_point == NULL);
704 cur_access_point = g_new0(wifi_access_point, 1);
833 cur_access_point = g_new0(NetplanWifiAccessPoint, 1);
705834 cur_access_point->ssid = g_strdup(scalar(key));
706835 g_debug("%s: adding wifi AP '%s'", cur_netdef->id, cur_access_point->ssid);
707836
738867 /* all entries must refer to already defined IDs */
739868 for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
740869 yaml_node_t *entry = yaml_document_get_node(doc, *i);
741 net_definition *component;
870 NetplanNetDefinition *component;
742871
743872 assert_type(entry, YAML_SCALAR_NODE);
744873 component = g_hash_table_lookup(netdefs, scalar(entry));
760889
761890 /**
762891 * Handler for bond "mode" types.
763 * @data: offset into net_definition where the const char* field to write is
892 * @data: offset into NetplanNetDefinition where the const char* field to write is
764893 * located
765894 */
766895 static gboolean
788917 /* all entries must refer to already defined IDs */
789918 for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
790919 yaml_node_t *entry = yaml_document_get_node(doc, *i);
791 net_definition *component;
920 NetplanNetDefinition *component;
792921
793922 assert_type(entry, YAML_SCALAR_NODE);
794923 component = g_hash_table_lookup(netdefs, scalar(entry));
8791008 return TRUE;
8801009 }
8811010
882 struct optional_address_option optional_address_options[] = {
883 {"ipv4-ll", OPTIONAL_IPV4_LL},
884 {"ipv6-ra", OPTIONAL_IPV6_RA},
885 {"dhcp4", OPTIONAL_DHCP4},
886 {"dhcp6", OPTIONAL_DHCP6},
887 {"static", OPTIONAL_STATIC},
1011 struct NetplanOptionalAddressType NETPLAN_OPTIONAL_ADDRESS_TYPES[] = {
1012 {"ipv4-ll", NETPLAN_OPTIONAL_IPV4_LL},
1013 {"ipv6-ra", NETPLAN_OPTIONAL_IPV6_RA},
1014 {"dhcp4", NETPLAN_OPTIONAL_DHCP4},
1015 {"dhcp6", NETPLAN_OPTIONAL_DHCP6},
1016 {"static", NETPLAN_OPTIONAL_STATIC},
8881017 {NULL},
8891018 };
8901019
8961025 assert_type(entry, YAML_SCALAR_NODE);
8971026 int found = FALSE;
8981027
899 for (unsigned i = 0; optional_address_options[i].name != NULL; ++i) {
900 if (g_ascii_strcasecmp(scalar(entry), optional_address_options[i].name) == 0) {
901 cur_netdef->optional_addresses |= optional_address_options[i].flag;
1028 for (unsigned i = 0; NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name != NULL; ++i) {
1029 if (g_ascii_strcasecmp(scalar(entry), NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name) == 0) {
1030 cur_netdef->optional_addresses |= NETPLAN_OPTIONAL_ADDRESS_TYPES[i].flag;
9021031 found = TRUE;
9031032 break;
9041033 }
11301259 yaml_node_t* key, *value;
11311260 guint v;
11321261 gchar* endptr;
1133 net_definition *component;
1262 NetplanNetDefinition *component;
11341263 guint* ref_ptr;
11351264
11361265 key = yaml_document_get_node(doc, entry->key);
11661295 yaml_node_t* key, *value;
11671296 guint v;
11681297 gchar* endptr;
1169 net_definition *component;
1298 NetplanNetDefinition *component;
11701299 guint* ref_ptr;
11711300
11721301 key = yaml_document_get_node(doc, entry->key);
11961325 return TRUE;
11971326 }
11981327
1199 const mapping_entry_handler bridge_params_handlers[] = {
1328 static const mapping_entry_handler bridge_params_handlers[] = {
12001329 {"ageing-time", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(bridge_params.ageing_time)},
12011330 {"forward-delay", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(bridge_params.forward_delay)},
12021331 {"hello-time", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(bridge_params.hello_time)},
12201349 * Grammar and handlers for network config "routes" entry
12211350 ****************************************************/
12221351
1223 const mapping_entry_handler routes_handlers[] = {
1352 static const mapping_entry_handler routes_handlers[] = {
12241353 {"from", YAML_SCALAR_NODE, handle_routes_ip, NULL, route_offset(from)},
12251354 {"on-link", YAML_SCALAR_NODE, handle_routes_bool, NULL, route_offset(onlink)},
12261355 {"scope", YAML_SCALAR_NODE, handle_routes_scope},
12381367 for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
12391368 yaml_node_t *entry = yaml_document_get_node(doc, *i);
12401369
1241 cur_route = g_new0(ip_route, 1);
1370 cur_route = g_new0(NetplanIPRoute, 1);
12421371 cur_route->type = g_strdup("unicast");
12431372 cur_route->scope = g_strdup("global");
12441373 cur_route->family = G_MAXUINT; /* 0 is a valid family ID */
1245 cur_route->metric = METRIC_UNSPEC; /* 0 is a valid metric */
1374 cur_route->metric = NETPLAN_METRIC_UNSPEC; /* 0 is a valid metric */
12461375
12471376 if (process_mapping(doc, entry, routes_handlers, error)) {
12481377 if (!cur_netdef->routes) {
1249 cur_netdef->routes = g_array_new(FALSE, FALSE, sizeof(ip_route*));
1378 cur_netdef->routes = g_array_new(FALSE, FALSE, sizeof(NetplanIPRoute*));
12501379 }
12511380
12521381 g_array_append_val(cur_netdef->routes, cur_route);
12711400 return TRUE;
12721401 }
12731402
1274 const mapping_entry_handler ip_rules_handlers[] = {
1403 static const mapping_entry_handler ip_rules_handlers[] = {
12751404 {"from", YAML_SCALAR_NODE, handle_ip_rule_ip, NULL, ip_rule_offset(from)},
12761405 {"mark", YAML_SCALAR_NODE, handle_ip_rule_fwmark},
12771406 {"priority", YAML_SCALAR_NODE, handle_ip_rule_prio},
12871416 for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) {
12881417 yaml_node_t *entry = yaml_document_get_node(doc, *i);
12891418
1290 cur_ip_rule = g_new0(ip_rule, 1);
1419 cur_ip_rule = g_new0(NetplanIPRule, 1);
12911420 cur_ip_rule->family = G_MAXUINT; /* 0 is a valid family ID */
1292 cur_ip_rule->priority = IP_RULE_PRIO_UNSPEC;
1293 cur_ip_rule->table = ROUTE_TABLE_UNSPEC;
1294 cur_ip_rule->tos = IP_RULE_TOS_UNSPEC;
1295 cur_ip_rule->fwmark = IP_RULE_FW_MARK_UNSPEC;
1421 cur_ip_rule->priority = NETPLAN_IP_RULE_PRIO_UNSPEC;
1422 cur_ip_rule->table = NETPLAN_ROUTE_TABLE_UNSPEC;
1423 cur_ip_rule->tos = NETPLAN_IP_RULE_TOS_UNSPEC;
1424 cur_ip_rule->fwmark = NETPLAN_IP_RULE_FW_MARK_UNSPEC;
12961425
12971426 if (process_mapping(doc, entry, ip_rules_handlers, error)) {
12981427 if (!cur_netdef->ip_rules) {
1299 cur_netdef->ip_rules = g_array_new(FALSE, FALSE, sizeof(ip_rule*));
1428 cur_netdef->ip_rules = g_array_new(FALSE, FALSE, sizeof(NetplanIPRule*));
13001429 }
13011430
13021431 g_array_append_val(cur_netdef->ip_rules, cur_ip_rule);
13451474 static gboolean
13461475 handle_bond_primary_slave(yaml_document_t* doc, yaml_node_t* node, const void* data, GError** error)
13471476 {
1348 net_definition *component;
1477 NetplanNetDefinition *component;
13491478 char** ref_ptr;
13501479
13511480 component = g_hash_table_lookup(netdefs, scalar(node));
13641493 return TRUE;
13651494 }
13661495
1367 const mapping_entry_handler bond_params_handlers[] = {
1496 static const mapping_entry_handler bond_params_handlers[] = {
13681497 {"mode", YAML_SCALAR_NODE, handle_bond_mode, NULL, netdef_offset(bond_params.mode)},
13691498 {"lacp-rate", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(bond_params.lacp_rate)},
13701499 {"mii-monitor-interval", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(bond_params.monitor_interval)},
14171546 ****************************************************/
14181547
14191548 const char*
1420 tunnel_mode_to_string(tunnel_mode mode)
1421 {
1422 return tunnel_mode_table[mode];
1549 tunnel_mode_to_string(NetplanTunnelMode mode)
1550 {
1551 return netplan_tunnel_mode_table[mode];
14231552 }
14241553
14251554 static gboolean
14491578 handle_tunnel_mode(yaml_document_t* doc, yaml_node_t* node, const void* _, GError** error)
14501579 {
14511580 const char *key = scalar(node);
1452 tunnel_mode i;
1581 NetplanTunnelMode i;
14531582
14541583 // Skip over unknown (0) tunnel mode.
1455 for (i = 1; i < _TUNNEL_MODE_MAX; ++i) {
1456 if (g_strcmp0(tunnel_mode_table[i], key) == 0) {
1584 for (i = 1; i < NETPLAN_TUNNEL_MODE_MAX_; ++i) {
1585 if (g_strcmp0(netplan_tunnel_mode_table[i], key) == 0) {
14571586 cur_netdef->tunnel.mode = i;
14581587 return TRUE;
14591588 }
14841613 return TRUE;
14851614 }
14861615
1487 const mapping_entry_handler tunnel_keys_handlers[] = {
1616 static const mapping_entry_handler tunnel_keys_handlers[] = {
14881617 {"input", YAML_SCALAR_NODE, handle_tunnel_key, NULL, netdef_offset(tunnel.input_key)},
14891618 {"output", YAML_SCALAR_NODE, handle_tunnel_key, NULL, netdef_offset(tunnel.output_key)},
14901619 {NULL}
15181647 * Grammar and handlers for network devices
15191648 ****************************************************/
15201649
1521 const mapping_entry_handler nameservers_handlers[] = {
1650 static const mapping_entry_handler nm_backend_settings_handlers[] = {
1651 {"name", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(backend_settings.nm.name)},
1652 {"uuid", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(backend_settings.nm.uuid)},
1653 {"stable-id", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(backend_settings.nm.stable_id)},
1654 {"device", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(backend_settings.nm.device)},
1655 {NULL}
1656 };
1657
1658 static const mapping_entry_handler nameservers_handlers[] = {
15221659 {"search", YAML_SEQUENCE_NODE, handle_nameservers_search},
15231660 {"addresses", YAML_SEQUENCE_NODE, handle_nameservers_addresses},
15241661 {NULL}
15361673 {"use-ntp", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(overrides.use_ntp)}, \
15371674 {"use-routes", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(overrides.use_routes)}
15381675
1539 const mapping_entry_handler dhcp4_overrides_handlers[] = {
1676 static const mapping_entry_handler dhcp4_overrides_handlers[] = {
15401677 COMMON_DHCP_OVERRIDES_HANDLERS(dhcp4_overrides),
15411678 {NULL},
15421679 };
15431680
1544 const mapping_entry_handler dhcp6_overrides_handlers[] = {
1681 static const mapping_entry_handler dhcp6_overrides_handlers[] = {
15451682 COMMON_DHCP_OVERRIDES_HANDLERS(dhcp6_overrides),
15461683 {NULL},
15471684 };
15581695 {"dhcp6-overrides", YAML_MAPPING_NODE, NULL, dhcp6_overrides_handlers}, \
15591696 {"gateway4", YAML_SCALAR_NODE, handle_gateway4}, \
15601697 {"gateway6", YAML_SCALAR_NODE, handle_gateway6}, \
1698 {"ipv6-address-generation", YAML_SCALAR_NODE, handle_netdef_addrgen}, \
15611699 {"ipv6-mtu", YAML_SCALAR_NODE, handle_netdef_guint, NULL, netdef_offset(ipv6_mtubytes)}, \
15621700 {"ipv6-privacy", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(ip6_privacy)}, \
15631701 {"link-local", YAML_SEQUENCE_NODE, handle_link_local}, \
15701708 {"routes", YAML_SEQUENCE_NODE, handle_routes}, \
15711709 {"routing-policy", YAML_SEQUENCE_NODE, handle_ip_rules}
15721710
1711 #define COMMON_BACKEND_HANDLERS \
1712 {"networkmanager", YAML_MAPPING_NODE, NULL, nm_backend_settings_handlers}
1713
15731714 /* Handlers for physical links */
15741715 #define PHYSICAL_LINK_HANDLERS \
15751716 {"match", YAML_MAPPING_NODE, handle_match}, \
15761717 {"set-name", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(set_name)}, \
1577 {"wakeonlan", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(wake_on_lan)}
1578
1579 const mapping_entry_handler ethernet_def_handlers[] = {
1718 {"wakeonlan", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(wake_on_lan)}, \
1719 {"wakeonwlan", YAML_SEQUENCE_NODE, handle_wowlan, NULL, netdef_offset(wowlan)}, \
1720 {"emit-lldp", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(emit_lldp)}
1721
1722 static const mapping_entry_handler ethernet_def_handlers[] = {
15801723 COMMON_LINK_HANDLERS,
1724 COMMON_BACKEND_HANDLERS,
15811725 PHYSICAL_LINK_HANDLERS,
15821726 {"auth", YAML_MAPPING_NODE, handle_auth},
1727 {"link", YAML_SCALAR_NODE, handle_netdef_id_ref, NULL, netdef_offset(sriov_link)},
1728 {"virtual-function-count", YAML_SCALAR_NODE, handle_netdef_guint, NULL, netdef_offset(sriov_explicit_vf_count)},
15831729 {NULL}
15841730 };
15851731
1586 const mapping_entry_handler wifi_def_handlers[] = {
1732 static const mapping_entry_handler wifi_def_handlers[] = {
15871733 COMMON_LINK_HANDLERS,
1734 COMMON_BACKEND_HANDLERS,
15881735 PHYSICAL_LINK_HANDLERS,
15891736 {"access-points", YAML_MAPPING_NODE, handle_wifi_access_points},
15901737 {"auth", YAML_MAPPING_NODE, handle_auth},
15911738 {NULL}
15921739 };
15931740
1594 const mapping_entry_handler bridge_def_handlers[] = {
1741 static const mapping_entry_handler bridge_def_handlers[] = {
15951742 COMMON_LINK_HANDLERS,
1743 COMMON_BACKEND_HANDLERS,
15961744 {"interfaces", YAML_SEQUENCE_NODE, handle_bridge_interfaces, NULL, NULL},
15971745 {"parameters", YAML_MAPPING_NODE, handle_bridge},
15981746 {NULL}
15991747 };
16001748
1601 const mapping_entry_handler bond_def_handlers[] = {
1749 static const mapping_entry_handler bond_def_handlers[] = {
16021750 COMMON_LINK_HANDLERS,
1751 COMMON_BACKEND_HANDLERS,
16031752 {"interfaces", YAML_SEQUENCE_NODE, handle_bond_interfaces, NULL, NULL},
16041753 {"parameters", YAML_MAPPING_NODE, handle_bonding},
16051754 {NULL}
16061755 };
16071756
1608 const mapping_entry_handler vlan_def_handlers[] = {
1757 static const mapping_entry_handler vlan_def_handlers[] = {
16091758 COMMON_LINK_HANDLERS,
1759 COMMON_BACKEND_HANDLERS,
16101760 {"id", YAML_SCALAR_NODE, handle_netdef_guint, NULL, netdef_offset(vlan_id)},
16111761 {"link", YAML_SCALAR_NODE, handle_netdef_id_ref, NULL, netdef_offset(vlan_link)},
16121762 {NULL}
16131763 };
16141764
1615 const mapping_entry_handler tunnel_def_handlers[] = {
1765 static const mapping_entry_handler modem_def_handlers[] = {
16161766 COMMON_LINK_HANDLERS,
1767 {"apn", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.apn)},
1768 {"auto-config", YAML_SCALAR_NODE, handle_netdef_bool, NULL, netdef_offset(modem_params.auto_config)},
1769 {"device-id", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.device_id)},
1770 {"network-id", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.network_id)},
1771 {"number", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.number)},
1772 {"password", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.password)},
1773 {"pin", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.pin)},
1774 {"sim-id", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.sim_id)},
1775 {"sim-operator-id", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.sim_operator_id)},
1776 {"username", YAML_SCALAR_NODE, handle_netdef_str, NULL, netdef_offset(modem_params.username)},
1777 };
1778
1779 static const mapping_entry_handler tunnel_def_handlers[] = {
1780 COMMON_LINK_HANDLERS,
1781 COMMON_BACKEND_HANDLERS,
16171782 {"mode", YAML_SCALAR_NODE, handle_tunnel_mode},
16181783 {"local", YAML_SCALAR_NODE, handle_tunnel_addr, NULL, netdef_offset(tunnel.local_ip)},
16191784 {"remote", YAML_SCALAR_NODE, handle_tunnel_addr, NULL, netdef_offset(tunnel.remote_ip)},
16491814 }
16501815
16511816 static void
1652 initialize_dhcp_overrides(dhcp_overrides* overrides)
1817 initialize_dhcp_overrides(NetplanDHCPOverrides* overrides)
16531818 {
16541819 overrides->use_dns = TRUE;
16551820 overrides->use_domains = NULL;
16591824 overrides->use_mtu = TRUE;
16601825 overrides->use_routes = TRUE;
16611826 overrides->hostname = NULL;
1662 overrides->metric = METRIC_UNSPEC;
1827 overrides->metric = NETPLAN_METRIC_UNSPEC;
16631828 }
16641829
16651830 /**
17041869 return yaml_error(key, error, "Updated definition '%s' changes device type", scalar(key));
17051870 } else {
17061871 /* create new network definition */
1707 cur_netdef = g_new0(net_definition, 1);
1872 cur_netdef = g_new0(NetplanNetDefinition, 1);
17081873 cur_netdef->type = GPOINTER_TO_UINT(data);
1709 cur_netdef->backend = backend_cur_type ?: BACKEND_NONE;
1874 cur_netdef->backend = backend_cur_type ?: NETPLAN_BACKEND_NONE;
17101875 cur_netdef->id = g_strdup(scalar(key));
17111876
17121877 /* Set some default values */
17131878 cur_netdef->vlan_id = G_MAXUINT; /* 0 is a valid ID */
1714 cur_netdef->tunnel.mode = TUNNEL_MODE_UNKNOWN;
1879 cur_netdef->tunnel.mode = NETPLAN_TUNNEL_MODE_UNKNOWN;
17151880 cur_netdef->dhcp_identifier = g_strdup("duid"); /* keep networkd's default */
17161881 /* systemd-networkd defaults to IPv6 LL enabled; keep that default */
17171882 cur_netdef->linklocal.ipv6 = TRUE;
17181883 g_hash_table_insert(netdefs, cur_netdef->id, cur_netdef);
17191884 netdefs_ordered = g_list_append(netdefs_ordered, cur_netdef);
1885 cur_netdef->sriov_vlan_filter = FALSE;
17201886
17211887 /* DHCP override defaults */
17221888 initialize_dhcp_overrides(&cur_netdef->dhcp4_overrides);
17311897
17321898 /* and fill it with definitions */
17331899 switch (cur_netdef->type) {
1734 case ND_BOND: handlers = bond_def_handlers; break;
1735 case ND_BRIDGE: handlers = bridge_def_handlers; break;
1736 case ND_ETHERNET: handlers = ethernet_def_handlers; break;
1737 case ND_TUNNEL: handlers = tunnel_def_handlers; break;
1738 case ND_VLAN: handlers = vlan_def_handlers; break;
1739 case ND_WIFI: handlers = wifi_def_handlers; break;
1900 case NETPLAN_DEF_TYPE_BOND: handlers = bond_def_handlers; break;
1901 case NETPLAN_DEF_TYPE_BRIDGE: handlers = bridge_def_handlers; break;
1902 case NETPLAN_DEF_TYPE_ETHERNET: handlers = ethernet_def_handlers; break;
1903 case NETPLAN_DEF_TYPE_MODEM: handlers = modem_def_handlers; break;
1904 case NETPLAN_DEF_TYPE_TUNNEL: handlers = tunnel_def_handlers; break;
1905 case NETPLAN_DEF_TYPE_VLAN: handlers = vlan_def_handlers; break;
1906 case NETPLAN_DEF_TYPE_WIFI: handlers = wifi_def_handlers; break;
17401907 default: g_assert_not_reached(); // LCOV_EXCL_LINE
17411908 }
17421909 if (!process_mapping(doc, value, handlers, error))
17481915
17491916 /* convenience shortcut: physical device without match: means match
17501917 * name on ID */
1751 if (cur_netdef->type < ND_VIRTUAL && !cur_netdef->has_match)
1918 if (cur_netdef->type < NETPLAN_DEF_TYPE_VIRTUAL && !cur_netdef->has_match)
17521919 cur_netdef->match.original_name = cur_netdef->id;
17531920 }
1754 backend_cur_type = BACKEND_NONE;
1755 return TRUE;
1756 }
1757
1758 const mapping_entry_handler network_handlers[] = {
1759 {"bonds", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_BOND)},
1760 {"bridges", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_BRIDGE)},
1761 {"ethernets", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_ETHERNET)},
1921 backend_cur_type = NETPLAN_BACKEND_NONE;
1922 return TRUE;
1923 }
1924
1925 static const mapping_entry_handler network_handlers[] = {
1926 {"bonds", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_BOND)},
1927 {"bridges", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_BRIDGE)},
1928 {"ethernets", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_ETHERNET)},
17621929 {"renderer", YAML_SCALAR_NODE, handle_network_renderer},
1763 {"tunnels", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_TUNNEL)},
1930 {"tunnels", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_TUNNEL)},
17641931 {"version", YAML_SCALAR_NODE, handle_network_version},
1765 {"vlans", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_VLAN)},
1766 {"wifis", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(ND_WIFI)},
1932 {"vlans", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_VLAN)},
1933 {"wifis", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_WIFI)},
1934 {"modems", YAML_MAPPING_NODE, handle_network_type, NULL, GUINT_TO_POINTER(NETPLAN_DEF_TYPE_MODEM)},
17671935 {NULL}
17681936 };
17691937
17711939 * Grammar and handlers for root node
17721940 ****************************************************/
17731941
1774 const mapping_entry_handler root_handlers[] = {
1942 static const mapping_entry_handler root_handlers[] = {
17751943 {"network", YAML_MAPPING_NODE, NULL, network_handlers},
17761944 {NULL}
17771945 };
18081976 if (g_hash_table_size(missing_id) > 0) {
18091977 GHashTableIter iter;
18101978 gpointer key, value;
1811 missing_node *missing;
1979 NetplanMissingNode *missing;
18121980
18131981 g_clear_error(error);
18141982
18161984 * approximate early failure and give the user a meaningful error. */
18171985 g_hash_table_iter_init (&iter, missing_id);
18181986 g_hash_table_iter_next (&iter, &key, &value);
1819 missing = (missing_node*) value;
1987 missing = (NetplanMissingNode*) value;
18201988
18211989 return yaml_error(missing->node, error, "%s: interface '%s' is not defined",
18221990 missing->netdef_id,
18322000 * Parse given YAML file and create/update global "netdefs" list.
18332001 */
18342002 gboolean
1835 parse_yaml(const char* filename, GError** error)
2003 netplan_parse_yaml(const char* filename, GError** error)
18362004 {
18372005 yaml_document_t doc;
18382006 gboolean ret;
18392007
18402008 if (!load_yaml(filename, &doc, error))
18412009 return FALSE;
2010
2011 if (!netdefs)
2012 netdefs = g_hash_table_new(g_str_hash, g_str_equal);
18422013
18432014 /* empty file? */
18442015 if (yaml_document_get_root_node(&doc) == NULL)
18452016 return TRUE;
1846
1847 if (!netdefs)
1848 netdefs = g_hash_table_new(g_str_hash, g_str_equal);
18492017
18502018 g_assert(ids_in_file == NULL);
18512019 ids_in_file = g_hash_table_new(g_str_hash, NULL);
18632031 finish_iterator(gpointer key, gpointer value, gpointer user_data)
18642032 {
18652033 GError **error = (GError **)user_data;
1866 net_definition* nd = value;
2034 NetplanNetDefinition* nd = value;
18672035
18682036 /* Take more steps to make sure we always have a backend set for netdefs */
1869 if (nd->backend == BACKEND_NONE) {
2037 if (nd->backend == NETPLAN_BACKEND_NONE) {
18702038 nd->backend = get_default_backend_for_type(nd->type);
18712039 g_debug("%s: setting default backend to %i", nd->id, nd->backend);
18722040 }
18792047 /**
18802048 * Post-processing after parsing all config files
18812049 */
1882 gboolean
1883 finish_parse(GError** error)
1884 {
1885 if (netdefs)
2050 GHashTable *
2051 netplan_finish_parse(GError** error)
2052 {
2053 if (netdefs) {
2054 g_debug("We have some netdefs, pass them through a final round of validation");
18862055 g_hash_table_foreach(netdefs, finish_iterator, error);
2056 }
18872057
18882058 if (error && *error)
1889 return FALSE;
1890
1891 return TRUE;
2059 return NULL;
2060
2061 return netdefs;
18922062 }
18932063
18942064 /**
18952065 * Return current global backend.
18962066 */
1897 netdef_backend
1898 get_global_backend()
2067 NetplanBackend
2068 netplan_get_global_backend()
18992069 {
19002070 return backend_global;
19012071 }
3838 ****************************************************/
3939
4040 typedef enum {
41 ND_NONE,
41 NETPLAN_DEF_TYPE_NONE,
4242 /* physical devices */
43 ND_ETHERNET,
44 ND_WIFI,
43 NETPLAN_DEF_TYPE_ETHERNET,
44 NETPLAN_DEF_TYPE_WIFI,
45 NETPLAN_DEF_TYPE_MODEM,
4546 /* virtual devices */
46 ND_VIRTUAL,
47 ND_BRIDGE = ND_VIRTUAL,
48 ND_BOND,
49 ND_VLAN,
50 ND_TUNNEL,
51 } netdef_type;
52
53 typedef enum {
54 BACKEND_NONE,
55 BACKEND_NETWORKD,
56 BACKEND_NM,
57 _BACKEND_MAX,
58 } netdef_backend;
59
60 static const char* const netdef_backend_to_name[_BACKEND_MAX] = {
61 [BACKEND_NONE] = "none",
62 [BACKEND_NETWORKD] = "networkd",
63 [BACKEND_NM] = "NetworkManager",
47 NETPLAN_DEF_TYPE_VIRTUAL,
48 NETPLAN_DEF_TYPE_BRIDGE = NETPLAN_DEF_TYPE_VIRTUAL,
49 NETPLAN_DEF_TYPE_BOND,
50 NETPLAN_DEF_TYPE_VLAN,
51 NETPLAN_DEF_TYPE_TUNNEL,
52 } NetplanDefType;
53
54 typedef enum {
55 NETPLAN_BACKEND_NONE,
56 NETPLAN_BACKEND_NETWORKD,
57 NETPLAN_BACKEND_NM,
58 NETPLAN_BACKEND_MAX_,
59 } NetplanBackend;
60
61 static const char* const netplan_backend_to_name[NETPLAN_BACKEND_MAX_] = {
62 [NETPLAN_BACKEND_NONE] = "none",
63 [NETPLAN_BACKEND_NETWORKD] = "networkd",
64 [NETPLAN_BACKEND_NM] = "NetworkManager",
6465 };
6566
6667 typedef enum {
67 ACCEPT_RA_KERNEL,
68 ACCEPT_RA_ENABLED,
69 ACCEPT_RA_DISABLED,
70 } ra_mode;
71
72 typedef enum {
73 OPTIONAL_IPV4_LL = 1<<0,
74 OPTIONAL_IPV6_RA = 1<<1,
75 OPTIONAL_DHCP4 = 1<<2,
76 OPTIONAL_DHCP6 = 1<<3,
77 OPTIONAL_STATIC = 1<<4,
78 } optional_addr;
68 NETPLAN_RA_MODE_KERNEL,
69 NETPLAN_RA_MODE_ENABLED,
70 NETPLAN_RA_MODE_DISABLED,
71 } NetplanRAMode;
72
73 typedef enum {
74 NETPLAN_OPTIONAL_IPV4_LL = 1<<0,
75 NETPLAN_OPTIONAL_IPV6_RA = 1<<1,
76 NETPLAN_OPTIONAL_DHCP4 = 1<<2,
77 NETPLAN_OPTIONAL_DHCP6 = 1<<3,
78 NETPLAN_OPTIONAL_STATIC = 1<<4,
79 } NetplanOptionalAddressFlag;
80
81 typedef enum {
82 NETPLAN_ADDRGEN_DEFAULT,
83 NETPLAN_ADDRGEN_EUI64,
84 NETPLAN_ADDRGEN_STABLEPRIVACY,
85 } NetplanAddrGenMode;
86
87 struct NetplanOptionalAddressType {
88 char* name;
89 NetplanOptionalAddressFlag flag;
90 };
91
92 extern struct NetplanOptionalAddressType NETPLAN_OPTIONAL_ADDRESS_TYPES[];
7993
8094 /* Tunnel mode enum; sync with NetworkManager's DBUS API */
8195 /* TODO: figure out whether networkd's GRETAP and NM's ISATAP
8296 * are the same thing.
8397 */
8498 typedef enum {
85 TUNNEL_MODE_UNKNOWN = 0,
86 TUNNEL_MODE_IPIP = 1,
87 TUNNEL_MODE_GRE = 2,
88 TUNNEL_MODE_SIT = 3,
89 TUNNEL_MODE_ISATAP = 4, // NM only.
90 TUNNEL_MODE_VTI = 5,
91 TUNNEL_MODE_IP6IP6 = 6,
92 TUNNEL_MODE_IPIP6 = 7,
93 TUNNEL_MODE_IP6GRE = 8,
94 TUNNEL_MODE_VTI6 = 9,
99 NETPLAN_TUNNEL_MODE_UNKNOWN = 0,
100 NETPLAN_TUNNEL_MODE_IPIP = 1,
101 NETPLAN_TUNNEL_MODE_GRE = 2,
102 NETPLAN_TUNNEL_MODE_SIT = 3,
103 NETPLAN_TUNNEL_MODE_ISATAP = 4, // NM only.
104 NETPLAN_TUNNEL_MODE_VTI = 5,
105 NETPLAN_TUNNEL_MODE_IP6IP6 = 6,
106 NETPLAN_TUNNEL_MODE_IPIP6 = 7,
107 NETPLAN_TUNNEL_MODE_IP6GRE = 8,
108 NETPLAN_TUNNEL_MODE_VTI6 = 9,
95109
96110 /* systemd-only, apparently? */
97 TUNNEL_MODE_GRETAP = 101,
98 TUNNEL_MODE_IP6GRETAP = 102,
99
100 _TUNNEL_MODE_MAX,
101 } tunnel_mode;
111 NETPLAN_TUNNEL_MODE_GRETAP = 101,
112 NETPLAN_TUNNEL_MODE_IP6GRETAP = 102,
113
114 NETPLAN_TUNNEL_MODE_MAX_,
115 } NetplanTunnelMode;
102116
103117 static const char* const
104 tunnel_mode_table[_TUNNEL_MODE_MAX] = {
105 [TUNNEL_MODE_UNKNOWN] = "unknown",
106 [TUNNEL_MODE_IPIP] = "ipip",
107 [TUNNEL_MODE_GRE] = "gre",
108 [TUNNEL_MODE_SIT] = "sit",
109 [TUNNEL_MODE_ISATAP] = "isatap",
110 [TUNNEL_MODE_VTI] = "vti",
111 [TUNNEL_MODE_IP6IP6] = "ip6ip6",
112 [TUNNEL_MODE_IPIP6] = "ipip6",
113 [TUNNEL_MODE_IP6GRE] = "ip6gre",
114 [TUNNEL_MODE_VTI6] = "vti6",
115
116 [TUNNEL_MODE_GRETAP] = "gretap",
117 [TUNNEL_MODE_IP6GRETAP] = "ip6gretap",
118 netplan_tunnel_mode_table[NETPLAN_TUNNEL_MODE_MAX_] = {
119 [NETPLAN_TUNNEL_MODE_UNKNOWN] = "unknown",
120 [NETPLAN_TUNNEL_MODE_IPIP] = "ipip",
121 [NETPLAN_TUNNEL_MODE_GRE] = "gre",
122 [NETPLAN_TUNNEL_MODE_SIT] = "sit",
123 [NETPLAN_TUNNEL_MODE_ISATAP] = "isatap",
124 [NETPLAN_TUNNEL_MODE_VTI] = "vti",
125 [NETPLAN_TUNNEL_MODE_IP6IP6] = "ip6ip6",
126 [NETPLAN_TUNNEL_MODE_IPIP6] = "ipip6",
127 [NETPLAN_TUNNEL_MODE_IP6GRE] = "ip6gre",
128 [NETPLAN_TUNNEL_MODE_VTI6] = "vti6",
129
130 [NETPLAN_TUNNEL_MODE_GRETAP] = "gretap",
131 [NETPLAN_TUNNEL_MODE_IP6GRETAP] = "ip6gretap",
118132 };
119133
120 struct optional_address_option {
134 typedef enum {
135 NETPLAN_WIFI_WOWLAN_DEFAULT = 1<<0,
136 NETPLAN_WIFI_WOWLAN_ANY = 1<<1,
137 NETPLAN_WIFI_WOWLAN_DISCONNECT = 1<<2,
138 NETPLAN_WIFI_WOWLAN_MAGIC = 1<<3,
139 NETPLAN_WIFI_WOWLAN_GTK_REKEY_FAILURE = 1<<4,
140 NETPLAN_WIFI_WOWLAN_EAP_IDENTITY_REQ = 1<<5,
141 NETPLAN_WIFI_WOWLAN_4WAY_HANDSHAKE = 1<<6,
142 NETPLAN_WIFI_WOWLAN_RFKILL_RELEASE = 1<<7,
143 NETPLAN_WIFI_WOWLAN_TCP = 1<<8,
144 } NetplanWifiWowlanFlag;
145
146 struct NetplanWifiWowlanType {
121147 char* name;
122 optional_addr flag;
148 NetplanWifiWowlanFlag flag;
123149 };
124150
125 extern struct optional_address_option optional_address_options[];
126
127 typedef enum {
128 KEY_MANAGEMENT_NONE,
129 KEY_MANAGEMENT_WPA_PSK,
130 KEY_MANAGEMENT_WPA_EAP,
131 KEY_MANAGEMENT_8021X,
132 } auth_key_management_type;
133
134 typedef enum {
135 EAP_NONE,
136 EAP_TLS,
137 EAP_PEAP,
138 EAP_TTLS,
139 } auth_eap_method;
151 extern struct NetplanWifiWowlanType NETPLAN_WIFI_WOWLAN_TYPES[];
152
153 typedef enum {
154 NETPLAN_AUTH_KEY_MANAGEMENT_NONE,
155 NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK,
156 NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP,
157 NETPLAN_AUTH_KEY_MANAGEMENT_8021X,
158 } NetplanAuthKeyManagementType;
159
160 typedef enum {
161 NETPLAN_AUTH_EAP_NONE,
162 NETPLAN_AUTH_EAP_TLS,
163 NETPLAN_AUTH_EAP_PEAP,
164 NETPLAN_AUTH_EAP_TTLS,
165 } NetplanAuthEAPMethod;
140166
141167 typedef struct missing_node {
142168 char* netdef_id;
143169 const yaml_node_t* node;
144 } missing_node;
170 } NetplanMissingNode;
145171
146172 typedef struct authentication_settings {
147 auth_key_management_type key_management;
148 auth_eap_method eap_method;
173 NetplanAuthKeyManagementType key_management;
174 NetplanAuthEAPMethod eap_method;
149175 char* identity;
150176 char* anonymous_identity;
151177 char* password;
153179 char* client_certificate;
154180 char* client_key;
155181 char* client_key_password;
156 } authentication_settings;
182 char* phase2_auth; /* netplan-feature: auth-phase2 */
183 } NetplanAuthenticationSettings;
157184
158185 /* Fields below are valid for dhcp4 and dhcp6 unless otherwise noted. */
159186 typedef struct dhcp_overrides {
166193 char* use_domains; /* netplan-feature: dhcp-use-domains */
167194 char* hostname;
168195 guint metric;
169 } dhcp_overrides;
196 } NetplanDHCPOverrides;
170197
171198 /**
172199 * Represent a configuration stanza
173200 */
174 typedef struct net_definition {
175 netdef_type type;
176 netdef_backend backend;
201
202 struct net_definition;
203
204 typedef struct net_definition NetplanNetDefinition;
205
206 struct net_definition {
207 NetplanDefType type;
208 NetplanBackend backend;
177209 char* id;
178210 /* only necessary for NetworkManager connection UUIDs in some cases */
179211 uuid_t uuid;
180212
181213 /* status options */
182214 gboolean optional;
183 optional_addr optional_addresses;
215 NetplanOptionalAddressFlag optional_addresses;
184216 gboolean critical;
185217
186218 /* addresses */
187219 gboolean dhcp4;
188220 gboolean dhcp6;
189221 char* dhcp_identifier;
190 dhcp_overrides dhcp4_overrides;
191 dhcp_overrides dhcp6_overrides;
192 ra_mode accept_ra;
222 NetplanDHCPOverrides dhcp4_overrides;
223 NetplanDHCPOverrides dhcp6_overrides;
224 NetplanRAMode accept_ra;
193225 GArray* ip4_addresses;
194226 GArray* ip6_addresses;
195227 gboolean ip6_privacy;
228 guint ip6_addr_gen_mode;
196229 char* gateway4;
197230 char* gateway6;
198231 GArray* ip4_nameservers;
211244
212245 /* vlan */
213246 guint vlan_id;
214 struct net_definition* vlan_link;
247 NetplanNetDefinition* vlan_link;
215248 gboolean has_vlans;
216249
217250 /* Configured custom MAC address */
232265 } match;
233266 gboolean has_match;
234267 gboolean wake_on_lan;
235
236 /* these properties are only valid for ND_WIFI */
237 GHashTable* access_points; /* SSID → wifi_access_point* */
268 NetplanWifiWowlanFlag wowlan;
269 gboolean emit_lldp;
270
271
272 /* these properties are only valid for NETPLAN_DEF_TYPE_WIFI */
273 GHashTable* access_points; /* SSID → NetplanWifiAccessPoint* */
238274
239275 struct {
240276 char* mode;
261297 } bond_params;
262298
263299 struct {
300 char* apn;
301 gboolean auto_config;
302 char* device_id;
303 char* network_id;
304 char* number;
305 char* password;
306 char* pin;
307 char* sim_id;
308 char* sim_operator_id;
309 char* username;
310 } modem_params;
311
312 struct {
264313 char* ageing_time;
265314 guint priority;
266315 guint port_priority;
273322 gboolean custom_bridging;
274323
275324 struct {
276 tunnel_mode mode;
325 NetplanTunnelMode mode;
277326 char *local_ip;
278327 char *remote_ip;
279328 char *input_key;
280329 char *output_key;
281330 } tunnel;
282331
283 authentication_settings auth;
332 NetplanAuthenticationSettings auth;
284333 gboolean has_auth;
285 } net_definition;
286
287 typedef enum {
288 WIFI_MODE_INFRASTRUCTURE,
289 WIFI_MODE_ADHOC,
290 WIFI_MODE_AP
291 } wifi_mode;
334
335 /* these properties are only valid for SR-IOV NICs */
336 struct net_definition* sriov_link;
337 gboolean sriov_vlan_filter;
338 guint sriov_explicit_vf_count;
339
340 union {
341 struct NetplanNMSettings {
342 char *name;
343 char *uuid;
344 char *stable_id;
345 char *device;
346 } nm;
347 struct NetplanNetworkdSettings {
348 char *unit;
349 } networkd;
350 } backend_settings;
351 };
352
353 typedef enum {
354 NETPLAN_WIFI_MODE_INFRASTRUCTURE,
355 NETPLAN_WIFI_MODE_ADHOC,
356 NETPLAN_WIFI_MODE_AP
357 } NetplanWifiMode;
358
359 typedef enum {
360 NETPLAN_WIFI_BAND_DEFAULT,
361 NETPLAN_WIFI_BAND_5,
362 NETPLAN_WIFI_BAND_24
363 } NetplanWifiBand;
292364
293365 typedef struct {
294 wifi_mode mode;
366 NetplanWifiMode mode;
295367 char* ssid;
296
297 authentication_settings auth;
368 NetplanWifiBand band;
369 char* bssid;
370 guint channel;
371
372 NetplanAuthenticationSettings auth;
298373 gboolean has_auth;
299 } wifi_access_point;
300
301 #define METRIC_UNSPEC G_MAXUINT
302 #define ROUTE_TABLE_UNSPEC 0
303 #define IP_RULE_PRIO_UNSPEC G_MAXUINT
304 #define IP_RULE_FW_MARK_UNSPEC 0
305 #define IP_RULE_TOS_UNSPEC G_MAXUINT
374 } NetplanWifiAccessPoint;
375
376 #define NETPLAN_METRIC_UNSPEC G_MAXUINT
377 #define NETPLAN_ROUTE_TABLE_UNSPEC 0
378 #define NETPLAN_IP_RULE_PRIO_UNSPEC G_MAXUINT
379 #define NETPLAN_IP_RULE_FW_MARK_UNSPEC 0
380 #define NETPLAN_IP_RULE_TOS_UNSPEC G_MAXUINT
306381
307382 typedef struct {
308383 guint family;
319394 /* valid metrics are valid positive integers.
320395 * invalid metrics are represented by METRIC_UNSPEC */
321396 guint metric;
322 } ip_route;
397 } NetplanIPRoute;
323398
324399 typedef struct {
325400 guint family;
334409 guint fwmark;
335410 /* type-of-service: between 0 and 255 */
336411 guint tos;
337 } ip_rule;
412 } NetplanIPRule;
338413
339414 /* Written/updated by parse_yaml(): char* id → net_definition */
340415 extern GHashTable* netdefs;
344419 * Functions
345420 ****************************************************/
346421
347 gboolean parse_yaml(const char* filename, GError** error);
348 gboolean finish_parse(GError** error);
349 netdef_backend get_global_backend();
350 const char* tunnel_mode_to_string(tunnel_mode mode);
422 gboolean netplan_parse_yaml(const char* filename, GError** error);
423 GHashTable* netplan_finish_parse(GError** error);
424 NetplanBackend netplan_get_global_backend();
425 const char* tunnel_mode_to_string(NetplanTunnelMode mode);
8585 for (size_t i = 0; i < gl.gl_pathc; ++i)
8686 unlink(gl.gl_pathv[i]);
8787 }
88
89 /**
90 * Get the frequency of a given 2.4GHz WiFi channel
91 */
92 int
93 wifi_get_freq24(int channel)
94 {
95 if (channel < 1 || channel > 14) {
96 g_fprintf(stderr, "ERROR: invalid 2.4GHz WiFi channel: %d\n", channel);
97 exit(1);
98 }
99
100 if (!wifi_frequency_24) {
101 wifi_frequency_24 = g_hash_table_new(g_direct_hash, g_direct_equal);
102 /* Initialize 2.4GHz frequencies, as of:
103 * https://en.wikipedia.org/wiki/List_of_WLAN_channels#2.4_GHz_(802.11b/g/n/ax) */
104 for (unsigned i = 0; i < 13; i++) {
105 g_hash_table_insert(wifi_frequency_24, GINT_TO_POINTER(i+1),
106 GINT_TO_POINTER(2412+i*5));
107 }
108 g_hash_table_insert(wifi_frequency_24, GINT_TO_POINTER(14),
109 GINT_TO_POINTER(2484));
110 }
111 return GPOINTER_TO_INT(g_hash_table_lookup(wifi_frequency_24,
112 GINT_TO_POINTER(channel)));
113 }
114
115 /**
116 * Get the frequency of a given 5GHz WiFi channel
117 */
118 int
119 wifi_get_freq5(int channel)
120 {
121 int channels[] = { 7, 8, 9, 11, 12, 16, 32, 34, 36, 38, 40, 42, 44, 46, 48,
122 50, 52, 54, 56, 58, 60, 62, 64, 68, 96, 100, 102, 104,
123 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126,
124 128, 132, 134, 136, 138, 140, 142, 144, 149, 151, 153,
125 155, 157, 159, 161, 165, 169, 173 };
126 gboolean found = FALSE;
127 for (unsigned i = 0; i < sizeof(channels) / sizeof(int); i++) {
128 if (channel == channels[i]) {
129 found = TRUE;
130 break;
131 }
132 }
133 if (!found) {
134 g_fprintf(stderr, "ERROR: invalid 5GHz WiFi channel: %d\n", channel);
135 exit(1);
136 }
137 if (!wifi_frequency_5) {
138 wifi_frequency_5 = g_hash_table_new(g_direct_hash, g_direct_equal);
139 /* Initialize 5GHz frequencies, as of:
140 * https://en.wikipedia.org/wiki/List_of_WLAN_channels#5.0_GHz_(802.11j)_WLAN
141 * Skipping channels 183-196. They are valid only in Japan with registration needed */
142 for (unsigned i = 0; i < sizeof(channels) / sizeof(int); i++) {
143 g_hash_table_insert(wifi_frequency_5, GINT_TO_POINTER(channels[i]),
144 GINT_TO_POINTER(5000+channels[i]*5));
145 }
146 }
147 return GPOINTER_TO_INT(g_hash_table_lookup(wifi_frequency_5,
148 GINT_TO_POINTER(channel)));
149 }
1616
1717 #pragma once
1818
19 GHashTable* wifi_frequency_24;
20 GHashTable* wifi_frequency_5;
21
1922 void safe_mkdir_p_dir(const char* file_path);
2023 void g_string_free_to_file(GString* s, const char* rootdir, const char* path, const char* suffix);
2124 void unlink_glob(const char* rootdir, const char* _glob);
25
26 int wifi_get_freq24(int channel);
27 int wifi_get_freq5(int channel);
1818 #include <glib/gstdio.h>
1919 #include <gio/gio.h>
2020 #include <arpa/inet.h>
21 #include <regex.h>
2122
2223 #include <yaml.h>
2324
5960 * Validation for grammar and backend rules.
6061 ************************************************/
6162 static gboolean
62 validate_tunnel_grammar(net_definition* nd, yaml_node_t* node, GError** error)
63 {
64 if (nd->tunnel.mode == TUNNEL_MODE_UNKNOWN)
63 validate_tunnel_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** error)
64 {
65 if (nd->tunnel.mode == NETPLAN_TUNNEL_MODE_UNKNOWN)
6566 return yaml_error(node, error, "%s: missing 'mode' property for tunnel", nd->id);
6667
6768 /* Validate local/remote IPs */
7172 return yaml_error(node, error, "%s: missing 'remote' property for tunnel", nd->id);
7273
7374 switch(nd->tunnel.mode) {
74 case TUNNEL_MODE_IPIP6:
75 case TUNNEL_MODE_IP6IP6:
76 case TUNNEL_MODE_IP6GRE:
77 case TUNNEL_MODE_IP6GRETAP:
78 case TUNNEL_MODE_VTI6:
75 case NETPLAN_TUNNEL_MODE_IPIP6:
76 case NETPLAN_TUNNEL_MODE_IP6IP6:
77 case NETPLAN_TUNNEL_MODE_IP6GRE:
78 case NETPLAN_TUNNEL_MODE_IP6GRETAP:
79 case NETPLAN_TUNNEL_MODE_VTI6:
7980 if (!is_ip6_address(nd->tunnel.local_ip))
8081 return yaml_error(node, error, "%s: 'local' must be a valid IPv6 address for this tunnel type", nd->id);
8182 if (!is_ip6_address(nd->tunnel.remote_ip))
9495 }
9596
9697 static gboolean
97 validate_tunnel_backend_rules(net_definition* nd, yaml_node_t* node, GError** error)
98 validate_tunnel_backend_rules(NetplanNetDefinition* nd, yaml_node_t* node, GError** error)
9899 {
99100 /* Backend-specific validation rules for tunnels */
100101 switch (nd->backend) {
101 case BACKEND_NETWORKD:
102 case NETPLAN_BACKEND_NETWORKD:
102103 switch (nd->tunnel.mode) {
103 case TUNNEL_MODE_VTI:
104 case TUNNEL_MODE_VTI6:
104 case NETPLAN_TUNNEL_MODE_VTI:
105 case NETPLAN_TUNNEL_MODE_VTI6:
105106 break;
106107
107108 /* TODO: Remove this exception and fix ISATAP handling with the
108109 * networkd backend.
109110 * systemd-networkd has grown ISATAP support in 918049a.
110111 */
111 case TUNNEL_MODE_ISATAP:
112 case NETPLAN_TUNNEL_MODE_ISATAP:
112113 return yaml_error(node, error,
113114 "%s: %s tunnel mode is not supported by networkd",
114115 nd->id,
124125 }
125126 break;
126127
127 case BACKEND_NM:
128 case NETPLAN_BACKEND_NM:
128129 switch (nd->tunnel.mode) {
129 case TUNNEL_MODE_GRE:
130 case TUNNEL_MODE_IP6GRE:
131 break;
132
133 case TUNNEL_MODE_GRETAP:
134 case TUNNEL_MODE_IP6GRETAP:
130 case NETPLAN_TUNNEL_MODE_GRE:
131 case NETPLAN_TUNNEL_MODE_IP6GRE:
132 break;
133
134 case NETPLAN_TUNNEL_MODE_GRETAP:
135 case NETPLAN_TUNNEL_MODE_IP6GRETAP:
135136 return yaml_error(node, error,
136137 "%s: %s tunnel mode is not supported by NetworkManager",
137138 nd->id,
157158 }
158159
159160 gboolean
160 validate_netdef_grammar(net_definition* nd, yaml_node_t* node, GError** error)
161 validate_netdef_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** error)
161162 {
162163 int missing_id_count = g_hash_table_size(missing_id);
163164 gboolean valid = FALSE;
164165
165 g_assert(nd->type != ND_NONE);
166 g_assert(nd->type != NETPLAN_DEF_TYPE_NONE);
166167
167168 /* Skip all validation if we're missing some definition IDs (devices).
168169 * The ones we have yet to see may be necessary for validation to succeed,
174175 if (nd->set_name && !nd->has_match)
175176 return yaml_error(node, error, "%s: 'set-name:' requires 'match:' properties", nd->id);
176177
177 if (nd->type == ND_WIFI && nd->access_points == NULL)
178 if (nd->type == NETPLAN_DEF_TYPE_WIFI && nd->access_points == NULL)
178179 return yaml_error(node, error, "%s: No access points defined", nd->id);
179180
180 if (nd->type == ND_VLAN) {
181 if (nd->type == NETPLAN_DEF_TYPE_VLAN) {
181182 if (!nd->vlan_link)
182183 return yaml_error(node, error, "%s: missing 'link' property", nd->id);
183184 nd->vlan_link->has_vlans = TRUE;
187188 return yaml_error(node, error, "%s: invalid id '%u' (allowed values are 0 to 4094)", nd->id, nd->vlan_id);
188189 }
189190
190 if (nd->type == ND_TUNNEL) {
191 if (nd->type == NETPLAN_DEF_TYPE_TUNNEL) {
191192 valid = validate_tunnel_grammar(nd, node, error);
192193 if (!valid)
193194 goto netdef_grammar_error;
200201 }
201202
202203 gboolean
203 validate_backend_rules(net_definition* nd, GError** error)
204 validate_backend_rules(NetplanNetDefinition* nd, GError** error)
204205 {
205206 gboolean valid = FALSE;
206207 /* Set a dummy, NULL yaml_node_t for error reporting */
207208 yaml_node_t* node = NULL;
208209
209 g_assert(nd->type != ND_NONE);
210
211 if (nd->type == ND_TUNNEL) {
210 g_assert(nd->type != NETPLAN_DEF_TYPE_NONE);
211
212 if (nd->type == NETPLAN_DEF_TYPE_TUNNEL) {
212213 valid = validate_tunnel_backend_rules(nd, node, error);
213214 if (!valid)
214215 goto backend_rules_error;
2323 gboolean is_ip6_address(const char* address);
2424
2525 gboolean
26 validate_netdef_grammar(net_definition* nd, yaml_node_t* node, GError** error);
26 validate_netdef_grammar(NetplanNetDefinition* nd, yaml_node_t* node, GError** error);
2727
2828 gboolean
29 validate_backend_rules(net_definition* nd, GError** error);
29 validate_backend_rules(NetplanNetDefinition* nd, GError** error);
3232
3333 # Make sure we can import our development netplan.
3434 os.environ.update({'PYTHONPATH': '.'})
35 os.environ.update({'LD_LIBRARY_PATH': '.:{}'.format(os.environ.get('LD_LIBRARY_PATH'))})
36
37
38 def _load_yaml(text):
39 return yaml.load(text, Loader=yaml.SafeLoader)
3540
3641
3742 class TestArgs(unittest.TestCase):
6267 self.workdir = tempfile.TemporaryDirectory()
6368
6469 def test_no_config(self):
65 out = subprocess.check_output(exe_cli + ['generate', '--root-dir', self.workdir.name])
70 p = subprocess.Popen(exe_cli + ['generate', '--root-dir', self.workdir.name], stdout=subprocess.PIPE,
71 stderr=subprocess.PIPE)
72 (out, err) = p.communicate()
6673 self.assertEqual(out, b'')
6774 self.assertEqual(os.listdir(self.workdir.name), [])
6875
144151
145152
146153 class TestIfupdownMigrate(unittest.TestCase):
154
147155 def setUp(self):
148156 self.workdir = tempfile.TemporaryDirectory()
149157 self.ifaces_path = os.path.join(self.workdir.name, 'etc/network/interfaces')
202210
203211 def test_dhcp4(self):
204212 out = self.do_test('auto en1\niface en1 inet dhcp')[0]
205 self.assertEqual(yaml.load(out), {'network': {
213 self.assertEqual(_load_yaml(out), {'network': {
206214 'version': 2,
207215 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
208216
209217 def test_dhcp6(self):
210218 out = self.do_test('auto en1\niface en1 inet6 dhcp')[0]
211 self.assertEqual(yaml.load(out), {'network': {
219 self.assertEqual(_load_yaml(out), {'network': {
212220 'version': 2,
213221 'ethernets': {'en1': {'dhcp6': True}}}}, out.decode())
214222
215223 def test_dhcp4_and_6(self):
216224 out = self.do_test('auto lo\niface lo inet loopback\n\n'
217225 'auto en1\niface en1 inet dhcp\niface en1 inet6 dhcp')[0]
218 self.assertEqual(yaml.load(out), {'network': {
226 self.assertEqual(_load_yaml(out), {'network': {
219227 'version': 2,
220228 'ethernets': {'en1': {'dhcp4': True, 'dhcp6': True}}}}, out.decode())
221229
223231 out = self.do_test('iface lo inet loopback\nauto lo\nsource-directory interfaces.d',
224232 dropins={'interfaces.d/std': 'auto en1\niface en1 inet dhcp',
225233 'interfaces.d/std.bak': 'some_bogus dontreadme'})[0]
226 self.assertEqual(yaml.load(out), {'network': {
234 self.assertEqual(_load_yaml(out), {'network': {
227235 'version': 2,
228236 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
229237
231239 out = self.do_test('iface lo inet loopback\nauto lo\nsource-directory /etc/network/defs/my',
232240 dropins={'defs/my/std': 'auto en1\niface en1 inet dhcp',
233241 'defs/my/std.bak': 'some_bogus dontreadme'})[0]
234 self.assertEqual(yaml.load(out), {'network': {
242 self.assertEqual(_load_yaml(out), {'network': {
235243 'version': 2,
236244 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
237245
239247 out = self.do_test('iface lo inet loopback\nauto lo\nsource interfaces.d/*.cfg',
240248 dropins={'interfaces.d/std.cfg': 'auto en1\niface en1 inet dhcp',
241249 'interfaces.d/std.cfgold': 'some_bogus dontreadme'})[0]
242 self.assertEqual(yaml.load(out), {'network': {
250 self.assertEqual(_load_yaml(out), {'network': {
243251 'version': 2,
244252 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
245253
247255 out = self.do_test('iface lo inet loopback\nauto lo\nsource /etc/network/*.cfg',
248256 dropins={'std.cfg': 'auto en1\niface en1 inet dhcp',
249257 'std.cfgold': 'some_bogus dontreadme'})[0]
250 self.assertEqual(yaml.load(out), {'network': {
258 self.assertEqual(_load_yaml(out), {'network': {
251259 'version': 2,
252260 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
253261
254262 def test_allow(self):
255263 out = self.do_test('allow-hotplug en1\niface en1 inet dhcp\n'
256264 'allow-auto en2\niface en2 inet dhcp')[0]
257 self.assertEqual(yaml.load(out), {'network': {
265 self.assertEqual(_load_yaml(out), {'network': {
258266 'version': 2,
259267 'ethernets': {'en1': {'dhcp4': True},
260268 'en2': {'dhcp4': True}}}}, out.decode())
261269
262270 def test_no_scripts(self):
263271 out = self.do_test('auto en1\niface en1 inet dhcp\nno-scripts en1')[0]
264 self.assertEqual(yaml.load(out), {'network': {
272 self.assertEqual(_load_yaml(out), {'network': {
265273 'version': 2,
266274 'ethernets': {'en1': {'dhcp4': True}}}}, out.decode())
267275
275283 def test_write_file_haveconfig(self):
276284 (out, err) = self.do_test('auto en1\niface en1 inet dhcp', dry_run=False)
277285 with open(self.converted_path) as f:
278 config = yaml.load(f)
286 config = _load_yaml(f)
279287 self.assertEqual(config, {'network': {
280288 'version': 2,
281289 'ethernets': {'en1': {'dhcp4': True}}}})
301309
302310 def test_static_ipv4_prefix(self):
303311 out = self.do_test('auto en1\niface en1 inet static\naddress 1.2.3.4/8', dry_run=True)[0]
304 self.assertEqual(yaml.load(out), {'network': {
312 self.assertEqual(_load_yaml(out), {'network': {
305313 'version': 2,
306314 'ethernets': {'en1': {'addresses': ["1.2.3.4/8"]}}}}, out.decode())
307315
308316 def test_static_ipv4_netmask(self):
309317 out = self.do_test('auto en1\niface en1 inet static\naddress 1.2.3.4\nnetmask 255.0.0.0', dry_run=True)[0]
310 self.assertEqual(yaml.load(out), {'network': {
318 self.assertEqual(_load_yaml(out), {'network': {
311319 'version': 2,
312320 'ethernets': {'en1': {'addresses': ["1.2.3.4/8"]}}}}, out.decode())
313321
348356
349357 def test_static_ipv6_prefix(self):
350358 out = self.do_test('auto en1\niface en1 inet6 static\naddress fc00:0123:4567:89ab:cdef::1234/64', dry_run=True)[0]
351 self.assertEqual(yaml.load(out), {'network': {
359 self.assertEqual(_load_yaml(out), {'network': {
352360 'version': 2,
353361 'ethernets': {'en1': {'addresses': ["fc00:123:4567:89ab:cdef::1234/64"]}}}}, out.decode())
354362
355363 def test_static_ipv6_netmask(self):
356364 out = self.do_test('auto en1\niface en1 inet6 static\n'
357365 'address fc00:0123:4567:89ab:cdef::1234\nnetmask 64', dry_run=True)[0]
358 self.assertEqual(yaml.load(out), {'network': {
366 self.assertEqual(_load_yaml(out), {'network': {
359367 'version': 2,
360368 'ethernets': {'en1': {'addresses': ["fc00:123:4567:89ab:cdef::1234/64"]}}}}, out.decode())
361369
403411 def test_static_ipv6_accept_ra_0(self):
404412 out = self.do_test('auto en1\niface en1 inet6 static\n'
405413 'address fc00:0123:4567:89ab:cdef::1234/64\naccept_ra 0', dry_run=True)[0]
406 self.assertEqual(yaml.load(out), {'network': {
414 self.assertEqual(_load_yaml(out), {'network': {
407415 'version': 2,
408416 'ethernets': {'en1': {'addresses': ["fc00:123:4567:89ab:cdef::1234/64"],
409417 'accept_ra': False}}}}, out.decode())
411419 def test_static_ipv6_accept_ra_1(self):
412420 out = self.do_test('auto en1\niface en1 inet6 static\n'
413421 'address fc00:0123:4567:89ab:cdef::1234/64\naccept_ra 1', dry_run=True)[0]
414 self.assertEqual(yaml.load(out), {'network': {
422 self.assertEqual(_load_yaml(out), {'network': {
415423 'version': 2,
416424 'ethernets': {'en1': {'addresses': ["fc00:123:4567:89ab:cdef::1234/64"],
417425 'accept_ra': True}}}}, out.decode())
437445 iface en1 inet6 static
438446 address fc00:0123:4567:89ab:cdef::1234/64
439447 gateway fc00:0123:4567:89ab::1""", dry_run=True)[0]
440 self.assertEqual(yaml.load(out), {'network': {
448 self.assertEqual(_load_yaml(out), {'network': {
441449 'version': 2,
442450 'ethernets': {'en1':
443451 {'addresses': ["1.2.3.4/8", "fc00:123:4567:89ab:cdef::1234/64"],
454462 iface en1 inet6 static
455463 address fc00:0123:4567:89ab:cdef::1234/64
456464 dns-nameservers fc00:0123:4567:89ab:1::1 fc00:0123:4567:89ab:2::1""", dry_run=True)[0]
457 self.assertEqual(yaml.load(out), {'network': {
465 self.assertEqual(_load_yaml(out), {'network': {
458466 'version': 2,
459467 'ethernets': {'en1':
460468 {'addresses': ["1.2.3.4/8", "fc00:123:4567:89ab:cdef::1234/64"],
466474
467475 def test_static_dns2(self):
468476 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]
469 self.assertEqual(yaml.load(out), {'network': {
477 self.assertEqual(_load_yaml(out), {'network': {
470478 'version': 2,
471479 'ethernets': {'en1': {'addresses': ["1.2.3.4/8"],
472480 'nameservers': {
475483
476484 def test_static_mtu(self):
477485 out = self.do_test('auto en1\niface en1 inet static\naddress 1.2.3.4/8\nmtu 1280', dry_run=True)[0]
478 self.assertEqual(yaml.load(out), {'network': {
486 self.assertEqual(_load_yaml(out), {'network': {
479487 'version': 2,
480488 'ethernets': {'en1': {'addresses': ["1.2.3.4/8"],
481489 'mtu': 1280}}}}, out.decode())
493501
494502 def test_static_hwaddress(self):
495503 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]
496 self.assertEqual(yaml.load(out), {'network': {
504 self.assertEqual(_load_yaml(out), {'network': {
497505 'version': 2,
498506 'ethernets': {'en1': {'addresses': ["1.2.3.4/8"],
499507 'macaddress': '52:54:00:6b:3c:59'}}}}, out.decode())
2727
2828 exe_generate = os.path.join(os.path.dirname(os.path.dirname(
2929 os.path.dirname(os.path.abspath(__file__)))), 'generate')
30
31 # make sure we point to libnetplan properly.
32 os.environ.update({'LD_LIBRARY_PATH': '.:{}'.format(os.environ.get('LD_LIBRARY_PATH'))})
3033
3134 # make sure we fail on criticals
3235 os.environ['G_DEBUG'] = 'fatal-criticals'
3030 wl0:
3131 access-points:
3232 "Joe's Home":
33 password: "s3kr1t"
33 password: "s0s3kr1t"
3434 "Luke's Home":
3535 auth:
3636 key-management: psk
3737 password: "4lsos3kr1t"
38 "BobsHome":
39 password: "e03ce667c87bc81ca968d9120ca37f89eb09aec3c55b80386e5d772efd6b926e"
40 "BillsHome":
41 auth:
42 key-management: psk
43 password: "db3b0acf5653aeaddd5fe034fb9f07175b2864f847b005aaa2f09182d9411b04"
3844 workplace:
3945 auth:
4046 key-management: eap
97103 ssid="Luke's Home"
98104 key_mgmt=WPA-PSK
99105 psk="4lsos3kr1t"
106 }
107 ''', new_config)
108 self.assertIn('''
109 network={
110 ssid="BobsHome"
111 key_mgmt=WPA-PSK
112 psk=e03ce667c87bc81ca968d9120ca37f89eb09aec3c55b80386e5d772efd6b926e
113 }
114 ''', new_config)
115 self.assertIn('''
116 network={
117 ssid="BillsHome"
118 key_mgmt=WPA-PSK
119 psk=db3b0acf5653aeaddd5fe034fb9f07175b2864f847b005aaa2f09182d9411b04
100120 }
101121 ''', new_config)
102122 self.assertIn('''
152172 network={
153173 ssid="Joe's Home"
154174 key_mgmt=WPA-PSK
155 psk="s3kr1t"
175 psk="s0s3kr1t"
156176 }
157177 ''', new_config)
158178 self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o600)
179 self.assertTrue(os.path.isfile(os.path.join(
180 self.workdir.name, 'run/systemd/system/netplan-wpa-wl0.service')))
159181 self.assertTrue(os.path.islink(os.path.join(
160 self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa@wl0.service')))
182 self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl0.service')))
161183
162184 def test_auth_wired(self):
163185 self.generate('''network:
173195 client-certificate: /etc/ssl/cust-crt.pem
174196 client-key: /etc/ssl/cust-key.pem
175197 client-key-password: "d3cryptPr1v4t3K3y"
198 phase2-auth: MSCHAPV2
176199 dhcp4: yes
177200 ''')
178201
195218 client_cert="/etc/ssl/cust-crt.pem"
196219 private_key="/etc/ssl/cust-key.pem"
197220 private_key_passwd="d3cryptPr1v4t3K3y"
221 phase2="auth=MSCHAPV2"
198222 }
199223 ''')
200224 self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o600)
225 self.assertTrue(os.path.isfile(os.path.join(
226 self.workdir.name, 'run/systemd/system/netplan-wpa-eth0.service')))
201227 self.assertTrue(os.path.islink(os.path.join(
202 self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa@eth0.service')))
228 self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-eth0.service')))
203229
204230
205231 class TestNetworkManager(TestBase):
212238 wl0:
213239 access-points:
214240 "Joe's Home":
215 password: "s3kr1t"
241 password: "s0s3kr1t"
216242 "Luke's Home":
217243 auth:
218244 key-management: psk
248274 client-certificate: /etc/ssl/cust-crt.pem
249275 client-key: /etc/ssl/cust-key.pem
250276 client-key-password: "d3cryptPr1v4t3K3y"
277 phase2-auth: MSCHAPV2
251278 opennet:
252279 auth:
253280 key-management: none
278305
279306 [wifi-security]
280307 key-mgmt=wpa-psk
281 psk=s3kr1t
308 psk=s0s3kr1t
282309 ''',
283310 'wl0-Luke%27s%20Home': '''[connection]
284311 id=netplan-wl0-Luke's Home
412439 client-cert=/etc/ssl/cust-crt.pem
413440 private-key=/etc/ssl/cust-key.pem
414441 private-key-password=d3cryptPr1v4t3K3y
442 phase2-auth=MSCHAPV2
415443 ''',
416444 'wl0-opennet': '''[connection]
417445 id=netplan-wl0-opennet
515543 auth:
516544 method: bogus''', expect_fail=True)
517545 self.assertIn("unknown EAP method 'bogus'", err)
546
547 def test_auth_networkd_wifi_psk_too_big(self):
548 err = self.generate('''network:
549 version: 2
550 wifis:
551 wl0:
552 access-points:
553 "Joe's Home":
554 password: "LoremipsumdolorsitametconsecteturadipiscingelitCrastemporvelitnunc"
555 dhcp4: yes''', expect_fail=True)
556 self.assertIn("ASCII passphrase must be between 8 and 63 characters (inclusive)", err)
557
558 def test_auth_networkd_wifi_psk_too_small(self):
559 err = self.generate('''network:
560 version: 2
561 wifis:
562 wl0:
563 access-points:
564 "Joe's Home":
565 password: "p4ss"
566 dhcp4: yes''', expect_fail=True)
567 self.assertIn("ASCII passphrase must be between 8 and 63 characters (inclusive)", err)
568
569 def test_auth_networkd_wifi_psk_64_non_hexdigit(self):
570 err = self.generate('''network:
571 version: 2
572 wifis:
573 wl0:
574 access-points:
575 "Joe's Home":
576 password: "LoremipsumdolorsitametconsecteturadipiscingelitCrastemporvelitnu"
577 dhcp4: yes''', expect_fail=True)
578 self.assertIn("PSK length of 64 is only supported for hex-digit representation", err)
103103 'bond0.network': '''[Match]
104104 Name=bond0
105105
106 [Link]
107 MTUBytes=9000
108
106109 [Network]
107110 LinkLocalAddressing=ipv6
108111 ConfigureWithoutCarrier=yes
109112 VLAN=bond0.108
110113 ''',
111114 'eth1.link': '[Match]\nOriginalName=eth1\n\n[Link]\nWakeOnLan=off\nMTUBytes=9000\n',
112 'eth1.network': '[Match]\nName=eth1\n\n[Network]\nLinkLocalAddressing=no\nIPv6MTUBytes=2000\nBond=bond0\n'
115 'eth1.network': '''[Match]
116 Name=eth1
117
118 [Link]
119 MTUBytes=9000
120
121 [Network]
122 LinkLocalAddressing=no
123 IPv6MTUBytes=2000
124 Bond=bond0
125 '''
113126 })
114127 self.assert_networkd_udev(None)
115128
282295 version: 2
283296 ethernets:
284297 engreen:
285 dhcp4: yes
286298 critical: yes
287299 ''')
288300
290302 Name=engreen
291303
292304 [Network]
293 DHCP=ipv4
294305 LinkLocalAddressing=ipv6
295306
296307 [DHCP]
297308 CriticalConnection=true
298 RouteMetric=100
299 UseMTU=true
300309 '''})
301310
302311 def test_dhcp_identifier_mac(self):
714723 method=auto
715724 '''})
716725
726 def test_ip6_addr_gen_mode(self):
727 self.generate('''network:
728 version: 2
729 renderer: NetworkManager
730 ethernets:
731 engreen:
732 dhcp6: yes
733 ipv6-address-generation: stable-privacy
734 enblue:
735 dhcp6: yes
736 ipv6-address-generation: eui64''')
737 self.assert_nm({'engreen': '''[connection]
738 id=netplan-engreen
739 type=ethernet
740 interface-name=engreen
741
742 [ethernet]
743 wake-on-lan=0
744
745 [ipv4]
746 method=link-local
747
748 [ipv6]
749 method=auto
750 addr-gen-mode=1
751 ''',
752 'enblue': '''[connection]
753 id=netplan-enblue
754 type=ethernet
755 interface-name=enblue
756
757 [ethernet]
758 wake-on-lan=0
759
760 [ipv4]
761 method=link-local
762
763 [ipv6]
764 method=auto
765 addr-gen-mode=0
766 '''})
767
717768 def test_eth_manual_addresses(self):
718769 self.generate('''network:
719770 version: 2
209209 mode: bogus''', expect_fail=True)
210210 self.assertIn("unknown wifi mode 'bogus'", err)
211211
212 def test_wifi_ap_unknown_band(self):
213 err = self.generate('''network:
214 version: 2
215 wifis:
216 wl0:
217 access-points:
218 workplace:
219 band: bogus''', expect_fail=True)
220 self.assertIn("unknown wifi band 'bogus'", err)
221
222 def test_wifi_ap_invalid_freq24(self):
223 err = self.generate('''network:
224 version: 2
225 renderer: NetworkManager
226 wifis:
227 wl0:
228 access-points:
229 workplace:
230 band: 2.4GHz
231 channel: 15''', expect_fail=True)
232 self.assertIn("ERROR: invalid 2.4GHz WiFi channel: 15", err)
233
234 def test_wifi_ap_invalid_freq5(self):
235 err = self.generate('''network:
236 version: 2
237 wifis:
238 wl0:
239 access-points:
240 workplace:
241 band: 5GHz
242 channel: 14''', expect_fail=True)
243 self.assertIn("ERROR: invalid 5GHz WiFi channel: 14", err)
244
212245 def test_invalid_ipv4_address(self):
213246 err = self.generate('''network:
214247 version: 2
286319 addresses:
287320 - 2001::1/''', expect_fail=True)
288321 self.assertIn("invalid prefix length in address '2001::1/'", err)
322
323 def test_invalid_addr_gen_mode(self):
324 err = self.generate('''network:
325 version: 2
326 renderer: NetworkManager
327 ethernets:
328 engreen:
329 ipv6-address-generation: 0''', expect_fail=True)
330 self.assertIn("unknown ipv6-address-generation '0'", err)
331
332 def test_addr_gen_mode_not_supported(self):
333 err = self.generate('''network:
334 version: 2
335 ethernets:
336 engreen:
337 ipv6-address-generation: eui64''', expect_fail=True)
338 self.assertIn("ERROR: engreen: ipv6-address-generation is not supported by networkd", err)
289339
290340 def test_invalid_gateway4(self):
291341 for a in ['300.400.1.1', '1.2.3', '192.168.14.1/24']:
362412 ena: {id: 1, link: en1}''', expect_fail=True)
363413 self.assertIn("ena: interface 'en1' is not defined", err)
364414
415 def test_vlan_unknown_renderer(self):
416 err = self.generate('''network:
417 version: 2
418 ethernets: {en1: {}}
419 vlans:
420 ena: {id: 1, link: en1, renderer: foo}''', expect_fail=True)
421 self.assertIn("unknown renderer 'foo'", err)
422
365423 def test_device_bad_route_to(self):
366424 self.generate('''network:
367425 version: 2
4545 # should not allow NM to manage everything
4646 self.assertFalse(os.path.exists(self.nm_enable_all_conf))
4747
48 def test_eth_lldp(self):
49 self.generate('''network:
50 version: 2
51 ethernets:
52 eth0:
53 dhcp4: n
54 emit-lldp: true''')
55
56 self.assert_networkd({'eth0.network': '''[Match]
57 Name=eth0
58
59 [Network]
60 EmitLLDP=true
61 LinkLocalAddressing=ipv6
62 '''})
63
4864 def test_eth_mtu(self):
4965 self.generate('''network:
5066 version: 2
5672 self.assert_networkd({'eth1.link': '[Match]\nOriginalName=eth1\n\n[Link]\nWakeOnLan=off\nMTUBytes=1280\n',
5773 'eth1.network': '''[Match]
5874 Name=eth1
75
76 [Link]
77 MTUBytes=1280
78
79 [Network]
80 LinkLocalAddressing=ipv6
81 '''})
82 self.assert_networkd_udev(None)
83
84 def test_eth_sriov_vlan_filterv_link(self):
85 self.generate('''network:
86 version: 2
87 ethernets:
88 enp1:
89 dhcp4: n
90 enp1s16f1:
91 dhcp4: n
92 link: enp1''')
93
94 self.assert_networkd({'enp1.network': '''[Match]
95 Name=enp1
96
97 [Network]
98 LinkLocalAddressing=ipv6
99 ''',
100 'enp1s16f1.network': '''[Match]
101 Name=enp1s16f1
102
103 [Network]
104 LinkLocalAddressing=ipv6
105 '''})
106 self.assert_networkd_udev(None)
107
108 def test_eth_sriov_virtual_functions(self):
109 self.generate('''network:
110 version: 2
111 ethernets:
112 enp1:
113 virtual-function-count: 8''')
114
115 self.assert_networkd({'enp1.network': '''[Match]
116 Name=enp1
59117
60118 [Network]
61119 LinkLocalAddressing=ipv6
306364 method=ignore
307365 '''})
308366
367 def test_eth_sriov_link(self):
368 self.generate('''network:
369 version: 2
370 renderer: NetworkManager
371 ethernets:
372 enp1:
373 dhcp4: n
374 enp1s16f1:
375 dhcp4: n
376 link: enp1''')
377
378 self.assert_networkd({})
379 self.assert_nm({'enp1': '''[connection]
380 id=netplan-enp1
381 type=ethernet
382 interface-name=enp1
383
384 [ethernet]
385 wake-on-lan=0
386
387 [ipv4]
388 method=link-local
389
390 [ipv6]
391 method=ignore
392 ''',
393 'enp1s16f1': '''[connection]
394 id=netplan-enp1s16f1
395 type=ethernet
396 interface-name=enp1s16f1
397
398 [ethernet]
399 wake-on-lan=0
400
401 [ipv4]
402 method=link-local
403
404 [ipv6]
405 method=ignore
406 '''})
407
408 def test_eth_sriov_virtual_functions(self):
409 self.generate('''network:
410 version: 2
411 renderer: NetworkManager
412 ethernets:
413 enp1:
414 dhcp4: n
415 virtual-function-count: 8''')
416
417 self.assert_networkd({})
418 self.assert_nm({'enp1': '''[connection]
419 id=netplan-enp1
420 type=ethernet
421 interface-name=enp1
422
423 [ethernet]
424 wake-on-lan=0
425
426 [ipv4]
427 method=link-local
428
429 [ipv6]
430 method=ignore
431 '''})
432
309433 def test_eth_set_mac(self):
310434 self.generate('''network:
311435 version: 2
0 #
1 # Tests for gsm/cdma modem devices config generated via netplan
2 #
3 # Copyright (C) 2020 Canonical, Ltd.
4 # Author: Lukas Märdian <lukas.maerdian@canonical.com>
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; version 3.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 from .base import TestBase
19
20
21 class TestNetworkd(TestBase):
22 '''networkd output'''
23
24 def test_not_supported(self):
25 # does not produce any output, but fails with:
26 # "networkd backend does not support GSM modem configuration"
27 err = self.generate('''network:
28 version: 2
29 modems:
30 mobilephone:
31 auto-config: true''', expect_fail=True)
32 self.assertIn("ERROR: mobilephone: networkd backend does not support GSM/CDMA modem configuration", err)
33
34 self.assert_networkd({})
35 self.assert_nm({})
36
37
38 class TestNetworkManager(TestBase):
39 '''networkmanager output'''
40
41 def test_cdma_config(self):
42 self.generate('''network:
43 version: 2
44 renderer: NetworkManager
45 modems:
46 mobilephone:
47 mtu: 0
48 number: "#666"
49 username: test-user
50 password: s0s3kr1t''')
51 self.assert_nm({'mobilephone': '''[connection]
52 id=netplan-mobilephone
53 type=cdma
54 interface-name=mobilephone
55
56 [cdma]
57 password=s0s3kr1t
58 username=test-user
59 number=#666
60
61 [ethernet]
62 wake-on-lan=0
63
64 [ipv4]
65 method=link-local
66
67 [ipv6]
68 method=ignore
69 '''})
70 self.assert_networkd({})
71 self.assert_nm_udev(None)
72
73 def test_gsm_auto_config(self):
74 self.generate('''network:
75 version: 2
76 renderer: NetworkManager
77 modems:
78 mobilephone:
79 auto-config: true''')
80 self.assert_nm({'mobilephone': '''[connection]
81 id=netplan-mobilephone
82 type=gsm
83 interface-name=mobilephone
84
85 [gsm]
86 auto-config=true
87
88 [ethernet]
89 wake-on-lan=0
90
91 [ipv4]
92 method=link-local
93
94 [ipv6]
95 method=ignore
96 '''})
97 self.assert_networkd({})
98 self.assert_nm_udev(None)
99
100 def test_gsm_auto_config_implicit(self):
101 self.generate('''network:
102 version: 2
103 renderer: NetworkManager
104 modems:
105 mobilephone:
106 number: "*99#"
107 mtu: 1600
108 pin: "1234"''')
109 self.assert_nm({'mobilephone': '''[connection]
110 id=netplan-mobilephone
111 type=gsm
112 interface-name=mobilephone
113
114 [gsm]
115 auto-config=true
116 mtu=1600
117 number=*99#
118 pin=1234
119
120 [ethernet]
121 wake-on-lan=0
122
123 [ipv4]
124 method=link-local
125
126 [ipv6]
127 method=ignore
128 '''})
129 self.assert_networkd({})
130 self.assert_nm_udev(None)
131
132 def test_gsm_apn(self):
133 self.generate('''network:
134 version: 2
135 renderer: NetworkManager
136 modems:
137 mobilephone:
138 apn: internet''')
139 self.assert_nm({'mobilephone': '''[connection]
140 id=netplan-mobilephone
141 type=gsm
142 interface-name=mobilephone
143
144 [gsm]
145 apn=internet
146
147 [ethernet]
148 wake-on-lan=0
149
150 [ipv4]
151 method=link-local
152
153 [ipv6]
154 method=ignore
155 '''})
156 self.assert_networkd({})
157 self.assert_nm_udev(None)
158
159 def test_gsm_apn_username_password(self):
160 self.generate('''network:
161 version: 2
162 renderer: NetworkManager
163 modems:
164 mobilephone:
165 apn: internet
166 username: some-user
167 password: some-pass''')
168 self.assert_nm({'mobilephone': '''[connection]
169 id=netplan-mobilephone
170 type=gsm
171 interface-name=mobilephone
172
173 [gsm]
174 apn=internet
175 password=some-pass
176 username=some-user
177
178 [ethernet]
179 wake-on-lan=0
180
181 [ipv4]
182 method=link-local
183
184 [ipv6]
185 method=ignore
186 '''})
187 self.assert_networkd({})
188 self.assert_nm_udev(None)
189
190 def test_gsm_device_id(self):
191 self.generate('''network:
192 version: 2
193 renderer: NetworkManager
194 modems:
195 mobilephone:
196 device-id: test''')
197 self.assert_nm({'mobilephone': '''[connection]
198 id=netplan-mobilephone
199 type=gsm
200 interface-name=mobilephone
201
202 [gsm]
203 auto-config=true
204 device-id=test
205
206 [ethernet]
207 wake-on-lan=0
208
209 [ipv4]
210 method=link-local
211
212 [ipv6]
213 method=ignore
214 '''})
215 self.assert_networkd({})
216 self.assert_nm_udev(None)
217
218 def test_gsm_network_id(self):
219 self.generate('''network:
220 version: 2
221 renderer: NetworkManager
222 modems:
223 mobilephone:
224 network-id: test''')
225 self.assert_nm({'mobilephone': '''[connection]
226 id=netplan-mobilephone
227 type=gsm
228 interface-name=mobilephone
229
230 [gsm]
231 auto-config=true
232 network-id=test
233
234 [ethernet]
235 wake-on-lan=0
236
237 [ipv4]
238 method=link-local
239
240 [ipv6]
241 method=ignore
242 '''})
243 self.assert_networkd({})
244 self.assert_nm_udev(None)
245
246 def test_gsm_pin(self):
247 self.generate('''network:
248 version: 2
249 renderer: NetworkManager
250 modems:
251 mobilephone:
252 pin: 1234''')
253 self.assert_nm({'mobilephone': '''[connection]
254 id=netplan-mobilephone
255 type=gsm
256 interface-name=mobilephone
257
258 [gsm]
259 auto-config=true
260 pin=1234
261
262 [ethernet]
263 wake-on-lan=0
264
265 [ipv4]
266 method=link-local
267
268 [ipv6]
269 method=ignore
270 '''})
271 self.assert_networkd({})
272 self.assert_nm_udev(None)
273
274 def test_gsm_sim_id(self):
275 self.generate('''network:
276 version: 2
277 renderer: NetworkManager
278 modems:
279 mobilephone:
280 sim-id: test''')
281 self.assert_nm({'mobilephone': '''[connection]
282 id=netplan-mobilephone
283 type=gsm
284 interface-name=mobilephone
285
286 [gsm]
287 auto-config=true
288 sim-id=test
289
290 [ethernet]
291 wake-on-lan=0
292
293 [ipv4]
294 method=link-local
295
296 [ipv6]
297 method=ignore
298 '''})
299 self.assert_networkd({})
300 self.assert_nm_udev(None)
301
302 def test_gsm_sim_operator_id(self):
303 self.generate('''network:
304 version: 2
305 renderer: NetworkManager
306 modems:
307 mobilephone:
308 sim-operator-id: test''')
309 self.assert_nm({'mobilephone': '''[connection]
310 id=netplan-mobilephone
311 type=gsm
312 interface-name=mobilephone
313
314 [gsm]
315 auto-config=true
316 sim-operator-id=test
317
318 [ethernet]
319 wake-on-lan=0
320
321 [ipv4]
322 method=link-local
323
324 [ipv6]
325 method=ignore
326 '''})
327 self.assert_networkd({})
328 self.assert_nm_udev(None)
329
330 def test_gsm_example(self):
331 self.generate('''network:
332 version: 2
333 renderer: NetworkManager
334 modems:
335 cdc-wdm1:
336 mtu: 1600
337 apn: ISP.CINGULAR
338 username: ISP@CINGULARGPRS.COM
339 password: CINGULAR1
340 number: "*99#"
341 network-id: 24005
342 device-id: da812de91eec16620b06cd0ca5cbc7ea25245222
343 pin: 2345
344 sim-id: 89148000000060671234
345 sim-operator-id: 310260''')
346 self.assert_nm({'cdc-wdm1': '''[connection]
347 id=netplan-cdc-wdm1
348 type=gsm
349 interface-name=cdc-wdm1
350
351 [gsm]
352 apn=ISP.CINGULAR
353 password=CINGULAR1
354 username=ISP@CINGULARGPRS.COM
355 device-id=da812de91eec16620b06cd0ca5cbc7ea25245222
356 mtu=1600
357 network-id=24005
358 number=*99#
359 pin=2345
360 sim-id=89148000000060671234
361 sim-operator-id=310260
362
363 [ethernet]
364 wake-on-lan=0
365
366 [ipv4]
367 method=link-local
368
369 [ipv6]
370 method=ignore
371 '''})
372 self.assert_networkd({})
373 self.assert_nm_udev(None)
104104 unmanaged-devices+=interface-name:en1,interface-name:enblue,interface-name:enred,interface-name:engreen,''')
105105 self.assert_nm_udev(None)
106106
107 def test_vlan_sriov(self):
108 # we need to make sure renderer: sriov vlans are not saved as part of
109 # the NM/networkd config
110 self.generate('''network:
111 version: 2
112 ethernets:
113 en1: {}
114 vlans:
115 enblue:
116 id: 1
117 link: en1
118 renderer: sriov
119 engreen: {id: 2, link: en1, dhcp6: true}''')
120
121 self.assert_networkd({'en1.network': '''[Match]
122 Name=en1
123
124 [Network]
125 LinkLocalAddressing=ipv6
126 VLAN=engreen
127 ''',
128 'engreen.netdev': '''[NetDev]
129 Name=engreen
130 Kind=vlan
131
132 [VLAN]
133 Id=2
134 ''',
135 'engreen.network': '''[Match]
136 Name=engreen
137
138 [Network]
139 DHCP=ipv6
140 LinkLocalAddressing=ipv6
141 ConfigureWithoutCarrier=yes
142
143 [DHCP]
144 RouteMetric=100
145 UseMTU=true
146 '''})
147 self.assert_nm(None, '''[keyfile]
148 # devices managed by networkd
149 unmanaged-devices+=interface-name:en1,interface-name:enblue,interface-name:engreen,''')
150 self.assert_nm_udev(None)
151
107152
108153 class TestNetworkManager(TestBase):
109154
220265 method=ignore
221266 ''' % uuid})
222267 self.assert_nm_udev(None)
268
269 def test_vlan_sriov(self):
270 # we need to make sure renderer: sriov vlans are not saved as part of
271 # the NM/networkd config
272 self.generate('''network:
273 version: 2
274 renderer: NetworkManager
275 ethernets:
276 en1: {}
277 vlans:
278 enblue:
279 id: 1
280 link: en1
281 addresses: [1.2.3.4/24]
282 renderer: sriov
283 engreen: {id: 2, link: en1, dhcp6: true}''')
284
285 self.assert_networkd({})
286 self.assert_nm({'en1': '''[connection]
287 id=netplan-en1
288 type=ethernet
289 interface-name=en1
290
291 [ethernet]
292 wake-on-lan=0
293
294 [ipv4]
295 method=link-local
296
297 [ipv6]
298 method=ignore
299 ''',
300 'engreen': '''[connection]
301 id=netplan-engreen
302 type=vlan
303 interface-name=engreen
304
305 [vlan]
306 id=2
307 parent=en1
308
309 [ipv4]
310 method=link-local
311
312 [ipv6]
313 method=auto
314 '''})
315 self.assert_nm_udev(None)
3030 wl0:
3131 access-points:
3232 "Joe's Home":
33 password: "s3kr1t"
33 password: "s0s3kr1t"
34 bssid: 00:11:22:33:44:55
35 band: 2.4GHz
36 channel: 11
3437 workplace:
35 password: "c0mpany"
38 password: "c0mpany1"
39 bssid: de:ad:be:ef:ca:fe
40 band: 5GHz
41 channel: 100
3642 peer2peer:
3743 mode: adhoc
44 channel-no-band:
45 channel: 7
46 band-no-channel:
47 band: 2.4G
48 band-no-channel2:
49 band: 5G
3850 dhcp4: yes''')
3951
4052 self.assert_networkd({'wl0.network': ND_WIFI_DHCP4 % 'wl0'})
4658 # generates wpa config and enables wpasupplicant unit
4759 with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f:
4860 new_config = f.read()
61
62 network = 'ssid="{}"\n freq_list='.format('band-no-channel2')
63 freqs_5GHz = [5610, 5310, 5620, 5320, 5630, 5640, 5340, 5035, 5040, 5045, 5055, 5060, 5660, 5680, 5670, 5080, 5690,
64 5700, 5710, 5720, 5825, 5745, 5755, 5805, 5765, 5160, 5775, 5170, 5480, 5180, 5795, 5190, 5500, 5200,
65 5510, 5210, 5520, 5220, 5530, 5230, 5540, 5240, 5550, 5250, 5560, 5260, 5570, 5270, 5580, 5280, 5590,
66 5290, 5600, 5300, 5865, 5845, 5785]
67 freqs = new_config.split(network)
68 freqs = freqs[1].split('\n')[0]
69 self.assertEqual(len(freqs.split(' ')), len(freqs_5GHz))
70 for freq in freqs_5GHz:
71 self.assertRegexpMatches(new_config, '{}[ 0-9]*{}[ 0-9]*\n'.format(network, freq))
72
73 network = 'ssid="{}"\n freq_list='.format('band-no-channel')
74 freqs_24GHz = [2412, 2417, 2422, 2427, 2432, 2442, 2447, 2437, 2452, 2457, 2462, 2467, 2472, 2484]
75 freqs = new_config.split(network)
76 freqs = freqs[1].split('\n')[0]
77 self.assertEqual(len(freqs.split(' ')), len(freqs_24GHz))
78 for freq in freqs_24GHz:
79 self.assertRegexpMatches(new_config, '{}[ 0-9]*{}[ 0-9]*\n'.format(network, freq))
80
81 self.assertIn('''
82 network={
83 ssid="channel-no-band"
84 key_mgmt=NONE
85 }
86 ''', new_config)
4987 self.assertIn('''
5088 network={
5189 ssid="peer2peer"
5694 self.assertIn('''
5795 network={
5896 ssid="workplace"
97 bssid=de:ad:be:ef:ca:fe
98 freq_list=5500
5999 key_mgmt=WPA-PSK
60 psk="c0mpany"
100 psk="c0mpany1"
61101 }
62102 ''', new_config)
63103 self.assertIn('''
64104 network={
65105 ssid="Joe's Home"
106 bssid=00:11:22:33:44:55
107 freq_list=2462
66108 key_mgmt=WPA-PSK
67 psk="s3kr1t"
109 psk="s0s3kr1t"
68110 }
69111 ''', new_config)
70112 self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o600)
113 self.assertTrue(os.path.isfile(os.path.join(
114 self.workdir.name, 'run/systemd/system/netplan-wpa-wl0.service')))
71115 self.assertTrue(os.path.islink(os.path.join(
72 self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa@wl0.service')))
116 self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl0.service')))
73117
74118 def test_wifi_route(self):
75119 self.generate('''network:
78122 wl0:
79123 access-points:
80124 workplace:
81 password: "c0mpany"
125 password: "c0mpany1"
82126 dhcp4: yes
83127 routes:
84128 - to: 10.10.10.0/24
114158 driver: foo
115159 access-points:
116160 workplace:
117 password: "c0mpany"
161 password: "c0mpany1"
118162 dhcp4: yes''', expect_fail=True)
119163 self.assertIn('networkd backend does not support wifi with match:', err)
120164
125169 wl0:
126170 access-points:
127171 workplace:
128 password: "c0mpany"
172 password: "c0mpany1"
129173 mode: ap
130174 dhcp4: yes''', expect_fail=True)
131175 self.assertIn('networkd does not support wifi in access point mode', err)
132176
177 def test_wifi_wowlan(self):
178 self.generate('''network:
179 version: 2
180 wifis:
181 wl0:
182 wakeonwlan:
183 - any
184 - disconnect
185 - magic_pkt
186 - gtk_rekey_failure
187 - eap_identity_req
188 - four_way_handshake
189 - rfkill_release
190 access-points:
191 homenet: {mode: infrastructure}''')
192
193 self.assert_networkd({'wl0.network': '''[Match]
194 Name=wl0
195
196 [Network]
197 LinkLocalAddressing=ipv6
198 '''})
199 self.assert_nm(None, '''[keyfile]
200 # devices managed by networkd
201 unmanaged-devices+=interface-name:wl0,''')
202 self.assert_nm_udev(None)
203
204 # generates wpa config and enables wpasupplicant unit
205 with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f:
206 new_config = f.read()
207 self.assertIn('''
208 wowlan_triggers=any disconnect magic_pkt gtk_rekey_failure eap_identity_req four_way_handshake rfkill_release
209 network={
210 ssid="homenet"
211 key_mgmt=NONE
212 }
213 ''', new_config)
214 self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o600)
215 self.assertTrue(os.path.isfile(os.path.join(
216 self.workdir.name, 'run/systemd/system/netplan-wpa-wl0.service')))
217 self.assertTrue(os.path.islink(os.path.join(
218 self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl0.service')))
219
220 def test_wifi_wowlan_default(self):
221 self.generate('''network:
222 version: 2
223 wifis:
224 wl0:
225 wakeonwlan: [default]
226 access-points:
227 homenet: {mode: infrastructure}''')
228
229 self.assert_networkd({'wl0.network': '''[Match]
230 Name=wl0
231
232 [Network]
233 LinkLocalAddressing=ipv6
234 '''})
235 self.assert_nm(None, '''[keyfile]
236 # devices managed by networkd
237 unmanaged-devices+=interface-name:wl0,''')
238 self.assert_nm_udev(None)
239
240 # generates wpa config and enables wpasupplicant unit
241 with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f:
242 new_config = f.read()
243 self.assertIn('''
244 network={
245 ssid="homenet"
246 key_mgmt=NONE
247 }
248 ''', new_config)
249 self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o600)
250 self.assertTrue(os.path.isfile(os.path.join(
251 self.workdir.name, 'run/systemd/system/netplan-wpa-wl0.service')))
252 self.assertTrue(os.path.islink(os.path.join(
253 self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl0.service')))
254
133255
134256 class TestNetworkManager(TestBase):
135257
141263 wl0:
142264 access-points:
143265 "Joe's Home":
144 password: "s3kr1t"
266 password: "s0s3kr1t"
267 bssid: 00:11:22:33:44:55
268 band: 2.4GHz
269 channel: 11
145270 workplace:
146 password: "c0mpany"
271 password: "c0mpany1"
272 bssid: de:ad:be:ef:ca:fe
273 band: 5GHz
274 channel: 100
275 channel-no-band:
276 channel: 22
277 band-no-channel:
278 band: 5GHz
147279 dhcp4: yes''')
148280
149281 self.assert_nm({'wl0-Joe%27s%20Home': '''[connection]
163295 [wifi]
164296 ssid=Joe's Home
165297 mode=infrastructure
298 bssid=00:11:22:33:44:55
299 band=bg
300 channel=11
166301
167302 [wifi-security]
168303 key-mgmt=wpa-psk
169 psk=s3kr1t
304 psk=s0s3kr1t
170305 ''',
171306 'wl0-workplace': '''[connection]
172307 id=netplan-wl0-workplace
185320 [wifi]
186321 ssid=workplace
187322 mode=infrastructure
323 bssid=de:ad:be:ef:ca:fe
324 band=a
325 channel=100
188326
189327 [wifi-security]
190328 key-mgmt=wpa-psk
191 psk=c0mpany
329 psk=c0mpany1
330 ''',
331 'wl0-channel-no-band': '''[connection]
332 id=netplan-wl0-channel-no-band
333 type=wifi
334 interface-name=wl0
335
336 [ethernet]
337 wake-on-lan=0
338
339 [ipv4]
340 method=auto
341
342 [ipv6]
343 method=ignore
344
345 [wifi]
346 ssid=channel-no-band
347 mode=infrastructure
348 ''',
349 'wl0-band-no-channel': '''[connection]
350 id=netplan-wl0-band-no-channel
351 type=wifi
352 interface-name=wl0
353
354 [ethernet]
355 wake-on-lan=0
356
357 [ipv4]
358 method=auto
359
360 [ipv6]
361 method=ignore
362
363 [wifi]
364 ssid=band-no-channel
365 mode=infrastructure
366 band=a
192367 '''})
193368 self.assert_networkd({})
194369 self.assert_nm_udev(None)
262437 access-points:
263438 homenet:
264439 mode: ap
265 password: s3cret''')
440 password: s0s3cret''')
266441
267442 self.assert_nm({'wl0-homenet': '''[connection]
268443 id=netplan-wl0-homenet
284459
285460 [wifi-security]
286461 key-mgmt=wpa-psk
287 psk=s3cret
462 psk=s0s3cret
288463 '''})
289464 self.assert_networkd({})
290465 self.assert_nm_udev(None)
317492 ssid=homenet
318493 mode=adhoc
319494 '''})
495
496 def test_wifi_wowlan(self):
497 self.generate('''network:
498 version: 2
499 renderer: NetworkManager
500 wifis:
501 wl0:
502 wakeonwlan: [any, tcp, four_way_handshake, magic_pkt]
503 access-points:
504 homenet: {mode: infrastructure}''')
505
506 self.assert_nm({'wl0-homenet': '''[connection]
507 id=netplan-wl0-homenet
508 type=wifi
509 interface-name=wl0
510
511 [ethernet]
512 wake-on-lan=0
513
514 [802-11-wireless]
515 wake-on-wlan=330
516
517 [ipv4]
518 method=link-local
519
520 [ipv6]
521 method=ignore
522
523 [wifi]
524 ssid=homenet
525 mode=infrastructure
526 '''})
527
528 def test_wifi_wowlan_default(self):
529 self.generate('''network:
530 version: 2
531 renderer: NetworkManager
532 wifis:
533 wl0:
534 wakeonwlan: [default]
535 access-points:
536 homenet: {mode: infrastructure}''')
537
538 self.assert_nm({'wl0-homenet': '''[connection]
539 id=netplan-wl0-homenet
540 type=wifi
541 interface-name=wl0
542
543 [ethernet]
544 wake-on-lan=0
545
546 [ipv4]
547 method=link-local
548
549 [ipv6]
550 method=ignore
551
552 [wifi]
553 ssid=homenet
554 mode=infrastructure
555 '''})
556
557
558 class TestConfigErrors(TestBase):
559
560 def test_wifi_invalid_wowlan(self):
561 err = self.generate('''network:
562 version: 2
563 wifis:
564 wl0:
565 wakeonwlan: [bogus]
566 access-points:
567 homenet: {mode: infrastructure}''', expect_fail=True)
568 self.assertIn("Error in network definition: invalid value for wakeonwlan: 'bogus'", err)
569
570 def test_wifi_wowlan_unsupported(self):
571 err = self.generate('''network:
572 version: 2
573 wifis:
574 wl0:
575 wakeonwlan: [tcp]
576 access-points:
577 homenet: {mode: infrastructure}''', expect_fail=True)
578 self.assertIn("ERROR: unsupported wowlan_triggers mask: 0x100", err)
579
580 def test_wifi_wowlan_exclusive(self):
581 err = self.generate('''network:
582 version: 2
583 wifis:
584 wl0:
585 wakeonwlan: [default, magic_pkt]
586 access-points:
587 homenet: {mode: infrastructure}''', expect_fail=True)
588 self.assertIn("Error in network definition: 'default' is an exclusive flag for wakeonwlan", err)
2727 import tempfile
2828 import unittest
2929 import shutil
30 import gi
31
32 # make sure we point to libnetplan properly.
33 os.environ.update({'LD_LIBRARY_PATH': '.:{}'.format(os.environ.get('LD_LIBRARY_PATH'))})
3034
3135 test_backends = "networkd NetworkManager" if "NETPLAN_TEST_BACKENDS" not in os.environ else os.environ["NETPLAN_TEST_BACKENDS"]
3236
361365 stderr=subprocess.PIPE, universal_newlines=True)
362366 self.fail('timed out waiting for networkd to settle down:\n%s\n%s\n%s' % (st, st_e, st_e2))
363367
364 if subprocess.call(['nm-online', '--quiet', '--timeout=120', '--wait-for-startup']) != 0:
368 if subprocess.call(['nm-online', '--quiet', '--timeout=240', '--wait-for-startup']) != 0:
365369 self.fail('timed out waiting for NetworkManager to settle down')
370
371 def nm_online_full(self, iface, timeout=60):
372 '''Wait for NetworkManager connection to be completed (incl. IP4 & DHCP)'''
373
374 gi.require_version('NM', '1.0')
375 from gi.repository import NM
376 for t in range(timeout):
377 c = NM.Client.new(None)
378 con = c.get_device_by_iface(iface).get_active_connection()
379 if not con:
380 self.fail('no active connection for %s by NM' % iface)
381 flags = NM.utils_enum_to_str(NM.ActivationStateFlags, con.get_state_flags())
382 if "ip4-ready" in flags:
383 break
384 time.sleep(1)
385 else:
386 self.fail('timed out waiting for %s to get ready by NM' % iface)
366387
367388 def nm_wait_connected(self, iface, timeout):
368389 for t in range(timeout):
9292 search: ["fakesuffix"]
9393 ''' % {'r': self.backend, 'ec': self.dev_e_client, 'e2c': self.dev_e2_client})
9494 self.generate_and_settle()
95 if self.backend == 'NetworkManager':
96 self.nm_online_full(self.dev_e_client)
9597 self.assert_iface_up(self.dev_e_client,
9698 ['inet 172.16.42.99/18',
9799 'inet6 1234:ffff::42/64',
0 #!/usr/bin/python3
1 #
2 # Copyright (C) 2020 Canonical, Ltd.
3 # Author: Łukasz 'sil2100' Zemczak <lukasz.zemczak@canonical.com>
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; version 3.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 import os
18 import unittest
19 import tempfile
20
21 from subprocess import CalledProcessError
22 from collections import defaultdict
23 from unittest.mock import patch, mock_open, call
24
25 import netplan.cli.sriov as sriov
26
27 from netplan.configmanager import ConfigManager, ConfigurationError
28
29
30 class MockSRIOVOpen():
31 def __init__(self):
32 # now this is a VERY ugly hack to make mock_open() better
33 self.read_queue = []
34 self.write_queue = []
35
36 def sriov_read():
37 action = self.read_queue.pop(0)
38 if isinstance(action, str):
39 return action
40 else:
41 raise action
42
43 def sriov_write(data):
44 if not self.write_queue:
45 return
46 action = self.write_queue.pop(0)
47 if isinstance(action, Exception):
48 raise action
49
50 self.open = mock_open()
51 self.open.return_value.read.side_effect = sriov_read
52 self.open.return_value.write.side_effect = sriov_write
53
54
55 def mock_set_counts(interfaces, config_manager, vf_counts, active_vfs, active_pfs):
56 counts = {'enp1': 2, 'enp2': 1}
57 vfs = {'enp1s16f1': None, 'enp1s16f2': None, 'customvf1': None}
58 pfs = {'enp1': 'enp1', 'enpx': 'enp2'}
59 vf_counts.update(counts)
60 active_vfs.update(vfs)
61 active_pfs.update(pfs)
62
63
64 class TestSRIOV(unittest.TestCase):
65 def setUp(self):
66 self.workdir = tempfile.TemporaryDirectory()
67 os.makedirs(os.path.join(self.workdir.name, 'etc/netplan'))
68 self.configmanager = ConfigManager(prefix=self.workdir.name, extra_files={})
69
70 def _prepare_sysfs_dir_structure(self):
71 # prepare a directory hierarchy for testing the matching
72 # this might look really scary, but that's how sysfs presents devices
73 # such as these
74 os.makedirs(os.path.join(self.workdir.name, 'sys/class/net'))
75
76 # first the VF
77 vf_iface_path = os.path.join(self.workdir.name, 'sys/devices/pci0000:00/0000:00:1f.6/net/enp2s16f1')
78 vf_dev_path = os.path.join(self.workdir.name, 'sys/devices/pci0000:00/0000:00:1f.6')
79 os.makedirs(vf_iface_path)
80 with open(os.path.join(vf_dev_path, 'vendor'), 'w') as f:
81 f.write('0x001f\n')
82 with open(os.path.join(vf_dev_path, 'device'), 'w') as f:
83 f.write('0xb33f\n')
84 os.symlink('../../devices/pci0000:00/0000:00:1f.6/net/enp2s16f1',
85 os.path.join(self.workdir.name, 'sys/class/net/enp2s16f1'))
86 os.symlink('../../../0000:00:1f.6', os.path.join(self.workdir.name, 'sys/class/net/enp2s16f1/device'))
87
88 # now the PF
89 os.path.join(self.workdir.name, 'sys/class/net/enp2')
90 pf_iface_path = os.path.join(self.workdir.name, 'sys/devices/pci0000:00/0000:00:1f.0/net/enp2')
91 pf_dev_path = os.path.join(self.workdir.name, 'sys/devices/pci0000:00/0000:00:1f.0')
92 os.makedirs(pf_iface_path)
93 with open(os.path.join(pf_dev_path, 'vendor'), 'w') as f:
94 f.write('0x001f\n')
95 with open(os.path.join(pf_dev_path, 'device'), 'w') as f:
96 f.write('0x1337\n')
97 os.symlink('../../devices/pci0000:00/0000:00:1f.0/net/enp2',
98 os.path.join(self.workdir.name, 'sys/class/net/enp2'))
99 os.symlink('../../../0000:00:1f.0', os.path.join(self.workdir.name, 'sys/class/net/enp2/device'))
100 # the PF additionally has device links to all the VFs defined for it
101 os.symlink('../../../0000:00:1f.4', os.path.join(pf_dev_path, 'virtfn1'))
102 os.symlink('../../../0000:00:1f.5', os.path.join(pf_dev_path, 'virtfn2'))
103 os.symlink('../../../0000:00:1f.6', os.path.join(pf_dev_path, 'virtfn3'))
104 os.symlink('../../../0000:00:1f.7', os.path.join(pf_dev_path, 'virtfn4'))
105
106 @patch('netplan.cli.utils.get_interface_driver_name')
107 @patch('netplan.cli.utils.get_interface_macaddress')
108 def test_get_vf_count_and_functions(self, gim, gidn):
109 # we mock-out get_interface_driver_name and get_interface_macaddress
110 # to return useful values for the test
111 gim.side_effect = lambda x: '00:01:02:03:04:05' if x == 'enp3' else '00:00:00:00:00:00'
112 gidn.side_effect = lambda x: 'foo' if x == 'enp2' else 'bar'
113 with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"), 'w') as fd:
114 print('''network:
115 version: 2
116 renderer: networkd
117 ethernets:
118 renderer: networkd
119 enp1:
120 mtu: 9000
121 enp2:
122 match:
123 driver: foo
124 enp3:
125 match:
126 macaddress: 00:01:02:03:04:05
127 enpx:
128 match:
129 name: enp[4-5]
130 enp0:
131 mtu: 9000
132 enp8:
133 virtual-function-count: 7
134 enp9: {}
135 wlp6s0: {}
136 enp1s16f1:
137 link: enp1
138 macaddress: 01:02:03:04:05:00
139 enp1s16f2:
140 link: enp1
141 macaddress: 01:02:03:04:05:01
142 enp2s16f1:
143 link: enp2
144 enp2s16f2: {link: enp2}
145 enp3s16f1:
146 link: enp3
147 enpxs16f1:
148 match:
149 name: enp[4-5]s16f1
150 link: enpx
151 enp9s16f1:
152 link: enp9
153 ''', file=fd)
154 self.configmanager.parse()
155 interfaces = ['enp1', 'enp2', 'enp3', 'enp5', 'enp0', 'enp8']
156 vf_counts = defaultdict(int)
157 vfs = {}
158 pfs = {}
159
160 # call the function under test
161 sriov.get_vf_count_and_functions(interfaces, self.configmanager,
162 vf_counts, vfs, pfs)
163 # check if the right vf counts have been recorded in vf_counts
164 self.assertDictEqual(
165 vf_counts,
166 {'enp1': 2, 'enp2': 2, 'enp3': 1, 'enp5': 1, 'enp8': 7})
167 # also check if the vfs and pfs dictionaries got properly set
168 self.assertDictEqual(
169 vfs,
170 {'enp1s16f1': None, 'enp1s16f2': None, 'enp2s16f1': None,
171 'enp2s16f2': None, 'enp3s16f1': None, 'enpxs16f1': None})
172 self.assertDictEqual(
173 pfs,
174 {'enp1': 'enp1', 'enp2': 'enp2', 'enp3': 'enp3',
175 'enpx': 'enp5', 'enp8': 'enp8'})
176
177 @patch('netplan.cli.utils.get_interface_driver_name')
178 @patch('netplan.cli.utils.get_interface_macaddress')
179 def test_get_vf_count_and_functions_many_match(self, gim, gidn):
180 # we mock-out get_interface_driver_name and get_interface_macaddress
181 # to return useful values for the test
182 gim.side_effect = lambda x: '00:01:02:03:04:05' if x == 'enp3' else '00:00:00:00:00:00'
183 gidn.side_effect = lambda x: 'foo' if x == 'enp2' else 'bar'
184 with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"), 'w') as fd:
185 print('''network:
186 version: 2
187 renderer: networkd
188 ethernets:
189 renderer: networkd
190 enpx:
191 match:
192 name: enp*
193 mtu: 9000
194 enpxs16f1:
195 link: enpx
196 ''', file=fd)
197 self.configmanager.parse()
198 interfaces = ['enp1', 'wlp6s0', 'enp2', 'enp3']
199 vf_counts = defaultdict(int)
200 vfs = {}
201 pfs = {}
202
203 # call the function under test
204 with self.assertRaises(ConfigurationError) as e:
205 sriov.get_vf_count_and_functions(interfaces, self.configmanager,
206 vf_counts, vfs, pfs)
207
208 self.assertIn('matched more than one interface for a PF device: enpx',
209 str(e.exception))
210
211 @patch('netplan.cli.utils.get_interface_driver_name')
212 @patch('netplan.cli.utils.get_interface_macaddress')
213 def test_get_vf_count_and_functions_not_enough_explicit(self, gim, gidn):
214 # we mock-out get_interface_driver_name and get_interface_macaddress
215 # to return useful values for the test
216 gim.side_effect = lambda x: '00:01:02:03:04:05' if x == 'enp3' else '00:00:00:00:00:00'
217 gidn.side_effect = lambda x: 'foo' if x == 'enp2' else 'bar'
218 with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"), 'w') as fd:
219 print('''network:
220 version: 2
221 renderer: networkd
222 ethernets:
223 renderer: networkd
224 enp1:
225 virtual-function-count: 2
226 mtu: 9000
227 enp1s16f1:
228 link: enp1
229 enp1s16f2:
230 link: enp1
231 enp1s16f3:
232 link: enp1
233 ''', file=fd)
234 self.configmanager.parse()
235 interfaces = ['enp1', 'wlp6s0']
236 vf_counts = defaultdict(int)
237 vfs = {}
238 pfs = {}
239
240 # call the function under test
241 with self.assertRaises(ConfigurationError) as e:
242 sriov.get_vf_count_and_functions(interfaces, self.configmanager,
243 vf_counts, vfs, pfs)
244
245 self.assertIn('more VFs allocated than the explicit size declared: 3 > 2',
246 str(e.exception))
247
248 def test_set_numvfs_for_pf(self):
249 sriov_open = MockSRIOVOpen()
250 sriov_open.read_queue = ['8\n']
251
252 with patch('builtins.open', sriov_open.open):
253 ret = sriov.set_numvfs_for_pf('enp1', 2)
254
255 self.assertTrue(ret)
256 self.assertListEqual(sriov_open.open.call_args_list,
257 [call('/sys/class/net/enp1/device/sriov_totalvfs'),
258 call('/sys/class/net/enp1/device/sriov_numvfs', 'w')])
259 handle = sriov_open.open()
260 handle.write.assert_called_once_with('2')
261
262 def test_set_numvfs_for_pf_failsafe(self):
263 sriov_open = MockSRIOVOpen()
264 sriov_open.read_queue = ['8\n']
265 sriov_open.write_queue = [IOError(16, 'Error'), None, None]
266
267 with patch('builtins.open', sriov_open.open):
268 ret = sriov.set_numvfs_for_pf('enp1', 2)
269
270 self.assertTrue(ret)
271 handle = sriov_open.open()
272 self.assertEqual(handle.write.call_count, 3)
273
274 def test_set_numvfs_for_pf_over_max(self):
275 sriov_open = MockSRIOVOpen()
276 sriov_open.read_queue = ['8\n']
277
278 with patch('builtins.open', sriov_open.open):
279 with self.assertRaises(ConfigurationError) as e:
280 sriov.set_numvfs_for_pf('enp1', 9)
281
282 self.assertIn('cannot allocate more VFs for PF enp1 than supported',
283 str(e.exception))
284
285 def test_set_numvfs_for_pf_over_theoretical_max(self):
286 sriov_open = MockSRIOVOpen()
287 sriov_open.read_queue = ['1337\n']
288
289 with patch('builtins.open', sriov_open.open):
290 with self.assertRaises(ConfigurationError) as e:
291 sriov.set_numvfs_for_pf('enp1', 345)
292
293 self.assertIn('cannot allocate more VFs for PF enp1 than the SR-IOV maximum',
294 str(e.exception))
295
296 def test_set_numvfs_for_pf_read_failed(self):
297 sriov_open = MockSRIOVOpen()
298 cases = (
299 [IOError],
300 ['not a number\n'],
301 )
302
303 with patch('builtins.open', sriov_open.open):
304 for case in cases:
305 sriov_open.read_queue = case
306 with self.assertRaises(RuntimeError):
307 sriov.set_numvfs_for_pf('enp1', 3)
308
309 def test_set_numvfs_for_pf_write_failed(self):
310 sriov_open = MockSRIOVOpen()
311 sriov_open.read_queue = ['8\n']
312 sriov_open.write_queue = [IOError(16, 'Error'), IOError(16, 'Error')]
313
314 with patch('builtins.open', sriov_open.open):
315 with self.assertRaises(RuntimeError) as e:
316 sriov.set_numvfs_for_pf('enp1', 2)
317
318 self.assertIn('failed setting sriov_numvfs to 2 for enp1',
319 str(e.exception))
320
321 def test_perform_hardware_specific_quirks(self):
322 # for now we have no custom quirks defined, so we just
323 # check if the function succeeds
324 sriov_open = MockSRIOVOpen()
325 sriov_open.read_queue = ['0x001f\n', '0x1337\n']
326
327 with patch('builtins.open', sriov_open.open):
328 sriov.perform_hardware_specific_quirks('enp1')
329
330 # it's good enough if it did all the matching
331 self.assertListEqual(sriov_open.open.call_args_list,
332 [call('/sys/class/net/enp1/device/vendor'),
333 call('/sys/class/net/enp1/device/device'), ])
334
335 def test_perform_hardware_specific_quirks_failed(self):
336 sriov_open = MockSRIOVOpen()
337 cases = (
338 [IOError],
339 ['0x001f\n', IOError],
340 )
341
342 with patch('builtins.open', sriov_open.open):
343 for case in cases:
344 sriov_open.read_queue = case
345 with self.assertRaises(RuntimeError) as e:
346 sriov.perform_hardware_specific_quirks('enp1')
347
348 self.assertIn('could not determine vendor and device ID of enp1',
349 str(e.exception))
350
351 @patch('subprocess.check_call')
352 def test_apply_vlan_filter_for_vf(self, check_call):
353 self._prepare_sysfs_dir_structure()
354
355 sriov.apply_vlan_filter_for_vf('enp2', 'enp2s16f1', 'vlan10', 10, prefix=self.workdir.name)
356
357 self.assertEqual(check_call.call_count, 1)
358 self.assertListEqual(check_call.call_args[0][0],
359 ['ip', 'link', 'set', 'dev', 'enp2',
360 'vf', '3', 'vlan', '10'])
361
362 @patch('subprocess.check_call')
363 def test_apply_vlan_filter_for_vf_failed_no_index(self, check_call):
364 self._prepare_sysfs_dir_structure()
365 # we remove the PF -> VF link, simulating a system error
366 os.unlink(os.path.join(self.workdir.name, 'sys/class/net/enp2/device/virtfn3'))
367
368 with self.assertRaises(RuntimeError) as e:
369 sriov.apply_vlan_filter_for_vf('enp2', 'enp2s16f1', 'vlan10', 10, prefix=self.workdir.name)
370
371 self.assertIn('could not determine the VF index for enp2s16f1 while configuring vlan vlan10',
372 str(e.exception))
373 self.assertEqual(check_call.call_count, 0)
374
375 @patch('subprocess.check_call')
376 def test_apply_vlan_filter_for_vf_failed_ip_link_set(self, check_call):
377 self._prepare_sysfs_dir_structure()
378 check_call.side_effect = CalledProcessError(-1, None)
379
380 with self.assertRaises(RuntimeError) as e:
381 sriov.apply_vlan_filter_for_vf('enp2', 'enp2s16f1', 'vlan10', 10, prefix=self.workdir.name)
382
383 self.assertIn('failed setting SR-IOV VLAN filter for vlan vlan10',
384 str(e.exception))
385
386 @patch('netifaces.interfaces')
387 @patch('netplan.cli.sriov.get_vf_count_and_functions')
388 @patch('netplan.cli.sriov.set_numvfs_for_pf')
389 @patch('netplan.cli.sriov.perform_hardware_specific_quirks')
390 @patch('netplan.cli.sriov.apply_vlan_filter_for_vf')
391 @patch('netplan.cli.utils.get_interface_driver_name')
392 @patch('netplan.cli.utils.get_interface_macaddress')
393 def test_apply_sriov_config(self, gim, gidn, apply_vlan, quirks,
394 set_numvfs, get_counts, netifs):
395 # set up the environment
396 with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"), 'w') as fd:
397 print('''network:
398 version: 2
399 renderer: networkd
400 ethernets:
401 enp1:
402 mtu: 9000
403 enpx:
404 match:
405 name: enp[2-3]
406 enp1s16f1:
407 link: enp1
408 macaddress: 01:02:03:04:05:00
409 enp1s16f2:
410 link: enp1
411 customvf1:
412 match:
413 name: enp[2-3]s16f[1-4]
414 link: enpx
415 vlans:
416 vf1.15:
417 renderer: sriov
418 id: 15
419 link: customvf1
420 vf1.16:
421 renderer: sriov
422 id: 16
423 link: foobar
424 ''', file=fd)
425 self.configmanager.parse()
426 interfaces = ['enp1', 'enp2', 'enp5', 'wlp6s0']
427 # set up all the mock objects
428 netifs.return_value = ['enp1', 'enp2', 'enp5', 'wlp6s0',
429 'enp1s16f1', 'enp1s16f2', 'enp2s16f1']
430 get_counts.side_effect = mock_set_counts
431 set_numvfs.side_effect = lambda pf, _: False if pf == 'enp2' else True
432 gidn.return_value = 'foodriver'
433 gim.return_value = '00:01:02:03:04:05'
434
435 # call method under test
436 sriov.apply_sriov_config(interfaces, self.configmanager)
437
438 # check if the config got applied as expected
439 # we had 2 PFs, one having two VFs and the other only one
440 self.assertEqual(set_numvfs.call_count, 2)
441 self.assertListEqual(set_numvfs.call_args_list,
442 [call('enp1', 2),
443 call('enp2', 1)])
444 # one of the pfs already had sufficient VFs allocated, so only enp1
445 # changed the vf count and only that one should trigger quirks
446 quirks.assert_called_once_with('enp1')
447 # only one had a hardware vlan
448 apply_vlan.assert_called_once_with('enp2', 'enp2s16f1', 'vf1.15', 15)
449
450 @patch('netifaces.interfaces')
451 @patch('netplan.cli.sriov.get_vf_count_and_functions')
452 @patch('netplan.cli.sriov.set_numvfs_for_pf')
453 @patch('netplan.cli.sriov.perform_hardware_specific_quirks')
454 @patch('netplan.cli.sriov.apply_vlan_filter_for_vf')
455 @patch('netplan.cli.utils.get_interface_driver_name')
456 @patch('netplan.cli.utils.get_interface_macaddress')
457 def test_apply_sriov_config_invalid_vlan(self, gim, gidn, apply_vlan, quirks,
458 set_numvfs, get_counts, netifs):
459 # set up the environment
460 with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"), 'w') as fd:
461 print('''network:
462 version: 2
463 renderer: networkd
464 ethernets:
465 enp1:
466 mtu: 9000
467 enpx:
468 match:
469 name: enp[2-3]
470 enp1s16f1:
471 link: enp1
472 macaddress: 01:02:03:04:05:00
473 enp1s16f2:
474 link: enp1
475 customvf1:
476 match:
477 name: enp[2-3]s16f[1-4]
478 link: enpx
479 vlans:
480 vf1.15:
481 renderer: sriov
482 link: customvf1
483 ''', file=fd)
484 self.configmanager.parse()
485 interfaces = ['enp1', 'enp2', 'enp5', 'wlp6s0']
486 # set up all the mock objects
487 netifs.return_value = ['enp1', 'enp2', 'enp5', 'wlp6s0',
488 'enp1s16f1', 'enp1s16f2', 'enp2s16f1']
489 get_counts.side_effect = mock_set_counts
490 set_numvfs.side_effect = lambda pf, _: False if pf == 'enp2' else True
491 gidn.return_value = 'foodriver'
492 gim.return_value = '00:01:02:03:04:05'
493
494 # call method under test
495 with self.assertRaises(ConfigurationError) as e:
496 sriov.apply_sriov_config(interfaces, self.configmanager)
497
498 self.assertIn('no id property defined for SR-IOV vlan vf1.15',
499 str(e.exception))
500 self.assertEqual(apply_vlan.call_count, 0)
501
502 @patch('netifaces.interfaces')
503 @patch('netplan.cli.sriov.get_vf_count_and_functions')
504 @patch('netplan.cli.sriov.set_numvfs_for_pf')
505 @patch('netplan.cli.sriov.perform_hardware_specific_quirks')
506 @patch('netplan.cli.sriov.apply_vlan_filter_for_vf')
507 @patch('netplan.cli.utils.get_interface_driver_name')
508 @patch('netplan.cli.utils.get_interface_macaddress')
509 def test_apply_sriov_config_too_many_vlans(self, gim, gidn, apply_vlan, quirks,
510 set_numvfs, get_counts, netifs):
511 # set up the environment
512 with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"), 'w') as fd:
513 print('''network:
514 version: 2
515 renderer: networkd
516 ethernets:
517 enp1:
518 mtu: 9000
519 enpx:
520 match:
521 name: enp[2-3]
522 enp1s16f1:
523 link: enp1
524 macaddress: 01:02:03:04:05:00
525 enp1s16f2:
526 link: enp1
527 customvf1:
528 match:
529 name: enp[2-3]s16f[1-4]
530 link: enpx
531 vlans:
532 vf1.15:
533 renderer: sriov
534 id: 15
535 link: customvf1
536 vf1.16:
537 renderer: sriov
538 id: 16
539 link: customvf1
540 ''', file=fd)
541 self.configmanager.parse()
542 interfaces = ['enp1', 'enp2', 'enp5', 'wlp6s0']
543 # set up all the mock objects
544 netifs.return_value = ['enp1', 'enp2', 'enp5', 'wlp6s0',
545 'enp1s16f1', 'enp1s16f2', 'enp2s16f1']
546 get_counts.side_effect = mock_set_counts
547 set_numvfs.side_effect = lambda pf, _: False if pf == 'enp2' else True
548 gidn.return_value = 'foodriver'
549 gim.return_value = '00:01:02:03:04:05'
550
551 # call method under test
552 with self.assertRaises(ConfigurationError) as e:
553 sriov.apply_sriov_config(interfaces, self.configmanager)
554
555 self.assertIn('interface enp2s16f1 for netplan device customvf1 (vf1.16) already has an SR-IOV vlan defined',
556 str(e.exception))
557 self.assertEqual(apply_vlan.call_count, 1)
558
559 @patch('netifaces.interfaces')
560 @patch('netplan.cli.sriov.get_vf_count_and_functions')
561 @patch('netplan.cli.sriov.set_numvfs_for_pf')
562 @patch('netplan.cli.sriov.perform_hardware_specific_quirks')
563 @patch('netplan.cli.sriov.apply_vlan_filter_for_vf')
564 @patch('netplan.cli.utils.get_interface_driver_name')
565 @patch('netplan.cli.utils.get_interface_macaddress')
566 def test_apply_sriov_config_many_match(self, gim, gidn, apply_vlan, quirks,
567 set_numvfs, get_counts, netifs):
568 # set up the environment
569 with open(os.path.join(self.workdir.name, "etc/netplan/test.yaml"), 'w') as fd:
570 print('''network:
571 version: 2
572 renderer: networkd
573 ethernets:
574 enp1:
575 mtu: 9000
576 enpx:
577 match:
578 name: enp[2-3]
579 enp1s16f1:
580 link: enp1
581 macaddress: 01:02:03:04:05:00
582 enp1s16f2:
583 link: enp1
584 customvf1:
585 match:
586 name: enp*s16f[1-4]
587 link: enpx
588 ''', file=fd)
589 self.configmanager.parse()
590 interfaces = ['enp1', 'enp2', 'enp5', 'wlp6s0']
591 # set up all the mock objects
592 netifs.return_value = ['enp1', 'enp2', 'enp5', 'wlp6s0',
593 'enp1s16f1', 'enp1s16f2', 'enp2s16f1']
594 get_counts.side_effect = mock_set_counts
595 set_numvfs.side_effect = lambda pf, _: False if pf == 'enp2' else True
596 gidn.return_value = 'foodriver'
597 gim.return_value = '00:01:02:03:04:05'
598
599 # call method under test
600 with self.assertRaises(ConfigurationError) as e:
601 sriov.apply_sriov_config(interfaces, self.configmanager)
602
603 self.assertIn('matched more than one interface for a VF device: customvf1',
604 str(e.exception))