Merge ~danilogondolfo/netplan/+git/ubuntu:noble_1.0.1-1ubuntu1 into ~ubuntu-core-dev/netplan/+git/ubuntu:ubuntu-noble
- Git
- lp:~danilogondolfo/netplan/+git/ubuntu
- noble_1.0.1-1ubuntu1
- Merge into ubuntu-noble
Status: | Merged | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 33e26311cc46ffd78a2e438be62961186dec2c9f | ||||||||||||||||
Proposed branch: | ~danilogondolfo/netplan/+git/ubuntu:noble_1.0.1-1ubuntu1 | ||||||||||||||||
Merge into: | ~ubuntu-core-dev/netplan/+git/ubuntu:ubuntu-noble | ||||||||||||||||
Diff against target: |
9520 lines (+2927/-1381) 72 files modified
.github/workflows/autopkgtest.yml (+5/-1) .github/workflows/build-abi.yml (+1/-0) .github/workflows/check-address-sanitizer.yml (+1/-0) .github/workflows/check-coverage.yml (+1/-0) .github/workflows/coverity.yml (+1/-0) .github/workflows/debci.yml (+1/-1) .github/workflows/snapd.patch (+4/-4) .github/workflows/spread.yml (+5/-1) README.md (+3/-3) debian/changelog (+73/-0) debian/control (+1/-0) debian/copyright (+1/-1) debian/patches/0002-CLI-apply-call-udevadm-trigger-using-action-add-Clos.patch (+72/-0) debian/patches/0003-Revert-wait-online-disabled-wait-online-for-stable-1.patch (+155/-0) debian/patches/0004-generate-avoid-calling-udevadm-control-reload-LP-199.patch (+82/-0) debian/patches/series (+3/-16) debian/tests/control (+1/-1) dev/null (+0/-386) doc/.sphinx/requirements.txt (+1/-0) doc/conf.py (+0/-1) doc/creating-link-aggregation.md (+86/-0) doc/howto.md (+26/-14) doc/matching-interface-by-mac-address.md (+85/-0) doc/netplan-generate.md (+2/-2) doc/netplan-status.md (+3/-3) doc/netplan-tutorial.md (+239/-533) doc/netplan-yaml.md (+6/-5) doc/tutorial.md (+2/-0) doc/using-static-ip-addresses.md (+128/-0) meson.build (+1/-1) netplan_cli/__init__.py (+1/-1) netplan_cli/cli/commands/apply.py (+0/-4) netplan_cli/cli/commands/generate.py (+10/-1) netplan_cli/cli/core.py (+2/-2) netplan_cli/cli/sriov.py (+107/-56) netplan_cli/cli/state_diff.py (+1/-1) netplan_cli/cli/utils.py (+1/-1) python-cffi/netplan/__init__.py (+5/-5) spread.yaml (+1/-0) src/generate.c (+25/-10) src/networkd.c (+219/-58) src/networkd.h (+5/-0) src/nm.c (+18/-12) src/openvswitch.c (+14/-10) src/parse-nm.c (+15/-0) src/parse.c (+115/-40) src/sriov.c (+16/-5) src/util-internal.h (+12/-0) src/util.c (+116/-4) src/validation.c (+4/-3) src/yaml-helpers.h (+1/-0) tests/cli/test_get_set.py (+2/-0) tests/cli_legacy.py (+1/-2) tests/ctests/meson.build (+1/-0) tests/ctests/test_netplan_keyfile.c (+65/-0) tests/ctests/test_netplan_misc.c (+45/-0) tests/ctests/test_netplan_networkd.c (+74/-0) tests/ctests/test_netplan_parser.c (+51/-0) tests/generator/base.py (+3/-0) tests/generator/test_args.py (+106/-5) tests/generator/test_auth.py (+11/-11) tests/generator/test_bonds.py (+14/-0) tests/generator/test_common.py (+144/-9) tests/generator/test_ovs.py (+82/-0) tests/generator/test_wifis.py (+61/-90) tests/integration/base.py (+93/-0) tests/integration/diff.py (+2/-2) tests/integration/ethernets.py (+46/-3) tests/netplan_dbus/test_dbus.py (+6/-6) tests/parser/test_keyfile.py (+39/-0) tests/test_sriov.py (+403/-66) tests/test_utils.py (+1/-1) |
||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Lukas Märdian | Approve | ||
Ubuntu Core Development Team | Pending | ||
Review via email:
|
Commit message
Description of the change
This is essentially the same netplan.io found in Oracular.
Apart from the changelog, there is just a small diff with distro specific changes, a patch file I renamed to avoid a prefix number collision and a DEP-3 header I added to another patch file.
A PPA is available here https:/
Autopkgtests look good except for one "scenarios" test (TestNetworkMan
Autopkgtests:
https:/
https:/
https:/
https:/
On ppc64el, there is one test from "scenarios" (TestNetworkMan
On three consecutive runs it passed once:
https:/
https:/
https:/
Update scan failed
At least one of the branches involved have failed to scan. You can manually schedule a rescan if required.
Preview Diff
1 | diff --git a/doc/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md |
2 | similarity index 100% |
3 | rename from doc/CODE_OF_CONDUCT.md |
4 | rename to .github/CODE_OF_CONDUCT.md |
5 | diff --git a/.github/workflows/autopkgtest.yml b/.github/workflows/autopkgtest.yml |
6 | index 310e99b..5dc8bec 100644 |
7 | --- a/.github/workflows/autopkgtest.yml |
8 | +++ b/.github/workflows/autopkgtest.yml |
9 | @@ -33,6 +33,8 @@ jobs: |
10 | # it's needed (will be auto-loaded) by routing.test_vrf_basic |
11 | - name: Install dependencies |
12 | run: | |
13 | + echo "APT::Get::Always-Include-Phased-Updates \"true\";" | sudo tee /etc/apt/apt.conf.d/90phased-updates |
14 | + sudo add-apt-repository -y -u -s ppa:slyon/netplan-ci |
15 | sudo apt update |
16 | sudo apt install autopkgtest ubuntu-dev-tools devscripts openvswitch-switch linux-modules-extra-$(uname -r) |
17 | # work around LP: #1878225 as fallback |
18 | @@ -46,7 +48,9 @@ jobs: |
19 | cp -r netplan.io-*/debian . |
20 | rm -r debian/patches/ # clear any distro patches |
21 | # usrmerge-fix |
22 | - sed -i 's|rm debian/tmp/lib/netplan/generate|sh -c "mkdir -p debian/tmp/usr/lib/systemd/system-generators; rm -rf debian/tmp/lib; ln -sf /usr/libexec/netplan/generate debian/tmp/usr/lib/systemd/system-generators/netplan"|' debian/rules |
23 | + echo "" >> debian/rules |
24 | + echo "execute_after_dh_auto_install:" >> debian/rules |
25 | + echo " sh -c \"mkdir -p debian/tmp/usr/lib/systemd/system-generators; rm -rf debian/tmp/lib; ln -sf /usr/libexec/netplan/generate debian/tmp/usr/lib/systemd/system-generators/netplan\"" >> debian/rules |
26 | # usrmerge-fix-end |
27 | sed -i 's| systemd-dev|# DELETED: systemd-dev|' debian/control |
28 | TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) # find latest (stable) tag |
29 | diff --git a/.github/workflows/build-abi.yml b/.github/workflows/build-abi.yml |
30 | index e517101..c7f1b18 100644 |
31 | --- a/.github/workflows/build-abi.yml |
32 | +++ b/.github/workflows/build-abi.yml |
33 | @@ -29,6 +29,7 @@ jobs: |
34 | - name: Install build depends |
35 | run: | |
36 | echo "APT::Get::Always-Include-Phased-Updates \"true\";" | sudo tee /etc/apt/apt.conf.d/90phased-updates |
37 | + sudo add-apt-repository -y -u -s ppa:slyon/netplan-ci |
38 | sudo apt update |
39 | sudo apt install abigail-tools ubuntu-dev-tools devscripts equivs |
40 | pull-lp-source netplan.io |
41 | diff --git a/.github/workflows/check-address-sanitizer.yml b/.github/workflows/check-address-sanitizer.yml |
42 | index 6d97a52..e604a53 100644 |
43 | --- a/.github/workflows/check-address-sanitizer.yml |
44 | +++ b/.github/workflows/check-address-sanitizer.yml |
45 | @@ -25,6 +25,7 @@ jobs: |
46 | - name: Install build depends |
47 | run: | |
48 | echo "APT::Get::Always-Include-Phased-Updates \"true\";" | sudo tee /etc/apt/apt.conf.d/90phased-updates |
49 | + sudo add-apt-repository -y -u -s ppa:slyon/netplan-ci |
50 | sudo apt update |
51 | sudo apt -y install curl expect ubuntu-dev-tools devscripts equivs |
52 | pull-lp-source netplan.io |
53 | diff --git a/.github/workflows/check-coverage.yml b/.github/workflows/check-coverage.yml |
54 | index c8caca7..d18f8b4 100644 |
55 | --- a/.github/workflows/check-coverage.yml |
56 | +++ b/.github/workflows/check-coverage.yml |
57 | @@ -26,6 +26,7 @@ jobs: |
58 | - name: Install build depends |
59 | run: | |
60 | echo "APT::Get::Always-Include-Phased-Updates \"true\";" | sudo tee /etc/apt/apt.conf.d/90phased-updates |
61 | + sudo add-apt-repository -y -u -s ppa:slyon/netplan-ci |
62 | sudo apt update |
63 | sudo apt install curl expect ubuntu-dev-tools devscripts equivs gcovr |
64 | pull-lp-source netplan.io |
65 | diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml |
66 | index 330652f..f654f30 100644 |
67 | --- a/.github/workflows/coverity.yml |
68 | +++ b/.github/workflows/coverity.yml |
69 | @@ -14,6 +14,7 @@ jobs: |
70 | - name: Install dependencies |
71 | run: | |
72 | echo "APT::Get::Always-Include-Phased-Updates \"true\";" | sudo tee /etc/apt/apt.conf.d/90phased-updates |
73 | + sudo add-apt-repository -y -u -s ppa:slyon/netplan-ci |
74 | sudo apt update |
75 | sudo apt -y install curl ubuntu-dev-tools equivs |
76 | pull-lp-source netplan.io |
77 | diff --git a/.github/workflows/debci.yml b/.github/workflows/debci.yml |
78 | index f20e908..8e9dd5b 100644 |
79 | --- a/.github/workflows/debci.yml |
80 | +++ b/.github/workflows/debci.yml |
81 | @@ -45,7 +45,7 @@ jobs: |
82 | - name: Prepare test |
83 | run: | |
84 | # pull-debian-source netplan.io # snapshot.debian.org is not up-to-date |
85 | - V=$(rmadison -u debian -s unstable netplan.io | tail -n1 | cut -d"|" -f2 | xargs) |
86 | + V=$(rmadison -u debian -s unstable -a source netplan.io | tail -n1 | cut -d"|" -f2 | xargs) |
87 | dget -u "https://deb.debian.org/debian/pool/main/n/netplan.io/netplan.io_$V.dsc" |
88 | cp -r netplan.io-*/debian . |
89 | rm -r debian/patches/ # clear any distro patches |
90 | diff --git a/.github/workflows/snapd.patch b/.github/workflows/snapd.patch |
91 | index 437e1f7..de964c3 100644 |
92 | --- a/.github/workflows/snapd.patch |
93 | +++ b/.github/workflows/snapd.patch |
94 | @@ -1,9 +1,9 @@ |
95 | @@ -70,6 +70,8 @@ |
96 | |
97 | sleep 5 |
98 | - if lxc exec "$CONTAINER" -- systemctl mask serial-getty@getty.service; then |
99 | -+ lxc exec "$CONTAINER" -- systemctl mask snapd.service |
100 | -+ lxc exec "$CONTAINER" -- systemctl mask snapd.seeded.service |
101 | - lxc exec "$CONTAINER" -- reboot |
102 | + if "$COMMAND" exec "$CONTAINER" -- systemctl mask serial-getty@getty.service; then |
103 | ++ "$COMMAND" exec "$CONTAINER" -- systemctl mask snapd.service |
104 | ++ "$COMMAND" exec "$CONTAINER" -- systemctl mask snapd.seeded.service |
105 | + "$COMMAND" exec "$CONTAINER" -- reboot |
106 | fi |
107 | |
108 | diff --git a/.github/workflows/spread.yml b/.github/workflows/spread.yml |
109 | index c1593f5..a681057 100644 |
110 | --- a/.github/workflows/spread.yml |
111 | +++ b/.github/workflows/spread.yml |
112 | @@ -18,7 +18,11 @@ jobs: |
113 | - uses: actions/checkout@v3 |
114 | - name: Install spread |
115 | run: | |
116 | - go install github.com/snapcore/spread/cmd/spread@latest |
117 | + # TODO: Once Spread PR #179 is merged, go back to: |
118 | + # go install github.com/snapcore/spread/cmd/spread@latest |
119 | + git clone --depth 1 --branch thp https://github.com/thp-canonical/spread spread-fork |
120 | + cd spread-fork && go install ./cmd/spread |
121 | + cd .. && rm -r spread-fork |
122 | - name: Run the spread test inside LXD |
123 | run: | |
124 | ~/go/bin/spread -v lxd: |
125 | diff --git a/README.md b/README.md |
126 | index 6051ace..68d9e6e 100644 |
127 | --- a/README.md |
128 | +++ b/README.md |
129 | @@ -1,4 +1,4 @@ |
130 | -# netplan - Backend-agnostic network configuration in YAML |
131 | +# Netplan - Declarative network configuration for various backends |
132 | |
133 | [![Build+ABI](https://github.com/canonical/netplan/workflows/Build%20&%20ABI%20compatibility/badge.svg?branch=main)](https://github.com/canonical/netplan/actions/workflows/build-abi.yml?query=branch%3Amain) |
134 | [![Test+Coverage](https://github.com/canonical/netplan/workflows/Unit%20tests%20&%20Coverage/badge.svg?branch=main)](https://github.com/canonical/netplan/actions/workflows/check-coverage.yml?query=branch%3Amain) |
135 | @@ -13,7 +13,7 @@ http://netplan.io |
136 | |
137 | An overview of the architecture can be found at [netplan.io/design](https://netplan.io/design) |
138 | |
139 | -The full documentation for netplan is available in the [doc/](../main/doc/) directory. |
140 | +Find the full [documentation for Netplan](https://netplan.readthedocs.io) on "Read the Docs". |
141 | |
142 | # Build using Meson |
143 | |
144 | @@ -30,7 +30,7 @@ Please file bug reports in [Launchpad](https://bugs.launchpad.net/netplan/+fileb |
145 | |
146 | # Contact us |
147 | |
148 | -Please join us on IRC in #netplan at [Libera.Chat](https://libera.chat/). |
149 | +Please join us on [IRC in #netplan](https://web.libera.chat/gamja/?channels=%23netplan) at Libera.Chat. |
150 | |
151 | Our mailing list is [here](https://lists.launchpad.net/netplan-developers/). |
152 | |
153 | diff --git a/debian/changelog b/debian/changelog |
154 | index a79d91d..743bab0 100644 |
155 | --- a/debian/changelog |
156 | +++ b/debian/changelog |
157 | @@ -1,3 +1,76 @@ |
158 | +netplan.io (1.0.1-1ubuntu2~24.04.1) noble; urgency=medium |
159 | + |
160 | + * Backport netplan.io 1.0.1-1ubuntu2 to 24.04 (LP: #2074197): |
161 | + - sriov: accept setting the eswitch mode without VFs (LP: 2020409) |
162 | + - cli/sriov: refactoring |
163 | + - tests: use proper 0o600 file permissions in more places |
164 | + - doc: Adding missing 'watchfiles' dependency for Sphinx |
165 | + - doc: Minor fixes in lang. and mark-up in YAML reference |
166 | + - doc: Tutorial reorg & lang. + formatting improvements |
167 | + - networkd: add wait-online enumeration utils |
168 | + - generate: enable systemd-networkd-wait-online for non-optional interfaces |
169 | + - CLI:utils: Do not ask for daemon-reload password interactively |
170 | + - CLI:generate: call daemon-reload after (re-)generating services |
171 | + - wait-online: Do not block on loopback interface |
172 | + - generate: Do not touch wait-online, if we don't have any networkd NetDefs |
173 | + - wait-online: wait for existing interfaces only and downgrade operational |
174 | + state for interfaces without IP configuration |
175 | + - wait-online: account for DHCPv4/v6 addresses |
176 | + - wait-online: do not require virtual devices to be created already |
177 | + - wait-online: recognize that bridge/bond members will never gain |
178 | + link-local addresses |
179 | + - networkd:apply: Drop handling of legacy wpa@ instance units |
180 | + - wait-online: disabled wait-online for stable 1.0 |
181 | + - test:integration: Try to improve test flakyness |
182 | + - autopkgtest: More fixes for flaky 'ethernets' test |
183 | + - Increase some test timeouts to account for slow (riscv64) buildds |
184 | + SECURITY UPDATE: |
185 | + - libnetplan: use more restrictive file permissions |
186 | + (Closes: #1072789, LP: 2065738, LP: 1987842) |
187 | + - CVE-2022-4968 |
188 | + - libnetplan: escape control characters |
189 | + - backends: escape file paths |
190 | + - backends: escape semicolons in service units (LP: 2066258) |
191 | + Bug fixes: |
192 | + - cli: Fix logging setup when python-rich is not present |
193 | + - CI: fix DebCI case for no-change rebuilds |
194 | + - CI: adopt autopkgtest for 1.0-1 on 22.04 |
195 | + - doc: Update README, move CODE_OF_CONDUCT |
196 | + - doc: fix en_GB spelling |
197 | + - CI: adopt snapd.patch for autopkgtest SRU (LP: 2051939) |
198 | + - parse-nm: add a workaround for the DoT DNS option (LP: 2055148) |
199 | + - CI: Install netplan-ci PPA |
200 | + - parse: don't remove datalist items during iteration |
201 | + - ATTN: parse/bonds: handle same primary in multiple bonds |
202 | + - parse/bonds: don't fail on primary reassignment |
203 | + - cli/sriov: set eswitch regardless of pcidev.vfs |
204 | + - doc: Fix wrong bonds.parameters.mode syntax in example |
205 | + - parse: fix redefinition of gateway(4|6) |
206 | + - doc:tutorial: fix whitespace formatting |
207 | + - util: fix potential NULL pointer assert |
208 | + - python: elements of __all__ must be strings |
209 | + - tests: fix diff test with iproute2 6.8 |
210 | + - cli/generate: skip daemon_reload with --mapping |
211 | + - test: cleanup after wait_online test to fix DebCI |
212 | + - CI: fork spread to get !179 fixes |
213 | + - doc: Fix netplan-generate.md formatting !483 |
214 | + - emitter: allow unicode characters in the emitter (LP: 2071652) |
215 | + - parse: do not escape all non-ascii bytes |
216 | + * d/t/control: 'diff' autopkgtest is not flaky anymore |
217 | + * d/patches: Drop patches, applied upstream |
218 | + * d/p/0002-CLI-apply-call-udevadm-trigger-using-action-add-Clos.patch: |
219 | + Update 'udevadm trigger' patch, using MOVE action (LP: 2071363) |
220 | + * d/p/0003-Revert-wait-online-disabled-wait-online-for-stable-1.patch: |
221 | + Fix wait-online via s-n-wait-online.service.d/10-netplan. |
222 | + * debian/netplan-generator.postinst: Add a postinst maintainer script to call |
223 | + the generator, so the file permissions fixes will be applied automatically. |
224 | + * d/libnetplan1.symbols: |
225 | + - Update for new internal wait-online symbol |
226 | + - Update for new (private) symbol |
227 | + * d/copyright: Update for 2024 |
228 | + |
229 | + -- Danilo Egea Gondolfo <danilo.egea.gondolfo@canonical.com> Wed, 17 Jul 2024 18:12:26 +0100 |
230 | + |
231 | netplan.io (1.0-2ubuntu1.2) noble-security; urgency=medium |
232 | |
233 | * SECURITY REGRESSION: failure on systems without dbus |
234 | diff --git a/debian/control b/debian/control |
235 | index 86d1d90..fc6bf78 100644 |
236 | --- a/debian/control |
237 | +++ b/debian/control |
238 | @@ -27,6 +27,7 @@ Build-Depends: |
239 | libcmocka-dev, |
240 | libpython3-dev, |
241 | libsystemd-dev, |
242 | + udev <!nocheck>, |
243 | systemd <!nocheck>, |
244 | systemd-dev, |
245 | dbus-x11 <!nocheck>, |
246 | diff --git a/debian/copyright b/debian/copyright |
247 | index f145c74..229f7e4 100644 |
248 | --- a/debian/copyright |
249 | +++ b/debian/copyright |
250 | @@ -9,7 +9,7 @@ License: GPL-3 |
251 | |
252 | Files: debian/* |
253 | Copyright: |
254 | - 2016—2023 Canonical Ltd. |
255 | + 2016—2024 Canonical Ltd. |
256 | 2018—2023 Andrej Shadura <andrewsh@debian.org> |
257 | License: GPL-3 |
258 | |
259 | diff --git a/debian/patches/0002-CLI-apply-call-udevadm-trigger-using-action-add-Clos.patch b/debian/patches/0002-CLI-apply-call-udevadm-trigger-using-action-add-Clos.patch |
260 | new file mode 100644 |
261 | index 0000000..dda68ca |
262 | --- /dev/null |
263 | +++ b/debian/patches/0002-CLI-apply-call-udevadm-trigger-using-action-add-Clos.patch |
264 | @@ -0,0 +1,72 @@ |
265 | +From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
266 | +Date: Wed, 29 May 2024 17:41:28 +0200 |
267 | +Subject: CLI:apply: call udevadm trigger, |
268 | + using --action=add (Closes: #1071220) (LP: #2066344) |
269 | + |
270 | +This will (re)-apply .link files, e.g. to set offloading options |
271 | + |
272 | +Also, containing a drive-by fix, solving FTBFS on unstable (probably /tmp on tmpfs?) |
273 | + |
274 | +Bug: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1071220 |
275 | +Bug-Ubuntu: https://pad.lv/2066344 |
276 | +Origin: https://github.com/canonical/netplan/pull/479 |
277 | +Forwarded: not-needed |
278 | +--- |
279 | + netplan_cli/cli/commands/apply.py | 3 +-- |
280 | + tests/netplan_dbus/test_dbus.py | 8 ++++---- |
281 | + 2 files changed, 5 insertions(+), 5 deletions(-) |
282 | + |
283 | +diff --git a/netplan_cli/cli/commands/apply.py b/netplan_cli/cli/commands/apply.py |
284 | +index 9c88a3c..80c09e7 100644 |
285 | +--- a/netplan_cli/cli/commands/apply.py |
286 | ++++ b/netplan_cli/cli/commands/apply.py |
287 | +@@ -254,8 +254,7 @@ def command_apply(self, run_generate=True, sync=False, exit_on_error=True, state |
288 | + |
289 | + # Reloading of udev rules happens during 'netplan generate' already |
290 | + # subprocess.check_call(['udevadm', 'control', '--reload-rules']) |
291 | +- subprocess.check_call(['udevadm', 'trigger', '--attr-match=subsystem=net']) |
292 | +- subprocess.check_call(['udevadm', 'settle']) |
293 | ++ subprocess.check_call(['udevadm', 'trigger', '--action=move', '--subsystem-match=net', '--settle']) |
294 | + |
295 | + # apply any SR-IOV related changes, if applicable |
296 | + NetplanApply.process_sriov_config(config_manager, exit_on_error) |
297 | +diff --git a/tests/netplan_dbus/test_dbus.py b/tests/netplan_dbus/test_dbus.py |
298 | +index fdfa92f..9c08af7 100644 |
299 | +--- a/tests/netplan_dbus/test_dbus.py |
300 | ++++ b/tests/netplan_dbus/test_dbus.py |
301 | +@@ -48,7 +48,7 @@ class TestNetplanDBus(unittest.TestCase): |
302 | + ethernets: |
303 | + eth0: |
304 | + dhcp4: true""") |
305 | +- self.addCleanup(shutil.rmtree, self.tmp) |
306 | ++ self.addCleanup(shutil.rmtree, self.tmp, ignore_errors=True) |
307 | + self.mock_netplan_cmd = MockCmd("netplan") |
308 | + self._create_mock_system_bus() |
309 | + self._run_netplan_dbus_on_mock_bus() |
310 | +@@ -284,7 +284,7 @@ class TestNetplanDBus(unittest.TestCase): |
311 | + |
312 | + cid = self._new_config_object() |
313 | + tmpdir = self.tmp + '/run/netplan/config-{}'.format(cid) |
314 | +- self.addClassCleanup(shutil.rmtree, tmpdir) |
315 | ++ self.addClassCleanup(shutil.rmtree, tmpdir, ignore_errors=True) |
316 | + |
317 | + # Verify the object path has been created, by calling .Config.Get() on that object |
318 | + # it would throw an error if it does not exist |
319 | +@@ -319,7 +319,7 @@ class TestNetplanDBus(unittest.TestCase): |
320 | + def test_netplan_dbus_config_set(self): |
321 | + cid = self._new_config_object() |
322 | + tmpdir = self.tmp + '/run/netplan/config-{}'.format(cid) |
323 | +- self.addCleanup(shutil.rmtree, tmpdir) |
324 | ++ self.addCleanup(shutil.rmtree, tmpdir, ignore_errors=True) |
325 | + |
326 | + # Verify .Config.Set() on the config object |
327 | + # No actual YAML file will be created, as the netplan command is mocked |
328 | +@@ -340,7 +340,7 @@ class TestNetplanDBus(unittest.TestCase): |
329 | + def test_netplan_dbus_config_get(self): |
330 | + cid = self._new_config_object() |
331 | + tmpdir = self.tmp + '/run/netplan/config-{}'.format(cid) |
332 | +- self.addCleanup(shutil.rmtree, tmpdir) |
333 | ++ self.addCleanup(shutil.rmtree, tmpdir, ignore_errors=True) |
334 | + |
335 | + # Verify .Config.Get() on the config object |
336 | + self.mock_netplan_cmd.set_output("network:\n eth42:\n dhcp6: true") |
337 | diff --git a/debian/patches/0002-parse-nm-add-a-workaround-for-the-DoT-DNS-option.patch b/debian/patches/0002-parse-nm-add-a-workaround-for-the-DoT-DNS-option.patch |
338 | deleted file mode 100644 |
339 | index 9732eed..0000000 |
340 | --- a/debian/patches/0002-parse-nm-add-a-workaround-for-the-DoT-DNS-option.patch |
341 | +++ /dev/null |
342 | @@ -1,91 +0,0 @@ |
343 | -From: Danilo Egea Gondolfo <danilogondolfo@gmail.com> |
344 | -Date: Mon, 25 Mar 2024 11:35:13 +0000 |
345 | -Subject: parse-nm: add a workaround for the DoT DNS option |
346 | - |
347 | -Netplan doesn't support DNS options such as SNI. When we parse a |
348 | -nameserver entry with the SNI server name, example: 1.2.3.4#name.domain, |
349 | -the string is added to the list of nameservers without proper |
350 | -parsing/validation. |
351 | - |
352 | -This is a workaround for LP: #2055148. |
353 | - |
354 | -Origin: upstream, https://github.com/canonical/netplan/pull/447 |
355 | -Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/netplan.io/+bug/2055148 |
356 | ---- |
357 | - src/parse-nm.c | 15 +++++++++++++++ |
358 | - tests/parser/test_keyfile.py | 39 +++++++++++++++++++++++++++++++++++++++ |
359 | - 2 files changed, 54 insertions(+) |
360 | - |
361 | -diff --git a/src/parse-nm.c b/src/parse-nm.c |
362 | -index 79275a0..e1b814b 100644 |
363 | ---- a/src/parse-nm.c |
364 | -+++ b/src/parse-nm.c |
365 | -@@ -348,6 +348,21 @@ parse_nameservers(GKeyFile* kf, const gchar* group, GArray** nameserver_arr) |
366 | - g_assert(nameserver_arr); |
367 | - gchar **split = g_key_file_get_string_list(kf, group, "dns", NULL, NULL); |
368 | - if (split) { |
369 | -+ |
370 | -+ /* Workaround for LP: #2055148 |
371 | -+ * When an SNI server name is appended to the DNS server IP address, |
372 | -+ * for example: 1.2.3.4#example.com, we skip the parsing and keep the configuration |
373 | -+ * in the passthrough section by checking if the string is a valid IP address. |
374 | -+ * TODO: implement proper DNS options for both NM and ND and drop this |
375 | -+ * workaround. |
376 | -+ */ |
377 | -+ for (unsigned i = 0; split[i]; ++i) { |
378 | -+ if (!is_ip4_address(split[i]) && !is_ip6_address(split[i])) { |
379 | -+ g_strfreev(split); |
380 | -+ return; |
381 | -+ } |
382 | -+ } |
383 | -+ |
384 | - if (!*nameserver_arr) |
385 | - *nameserver_arr = g_array_new(FALSE, FALSE, sizeof(char*)); |
386 | - for(unsigned i = 0; split[i]; ++i) { |
387 | -diff --git a/tests/parser/test_keyfile.py b/tests/parser/test_keyfile.py |
388 | -index 5dcec8e..ce5cdca 100644 |
389 | ---- a/tests/parser/test_keyfile.py |
390 | -+++ b/tests/parser/test_keyfile.py |
391 | -@@ -2355,3 +2355,42 @@ route1_options=table=1000 |
392 | - method=link-local\n'''.format(UUID), expect_fail=True) |
393 | - |
394 | - self.assertIn('missing \'table\' property', out) |
395 | -+ |
396 | -+ def test_nameserver_with_DoT_lp2055148(self): |
397 | -+ self.generate_from_keyfile('''[connection] |
398 | -+id=ethernet-eth123 |
399 | -+uuid={} |
400 | -+type=ethernet |
401 | -+interface-name=eth123 |
402 | -+ |
403 | -+[ethernet] |
404 | -+ |
405 | -+[ipv4] |
406 | -+dns=8.8.8.8;1.1.1.1#lxd;192.168.0.1#domain.local; |
407 | -+method=auto |
408 | -+ |
409 | -+[ipv6] |
410 | -+addr-gen-mode=default |
411 | -+method=auto |
412 | -+ |
413 | -+[proxy]\n'''.format(UUID)) |
414 | -+ self.assert_netplan({UUID: '''network: |
415 | -+ version: 2 |
416 | -+ ethernets: |
417 | -+ NM-{}: |
418 | -+ renderer: NetworkManager |
419 | -+ match: |
420 | -+ name: "eth123" |
421 | -+ dhcp4: true |
422 | -+ dhcp6: true |
423 | -+ wakeonlan: true |
424 | -+ networkmanager: |
425 | -+ uuid: "{}" |
426 | -+ name: "ethernet-eth123" |
427 | -+ passthrough: |
428 | -+ ethernet._: "" |
429 | -+ ipv4.dns: "8.8.8.8;1.1.1.1#lxd;192.168.0.1#domain.local;" |
430 | -+ ipv6.addr-gen-mode: "default" |
431 | -+ ipv6.ip6-privacy: "-1" |
432 | -+ proxy._: "" |
433 | -+'''.format(UUID, UUID)}) |
434 | diff --git a/debian/patches/0003-Revert-wait-online-disabled-wait-online-for-stable-1.patch b/debian/patches/0003-Revert-wait-online-disabled-wait-online-for-stable-1.patch |
435 | new file mode 100644 |
436 | index 0000000..8fa2663 |
437 | --- /dev/null |
438 | +++ b/debian/patches/0003-Revert-wait-online-disabled-wait-online-for-stable-1.patch |
439 | @@ -0,0 +1,155 @@ |
440 | +From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
441 | +Date: Thu, 4 Jul 2024 15:52:19 +0200 |
442 | +Subject: Revert "wait-online: disabled wait-online for stable 1.0" |
443 | + |
444 | +This reverts commit 933baebe3f203ac7fd24bb4747cf49afc998784a. |
445 | +--- |
446 | + src/generate.c | 10 +--------- |
447 | + src/networkd.c | 7 +------ |
448 | + tests/generator/test_args.py | 33 +++++++++++++++++++++++++++++++++ |
449 | + tests/integration/ethernets.py | 1 - |
450 | + 4 files changed, 35 insertions(+), 16 deletions(-) |
451 | + |
452 | +diff --git a/src/generate.c b/src/generate.c |
453 | +index f961126..d6edb0a 100644 |
454 | +--- a/src/generate.c |
455 | ++++ b/src/generate.c |
456 | +@@ -63,7 +63,7 @@ reload_udevd(void) |
457 | + * Create enablement symlink for systemd-networkd.service. |
458 | + */ |
459 | + static void |
460 | +-enable_networkd(const char* generator_dir, __unused gboolean enable_wait_online) |
461 | ++enable_networkd(const char* generator_dir, gboolean enable_wait_online) |
462 | + { |
463 | + g_autofree char* link = g_build_path(G_DIR_SEPARATOR_S, generator_dir, "multi-user.target.wants", "systemd-networkd.service", NULL); |
464 | + g_debug("We created networkd configuration, adding %s enablement symlink", link); |
465 | +@@ -75,9 +75,6 @@ enable_networkd(const char* generator_dir, __unused gboolean enable_wait_online) |
466 | + // LCOV_EXCL_STOP |
467 | + } |
468 | + |
469 | +- #if 0 |
470 | +- // Disabled for netplan 1.0 stable |
471 | +- // LCOV_EXCL_START |
472 | + if (enable_wait_online) { |
473 | + g_autofree char* link2 = g_build_path(G_DIR_SEPARATOR_S, generator_dir, "network-online.target.wants", "systemd-networkd-wait-online.service", NULL); |
474 | + _netplan_safe_mkdir_p_dir(link2); |
475 | +@@ -88,8 +85,6 @@ enable_networkd(const char* generator_dir, __unused gboolean enable_wait_online) |
476 | + // LCOV_EXCL_STOP |
477 | + } |
478 | + } |
479 | +- // LCOV_EXCL_STOP |
480 | +- #endif |
481 | + } |
482 | + |
483 | + // LCOV_EXCL_START |
484 | +@@ -335,11 +330,8 @@ int main(int argc, char** argv) |
485 | + /* covered via 'cloud-init' integration test */ |
486 | + if (any_networkd) { |
487 | + start_unit_jit("systemd-networkd.socket"); |
488 | +- #if 0 |
489 | +- // Disabled for netplan 1.0 stable |
490 | + if (enable_wait_online) |
491 | + start_unit_jit("systemd-networkd-wait-online.service"); |
492 | +- #endif |
493 | + start_unit_jit("systemd-networkd.service"); |
494 | + } |
495 | + g_autofree char* glob_run = g_build_path(G_DIR_SEPARATOR_S, |
496 | +diff --git a/src/networkd.c b/src/networkd.c |
497 | +index a6faeb2..b4d7b87 100644 |
498 | +--- a/src/networkd.c |
499 | ++++ b/src/networkd.c |
500 | +@@ -1551,15 +1551,10 @@ _netplan_networkd_write_wait_online(const NetplanState* np_state, const char* ro |
501 | + } |
502 | + g_string_append(content, "\n"); |
503 | + |
504 | +- g_string_free(content, TRUE); |
505 | +- #if 0 |
506 | +- // Disabled for netplan 1.0 stable |
507 | +- // LCOV_EXCL_START |
508 | + g_autofree char* new_content = _netplan_scrub_systemd_unit_contents(content->str); |
509 | ++ g_string_free(content, TRUE); |
510 | + content = g_string_new(new_content); |
511 | + _netplan_g_string_free_to_file_with_permissions(content, rootdir, override, NULL, "root", "root", 0640); |
512 | +- // LCOV_EXCL_STOP |
513 | +- #endif |
514 | + g_hash_table_destroy(non_optional_interfaces); |
515 | + return TRUE; |
516 | + } |
517 | +diff --git a/tests/generator/test_args.py b/tests/generator/test_args.py |
518 | +index 78a76b7..6909194 100644 |
519 | +--- a/tests/generator/test_args.py |
520 | ++++ b/tests/generator/test_args.py |
521 | +@@ -172,8 +172,21 @@ class TestConfigArgs(TestBase): |
522 | + os.unlink(n) |
523 | + |
524 | + # should auto-enable networkd and -wait-online |
525 | ++ service_dir = os.path.join(self.workdir.name, 'run', 'systemd', 'system') |
526 | + self.assertTrue(os.path.islink(os.path.join( |
527 | + outdir, 'multi-user.target.wants', 'systemd-networkd.service'))) |
528 | ++ self.assertTrue(os.path.islink(os.path.join( |
529 | ++ outdir, 'network-online.target.wants', 'systemd-networkd-wait-online.service'))) |
530 | ++ override = os.path.join(service_dir, 'systemd-networkd-wait-online.service.d', '10-netplan.conf') |
531 | ++ self.assertTrue(os.path.isfile(override)) |
532 | ++ with open(override, 'r') as f: |
533 | ++ # eth99 does not exist on the system, so will not be listed |
534 | ++ self.assertEqual(f.read(), '''[Unit] |
535 | ++ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/systemd-networkd-wait-online.service |
536 | ++ |
537 | ++[Service] |
538 | ++ExecStart= |
539 | ++ExecStart=/lib/systemd/systemd-networkd-wait-online -i eth99.43:degraded -i br0:degraded -i lo:carrier -i eth99.42:carrier\n''') |
540 | + |
541 | + # should be a no-op the second time while the stamp exists |
542 | + out = subprocess.check_output([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir], |
543 | +@@ -210,10 +223,17 @@ class TestConfigArgs(TestBase): |
544 | + os.unlink(n) |
545 | + |
546 | + # should auto-enable networkd but not -wait-online |
547 | ++ service_dir = os.path.join(self.workdir.name, 'run', 'systemd', 'system') |
548 | + self.assertTrue(os.path.islink(os.path.join( |
549 | + outdir, 'multi-user.target.wants', 'systemd-networkd.service'))) |
550 | + self.assertFalse(os.path.islink(os.path.join( |
551 | + outdir, 'network-online.target.wants', 'systemd-networkd-wait-online.service'))) |
552 | ++ override = os.path.join(service_dir, 'systemd-networkd-wait-online.service.d', '10-netplan.conf') |
553 | ++ self.assertTrue(os.path.isfile(override)) |
554 | ++ with open(override, 'r') as f: |
555 | ++ self.assertEqual(f.read(), '''[Unit] |
556 | ++ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/systemd-networkd-wait-online.service |
557 | ++''') |
558 | + |
559 | + def test_systemd_generator_noconf(self): |
560 | + outdir = os.path.join(self.workdir.name, 'out') |
561 | +@@ -269,8 +289,21 @@ class TestConfigArgs(TestBase): |
562 | + os.unlink(n) |
563 | + |
564 | + # should auto-enable networkd and -wait-online |
565 | ++ service_dir = os.path.join(self.workdir.name, 'run', 'systemd', 'system') |
566 | + self.assertTrue(os.path.islink(os.path.join( |
567 | + outdir, 'multi-user.target.wants', 'systemd-networkd.service'))) |
568 | ++ self.assertTrue(os.path.islink(os.path.join( |
569 | ++ outdir, 'network-online.target.wants', 'systemd-networkd-wait-online.service'))) |
570 | ++ override = os.path.join(service_dir, 'systemd-networkd-wait-online.service.d', '10-netplan.conf') |
571 | ++ self.assertTrue(os.path.isfile(override)) |
572 | ++ with open(override, 'r') as f: |
573 | ++ # eth99 does not exist on the system, so will not be listed |
574 | ++ self.assertEqual(f.read(), '''[Unit] |
575 | ++ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/systemd-networkd-wait-online.service |
576 | ++ |
577 | ++[Service] |
578 | ++ExecStart= |
579 | ++ExecStart=/lib/systemd/systemd-networkd-wait-online -i a \\; b\\t; c\\t; d \\n 123 \\; echo :degraded\n''') |
580 | + |
581 | + # should be a no-op the second time while the stamp exists |
582 | + out = subprocess.check_output([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir], |
583 | +diff --git a/tests/integration/ethernets.py b/tests/integration/ethernets.py |
584 | +index c42e8b5..4ac7fe7 100644 |
585 | +--- a/tests/integration/ethernets.py |
586 | ++++ b/tests/integration/ethernets.py |
587 | +@@ -391,7 +391,6 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests): |
588 | + ['inet6 9876:bbbb::11/70', 'inet 172.16.5.3/20'], |
589 | + ['inet6 fe80:', 'inet 169.254.']) |
590 | + |
591 | +- @unittest.skip("networkd wait-online is disabled for netplan 1.0 stable") |
592 | + def test_systemd_networkd_wait_online(self): |
593 | + self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'br0']) |
594 | + self.setup_eth(None, False) |
595 | diff --git a/debian/patches/0004-generate-avoid-calling-udevadm-control-reload-LP-199.patch b/debian/patches/0004-generate-avoid-calling-udevadm-control-reload-LP-199.patch |
596 | new file mode 100644 |
597 | index 0000000..8e5bb87 |
598 | --- /dev/null |
599 | +++ b/debian/patches/0004-generate-avoid-calling-udevadm-control-reload-LP-199.patch |
600 | @@ -0,0 +1,82 @@ |
601 | +From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
602 | +Date: Thu, 4 Jul 2024 12:50:53 +0200 |
603 | +Subject: generate: avoid calling 'udevadm control --reload' (LP: #1999178) |
604 | + |
605 | +The udev rules directories are monitored and re-loaded automatically with |
606 | +modern systemd-udevd. No need to manually reload them in the generator, |
607 | +causing side-effects. |
608 | + |
609 | +We can still force-reload them when issuing 'netplan generate' or |
610 | +'netplan apply' through the CLI, just to be on the safe side. |
611 | + |
612 | +Replaces: https://github.com/canonical/netplan/pull/304 |
613 | + |
614 | +Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/1999178 |
615 | +Origin: https://github.com/canonical/netplan/pull/488 |
616 | +--- |
617 | + netplan_cli/cli/commands/apply.py | 3 +-- |
618 | + netplan_cli/cli/commands/generate.py | 4 ++++ |
619 | + src/generate.c | 14 -------------- |
620 | + 3 files changed, 5 insertions(+), 16 deletions(-) |
621 | + |
622 | +diff --git a/netplan_cli/cli/commands/apply.py b/netplan_cli/cli/commands/apply.py |
623 | +index cfcaf26..a4e744f 100644 |
624 | +--- a/netplan_cli/cli/commands/apply.py |
625 | ++++ b/netplan_cli/cli/commands/apply.py |
626 | +@@ -252,8 +252,7 @@ class NetplanApply(utils.NetplanCommand): |
627 | + stdout=subprocess.DEVNULL, |
628 | + stderr=subprocess.DEVNULL) |
629 | + |
630 | +- # Reloading of udev rules happens during 'netplan generate' already |
631 | +- # subprocess.check_call(['udevadm', 'control', '--reload-rules']) |
632 | ++ subprocess.check_call(['udevadm', 'control', '--reload']) |
633 | + subprocess.check_call(['udevadm', 'trigger', '--action=move', '--subsystem-match=net', '--settle']) |
634 | + |
635 | + # apply any SR-IOV related changes, if applicable |
636 | +diff --git a/netplan_cli/cli/commands/generate.py b/netplan_cli/cli/commands/generate.py |
637 | +index ba51eb0..889411d 100644 |
638 | +--- a/netplan_cli/cli/commands/generate.py |
639 | ++++ b/netplan_cli/cli/commands/generate.py |
640 | +@@ -82,6 +82,10 @@ class NetplanGenerate(utils.NetplanCommand): |
641 | + argv += ['--mapping', self.mapping] |
642 | + logging.debug('command generate: running %s', argv) |
643 | + res = subprocess.call(argv) |
644 | ++ try: |
645 | ++ subprocess.check_call(['udevadm', 'control', '--reload']) |
646 | ++ except subprocess.CalledProcessError as e: |
647 | ++ logging.debug(f'Could not call "udevadm control --reload": {str(e)}') |
648 | + # reload systemd, as we might have changed service units, such as |
649 | + # /run/systemd/system/systemd-networkd-wait-online.service.d/10-netplan.conf |
650 | + # Skip it if --mapping is used as nothing will be generated |
651 | +diff --git a/src/generate.c b/src/generate.c |
652 | +index d6edb0a..ba02a32 100644 |
653 | +--- a/src/generate.c |
654 | ++++ b/src/generate.c |
655 | +@@ -52,13 +52,6 @@ static GOptionEntry options[] = { |
656 | + {NULL} |
657 | + }; |
658 | + |
659 | +-static void |
660 | +-reload_udevd(void) |
661 | +-{ |
662 | +- const gchar *argv[] = { "/bin/udevadm", "control", "--reload", NULL }; |
663 | +- g_spawn_sync(NULL, (gchar**)argv, NULL, G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, NULL, NULL, NULL, NULL); |
664 | +-}; |
665 | +- |
666 | + /** |
667 | + * Create enablement symlink for systemd-networkd.service. |
668 | + */ |
669 | +@@ -288,13 +281,6 @@ int main(int argc, char** argv) |
670 | + |
671 | + CHECK_CALL(netplan_state_finish_nm_write(np_state, rootdir, &error)); |
672 | + CHECK_CALL(netplan_state_finish_sriov_write(np_state, rootdir, &error)); |
673 | +- /* We may have written .rules & .link files, thus we must |
674 | +- * invalidate udevd cache of its config as by default it only |
675 | +- * invalidates cache at most every 3 seconds. Not sure if this |
676 | +- * should live in `generate' or `apply', but it is confusing |
677 | +- * when udevd ignores just-in-time created rules files. |
678 | +- */ |
679 | +- reload_udevd(); |
680 | + } |
681 | + |
682 | + /* Disable /usr/lib/NetworkManager/conf.d/10-globally-managed-devices.conf |
683 | diff --git a/debian/patches/lp2060311/0003-util-fix-potential-NULL-pointer-assert.patch b/debian/patches/lp2060311/0003-util-fix-potential-NULL-pointer-assert.patch |
684 | deleted file mode 100644 |
685 | index 9110a71..0000000 |
686 | --- a/debian/patches/lp2060311/0003-util-fix-potential-NULL-pointer-assert.patch |
687 | +++ /dev/null |
688 | @@ -1,24 +0,0 @@ |
689 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
690 | -Date: Tue, 16 Apr 2024 09:50:51 +0200 |
691 | -Subject: util: fix potential NULL pointer assert |
692 | - |
693 | -Origin: https://github.com/canonical/netplan/pull/456/commits/e56a3dcedbf82d5154ad9cc87ef27a375fdfb9bb |
694 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
695 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
696 | ---- |
697 | - src/util.c | 2 +- |
698 | - 1 file changed, 1 insertion(+), 1 deletion(-) |
699 | - |
700 | -diff --git a/src/util.c b/src/util.c |
701 | -index 36eb896..3e6c80d 100644 |
702 | ---- a/src/util.c |
703 | -+++ b/src/util.c |
704 | -@@ -955,7 +955,7 @@ netplan_netdef_match_interface(const NetplanNetDefinition* netdef, const char* n |
705 | - return !g_strcmp0(name, netdef->id); |
706 | - |
707 | - if (netdef->match.mac) { |
708 | -- if (g_ascii_strcasecmp(netdef->match.mac, mac)) |
709 | -+ if (g_ascii_strcasecmp(netdef->match.mac ?: "", mac ?: "")) |
710 | - return FALSE; |
711 | - } |
712 | - |
713 | diff --git a/debian/patches/lp2060311/0004-networkd-add-wait-online-enumeration-utils.patch b/debian/patches/lp2060311/0004-networkd-add-wait-online-enumeration-utils.patch |
714 | deleted file mode 100644 |
715 | index d842be6..0000000 |
716 | --- a/debian/patches/lp2060311/0004-networkd-add-wait-online-enumeration-utils.patch |
717 | +++ /dev/null |
718 | @@ -1,189 +0,0 @@ |
719 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
720 | -Date: Tue, 16 Apr 2024 09:30:34 +0200 |
721 | -Subject: networkd: add wait-online enumeration utils |
722 | - |
723 | -Origin: https://github.com/canonical/netplan/pull/456/commits/4bc25c9ae24b6a6a7e81b92ae901cb7fef4c30df |
724 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
725 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
726 | ---- |
727 | - src/networkd.c | 65 +++++++++++++++++++++++++++++++ |
728 | - tests/ctests/meson.build | 1 + |
729 | - tests/ctests/test_netplan_networkd.c | 74 ++++++++++++++++++++++++++++++++++++ |
730 | - 3 files changed, 140 insertions(+) |
731 | - create mode 100644 tests/ctests/test_netplan_networkd.c |
732 | - |
733 | -diff --git a/src/networkd.c b/src/networkd.c |
734 | -index 25121c4..9289388 100644 |
735 | ---- a/src/networkd.c |
736 | -+++ b/src/networkd.c |
737 | -@@ -20,6 +20,7 @@ |
738 | - #include <unistd.h> |
739 | - #include <ctype.h> |
740 | - #include <errno.h> |
741 | -+#include <net/if.h> |
742 | - #include <sys/stat.h> |
743 | - |
744 | - #include <glib.h> |
745 | -@@ -32,6 +33,70 @@ |
746 | - #include "util-internal.h" |
747 | - #include "validation.h" |
748 | - |
749 | -+/** |
750 | -+ * Query sysfs for the MAC address (up to 20 bytes for infiniband) of @ifname |
751 | -+ * The caller owns the returned string and needs to free it. |
752 | -+ */ |
753 | -+STATIC char* |
754 | -+_netplan_sysfs_get_mac_by_ifname(const char* ifname, const char* rootdir) |
755 | -+{ |
756 | -+ g_autofree gchar* content = NULL; |
757 | -+ g_autofree gchar* sysfs_path = NULL; |
758 | -+ sysfs_path = g_build_path(G_DIR_SEPARATOR_S, rootdir ?: G_DIR_SEPARATOR_S, |
759 | -+ "sys", "class", "net", ifname, "address", NULL); |
760 | -+ |
761 | -+ if (!g_file_get_contents (sysfs_path, &content, NULL, NULL)) { |
762 | -+ g_debug("%s: Cannot read file contents.", __FUNCTION__); |
763 | -+ return NULL; |
764 | -+ } |
765 | -+ |
766 | -+ // Trim whitespace & clone value |
767 | -+ return g_strdup(g_strstrip(content)); |
768 | -+} |
769 | -+ |
770 | -+/** |
771 | -+ * Query sysfs for the driver used by @ifname |
772 | -+ * The caller owns the returned string and needs to free it. |
773 | -+ */ |
774 | -+STATIC char* |
775 | -+_netplan_sysfs_get_driver_by_ifname(const char* ifname, const char* rootdir) |
776 | -+{ |
777 | -+ g_autofree gchar* link = NULL; |
778 | -+ g_autofree gchar* sysfs_path = NULL; |
779 | -+ sysfs_path = g_build_path(G_DIR_SEPARATOR_S, rootdir ?: G_DIR_SEPARATOR_S, |
780 | -+ "sys", "class", "net", ifname, "device", "driver", NULL); |
781 | -+ |
782 | -+ link = g_file_read_link(sysfs_path, NULL); |
783 | -+ if (!link) { |
784 | -+ g_debug("%s: Cannot read symlink of %s.", __FUNCTION__, sysfs_path); |
785 | -+ return NULL; |
786 | -+ } |
787 | -+ |
788 | -+ return g_path_get_basename(link); |
789 | -+} |
790 | -+ |
791 | -+/** |
792 | -+ * Enumerate all network interfaces (/sys/clas/net/...) and check |
793 | -+ * netplan_netdef_match_interface() to see if they match the current NetDef |
794 | -+ */ |
795 | -+STATIC void |
796 | -+_netplan_enumerate_interfaces(const NetplanNetDefinition* def, GHashTable* tbl, const char* rootdir) |
797 | -+{ |
798 | -+ g_assert(tbl); |
799 | -+ struct if_nameindex *if_nidxs, *intf; |
800 | -+ if_nidxs = if_nameindex(); |
801 | -+ if (if_nidxs != NULL) { |
802 | -+ for (intf = if_nidxs; intf->if_index != 0 || intf->if_name != NULL; intf++) { |
803 | -+ if (g_hash_table_contains(tbl, intf->if_name)) continue; |
804 | -+ g_autofree gchar* mac = _netplan_sysfs_get_mac_by_ifname(intf->if_name, rootdir); |
805 | -+ g_autofree gchar* driver = _netplan_sysfs_get_driver_by_ifname(intf->if_name, rootdir); |
806 | -+ if (netplan_netdef_match_interface(def, intf->if_name, mac, driver)) |
807 | -+ g_hash_table_add(tbl, g_strdup(intf->if_name)); |
808 | -+ } |
809 | -+ if_freenameindex(if_nidxs); |
810 | -+ } |
811 | -+} |
812 | -+ |
813 | - /** |
814 | - * Append WiFi frequencies to wpa_supplicant's freq_list= |
815 | - */ |
816 | -diff --git a/tests/ctests/meson.build b/tests/ctests/meson.build |
817 | -index ed3bc5d..dcf4b6c 100644 |
818 | ---- a/tests/ctests/meson.build |
819 | -+++ b/tests/ctests/meson.build |
820 | -@@ -5,6 +5,7 @@ tests = { |
821 | - 'test_netplan_misc': false, |
822 | - 'test_netplan_validation': false, |
823 | - 'test_netplan_keyfile': false, |
824 | -+ 'test_netplan_networkd': false, |
825 | - 'test_netplan_nm': false, |
826 | - 'test_netplan_openvswitch': false, |
827 | - } |
828 | -diff --git a/tests/ctests/test_netplan_networkd.c b/tests/ctests/test_netplan_networkd.c |
829 | -new file mode 100644 |
830 | -index 0000000..e9e4b79 |
831 | ---- /dev/null |
832 | -+++ b/tests/ctests/test_netplan_networkd.c |
833 | -@@ -0,0 +1,74 @@ |
834 | -+#include <stdio.h> |
835 | -+#include <stdarg.h> |
836 | -+#include <stddef.h> |
837 | -+#include <setjmp.h> |
838 | -+ |
839 | -+#include <cmocka.h> |
840 | -+ |
841 | -+#include "netplan.h" |
842 | -+#include "util-internal.h" |
843 | -+#include "networkd.c" |
844 | -+ |
845 | -+#include "test_utils.h" |
846 | -+ |
847 | -+void |
848 | -+test_wait_online_utils(__unused void** state) |
849 | -+{ |
850 | -+ char template[] = "/tmp/netplan.XXXXXX"; |
851 | -+ const char* rootdir = mkdtemp(template); |
852 | -+ |
853 | -+ // create mock sysfs |
854 | -+ g_autofree gchar* sys = g_strdup_printf("%s/sys", rootdir); |
855 | -+ g_autofree gchar* sys_class = g_strdup_printf("%s/sys/class", rootdir); |
856 | -+ g_autofree gchar* sys_class_net = g_strdup_printf("%s/sys/class/net", rootdir); |
857 | -+ g_autofree gchar* eth99 = g_strdup_printf("%s/sys/class/net/eth99", rootdir); |
858 | -+ g_autofree gchar* eth99_device = g_strdup_printf("%s/device", eth99); |
859 | -+ g_autofree gchar* driver = g_strdup_printf("%s/device/driver", eth99); |
860 | -+ g_autofree gchar* mac = g_strdup_printf("%s/address", eth99); |
861 | -+ g_mkdir_with_parents(eth99_device, 0700); |
862 | -+ |
863 | -+ // assert MAC address file |
864 | -+ assert_true(g_file_set_contents(mac, " aa:bb:cc:dd:ee:ff \r\n\n", -1, NULL)); |
865 | -+ g_autofree gchar* mac_value = _netplan_sysfs_get_mac_by_ifname("eth99", rootdir); |
866 | -+ assert_string_equal(mac_value, "aa:bb:cc:dd:ee:ff"); |
867 | -+ |
868 | -+ // assert driver link |
869 | -+ assert_int_equal(symlink("../somewhere/drivers/mock_drv", driver), 0); |
870 | -+ g_autofree gchar* driver_value = _netplan_sysfs_get_driver_by_ifname("eth99", rootdir); |
871 | -+ assert_string_equal(driver_value, "mock_drv"); |
872 | -+ |
873 | -+ // Cleanup |
874 | -+ remove(mac); |
875 | -+ remove(driver); |
876 | -+ rmdir(eth99_device); |
877 | -+ rmdir(eth99); |
878 | -+ rmdir(sys_class_net); |
879 | -+ rmdir(sys_class); |
880 | -+ rmdir(sys); |
881 | -+ rmdir(rootdir); |
882 | -+} |
883 | -+ |
884 | -+ |
885 | -+int |
886 | -+setup(__unused void** state) |
887 | -+{ |
888 | -+ return 0; |
889 | -+} |
890 | -+ |
891 | -+int |
892 | -+tear_down(__unused void** state) |
893 | -+{ |
894 | -+ return 0; |
895 | -+} |
896 | -+ |
897 | -+int |
898 | -+main() |
899 | -+{ |
900 | -+ |
901 | -+ const struct CMUnitTest tests[] = { |
902 | -+ cmocka_unit_test(test_wait_online_utils), |
903 | -+ }; |
904 | -+ |
905 | -+ return cmocka_run_group_tests(tests, setup, tear_down); |
906 | -+ |
907 | -+} |
908 | diff --git a/debian/patches/lp2060311/0005-generate-enable-systemd-networkd-wait-online-for-non.patch b/debian/patches/lp2060311/0005-generate-enable-systemd-networkd-wait-online-for-non.patch |
909 | deleted file mode 100644 |
910 | index 573d344..0000000 |
911 | --- a/debian/patches/lp2060311/0005-generate-enable-systemd-networkd-wait-online-for-non.patch |
912 | +++ /dev/null |
913 | @@ -1,332 +0,0 @@ |
914 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
915 | -Date: Tue, 16 Apr 2024 09:51:40 +0200 |
916 | -Subject: generate: enable systemd-networkd-wait-online for non-optional |
917 | - interfaces only |
918 | - |
919 | -Skip s-n-wait-online if we don't have any non-optional interfaces, using a |
920 | -"ConditionPathIsSymbolicLink=" checking Netplan's s-n-wait-online.service |
921 | -enablement symlink. |
922 | - |
923 | -This is in favor to RequiredForOnline=yes as the behavior of upstream (pure) |
924 | -systemd-networkd-wait-online.service is not mean to be used in this way. |
925 | - |
926 | -If "RequiredForOnline=no" sd-networkd-wait-online will fully ignore the |
927 | -corresponding interface and it will block/delay network-online.target if |
928 | -no interfaces are "RequiredForOnline=yes" at all. |
929 | - |
930 | -FR-7246 |
931 | - |
932 | -Origin: https://github.com/canonical/netplan/pull/456/commits/494b2e41dd9e17b79502aa53b49fd92e82acf5c1 |
933 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
934 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
935 | ---- |
936 | - src/generate.c | 24 ++++++++------- |
937 | - src/networkd.c | 66 +++++++++++++++++++++++++++++++++++------- |
938 | - src/networkd.h | 3 ++ |
939 | - tests/generator/base.py | 2 ++ |
940 | - tests/generator/test_args.py | 56 +++++++++++++++++++++++++++++++++++ |
941 | - tests/generator/test_common.py | 5 ---- |
942 | - 6 files changed, 131 insertions(+), 25 deletions(-) |
943 | - |
944 | -diff --git a/src/generate.c b/src/generate.c |
945 | -index 35179a2..19198ff 100644 |
946 | ---- a/src/generate.c |
947 | -+++ b/src/generate.c |
948 | -@@ -63,7 +63,7 @@ reload_udevd(void) |
949 | - * Create enablement symlink for systemd-networkd.service. |
950 | - */ |
951 | - static void |
952 | --enable_networkd(const char* generator_dir) |
953 | -+enable_networkd(const char* generator_dir, gboolean enable_wait_online) |
954 | - { |
955 | - g_autofree char* link = g_build_path(G_DIR_SEPARATOR_S, generator_dir, "multi-user.target.wants", "systemd-networkd.service", NULL); |
956 | - g_debug("We created networkd configuration, adding %s enablement symlink", link); |
957 | -@@ -75,13 +75,15 @@ enable_networkd(const char* generator_dir) |
958 | - // LCOV_EXCL_STOP |
959 | - } |
960 | - |
961 | -- g_autofree char* link2 = g_build_path(G_DIR_SEPARATOR_S, generator_dir, "network-online.target.wants", "systemd-networkd-wait-online.service", NULL); |
962 | -- _netplan_safe_mkdir_p_dir(link2); |
963 | -- if (symlink("/lib/systemd/system/systemd-networkd-wait-online.service", link2) < 0 && errno != EEXIST) { |
964 | -- // LCOV_EXCL_START |
965 | -- g_fprintf(stderr, "failed to create enablement symlink: %m\n"); |
966 | -- exit(1); |
967 | -- // LCOV_EXCL_STOP |
968 | -+ if (enable_wait_online) { |
969 | -+ g_autofree char* link2 = g_build_path(G_DIR_SEPARATOR_S, generator_dir, "network-online.target.wants", "systemd-networkd-wait-online.service", NULL); |
970 | -+ _netplan_safe_mkdir_p_dir(link2); |
971 | -+ if (symlink("/lib/systemd/system/systemd-networkd-wait-online.service", link2) < 0 && errno != EEXIST) { |
972 | -+ // LCOV_EXCL_START |
973 | -+ g_fprintf(stderr, "failed to create enablement symlink: %m\n"); |
974 | -+ exit(1); |
975 | -+ // LCOV_EXCL_STOP |
976 | -+ } |
977 | - } |
978 | - } |
979 | - |
980 | -@@ -300,10 +302,11 @@ int main(int argc, char** argv) |
981 | - if (netplan_state_get_backend(np_state) == NETPLAN_BACKEND_NM || any_nm) |
982 | - _netplan_g_string_free_to_file(g_string_new(NULL), rootdir, "/run/NetworkManager/conf.d/10-globally-managed-devices.conf", NULL); |
983 | - |
984 | -+ gboolean enable_wait_online = _netplan_networkd_write_wait_online(np_state, rootdir); |
985 | - if (called_as_generator) { |
986 | - /* Ensure networkd starts if we have any configuration for it */ |
987 | - if (any_networkd) |
988 | -- enable_networkd(files[0]); |
989 | -+ enable_networkd(files[0], enable_wait_online); |
990 | - |
991 | - /* Leave a stamp file so that we don't regenerate the configuration |
992 | - * multiple times and userspace can wait for it to finish */ |
993 | -@@ -324,7 +327,8 @@ int main(int argc, char** argv) |
994 | - /* covered via 'cloud-init' integration test */ |
995 | - if (any_networkd) { |
996 | - start_unit_jit("systemd-networkd.socket"); |
997 | -- start_unit_jit("systemd-networkd-wait-online.service"); |
998 | -+ if (enable_wait_online) |
999 | -+ start_unit_jit("systemd-networkd-wait-online.service"); |
1000 | - start_unit_jit("systemd-networkd.service"); |
1001 | - } |
1002 | - g_autofree char* glob_run = g_build_path(G_DIR_SEPARATOR_S, |
1003 | -diff --git a/src/networkd.c b/src/networkd.c |
1004 | -index 9289388..cd1f7a2 100644 |
1005 | ---- a/src/networkd.c |
1006 | -+++ b/src/networkd.c |
1007 | -@@ -803,7 +803,6 @@ _netplan_netdef_write_network_file( |
1008 | - g_autoptr(GString) link = NULL; |
1009 | - GString* s = NULL; |
1010 | - mode_t orig_umask; |
1011 | -- gboolean is_optional = def->optional; |
1012 | - |
1013 | - SET_OPT_OUT_PTR(has_been_written, FALSE); |
1014 | - |
1015 | -@@ -826,17 +825,9 @@ _netplan_netdef_write_network_file( |
1016 | - else /* "off" */ |
1017 | - mode = "always-down"; |
1018 | - g_string_append_printf(link, "ActivationPolicy=%s\n", mode); |
1019 | -- /* When activation-mode is used we default to being optional. |
1020 | -- * Otherwise systemd might wait indefinitely for the interface to |
1021 | -- * become online. |
1022 | -- */ |
1023 | -- is_optional = TRUE; |
1024 | - } |
1025 | - |
1026 | -- if (is_optional || def->optional_addresses) { |
1027 | -- if (is_optional) { |
1028 | -- g_string_append(link, "RequiredForOnline=no\n"); |
1029 | -- } |
1030 | -+ if (def->optional_addresses) { |
1031 | - for (unsigned i = 0; NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name != NULL; ++i) { |
1032 | - if (def->optional_addresses & NETPLAN_OPTIONAL_ADDRESS_TYPES[i].flag) { |
1033 | - g_string_append_printf(link, "OptionalAddresses=%s\n", NETPLAN_OPTIONAL_ADDRESS_TYPES[i].name); |
1034 | -@@ -1466,6 +1457,60 @@ _netplan_netdef_write_networkd( |
1035 | - return TRUE; |
1036 | - } |
1037 | - |
1038 | -+gboolean |
1039 | -+_netplan_networkd_write_wait_online(const NetplanState* np_state, const char* rootdir) |
1040 | -+{ |
1041 | -+ // Hash set of non-optional interfaces to wait for |
1042 | -+ GHashTable* non_optional_interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
1043 | -+ NetplanStateIterator iter; |
1044 | -+ netplan_state_iterator_init(np_state, &iter); |
1045 | -+ while (netplan_state_iterator_has_next(&iter)) { |
1046 | -+ const NetplanNetDefinition* def = netplan_state_iterator_next(&iter); |
1047 | -+ if (def->backend != NETPLAN_BACKEND_NETWORKD) |
1048 | -+ continue; |
1049 | -+ /* When activation-mode is used we default to being optional. |
1050 | -+ * Otherwise, systemd might wait indefinitely for the interface to |
1051 | -+ * come online. |
1052 | -+ */ |
1053 | -+ if (!(def->optional || def->activation_mode)) { |
1054 | -+ if (!netplan_netdef_has_match(def)) { // no matching => single interface |
1055 | -+ g_hash_table_add(non_optional_interfaces, g_strdup(def->id)); |
1056 | -+ } else if (def->set_name) { // matching on a single interface |
1057 | -+ g_hash_table_add(non_optional_interfaces, g_strdup(def->set_name)); |
1058 | -+ } else { // matching on potentially multiple interfaces |
1059 | -+ // XXX: we shouldn't run this enumeration for every NetDef... |
1060 | -+ _netplan_enumerate_interfaces(def, non_optional_interfaces, rootdir); |
1061 | -+ } |
1062 | -+ } |
1063 | -+ } |
1064 | -+ |
1065 | -+ // create run/systemd/system/systemd-networkd-wait-online.service.d/ |
1066 | -+ const char* override = "/run/systemd/system/systemd-networkd-wait-online.service.d/10-netplan.conf"; |
1067 | -+ // The "ConditionPathIsSymbolicLink" is Netplan's s-n-wait-online enablement symlink, |
1068 | -+ // as we want to run -wait-online only if enabled by Netplan. |
1069 | -+ GString* content = g_string_new("[Unit]\n" |
1070 | -+ "ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/systemd-networkd-wait-online.service\n"); |
1071 | -+ if (g_hash_table_size(non_optional_interfaces) == 0) { |
1072 | -+ _netplan_g_string_free_to_file(content, rootdir, override, NULL); |
1073 | -+ g_hash_table_destroy(non_optional_interfaces); |
1074 | -+ return FALSE; |
1075 | -+ } |
1076 | -+ |
1077 | -+ // We have non-optional interface, so let's wait for those explicitly |
1078 | -+ GHashTableIter idx; |
1079 | -+ gpointer ifname; |
1080 | -+ g_string_append(content, "\n[Service]\nExecStart=\n" |
1081 | -+ "ExecStart=/lib/systemd/systemd-networkd-wait-online"); |
1082 | -+ g_hash_table_iter_init (&idx, non_optional_interfaces); |
1083 | -+ while (g_hash_table_iter_next (&idx, &ifname, NULL)) |
1084 | -+ g_string_append_printf(content, " -i %s", (const char*)ifname); |
1085 | -+ g_string_append(content, "\n"); |
1086 | -+ |
1087 | -+ _netplan_g_string_free_to_file(content, rootdir, override, NULL); |
1088 | -+ g_hash_table_destroy(non_optional_interfaces); |
1089 | -+ return TRUE; |
1090 | -+} |
1091 | -+ |
1092 | - /** |
1093 | - * Clean up all generated configurations in @rootdir from previous runs. |
1094 | - */ |
1095 | -@@ -1479,6 +1524,7 @@ _netplan_networkd_cleanup(const char* rootdir) |
1096 | - _netplan_unlink_glob(rootdir, "/run/udev/rules.d/99-netplan-*"); |
1097 | - _netplan_unlink_glob(rootdir, "/run/systemd/system/network.target.wants/netplan-regdom.service"); |
1098 | - _netplan_unlink_glob(rootdir, "/run/systemd/system/netplan-regdom.service"); |
1099 | -+ _netplan_unlink_glob(rootdir, "/run/systemd/system/systemd-networkd-wait-online.service.d/10-netplan*.conf"); |
1100 | - /* Historically (up to v0.98) we had netplan-wpa@*.service files, in case of an |
1101 | - * upgraded system, we need to make sure to clean those up. */ |
1102 | - _netplan_unlink_glob(rootdir, "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa@*.service"); |
1103 | -diff --git a/src/networkd.h b/src/networkd.h |
1104 | -index 2bd0848..8332b7b 100644 |
1105 | ---- a/src/networkd.h |
1106 | -+++ b/src/networkd.h |
1107 | -@@ -37,5 +37,8 @@ _netplan_netdef_write_network_file( |
1108 | - gboolean* has_been_written, |
1109 | - GError** error); |
1110 | - |
1111 | -+NETPLAN_INTERNAL gboolean |
1112 | -+_netplan_networkd_write_wait_online(const NetplanState* np_state, const char* rootdir); |
1113 | -+ |
1114 | - NETPLAN_INTERNAL void |
1115 | - _netplan_networkd_cleanup(const char* rootdir); |
1116 | -diff --git a/tests/generator/base.py b/tests/generator/base.py |
1117 | -index bcc9d8a..4f27e94 100644 |
1118 | ---- a/tests/generator/base.py |
1119 | -+++ b/tests/generator/base.py |
1120 | -@@ -465,6 +465,8 @@ class TestBase(unittest.TestCase): |
1121 | - self.assertEqual(set(os.listdir(self.workdir.name)) - {'lib'}, {'etc', 'run'}) |
1122 | - ovs_systemd_dir = set(os.listdir(systemd_dir)) |
1123 | - ovs_systemd_dir.remove('systemd-networkd.service.wants') |
1124 | -+ if 'systemd-networkd-wait-online.service.d' in ovs_systemd_dir: |
1125 | -+ ovs_systemd_dir.remove('systemd-networkd-wait-online.service.d') |
1126 | - self.assertEqual(ovs_systemd_dir, {'netplan-ovs-' + f for f in file_contents_map}) |
1127 | - for fname, contents in file_contents_map.items(): |
1128 | - fname = 'netplan-ovs-' + fname |
1129 | -diff --git a/tests/generator/test_args.py b/tests/generator/test_args.py |
1130 | -index 2c5ffe2..ffa603a 100644 |
1131 | ---- a/tests/generator/test_args.py |
1132 | -+++ b/tests/generator/test_args.py |
1133 | -@@ -124,6 +124,11 @@ class TestConfigArgs(TestBase): |
1134 | - version: 2 |
1135 | - ethernets: |
1136 | - eth0: |
1137 | -+ dhcp4: true |
1138 | -+ eth1: |
1139 | -+ dhcp4: true |
1140 | -+ optional: true |
1141 | -+ eth2: |
1142 | - dhcp4: true''') |
1143 | - outdir = os.path.join(self.workdir.name, 'out') |
1144 | - os.mkdir(outdir) |
1145 | -@@ -136,12 +141,28 @@ class TestConfigArgs(TestBase): |
1146 | - n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-eth0.network') |
1147 | - self.assertTrue(os.path.exists(n)) |
1148 | - os.unlink(n) |
1149 | -+ n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-eth1.network') |
1150 | -+ self.assertTrue(os.path.exists(n)) |
1151 | -+ os.unlink(n) |
1152 | -+ n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-eth2.network') |
1153 | -+ self.assertTrue(os.path.exists(n)) |
1154 | -+ os.unlink(n) |
1155 | - |
1156 | - # should auto-enable networkd and -wait-online |
1157 | -+ service_dir = os.path.join(self.workdir.name, 'run', 'systemd', 'system') |
1158 | - self.assertTrue(os.path.islink(os.path.join( |
1159 | - outdir, 'multi-user.target.wants', 'systemd-networkd.service'))) |
1160 | - self.assertTrue(os.path.islink(os.path.join( |
1161 | - outdir, 'network-online.target.wants', 'systemd-networkd-wait-online.service'))) |
1162 | -+ override = os.path.join(service_dir, 'systemd-networkd-wait-online.service.d', '10-netplan.conf') |
1163 | -+ self.assertTrue(os.path.isfile(override)) |
1164 | -+ with open(override, 'r') as f: |
1165 | -+ self.assertEqual(f.read(), '''[Unit] |
1166 | -+ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/systemd-networkd-wait-online.service |
1167 | -+ |
1168 | -+[Service] |
1169 | -+ExecStart= |
1170 | -+ExecStart=/lib/systemd/systemd-networkd-wait-online -i eth0 -i eth2\n''') |
1171 | - |
1172 | - # should be a no-op the second time while the stamp exists |
1173 | - out = subprocess.check_output([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir], |
1174 | -@@ -155,6 +176,41 @@ class TestConfigArgs(TestBase): |
1175 | - subprocess.check_output([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir]) |
1176 | - self.assertTrue(os.path.exists(n)) |
1177 | - |
1178 | -+ def test_systemd_generator_all_optional(self): |
1179 | -+ conf = os.path.join(self.confdir, 'a.yaml') |
1180 | -+ os.makedirs(os.path.dirname(conf)) |
1181 | -+ with open(conf, 'w') as f: |
1182 | -+ f.write('''network: |
1183 | -+ version: 2 |
1184 | -+ ethernets: |
1185 | -+ eth0: |
1186 | -+ dhcp4: true |
1187 | -+ optional: true''') |
1188 | -+ outdir = os.path.join(self.workdir.name, 'out') |
1189 | -+ os.mkdir(outdir) |
1190 | -+ |
1191 | -+ generator = os.path.join(self.workdir.name, 'systemd', 'system-generators', 'netplan') |
1192 | -+ os.makedirs(os.path.dirname(generator)) |
1193 | -+ os.symlink(exe_generate, generator) |
1194 | -+ |
1195 | -+ subprocess.check_call([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir]) |
1196 | -+ n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-eth0.network') |
1197 | -+ self.assertTrue(os.path.exists(n)) |
1198 | -+ os.unlink(n) |
1199 | -+ |
1200 | -+ # should auto-enable networkd but not -wait-online |
1201 | -+ service_dir = os.path.join(self.workdir.name, 'run', 'systemd', 'system') |
1202 | -+ self.assertTrue(os.path.islink(os.path.join( |
1203 | -+ outdir, 'multi-user.target.wants', 'systemd-networkd.service'))) |
1204 | -+ self.assertFalse(os.path.islink(os.path.join( |
1205 | -+ outdir, 'network-online.target.wants', 'systemd-networkd-wait-online.service'))) |
1206 | -+ override = os.path.join(service_dir, 'systemd-networkd-wait-online.service.d', '10-netplan.conf') |
1207 | -+ self.assertTrue(os.path.isfile(override)) |
1208 | -+ with open(override, 'r') as f: |
1209 | -+ self.assertEqual(f.read(), '''[Unit] |
1210 | -+ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/systemd-networkd-wait-online.service |
1211 | -+''') |
1212 | -+ |
1213 | - def test_systemd_generator_noconf(self): |
1214 | - outdir = os.path.join(self.workdir.name, 'out') |
1215 | - os.mkdir(outdir) |
1216 | -diff --git a/tests/generator/test_common.py b/tests/generator/test_common.py |
1217 | -index 785f6f4..d98eb32 100644 |
1218 | ---- a/tests/generator/test_common.py |
1219 | -+++ b/tests/generator/test_common.py |
1220 | -@@ -35,9 +35,6 @@ class TestNetworkd(TestBase): |
1221 | - self.assert_networkd({'eth0.network': '''[Match] |
1222 | - Name=eth0 |
1223 | - |
1224 | --[Link] |
1225 | --RequiredForOnline=no |
1226 | -- |
1227 | - [Network] |
1228 | - DHCP=ipv6 |
1229 | - LinkLocalAddressing=ipv6 |
1230 | -@@ -85,7 +82,6 @@ Name=eth0 |
1231 | - |
1232 | - [Link] |
1233 | - ActivationPolicy=always-down |
1234 | --RequiredForOnline=no |
1235 | - |
1236 | - [Network] |
1237 | - DHCP=ipv6 |
1238 | -@@ -109,7 +105,6 @@ Name=eth0 |
1239 | - |
1240 | - [Link] |
1241 | - ActivationPolicy=manual |
1242 | --RequiredForOnline=no |
1243 | - |
1244 | - [Network] |
1245 | - DHCP=ipv6 |
1246 | diff --git a/debian/patches/lp2060311/0006-CLI-utils-Do-not-ask-for-daemon-reload-password-inte.patch b/debian/patches/lp2060311/0006-CLI-utils-Do-not-ask-for-daemon-reload-password-inte.patch |
1247 | deleted file mode 100644 |
1248 | index b05d01a..0000000 |
1249 | --- a/debian/patches/lp2060311/0006-CLI-utils-Do-not-ask-for-daemon-reload-password-inte.patch |
1250 | +++ /dev/null |
1251 | @@ -1,53 +0,0 @@ |
1252 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
1253 | -Date: Wed, 17 Apr 2024 10:23:16 +0200 |
1254 | -Subject: CLI:utils: Do not ask for daemon-reload password interactively |
1255 | - |
1256 | -Origin: https://github.com/canonical/netplan/pull/456/commits/03c269d172955bff0fedff9504f6fa80e5a0b413 |
1257 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
1258 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
1259 | ---- |
1260 | - netplan_cli/cli/utils.py | 2 +- |
1261 | - tests/cli_legacy.py | 3 +-- |
1262 | - tests/test_utils.py | 2 +- |
1263 | - 3 files changed, 3 insertions(+), 4 deletions(-) |
1264 | - |
1265 | -diff --git a/netplan_cli/cli/utils.py b/netplan_cli/cli/utils.py |
1266 | -index c89af1a..c9f9ce2 100644 |
1267 | ---- a/netplan_cli/cli/utils.py |
1268 | -+++ b/netplan_cli/cli/utils.py |
1269 | -@@ -165,7 +165,7 @@ def systemctl_is_installed(unit_pattern): |
1270 | - |
1271 | - def systemctl_daemon_reload(): |
1272 | - '''Reload systemd unit files from disk and re-calculate its dependencies''' |
1273 | -- subprocess.check_call(['systemctl', 'daemon-reload']) |
1274 | -+ subprocess.check_call(['systemctl', 'daemon-reload', '--no-ask-password']) |
1275 | - |
1276 | - |
1277 | - def ip_addr_flush(iface): |
1278 | -diff --git a/tests/cli_legacy.py b/tests/cli_legacy.py |
1279 | -index 897043e..decde81 100755 |
1280 | ---- a/tests/cli_legacy.py |
1281 | -+++ b/tests/cli_legacy.py |
1282 | -@@ -98,8 +98,7 @@ class TestGenerate(unittest.TestCase): |
1283 | - enlol: {dhcp4: yes}''') |
1284 | - os.chmod(path_a, mode=0o600) |
1285 | - os.chmod(path_b, mode=0o600) |
1286 | -- out = subprocess.check_output(exe_cli + ['generate', '--root-dir', self.workdir.name], stderr=subprocess.STDOUT) |
1287 | -- self.assertEqual(out, b'') |
1288 | -+ subprocess.check_call(exe_cli + ['generate', '--root-dir', self.workdir.name]) |
1289 | - self.assertEqual(os.listdir(os.path.join(self.workdir.name, 'run', 'systemd', 'network')), |
1290 | - ['10-netplan-enlol.network']) |
1291 | - |
1292 | -diff --git a/tests/test_utils.py b/tests/test_utils.py |
1293 | -index 55c907c..c7ab060 100644 |
1294 | ---- a/tests/test_utils.py |
1295 | -+++ b/tests/test_utils.py |
1296 | -@@ -380,7 +380,7 @@ class TestUtils(unittest.TestCase): |
1297 | - os.environ['PATH'] = os.path.dirname(self.mock_cmd.path) + os.pathsep + path_env |
1298 | - utils.systemctl_daemon_reload() |
1299 | - self.assertEqual(self.mock_cmd.calls(), [ |
1300 | -- ['systemctl', 'daemon-reload'] |
1301 | -+ ['systemctl', 'daemon-reload', '--no-ask-password'] |
1302 | - ]) |
1303 | - |
1304 | - def test_ip_addr_flush(self): |
1305 | diff --git a/debian/patches/lp2060311/0007-CLI-generate-call-daemon-reload-after-re-generating-.patch b/debian/patches/lp2060311/0007-CLI-generate-call-daemon-reload-after-re-generating-.patch |
1306 | deleted file mode 100644 |
1307 | index 2998726..0000000 |
1308 | --- a/debian/patches/lp2060311/0007-CLI-generate-call-daemon-reload-after-re-generating-.patch |
1309 | +++ /dev/null |
1310 | @@ -1,29 +0,0 @@ |
1311 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
1312 | -Date: Wed, 17 Apr 2024 10:23:51 +0200 |
1313 | -Subject: CLI:generate: call daemon-reload after (re-)generating services |
1314 | - |
1315 | -Origin: https://github.com/canonical/netplan/pull/456/commits/f3d9994ae3d45d5e114e40efe6040319caaf5850 |
1316 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
1317 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
1318 | ---- |
1319 | - netplan_cli/cli/commands/generate.py | 9 ++++++++- |
1320 | - 1 file changed, 8 insertions(+), 1 deletion(-) |
1321 | - |
1322 | -diff --git a/netplan_cli/cli/commands/generate.py b/netplan_cli/cli/commands/generate.py |
1323 | -index f84b171..dcbbb53 100644 |
1324 | ---- a/netplan_cli/cli/commands/generate.py |
1325 | -+++ b/netplan_cli/cli/commands/generate.py |
1326 | -@@ -81,5 +81,12 @@ class NetplanGenerate(utils.NetplanCommand): |
1327 | - if self.mapping: |
1328 | - argv += ['--mapping', self.mapping] |
1329 | - logging.debug('command generate: running %s', argv) |
1330 | -+ res = subprocess.call(argv) |
1331 | -+ # reload systemd, as we might have changed service units, such as |
1332 | -+ # /run/systemd/system/systemd-networkd-wait-online.service.d/10-netplan.conf |
1333 | -+ try: |
1334 | -+ utils.systemctl_daemon_reload() |
1335 | -+ except subprocess.CalledProcessError as e: |
1336 | -+ logging.warning(e) |
1337 | - # FIXME: os.execv(argv[0], argv) would be better but fails coverage |
1338 | -- sys.exit(subprocess.call(argv)) |
1339 | -+ sys.exit(res) |
1340 | diff --git a/debian/patches/lp2060311/0008-wait-online-Do-not-block-on-loopback-interface.patch b/debian/patches/lp2060311/0008-wait-online-Do-not-block-on-loopback-interface.patch |
1341 | deleted file mode 100644 |
1342 | index 34c3ec2..0000000 |
1343 | --- a/debian/patches/lp2060311/0008-wait-online-Do-not-block-on-loopback-interface.patch |
1344 | +++ /dev/null |
1345 | @@ -1,71 +0,0 @@ |
1346 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
1347 | -Date: Wed, 17 Apr 2024 10:24:36 +0200 |
1348 | -Subject: wait-online: Do not block on loopback interface |
1349 | - |
1350 | -Origin: https://github.com/canonical/netplan/pull/456/commits/bd9456999aa6a1a71a475985c8b8556f57ed2b2f |
1351 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
1352 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
1353 | ---- |
1354 | - src/networkd.c | 12 +++++++++--- |
1355 | - tests/generator/test_args.py | 8 ++++---- |
1356 | - 2 files changed, 13 insertions(+), 7 deletions(-) |
1357 | - |
1358 | -diff --git a/src/networkd.c b/src/networkd.c |
1359 | -index cd1f7a2..862176b 100644 |
1360 | ---- a/src/networkd.c |
1361 | -+++ b/src/networkd.c |
1362 | -@@ -1498,12 +1498,18 @@ _netplan_networkd_write_wait_online(const NetplanState* np_state, const char* ro |
1363 | - |
1364 | - // We have non-optional interface, so let's wait for those explicitly |
1365 | - GHashTableIter idx; |
1366 | -- gpointer ifname; |
1367 | -+ gpointer key; |
1368 | - g_string_append(content, "\n[Service]\nExecStart=\n" |
1369 | - "ExecStart=/lib/systemd/systemd-networkd-wait-online"); |
1370 | - g_hash_table_iter_init (&idx, non_optional_interfaces); |
1371 | -- while (g_hash_table_iter_next (&idx, &ifname, NULL)) |
1372 | -- g_string_append_printf(content, " -i %s", (const char*)ifname); |
1373 | -+ while (g_hash_table_iter_next (&idx, &key, NULL)) { |
1374 | -+ const char* ifname = key; |
1375 | -+ g_string_append_printf(content, " -i %s", ifname); |
1376 | -+ // XXX: We should be checking IFF_LOOPBACK instead of interface name. |
1377 | -+ // But don't have access to the flags here. |
1378 | -+ if (!g_strcmp0(ifname, "lo")) |
1379 | -+ g_string_append(content, ":carrier"); // "carrier" as min-oper state for loopback |
1380 | -+ } |
1381 | - g_string_append(content, "\n"); |
1382 | - |
1383 | - _netplan_g_string_free_to_file(content, rootdir, override, NULL); |
1384 | -diff --git a/tests/generator/test_args.py b/tests/generator/test_args.py |
1385 | -index ffa603a..0db4d38 100644 |
1386 | ---- a/tests/generator/test_args.py |
1387 | -+++ b/tests/generator/test_args.py |
1388 | -@@ -128,8 +128,8 @@ class TestConfigArgs(TestBase): |
1389 | - eth1: |
1390 | - dhcp4: true |
1391 | - optional: true |
1392 | -- eth2: |
1393 | -- dhcp4: true''') |
1394 | -+ lo: |
1395 | -+ addresses: ["127.0.0.1/8", "::1/128"]''') |
1396 | - outdir = os.path.join(self.workdir.name, 'out') |
1397 | - os.mkdir(outdir) |
1398 | - |
1399 | -@@ -144,7 +144,7 @@ class TestConfigArgs(TestBase): |
1400 | - n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-eth1.network') |
1401 | - self.assertTrue(os.path.exists(n)) |
1402 | - os.unlink(n) |
1403 | -- n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-eth2.network') |
1404 | -+ n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-lo.network') |
1405 | - self.assertTrue(os.path.exists(n)) |
1406 | - os.unlink(n) |
1407 | - |
1408 | -@@ -162,7 +162,7 @@ ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/s |
1409 | - |
1410 | - [Service] |
1411 | - ExecStart= |
1412 | --ExecStart=/lib/systemd/systemd-networkd-wait-online -i eth0 -i eth2\n''') |
1413 | -+ExecStart=/lib/systemd/systemd-networkd-wait-online -i lo:carrier -i eth0\n''') |
1414 | - |
1415 | - # should be a no-op the second time while the stamp exists |
1416 | - out = subprocess.check_output([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir], |
1417 | diff --git a/debian/patches/lp2060311/0009-generate-Do-not-touch-wait-online-if-we-don-t-have-a.patch b/debian/patches/lp2060311/0009-generate-Do-not-touch-wait-online-if-we-don-t-have-a.patch |
1418 | deleted file mode 100644 |
1419 | index a836418..0000000 |
1420 | --- a/debian/patches/lp2060311/0009-generate-Do-not-touch-wait-online-if-we-don-t-have-a.patch |
1421 | +++ /dev/null |
1422 | @@ -1,44 +0,0 @@ |
1423 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
1424 | -Date: Wed, 17 Apr 2024 10:27:27 +0200 |
1425 | -Subject: generate: Do not touch wait-online, |
1426 | - if we don't have any networkd NetDefs |
1427 | - |
1428 | -Origin: https://github.com/canonical/netplan/pull/456/commits/e98eac8074cdd7cd99d17a40b15ae414b9eb6d3d |
1429 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
1430 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
1431 | ---- |
1432 | - src/generate.c | 5 ++++- |
1433 | - tests/generator/test_args.py | 4 ++++ |
1434 | - 2 files changed, 8 insertions(+), 1 deletion(-) |
1435 | - |
1436 | -diff --git a/src/generate.c b/src/generate.c |
1437 | -index 19198ff..d6edb0a 100644 |
1438 | ---- a/src/generate.c |
1439 | -+++ b/src/generate.c |
1440 | -@@ -302,7 +302,10 @@ int main(int argc, char** argv) |
1441 | - if (netplan_state_get_backend(np_state) == NETPLAN_BACKEND_NM || any_nm) |
1442 | - _netplan_g_string_free_to_file(g_string_new(NULL), rootdir, "/run/NetworkManager/conf.d/10-globally-managed-devices.conf", NULL); |
1443 | - |
1444 | -- gboolean enable_wait_online = _netplan_networkd_write_wait_online(np_state, rootdir); |
1445 | -+ gboolean enable_wait_online = FALSE; |
1446 | -+ if (any_networkd) |
1447 | -+ enable_wait_online = _netplan_networkd_write_wait_online(np_state, rootdir); |
1448 | -+ |
1449 | - if (called_as_generator) { |
1450 | - /* Ensure networkd starts if we have any configuration for it */ |
1451 | - if (any_networkd) |
1452 | -diff --git a/tests/generator/test_args.py b/tests/generator/test_args.py |
1453 | -index 0db4d38..5e36cf2 100644 |
1454 | ---- a/tests/generator/test_args.py |
1455 | -+++ b/tests/generator/test_args.py |
1456 | -@@ -39,6 +39,10 @@ class TestConfigArgs(TestBase): |
1457 | - self.assert_nm(None) |
1458 | - self.assert_nm_udev(None) |
1459 | - self.assert_ovs({'cleanup.service': OVS_CLEANUP % {'iface': 'cleanup'}}) |
1460 | -+ # should not touch -wait-online |
1461 | -+ service_dir = os.path.join(self.workdir.name, 'run', 'systemd', 'system') |
1462 | -+ override = os.path.join(service_dir, 'systemd-networkd-wait-online.service.d', '10-netplan.conf') |
1463 | -+ self.assertFalse(os.path.isfile(override)) |
1464 | - |
1465 | - def test_empty_config(self): |
1466 | - self.generate('') |
1467 | diff --git a/debian/patches/lp2060311/0010-wait-online-wait-for-existing-interfaces-only-and-do.patch b/debian/patches/lp2060311/0010-wait-online-wait-for-existing-interfaces-only-and-do.patch |
1468 | deleted file mode 100644 |
1469 | index 8568aa5..0000000 |
1470 | --- a/debian/patches/lp2060311/0010-wait-online-wait-for-existing-interfaces-only-and-do.patch |
1471 | +++ /dev/null |
1472 | @@ -1,238 +0,0 @@ |
1473 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
1474 | -Date: Wed, 17 Apr 2024 12:01:13 +0200 |
1475 | -Subject: wait-online: wait for existing interfaces only and downgrade |
1476 | - operational state for interfaces without IP configuration |
1477 | - |
1478 | -Origin: https://github.com/canonical/netplan/pull/456/commits/98d7f8af2013abc47dbaf3f11913ca06fb741255 |
1479 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
1480 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
1481 | ---- |
1482 | - src/networkd.c | 71 +++++++++++++++++++++++++++++------------- |
1483 | - tests/generator/test_args.py | 10 +++--- |
1484 | - tests/integration/ethernets.py | 37 ++++++++++++++++++++++ |
1485 | - 3 files changed, 92 insertions(+), 26 deletions(-) |
1486 | - |
1487 | -diff --git a/src/networkd.c b/src/networkd.c |
1488 | -index 862176b..71432d4 100644 |
1489 | ---- a/src/networkd.c |
1490 | -+++ b/src/networkd.c |
1491 | -@@ -75,28 +75,42 @@ _netplan_sysfs_get_driver_by_ifname(const char* ifname, const char* rootdir) |
1492 | - return g_path_get_basename(link); |
1493 | - } |
1494 | - |
1495 | --/** |
1496 | -- * Enumerate all network interfaces (/sys/clas/net/...) and check |
1497 | -- * netplan_netdef_match_interface() to see if they match the current NetDef |
1498 | -- */ |
1499 | - STATIC void |
1500 | --_netplan_enumerate_interfaces(const NetplanNetDefinition* def, GHashTable* tbl, const char* rootdir) |
1501 | -+_netplan_query_system_interfaces(GHashTable* tbl) |
1502 | - { |
1503 | - g_assert(tbl); |
1504 | - struct if_nameindex *if_nidxs, *intf; |
1505 | - if_nidxs = if_nameindex(); |
1506 | - if (if_nidxs != NULL) { |
1507 | -- for (intf = if_nidxs; intf->if_index != 0 || intf->if_name != NULL; intf++) { |
1508 | -- if (g_hash_table_contains(tbl, intf->if_name)) continue; |
1509 | -- g_autofree gchar* mac = _netplan_sysfs_get_mac_by_ifname(intf->if_name, rootdir); |
1510 | -- g_autofree gchar* driver = _netplan_sysfs_get_driver_by_ifname(intf->if_name, rootdir); |
1511 | -- if (netplan_netdef_match_interface(def, intf->if_name, mac, driver)) |
1512 | -- g_hash_table_add(tbl, g_strdup(intf->if_name)); |
1513 | -- } |
1514 | -+ for (intf = if_nidxs; intf->if_index != 0 || intf->if_name != NULL; intf++) |
1515 | -+ g_hash_table_add(tbl, g_strdup(intf->if_name)); |
1516 | - if_freenameindex(if_nidxs); |
1517 | - } |
1518 | - } |
1519 | - |
1520 | -+/** |
1521 | -+ * Enumerate all network interfaces (/sys/clas/net/...) and check |
1522 | -+ * netplan_netdef_match_interface() to see if they match the current NetDef |
1523 | -+ */ |
1524 | -+STATIC void |
1525 | -+_netplan_enumerate_interfaces(const NetplanNetDefinition* def, GHashTable* ifaces, GHashTable* tbl, const char* carrier, const char* set_name, const char* rootdir) |
1526 | -+{ |
1527 | -+ g_assert(ifaces); |
1528 | -+ g_assert(tbl); |
1529 | -+ |
1530 | -+ GHashTableIter iter; |
1531 | -+ gpointer key; |
1532 | -+ g_hash_table_iter_init (&iter, ifaces); |
1533 | -+ while (g_hash_table_iter_next (&iter, &key, NULL)) { |
1534 | -+ const char* ifname = key; |
1535 | -+ if (g_hash_table_contains(tbl, ifname)|| (set_name && g_hash_table_contains(tbl, set_name))) continue; |
1536 | -+ g_autofree gchar* mac = _netplan_sysfs_get_mac_by_ifname(ifname, rootdir); |
1537 | -+ g_autofree gchar* driver = _netplan_sysfs_get_driver_by_ifname(ifname, rootdir); |
1538 | -+ if (netplan_netdef_match_interface(def, ifname, mac, driver)) |
1539 | -+ g_hash_table_replace(tbl, set_name ? g_strdup(set_name) : g_strdup(ifname), g_strdup(carrier)); |
1540 | -+ } |
1541 | -+} |
1542 | -+ |
1543 | - /** |
1544 | - * Append WiFi frequencies to wpa_supplicant's freq_list= |
1545 | - */ |
1546 | -@@ -1460,29 +1474,41 @@ _netplan_netdef_write_networkd( |
1547 | - gboolean |
1548 | - _netplan_networkd_write_wait_online(const NetplanState* np_state, const char* rootdir) |
1549 | - { |
1550 | -+ // Set of all current network interfaces, potentially non yet renamed |
1551 | -+ GHashTable* system_interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
1552 | -+ _netplan_query_system_interfaces(system_interfaces); |
1553 | -+ |
1554 | - // Hash set of non-optional interfaces to wait for |
1555 | -- GHashTable* non_optional_interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
1556 | -+ GHashTable* non_optional_interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); |
1557 | - NetplanStateIterator iter; |
1558 | - netplan_state_iterator_init(np_state, &iter); |
1559 | - while (netplan_state_iterator_has_next(&iter)) { |
1560 | -- const NetplanNetDefinition* def = netplan_state_iterator_next(&iter); |
1561 | -+ NetplanNetDefinition* def = netplan_state_iterator_next(&iter); |
1562 | - if (def->backend != NETPLAN_BACKEND_NETWORKD) |
1563 | - continue; |
1564 | -+ |
1565 | - /* When activation-mode is used we default to being optional. |
1566 | - * Otherwise, systemd might wait indefinitely for the interface to |
1567 | - * come online. |
1568 | - */ |
1569 | - if (!(def->optional || def->activation_mode)) { |
1570 | -- if (!netplan_netdef_has_match(def)) { // no matching => single interface |
1571 | -- g_hash_table_add(non_optional_interfaces, g_strdup(def->id)); |
1572 | -- } else if (def->set_name) { // matching on a single interface |
1573 | -- g_hash_table_add(non_optional_interfaces, g_strdup(def->set_name)); |
1574 | -+ // Check if we have any IP configuration |
1575 | -+ struct address_iter* addr_iter = _netplan_netdef_new_address_iter(def); |
1576 | -+ gboolean any_ips = _netplan_address_iter_next(addr_iter) != NULL || netplan_netdef_get_link_local_ipv4(def) || netplan_netdef_get_link_local_ipv6(def); |
1577 | -+ _netplan_address_iter_free(addr_iter); |
1578 | -+ |
1579 | -+ // no matching => single interface, ignoring non-existing interfaces |
1580 | -+ if (!netplan_netdef_has_match(def) && g_hash_table_contains(system_interfaces, def->id)) { |
1581 | -+ g_hash_table_replace(non_optional_interfaces, g_strdup(def->id), any_ips ? g_strdup("degraded") : g_strdup("carrier")); |
1582 | -+ } else if (def->set_name) { // matching on a single interface, to be renamed |
1583 | -+ _netplan_enumerate_interfaces(def, system_interfaces, non_optional_interfaces, any_ips ? "degraded" : "carrier", def->set_name, rootdir); |
1584 | - } else { // matching on potentially multiple interfaces |
1585 | - // XXX: we shouldn't run this enumeration for every NetDef... |
1586 | -- _netplan_enumerate_interfaces(def, non_optional_interfaces, rootdir); |
1587 | -+ _netplan_enumerate_interfaces(def, system_interfaces, non_optional_interfaces, any_ips ? "degraded" : "carrier", NULL, rootdir); |
1588 | - } |
1589 | - } |
1590 | - } |
1591 | -+ g_hash_table_destroy(system_interfaces); |
1592 | - |
1593 | - // create run/systemd/system/systemd-networkd-wait-online.service.d/ |
1594 | - const char* override = "/run/systemd/system/systemd-networkd-wait-online.service.d/10-netplan.conf"; |
1595 | -@@ -1498,17 +1524,20 @@ _netplan_networkd_write_wait_online(const NetplanState* np_state, const char* ro |
1596 | - |
1597 | - // We have non-optional interface, so let's wait for those explicitly |
1598 | - GHashTableIter idx; |
1599 | -- gpointer key; |
1600 | -+ gpointer key, value; |
1601 | - g_string_append(content, "\n[Service]\nExecStart=\n" |
1602 | - "ExecStart=/lib/systemd/systemd-networkd-wait-online"); |
1603 | - g_hash_table_iter_init (&idx, non_optional_interfaces); |
1604 | -- while (g_hash_table_iter_next (&idx, &key, NULL)) { |
1605 | -+ while (g_hash_table_iter_next (&idx, &key, &value)) { |
1606 | - const char* ifname = key; |
1607 | -+ const char* min_oper_state = value; |
1608 | - g_string_append_printf(content, " -i %s", ifname); |
1609 | - // XXX: We should be checking IFF_LOOPBACK instead of interface name. |
1610 | - // But don't have access to the flags here. |
1611 | - if (!g_strcmp0(ifname, "lo")) |
1612 | - g_string_append(content, ":carrier"); // "carrier" as min-oper state for loopback |
1613 | -+ else if (min_oper_state) |
1614 | -+ g_string_append_printf(content, ":%s", min_oper_state); |
1615 | - } |
1616 | - g_string_append(content, "\n"); |
1617 | - |
1618 | -diff --git a/tests/generator/test_args.py b/tests/generator/test_args.py |
1619 | -index 5e36cf2..de5aaa8 100644 |
1620 | ---- a/tests/generator/test_args.py |
1621 | -+++ b/tests/generator/test_args.py |
1622 | -@@ -127,9 +127,9 @@ class TestConfigArgs(TestBase): |
1623 | - f.write('''network: |
1624 | - version: 2 |
1625 | - ethernets: |
1626 | -- eth0: |
1627 | -+ eth99: |
1628 | - dhcp4: true |
1629 | -- eth1: |
1630 | -+ eth98: |
1631 | - dhcp4: true |
1632 | - optional: true |
1633 | - lo: |
1634 | -@@ -142,10 +142,10 @@ class TestConfigArgs(TestBase): |
1635 | - os.symlink(exe_generate, generator) |
1636 | - |
1637 | - subprocess.check_call([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir]) |
1638 | -- n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-eth0.network') |
1639 | -+ n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-eth99.network') |
1640 | - self.assertTrue(os.path.exists(n)) |
1641 | - os.unlink(n) |
1642 | -- n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-eth1.network') |
1643 | -+ n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-eth98.network') |
1644 | - self.assertTrue(os.path.exists(n)) |
1645 | - os.unlink(n) |
1646 | - n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-lo.network') |
1647 | -@@ -166,7 +166,7 @@ ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/s |
1648 | - |
1649 | - [Service] |
1650 | - ExecStart= |
1651 | --ExecStart=/lib/systemd/systemd-networkd-wait-online -i lo:carrier -i eth0\n''') |
1652 | -+ExecStart=/lib/systemd/systemd-networkd-wait-online -i lo:carrier\n''') # eth99 does not exist on the system |
1653 | - |
1654 | - # should be a no-op the second time while the stamp exists |
1655 | - out = subprocess.check_output([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir], |
1656 | -diff --git a/tests/integration/ethernets.py b/tests/integration/ethernets.py |
1657 | -index e43f533..7028a49 100644 |
1658 | ---- a/tests/integration/ethernets.py |
1659 | -+++ b/tests/integration/ethernets.py |
1660 | -@@ -22,6 +22,7 @@ |
1661 | - # You should have received a copy of the GNU General Public License |
1662 | - # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1663 | - |
1664 | -+import os |
1665 | - import sys |
1666 | - import subprocess |
1667 | - import unittest |
1668 | -@@ -390,6 +391,42 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests): |
1669 | - ['inet6 9876:bbbb::11/70', 'inet 172.16.5.3/20'], |
1670 | - ['inet6 fe80:', 'inet 169.254.']) |
1671 | - |
1672 | -+ def test_systemd_networkd_wait_online(self): |
1673 | -+ self.setup_eth(None) |
1674 | -+ with open(self.config, 'w') as f: |
1675 | -+ f.write('''network: |
1676 | -+ renderer: %(r)s |
1677 | -+ ethernets: |
1678 | -+ lo: |
1679 | -+ addresses: ["127.0.0.1/8", "::1/128"] |
1680 | -+ optional: true |
1681 | -+ doesnotexist: |
1682 | -+ addresses: ["10.0.0.42/24"] |
1683 | -+ findme: |
1684 | -+ match: |
1685 | -+ macaddress: %(ec_mac)s |
1686 | -+ link-local: [] |
1687 | -+ set-name: "findme" |
1688 | -+ %(e2c)s: |
1689 | -+ addresses: ["10.0.0.1/24"] |
1690 | -+''' % {'r': self.backend, 'ec_mac': self.dev_e_client_mac, 'e2c': self.dev_e2_client}) |
1691 | -+ self.generate_and_settle([self.dev_e2_client]) |
1692 | -+ override = os.path.join('/run', 'systemd', 'system', 'systemd-networkd-wait-online.service.d', '10-netplan.conf') |
1693 | -+ self.assertTrue(os.path.isfile(override)) |
1694 | -+ |
1695 | -+ with open(override, 'r') as f: |
1696 | -+ # lo is optional/ignored and should not be listed |
1697 | -+ # doesnotexist should not be listed, as it does not exist |
1698 | -+ # <dev_e_client> should be listed as "findme", using reduced operational state |
1699 | -+ # <dev_e2_client> should be listed normally |
1700 | -+ self.assertEqual(f.read(), '''[Unit] |
1701 | -+ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/systemd-networkd-wait-online.service |
1702 | -+ |
1703 | -+[Service] |
1704 | -+ExecStart= |
1705 | -+ExecStart=/lib/systemd/systemd-networkd-wait-online -i %s:degraded -i findme:carrier |
1706 | -+''' % self.dev_e2_client) |
1707 | -+ |
1708 | - |
1709 | - @unittest.skipIf("NetworkManager" not in test_backends, |
1710 | - "skipping as NetworkManager backend tests are disabled") |
1711 | diff --git a/debian/patches/lp2060311/0011-wait-online-account-for-DHCPv4-v6-addresses.patch b/debian/patches/lp2060311/0011-wait-online-account-for-DHCPv4-v6-addresses.patch |
1712 | deleted file mode 100644 |
1713 | index 954ee57..0000000 |
1714 | --- a/debian/patches/lp2060311/0011-wait-online-account-for-DHCPv4-v6-addresses.patch |
1715 | +++ /dev/null |
1716 | @@ -1,28 +0,0 @@ |
1717 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
1718 | -Date: Wed, 17 Apr 2024 17:42:27 +0200 |
1719 | -Subject: wait-online: account for DHCPv4/v6 addresses |
1720 | - |
1721 | -Origin: https://github.com/canonical/netplan/pull/456/commits/3d62dfbf3bbd028e4e9d69f7f1a5ddd803d60a33 |
1722 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
1723 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
1724 | ---- |
1725 | - src/networkd.c | 6 +++++- |
1726 | - 1 file changed, 5 insertions(+), 1 deletion(-) |
1727 | - |
1728 | -diff --git a/src/networkd.c b/src/networkd.c |
1729 | -index 71432d4..f4c081c 100644 |
1730 | ---- a/src/networkd.c |
1731 | -+++ b/src/networkd.c |
1732 | -@@ -1494,7 +1494,11 @@ _netplan_networkd_write_wait_online(const NetplanState* np_state, const char* ro |
1733 | - if (!(def->optional || def->activation_mode)) { |
1734 | - // Check if we have any IP configuration |
1735 | - struct address_iter* addr_iter = _netplan_netdef_new_address_iter(def); |
1736 | -- gboolean any_ips = _netplan_address_iter_next(addr_iter) != NULL || netplan_netdef_get_link_local_ipv4(def) || netplan_netdef_get_link_local_ipv6(def); |
1737 | -+ gboolean any_ips = _netplan_address_iter_next(addr_iter) != NULL |
1738 | -+ || netplan_netdef_get_dhcp4(def) |
1739 | -+ || netplan_netdef_get_dhcp6(def) |
1740 | -+ || netplan_netdef_get_link_local_ipv4(def) |
1741 | -+ || netplan_netdef_get_link_local_ipv6(def); |
1742 | - _netplan_address_iter_free(addr_iter); |
1743 | - |
1744 | - // no matching => single interface, ignoring non-existing interfaces |
1745 | diff --git a/debian/patches/lp2060311/0012-wait-online-do-not-require-virtual-devices-to-be-cre.patch b/debian/patches/lp2060311/0012-wait-online-do-not-require-virtual-devices-to-be-cre.patch |
1746 | deleted file mode 100644 |
1747 | index 7a34949..0000000 |
1748 | --- a/debian/patches/lp2060311/0012-wait-online-do-not-require-virtual-devices-to-be-cre.patch |
1749 | +++ /dev/null |
1750 | @@ -1,94 +0,0 @@ |
1751 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
1752 | -Date: Wed, 17 Apr 2024 18:01:50 +0200 |
1753 | -Subject: wait-online: do not require virtual devices to be created already |
1754 | - |
1755 | -Origin: https://github.com/canonical/netplan/pull/456/commits/d04f4f9a9d740cdab286ef4865b68153001b6601 |
1756 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
1757 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
1758 | ---- |
1759 | - src/networkd.c | 8 ++++++-- |
1760 | - tests/generator/test_args.py | 10 ++++++++-- |
1761 | - tests/integration/ethernets.py | 8 ++++++-- |
1762 | - 3 files changed, 20 insertions(+), 6 deletions(-) |
1763 | - |
1764 | -diff --git a/src/networkd.c b/src/networkd.c |
1765 | -index f4c081c..f552bfd 100644 |
1766 | ---- a/src/networkd.c |
1767 | -+++ b/src/networkd.c |
1768 | -@@ -1501,8 +1501,12 @@ _netplan_networkd_write_wait_online(const NetplanState* np_state, const char* ro |
1769 | - || netplan_netdef_get_link_local_ipv6(def); |
1770 | - _netplan_address_iter_free(addr_iter); |
1771 | - |
1772 | -- // no matching => single interface, ignoring non-existing interfaces |
1773 | -- if (!netplan_netdef_has_match(def) && g_hash_table_contains(system_interfaces, def->id)) { |
1774 | -+ // no matching => single physical interface, ignoring non-existing interfaces |
1775 | -+ // OR: virtual interfaces, those will be created later on and cannot have a matching condition |
1776 | -+ gboolean physical_no_match_or_virtual = FALSE |
1777 | -+ || (!netplan_netdef_has_match(def) && g_hash_table_contains(system_interfaces, def->id)) |
1778 | -+ || (netplan_netdef_get_type(def) >= NETPLAN_DEF_TYPE_VIRTUAL); |
1779 | -+ if (physical_no_match_or_virtual) { |
1780 | - g_hash_table_replace(non_optional_interfaces, g_strdup(def->id), any_ips ? g_strdup("degraded") : g_strdup("carrier")); |
1781 | - } else if (def->set_name) { // matching on a single interface, to be renamed |
1782 | - _netplan_enumerate_interfaces(def, system_interfaces, non_optional_interfaces, any_ips ? "degraded" : "carrier", def->set_name, rootdir); |
1783 | -diff --git a/tests/generator/test_args.py b/tests/generator/test_args.py |
1784 | -index de5aaa8..b48291a 100644 |
1785 | ---- a/tests/generator/test_args.py |
1786 | -+++ b/tests/generator/test_args.py |
1787 | -@@ -133,7 +133,10 @@ class TestConfigArgs(TestBase): |
1788 | - dhcp4: true |
1789 | - optional: true |
1790 | - lo: |
1791 | -- addresses: ["127.0.0.1/8", "::1/128"]''') |
1792 | -+ addresses: ["127.0.0.1/8", "::1/128"] |
1793 | -+ bridges: |
1794 | -+ br0: |
1795 | -+ dhcp4: true''') |
1796 | - outdir = os.path.join(self.workdir.name, 'out') |
1797 | - os.mkdir(outdir) |
1798 | - |
1799 | -@@ -151,6 +154,9 @@ class TestConfigArgs(TestBase): |
1800 | - n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-lo.network') |
1801 | - self.assertTrue(os.path.exists(n)) |
1802 | - os.unlink(n) |
1803 | -+ n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-br0.network') |
1804 | -+ self.assertTrue(os.path.exists(n)) |
1805 | -+ os.unlink(n) |
1806 | - |
1807 | - # should auto-enable networkd and -wait-online |
1808 | - service_dir = os.path.join(self.workdir.name, 'run', 'systemd', 'system') |
1809 | -@@ -166,7 +172,7 @@ ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/s |
1810 | - |
1811 | - [Service] |
1812 | - ExecStart= |
1813 | --ExecStart=/lib/systemd/systemd-networkd-wait-online -i lo:carrier\n''') # eth99 does not exist on the system |
1814 | -+ExecStart=/lib/systemd/systemd-networkd-wait-online -i br0:degraded -i lo:carrier\n''') # eth99 does not exist on the system |
1815 | - |
1816 | - # should be a no-op the second time while the stamp exists |
1817 | - out = subprocess.check_output([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir], |
1818 | -diff --git a/tests/integration/ethernets.py b/tests/integration/ethernets.py |
1819 | -index 7028a49..1e57816 100644 |
1820 | ---- a/tests/integration/ethernets.py |
1821 | -+++ b/tests/integration/ethernets.py |
1822 | -@@ -409,8 +409,12 @@ class TestNetworkd(IntegrationTestsBase, _CommonTests): |
1823 | - set-name: "findme" |
1824 | - %(e2c)s: |
1825 | - addresses: ["10.0.0.1/24"] |
1826 | -+ bridges: |
1827 | -+ br0: |
1828 | -+ addresses: ["10.0.0.2/24"] |
1829 | -+ interfaces: [%(e2c)s] |
1830 | - ''' % {'r': self.backend, 'ec_mac': self.dev_e_client_mac, 'e2c': self.dev_e2_client}) |
1831 | -- self.generate_and_settle([self.dev_e2_client]) |
1832 | -+ self.generate_and_settle([self.dev_e2_client, 'br0']) |
1833 | - override = os.path.join('/run', 'systemd', 'system', 'systemd-networkd-wait-online.service.d', '10-netplan.conf') |
1834 | - self.assertTrue(os.path.isfile(override)) |
1835 | - |
1836 | -@@ -424,7 +428,7 @@ ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/s |
1837 | - |
1838 | - [Service] |
1839 | - ExecStart= |
1840 | --ExecStart=/lib/systemd/systemd-networkd-wait-online -i %s:degraded -i findme:carrier |
1841 | -+ExecStart=/lib/systemd/systemd-networkd-wait-online -i %s:degraded -i br0:degraded -i findme:carrier |
1842 | - ''' % self.dev_e2_client) |
1843 | - |
1844 | - |
1845 | diff --git a/debian/patches/lp2060311/0013-wait-online-recognize-that-bridge-bond-members-will-.patch b/debian/patches/lp2060311/0013-wait-online-recognize-that-bridge-bond-members-will-.patch |
1846 | deleted file mode 100644 |
1847 | index 5f99a69..0000000 |
1848 | --- a/debian/patches/lp2060311/0013-wait-online-recognize-that-bridge-bond-members-will-.patch |
1849 | +++ /dev/null |
1850 | @@ -1,79 +0,0 @@ |
1851 | -From: =?utf-8?q?Lukas_M=C3=A4rdian?= <slyon@ubuntu.com> |
1852 | -Date: Thu, 18 Apr 2024 12:16:56 +0200 |
1853 | -Subject: wait-online: recognize that bridge/bond members will never gain |
1854 | - link-local addresses |
1855 | - |
1856 | -Origin: https://github.com/canonical/netplan/pull/456/commits/347d3fde2cc1c8b14c98d13d789f6ceff43b8710 |
1857 | -Bug-Ubuntu: https://bugs.launchpad.net/netplan/+bug/2060311 |
1858 | -Forwarded: https://github.com/canonical/netplan/pull/456 |
1859 | ---- |
1860 | - src/networkd.c | 12 ++++++++---- |
1861 | - tests/generator/test_args.py | 16 ++++++++++++++-- |
1862 | - 2 files changed, 22 insertions(+), 6 deletions(-) |
1863 | - |
1864 | -diff --git a/src/networkd.c b/src/networkd.c |
1865 | -index f552bfd..523f717 100644 |
1866 | ---- a/src/networkd.c |
1867 | -+++ b/src/networkd.c |
1868 | -@@ -1493,12 +1493,16 @@ _netplan_networkd_write_wait_online(const NetplanState* np_state, const char* ro |
1869 | - */ |
1870 | - if (!(def->optional || def->activation_mode)) { |
1871 | - // Check if we have any IP configuration |
1872 | -+ // bond and bridge members will never ask for link-local addresses (see above) |
1873 | - struct address_iter* addr_iter = _netplan_netdef_new_address_iter(def); |
1874 | -- gboolean any_ips = _netplan_address_iter_next(addr_iter) != NULL |
1875 | -+ gboolean routable = _netplan_address_iter_next(addr_iter) != NULL |
1876 | - || netplan_netdef_get_dhcp4(def) |
1877 | -- || netplan_netdef_get_dhcp6(def) |
1878 | -- || netplan_netdef_get_link_local_ipv4(def) |
1879 | -- || netplan_netdef_get_link_local_ipv6(def); |
1880 | -+ || netplan_netdef_get_dhcp6(def); |
1881 | -+ gboolean degraded = ( netplan_netdef_get_link_local_ipv4(def) |
1882 | -+ && !(netplan_netdef_get_bond_link(def) || netplan_netdef_get_bridge_link(def))) |
1883 | -+ || ( netplan_netdef_get_link_local_ipv6(def) |
1884 | -+ && !(netplan_netdef_get_bond_link(def) || netplan_netdef_get_bridge_link(def))); |
1885 | -+ gboolean any_ips = routable || degraded; |
1886 | - _netplan_address_iter_free(addr_iter); |
1887 | - |
1888 | - // no matching => single physical interface, ignoring non-existing interfaces |
1889 | -diff --git a/tests/generator/test_args.py b/tests/generator/test_args.py |
1890 | -index b48291a..291d164 100644 |
1891 | ---- a/tests/generator/test_args.py |
1892 | -+++ b/tests/generator/test_args.py |
1893 | -@@ -134,9 +134,20 @@ class TestConfigArgs(TestBase): |
1894 | - optional: true |
1895 | - lo: |
1896 | - addresses: ["127.0.0.1/8", "::1/128"] |
1897 | -+ vlans: |
1898 | -+ eth99.42: |
1899 | -+ link: eth99 |
1900 | -+ id: 42 |
1901 | -+ link-local: [ipv4, ipv6] # this is ignored for bridge-members |
1902 | -+ eth99.43: |
1903 | -+ link: eth99 |
1904 | -+ id: 43 |
1905 | -+ link-local: [] |
1906 | -+ addresses: [10.0.0.2/24] |
1907 | - bridges: |
1908 | - br0: |
1909 | -- dhcp4: true''') |
1910 | -+ dhcp4: true |
1911 | -+ interfaces: [eth99.42, eth99.43]''') |
1912 | - outdir = os.path.join(self.workdir.name, 'out') |
1913 | - os.mkdir(outdir) |
1914 | - |
1915 | -@@ -167,12 +178,13 @@ class TestConfigArgs(TestBase): |
1916 | - override = os.path.join(service_dir, 'systemd-networkd-wait-online.service.d', '10-netplan.conf') |
1917 | - self.assertTrue(os.path.isfile(override)) |
1918 | - with open(override, 'r') as f: |
1919 | -+ # eth99 does not exist on the system, so will not be listed |
1920 | - self.assertEqual(f.read(), '''[Unit] |
1921 | - ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/systemd-networkd-wait-online.service |
1922 | - |
1923 | - [Service] |
1924 | - ExecStart= |
1925 | --ExecStart=/lib/systemd/systemd-networkd-wait-online -i br0:degraded -i lo:carrier\n''') # eth99 does not exist on the system |
1926 | -+ExecStart=/lib/systemd/systemd-networkd-wait-online -i eth99.43:degraded -i br0:degraded -i lo:carrier -i eth99.42:carrier\n''') |
1927 | - |
1928 | - # should be a no-op the second time while the stamp exists |
1929 | - out = subprocess.check_output([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir], |
1930 | diff --git a/debian/patches/lp2065738/0014-libnetplan-use-more-restrictive-file-permissions.patch b/debian/patches/lp2065738/0014-libnetplan-use-more-restrictive-file-permissions.patch |
1931 | deleted file mode 100644 |
1932 | index da5047c..0000000 |
1933 | --- a/debian/patches/lp2065738/0014-libnetplan-use-more-restrictive-file-permissions.patch |
1934 | +++ /dev/null |
1935 | @@ -1,462 +0,0 @@ |
1936 | -From: Danilo Egea Gondolfo <danilogondolfo@gmail.com> |
1937 | -Date: Wed, 22 May 2024 15:44:16 +0100 |
1938 | -Subject: libnetplan: use more restrictive file permissions |
1939 | - |
1940 | -A new util.c:_netplan_g_string_free_to_file_with_permissions() was added |
1941 | -and accepts the owner, group and file mode as arguments. When these |
1942 | -properties can't be set, when the generator is called by a non-root user |
1943 | -for example, it will not hard-fail. This function is called by unit |
1944 | -tests where we can't set the owner to a privileged account for example. |
1945 | - |
1946 | -When generating backend files, use more restrictive permissions: |
1947 | - |
1948 | -networkd related files will be owned by root:systemd-network and have |
1949 | -mode 0640. |
1950 | - |
1951 | -service unit files will be owned by root:root and have mode 0640. |
1952 | -udevd files will be owned by root:root with mode 0640. |
1953 | - |
1954 | -wpa_supplicant and Network Manager files will continue with the existing |
1955 | -permissions. |
1956 | - |
1957 | -Autopkgtests will check if the permissions are set as expected when |
1958 | -calling the generator. |
1959 | - |
1960 | -This fix addresses CVE-2022-4968 |
1961 | ---- |
1962 | - src/networkd.c | 40 +++++--------------- |
1963 | - src/networkd.h | 2 + |
1964 | - src/nm.c | 4 +- |
1965 | - src/openvswitch.c | 2 +- |
1966 | - src/sriov.c | 4 +- |
1967 | - src/util-internal.h | 3 ++ |
1968 | - src/util.c | 46 +++++++++++++++++++++++ |
1969 | - tests/generator/test_auth.py | 2 +- |
1970 | - tests/generator/test_wifis.py | 2 +- |
1971 | - tests/integration/base.py | 85 +++++++++++++++++++++++++++++++++++++++++++ |
1972 | - 10 files changed, 152 insertions(+), 38 deletions(-) |
1973 | - |
1974 | -diff --git a/src/networkd.c b/src/networkd.c |
1975 | -index 2379837..cb9547c 100644 |
1976 | ---- a/src/networkd.c |
1977 | -+++ b/src/networkd.c |
1978 | -@@ -300,7 +300,6 @@ STATIC void |
1979 | - write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char* path) |
1980 | - { |
1981 | - GString* s = NULL; |
1982 | -- mode_t orig_umask; |
1983 | - |
1984 | - /* Don't write .link files for virtual devices; they use .netdev instead. |
1985 | - * Don't write .link files for MODEM devices, as they aren't supported by networkd. |
1986 | -@@ -372,9 +371,7 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char |
1987 | - g_string_append_printf(s, "LargeReceiveOffload=%s\n", |
1988 | - (def->large_receive_offload ? "true" : "false")); |
1989 | - |
1990 | -- orig_umask = umask(022); |
1991 | -- _netplan_g_string_free_to_file(s, rootdir, path, ".link"); |
1992 | -- umask(orig_umask); |
1993 | -+ _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, ".link", "root", "root", 0640); |
1994 | - } |
1995 | - |
1996 | - STATIC gboolean |
1997 | -@@ -392,7 +389,7 @@ write_regdom(const NetplanNetDefinition* def, const char* rootdir, GError** erro |
1998 | - g_string_append(s, "\n[Service]\nType=oneshot\n"); |
1999 | - g_string_append_printf(s, "ExecStart="SBINDIR"/iw reg set %s\n", def->regulatory_domain); |
2000 | - |
2001 | -- _netplan_g_string_free_to_file(s, rootdir, path, NULL); |
2002 | -+ _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
2003 | - _netplan_safe_mkdir_p_dir(link); |
2004 | - if (symlink(path, link) < 0 && errno != EEXIST) { |
2005 | - // LCOV_EXCL_START |
2006 | -@@ -572,7 +569,6 @@ STATIC void |
2007 | - write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const char* path) |
2008 | - { |
2009 | - GString* s = NULL; |
2010 | -- mode_t orig_umask; |
2011 | - |
2012 | - g_assert(def->type >= NETPLAN_DEF_TYPE_VIRTUAL); |
2013 | - |
2014 | -@@ -668,11 +664,7 @@ write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const ch |
2015 | - default: g_assert_not_reached(); // LCOV_EXCL_LINE |
2016 | - } |
2017 | - |
2018 | -- /* these do not contain secrets and need to be readable by |
2019 | -- * systemd-networkd - LP: #1736965 */ |
2020 | -- orig_umask = umask(022); |
2021 | -- _netplan_g_string_free_to_file(s, rootdir, path, ".netdev"); |
2022 | -- umask(orig_umask); |
2023 | -+ _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, ".netdev", "root", NETWORKD_GROUP, 0640); |
2024 | - } |
2025 | - |
2026 | - STATIC void |
2027 | -@@ -816,7 +808,6 @@ _netplan_netdef_write_network_file( |
2028 | - g_autoptr(GString) network = NULL; |
2029 | - g_autoptr(GString) link = NULL; |
2030 | - GString* s = NULL; |
2031 | -- mode_t orig_umask; |
2032 | - |
2033 | - SET_OPT_OUT_PTR(has_been_written, FALSE); |
2034 | - |
2035 | -@@ -1063,11 +1054,7 @@ _netplan_netdef_write_network_file( |
2036 | - if (network->len > 0) |
2037 | - g_string_append_printf(s, "\n[Network]\n%s", network->str); |
2038 | - |
2039 | -- /* these do not contain secrets and need to be readable by |
2040 | -- * systemd-networkd - LP: #1736965 */ |
2041 | -- orig_umask = umask(022); |
2042 | -- _netplan_g_string_free_to_file(s, rootdir, path, ".network"); |
2043 | -- umask(orig_umask); |
2044 | -+ _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, ".network", "root", NETWORKD_GROUP, 0640); |
2045 | - } |
2046 | - |
2047 | - SET_OPT_OUT_PTR(has_been_written, TRUE); |
2048 | -@@ -1079,7 +1066,6 @@ write_rules_file(const NetplanNetDefinition* def, const char* rootdir) |
2049 | - { |
2050 | - GString* s = NULL; |
2051 | - g_autofree char* path = g_strjoin(NULL, "run/udev/rules.d/99-netplan-", def->id, ".rules", NULL); |
2052 | -- mode_t orig_umask; |
2053 | - |
2054 | - /* do we need to write a .rules file? |
2055 | - * It's only required for reliably setting the name of a physical device |
2056 | -@@ -1113,9 +1099,7 @@ write_rules_file(const NetplanNetDefinition* def, const char* rootdir) |
2057 | - |
2058 | - g_string_append_printf(s, "NAME=\"%s\"\n", def->set_name); |
2059 | - |
2060 | -- orig_umask = umask(022); |
2061 | -- _netplan_g_string_free_to_file(s, rootdir, path, NULL); |
2062 | -- umask(orig_umask); |
2063 | -+ _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
2064 | - } |
2065 | - |
2066 | - STATIC gboolean |
2067 | -@@ -1264,7 +1248,6 @@ STATIC void |
2068 | - write_wpa_unit(const NetplanNetDefinition* def, const char* rootdir) |
2069 | - { |
2070 | - g_autofree gchar *stdouth = NULL; |
2071 | -- mode_t orig_umask; |
2072 | - |
2073 | - stdouth = systemd_escape(def->id); |
2074 | - |
2075 | -@@ -1283,9 +1266,7 @@ write_wpa_unit(const NetplanNetDefinition* def, const char* rootdir) |
2076 | - } else { |
2077 | - g_string_append(s, " -Dnl80211,wext\n"); |
2078 | - } |
2079 | -- orig_umask = umask(022); |
2080 | -- _netplan_g_string_free_to_file(s, rootdir, path, NULL); |
2081 | -- umask(orig_umask); |
2082 | -+ _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
2083 | - } |
2084 | - |
2085 | - STATIC gboolean |
2086 | -@@ -1294,7 +1275,6 @@ write_wpa_conf(const NetplanNetDefinition* def, const char* rootdir, GError** er |
2087 | - GHashTableIter iter; |
2088 | - GString* s = g_string_new("ctrl_interface=/run/wpa_supplicant\n\n"); |
2089 | - g_autofree char* path = g_strjoin(NULL, "run/netplan/wpa-", def->id, ".conf", NULL); |
2090 | -- mode_t orig_umask; |
2091 | - |
2092 | - g_debug("%s: Creating wpa_supplicant configuration file %s", def->id, path); |
2093 | - if (def->type == NETPLAN_DEF_TYPE_WIFI) { |
2094 | -@@ -1383,9 +1363,7 @@ write_wpa_conf(const NetplanNetDefinition* def, const char* rootdir, GError** er |
2095 | - } |
2096 | - |
2097 | - /* use tight permissions as this contains secrets */ |
2098 | -- orig_umask = umask(077); |
2099 | -- _netplan_g_string_free_to_file(s, rootdir, path, NULL); |
2100 | -- umask(orig_umask); |
2101 | -+ _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0600); |
2102 | - return TRUE; |
2103 | - } |
2104 | - |
2105 | -@@ -1529,7 +1507,7 @@ _netplan_networkd_write_wait_online(const NetplanState* np_state, const char* ro |
2106 | - GString* content = g_string_new("[Unit]\n" |
2107 | - "ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/systemd-networkd-wait-online.service\n"); |
2108 | - if (g_hash_table_size(non_optional_interfaces) == 0) { |
2109 | -- _netplan_g_string_free_to_file(content, rootdir, override, NULL); |
2110 | -+ _netplan_g_string_free_to_file_with_permissions(content, rootdir, override, NULL, "root", "root", 0640); |
2111 | - g_hash_table_destroy(non_optional_interfaces); |
2112 | - return FALSE; |
2113 | - } |
2114 | -@@ -1553,7 +1531,7 @@ _netplan_networkd_write_wait_online(const NetplanState* np_state, const char* ro |
2115 | - } |
2116 | - g_string_append(content, "\n"); |
2117 | - |
2118 | -- _netplan_g_string_free_to_file(content, rootdir, override, NULL); |
2119 | -+ _netplan_g_string_free_to_file_with_permissions(content, rootdir, override, NULL, "root", "root", 0640); |
2120 | - g_hash_table_destroy(non_optional_interfaces); |
2121 | - return TRUE; |
2122 | - } |
2123 | -diff --git a/src/networkd.h b/src/networkd.h |
2124 | -index 8332b7b..9f80585 100644 |
2125 | ---- a/src/networkd.h |
2126 | -+++ b/src/networkd.h |
2127 | -@@ -20,6 +20,8 @@ |
2128 | - #include "netplan.h" |
2129 | - #include <glib.h> |
2130 | - |
2131 | -+#define NETWORKD_GROUP "systemd-network" |
2132 | -+ |
2133 | - NETPLAN_INTERNAL gboolean |
2134 | - _netplan_netdef_write_networkd( |
2135 | - const NetplanState* np_state, |
2136 | -diff --git a/src/nm.c b/src/nm.c |
2137 | -index 2b850af..8f1bf05 100644 |
2138 | ---- a/src/nm.c |
2139 | -+++ b/src/nm.c |
2140 | -@@ -1150,13 +1150,13 @@ netplan_state_finish_nm_write( |
2141 | - |
2142 | - /* write generated NetworkManager drop-in config */ |
2143 | - if (nm_conf->len > 0) |
2144 | -- _netplan_g_string_free_to_file(nm_conf, rootdir, "run/NetworkManager/conf.d/netplan.conf", NULL); |
2145 | -+ _netplan_g_string_free_to_file_with_permissions(nm_conf, rootdir, "run/NetworkManager/conf.d/netplan.conf", NULL, "root", "root", 0640); |
2146 | - else |
2147 | - g_string_free(nm_conf, TRUE); |
2148 | - |
2149 | - /* write generated udev rules */ |
2150 | - if (udev_rules->len > 0) |
2151 | -- _netplan_g_string_free_to_file(udev_rules, rootdir, "run/udev/rules.d/90-netplan.rules", NULL); |
2152 | -+ _netplan_g_string_free_to_file_with_permissions(udev_rules, rootdir, "run/udev/rules.d/90-netplan.rules", NULL, "root", "root", 0640); |
2153 | - else |
2154 | - g_string_free(udev_rules, TRUE); |
2155 | - |
2156 | -diff --git a/src/openvswitch.c b/src/openvswitch.c |
2157 | -index 6eb0688..2ab77e7 100644 |
2158 | ---- a/src/openvswitch.c |
2159 | -+++ b/src/openvswitch.c |
2160 | -@@ -66,7 +66,7 @@ write_ovs_systemd_unit(const char* id, const GString* cmds, const char* rootdir, |
2161 | - g_string_append(s, "StartLimitBurst=0\n"); |
2162 | - g_string_append(s, cmds->str); |
2163 | - |
2164 | -- _netplan_g_string_free_to_file(s, rootdir, path, NULL); |
2165 | -+ _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
2166 | - |
2167 | - _netplan_safe_mkdir_p_dir(link); |
2168 | - if (symlink(path, link) < 0 && errno != EEXIST) { |
2169 | -diff --git a/src/sriov.c b/src/sriov.c |
2170 | -index 1534c94..213f124 100644 |
2171 | ---- a/src/sriov.c |
2172 | -+++ b/src/sriov.c |
2173 | -@@ -54,7 +54,7 @@ write_sriov_rebind_systemd_unit(GHashTable* pfs, const char* rootdir, GError** e |
2174 | - g_string_truncate(interfaces, interfaces->len-1); /* cut trailing whitespace */ |
2175 | - g_string_append_printf(s, "ExecStart=" SBINDIR "/netplan rebind --debug %s\n", interfaces->str); |
2176 | - |
2177 | -- _netplan_g_string_free_to_file(s, rootdir, path, NULL); |
2178 | -+ _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
2179 | - g_string_free(interfaces, TRUE); |
2180 | - |
2181 | - _netplan_safe_mkdir_p_dir(link); |
2182 | -@@ -90,7 +90,7 @@ write_sriov_apply_systemd_unit(GHashTable* pfs, const char* rootdir, GError** er |
2183 | - g_string_append(s, "\n[Service]\nType=oneshot\n"); |
2184 | - g_string_append_printf(s, "ExecStart=" SBINDIR "/netplan apply --sriov-only\n"); |
2185 | - |
2186 | -- _netplan_g_string_free_to_file(s, rootdir, path, NULL); |
2187 | -+ _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
2188 | - |
2189 | - _netplan_safe_mkdir_p_dir(link); |
2190 | - if (symlink(path, link) < 0 && errno != EEXIST) { |
2191 | -diff --git a/src/util-internal.h b/src/util-internal.h |
2192 | -index 86bd1b7..7454e77 100644 |
2193 | ---- a/src/util-internal.h |
2194 | -+++ b/src/util-internal.h |
2195 | -@@ -40,6 +40,9 @@ _netplan_safe_mkdir_p_dir(const char* file_path); |
2196 | - NETPLAN_INTERNAL void |
2197 | - _netplan_g_string_free_to_file(GString* s, const char* rootdir, const char* path, const char* suffix); |
2198 | - |
2199 | -+void |
2200 | -+_netplan_g_string_free_to_file_with_permissions(GString* s, const char* rootdir, const char* path, const char* suffix, const char* owner, const char* group, mode_t mode); |
2201 | -+ |
2202 | - NETPLAN_INTERNAL void |
2203 | - _netplan_unlink_glob(const char* rootdir, const char* _glob); |
2204 | - |
2205 | -diff --git a/src/util.c b/src/util.c |
2206 | -index 3e6c80d..46aba6e 100644 |
2207 | ---- a/src/util.c |
2208 | -+++ b/src/util.c |
2209 | -@@ -23,6 +23,9 @@ |
2210 | - #include <regex.h> |
2211 | - #include <string.h> |
2212 | - #include <sys/mman.h> |
2213 | -+#include <sys/types.h> |
2214 | -+#include <pwd.h> |
2215 | -+#include <grp.h> |
2216 | - |
2217 | - #include <glib.h> |
2218 | - #include <glib/gprintf.h> |
2219 | -@@ -87,6 +90,49 @@ void _netplan_g_string_free_to_file(GString* s, const char* rootdir, const char* |
2220 | - } |
2221 | - } |
2222 | - |
2223 | -+void _netplan_g_string_free_to_file_with_permissions(GString* s, const char* rootdir, const char* path, const char* suffix, const char* owner, const char* group, mode_t mode) |
2224 | -+{ |
2225 | -+ g_autofree char* full_path = NULL; |
2226 | -+ g_autofree char* path_suffix = NULL; |
2227 | -+ g_autofree char* contents = g_string_free(s, FALSE); |
2228 | -+ GError* error = NULL; |
2229 | -+ struct passwd* pw = NULL; |
2230 | -+ struct group* gr = NULL; |
2231 | -+ int ret = 0; |
2232 | -+ |
2233 | -+ path_suffix = g_strjoin(NULL, path, suffix, NULL); |
2234 | -+ full_path = g_build_path(G_DIR_SEPARATOR_S, rootdir ?: G_DIR_SEPARATOR_S, path_suffix, NULL); |
2235 | -+ _netplan_safe_mkdir_p_dir(full_path); |
2236 | -+ if (!g_file_set_contents_full(full_path, contents, -1, G_FILE_SET_CONTENTS_CONSISTENT | G_FILE_SET_CONTENTS_ONLY_EXISTING, mode, &error)) { |
2237 | -+ /* the mkdir() just succeeded, there is no sensible |
2238 | -+ * method to test this without root privileges, bind mounts, and |
2239 | -+ * simulating ENOSPC */ |
2240 | -+ // LCOV_EXCL_START |
2241 | -+ g_fprintf(stderr, "ERROR: cannot create file %s: %s\n", path, error->message); |
2242 | -+ exit(1); |
2243 | -+ // LCOV_EXCL_STOP |
2244 | -+ } |
2245 | -+ |
2246 | -+ /* Here we take the owner and group names and look up for their IDs in the passwd and group files. |
2247 | -+ * It's OK to fail to set the owners and mode as this code will be called from unit tests. |
2248 | -+ * The autopkgtests will check if the owner/group and mode are correctly set. |
2249 | -+ */ |
2250 | -+ pw = getpwnam(owner); |
2251 | -+ if (!pw) { |
2252 | -+ g_debug("Failed to determine the UID of user %s: %s", owner, strerror(errno)); // LCOV_EXCL_LINE |
2253 | -+ } |
2254 | -+ gr = getgrnam(group); |
2255 | -+ if (!gr) { |
2256 | -+ g_debug("Failed to determine the GID of group %s: %s", group, strerror(errno)); // LCOV_EXCL_LINE |
2257 | -+ } |
2258 | -+ if (pw && gr) { |
2259 | -+ ret = chown(full_path, pw->pw_uid, gr->gr_gid); |
2260 | -+ if (ret != 0) { |
2261 | -+ g_debug("Failed to set owner and group for file %s: %s", full_path, strerror(errno)); |
2262 | -+ } |
2263 | -+ } |
2264 | -+} |
2265 | -+ |
2266 | - /** |
2267 | - * Remove all files matching given glob. |
2268 | - */ |
2269 | -diff --git a/tests/generator/test_auth.py b/tests/generator/test_auth.py |
2270 | -index de23adb..d3d886c 100644 |
2271 | ---- a/tests/generator/test_auth.py |
2272 | -+++ b/tests/generator/test_auth.py |
2273 | -@@ -226,7 +226,7 @@ network={ |
2274 | - |
2275 | - with open(os.path.join(self.workdir.name, 'run/systemd/system/netplan-wpa-eth0.service')) as f: |
2276 | - self.assertEqual(f.read(), SD_WPA % {'iface': 'eth0', 'drivers': 'wired'}) |
2277 | -- self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o644) |
2278 | -+ self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o640) |
2279 | - self.assertTrue(os.path.islink(os.path.join( |
2280 | - self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-eth0.service'))) |
2281 | - |
2282 | -diff --git a/tests/generator/test_wifis.py b/tests/generator/test_wifis.py |
2283 | -index b875172..610782a 100644 |
2284 | ---- a/tests/generator/test_wifis.py |
2285 | -+++ b/tests/generator/test_wifis.py |
2286 | -@@ -140,7 +140,7 @@ network={ |
2287 | - self.workdir.name, 'run/systemd/system/netplan-wpa-wl0.service'))) |
2288 | - with open(os.path.join(self.workdir.name, 'run/systemd/system/netplan-wpa-wl0.service')) as f: |
2289 | - self.assertEqual(f.read(), SD_WPA % {'iface': 'wl0', 'drivers': 'nl80211,wext'}) |
2290 | -- self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o644) |
2291 | -+ self.assertEqual(stat.S_IMODE(os.fstat(f.fileno()).st_mode), 0o640) |
2292 | - self.assertTrue(os.path.islink(os.path.join( |
2293 | - self.workdir.name, 'run/systemd/system/systemd-networkd.service.wants/netplan-wpa-wl0.service'))) |
2294 | - |
2295 | -diff --git a/tests/integration/base.py b/tests/integration/base.py |
2296 | -index e9c366c..74f0682 100644 |
2297 | ---- a/tests/integration/base.py |
2298 | -+++ b/tests/integration/base.py |
2299 | -@@ -32,6 +32,8 @@ import shutil |
2300 | - import gi |
2301 | - import glob |
2302 | - import json |
2303 | -+import pwd |
2304 | -+import grp |
2305 | - |
2306 | - # make sure we point to libnetplan properly. |
2307 | - os.environ.update({'LD_LIBRARY_PATH': '.:{}'.format(os.environ.get('LD_LIBRARY_PATH'))}) |
2308 | -@@ -367,6 +369,89 @@ class IntegrationTestsBase(unittest.TestCase): |
2309 | - if state: |
2310 | - self.wait_output(['ip', 'addr', 'show', iface], state, 30) |
2311 | - |
2312 | -+ # Assert file permissions |
2313 | -+ self.assert_file_permissions() |
2314 | -+ |
2315 | -+ def assert_file_permissions(self): |
2316 | -+ """ Check if the generated files have the expected permissions """ |
2317 | -+ |
2318 | -+ nd_expected_mode = 0o100640 |
2319 | -+ nd_expected_owner = 'root' |
2320 | -+ nd_expected_group = 'systemd-network' |
2321 | -+ |
2322 | -+ sd_expected_mode = 0o100640 |
2323 | -+ sd_expected_owner = 'root' |
2324 | -+ sd_expected_group = 'root' |
2325 | -+ |
2326 | -+ udev_expected_mode = 0o100640 |
2327 | -+ udev_expected_owner = 'root' |
2328 | -+ udev_expected_group = 'root' |
2329 | -+ |
2330 | -+ nm_expected_mode = 0o100600 |
2331 | -+ nm_expected_owner = 'root' |
2332 | -+ nm_expected_group = 'root' |
2333 | -+ |
2334 | -+ wpa_expected_mode = 0o100600 |
2335 | -+ wpa_expected_owner = 'root' |
2336 | -+ wpa_expected_group = 'root' |
2337 | -+ |
2338 | -+ # Check systemd-networkd files |
2339 | -+ base_path = '/run/systemd/network' |
2340 | -+ files = glob.glob(f'{base_path}/*.network') + glob.glob(f'{base_path}/*.netdev') |
2341 | -+ for file in files: |
2342 | -+ res = os.stat(file) |
2343 | -+ user = pwd.getpwuid(res.st_uid) |
2344 | -+ group = grp.getgrgid(res.st_gid) |
2345 | -+ self.assertEqual(res.st_mode, nd_expected_mode, f'file {file}') |
2346 | -+ self.assertEqual(user.pw_name, nd_expected_owner, f'file {file}') |
2347 | -+ self.assertEqual(group.gr_name, nd_expected_group, f'file {file}') |
2348 | -+ |
2349 | -+ # Check Network Manager files |
2350 | -+ base_path = '/run/NetworkManager/system-connections' |
2351 | -+ files = glob.glob(f'{base_path}/*.nmconnection') |
2352 | -+ for file in files: |
2353 | -+ res = os.stat(file) |
2354 | -+ user = pwd.getpwuid(res.st_uid) |
2355 | -+ group = grp.getgrgid(res.st_gid) |
2356 | -+ self.assertEqual(res.st_mode, nm_expected_mode, f'file {file}') |
2357 | -+ self.assertEqual(user.pw_name, nm_expected_owner, f'file {file}') |
2358 | -+ self.assertEqual(group.gr_name, nm_expected_group, f'file {file}') |
2359 | -+ |
2360 | -+ # Check wpa_supplicant configuration files |
2361 | -+ base_path = '/run/netplan' |
2362 | -+ files = glob.glob(f'{base_path}/wpa-*.conf') |
2363 | -+ for file in files: |
2364 | -+ res = os.stat(file) |
2365 | -+ user = pwd.getpwuid(res.st_uid) |
2366 | -+ group = grp.getgrgid(res.st_gid) |
2367 | -+ self.assertEqual(res.st_mode, wpa_expected_mode, f'file {file}') |
2368 | -+ self.assertEqual(user.pw_name, wpa_expected_owner, f'file {file}') |
2369 | -+ self.assertEqual(group.gr_name, wpa_expected_group, f'file {file}') |
2370 | -+ |
2371 | -+ # Check systemd service unit files |
2372 | -+ base_path = '/run/systemd/system/' |
2373 | -+ files = glob.glob(f'{base_path}/netplan-*.service') |
2374 | -+ files += glob.glob(f'{base_path}/systemd-networkd-wait-online.service.d/*.conf') |
2375 | -+ for file in files: |
2376 | -+ res = os.stat(file) |
2377 | -+ user = pwd.getpwuid(res.st_uid) |
2378 | -+ group = grp.getgrgid(res.st_gid) |
2379 | -+ self.assertEqual(res.st_mode, sd_expected_mode, f'file {file}') |
2380 | -+ self.assertEqual(user.pw_name, sd_expected_owner, f'file {file}') |
2381 | -+ self.assertEqual(group.gr_name, sd_expected_group, f'file {file}') |
2382 | -+ |
2383 | -+ # Check systemd-udevd files |
2384 | -+ udev_path = '/run/udev/rules.d' |
2385 | -+ link_path = '/run/systemd/network' |
2386 | -+ files = glob.glob(f'{udev_path}/*-netplan*.rules') + glob.glob(f'{link_path}/*.link') |
2387 | -+ for file in files: |
2388 | -+ res = os.stat(file) |
2389 | -+ user = pwd.getpwuid(res.st_uid) |
2390 | -+ group = grp.getgrgid(res.st_gid) |
2391 | -+ self.assertEqual(res.st_mode, udev_expected_mode, f'file {file}') |
2392 | -+ self.assertEqual(user.pw_name, udev_expected_owner, f'file {file}') |
2393 | -+ self.assertEqual(group.gr_name, udev_expected_group, f'file {file}') |
2394 | -+ |
2395 | - def state(self, iface, state): |
2396 | - '''Tell generate_and_settle() to wait for a specific state''' |
2397 | - return iface + '/' + state |
2398 | diff --git a/debian/patches/lp2066258/0015-libnetplan-escape-control-characters.patch b/debian/patches/lp2066258/0015-libnetplan-escape-control-characters.patch |
2399 | deleted file mode 100644 |
2400 | index 349ad9e..0000000 |
2401 | --- a/debian/patches/lp2066258/0015-libnetplan-escape-control-characters.patch |
2402 | +++ /dev/null |
2403 | @@ -1,867 +0,0 @@ |
2404 | -From: Danilo Egea Gondolfo <danilogondolfo@gmail.com> |
2405 | -Date: Wed, 29 May 2024 14:50:55 +0100 |
2406 | -Subject: libnetplan: escape control characters |
2407 | - |
2408 | -Control characters are escaped in the parser using glib's g_strescape. |
2409 | -Quotes and backslashes were added to the list of exception. |
2410 | - |
2411 | -In places where double quotes are not escaped, such as netdef IDs as it |
2412 | -is allowed as interface names, they are escaped as needed when |
2413 | -generating back end configuration. |
2414 | - |
2415 | -To support escaping in wpa_supplicant configuration, the syntax for |
2416 | -setting the SSID was changed to 'ssid=P"string here"'. With that, |
2417 | -escaping is support in a printf-style. |
2418 | ---- |
2419 | - src/networkd.c | 32 +++++++++----- |
2420 | - src/nm.c | 21 ++++++---- |
2421 | - src/parse.c | 94 +++++++++++++++++++++++++++--------------- |
2422 | - src/util-internal.h | 3 ++ |
2423 | - src/util.c | 11 +++++ |
2424 | - tests/generator/test_auth.py | 20 ++++----- |
2425 | - tests/generator/test_common.py | 42 +++++++++++++++++-- |
2426 | - tests/generator/test_wifis.py | 78 +++++++++++++++++++++++++++-------- |
2427 | - 8 files changed, 217 insertions(+), 84 deletions(-) |
2428 | - |
2429 | -diff --git a/src/networkd.c b/src/networkd.c |
2430 | -index cb9547c..0414c92 100644 |
2431 | ---- a/src/networkd.c |
2432 | -+++ b/src/networkd.c |
2433 | -@@ -1089,7 +1089,8 @@ write_rules_file(const NetplanNetDefinition* def, const char* rootdir) |
2434 | - g_string_append(s, "SUBSYSTEM==\"net\", ACTION==\"add\", "); |
2435 | - |
2436 | - if (def->match.driver) { |
2437 | -- g_string_append_printf(s,"DRIVERS==\"%s\", ", def->match.driver); |
2438 | -+ g_autofree char* driver = _netplan_scrub_string(def->match.driver); |
2439 | -+ g_string_append_printf(s,"DRIVERS==\"%s\", ", driver); |
2440 | - } else { |
2441 | - g_string_append(s, "DRIVERS==\"?*\", "); |
2442 | - } |
2443 | -@@ -1097,7 +1098,8 @@ write_rules_file(const NetplanNetDefinition* def, const char* rootdir) |
2444 | - if (def->match.mac) |
2445 | - g_string_append_printf(s, "ATTR{address}==\"%s\", ", def->match.mac); |
2446 | - |
2447 | -- g_string_append_printf(s, "NAME=\"%s\"\n", def->set_name); |
2448 | -+ g_autofree char* set_name = _netplan_scrub_string(def->set_name); |
2449 | -+ g_string_append_printf(s, "NAME=\"%s\"\n", set_name); |
2450 | - |
2451 | - _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
2452 | - } |
2453 | -@@ -1185,10 +1187,12 @@ append_wpa_auth_conf(GString* s, const NetplanAuthenticationSettings* auth, cons |
2454 | - } |
2455 | - |
2456 | - if (auth->identity) { |
2457 | -- g_string_append_printf(s, " identity=\"%s\"\n", auth->identity); |
2458 | -+ g_autofree char* identity = _netplan_scrub_string(auth->identity); |
2459 | -+ g_string_append_printf(s, " identity=\"%s\"\n", identity); |
2460 | - } |
2461 | - if (auth->anonymous_identity) { |
2462 | -- g_string_append_printf(s, " anonymous_identity=\"%s\"\n", auth->anonymous_identity); |
2463 | -+ g_autofree char* anonymous_identity = _netplan_scrub_string(auth->anonymous_identity); |
2464 | -+ g_string_append_printf(s, " anonymous_identity=\"%s\"\n", anonymous_identity); |
2465 | - } |
2466 | - |
2467 | - char* psk = NULL; |
2468 | -@@ -1226,19 +1230,23 @@ append_wpa_auth_conf(GString* s, const NetplanAuthenticationSettings* auth, cons |
2469 | - } |
2470 | - } |
2471 | - if (auth->ca_certificate) { |
2472 | -- g_string_append_printf(s, " ca_cert=\"%s\"\n", auth->ca_certificate); |
2473 | -+ g_autofree char* ca_certificate = _netplan_scrub_string(auth->ca_certificate); |
2474 | -+ g_string_append_printf(s, " ca_cert=\"%s\"\n", ca_certificate); |
2475 | - } |
2476 | - if (auth->client_certificate) { |
2477 | -- g_string_append_printf(s, " client_cert=\"%s\"\n", auth->client_certificate); |
2478 | -+ g_autofree char* client_certificate = _netplan_scrub_string(auth->client_certificate); |
2479 | -+ g_string_append_printf(s, " client_cert=\"%s\"\n", client_certificate); |
2480 | - } |
2481 | - if (auth->client_key) { |
2482 | -- g_string_append_printf(s, " private_key=\"%s\"\n", auth->client_key); |
2483 | -+ g_autofree char* client_key = _netplan_scrub_string(auth->client_key); |
2484 | -+ g_string_append_printf(s, " private_key=\"%s\"\n", client_key); |
2485 | - } |
2486 | - if (auth->client_key_password) { |
2487 | - g_string_append_printf(s, " private_key_passwd=\"%s\"\n", auth->client_key_password); |
2488 | - } |
2489 | - if (auth->phase2_auth) { |
2490 | -- g_string_append_printf(s, " phase2=\"auth=%s\"\n", auth->phase2_auth); |
2491 | -+ g_autofree char* phase2_auth = _netplan_scrub_string(auth->phase2_auth); |
2492 | -+ g_string_append_printf(s, " phase2=\"auth=%s\"\n", phase2_auth); |
2493 | - } |
2494 | - return TRUE; |
2495 | - } |
2496 | -@@ -1287,14 +1295,16 @@ write_wpa_conf(const NetplanNetDefinition* def, const char* rootdir, GError** er |
2497 | - } |
2498 | - /* available as of wpa_supplicant version 0.6.7 */ |
2499 | - if (def->regulatory_domain) { |
2500 | -- g_string_append_printf(s, "country=%s\n", def->regulatory_domain); |
2501 | -+ g_autofree char* regdom = _netplan_scrub_string(def->regulatory_domain); |
2502 | -+ g_string_append_printf(s, "country=%s\n", regdom); |
2503 | - } |
2504 | - NetplanWifiAccessPoint* ap; |
2505 | - g_hash_table_iter_init(&iter, def->access_points); |
2506 | - while (g_hash_table_iter_next(&iter, NULL, (gpointer) &ap)) { |
2507 | - gchar* freq_config_str = ap->mode == NETPLAN_WIFI_MODE_ADHOC ? "frequency" : "freq_list"; |
2508 | -+ g_autofree char* ssid = _netplan_scrub_string(ap->ssid); |
2509 | - |
2510 | -- g_string_append_printf(s, "network={\n ssid=\"%s\"\n", ap->ssid); |
2511 | -+ g_string_append_printf(s, "network={\n ssid=P\"%s\"\n", ssid); |
2512 | - if (ap->bssid) { |
2513 | - g_string_append_printf(s, " bssid=%s\n", ap->bssid); |
2514 | - } |
2515 | -@@ -1341,7 +1351,7 @@ write_wpa_conf(const NetplanNetDefinition* def, const char* rootdir, GError** er |
2516 | - |
2517 | - /* wifi auth trumps netdef auth */ |
2518 | - if (ap->has_auth) { |
2519 | -- if (!append_wpa_auth_conf(s, &ap->auth, ap->ssid, error)) { |
2520 | -+ if (!append_wpa_auth_conf(s, &ap->auth, ssid, error)) { |
2521 | - g_string_free(s, TRUE); |
2522 | - return FALSE; |
2523 | - } |
2524 | -diff --git a/src/nm.c b/src/nm.c |
2525 | -index 8f1bf05..cb09559 100644 |
2526 | ---- a/src/nm.c |
2527 | -+++ b/src/nm.c |
2528 | -@@ -1082,28 +1082,30 @@ netplan_state_finish_nm_write( |
2529 | - GString *tmp = NULL; |
2530 | - guint unmanaged = nd->backend == NETPLAN_BACKEND_NM ? 0 : 1; |
2531 | - |
2532 | -+ g_autofree char* netdef_id = _netplan_scrub_string(nd->id); |
2533 | - /* Special case: manage or ignore any device of given type on empty "match: {}" stanza */ |
2534 | - if (nd->has_match && !nd->match.driver && !nd->match.mac && !nd->match.original_name) { |
2535 | - nm_type = type_str(nd); |
2536 | - g_assert(nm_type); |
2537 | - g_string_append_printf(nm_conf, "[device-netplan.%s.%s]\nmatch-device=type:%s\n" |
2538 | - "managed=%d\n\n", netplan_def_type_name(nd->type), |
2539 | -- nd->id, nm_type, !unmanaged); |
2540 | -+ netdef_id, nm_type, !unmanaged); |
2541 | - } |
2542 | - /* Normal case: manage or ignore devices by specific udev rules */ |
2543 | - else { |
2544 | - const gchar *prefix = "SUBSYSTEM==\"net\", ACTION==\"add|change|move\","; |
2545 | - const gchar *suffix = nd->backend == NETPLAN_BACKEND_NM ? " ENV{NM_UNMANAGED}=\"0\"\n" : " ENV{NM_UNMANAGED}=\"1\"\n"; |
2546 | - g_string_append_printf(udev_rules, "# netplan: network.%s.%s (on NetworkManager %s)\n", |
2547 | -- netplan_def_type_name(nd->type), nd->id, |
2548 | -+ netplan_def_type_name(nd->type), netdef_id, |
2549 | - unmanaged ? "deny-list" : "allow-list"); |
2550 | - /* Match by explicit interface name, if possible */ |
2551 | - if (nd->set_name) { |
2552 | - // simple case: explicit new interface name |
2553 | -- g_string_append_printf(udev_rules, "%s ENV{ID_NET_NAME}==\"%s\",%s", prefix, nd->set_name, suffix); |
2554 | -+ g_autofree char* set_name = _netplan_scrub_string(nd->set_name); |
2555 | -+ g_string_append_printf(udev_rules, "%s ENV{ID_NET_NAME}==\"%s\",%s", prefix, set_name, suffix); |
2556 | - } else if (!nd->has_match) { |
2557 | - // simple case: explicit netplan ID is interface name |
2558 | -- g_string_append_printf(udev_rules, "%s ENV{ID_NET_NAME}==\"%s\",%s", prefix, nd->id, suffix); |
2559 | -+ g_string_append_printf(udev_rules, "%s ENV{ID_NET_NAME}==\"%s\",%s", prefix, netdef_id, suffix); |
2560 | - } |
2561 | - /* Also, match by explicit (new) MAC, if available */ |
2562 | - if (nd->set_mac && _is_valid_macaddress(nd->set_mac)) { |
2563 | -@@ -1119,9 +1121,10 @@ netplan_state_finish_nm_write( |
2564 | - // match on original name glob |
2565 | - // TODO: maybe support matching on multiple name globs in the future (like drivers) |
2566 | - g_string_append(udev_rules, prefix); |
2567 | -- if (nd->match.original_name) |
2568 | -- g_string_append_printf(udev_rules, " ENV{ID_NET_NAME}==\"%s\",", nd->match.original_name); |
2569 | -- |
2570 | -+ if (nd->match.original_name) { |
2571 | -+ g_autofree char* original_name = _netplan_scrub_string(nd->match.original_name); |
2572 | -+ g_string_append_printf(udev_rules, " ENV{ID_NET_NAME}==\"%s\",", original_name); |
2573 | -+ } |
2574 | - // match on (explicit) MAC address. Yes this would be unique on its own, but we |
2575 | - // keep it within the "full match" to make the logic more comprehensible. |
2576 | - if (nd->match.mac) { |
2577 | -@@ -1139,7 +1142,9 @@ netplan_state_finish_nm_write( |
2578 | - g_strfreev(split); |
2579 | - } else |
2580 | - drivers = g_strdup(nd->match.driver); |
2581 | -- g_string_append_printf(udev_rules, " ENV{ID_NET_DRIVER}==\"%s\",", drivers); |
2582 | -+ |
2583 | -+ g_autofree char* escaped_drivers = _netplan_scrub_string(drivers); |
2584 | -+ g_string_append_printf(udev_rules, " ENV{ID_NET_DRIVER}==\"%s\",", escaped_drivers); |
2585 | - g_free(drivers); |
2586 | - } |
2587 | - g_string_append(udev_rules, suffix); |
2588 | -diff --git a/src/parse.c b/src/parse.c |
2589 | -index 7f7258c..55a236c 100644 |
2590 | ---- a/src/parse.c |
2591 | -+++ b/src/parse.c |
2592 | -@@ -55,6 +55,15 @@ |
2593 | - dst = g_strdup(src); \ |
2594 | - } } |
2595 | - |
2596 | -+/* |
2597 | -+ * We use g_strescape to escape control characters from the input. |
2598 | -+ * Besides control characters, g_strescape will also escape double quotes and backslashes. |
2599 | -+ * Quotes are escaped at configuration generation time as needed, as they might be part of passwords for example. |
2600 | -+ * Escaping backslashes in the parser affects "netplan set" as it will always escape \'s from |
2601 | -+ * the input and update YAMLs with all the \'s escaped again. |
2602 | -+*/ |
2603 | -+static char* STRESCAPE_EXCEPTIONS = "\"\\"; |
2604 | -+ |
2605 | - STATIC gboolean |
2606 | - insert_kv_into_hash(void *key, void *value, void *hash); |
2607 | - |
2608 | -@@ -361,7 +370,7 @@ handle_generic_str(NetplanParser* npp, yaml_node_t* node, void* entryptr, const |
2609 | - guint offset = GPOINTER_TO_UINT(data); |
2610 | - char** dest = (char**) ((void*) entryptr + offset); |
2611 | - g_free(*dest); |
2612 | -- *dest = g_strdup(scalar(node)); |
2613 | -+ *dest = g_strescape(scalar(node), STRESCAPE_EXCEPTIONS); |
2614 | - mark_data_as_dirty(npp, dest); |
2615 | - return TRUE; |
2616 | - } |
2617 | -@@ -481,22 +490,25 @@ handle_generic_map(NetplanParser *npp, yaml_node_t* node, const char* key_prefix |
2618 | - assert_type(npp, key, YAML_SCALAR_NODE); |
2619 | - assert_type(npp, value, YAML_SCALAR_NODE); |
2620 | - |
2621 | -+ g_autofree char* escaped_key = g_strescape(scalar(key), STRESCAPE_EXCEPTIONS); |
2622 | -+ g_autofree char* escaped_value = g_strescape(scalar(value), STRESCAPE_EXCEPTIONS); |
2623 | -+ |
2624 | - if (key_prefix && npp->null_fields) { |
2625 | - g_autofree char* full_key = NULL; |
2626 | -- full_key = g_strdup_printf("%s\t%s", key_prefix, key->data.scalar.value); |
2627 | -+ full_key = g_strdup_printf("%s\t%s", key_prefix, escaped_key); |
2628 | - if (g_hash_table_contains(npp->null_fields, full_key)) |
2629 | - continue; |
2630 | - } |
2631 | - |
2632 | - char* stored_value = NULL; |
2633 | -- if (g_hash_table_lookup_extended(*map, scalar(key), NULL, (void**)&stored_value)) { |
2634 | -+ if (g_hash_table_lookup_extended(*map, escaped_key, NULL, (void**)&stored_value)) { |
2635 | - /* We can safely skip this if it is the exact key/value match |
2636 | - * (probably caused by multi-pass processing) */ |
2637 | -- if (g_strcmp0(stored_value, scalar(value)) == 0) |
2638 | -+ if (g_strcmp0(stored_value, escaped_value) == 0) |
2639 | - continue; |
2640 | -- return yaml_error(npp, node, error, "duplicate map entry '%s'", scalar(key)); |
2641 | -+ return yaml_error(npp, node, error, "duplicate map entry '%s'", escaped_key); |
2642 | - } else |
2643 | -- g_hash_table_insert(*map, g_strdup(scalar(key)), g_strdup(scalar(value))); |
2644 | -+ g_hash_table_insert(*map, g_strdup(escaped_key), g_strdup(escaped_value)); |
2645 | - } |
2646 | - mark_data_as_dirty(npp, map); |
2647 | - |
2648 | -@@ -519,20 +531,26 @@ handle_generic_datalist(NetplanParser *npp, yaml_node_t* node, const char* key_p |
2649 | - for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) { |
2650 | - yaml_node_t* key, *value; |
2651 | - g_autofree char* full_key = NULL; |
2652 | -+ g_autofree char* escaped_key = NULL; |
2653 | -+ g_autofree char* escaped_value = NULL; |
2654 | - |
2655 | - key = yaml_document_get_node(&npp->doc, entry->key); |
2656 | - value = yaml_document_get_node(&npp->doc, entry->value); |
2657 | - |
2658 | - assert_type(npp, key, YAML_SCALAR_NODE); |
2659 | - assert_type(npp, value, YAML_SCALAR_NODE); |
2660 | -+ |
2661 | -+ escaped_key = g_strescape(scalar(key), STRESCAPE_EXCEPTIONS); |
2662 | -+ escaped_value = g_strescape(scalar(value), STRESCAPE_EXCEPTIONS); |
2663 | -+ |
2664 | - if (npp->null_fields && key_prefix) { |
2665 | -- full_key = g_strdup_printf("%s\t%s", key_prefix, scalar(key)); |
2666 | -+ full_key = g_strdup_printf("%s\t%s", key_prefix, escaped_key); |
2667 | - if (g_hash_table_contains(npp->null_fields, full_key)) |
2668 | - continue; |
2669 | - } |
2670 | - |
2671 | -- g_datalist_id_set_data_full(list, g_quark_from_string(scalar(key)), |
2672 | -- g_strdup(scalar(value)), g_free); |
2673 | -+ g_datalist_id_set_data_full(list, g_quark_from_string(escaped_key), |
2674 | -+ g_strdup(escaped_value), g_free); |
2675 | - } |
2676 | - mark_data_as_dirty(npp, list); |
2677 | - |
2678 | -@@ -888,13 +906,14 @@ handle_match_driver(NetplanParser* npp, yaml_node_t* node, __unused const char* |
2679 | - for (yaml_node_item_t *iter = node->data.sequence.items.start; iter < node->data.sequence.items.top; iter++) { |
2680 | - elem = yaml_document_get_node(&npp->doc, *iter); |
2681 | - assert_type(npp, elem, YAML_SCALAR_NODE); |
2682 | -- if (g_strrstr(scalar(elem), " ")) |
2683 | -+ g_autofree char* escaped_elem = g_strescape(scalar(elem), STRESCAPE_EXCEPTIONS); |
2684 | -+ if (g_strrstr(escaped_elem, " ")) |
2685 | - return yaml_error(npp, node, error, "A 'driver' glob cannot contain whitespace"); |
2686 | - |
2687 | - if (!sequence) |
2688 | -- sequence = g_string_new(scalar(elem)); |
2689 | -+ sequence = g_string_new(escaped_elem); |
2690 | - else |
2691 | -- g_string_append_printf(sequence, "\t%s", scalar(elem)); /* tab separated */ |
2692 | -+ g_string_append_printf(sequence, "\t%s", escaped_elem); /* tab separated */ |
2693 | - } |
2694 | - |
2695 | - if (!sequence) |
2696 | -@@ -926,7 +945,7 @@ handle_auth_str(NetplanParser* npp, yaml_node_t* node, const void* data, __unuse |
2697 | - guint offset = GPOINTER_TO_UINT(data); |
2698 | - char** dest = (char**) ((void*) npp->current.auth + offset); |
2699 | - g_free(*dest); |
2700 | -- *dest = g_strdup(scalar(node)); |
2701 | -+ *dest = g_strescape(scalar(node), STRESCAPE_EXCEPTIONS); |
2702 | - mark_data_as_dirty(npp, dest); |
2703 | - return TRUE; |
2704 | - } |
2705 | -@@ -1074,7 +1093,7 @@ handle_access_point_password(NetplanParser* npp, yaml_node_t* node, __unused con |
2706 | - |
2707 | - access_point->auth.pmf_mode = NETPLAN_AUTH_PMF_MODE_OPTIONAL; |
2708 | - g_free(access_point->auth.psk); |
2709 | -- access_point->auth.psk = g_strdup(scalar(node)); |
2710 | -+ access_point->auth.psk = g_strescape(scalar(node), STRESCAPE_EXCEPTIONS); |
2711 | - return TRUE; |
2712 | - } |
2713 | - |
2714 | -@@ -1458,6 +1477,7 @@ handle_wifi_access_points(NetplanParser* npp, yaml_node_t* node, const char* key |
2715 | - for (yaml_node_pair_t* entry = node->data.mapping.pairs.start; entry < node->data.mapping.pairs.top; entry++) { |
2716 | - NetplanWifiAccessPoint *access_point = NULL; |
2717 | - g_autofree char* full_key = NULL; |
2718 | -+ g_autofree char* escaped_key = NULL; |
2719 | - yaml_node_t* key, *value; |
2720 | - const gchar* ssid; |
2721 | - |
2722 | -@@ -1466,13 +1486,15 @@ handle_wifi_access_points(NetplanParser* npp, yaml_node_t* node, const char* key |
2723 | - value = yaml_document_get_node(&npp->doc, entry->value); |
2724 | - assert_type(npp, value, YAML_MAPPING_NODE); |
2725 | - |
2726 | -+ escaped_key = g_strescape(scalar(key), STRESCAPE_EXCEPTIONS); |
2727 | -+ |
2728 | - if (key_prefix && npp->null_fields) { |
2729 | -- full_key = g_strdup_printf("%s\t%s", key_prefix, key->data.scalar.value); |
2730 | -+ full_key = g_strdup_printf("%s\t%s", key_prefix, escaped_key); |
2731 | - if (g_hash_table_contains(npp->null_fields, full_key)) |
2732 | - continue; |
2733 | - } |
2734 | - |
2735 | -- ssid = scalar(key); |
2736 | -+ ssid = escaped_key; |
2737 | - |
2738 | - /* |
2739 | - * Delete the access-point if it already exists in the netdef and let the new |
2740 | -@@ -1630,15 +1652,16 @@ handle_nameservers_search(NetplanParser* npp, yaml_node_t* node, __unused const |
2741 | - for (yaml_node_item_t *i = node->data.sequence.items.start; i < node->data.sequence.items.top; i++) { |
2742 | - yaml_node_t *entry = yaml_document_get_node(&npp->doc, *i); |
2743 | - assert_type(npp, entry, YAML_SCALAR_NODE); |
2744 | -+ g_autofree char* escaped_entry = g_strescape(scalar(entry), STRESCAPE_EXCEPTIONS); |
2745 | - |
2746 | - if (!npp->current.netdef->search_domains) |
2747 | - npp->current.netdef->search_domains = g_array_new(FALSE, FALSE, sizeof(char*)); |
2748 | - |
2749 | -- if (!is_string_in_array(npp->current.netdef->search_domains, scalar(entry))) { |
2750 | -- char* s = g_strdup(scalar(entry)); |
2751 | -+ if (!is_string_in_array(npp->current.netdef->search_domains, escaped_entry)) { |
2752 | -+ char* s = g_strdup(escaped_entry); |
2753 | - g_array_append_val(npp->current.netdef->search_domains, s); |
2754 | - } else { |
2755 | -- g_debug("%s: Search domain '%s' has already been added", npp->current.netdef->id, scalar(entry)); |
2756 | -+ g_debug("%s: Search domain '%s' has already been added", npp->current.netdef->id, escaped_entry); |
2757 | - } |
2758 | - } |
2759 | - mark_data_as_dirty(npp, &npp->current.netdef->search_domains); |
2760 | -@@ -2744,19 +2767,19 @@ handle_ovs_bridge_controller_addresses(NetplanParser* npp, yaml_node_t* node, __ |
2761 | - |
2762 | - /* Format: [p]unix:file */ |
2763 | - if (is_unix && vec[1] != NULL && vec[2] == NULL) { |
2764 | -- char* s = g_strdup(scalar(entry)); |
2765 | -+ char* s = g_strescape(scalar(entry), STRESCAPE_EXCEPTIONS); |
2766 | - g_array_append_val(npp->current.netdef->ovs_settings.controller.addresses, s); |
2767 | - g_strfreev(vec); |
2768 | - continue; |
2769 | - /* Format tcp:host[:port] or ssl:host[:port] */ |
2770 | - } else if (is_host && validate_ovs_target(TRUE, vec[1])) { |
2771 | -- char* s = g_strdup(scalar(entry)); |
2772 | -+ char* s = g_strescape(scalar(entry), STRESCAPE_EXCEPTIONS); |
2773 | - g_array_append_val(npp->current.netdef->ovs_settings.controller.addresses, s); |
2774 | - g_strfreev(vec); |
2775 | - continue; |
2776 | - /* Format ptcp:[port][:host] or pssl:[port][:host] */ |
2777 | - } else if (is_port && validate_ovs_target(FALSE, vec[1])) { |
2778 | -- char* s = g_strdup(scalar(entry)); |
2779 | -+ char* s = g_strescape(scalar(entry), STRESCAPE_EXCEPTIONS); |
2780 | - g_array_append_val(npp->current.netdef->ovs_settings.controller.addresses, s); |
2781 | - g_strfreev(vec); |
2782 | - continue; |
2783 | -@@ -3070,6 +3093,8 @@ handle_network_ovs_settings_global_ports(NetplanParser* npp, yaml_node_t* node, |
2784 | - NetplanNetDefinition *component2 = NULL; |
2785 | - |
2786 | - for (yaml_node_item_t *iter = node->data.sequence.items.start; iter < node->data.sequence.items.top; iter++) { |
2787 | -+ g_autofree char* escaped_port = NULL; |
2788 | -+ g_autofree char* escaped_peer = NULL; |
2789 | - pair = yaml_document_get_node(&npp->doc, *iter); |
2790 | - assert_type(npp, pair, YAML_SEQUENCE_NODE); |
2791 | - |
2792 | -@@ -3084,14 +3109,17 @@ handle_network_ovs_settings_global_ports(NetplanParser* npp, yaml_node_t* node, |
2793 | - peer = yaml_document_get_node(&npp->doc, *(item+1)); |
2794 | - assert_type(npp, peer, YAML_SCALAR_NODE); |
2795 | - |
2796 | -- if (!g_strcmp0(scalar(port), scalar(peer))) |
2797 | -+ escaped_port = g_strescape(scalar(port), STRESCAPE_EXCEPTIONS); |
2798 | -+ escaped_peer = g_strescape(scalar(peer), STRESCAPE_EXCEPTIONS); |
2799 | -+ |
2800 | -+ if (!g_strcmp0(escaped_port, escaped_peer)) |
2801 | - return yaml_error(npp, peer, error, "Open vSwitch patch ports must be of different name"); |
2802 | - |
2803 | - /* Create port 1 netdef */ |
2804 | -- component1 = npp->parsed_defs ? g_hash_table_lookup(npp->parsed_defs, scalar(port)) : NULL; |
2805 | -+ component1 = npp->parsed_defs ? g_hash_table_lookup(npp->parsed_defs, escaped_port) : NULL; |
2806 | - if (!component1) { |
2807 | -- component1 = netplan_netdef_new(npp, scalar(port), NETPLAN_DEF_TYPE_PORT, NETPLAN_BACKEND_OVS); |
2808 | -- if (g_hash_table_remove(npp->missing_id, scalar(port))) |
2809 | -+ component1 = netplan_netdef_new(npp, escaped_port, NETPLAN_DEF_TYPE_PORT, NETPLAN_BACKEND_OVS); |
2810 | -+ if (g_hash_table_remove(npp->missing_id, escaped_port)) |
2811 | - npp->missing_ids_found++; |
2812 | - } |
2813 | - |
2814 | -@@ -3102,15 +3130,15 @@ handle_network_ovs_settings_global_ports(NetplanParser* npp, yaml_node_t* node, |
2815 | - component1->filepath = g_strdup(npp->current.filepath); |
2816 | - } |
2817 | - |
2818 | -- if (component1->peer && g_strcmp0(component1->peer, scalar(peer))) |
2819 | -+ if (component1->peer && g_strcmp0(component1->peer, escaped_peer)) |
2820 | - return yaml_error(npp, port, error, "Open vSwitch port '%s' is already assigned to peer '%s'", |
2821 | - component1->id, component1->peer); |
2822 | - |
2823 | - /* Create port 2 (peer) netdef */ |
2824 | -- component2 = npp->parsed_defs ? g_hash_table_lookup(npp->parsed_defs, scalar(peer)) : NULL; |
2825 | -+ component2 = npp->parsed_defs ? g_hash_table_lookup(npp->parsed_defs, escaped_peer) : NULL; |
2826 | - if (!component2) { |
2827 | -- component2 = netplan_netdef_new(npp, scalar(peer), NETPLAN_DEF_TYPE_PORT, NETPLAN_BACKEND_OVS); |
2828 | -- if (g_hash_table_remove(npp->missing_id, scalar(peer))) |
2829 | -+ component2 = netplan_netdef_new(npp, escaped_peer, NETPLAN_DEF_TYPE_PORT, NETPLAN_BACKEND_OVS); |
2830 | -+ if (g_hash_table_remove(npp->missing_id, escaped_peer)) |
2831 | - npp->missing_ids_found++; |
2832 | - } |
2833 | - |
2834 | -@@ -3121,16 +3149,16 @@ handle_network_ovs_settings_global_ports(NetplanParser* npp, yaml_node_t* node, |
2835 | - component2->filepath = g_strdup(npp->current.filepath); |
2836 | - } |
2837 | - |
2838 | -- if (component2->peer && g_strcmp0(component2->peer, scalar(port))) |
2839 | -+ if (component2->peer && g_strcmp0(component2->peer, escaped_port)) |
2840 | - return yaml_error(npp, peer, error, "Open vSwitch port '%s' is already assigned to peer '%s'", |
2841 | - component2->id, component2->peer); |
2842 | - |
2843 | - if (!component1->peer) { |
2844 | -- component1->peer = g_strdup(scalar(peer)); |
2845 | -+ component1->peer = g_strdup(escaped_peer); |
2846 | - component1->peer_link = component2; |
2847 | - } |
2848 | - if (!component2->peer) { |
2849 | -- component2->peer = g_strdup(scalar(port)); |
2850 | -+ component2->peer = g_strdup(escaped_port); |
2851 | - component2->peer_link = component1; |
2852 | - } |
2853 | - } |
2854 | -diff --git a/src/util-internal.h b/src/util-internal.h |
2855 | -index 7454e77..3b33c03 100644 |
2856 | ---- a/src/util-internal.h |
2857 | -+++ b/src/util-internal.h |
2858 | -@@ -185,3 +185,6 @@ _netplan_netdef_pertype_iter_next(struct netdef_pertype_iter* it); |
2859 | - |
2860 | - NETPLAN_INTERNAL void |
2861 | - _netplan_netdef_pertype_iter_free(struct netdef_pertype_iter* it); |
2862 | -+ |
2863 | -+gchar* |
2864 | -+_netplan_scrub_string(const char* content); |
2865 | -diff --git a/src/util.c b/src/util.c |
2866 | -index 46aba6e..86f8c1f 100644 |
2867 | ---- a/src/util.c |
2868 | -+++ b/src/util.c |
2869 | -@@ -1230,3 +1230,14 @@ _is_valid_macaddress(const char* value) |
2870 | - |
2871 | - return regexec(&re, value, 0, NULL, 0) == 0; |
2872 | - } |
2873 | -+ |
2874 | -+gchar* |
2875 | -+_netplan_scrub_string(const char* content) |
2876 | -+{ |
2877 | -+ GString* s = g_string_new(content); |
2878 | -+ |
2879 | -+ // Escape double quotes |
2880 | -+ g_string_replace(s, "\"", "\\\"", 0); |
2881 | -+ |
2882 | -+ return g_string_free(s, FALSE); |
2883 | -+} |
2884 | -diff --git a/tests/generator/test_auth.py b/tests/generator/test_auth.py |
2885 | -index d3d886c..bf0108a 100644 |
2886 | ---- a/tests/generator/test_auth.py |
2887 | -+++ b/tests/generator/test_auth.py |
2888 | -@@ -92,21 +92,21 @@ class TestNetworkd(TestBase): |
2889 | - self.assertIn('ctrl_interface=/run/wpa_supplicant', new_config) |
2890 | - self.assertIn(''' |
2891 | - network={ |
2892 | -- ssid="peer2peer" |
2893 | -+ ssid=P"peer2peer" |
2894 | - mode=1 |
2895 | - key_mgmt=NONE |
2896 | - } |
2897 | - ''', new_config) |
2898 | - self.assertIn(''' |
2899 | - network={ |
2900 | -- ssid="Luke's Home" |
2901 | -+ ssid=P"Luke's Home" |
2902 | - key_mgmt=WPA-PSK |
2903 | - psk="4lsos3kr1t" |
2904 | - } |
2905 | - ''', new_config) |
2906 | - self.assertIn(''' |
2907 | - network={ |
2908 | -- ssid="BobsHome" |
2909 | -+ ssid=P"BobsHome" |
2910 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
2911 | - ieee80211w=1 |
2912 | - psk=e03ce667c87bc81ca968d9120ca37f89eb09aec3c55b80386e5d772efd6b926e |
2913 | -@@ -114,14 +114,14 @@ network={ |
2914 | - ''', new_config) |
2915 | - self.assertIn(''' |
2916 | - network={ |
2917 | -- ssid="BillsHome" |
2918 | -+ ssid=P"BillsHome" |
2919 | - key_mgmt=WPA-PSK |
2920 | - psk=db3b0acf5653aeaddd5fe034fb9f07175b2864f847b005aaa2f09182d9411b04 |
2921 | - } |
2922 | - ''', new_config) |
2923 | - self.assertIn(''' |
2924 | - network={ |
2925 | -- ssid="workplace2" |
2926 | -+ ssid=P"workplace2" |
2927 | - key_mgmt=WPA-EAP |
2928 | - eap=PEAP |
2929 | - identity="joe@internal.example.com" |
2930 | -@@ -131,7 +131,7 @@ network={ |
2931 | - ''', new_config) |
2932 | - self.assertIn(''' |
2933 | - network={ |
2934 | -- ssid="workplace" |
2935 | -+ ssid=P"workplace" |
2936 | - key_mgmt=WPA-EAP |
2937 | - eap=TTLS |
2938 | - identity="joe@internal.example.com" |
2939 | -@@ -141,7 +141,7 @@ network={ |
2940 | - ''', new_config) |
2941 | - self.assertIn(''' |
2942 | - network={ |
2943 | -- ssid="workplacehashed" |
2944 | -+ ssid=P"workplacehashed" |
2945 | - key_mgmt=WPA-EAP |
2946 | - eap=TTLS |
2947 | - identity="joe@internal.example.com" |
2948 | -@@ -151,7 +151,7 @@ network={ |
2949 | - ''', new_config) |
2950 | - self.assertIn(''' |
2951 | - network={ |
2952 | -- ssid="customernet" |
2953 | -+ ssid=P"customernet" |
2954 | - key_mgmt=WPA-EAP |
2955 | - eap=TLS |
2956 | - identity="cert-joe@cust.example.com" |
2957 | -@@ -164,13 +164,13 @@ network={ |
2958 | - ''', new_config) |
2959 | - self.assertIn(''' |
2960 | - network={ |
2961 | -- ssid="opennet" |
2962 | -+ ssid=P"opennet" |
2963 | - key_mgmt=NONE |
2964 | - } |
2965 | - ''', new_config) |
2966 | - self.assertIn(''' |
2967 | - network={ |
2968 | -- ssid="Joe's Home" |
2969 | -+ ssid=P"Joe's Home" |
2970 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
2971 | - ieee80211w=1 |
2972 | - psk="s0s3kr1t" |
2973 | -diff --git a/tests/generator/test_common.py b/tests/generator/test_common.py |
2974 | -index d98eb32..8dfaf7d 100644 |
2975 | ---- a/tests/generator/test_common.py |
2976 | -+++ b/tests/generator/test_common.py |
2977 | -@@ -19,7 +19,7 @@ |
2978 | - import os |
2979 | - import textwrap |
2980 | - |
2981 | --from .base import TestBase, ND_DHCP4, ND_DHCP6, ND_DHCPYES, ND_EMPTY, NM_MANAGED, NM_UNMANAGED |
2982 | -+from .base import UDEV_NO_MAC_RULE, TestBase, ND_DHCP4, ND_DHCP6, ND_DHCPYES, ND_EMPTY, NM_MANAGED, NM_UNMANAGED |
2983 | - |
2984 | - |
2985 | - class TestNetworkd(TestBase): |
2986 | -@@ -810,6 +810,18 @@ RouteMetric=100 |
2987 | - UseMTU=true |
2988 | - '''}) |
2989 | - |
2990 | -+ def test_nd_udev_rules_escaped(self): |
2991 | -+ self.generate('''network: |
2992 | -+ version: 2 |
2993 | -+ renderer: NetworkManager |
2994 | -+ ethernets: |
2995 | -+ def1: |
2996 | -+ match: |
2997 | -+ driver: "abc\\"xyz\\n0\\n\\n1" |
2998 | -+ set-name: "eth\\"\\n\\nxyz\\n0"''', skip_generated_yaml_validation=True) |
2999 | -+ |
3000 | -+ self.assert_networkd_udev({'def1.rules': (UDEV_NO_MAC_RULE % ('abc\\"xyz\\n0\\n\\n1', 'eth\\"\\n\\nxyz\\n0'))}) |
3001 | -+ |
3002 | - |
3003 | - class TestNetworkManager(TestBase): |
3004 | - |
3005 | -@@ -1315,6 +1327,28 @@ dns=8.8.8.8; |
3006 | - method=ignore |
3007 | - '''}) |
3008 | - |
3009 | -+ def test_nm_udev_rules_escaped(self): |
3010 | -+ self.generate('''network: |
3011 | -+ version: 2 |
3012 | -+ renderer: networkd |
3013 | -+ ethernets: |
3014 | -+ eth0: |
3015 | -+ match: |
3016 | -+ name: "eth\\"0" |
3017 | -+ dhcp4: true''') |
3018 | -+ self.assert_nm_udev(NM_UNMANAGED % 'eth\\"0') |
3019 | -+ |
3020 | -+ self.generate('''network: |
3021 | -+ version: 2 |
3022 | -+ renderer: networkd |
3023 | -+ ethernets: |
3024 | -+ eth0: |
3025 | -+ match: |
3026 | -+ name: "eth0" |
3027 | -+ set-name: "eth\\n0" |
3028 | -+ dhcp4: true''', skip_generated_yaml_validation=True) |
3029 | -+ self.assert_nm_udev(NM_UNMANAGED % 'eth\\n0' + NM_UNMANAGED % 'eth0') |
3030 | -+ |
3031 | - |
3032 | - class TestForwardDeclaration(TestBase): |
3033 | - |
3034 | -@@ -1777,13 +1811,13 @@ LinkLocalAddressing=ipv6 |
3035 | - self.assert_wpa_supplicant("wlan0", """ctrl_interface=/run/wpa_supplicant |
3036 | - |
3037 | - network={ |
3038 | -- ssid="mynewwifi" |
3039 | -+ ssid=P"mynewwifi" |
3040 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
3041 | - ieee80211w=1 |
3042 | - psk="aaaaaaaa" |
3043 | - } |
3044 | - network={ |
3045 | -- ssid="mywifi" |
3046 | -+ ssid=P"mywifi" |
3047 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
3048 | - ieee80211w=1 |
3049 | - psk="aaaaaaaa" |
3050 | -@@ -1814,7 +1848,7 @@ network={ |
3051 | - self.assert_wpa_supplicant("wlan0", """ctrl_interface=/run/wpa_supplicant |
3052 | - |
3053 | - network={ |
3054 | -- ssid="mywifi" |
3055 | -+ ssid=P"mywifi" |
3056 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
3057 | - ieee80211w=1 |
3058 | - psk="bbbbbbbb" |
3059 | -diff --git a/tests/generator/test_wifis.py b/tests/generator/test_wifis.py |
3060 | -index 610782a..9f7359b 100644 |
3061 | ---- a/tests/generator/test_wifis.py |
3062 | -+++ b/tests/generator/test_wifis.py |
3063 | -@@ -65,7 +65,7 @@ class TestNetworkd(TestBase): |
3064 | - with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f: |
3065 | - new_config = f.read() |
3066 | - |
3067 | -- network = 'ssid="{}"\n freq_list='.format('band-no-channel2') |
3068 | -+ network = 'ssid=P"{}"\n freq_list='.format('band-no-channel2') |
3069 | - freqs_5GHz = [5610, 5310, 5620, 5320, 5630, 5640, 5340, 5035, 5040, 5045, 5055, 5060, 5660, 5680, 5670, 5080, 5690, |
3070 | - 5700, 5710, 5720, 5825, 5745, 5755, 5805, 5765, 5160, 5775, 5170, 5480, 5180, 5795, 5190, 5500, 5200, |
3071 | - 5510, 5210, 5520, 5220, 5530, 5230, 5540, 5240, 5550, 5250, 5560, 5260, 5570, 5270, 5580, 5280, 5590, |
3072 | -@@ -76,7 +76,7 @@ class TestNetworkd(TestBase): |
3073 | - for freq in freqs_5GHz: |
3074 | - self.assertRegex(new_config, '{}[ 0-9]*{}[ 0-9]*\n'.format(network, freq)) |
3075 | - |
3076 | -- network = 'ssid="{}"\n freq_list='.format('band-no-channel') |
3077 | -+ network = 'ssid=P"{}"\n freq_list='.format('band-no-channel') |
3078 | - freqs_24GHz = [2412, 2417, 2422, 2427, 2432, 2442, 2447, 2437, 2452, 2457, 2462, 2467, 2472, 2484] |
3079 | - freqs = new_config.split(network) |
3080 | - freqs = freqs[1].split('\n')[0] |
3081 | -@@ -86,20 +86,20 @@ class TestNetworkd(TestBase): |
3082 | - |
3083 | - self.assertIn(''' |
3084 | - network={ |
3085 | -- ssid="channel-no-band" |
3086 | -+ ssid=P"channel-no-band" |
3087 | - key_mgmt=NONE |
3088 | - } |
3089 | - ''', new_config) |
3090 | - self.assertIn(''' |
3091 | - network={ |
3092 | -- ssid="peer2peer" |
3093 | -+ ssid=P"peer2peer" |
3094 | - mode=1 |
3095 | - key_mgmt=NONE |
3096 | - } |
3097 | - ''', new_config) |
3098 | - self.assertIn(''' |
3099 | - network={ |
3100 | -- ssid="hidden-y" |
3101 | -+ ssid=P"hidden-y" |
3102 | - scan_ssid=1 |
3103 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
3104 | - ieee80211w=1 |
3105 | -@@ -108,7 +108,7 @@ network={ |
3106 | - ''', new_config) |
3107 | - self.assertIn(''' |
3108 | - network={ |
3109 | -- ssid="hidden-n" |
3110 | -+ ssid=P"hidden-n" |
3111 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
3112 | - ieee80211w=1 |
3113 | - psk="5ecur1ty" |
3114 | -@@ -116,7 +116,7 @@ network={ |
3115 | - ''', new_config) |
3116 | - self.assertIn(''' |
3117 | - network={ |
3118 | -- ssid="workplace" |
3119 | -+ ssid=P"workplace" |
3120 | - bssid=de:ad:be:ef:ca:fe |
3121 | - freq_list=5500 |
3122 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
3123 | -@@ -126,7 +126,7 @@ network={ |
3124 | - ''', new_config) |
3125 | - self.assertIn(''' |
3126 | - network={ |
3127 | -- ssid="Joe's Home" |
3128 | -+ ssid=P"Joe's Home" |
3129 | - bssid=00:11:22:33:44:55 |
3130 | - freq_list=2462 |
3131 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
3132 | -@@ -303,7 +303,7 @@ LinkLocalAddressing=ipv6 |
3133 | - self.assertIn(''' |
3134 | - wowlan_triggers=any disconnect magic_pkt gtk_rekey_failure eap_identity_req four_way_handshake rfkill_release |
3135 | - network={ |
3136 | -- ssid="homenet" |
3137 | -+ ssid=P"homenet" |
3138 | - key_mgmt=NONE |
3139 | - } |
3140 | - ''', new_config) |
3141 | -@@ -336,7 +336,7 @@ LinkLocalAddressing=ipv6 |
3142 | - new_config = f.read() |
3143 | - self.assertIn(''' |
3144 | - network={ |
3145 | -- ssid="homenet" |
3146 | -+ ssid=P"homenet" |
3147 | - key_mgmt=NONE |
3148 | - } |
3149 | - ''', new_config) |
3150 | -@@ -360,7 +360,7 @@ network={ |
3151 | - self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant |
3152 | - |
3153 | - network={ |
3154 | -- ssid="homenet" |
3155 | -+ ssid=P"homenet" |
3156 | - key_mgmt=SAE |
3157 | - ieee80211w=2 |
3158 | - psk="********" |
3159 | -@@ -387,7 +387,7 @@ network={ |
3160 | - self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant |
3161 | - |
3162 | - network={ |
3163 | -- ssid="homenet" |
3164 | -+ ssid=P"homenet" |
3165 | - key_mgmt=WPA-EAP WPA-EAP-SHA256 |
3166 | - eap=TLS |
3167 | - ieee80211w=1 |
3168 | -@@ -420,7 +420,7 @@ network={ |
3169 | - self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant |
3170 | - |
3171 | - network={ |
3172 | -- ssid="homenet" |
3173 | -+ ssid=P"homenet" |
3174 | - key_mgmt=WPA-EAP-SUITE-B-192 |
3175 | - eap=TLS |
3176 | - ieee80211w=2 |
3177 | -@@ -449,7 +449,7 @@ network={ |
3178 | - self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant |
3179 | - |
3180 | - network={ |
3181 | -- ssid="homenet" |
3182 | -+ ssid=P"homenet" |
3183 | - key_mgmt=IEEE8021X |
3184 | - eap=LEAP |
3185 | - identity="some-id" |
3186 | -@@ -473,7 +473,7 @@ network={ |
3187 | - self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant |
3188 | - |
3189 | - network={ |
3190 | -- ssid="homenet" |
3191 | -+ ssid=P"homenet" |
3192 | - key_mgmt=IEEE8021X |
3193 | - eap=PWD |
3194 | - identity="some-id" |
3195 | -@@ -498,7 +498,7 @@ network={ |
3196 | - self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant |
3197 | - |
3198 | - network={ |
3199 | -- ssid="homenet" |
3200 | -+ ssid=P"homenet" |
3201 | - key_mgmt=WPA-EAP |
3202 | - eap=LEAP |
3203 | - ieee80211w=1 |
3204 | -@@ -508,6 +508,48 @@ network={ |
3205 | - } |
3206 | - """) |
3207 | - |
3208 | -+ def test_escaping_special_characters(self): |
3209 | -+ self.generate('''network: |
3210 | -+ version: 2 |
3211 | -+ wifis: |
3212 | -+ wl0: |
3213 | -+ regulatory-domain: "abc\\n\\n321\\n\\"123" |
3214 | -+ access-points: |
3215 | -+ "abc\\n\\n123\\"x\\ry\\bz": |
3216 | -+ password: "abc\\n\\n\\n\\"123" |
3217 | -+ auth: |
3218 | -+ key-management: eap |
3219 | -+ method: leap |
3220 | -+ anonymous-identity: "abc\\n\\n321\\n\\"123" |
3221 | -+ identity: "abc\\n\\n321\\n\\"123" |
3222 | -+ password: "abc\\n\\n\\n\\"123" |
3223 | -+ ca-certificate: "abc\\n\\n321\\n\\"123" |
3224 | -+ client-certificate: "abc\\n\\n321\\n\\"123" |
3225 | -+ client-key: "abc\\n\\n321\\n\\"123" |
3226 | -+ client-key-password: "abc\\n\\n321\\n\\"123" |
3227 | -+ phase2-auth: "abc\\n\\n321\\n\\"123" |
3228 | -+ ''', skip_generated_yaml_validation=True) |
3229 | -+ |
3230 | -+ self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant |
3231 | -+ |
3232 | -+country=abc\\n\\n321\\n\\"123 |
3233 | -+network={ |
3234 | -+ ssid=P"abc\\n\\n123\\\"x\\ry\\bz" |
3235 | -+ key_mgmt=WPA-EAP |
3236 | -+ eap=LEAP |
3237 | -+ ieee80211w=1 |
3238 | -+ identity="abc\\n\\n321\\n\\\"123" |
3239 | -+ anonymous_identity="abc\\n\\n321\\n\\\"123" |
3240 | -+ psk="abc\\n\\n\\n\"123" |
3241 | -+ password="abc\\n\\n\\n\"123" |
3242 | -+ ca_cert="abc\\n\\n321\\n\\\"123" |
3243 | -+ client_cert="abc\\n\\n321\\n\\\"123" |
3244 | -+ private_key="abc\\n\\n321\\n\\\"123" |
3245 | -+ private_key_passwd="abc\\n\\n321\\n\"123" |
3246 | -+ phase2="auth=abc\\n\\n321\\n\\\"123" |
3247 | -+} |
3248 | -+""") |
3249 | -+ |
3250 | - |
3251 | - class TestNetworkManager(TestBase): |
3252 | - |
3253 | -@@ -790,7 +832,7 @@ mode=adhoc |
3254 | - self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant |
3255 | - |
3256 | - network={ |
3257 | -- ssid="homenet" |
3258 | -+ ssid=P"homenet" |
3259 | - frequency=2442 |
3260 | - mode=1 |
3261 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
3262 | -@@ -814,7 +856,7 @@ network={ |
3263 | - self.assert_wpa_supplicant("wl0", """ctrl_interface=/run/wpa_supplicant |
3264 | - |
3265 | - network={ |
3266 | -- ssid="homenet" |
3267 | -+ ssid=P"homenet" |
3268 | - frequency=5035 |
3269 | - mode=1 |
3270 | - key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
3271 | diff --git a/debian/patches/lp2066258/0016-backends-escape-file-paths.patch b/debian/patches/lp2066258/0016-backends-escape-file-paths.patch |
3272 | deleted file mode 100644 |
3273 | index 1e42442..0000000 |
3274 | --- a/debian/patches/lp2066258/0016-backends-escape-file-paths.patch |
3275 | +++ /dev/null |
3276 | @@ -1,288 +0,0 @@ |
3277 | -From: Danilo Egea Gondolfo <danilogondolfo@gmail.com> |
3278 | -Date: Thu, 23 May 2024 15:54:43 +0100 |
3279 | -Subject: backends: escape file paths |
3280 | - |
3281 | -Escape strings used to build paths with g_uri_escape_string(). |
3282 | -systemd_escape() could also be used but it has the downside of calling |
3283 | -an external program and, by default, it escapes dashes (which are |
3284 | -present in files generated from Network Manager for example). |
3285 | ---- |
3286 | - src/networkd.c | 13 +++++---- |
3287 | - src/nm.c | 5 ++-- |
3288 | - src/openvswitch.c | 19 ++++++------ |
3289 | - src/util.c | 7 +++-- |
3290 | - tests/generator/test_common.py | 66 ++++++++++++++++++++++++++++++++++++++++++ |
3291 | - tests/generator/test_ovs.py | 24 +++++++++++++++ |
3292 | - 6 files changed, 115 insertions(+), 19 deletions(-) |
3293 | - |
3294 | -diff --git a/src/networkd.c b/src/networkd.c |
3295 | -index 0414c92..1da684b 100644 |
3296 | ---- a/src/networkd.c |
3297 | -+++ b/src/networkd.c |
3298 | -@@ -1065,7 +1065,8 @@ STATIC void |
3299 | - write_rules_file(const NetplanNetDefinition* def, const char* rootdir) |
3300 | - { |
3301 | - GString* s = NULL; |
3302 | -- g_autofree char* path = g_strjoin(NULL, "run/udev/rules.d/99-netplan-", def->id, ".rules", NULL); |
3303 | -+ g_autofree char* escaped_netdef_id = g_uri_escape_string(def->id, NULL, TRUE); |
3304 | -+ g_autofree char* path = g_strjoin(NULL, "run/udev/rules.d/99-netplan-", escaped_netdef_id, ".rules", NULL); |
3305 | - |
3306 | - /* do we need to write a .rules file? |
3307 | - * It's only required for reliably setting the name of a physical device |
3308 | -@@ -1282,7 +1283,8 @@ write_wpa_conf(const NetplanNetDefinition* def, const char* rootdir, GError** er |
3309 | - { |
3310 | - GHashTableIter iter; |
3311 | - GString* s = g_string_new("ctrl_interface=/run/wpa_supplicant\n\n"); |
3312 | -- g_autofree char* path = g_strjoin(NULL, "run/netplan/wpa-", def->id, ".conf", NULL); |
3313 | -+ g_autofree char* escaped_netdef_id = g_uri_escape_string(def->id, NULL, TRUE); |
3314 | -+ g_autofree char* path = g_strjoin(NULL, "run/netplan/wpa-", escaped_netdef_id, ".conf", NULL); |
3315 | - |
3316 | - g_debug("%s: Creating wpa_supplicant configuration file %s", def->id, path); |
3317 | - if (def->type == NETPLAN_DEF_TYPE_WIFI) { |
3318 | -@@ -1394,7 +1396,8 @@ _netplan_netdef_write_networkd( |
3319 | - GError** error) |
3320 | - { |
3321 | - /* TODO: make use of netplan_netdef_get_output_filename() */ |
3322 | -- g_autofree char* path_base = g_strjoin(NULL, "run/systemd/network/10-netplan-", def->id, NULL); |
3323 | -+ g_autofree char* escaped_netdef_id = g_uri_escape_string(def->id, NULL, TRUE); |
3324 | -+ g_autofree char* path_base = g_strjoin(NULL, "run/systemd/network/10-netplan-", escaped_netdef_id, NULL); |
3325 | - SET_OPT_OUT_PTR(has_been_written, FALSE); |
3326 | - |
3327 | - /* We want this for all backends when renaming, as *.link and *.rules files are |
3328 | -@@ -1416,8 +1419,8 @@ _netplan_netdef_write_networkd( |
3329 | - } |
3330 | - |
3331 | - if (def->type == NETPLAN_DEF_TYPE_WIFI || def->has_auth) { |
3332 | -- g_autofree char* link = g_strjoin(NULL, rootdir ?: "", "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa-", def->id, ".service", NULL); |
3333 | -- g_autofree char* slink = g_strjoin(NULL, "/run/systemd/system/netplan-wpa-", def->id, ".service", NULL); |
3334 | -+ g_autofree char* link = g_strjoin(NULL, rootdir ?: "", "/run/systemd/system/systemd-networkd.service.wants/netplan-wpa-", escaped_netdef_id, ".service", NULL); |
3335 | -+ g_autofree char* slink = g_strjoin(NULL, "/run/systemd/system/netplan-wpa-", escaped_netdef_id, ".service", NULL); |
3336 | - if (def->type == NETPLAN_DEF_TYPE_WIFI && def->has_match) { |
3337 | - g_set_error(error, NETPLAN_BACKEND_ERROR, NETPLAN_ERROR_UNSUPPORTED, "ERROR: %s: networkd backend does not support wifi with match:, only by interface name\n", def->id); |
3338 | - return FALSE; |
3339 | -diff --git a/src/nm.c b/src/nm.c |
3340 | -index cb09559..aa22567 100644 |
3341 | ---- a/src/nm.c |
3342 | -+++ b/src/nm.c |
3343 | -@@ -932,10 +932,11 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir, |
3344 | - g_datalist_foreach((GData**)&def->backend_settings.passthrough, write_fallback_key_value, kf); |
3345 | - } |
3346 | - |
3347 | -+ g_autofree char* escaped_netdef_id = g_uri_escape_string(def->id, NULL, TRUE); |
3348 | - if (ap) { |
3349 | - g_autofree char* escaped_ssid = g_uri_escape_string(ap->ssid, NULL, TRUE); |
3350 | - /* TODO: make use of netplan_netdef_get_output_filename() */ |
3351 | -- conf_path = g_strjoin(NULL, "run/NetworkManager/system-connections/netplan-", def->id, "-", escaped_ssid, ".nmconnection", NULL); |
3352 | -+ conf_path = g_strjoin(NULL, "run/NetworkManager/system-connections/netplan-", escaped_netdef_id, "-", escaped_ssid, ".nmconnection", NULL); |
3353 | - |
3354 | - g_key_file_set_string(kf, "wifi", "ssid", ap->ssid); |
3355 | - if (ap->mode < NETPLAN_WIFI_MODE_OTHER) |
3356 | -@@ -970,7 +971,7 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir, |
3357 | - } |
3358 | - } else { |
3359 | - /* TODO: make use of netplan_netdef_get_output_filename() */ |
3360 | -- conf_path = g_strjoin(NULL, "run/NetworkManager/system-connections/netplan-", def->id, ".nmconnection", NULL); |
3361 | -+ conf_path = g_strjoin(NULL, "run/NetworkManager/system-connections/netplan-", escaped_netdef_id, ".nmconnection", NULL); |
3362 | - if (def->has_auth) { |
3363 | - write_dot1x_auth_parameters(&def->auth, kf); |
3364 | - } |
3365 | -diff --git a/src/openvswitch.c b/src/openvswitch.c |
3366 | -index 2ab77e7..0a81a59 100644 |
3367 | ---- a/src/openvswitch.c |
3368 | -+++ b/src/openvswitch.c |
3369 | -@@ -31,9 +31,9 @@ |
3370 | - STATIC gboolean |
3371 | - write_ovs_systemd_unit(const char* id, const GString* cmds, const char* rootdir, gboolean physical, gboolean cleanup, const char* dependency, GError** error) |
3372 | - { |
3373 | -- g_autofree gchar* id_escaped = NULL; |
3374 | -- g_autofree char* link = g_strjoin(NULL, rootdir ?: "", "/run/systemd/system/systemd-networkd.service.wants/netplan-ovs-", id, ".service", NULL); |
3375 | -- g_autofree char* path = g_strjoin(NULL, "/run/systemd/system/netplan-ovs-", id, ".service", NULL); |
3376 | -+ g_autofree char* escaped_netdef_id = g_uri_escape_string(id, NULL, TRUE); |
3377 | -+ g_autofree char* link = g_strjoin(NULL, rootdir ?: "", "/run/systemd/system/systemd-networkd.service.wants/netplan-ovs-", escaped_netdef_id, ".service", NULL); |
3378 | -+ g_autofree char* path = g_strjoin(NULL, "/run/systemd/system/netplan-ovs-", escaped_netdef_id, ".service", NULL); |
3379 | - |
3380 | - GString* s = g_string_new("[Unit]\n"); |
3381 | - g_string_append_printf(s, "Description=OpenVSwitch configuration for %s\n", id); |
3382 | -@@ -42,9 +42,8 @@ write_ovs_systemd_unit(const char* id, const GString* cmds, const char* rootdir, |
3383 | - g_string_append_printf(s, "Wants=ovsdb-server.service\n"); |
3384 | - g_string_append_printf(s, "After=ovsdb-server.service\n"); |
3385 | - if (physical) { |
3386 | -- id_escaped = systemd_escape((char*) id); |
3387 | -- g_string_append_printf(s, "Requires=sys-subsystem-net-devices-%s.device\n", id_escaped); |
3388 | -- g_string_append_printf(s, "After=sys-subsystem-net-devices-%s.device\n", id_escaped); |
3389 | -+ g_string_append_printf(s, "Requires=sys-subsystem-net-devices-%s.device\n", escaped_netdef_id); |
3390 | -+ g_string_append_printf(s, "After=sys-subsystem-net-devices-%s.device\n", escaped_netdef_id); |
3391 | - } |
3392 | - if (!cleanup) { |
3393 | - g_string_append_printf(s, "After=netplan-ovs-cleanup.service\n"); |
3394 | -@@ -54,8 +53,9 @@ write_ovs_systemd_unit(const char* id, const GString* cmds, const char* rootdir, |
3395 | - } |
3396 | - g_string_append(s, "Before=network.target\nWants=network.target\n"); |
3397 | - if (dependency) { |
3398 | -- g_string_append_printf(s, "Requires=netplan-ovs-%s.service\n", dependency); |
3399 | -- g_string_append_printf(s, "After=netplan-ovs-%s.service\n", dependency); |
3400 | -+ g_autofree char* escaped_dependency = g_uri_escape_string(dependency, NULL, TRUE); |
3401 | -+ g_string_append_printf(s, "Requires=netplan-ovs-%s.service\n", escaped_dependency); |
3402 | -+ g_string_append_printf(s, "After=netplan-ovs-%s.service\n", escaped_dependency); |
3403 | - } |
3404 | - |
3405 | - g_string_append(s, "\n[Service]\nType=oneshot\nTimeoutStartSec=10s\n"); |
3406 | -@@ -325,6 +325,7 @@ _netplan_netdef_write_ovs(const NetplanState* np_state, const NetplanNetDefiniti |
3407 | - gchar* dependency = NULL; |
3408 | - const char* type = netplan_type_to_table_name(def->type); |
3409 | - g_autofree char* base_config_path = NULL; |
3410 | -+ g_autofree char* escaped_netdef_id = g_uri_escape_string(def->id, NULL, TRUE); |
3411 | - char* value = NULL; |
3412 | - const NetplanOVSSettings* settings = &np_state->ovs_settings; |
3413 | - |
3414 | -@@ -415,7 +416,7 @@ _netplan_netdef_write_ovs(const NetplanState* np_state, const NetplanNetDefiniti |
3415 | - |
3416 | - /* Try writing out a base config */ |
3417 | - /* TODO: make use of netplan_netdef_get_output_filename() */ |
3418 | -- base_config_path = g_strjoin(NULL, "run/systemd/network/10-netplan-", def->id, NULL); |
3419 | -+ base_config_path = g_strjoin(NULL, "run/systemd/network/10-netplan-", escaped_netdef_id, NULL); |
3420 | - if (!_netplan_netdef_write_network_file(np_state, def, rootdir, base_config_path, has_been_written, error)) |
3421 | - return FALSE; |
3422 | - } else { |
3423 | -diff --git a/src/util.c b/src/util.c |
3424 | -index 86f8c1f..d8477a5 100644 |
3425 | ---- a/src/util.c |
3426 | -+++ b/src/util.c |
3427 | -@@ -663,17 +663,18 @@ ssize_t |
3428 | - netplan_netdef_get_output_filename(const NetplanNetDefinition* netdef, const char* ssid, char* out_buffer, size_t out_buf_size) |
3429 | - { |
3430 | - g_autofree gchar* conf_path = NULL; |
3431 | -+ g_autofree char* escaped_netdef_id = g_uri_escape_string(netdef->id, NULL, TRUE); |
3432 | - |
3433 | - if (netdef->backend == NETPLAN_BACKEND_NM) { |
3434 | - if (ssid) { |
3435 | - g_autofree char* escaped_ssid = g_uri_escape_string(ssid, NULL, TRUE); |
3436 | -- conf_path = g_strjoin(NULL, "/run/NetworkManager/system-connections/netplan-", netdef->id, "-", escaped_ssid, ".nmconnection", NULL); |
3437 | -+ conf_path = g_strjoin(NULL, "/run/NetworkManager/system-connections/netplan-", escaped_netdef_id, "-", escaped_ssid, ".nmconnection", NULL); |
3438 | - } else { |
3439 | -- conf_path = g_strjoin(NULL, "/run/NetworkManager/system-connections/netplan-", netdef->id, ".nmconnection", NULL); |
3440 | -+ conf_path = g_strjoin(NULL, "/run/NetworkManager/system-connections/netplan-", escaped_netdef_id, ".nmconnection", NULL); |
3441 | - } |
3442 | - |
3443 | - } else if (netdef->backend == NETPLAN_BACKEND_NETWORKD || netdef->backend == NETPLAN_BACKEND_OVS) { |
3444 | -- conf_path = g_strjoin(NULL, "/run/systemd/network/10-netplan-", netdef->id, ".network", NULL); |
3445 | -+ conf_path = g_strjoin(NULL, "/run/systemd/network/10-netplan-", escaped_netdef_id, ".network", NULL); |
3446 | - } |
3447 | - |
3448 | - if (conf_path) |
3449 | -diff --git a/tests/generator/test_common.py b/tests/generator/test_common.py |
3450 | -index 8dfaf7d..f87fef0 100644 |
3451 | ---- a/tests/generator/test_common.py |
3452 | -+++ b/tests/generator/test_common.py |
3453 | -@@ -822,6 +822,47 @@ UseMTU=true |
3454 | - |
3455 | - self.assert_networkd_udev({'def1.rules': (UDEV_NO_MAC_RULE % ('abc\\"xyz\\n0\\n\\n1', 'eth\\"\\n\\nxyz\\n0'))}) |
3456 | - |
3457 | -+ def test_nd_file_paths_escaped(self): |
3458 | -+ self.generate('''network: |
3459 | -+ version: 2 |
3460 | -+ ethernets: |
3461 | -+ "abc/../../xyz0": |
3462 | -+ match: |
3463 | -+ driver: "drv" |
3464 | -+ set-name: "eth123"''') |
3465 | -+ |
3466 | -+ self.assert_networkd_udev({'abc%2F..%2F..%2Fxyz0.rules': (UDEV_NO_MAC_RULE % ('drv', 'eth123'))}) |
3467 | -+ self.assert_networkd({'abc%2F..%2F..%2Fxyz0.network': '''[Match]\nDriver=drv |
3468 | -+Name=eth123 |
3469 | -+ |
3470 | -+[Network] |
3471 | -+LinkLocalAddressing=ipv6 |
3472 | -+''', |
3473 | -+ 'abc%2F..%2F..%2Fxyz0.link': '''[Match]\nDriver=drv\n |
3474 | -+[Link] |
3475 | -+Name=eth123 |
3476 | -+WakeOnLan=off |
3477 | -+'''}) |
3478 | -+ |
3479 | -+ self.generate('''network: |
3480 | -+ version: 2 |
3481 | -+ wifis: |
3482 | -+ "abc/../../xyz0": |
3483 | -+ dhcp4: true |
3484 | -+ access-points: |
3485 | -+ "mywifi": |
3486 | -+ password: "aaaaaaaa"''') |
3487 | -+ |
3488 | -+ self.assert_wpa_supplicant("abc%2F..%2F..%2Fxyz0", """ctrl_interface=/run/wpa_supplicant |
3489 | -+ |
3490 | -+network={ |
3491 | -+ ssid=P"mywifi" |
3492 | -+ key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE |
3493 | -+ ieee80211w=1 |
3494 | -+ psk="aaaaaaaa" |
3495 | -+} |
3496 | -+""") |
3497 | -+ |
3498 | - |
3499 | - class TestNetworkManager(TestBase): |
3500 | - |
3501 | -@@ -1349,6 +1390,31 @@ method=ignore |
3502 | - dhcp4: true''', skip_generated_yaml_validation=True) |
3503 | - self.assert_nm_udev(NM_UNMANAGED % 'eth\\n0' + NM_UNMANAGED % 'eth0') |
3504 | - |
3505 | -+ def test_nm_file_paths_escaped(self): |
3506 | -+ self.generate('''network: |
3507 | -+ version: 2 |
3508 | -+ renderer: NetworkManager |
3509 | -+ ethernets: |
3510 | -+ "abc/../../xyz0": |
3511 | -+ match: |
3512 | -+ driver: "drv" |
3513 | -+ set-name: "eth123"''') |
3514 | -+ |
3515 | -+ self.assert_nm({'abc%2F..%2F..%2Fxyz0': '''[connection] |
3516 | -+id=netplan-abc/../../xyz0 |
3517 | -+type=ethernet |
3518 | -+interface-name=eth123 |
3519 | -+ |
3520 | -+[ethernet] |
3521 | -+wake-on-lan=0 |
3522 | -+ |
3523 | -+[ipv4] |
3524 | -+method=link-local |
3525 | -+ |
3526 | -+[ipv6] |
3527 | -+method=ignore |
3528 | -+'''}) |
3529 | -+ |
3530 | - |
3531 | - class TestForwardDeclaration(TestBase): |
3532 | - |
3533 | -diff --git a/tests/generator/test_ovs.py b/tests/generator/test_ovs.py |
3534 | -index a6ebbcb..7116705 100644 |
3535 | ---- a/tests/generator/test_ovs.py |
3536 | -+++ b/tests/generator/test_ovs.py |
3537 | -@@ -1110,3 +1110,27 @@ ExecStart=/usr/bin/ovs-vsctl set Bridge br123 external-ids:netplan/global/set-co |
3538 | - - [portname, portname] |
3539 | - ''', expect_fail=True) |
3540 | - self.assertIn('Open vSwitch patch ports must be of different name', err) |
3541 | -+ |
3542 | -+ def test_file_paths_escaped(self): |
3543 | -+ self.generate('''network: |
3544 | -+ version: 2 |
3545 | -+ bridges: |
3546 | -+ "abc/../../123": |
3547 | -+ openvswitch: {} |
3548 | -+ vlans: |
3549 | -+ "abc/../../123.100": |
3550 | -+ id: 100 |
3551 | -+ link: "abc/../../123" |
3552 | -+''') |
3553 | -+ self.assert_ovs({'abc%2F..%2F..%2F123.service': OVS_BR_EMPTY % {'iface': 'abc/../../123'}, |
3554 | -+ 'abc%2F..%2F..%2F123.100.service': OVS_VIRTUAL % {'iface': 'abc/../../123.100', 'extra': |
3555 | -+ '''Requires=netplan-ovs-abc%2F..%2F..%2F123.service |
3556 | -+After=netplan-ovs-abc%2F..%2F..%2F123.service |
3557 | -+ |
3558 | -+[Service] |
3559 | -+Type=oneshot |
3560 | -+TimeoutStartSec=10s |
3561 | -+ExecStart=/usr/bin/ovs-vsctl --may-exist add-br abc/../../123.100 abc/../../123 100 |
3562 | -+ExecStart=/usr/bin/ovs-vsctl set Interface abc/../../123.100 external-ids:netplan=true |
3563 | -+'''}, |
3564 | -+ 'cleanup.service': OVS_CLEANUP % {'iface': 'cleanup'}}) |
3565 | diff --git a/debian/patches/lp2066258/0017-backends-escape-semicolons-in-service-units.patch b/debian/patches/lp2066258/0017-backends-escape-semicolons-in-service-units.patch |
3566 | deleted file mode 100644 |
3567 | index 008ffad..0000000 |
3568 | --- a/debian/patches/lp2066258/0017-backends-escape-semicolons-in-service-units.patch |
3569 | +++ /dev/null |
3570 | @@ -1,386 +0,0 @@ |
3571 | -From: Danilo Egea Gondolfo <danilogondolfo@gmail.com> |
3572 | -Date: Fri, 24 May 2024 14:11:12 +0100 |
3573 | -Subject: backends: escape semicolons in service units |
3574 | - |
3575 | -Semicolons separated from other words by a combination of spaces and/or |
3576 | -tabs will be escaped. |
3577 | ---- |
3578 | - src/networkd.c | 10 +++++++ |
3579 | - src/openvswitch.c | 3 +++ |
3580 | - src/sriov.c | 6 +++++ |
3581 | - src/util-internal.h | 3 +++ |
3582 | - src/util.c | 34 +++++++++++++++++++++++ |
3583 | - tests/ctests/test_netplan_misc.c | 45 +++++++++++++++++++++++++++++++ |
3584 | - tests/generator/test_args.py | 54 +++++++++++++++++++++++++++++++++++++ |
3585 | - tests/generator/test_ovs.py | 58 ++++++++++++++++++++++++++++++++++++++++ |
3586 | - tests/test_sriov.py | 40 +++++++++++++++++++++++++++ |
3587 | - 9 files changed, 253 insertions(+) |
3588 | - |
3589 | -diff --git a/src/networkd.c b/src/networkd.c |
3590 | -index 1da684b..1145547 100644 |
3591 | ---- a/src/networkd.c |
3592 | -+++ b/src/networkd.c |
3593 | -@@ -389,6 +389,9 @@ write_regdom(const NetplanNetDefinition* def, const char* rootdir, GError** erro |
3594 | - g_string_append(s, "\n[Service]\nType=oneshot\n"); |
3595 | - g_string_append_printf(s, "ExecStart="SBINDIR"/iw reg set %s\n", def->regulatory_domain); |
3596 | - |
3597 | -+ g_autofree char* new_s = _netplan_scrub_systemd_unit_contents(s->str); |
3598 | -+ g_string_free(s, TRUE); |
3599 | -+ s = g_string_new(new_s); |
3600 | - _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
3601 | - _netplan_safe_mkdir_p_dir(link); |
3602 | - if (symlink(path, link) < 0 && errno != EEXIST) { |
3603 | -@@ -1275,6 +1278,10 @@ write_wpa_unit(const NetplanNetDefinition* def, const char* rootdir) |
3604 | - } else { |
3605 | - g_string_append(s, " -Dnl80211,wext\n"); |
3606 | - } |
3607 | -+ |
3608 | -+ g_autofree char* new_s = _netplan_scrub_systemd_unit_contents(s->str); |
3609 | -+ g_string_free(s, TRUE); |
3610 | -+ s = g_string_new(new_s); |
3611 | - _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
3612 | - } |
3613 | - |
3614 | -@@ -1544,6 +1551,9 @@ _netplan_networkd_write_wait_online(const NetplanState* np_state, const char* ro |
3615 | - } |
3616 | - g_string_append(content, "\n"); |
3617 | - |
3618 | -+ g_autofree char* new_content = _netplan_scrub_systemd_unit_contents(content->str); |
3619 | -+ g_string_free(content, TRUE); |
3620 | -+ content = g_string_new(new_content); |
3621 | - _netplan_g_string_free_to_file_with_permissions(content, rootdir, override, NULL, "root", "root", 0640); |
3622 | - g_hash_table_destroy(non_optional_interfaces); |
3623 | - return TRUE; |
3624 | -diff --git a/src/openvswitch.c b/src/openvswitch.c |
3625 | -index 0a81a59..4723230 100644 |
3626 | ---- a/src/openvswitch.c |
3627 | -+++ b/src/openvswitch.c |
3628 | -@@ -66,6 +66,9 @@ write_ovs_systemd_unit(const char* id, const GString* cmds, const char* rootdir, |
3629 | - g_string_append(s, "StartLimitBurst=0\n"); |
3630 | - g_string_append(s, cmds->str); |
3631 | - |
3632 | -+ g_autofree char* new_s = _netplan_scrub_systemd_unit_contents(s->str); |
3633 | -+ g_string_free(s, TRUE); |
3634 | -+ s = g_string_new(new_s); |
3635 | - _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
3636 | - |
3637 | - _netplan_safe_mkdir_p_dir(link); |
3638 | -diff --git a/src/sriov.c b/src/sriov.c |
3639 | -index 213f124..0435731 100644 |
3640 | ---- a/src/sriov.c |
3641 | -+++ b/src/sriov.c |
3642 | -@@ -54,6 +54,9 @@ write_sriov_rebind_systemd_unit(GHashTable* pfs, const char* rootdir, GError** e |
3643 | - g_string_truncate(interfaces, interfaces->len-1); /* cut trailing whitespace */ |
3644 | - g_string_append_printf(s, "ExecStart=" SBINDIR "/netplan rebind --debug %s\n", interfaces->str); |
3645 | - |
3646 | -+ g_autofree char* new_s = _netplan_scrub_systemd_unit_contents(s->str); |
3647 | -+ g_string_free(s, TRUE); |
3648 | -+ s = g_string_new(new_s); |
3649 | - _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
3650 | - g_string_free(interfaces, TRUE); |
3651 | - |
3652 | -@@ -90,6 +93,9 @@ write_sriov_apply_systemd_unit(GHashTable* pfs, const char* rootdir, GError** er |
3653 | - g_string_append(s, "\n[Service]\nType=oneshot\n"); |
3654 | - g_string_append_printf(s, "ExecStart=" SBINDIR "/netplan apply --sriov-only\n"); |
3655 | - |
3656 | -+ g_autofree char* new_s = _netplan_scrub_systemd_unit_contents(s->str); |
3657 | -+ g_string_free(s, TRUE); |
3658 | -+ s = g_string_new(new_s); |
3659 | - _netplan_g_string_free_to_file_with_permissions(s, rootdir, path, NULL, "root", "root", 0640); |
3660 | - |
3661 | - _netplan_safe_mkdir_p_dir(link); |
3662 | -diff --git a/src/util-internal.h b/src/util-internal.h |
3663 | -index 3b33c03..6e602e1 100644 |
3664 | ---- a/src/util-internal.h |
3665 | -+++ b/src/util-internal.h |
3666 | -@@ -188,3 +188,6 @@ _netplan_netdef_pertype_iter_free(struct netdef_pertype_iter* it); |
3667 | - |
3668 | - gchar* |
3669 | - _netplan_scrub_string(const char* content); |
3670 | -+ |
3671 | -+gchar* |
3672 | -+_netplan_scrub_systemd_unit_contents(const char* content); |
3673 | -diff --git a/src/util.c b/src/util.c |
3674 | -index d8477a5..2c52e21 100644 |
3675 | ---- a/src/util.c |
3676 | -+++ b/src/util.c |
3677 | -@@ -1242,3 +1242,37 @@ _netplan_scrub_string(const char* content) |
3678 | - |
3679 | - return g_string_free(s, FALSE); |
3680 | - } |
3681 | -+ |
3682 | -+STATIC gboolean |
3683 | -+_is_space_or_tab(char c) |
3684 | -+{ |
3685 | -+ return c == ' ' || c == '\t'; |
3686 | -+} |
3687 | -+ |
3688 | -+char* |
3689 | -+_netplan_scrub_systemd_unit_contents(const char* content) |
3690 | -+{ |
3691 | -+ size_t content_len = strlen(content); |
3692 | -+ // Assume a few replacements will happen to reduce reallocation |
3693 | -+ GString* s = g_string_sized_new(content_len + 8); |
3694 | -+ |
3695 | -+ // Append the first character of "content" to the result string |
3696 | -+ g_string_append_len(s, content, 1); |
3697 | -+ |
3698 | -+ // Walk from the second element to the one before the last looking for isolated semicolons |
3699 | -+ // A semicolon is isolated if it's surrounded by either tabs or spaces |
3700 | -+ const char* p = content + 1; |
3701 | -+ while (p < (content + content_len - 1)) { |
3702 | -+ if (*p == ';' && _is_space_or_tab(*(p - 1)) && _is_space_or_tab(*(p + 1))) { |
3703 | -+ g_string_append_len(s, "\\;", 2); |
3704 | -+ } else { |
3705 | -+ g_string_append_len(s, p, 1); |
3706 | -+ } |
3707 | -+ p++; |
3708 | -+ } |
3709 | -+ |
3710 | -+ // Append the last character of "content" to the result string |
3711 | -+ g_string_append_len(s, p, 1); |
3712 | -+ |
3713 | -+ return g_string_free(s, FALSE); |
3714 | -+} |
3715 | -diff --git a/tests/ctests/test_netplan_misc.c b/tests/ctests/test_netplan_misc.c |
3716 | -index 9607662..06676da 100644 |
3717 | ---- a/tests/ctests/test_netplan_misc.c |
3718 | -+++ b/tests/ctests/test_netplan_misc.c |
3719 | -@@ -502,6 +502,50 @@ test_normalize_ip_address(__unused void** state) |
3720 | - assert_string_equal(normalize_ip_address("0.0.0.0/0", AF_INET), "0.0.0.0/0"); |
3721 | - } |
3722 | - |
3723 | -+void |
3724 | -+test_scrub_systemd_unit_content(__unused void** state) |
3725 | -+{ |
3726 | -+ char* str = ";abc;"; |
3727 | -+ char* res = _netplan_scrub_systemd_unit_contents(str); |
3728 | -+ assert_string_equal(res, ";abc;"); |
3729 | -+ g_free(res); |
3730 | -+ |
3731 | -+ str = ";;;;"; |
3732 | -+ res = _netplan_scrub_systemd_unit_contents(str); |
3733 | -+ assert_string_equal(res, ";;;;"); |
3734 | -+ g_free(res); |
3735 | -+ |
3736 | -+ str = " ;;;; "; |
3737 | -+ res = _netplan_scrub_systemd_unit_contents(str); |
3738 | -+ assert_string_equal(res, " ;;;; "); |
3739 | -+ g_free(res); |
3740 | -+ |
3741 | -+ str = "; ; ; ; ;"; |
3742 | -+ res = _netplan_scrub_systemd_unit_contents(str); |
3743 | -+ assert_string_equal(res, "; \\; \\; \\; ;"); |
3744 | -+ g_free(res); |
3745 | -+ |
3746 | -+ str = " ; ; ; ; ; "; |
3747 | -+ res = _netplan_scrub_systemd_unit_contents(str); |
3748 | -+ assert_string_equal(res, " \\; \\; \\; \\; \\; "); |
3749 | -+ g_free(res); |
3750 | -+ |
3751 | -+ str = "a ; ; ; ; ; b"; |
3752 | -+ res = _netplan_scrub_systemd_unit_contents(str); |
3753 | -+ assert_string_equal(res, "a \\; \\; \\; \\; \\; b"); |
3754 | -+ g_free(res); |
3755 | -+ |
3756 | -+ str = "\t;\t; ;\t; \t ;\t "; |
3757 | -+ res = _netplan_scrub_systemd_unit_contents(str); |
3758 | -+ assert_string_equal(res, "\t\\;\t\\; \\;\t\\; \t \\;\t "); |
3759 | -+ g_free(res); |
3760 | -+ |
3761 | -+ str = "\t;\t;\t;\t;\t;\t"; |
3762 | -+ res = _netplan_scrub_systemd_unit_contents(str); |
3763 | -+ assert_string_equal(res, "\t\\;\t\\;\t\\;\t\\;\t\\;\t"); |
3764 | -+ g_free(res); |
3765 | -+} |
3766 | -+ |
3767 | - int |
3768 | - setup(__unused void** state) |
3769 | - { |
3770 | -@@ -539,6 +583,7 @@ main() |
3771 | - cmocka_unit_test(test_normalize_ip_address), |
3772 | - cmocka_unit_test(test_util_get_link_local_true), |
3773 | - cmocka_unit_test(test_util_get_link_local_false), |
3774 | -+ cmocka_unit_test(test_scrub_systemd_unit_content), |
3775 | - }; |
3776 | - |
3777 | - return cmocka_run_group_tests(tests, setup, tear_down); |
3778 | -diff --git a/tests/generator/test_args.py b/tests/generator/test_args.py |
3779 | -index c2d0d98..45f51d0 100644 |
3780 | ---- a/tests/generator/test_args.py |
3781 | -+++ b/tests/generator/test_args.py |
3782 | -@@ -260,3 +260,57 @@ ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/s |
3783 | - except subprocess.CalledProcessError as e: |
3784 | - self.assertEqual(e.returncode, 1) |
3785 | - self.assertIn(b'can not be called directly', e.output) |
3786 | -+ |
3787 | -+ def test_systemd_generator_escaping(self): |
3788 | -+ conf = os.path.join(self.confdir, 'a.yaml') |
3789 | -+ os.makedirs(os.path.dirname(conf)) |
3790 | -+ with open(conf, 'w') as f: |
3791 | -+ f.write('''network: |
3792 | -+ version: 2 |
3793 | -+ ethernets: |
3794 | -+ lo: |
3795 | -+ match: |
3796 | -+ name: lo |
3797 | -+ set-name: "a ; b\\t; c\\t; d \\n 123 ; echo " |
3798 | -+ addresses: ["127.0.0.1/8", "::1/128"]''') |
3799 | -+ os.chmod(conf, mode=0o600) |
3800 | -+ outdir = os.path.join(self.workdir.name, 'out') |
3801 | -+ os.mkdir(outdir) |
3802 | -+ |
3803 | -+ generator = os.path.join(self.workdir.name, 'systemd', 'system-generators', 'netplan') |
3804 | -+ os.makedirs(os.path.dirname(generator)) |
3805 | -+ os.symlink(exe_generate, generator) |
3806 | -+ |
3807 | -+ subprocess.check_call([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir]) |
3808 | -+ n = os.path.join(self.workdir.name, 'run', 'systemd', 'network', '10-netplan-lo.network') |
3809 | -+ self.assertTrue(os.path.exists(n)) |
3810 | -+ os.unlink(n) |
3811 | -+ |
3812 | -+ # should auto-enable networkd and -wait-online |
3813 | -+ service_dir = os.path.join(self.workdir.name, 'run', 'systemd', 'system') |
3814 | -+ self.assertTrue(os.path.islink(os.path.join( |
3815 | -+ outdir, 'multi-user.target.wants', 'systemd-networkd.service'))) |
3816 | -+ self.assertTrue(os.path.islink(os.path.join( |
3817 | -+ outdir, 'network-online.target.wants', 'systemd-networkd-wait-online.service'))) |
3818 | -+ override = os.path.join(service_dir, 'systemd-networkd-wait-online.service.d', '10-netplan.conf') |
3819 | -+ self.assertTrue(os.path.isfile(override)) |
3820 | -+ with open(override, 'r') as f: |
3821 | -+ # eth99 does not exist on the system, so will not be listed |
3822 | -+ self.assertEqual(f.read(), '''[Unit] |
3823 | -+ConditionPathIsSymbolicLink=/run/systemd/generator/network-online.target.wants/systemd-networkd-wait-online.service |
3824 | -+ |
3825 | -+[Service] |
3826 | -+ExecStart= |
3827 | -+ExecStart=/lib/systemd/systemd-networkd-wait-online -i a \\; b\\t; c\\t; d \\n 123 \\; echo :degraded\n''') |
3828 | -+ |
3829 | -+ # should be a no-op the second time while the stamp exists |
3830 | -+ out = subprocess.check_output([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir], |
3831 | -+ stderr=subprocess.STDOUT) |
3832 | -+ self.assertFalse(os.path.exists(n)) |
3833 | -+ self.assertIn(b'netplan generate already ran', out) |
3834 | -+ |
3835 | -+ # after removing the stamp it generates again, and not trip over the |
3836 | -+ # existing enablement symlink |
3837 | -+ os.unlink(os.path.join(outdir, 'netplan.stamp')) |
3838 | -+ subprocess.check_output([generator, '--root-dir', self.workdir.name, outdir, outdir, outdir]) |
3839 | -+ self.assertTrue(os.path.exists(n)) |
3840 | -diff --git a/tests/generator/test_ovs.py b/tests/generator/test_ovs.py |
3841 | -index 7116705..4393cea 100644 |
3842 | ---- a/tests/generator/test_ovs.py |
3843 | -+++ b/tests/generator/test_ovs.py |
3844 | -@@ -1134,3 +1134,61 @@ ExecStart=/usr/bin/ovs-vsctl --may-exist add-br abc/../../123.100 abc/../../123 |
3845 | - ExecStart=/usr/bin/ovs-vsctl set Interface abc/../../123.100 external-ids:netplan=true |
3846 | - '''}, |
3847 | - 'cleanup.service': OVS_CLEANUP % {'iface': 'cleanup'}}) |
3848 | -+ |
3849 | -+ def test_control_characters_and_semicolons_escaping(self): |
3850 | -+ self.generate('''network: |
3851 | -+ version: 2 |
3852 | -+ bridges: # bridges first, to trigger multi-pass processing |
3853 | -+ ovs0: |
3854 | -+ interfaces: [eth0, eth1] |
3855 | -+ openvswitch: {} |
3856 | -+ ethernets: |
3857 | -+ eth0: |
3858 | -+ openvswitch: |
3859 | -+ external-ids: |
3860 | -+ "a\\n1\\ra": " ; a ; 1 ;a; ;b\\t;\\t3 ;\\ta\\t; 1" |
3861 | -+ other-config: |
3862 | -+ "a\\n1\\ra": " ; a ; 1 ;a; ;b\\t;\\t3 ;\\ta\\t; 1" |
3863 | -+ dhcp6: true |
3864 | -+ eth1: |
3865 | -+ dhcp4: true |
3866 | -+ openvswitch: |
3867 | -+ other-config: |
3868 | -+ disable-in-band: false\n''', skip_generated_yaml_validation=True) |
3869 | -+ self.assert_ovs({'ovs0.service': OVS_VIRTUAL % {'iface': 'ovs0', 'extra': ''' |
3870 | -+[Service] |
3871 | -+Type=oneshot |
3872 | -+TimeoutStartSec=10s |
3873 | -+ExecStart=/usr/bin/ovs-vsctl --may-exist add-br ovs0 |
3874 | -+ExecStart=/usr/bin/ovs-vsctl --may-exist add-port ovs0 eth1 |
3875 | -+ExecStart=/usr/bin/ovs-vsctl --may-exist add-port ovs0 eth0 |
3876 | -+''' + OVS_BR_DEFAULT % {'iface': 'ovs0'}}, |
3877 | -+ 'eth0.service': OVS_PHYSICAL % {'iface': 'eth0', 'extra': '''\ |
3878 | -+Requires=netplan-ovs-ovs0.service |
3879 | -+After=netplan-ovs-ovs0.service |
3880 | -+ |
3881 | -+[Service] |
3882 | -+Type=oneshot |
3883 | -+TimeoutStartSec=10s |
3884 | -+ExecStart=/usr/bin/ovs-vsctl set Interface eth0 external-ids:a\\n1\\ra= \\; a \\; 1 ;a; ;b\\t;\\t3 ;\\ta\\t; 1 |
3885 | -+ExecStart=/usr/bin/ovs-vsctl set Interface eth0 external-ids:netplan/external-ids/a\\n1\\ra=,;,a,;,1,;a;,;b\\t;\\t3,;\\ta\\t;,1 |
3886 | -+ExecStart=/usr/bin/ovs-vsctl set Interface eth0 other-config:a\\n1\\ra= \\; a \\; 1 ;a; ;b\\t;\\t3 ;\\ta\\t; 1 |
3887 | -+ExecStart=/usr/bin/ovs-vsctl set Interface eth0 external-ids:netplan/other-config/a\\n1\\ra=,;,a,;,1,;a;,;b\\t;\\t3,;\\ta\\t;,1 |
3888 | -+'''}, |
3889 | -+ 'eth1.service': OVS_PHYSICAL % {'iface': 'eth1', 'extra': '''\ |
3890 | -+Requires=netplan-ovs-ovs0.service |
3891 | -+After=netplan-ovs-ovs0.service |
3892 | -+ |
3893 | -+[Service] |
3894 | -+Type=oneshot |
3895 | -+TimeoutStartSec=10s |
3896 | -+ExecStart=/usr/bin/ovs-vsctl set Interface eth1 other-config:disable-in-band=false |
3897 | -+ExecStart=/usr/bin/ovs-vsctl set Interface eth1 external-ids:netplan/other-config/disable-in-band=false |
3898 | -+'''}, |
3899 | -+ 'cleanup.service': OVS_CLEANUP % {'iface': 'cleanup'}}) |
3900 | -+ # Confirm that the networkd config is still sane |
3901 | -+ self.assert_networkd({'ovs0.network': ND_EMPTY % ('ovs0', 'ipv6'), |
3902 | -+ 'eth0.network': (ND_DHCP6 % 'eth0') |
3903 | -+ .replace('LinkLocalAddressing=ipv6', 'LinkLocalAddressing=no\nBridge=ovs0'), |
3904 | -+ 'eth1.network': (ND_DHCP4 % 'eth1') |
3905 | -+ .replace('LinkLocalAddressing=ipv6', 'LinkLocalAddressing=no\nBridge=ovs0')}) |
3906 | -diff --git a/tests/test_sriov.py b/tests/test_sriov.py |
3907 | -index e842396..a7102fb 100644 |
3908 | ---- a/tests/test_sriov.py |
3909 | -+++ b/tests/test_sriov.py |
3910 | -@@ -1172,6 +1172,46 @@ Before=network-pre.target |
3911 | - After=sys-subsystem-net-devices-enblue.device |
3912 | - After=sys-subsystem-net-devices-engreen.device |
3913 | - |
3914 | -+[Service] |
3915 | -+Type=oneshot |
3916 | -+ExecStart=/usr/sbin/netplan apply --sriov-only |
3917 | -+'''}) |
3918 | -+ |
3919 | -+ def test_escaping_semicolons_from_unit_file(self): |
3920 | -+ ''' Check if semicolons and line breaks are properly escaped in the generated |
3921 | -+ systemd service unit. |
3922 | -+ ''' |
3923 | -+ self.generate('''network: |
3924 | -+ version: 2 |
3925 | -+ ethernets: |
3926 | -+ engreen: |
3927 | -+ embedded-switch-mode: switchdev |
3928 | -+ delay-virtual-functions-rebind: true |
3929 | -+ enblue: |
3930 | -+ match: {driver: dummy_driver} |
3931 | -+ set-name: ";en ; a\\t;\\tb ;\\tc\\t; d; \\n;\\nabc" |
3932 | -+ embedded-switch-mode: legacy |
3933 | -+ delay-virtual-functions-rebind: true |
3934 | -+ virtual-function-count: 4 |
3935 | -+ sriov_vf0: |
3936 | -+ link: engreen''', skip_generated_yaml_validation=True) |
3937 | -+ self.assert_sriov({'rebind.service': '''[Unit] |
3938 | -+Description=(Re-)bind SR-IOV Virtual Functions to their driver |
3939 | -+After=network.target |
3940 | -+After=netplan-sriov-apply.service |
3941 | -+After=sys-subsystem-net-devices-;en \\; a\\t;\\tb ;\\tc\\t; d; \\n;\\nabc.device |
3942 | -+After=sys-subsystem-net-devices-engreen.device |
3943 | -+ |
3944 | -+[Service] |
3945 | -+Type=oneshot |
3946 | -+ExecStart=/usr/sbin/netplan rebind --debug ;en \\; a\\t;\\tb ;\\tc\\t; d; \\n;\\nabc engreen |
3947 | -+''', 'apply.service': '''[Unit] |
3948 | -+Description=Apply SR-IOV configuration |
3949 | -+DefaultDependencies=no |
3950 | -+Before=network-pre.target |
3951 | -+After=sys-subsystem-net-devices-;en \\; a\\t;\\tb ;\\tc\\t; d; \\n;\\nabc.device |
3952 | -+After=sys-subsystem-net-devices-engreen.device |
3953 | -+ |
3954 | - [Service] |
3955 | - Type=oneshot |
3956 | - ExecStart=/usr/sbin/netplan apply --sriov-only |
3957 | diff --git a/debian/patches/series b/debian/patches/series |
3958 | index c40a500..63aae32 100644 |
3959 | --- a/debian/patches/series |
3960 | +++ b/debian/patches/series |
3961 | @@ -1,17 +1,4 @@ |
3962 | python-limited-stable-api.patch |
3963 | -0002-parse-nm-add-a-workaround-for-the-DoT-DNS-option.patch |
3964 | -lp2060311/0003-util-fix-potential-NULL-pointer-assert.patch |
3965 | -lp2060311/0004-networkd-add-wait-online-enumeration-utils.patch |
3966 | -lp2060311/0005-generate-enable-systemd-networkd-wait-online-for-non.patch |
3967 | -lp2060311/0006-CLI-utils-Do-not-ask-for-daemon-reload-password-inte.patch |
3968 | -lp2060311/0007-CLI-generate-call-daemon-reload-after-re-generating-.patch |
3969 | -lp2060311/0008-wait-online-Do-not-block-on-loopback-interface.patch |
3970 | -lp2060311/0009-generate-Do-not-touch-wait-online-if-we-don-t-have-a.patch |
3971 | -lp2060311/0010-wait-online-wait-for-existing-interfaces-only-and-do.patch |
3972 | -lp2060311/0011-wait-online-account-for-DHCPv4-v6-addresses.patch |
3973 | -lp2060311/0012-wait-online-do-not-require-virtual-devices-to-be-cre.patch |
3974 | -lp2060311/0013-wait-online-recognize-that-bridge-bond-members-will-.patch |
3975 | -lp2065738/0014-libnetplan-use-more-restrictive-file-permissions.patch |
3976 | -lp2066258/0015-libnetplan-escape-control-characters.patch |
3977 | -lp2066258/0016-backends-escape-file-paths.patch |
3978 | -lp2066258/0017-backends-escape-semicolons-in-service-units.patch |
3979 | +0002-CLI-apply-call-udevadm-trigger-using-action-add-Clos.patch |
3980 | +0003-Revert-wait-online-disabled-wait-online-for-stable-1.patch |
3981 | +0004-generate-avoid-calling-udevadm-control-reload-LP-199.patch |
3982 | diff --git a/debian/tests/control b/debian/tests/control |
3983 | index d7af917..7852549 100644 |
3984 | --- a/debian/tests/control |
3985 | +++ b/debian/tests/control |
3986 | @@ -195,7 +195,7 @@ Depends: @, |
3987 | libnm0, |
3988 | python3-gi, |
3989 | gir1.2-nm-1.0, |
3990 | -Restrictions: allow-stderr, needs-root, isolation-container, flaky |
3991 | +Restrictions: allow-stderr, needs-root, isolation-container |
3992 | Features: test-name=diff |
3993 | |
3994 | Test-Command: ./debian/tests/prep-testbed.sh && ./debian/tests/autostart.sh |
3995 | diff --git a/doc/.sphinx/requirements.txt b/doc/.sphinx/requirements.txt |
3996 | index bc73a32..e9c2a91 100644 |
3997 | --- a/doc/.sphinx/requirements.txt |
3998 | +++ b/doc/.sphinx/requirements.txt |
3999 | @@ -14,3 +14,4 @@ sphinx-reredirects |
4000 | sphinx-tabs |
4001 | sphinxcontrib-jquery |
4002 | sphinxext-opengraph |
4003 | +watchfiles |
4004 | diff --git a/doc/conf.py b/doc/conf.py |
4005 | index 0e638c0..3f60865 100644 |
4006 | --- a/doc/conf.py |
4007 | +++ b/doc/conf.py |
4008 | @@ -159,7 +159,6 @@ custom_excludes = [ |
4009 | 'doc-cheat-sheet*', |
4010 | 'manpage-footer.md', |
4011 | 'manpage-header.md', |
4012 | - 'CODE_OF_CONDUCT.md', |
4013 | ] |
4014 | |
4015 | # Add CSS files (located in .sphinx/_static/) |
4016 | diff --git a/doc/creating-link-aggregation.md b/doc/creating-link-aggregation.md |
4017 | new file mode 100644 |
4018 | index 0000000..2543d00 |
4019 | --- /dev/null |
4020 | +++ b/doc/creating-link-aggregation.md |
4021 | @@ -0,0 +1,86 @@ |
4022 | +# How to create link aggregation |
4023 | + |
4024 | +:::{note} |
4025 | +These instructions assume a system setup based on the example configuration outlined in the [Netplan tutorial](/netplan-tutorial). |
4026 | +::: |
4027 | + |
4028 | +Let's suppose now that you need to configure your system to connect to your |
4029 | +ISP links via a link aggregation. On Linux you can do that with a `bond` |
4030 | +virtual interface. |
4031 | + |
4032 | +On Netplan, an interface of type `bond` can be created inside a `bonds` mapping. |
4033 | + |
4034 | +Now that the traffic will flow through the link aggregation, you will move |
4035 | +all the addressing configuration to the bond itself. |
4036 | + |
4037 | +You can define a list of interfaces that will be attached to the bond. In our |
4038 | +simple scenario, we have a single one. |
4039 | + |
4040 | +Edit the file `/etc/netplan/second-interface.yaml` and make the following changes: |
4041 | + |
4042 | +```yaml |
4043 | +network: |
4044 | + version: 2 |
4045 | + ethernets: |
4046 | + netplan-isp-interface: |
4047 | + dhcp4: false |
4048 | + dhcp6: false |
4049 | + match: |
4050 | + macaddress: 00:16:3e:0c:97:8a |
4051 | + set-name: netplan-isp |
4052 | + bonds: |
4053 | + isp-bond0: |
4054 | + interfaces: |
4055 | + - netplan-isp-interface |
4056 | + dhcp4: false |
4057 | + dhcp6: false |
4058 | + accept-ra: false |
4059 | + link-local: [] |
4060 | + addresses: |
4061 | + - 172.16.0.1/24 |
4062 | + routes: |
4063 | + - to: default |
4064 | + via: 172.16.0.254 |
4065 | + nameservers: |
4066 | + search: |
4067 | + - netplanlab.local |
4068 | + addresses: |
4069 | + - 172.16.0.254 |
4070 | + - 172.16.0.253 |
4071 | +``` |
4072 | + |
4073 | +Note that you can reference the interface used in the bond by the name you |
4074 | +defined for it in the `ethernets` section. |
4075 | + |
4076 | +Now use `netplan apply` to apply your changes |
4077 | + |
4078 | +``` |
4079 | +netplan apply |
4080 | +``` |
4081 | + |
4082 | +Now your system has a new interface called `isp-bond0`. Use the command |
4083 | +`ip address show isp-bond0` or `netplan status` to check its state: |
4084 | + |
4085 | +``` |
4086 | +netplan status isp-bond0 |
4087 | +``` |
4088 | + |
4089 | +You should see an output similar to the one below: |
4090 | + |
4091 | +``` |
4092 | + Online state: online |
4093 | + DNS Addresses: 127.0.0.53 (stub) |
4094 | + DNS Search: lxd |
4095 | + netplanlab.local |
4096 | + |
4097 | +● 4: isp-bond0 bond UP (networkd: isp-bond0) |
4098 | + MAC Address: b2:6b:19:b1:9a:86 |
4099 | + Addresses: 172.16.0.1/24 |
4100 | + DNS Addresses: 172.16.0.254 |
4101 | + 172.16.0.253 |
4102 | + DNS Search: netplanlab.local |
4103 | + Routes: default via 172.16.0.254 (static) |
4104 | + 172.16.0.0/24 from 172.16.0.1 (link) |
4105 | + |
4106 | +3 inactive interfaces hidden. Use "--all" to show all. |
4107 | +``` |
4108 | diff --git a/doc/howto.md b/doc/howto.md |
4109 | index 8385090..326ec11 100644 |
4110 | --- a/doc/howto.md |
4111 | +++ b/doc/howto.md |
4112 | @@ -1,28 +1,40 @@ |
4113 | # How-to guides |
4114 | |
4115 | -Below is a collection of how-to guides for common scenarios. |
4116 | -If you see a scenario missing or have one to contribute, please, |
4117 | -[file a bug](https://bugs.launchpad.net/netplan/+filebug) against this |
4118 | -documentation with the example. |
4119 | +This is a collection of how-to guides for common scenarios. If you see a scenario missing or have one to contribute, [file an issue](https://bugs.launchpad.net/netplan/+filebug) against this documentation with the example. |
4120 | |
4121 | -To configure Netplan, save configuration files in the `/etc/netplan/` directory |
4122 | -with a `.yaml` extension (e.g. `/etc/netplan/config.yaml`), then run |
4123 | -`sudo netplan apply`. This command parses and applies the configuration to the |
4124 | -system. Configuration written to disk under `/etc/netplan/` persists between |
4125 | -reboots. |
4126 | +To configure Netplan, save configuration files in the `/etc/netplan/` directory with a `.yaml` extension (e.g. `/etc/netplan/config.yaml`), then run `sudo netplan apply`. This command parses and applies the configuration to the system. Configuration written to disk under `/etc/netplan/` persists between reboots. Visit [Applying new Netplan configuration](/netplan-tutorial.md#applying-new-netplan-configuration) for detailed guidance. |
4127 | |
4128 | -For each of the example below, use the `renderer` that applies to your scenario. |
4129 | -For example, for Ubuntu Desktop the `renderer` is usually `NetworkManager`, |
4130 | -and `networkd` for Ubuntu Server. |
4131 | +For each of the examples below, use the `renderer` that applies to your scenario. For example, for Ubuntu Desktop, the `renderer` is usually `NetworkManager`. For Ubuntu Server, it is `networkd`. |
4132 | |
4133 | -Also, see [/examples](https://github.com/canonical/netplan/tree/main/examples) |
4134 | -on GitHub. |
4135 | + |
4136 | +## Quick configuration examples |
4137 | |
4138 | ```{toctree} |
4139 | :maxdepth: 1 |
4140 | |
4141 | examples |
4142 | +``` |
4143 | + |
4144 | +YAML configuration snippets for these examples, as well as additional examples, are available in the [examples](https://github.com/canonical/netplan/tree/main/examples) directory on GitHub. |
4145 | + |
4146 | + |
4147 | +## Complex how-to guides |
4148 | + |
4149 | +```{toctree} |
4150 | +:maxdepth: 1 |
4151 | + |
4152 | +using-static-ip-addresses |
4153 | +matching-interface-by-mac-address |
4154 | +creating-link-aggregation |
4155 | dbus-config |
4156 | netplan-everywhere |
4157 | +``` |
4158 | + |
4159 | + |
4160 | +## Documentation |
4161 | + |
4162 | +```{toctree} |
4163 | +:maxdepth: 1 |
4164 | + |
4165 | contribute-docs |
4166 | ``` |
4167 | diff --git a/doc/matching-interface-by-mac-address.md b/doc/matching-interface-by-mac-address.md |
4168 | new file mode 100644 |
4169 | index 0000000..916cef3 |
4170 | --- /dev/null |
4171 | +++ b/doc/matching-interface-by-mac-address.md |
4172 | @@ -0,0 +1,85 @@ |
4173 | +# How to match the interface by MAC address |
4174 | + |
4175 | +:::{note} |
4176 | +These instructions assume a system setup based on the example configuration outlined in the [Netplan tutorial](/netplan-tutorial). |
4177 | +::: |
4178 | + |
4179 | +Sometimes you can't rely on the interface names to apply configuration to them. Changes in the system might cause a change in their names, such as when you move an interface card from a PCI slot to another. |
4180 | + |
4181 | +In this exercise you will use the `match` keyword to locate the device based on its MAC address and also set a more meaningful name to the interface. |
4182 | + |
4183 | +Let's assume that your second interface is connected to the Netplan ISP internet provider company and you want to identify it as such. |
4184 | + |
4185 | +First identify its MAC address: |
4186 | + |
4187 | +``` |
4188 | +ip link show enp6s0 |
4189 | +``` |
4190 | + |
4191 | +In the output, the MAC address is the number in front of the `link/ether` property. |
4192 | +``` |
4193 | +3: enp6s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 |
4194 | + link/ether 00:16:3e:0c:97:8a brd ff:ff:ff:ff:ff:ff |
4195 | +``` |
4196 | + |
4197 | +Edit the file `/etc/netplan/second-interface.yaml` and make the following changes: |
4198 | + |
4199 | +```yaml |
4200 | +network: |
4201 | + version: 2 |
4202 | + ethernets: |
4203 | + netplan-isp-interface: |
4204 | + match: |
4205 | + macaddress: 00:16:3e:0c:97:8a |
4206 | + set-name: netplan-isp |
4207 | + dhcp4: false |
4208 | + dhcp6: false |
4209 | + accept-ra: false |
4210 | + link-local: [] |
4211 | + addresses: |
4212 | + - 172.16.0.1/24 |
4213 | + routes: |
4214 | + - to: default |
4215 | + via: 172.16.0.254 |
4216 | + nameservers: |
4217 | + search: |
4218 | + - netplanlab.local |
4219 | + addresses: |
4220 | + - 172.16.0.254 |
4221 | + - 172.16.0.253 |
4222 | +``` |
4223 | + |
4224 | +These are the important changes in this exercise: |
4225 | + |
4226 | +```yaml |
4227 | + ethernets: |
4228 | + netplan-isp-interface: |
4229 | + match: |
4230 | + macaddress: 00:16:3e:0c:97:8a |
4231 | + set-name: netplan-isp |
4232 | +``` |
4233 | + |
4234 | +Note that, as you are now matching the interface by its MAC address, you are free to identify it with a different name. It makes it easier to read and find information in the YAML file. |
4235 | + |
4236 | +After changing the file, apply your new configuration: |
4237 | + |
4238 | +``` |
4239 | +netplan apply |
4240 | +``` |
4241 | + |
4242 | +Now list your interfaces: |
4243 | + |
4244 | +``` |
4245 | +ip link show |
4246 | +``` |
4247 | + |
4248 | +As you can see, your interface is now called `netplan-isp`. |
4249 | + |
4250 | +``` |
4251 | +1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 |
4252 | + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 |
4253 | +2: enp5s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 |
4254 | + link/ether 00:16:3e:13:ae:10 brd ff:ff:ff:ff:ff:ff |
4255 | +3: netplan-isp: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 |
4256 | + link/ether 00:16:3e:0c:97:8a brd ff:ff:ff:ff:ff:ff |
4257 | +``` |
4258 | diff --git a/doc/netplan-generate.md b/doc/netplan-generate.md |
4259 | index 3c0d415..c2675d0 100644 |
4260 | --- a/doc/netplan-generate.md |
4261 | +++ b/doc/netplan-generate.md |
4262 | @@ -58,8 +58,8 @@ There are 3 locations that **`netplan generate`** considers: |
4263 | * `/run/netplan/*.yaml` |
4264 | |
4265 | If there are multiple files with exactly the same name, then only one |
4266 | -will be read. A file in `/run/netplan` will shadow - completely replace |
4267 | -- a file with the same name in `/etc/netplan`. A file in `/etc/netplan` |
4268 | +will be read. A file in `/run/netplan` will shadow (completely replace) |
4269 | +a file with the same name in `/etc/netplan`. A file in `/etc/netplan` |
4270 | will itself shadow a file in `/lib/netplan`. |
4271 | |
4272 | Or, in other words, `/run/netplan` is top priority, then `/etc/netplan`, |
4273 | diff --git a/doc/netplan-status.md b/doc/netplan-status.md |
4274 | index c4ad453..374ce23 100644 |
4275 | --- a/doc/netplan-status.md |
4276 | +++ b/doc/netplan-status.md |
4277 | @@ -35,10 +35,10 @@ Currently, **`netplan status`** depends on `systemd-networkd` as a source of dat |
4278 | : Show all interface data including inactive. |
4279 | |
4280 | `--diff` |
4281 | -: Analyze and display differences between the current system configuration and network definitions present in the YAML files. |
4282 | - The configuration analyzed includes IP addresses, routes, MAC addresses, DNS addresses, search domains and missing network interfaces. |
4283 | +: Analyse and display differences between the current system configuration and network definitions present in the YAML files. |
4284 | + The configuration analysed includes IP addresses, routes, MAC addresses, DNS addresses, search domains and missing network interfaces. |
4285 | |
4286 | - The output format is similar to popular diff tools, such `diff` and `git diff`. Configuration present only in the system (and therefore missing in the Netplan YAMLs) |
4287 | + The output format is similar to popular diff tools, such `diff` and `git diff`. Configuration present only in the system (and therefore missing in the Netplan YAML) |
4288 | will be displayed with a `+` sign and will be highlighted in green. Configuration present only in Netplan (and therefore missing in the system) will be displayed |
4289 | with a `-` sign and highlighted in red. The same is applied to network interfaces. |
4290 | |
4291 | diff --git a/doc/netplan-tutorial.md b/doc/netplan-tutorial.md |
4292 | index ef4ebbc..344096f 100644 |
4293 | --- a/doc/netplan-tutorial.md |
4294 | +++ b/doc/netplan-tutorial.md |
4295 | @@ -1,108 +1,119 @@ |
4296 | -# Pre-requisites |
4297 | +# Netplan tutorial |
4298 | |
4299 | -In order to do the exercises yourself you will need a virtual machine, preferably running Ubuntu. In this tutorial, we will use LXD to create virtual networks and launch virtual machines. Feel free to use a cloud instance or a different hypervisor. As long as you can achieve the same results, you should be fine. If you're going to use your own desktop/laptop system, some of the exercises might interrupt your network connectivity. |
4300 | +Use this tutorial to learn about basic use of the Netplan utility: how to set up an environment to try it, run it for the first time, check its configuration, and modify its settings. |
4301 | |
4302 | -If you already have a setup where you can do the exercises you can just skip this section. |
4303 | |
4304 | -## Setting up the environment |
4305 | +# Trying Netplan in a virtual machine |
4306 | |
4307 | -You can follow the steps below to install and create a basic LXD configuration you can use to launch virtual machines. |
4308 | +To try Netplan, you can use a virtual environment, preferably running Ubuntu. This tutorial uses LXD to create virtual networks and launch virtual machines. You can also use a cloud instance or a different hypervisor. |
4309 | |
4310 | -For more information about LXD, please visit [linuxcontainers.org](https://documentation.ubuntu.com/lxd/). |
4311 | +:::{warning} |
4312 | +When using your own system without virtualisation, some of the exercises might interrupt your network connectivity. |
4313 | +::: |
4314 | |
4315 | -First, install LXD: [LXD | How to install LXD](https://documentation.ubuntu.com/lxd/en/latest/installing/) |
4316 | |
4317 | -On Ubuntu, you can install it using `snap`: |
4318 | +## Setting up the virtual environment |
4319 | |
4320 | -``` |
4321 | -snap install lxd |
4322 | -``` |
4323 | +Follow the steps below to install and create a basic LXD configuration to launch virtual machines (VM). For more information about LXD, visit [documentation.ubuntu.com/lxd](https://documentation.ubuntu.com/lxd/). |
4324 | |
4325 | -Now, initialise your LXD configuration: |
4326 | +1. Install LXD: [LXD | How to install LXD](https://documentation.ubuntu.com/lxd/en/latest/installing/). |
4327 | |
4328 | -``` |
4329 | -lxd init --minimal |
4330 | -``` |
4331 | + On Ubuntu, use `snap` to install LXD: |
4332 | |
4333 | -Run the command below to create a new network in LXD. For some of the exercises you will need a second network interface in your virtual machine. |
4334 | + ``` |
4335 | + snap install lxd |
4336 | + ``` |
4337 | |
4338 | -``` |
4339 | -lxc network create netplanbr0 --type=bridge |
4340 | -``` |
4341 | +2. Initialise LXD configuration: |
4342 | |
4343 | -You should see the output below: |
4344 | + ``` |
4345 | + lxd init --minimal |
4346 | + ``` |
4347 | |
4348 | -``` |
4349 | -Network netplanbr0 created |
4350 | -``` |
4351 | +3. Create a new network in LXD (some of the exercises require a second network interface in the virtual machine): |
4352 | |
4353 | -At this point you should have a usable LXD installation with a working network bridge. |
4354 | + ``` |
4355 | + lxc network create netplanbr0 --type=bridge |
4356 | + ``` |
4357 | |
4358 | -Now create a virtual machine called `netplan-lab0`: |
4359 | + The following output confirms the successful creation of the bridge network: |
4360 | |
4361 | -``` |
4362 | -lxc init --vm ubuntu:22.04 netplan-lab0 |
4363 | -``` |
4364 | + ``` |
4365 | + Network netplanbr0 created |
4366 | + ``` |
4367 | |
4368 | -You should see the output below: |
4369 | + Now you have a usable LXD installation with a working network bridge. |
4370 | |
4371 | -``` |
4372 | -Creating netplan-lab0 |
4373 | -``` |
4374 | +4. Create a virtual machine called `netplan-lab0`: |
4375 | |
4376 | -The new VM will have one network interface attached to the default LXD bridge. |
4377 | + ```none |
4378 | + lxc init --vm ubuntu:23.10 netplan-lab0 |
4379 | + ``` |
4380 | |
4381 | -Now attach the network you created just now, `netplanbr0`, to your VM, `netplan-lab0`, as interface `eth1`: |
4382 | + You should see the output below: |
4383 | |
4384 | -``` |
4385 | -lxc network attach netplanbr0 netplan-lab0 eth1 |
4386 | -``` |
4387 | + ``` |
4388 | + Creating netplan-lab0 |
4389 | + ``` |
4390 | |
4391 | -> See more: [LXD | Attach a network to an instance](https://documentation.ubuntu.com/lxd/en/latest/howto/network_create/#attach-a-network-to-an-instance) |
4392 | + The new VM has one network interface attached to the default LXD bridge. |
4393 | |
4394 | -And start your new VM: |
4395 | +5. Attach the network you created (`netplanbr0`) to the VM (`netplan-lab0`) as the `eth1` interface: |
4396 | |
4397 | -``` |
4398 | -lxc start netplan-lab0 |
4399 | -``` |
4400 | + ``` |
4401 | + lxc network attach netplanbr0 netplan-lab0 eth1 |
4402 | + ``` |
4403 | |
4404 | -Access your new VM using `lxc shell`: |
4405 | + :::{tip} |
4406 | + For more on LXD networking, visit [LXD | Attach a network to an instance](https://documentation.ubuntu.com/lxd/en/latest/howto/network_create/#attach-a-network-to-an-instance). |
4407 | + ::: |
4408 | |
4409 | -``` |
4410 | -lxc shell netplan-lab0 |
4411 | -``` |
4412 | +6. Start the new VM: |
4413 | |
4414 | -> **If this doesn't work for you**: Try instead `lxc exec netplan-lab0 bash` or `lxc console netplan-lab0`. |
4415 | + ``` |
4416 | + lxc start netplan-lab0 |
4417 | + ``` |
4418 | |
4419 | -You should now have a root shell inside your VM: |
4420 | +7. Access the new VM using `lxc shell`: |
4421 | |
4422 | -``` |
4423 | -root@netplan-lab0:~# |
4424 | -``` |
4425 | + ``` |
4426 | + lxc shell netplan-lab0 |
4427 | + ``` |
4428 | |
4429 | -Run the command `ip link` to show your network interfaces: |
4430 | + In case of problems, try running `lxc exec netplan-lab0 bash` or `lxc console netplan-lab0`. |
4431 | |
4432 | -``` |
4433 | -ip link |
4434 | -``` |
4435 | + You should now have a root shell inside the VM: |
4436 | |
4437 | -You should see an output similar to the below: |
4438 | + ```none |
4439 | + root@netplan-lab0:~# |
4440 | + ``` |
4441 | |
4442 | -``` |
4443 | -1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 |
4444 | - link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 |
4445 | -2: enp5s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 |
4446 | - link/ether 00:16:3e:13:ae:10 brd ff:ff:ff:ff:ff:ff |
4447 | -3: enp6s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 |
4448 | - link/ether 00:16:3e:0c:97:8a brd ff:ff:ff:ff:ff:ff |
4449 | -``` |
4450 | +8. Run the `ip link` command to show the network interfaces: |
4451 | + |
4452 | + ``` |
4453 | + ip link |
4454 | + ``` |
4455 | + |
4456 | + You should see an output similar to the below: |
4457 | + |
4458 | + ```none |
4459 | + 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 |
4460 | + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 |
4461 | + 2: enp5s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 |
4462 | + link/ether 00:16:3e:13:ae:10 brd ff:ff:ff:ff:ff:ff |
4463 | + 3: enp6s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 |
4464 | + link/ether 00:16:3e:0c:97:8a brd ff:ff:ff:ff:ff:ff |
4465 | + ``` |
4466 | |
4467 | -In this case, `enp5s0` is the primary interface connected to the default LXD network and `enp6s0` is the second interface you added connected to your custom network. |
4468 | + In this case: |
4469 | |
4470 | + * `enp5s0` is the primary interface connected to the default LXD network. |
4471 | + * `enp6s0` is the second interface you added connected to your custom network. |
4472 | |
4473 | -Now, let's start with a simple exercise. |
4474 | +You're ready to start the first exercise. |
4475 | |
4476 | -# Running netplan for the first time |
4477 | + |
4478 | +# Running Netplan for the first time |
4479 | |
4480 | Start by typing the command `netplan` in your shell: |
4481 | |
4482 | @@ -110,9 +121,9 @@ Start by typing the command `netplan` in your shell: |
4483 | netplan |
4484 | ``` |
4485 | |
4486 | -You should see the output below |
4487 | +You should see the following output: |
4488 | |
4489 | -``` |
4490 | +```none |
4491 | You need to specify a command |
4492 | usage: /usr/sbin/netplan [-h] [--debug] ... |
4493 | |
4494 | @@ -136,11 +147,12 @@ Available commands: |
4495 | try Try to apply a new Netplan config to running system, with automatic rollback |
4496 | ``` |
4497 | |
4498 | -As you can see, Netplan has a number of sub commands. Let's explore some of them. |
4499 | +As you can see, Netplan has a number of sub-commands. Let's explore some of them. |
4500 | + |
4501 | |
4502 | -# Showing your current Netplan configuration |
4503 | +# Showing current Netplan configuration |
4504 | |
4505 | -To show your current configuration, run the command `netplan get`. |
4506 | +To show the current configuration, run the `netplan get` command: |
4507 | |
4508 | ``` |
4509 | netplan get |
4510 | @@ -148,7 +160,6 @@ netplan get |
4511 | |
4512 | You should see an output similar to the one below: |
4513 | |
4514 | - |
4515 | ```yaml |
4516 | network: |
4517 | version: 2 |
4518 | @@ -157,16 +168,17 @@ network: |
4519 | dhcp4: true |
4520 | ``` |
4521 | |
4522 | -It shows you have an Ethernet interface called `enp5s0` and it has DHCP enabled for IPv4. |
4523 | +This means: |
4524 | |
4525 | +* There's an Ethernet interface called `enp5s0`. |
4526 | +* DHCP is enabled for the IPv4 protocol on `enp5s0`. |
4527 | |
4528 | -# Showing your current network configuration |
4529 | |
4530 | -Netplan 0.106 introduced the `netplan status` command. You can use it to |
4531 | -show your system's current network configuration. Try that by typing |
4532 | -`netplan status --all` in your console: |
4533 | +# Showing current network configuration |
4534 | |
4535 | -``` |
4536 | +Netplan 0.106 introduced the `netplan status` command. The command displays the current network configuration of the system. Try it by running: |
4537 | + |
4538 | +```none |
4539 | netplan status --all |
4540 | ``` |
4541 | |
4542 | @@ -202,11 +214,18 @@ You should see an output similar to the one below: |
4543 | MAC Address: 00:16:3e:0c:97:8a (Red Hat, Inc.) |
4544 | ``` |
4545 | |
4546 | -# Checking the file where your configuration is stored |
4547 | |
4548 | -The configuration you just listed is stored at `/etc/netplan`. You can see the contents of the file with the command below: |
4549 | +# Checking Netplan configuration files |
4550 | + |
4551 | +Netplan configuration is stored in YAML-formatted files in the `/etc/netplan` directory. To display the contents of the directory, run: |
4552 | |
4553 | +```none |
4554 | +ls -1 /etc/netplan/ |
4555 | ``` |
4556 | + |
4557 | +Provided your system was initialised using `cloud-init`, such as the Ubuntu virtual machine recommended for testing Netplan in [Trying Netplan in a virtual machine](#trying-netplan-in-a-virtual-machine), you can find the initial Netplan configuration in the `50-cloud-init.yaml` file: |
4558 | + |
4559 | +```none |
4560 | cat /etc/netplan/50-cloud-init.yaml |
4561 | ``` |
4562 | |
4563 | @@ -225,518 +244,205 @@ network: |
4564 | dhcp4: true |
4565 | ``` |
4566 | |
4567 | -This file was automatically generated by `cloud-init` when the system was initialised. As noted in the comments, changes to this file will not persist. |
4568 | +This configuration file is automatically generated by the `cloud-init` tool when the system is initialised. As noted in the file comments, direct changes to this file do not persist. |
4569 | |
4570 | -# Enabling your second network interface with DHCP |
4571 | |
4572 | -There are basically 2 ways to create or change Netplan configuration: |
4573 | +# Creating and modifying Netplan configuration |
4574 | |
4575 | -1) Using the `netplan set` command |
4576 | -2) Editing the YAML files manually |
4577 | +There are two methods to create or modify Netplan configuration: |
4578 | |
4579 | -Let's see how you can enable your second network interface using both ways. |
4580 | +Using the `netplan set` command |
4581 | +: Example: [Using netplan set to enable a network interface with DHCP](#using-netplan-set-to-enable-a-network-interface-with-dhcp) |
4582 | |
4583 | -## Using `netplan set` |
4584 | +Editing the YAML configuration files manually |
4585 | +: Example: [Editing Netplan YAML files to disable IPv6](#editing-netplan-yaml-files-to-disable-ipv6) |
4586 | |
4587 | -For simple tasks, you can use `netplan set` to change your configuration. |
4588 | |
4589 | -In the example below you are going to create a new YAML file called `second-interface.yaml` containing only the configuration needed to enable our second interfaces. |
4590 | +## Using `netplan set` to enable a network interface with DHCP |
4591 | |
4592 | -Considering your second network interface is `enp6s0`, run the command below: |
4593 | +For simple configuration changes, use the `netplan set` command. In the example below, you are going to create a new YAML file called `second-interface.yaml` containing only the configuration needed to enable the second network interface. |
4594 | |
4595 | -``` |
4596 | -netplan set --origin-hint second-interface ethernets.enp6s0.dhcp4=true |
4597 | -``` |
4598 | +1. To create a second network interface called `enp6s0`, run: |
4599 | |
4600 | -The command line parameter `--origin-hint` sets the name of the file where the configuration will be stored. |
4601 | + ``` |
4602 | + netplan set --origin-hint second-interface ethernets.enp6s0.dhcp4=true |
4603 | + ``` |
4604 | |
4605 | -Now list the files in the directory `/etc/netplan`: |
4606 | + The `--origin-hint` command-line parameter sets the name of the file in which the configuration is stored. |
4607 | |
4608 | -``` |
4609 | -ls /etc/netplan |
4610 | -``` |
4611 | +2. List the files in the directory `/etc/netplan`: |
4612 | |
4613 | -You should see the auto generated `cloud-init` file and a new file called `second-interface.yaml`: |
4614 | + ```none |
4615 | + ls -1 /etc/netplan |
4616 | + ``` |
4617 | |
4618 | -``` |
4619 | -root@netplan-lab0:~# ls /etc/netplan/ |
4620 | -50-cloud-init.yaml second-interface.yaml |
4621 | -``` |
4622 | - |
4623 | -Use the command `cat` to see its content: |
4624 | - |
4625 | -``` |
4626 | -cat /etc/netplan/second-interface.yaml |
4627 | -``` |
4628 | - |
4629 | -```yaml |
4630 | -network: |
4631 | - version: 2 |
4632 | - ethernets: |
4633 | - enp6s0: |
4634 | - dhcp4: true |
4635 | -``` |
4636 | - |
4637 | -You will notice it is very similar to the one generated by `cloud-init`. |
4638 | - |
4639 | -Now use `netplan get` to see your full configuration: |
4640 | - |
4641 | -``` |
4642 | -netplan get |
4643 | -``` |
4644 | + You should see the auto-generated `cloud-init` file and a new file called `second-interface.yaml`: |
4645 | |
4646 | -You should see an output similar to the one below with both Ethernet interfaces: |
4647 | + ```none |
4648 | + 50-cloud-init.yaml |
4649 | + second-interface.yaml |
4650 | + ``` |
4651 | |
4652 | -```yaml |
4653 | -network: |
4654 | - version: 2 |
4655 | - ethernets: |
4656 | - enp5s0: |
4657 | - dhcp4: true |
4658 | - enp6s0: |
4659 | - dhcp4: true |
4660 | -``` |
4661 | +3. Use the command `cat` to see the file content: |
4662 | |
4663 | -## Applying your new configuration |
4664 | + ``` |
4665 | + cat /etc/netplan/second-interface.yaml |
4666 | + ``` |
4667 | |
4668 | -The command `netplan set` created the configuration for your second network interface but it wasn't applied to the running system. |
4669 | + ```yaml |
4670 | + network: |
4671 | + version: 2 |
4672 | + ethernets: |
4673 | + enp6s0: |
4674 | + dhcp4: true |
4675 | + ``` |
4676 | |
4677 | -Run the command below to see the current state of your second network interface: |
4678 | + Notice it is similar to the configuration file generated by `cloud-init` ([Checking Netplan configuration files](#checking-netplan-configuration-files)). |
4679 | |
4680 | -``` |
4681 | -ip address show enp6s0 |
4682 | -``` |
4683 | +4. Check the full configuration using `netplan get`: |
4684 | |
4685 | -You should see an output similar to the one below: |
4686 | + ``` |
4687 | + netplan get |
4688 | + ``` |
4689 | |
4690 | -``` |
4691 | -3: enp6s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 |
4692 | - link/ether 00:16:3e:0c:97:8a brd ff:ff:ff:ff:ff:ff |
4693 | -``` |
4694 | + You should see an output similar to the one below with both Ethernet interfaces: |
4695 | |
4696 | -As you can see, this interface has no IP address and its state is DOWN. |
4697 | + ```yaml |
4698 | + network: |
4699 | + version: 2 |
4700 | + ethernets: |
4701 | + enp5s0: |
4702 | + dhcp4: true |
4703 | + enp6s0: |
4704 | + dhcp4: true |
4705 | + ``` |
4706 | |
4707 | -In order to apply the Netplan configuration, you can use the command `netplan apply`. |
4708 | +The interface configuration has been created. To apply the changes to the system, follow the instructions in [Applying new Netplan configuration](#applying-new-netplan-configuration). |
4709 | |
4710 | -Run the command below in your shell: |
4711 | |
4712 | -``` |
4713 | -netplan apply |
4714 | -``` |
4715 | +## Editing Netplan YAML files to disable IPv6 |
4716 | |
4717 | -Now check again the state of the interface `enp6s0`: |
4718 | +For more complex settings, you can edit existing or create new configuration files manually. |
4719 | |
4720 | -``` |
4721 | -ip address show enp6s0 |
4722 | -``` |
4723 | +For example, to disable automatic IPv6 configuration on the second network interface created in [Using netplan set to enable a network interface with DHCP](#using-netplan-set-to-enable-a-network-interface-with-dhcp), edit the `/etc/netplan/second-interface.yaml` file: |
4724 | |
4725 | -You should see an output similar to this: |
4726 | +1. Add the following lines to the configuration section of the interface: |
4727 | |
4728 | -``` |
4729 | -3: enp6s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 |
4730 | - link/ether 00:16:3e:0c:97:8a brd ff:ff:ff:ff:ff:ff |
4731 | - inet 10.33.59.157/24 metric 100 brd 10.33.59.255 scope global dynamic enp6s0 |
4732 | - valid_lft 3589sec preferred_lft 3589sec |
4733 | - inet6 fd42:ee65:61d0:abcb:216:3eff:fe0c:978a/64 scope global dynamic mngtmpaddr noprefixroute |
4734 | - valid_lft 3599sec preferred_lft 3599sec |
4735 | - inet6 fe80::216:3eff:fe0c:978a/64 scope link |
4736 | - valid_lft forever preferred_lft forever |
4737 | -``` |
4738 | + ```yaml |
4739 | + accept-ra: false |
4740 | + link-local: [] |
4741 | + ``` |
4742 | |
4743 | -You can also use `netplan status` to check the interface: |
4744 | + When you finish, the whole configuration in `second-interface.yaml` should look like this: |
4745 | |
4746 | -``` |
4747 | -netplan status enp6s0 |
4748 | -``` |
4749 | + ```yaml |
4750 | + network: |
4751 | + version: 2 |
4752 | + ethernets: |
4753 | + enp6s0: |
4754 | + dhcp4: true |
4755 | + accept-ra: false |
4756 | + link-local: [] |
4757 | + ``` |
4758 | |
4759 | -You should see an output similar to this: |
4760 | + With this new configuration, the network configuration back end (`systemd-networkd` in this case) does not accept Route Advertisements and does not add the `link-local` address to the interface. |
4761 | |
4762 | -``` |
4763 | - Online state: online |
4764 | - DNS Addresses: 127.0.0.53 (stub) |
4765 | - DNS Search: lxd |
4766 | +2. Check the new configuration using the `netplan get` command: |
4767 | |
4768 | -● 3: enp6s0 ethernet UP (networkd: enp6s0) |
4769 | - MAC Address: 00:16:3e:0c:97:8a (Red Hat, Inc.) |
4770 | - Addresses: 10.33.59.157/24 (dhcp) |
4771 | - fd42:ee65:61d0:abcb:216:3eff:fe0c:978a/64 |
4772 | - fe80::216:3eff:fe0c:978a/64 (link) |
4773 | - DNS Addresses: 10.33.59.1 |
4774 | - fe80::216:3eff:fea1:585b |
4775 | - DNS Search: lxd |
4776 | - Routes: default via 10.33.59.1 from 10.33.59.157 metric 100 (dhcp) |
4777 | - 10.33.59.0/24 from 10.33.59.157 metric 100 (link) |
4778 | - 10.33.59.1 from 10.33.59.157 metric 100 (dhcp, link) |
4779 | - fd42:ee65:61d0:abcb::/64 metric 100 (ra) |
4780 | - fe80::/64 metric 256 |
4781 | + ``` |
4782 | + netplan get |
4783 | + ``` |
4784 | |
4785 | -2 inactive interfaces hidden. Use "--all" to show all. |
4786 | -``` |
4787 | + You should see something similar to this: |
4788 | |
4789 | -As you can see, even though you haven't enabled DHCP for IPv6 on this interface, the network configuration back end (in this case systemd-networkd) enabled it anyway. But let's assume you want only IPv4. |
4790 | + ```yaml |
4791 | + network: |
4792 | + version: 2 |
4793 | + ethernets: |
4794 | + enp5s0: |
4795 | + dhcp4: true |
4796 | + enp6s0: |
4797 | + dhcp4: true |
4798 | + accept-ra: false |
4799 | + link-local: [] |
4800 | + ``` |
4801 | |
4802 | -Let's address this situation in the next exercise. |
4803 | +IPv6 has been disabled for the interface in the configuration. To apply the changes to the system, follow the instructions in [Applying new Netplan configuration](#applying-new-netplan-configuration). |
4804 | |
4805 | -## Editing YAML files |
4806 | |
4807 | -For more complex configuration, you can just create or edit a new file yourself using your favourite text editor. |
4808 | +# Applying new Netplan configuration |
4809 | |
4810 | -Continuing the exercise from the previous section, let's go ahead and disable automatic IPv6 configuration on your second interface. But this time let's do it by manually editing the YAML file. |
4811 | +New or modified Netplan settings need to be applied before they take effect on a running system. |
4812 | |
4813 | -Use your favourite text editor and open the file `/etc/netplan/second-interface.yaml`. |
4814 | +:::{note} |
4815 | +Using the `netplan set` command to modify configuration or editing (creating) the Netplan YAML configuration files directly does not automatically apply the new settings to the running system. |
4816 | +::: |
4817 | |
4818 | -Add the configuration below to the interface configuration section: |
4819 | +After creating a new configuration as described in [Using netplan set to enable a network interface with DHCP](#using-netplan-set-to-enable-a-network-interface-with-dhcp), follow these steps to apply the settings and confirm they have taken effect. |
4820 | |
4821 | -```yaml |
4822 | -accept-ra: false |
4823 | -link-local: [] |
4824 | -``` |
4825 | +1. Display the current state of the network interface: |
4826 | |
4827 | -When you finish, it should look like this: |
4828 | + ``` |
4829 | + ip address show enp6s0 |
4830 | + ``` |
4831 | |
4832 | -```yaml |
4833 | -network: |
4834 | - version: 2 |
4835 | - ethernets: |
4836 | - enp6s0: |
4837 | - dhcp4: true |
4838 | - accept-ra: false |
4839 | - link-local: [] |
4840 | -``` |
4841 | + Where `enp6s0` is the interface you wish to display status for. You should see an output similar to the one below: |
4842 | |
4843 | -With this new configuration, the network configuration back end (systemd-networkd in this case) will not accept Route Advertisements and will not add the link-local address to our interface. |
4844 | + ```none |
4845 | + 3: enp6s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 |
4846 | + link/ether 00:16:3e:0c:97:8a brd ff:ff:ff:ff:ff:ff |
4847 | + ``` |
4848 | |
4849 | -Now check your new configuration with the `netplan get` command: |
4850 | + This interface has no IP address and its state is `DOWN`. |
4851 | |
4852 | -``` |
4853 | -netplan get |
4854 | -``` |
4855 | +2. Apply the new Netplan configuration: |
4856 | |
4857 | -You should see something similar to this: |
4858 | + ``` |
4859 | + netplan apply |
4860 | + ``` |
4861 | |
4862 | -```yaml |
4863 | -network: |
4864 | - version: 2 |
4865 | - ethernets: |
4866 | - enp5s0: |
4867 | - dhcp4: true |
4868 | - enp6s0: |
4869 | - dhcp4: true |
4870 | - accept-ra: false |
4871 | - link-local: [] |
4872 | -``` |
4873 | +3. Check the state of the `enp6s0` interface again using one of the following two methods: |
4874 | |
4875 | -Now use `netplan apply` to apply your new configuration: |
4876 | + * Using the `ip` tool: |
4877 | |
4878 | -``` |
4879 | -netplan apply |
4880 | -``` |
4881 | + ``` |
4882 | + ip address show enp6s0 |
4883 | + ``` |
4884 | |
4885 | -And check your interface configuration: |
4886 | + You should see an output similar to this: |
4887 | |
4888 | -``` |
4889 | -ip address show enp6s0 |
4890 | -``` |
4891 | + ```none |
4892 | + 3: enp6s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 |
4893 | + link/ether 00:16:3e:0c:97:8a brd ff:ff:ff:ff:ff:ff |
4894 | + inet 10.33.59.157/24 metric 100 brd 10.33.59.255 scope global dynamic enp6s0 |
4895 | + valid_lft 3589sec preferred_lft 3589sec |
4896 | + ``` |
4897 | |
4898 | -You should see an output similar to this: |
4899 | + * Using the `netplan status` command: |
4900 | |
4901 | -``` |
4902 | -3: enp6s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 |
4903 | - link/ether 00:16:3e:0c:97:8a brd ff:ff:ff:ff:ff:ff |
4904 | - inet 10.33.59.157/24 metric 100 brd 10.33.59.255 scope global dynamic enp6s0 |
4905 | - valid_lft 3585sec preferred_lft 3585sec |
4906 | -``` |
4907 | + ``` |
4908 | + netplan status enp6s0 |
4909 | + ``` |
4910 | |
4911 | -And as you can see, now it only has an IPv4 address. |
4912 | + You should see an output similar to this: |
4913 | |
4914 | -In this exercise you explored the `netplan set`, `netplan get`, `netplan apply` and `netplan status` commands. You also used some of the Ethernet configuration options to get a network interface up and running with DHCP. |
4915 | + ``` |
4916 | + Online state: online |
4917 | + DNS Addresses: 127.0.0.53 (stub) |
4918 | + DNS Search: lxd |
4919 | |
4920 | -# Using static IP addresses |
4921 | + ● 3: enp6s0 ethernet UP (networkd: enp6s0) |
4922 | + MAC Address: 00:16:3e:0c:97:8a (Red Hat, Inc.) |
4923 | + Addresses: 10.33.59.157/24 (dhcp) |
4924 | + DNS Addresses: 10.33.59.1 |
4925 | + DNS Search: lxd |
4926 | + Routes: default via 10.33.59.1 from 10.33.59.157 metric 100 (dhcp) |
4927 | + 10.33.59.0/24 from 10.33.59.157 metric 100 (link) |
4928 | + 10.33.59.1 from 10.33.59.157 metric 100 (dhcp, link) |
4929 | |
4930 | -In this exercise you're going to add an static IP address to the second interface with a default route and DNS configuration. |
4931 | + 2 inactive interfaces hidden. Use "--all" to show all. |
4932 | + ``` |
4933 | |
4934 | -Edit the file `/etc/netplan/second-interface.yaml` created previously. Change it so it will look like this: |
4935 | +--- |
4936 | |
4937 | -```yaml |
4938 | -network: |
4939 | - version: 2 |
4940 | - ethernets: |
4941 | - enp6s0: |
4942 | - dhcp4: false |
4943 | - dhcp6: false |
4944 | - accept-ra: false |
4945 | - link-local: [] |
4946 | - addresses: |
4947 | - - 172.16.0.1/24 |
4948 | - routes: |
4949 | - - to: default |
4950 | - via: 172.16.0.254 |
4951 | - nameservers: |
4952 | - search: |
4953 | - - netplanlab.local |
4954 | - addresses: |
4955 | - - 172.16.0.254 |
4956 | - - 172.16.0.253 |
4957 | -``` |
4958 | - |
4959 | -The configuration above is what you'd expect in a desktop system for example. It defines the interface's IP address statically as `172.16.0.1/24`, a default route via gateway `172.16.0.254` and the DNS search domain and name servers. |
4960 | - |
4961 | -Now use `netplan get` to visualise all your network configuration: |
4962 | - |
4963 | -``` |
4964 | -netplan get |
4965 | -``` |
4966 | - |
4967 | -You should see an output similar to this: |
4968 | - |
4969 | -```yaml |
4970 | -network: |
4971 | - version: 2 |
4972 | - ethernets: |
4973 | - enp5s0: |
4974 | - dhcp4: true |
4975 | - enp6s0: |
4976 | - addresses: |
4977 | - - "172.16.0.1/24" |
4978 | - nameservers: |
4979 | - addresses: |
4980 | - - 172.16.0.254 |
4981 | - - 172.16.0.253 |
4982 | - search: |
4983 | - - netplanlab.local |
4984 | - dhcp4: false |
4985 | - dhcp6: false |
4986 | - accept-ra: false |
4987 | - routes: |
4988 | - - to: "default" |
4989 | - via: "172.16.0.254" |
4990 | - link-local: [] |
4991 | -``` |
4992 | - |
4993 | -You will notice that it might be a little different than what you have defined in the YAML file. Some things might be in a different order for example. |
4994 | - |
4995 | -The reason for that is that `netplan get` loads and parses your configuration before outputting it, and the YAML parsing engine used by Netplan might shuffle things around. Although, what you see from `netplan get` is equivalent to what you have in the file. |
4996 | - |
4997 | -Now use `netplan apply` to apply the new configuration: |
4998 | - |
4999 | -``` |
5000 | -netplan apply |
Thank you very much for running the different autopkgtest from the PPA and doing the analysis. This SRU is well prepared and LGTM!
Some notes from my review (nothing that needs any furhter action):
- note (non-blocking): 0002-CLI- apply-call- udevadm- trigger- using-action- add-Clos. patch Not necessarily needed on Noble's systemd v255, but this SRU is a full (Ubuntu-)version backport, so including this patch is sensible to keep the delta as small as possible.
- note (non-blocking): The SRU is mostly dropping distro patches for https:/ /github. com/canonical/ netplan/ pull/456 that got included (but disabled) in upstream 1.0.1. This SRU re-enables the functionality, to keep status quo for Noble
- note (non-blocking) Upstream 1.0.1 contains CVE-2022-4968 & related fixes, so we can drop the corresponding distro patches
- note (non-blocking) This SRU matches the upstream 1.0.1 tag, except for the 4 patches in d/p/series. limited- stable- api.patch apply-call- udevadm- trigger- using-action- add-Clos. patch wait-online- disabled- wait-online- for-stable- 1.patch avoid-calling- udevadm- control- reload- LP-199. patch
- python-
- 0002-CLI-
- 0003-Revert-
- 0004-generate-
Merged, tagged & sponsored for SRU review!