summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--NEWS4
-rw-r--r--TODO5
-rw-r--r--catalog/systemd.fr.catalog70
-rw-r--r--man/systemd-nspawn.xml67
-rw-r--r--man/systemd.mount.xml5
-rw-r--r--man/systemd.network.xml28
-rw-r--r--man/systemd.nspawn.xml10
-rw-r--r--network/80-container-host0.network6
-rw-r--r--network/80-container-ve.network6
-rw-r--r--network/80-container-vz.network22
-rw-r--r--shell-completion/bash/systemctl.in2
-rw-r--r--shell-completion/zsh/_systemctl.in3
-rw-r--r--src/basic/io-util.c5
-rw-r--r--src/basic/socket-util.c78
-rw-r--r--src/basic/socket-util.h4
-rw-r--r--src/core/load-fragment.c26
-rw-r--r--src/core/socket.c172
-rw-r--r--src/libsystemd-network/network-internal.c70
-rw-r--r--src/libsystemd-network/network-internal.h4
-rw-r--r--src/network/networkd-address.c27
-rw-r--r--src/network/networkd-link.c16
-rw-r--r--src/network/networkd-link.h2
-rw-r--r--src/network/networkd-lldp-tx.c102
-rw-r--r--src/network/networkd-lldp-tx.h14
-rw-r--r--src/network/networkd-network-gperf.gperf2
-rw-r--r--src/network/networkd-network.h3
-rw-r--r--src/nspawn/nspawn-gperf.gperf3
-rw-r--r--src/nspawn/nspawn-network.c187
-rw-r--r--src/nspawn/nspawn-network.h3
-rw-r--r--src/nspawn/nspawn-settings.c40
-rw-r--r--src/nspawn/nspawn-settings.h2
-rw-r--r--src/nspawn/nspawn.c89
-rw-r--r--src/shared/conf-parser.c38
-rw-r--r--src/shared/conf-parser.h1
-rw-r--r--src/shared/firewall-util.c6
-rw-r--r--src/test/test-socket-util.c25
-rwxr-xr-xtest/TEST-01-BASIC/test.sh2
-rwxr-xr-xtest/TEST-02-CRYPTSETUP/test.sh2
-rwxr-xr-xtest/TEST-08-ISSUE-2730/test.sh14
-rw-r--r--test/test-functions10
-rw-r--r--units/systemd-fsck@.service.in2
42 files changed, 910 insertions, 270 deletions
diff --git a/Makefile.am b/Makefile.am
index ee9e91a339..aea91e9c94 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3598,7 +3598,8 @@ INSTALL_DIRS += \
dist_network_DATA = \
network/99-default.link \
network/80-container-host0.network \
- network/80-container-ve.network
+ network/80-container-ve.network \
+ network/80-container-vz.network
dist_udevrules_DATA += \
rules/50-udev-default.rules \
diff --git a/NEWS b/NEWS
index 8f1cde01e8..b3e51877c9 100644
--- a/NEWS
+++ b/NEWS
@@ -28,7 +28,7 @@ CHANGES WITH 230 in spe:
* systemd-logind will now by default terminate user processes that are
part of the user session scope unit (session-XX.scope) when the user
- logs out. This behaviour is controlled by the KillUserProcesses=
+ logs out. This behavior is controlled by the KillUserProcesses=
setting in logind.conf, and the previous default of "no" is now
changed to "yes". This means that user sessions will be properly
cleaned up after, but additional steps are necessary to allow
@@ -92,7 +92,7 @@ CHANGES WITH 230 in spe:
* The compatibility libraries libsystemd-daemon.so,
libsystemd-journal.so, libsystemd-id128.so, and libsystemd-login.so
which have been deprecated since systemd-209 have been removed along
- with the corresponding pkg-config files. All symbols provided by the
+ with the corresponding pkg-config files. All symbols provided by
those libraries are provided by libsystemd.so.
* The Capabilities= unit file setting has been removed (it is ignored
diff --git a/TODO b/TODO
index bf80f86e04..792d63b3e3 100644
--- a/TODO
+++ b/TODO
@@ -125,7 +125,8 @@ Features:
* implement a per-service firewall based on net_cls
-* Port various tools to make use of verbs.[ch], where applicable
+* Port various tools to make use of verbs.[ch], where applicable: busctl,
+ bootctl, coredumpctl, hostnamectl, localectl, systemd-analyze, timedatectl
* hostnamectl: show root image uuid
@@ -287,8 +288,6 @@ Features:
* be more careful what we export on the bus as (usec_t) 0 and (usec_t) -1
-* unify dispatch table in systemctl_main() and friends
-
* rfkill,backlight: we probably should run the load tools inside of the udev rules so that the state is properly initialized by the time other software sees it
* After coming back from hibernation reset hibernation swap partition using the /dev/snapshot ioctl APIs
diff --git a/catalog/systemd.fr.catalog b/catalog/systemd.fr.catalog
index d71c2902d7..0cea629c31 100644
--- a/catalog/systemd.fr.catalog
+++ b/catalog/systemd.fr.catalog
@@ -38,6 +38,25 @@ Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
Le processus du journal système a été arrêté et tous ses fichiers actifs
ont été fermés.
+-- ec387f577b844b8fa948f33cad9a75e6
+Subject: Espace disque utilisé par le journal
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:journald.conf(5)
+
+@JOURNAL_NAME@ (@JOURNAL_PATH@) utilise actuellement @CURRENT_USE_PRETTY@.
+Le maximum autorisé est défini à @MAX_USE_PRETTY@.
+Au moins @DISK_KEEP_FREE_PRETTY@ doivent être laissés libres
+(sur @DISK_AVAILABLE_PRETTY@ d'espace disque actuellement libre).
+La limite appliquée est donc @LIMIT_PRETTY@, dont @AVAILABLE_PRETTY@
+sont toujours disponibles.
+
+Les limites définissant la quantité d'espace disque que peut utiliser le
+journal peuvent être configurées avec les paramètres SystemMaxUse=,
+SystemKeepFree=, SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree=,
+RuntimeMaxFileSize= dans le fichier /etc/systemd/journald.conf.
+Voir journald.conf(5) pour plus de détails.
+
-- a596d6fe7bfa4994828e72309e95d61e
Subject: Des messages d'un service ont été supprimés
Defined-By: systemd
@@ -98,7 +117,8 @@ Defined-By: systemd
Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
-Un nouveau poste (seat) @SEAT_ID@ a été configuré et est maintenant disponible.
+Un nouveau poste (seat) @SEAT_ID@ a été configuré et est maintenant
+disponible.
-- e7852bfe46784ed0accde04bc864c2d5
Subject: Le poste (seat) @SEAT_ID@ a été retiré
@@ -228,8 +248,8 @@ Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
Un ou plusieurs messages n'ont pas pu être transmis au service syslog
s'exécutant conjointement avec journald. Cela indique généralement que
-l'implémentation de syslog utilisée n'a pas été capable de suivre la cadence
-du flux de messages.
+l'implémentation de syslog utilisée n'a pas été capable de suivre
+la cadence du flux de messages.
-- 1dee0369c7fc4736b7099b38ecb46ee7
Subject: Le point de montage n'est pas vide
@@ -237,8 +257,8 @@ Defined-By: systemd
Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
Le répertoire @WHERE@ est spécifié comme point de montage (second champ du
-fichier /etc/fstab, ou champ Where= dans une unité (unit) systemd) et n'est pas
-vide.
+fichier /etc/fstab, ou champ Where= dans une unité (unit) systemd) et n'est
+pas vide.
Cela ne perturbe pas le montage du système de fichiers, mais les fichiers
préalablement présents dans ce répertoire sont devenus inaccessibles.
Pour atteindre ces fichiers, veuillez monter manuellement le système de
@@ -258,3 +278,43 @@ Defined-By: systemd
Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
La machine virtuelle @NAME@ avec le PID maître @LEADER@ a été arrêtée.
+
+-- 36db2dfa5a9045e1bd4af5f93e1cf057
+Subject: Le mode DNSSEC a été désactivé, car il n'est pas supporté par le serveur
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8) resolved.conf(5)
+
+Le service de résolution (systemd-resolved.service) a détecté que le serveur
+DNS configuré ne supporte pas DNSSEC, et la validation DNSSEC a donc été
+désactivée.
+
+Cet évènement se produit si DNSSEC=allow-downgrade est configuré dans
+resolved.conf et que le serveur DNS configuré n'est pas compatible avec
+DNSSEC.
+Veuillez noter que ce mode permet des attaques de rétrogradation DNSSEC,
+car un attaquant peut être capable de désactiver la validation DNSSEC sur
+le système en injectant des réponses DNS dans le canal de communication.
+
+Cet évènement indique que le serveur DNS est effectivement incompatible avec
+DNSSEC, ou qu'un attaquant a peut-être conduit une telle attaque avec succès.
+
+-- 1675d7f172174098b1108bf8c7dc8f5d
+Subject: La validation DNSSEC a échoué
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8)
+
+Une requête ou une ressource DNS n'a pas passé la validation DNSSEC.
+Ceci est généralement une indication que le canal de communication a été
+altéré.
+
+-- 4d4408cfd0d144859184d1e65d7c8a65
+Subject: Une ancre de confiance DNSSEC a été révoquée
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8)
+
+Une ancre de confiance DNSSEC a été révoquée. Une nouvelle ancre de
+confiance doit être configurée, ou le système d'exploitation a besoin
+d'être mis à jour, pour fournir une version à jour de l'ancre de confiance.
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index bd688a0ee1..0c8c699201 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -524,15 +524,23 @@
<term><option>-n</option></term>
<term><option>--network-veth</option></term>
- <listitem><para>Create a virtual Ethernet link
- (<literal>veth</literal>) between host and container. The host
- side of the Ethernet link will be available as a network
- interface named after the container's name (as specified with
- <option>--machine=</option>), prefixed with
- <literal>ve-</literal>. The container side of the Ethernet
- link will be named <literal>host0</literal>. Note that
- <option>--network-veth</option> implies
- <option>--private-network</option>.</para></listitem>
+ <listitem><para>Create a virtual Ethernet link (<literal>veth</literal>) between host and container. The host
+ side of the Ethernet link will be available as a network interface named after the container's name (as
+ specified with <option>--machine=</option>), prefixed with <literal>ve-</literal>. The container side of the
+ Ethernet link will be named <literal>host0</literal>. The <option>--network-veth</option> option implies
+ <option>--private-network</option>.</para>
+
+ <para>Note that
+ <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ includes by default a network file <filename>/usr/lib/systemd/network/80-container-ve.network</filename>
+ matching the host-side interfaces created this way, which contains settings to enable automatic address
+ provisioning on the created virtual link via DHCP, as well as automatic IP routing onto the host's external
+ network interfaces. It also contains <filename>/usr/lib/systemd/network/80-container-host0.network</filename>
+ matching the container-side interface created this way, containing settings to enable client side address
+ assignment via DHCP. In case <filename>systemd-networkd</filename> is running on both the host and inside the
+ container, automatic IP communication from the container to the host is thus available, with further
+ connectivity to the external network.</para>
+ </listitem>
</varlistentry>
<varlistentry>
@@ -553,16 +561,43 @@
<varlistentry>
<term><option>--network-bridge=</option></term>
- <listitem><para>Adds the host side of the Ethernet link
- created with <option>--network-veth</option> to the specified
- bridge. Note that <option>--network-bridge=</option> implies
- <option>--network-veth</option>. If this option is used, the
- host side of the Ethernet link will use the
- <literal>vb-</literal> prefix instead of
+ <listitem><para>Adds the host side of the Ethernet link created with <option>--network-veth</option> to the
+ specified Ethernet bridge interface. Expects a valid network interface name of a bridge device as
+ argument. Note that <option>--network-bridge=</option> implies <option>--network-veth</option>. If this option
+ is used, the host side of the Ethernet link will use the <literal>vb-</literal> prefix instead of
<literal>ve-</literal>.</para></listitem>
</varlistentry>
<varlistentry>
+ <term><option>--network-zone=</option></term>
+
+ <listitem><para>Creates a virtual Ethernet link (<literal>veth</literal>) to the container and adds it to an
+ automatically managed Ethernet bridge interface. The bridge interface is named after the passed argument,
+ prefixed with <literal>vz-</literal>. The bridge interface is automatically created when the first container
+ configured for its name is started, and is automatically removed when the last container configured for its
+ name exits. Hence, each bridge interface configured this way exists only as long as there's at least one
+ container referencing it running. This option is very similar to <option>--network-bridge=</option>, besides
+ this automatic creation/removal of the bridge device.</para>
+
+ <para>This setting makes it easy to place multiple related containers on a common, virtual Ethernet-based
+ broadcast domain, here called a "zone". Each container may only be part of one zone, but each zone may contain
+ any number of containers. Each zone is referenced by its name. Names may be chosen freely (as long as they form
+ valid network interface names when prefixed with <literal>vz-</literal>), and it is sufficient to pass the same
+ name to the <option>--network-zones=</option> switch of the various concurrently running containers to join
+ them in one zone.</para>
+
+ <para>Note that
+ <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ includes by default a network file <filename>/usr/lib/systemd/network/80-container-vz.network</filename>
+ matching the bridge interfaces created this way, which contains settings to enable automatic address
+ provisioning on the created virtual network via DHCP, as well as automatic IP routing onto the host's external
+ network interfaces. Using <option>--network-zone=</option> is hence in most cases fully automatic and
+ sufficient to connect multiple local containers in a joined broadcast domain to the host, with further
+ connectivity to the external network.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-p</option></term>
<term><option>--port=</option></term>
@@ -577,7 +612,7 @@
port number and its colon may be omitted, in which case the
same port as the host port is implied. This option is only
supported if private networking is used, such as with
- <option>--network-veth</option> or
+ <option>--network-veth</option>, <option>--network-zone=</option>
<option>--network-bridge=</option>.</para></listitem>
</varlistentry>
diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml
index bf56a49e58..66cddd72e0 100644
--- a/man/systemd.mount.xml
+++ b/man/systemd.mount.xml
@@ -159,6 +159,11 @@
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for details about the conversion.</para>
+ <para>The NFS mount option <option>bg</option> for NFS background mounts
+ as documented in <citerefentry><refentrytitle>nfs</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ is not supported in <filename>/etc/fstab</filename> entries. The systemd mount option <option>nofail</option>
+ provides similar functionality and should be used instead.</para>
+
<para>When reading <filename>/etc/fstab</filename> a few special
mount options are understood by systemd which influence how
dependencies are created for mount points. systemd will create a
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 6d45d6c807..70e3804746 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -363,18 +363,26 @@
<varlistentry>
<term><varname>EmitLLDP=</varname></term>
<listitem>
- <para>Controls support for Ethernet LLDP packet emission. Accepts a boolean parameter and defaults to
- false. If enabled a short LLDP packet with information about the local system is sent out in regular
- intervals on the link. The LLDP packet will contain information about the local host name, the local
- machine ID (as stored in
- <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>) and the
+ <para>Controls support for Ethernet LLDP packet emission. Accepts a boolean parameter or the special values
+ <literal>nearest-bridge</literal>, <literal>non-tpmr-bridge</literal> and
+ <literal>customer-bridge</literal>. Defaults to false, which turns off LLDP packet emission. If not false,
+ a short LLDP packet with information about the local system is sent out in regular intervals on the
+ link. The LLDP packet will contain information about the local host name, the local machine ID (as stored
+ in <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>) and the
local interface name, as well as the pretty hostname of the system (as set in
<citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry>). LLDP
- emission is only available on Ethernet links. Note that this setting passed data suitable for
- identification of host to the network and should thus not be used on untrusted networks, where such
- identification data should not be made available. Use this option to enable other systems to identify on
- which interface they are connected to this system. See <varname>LLDP=</varname> above for an option to
- enable LLDP reception.</para>
+ emission is only available on Ethernet links. Note that this setting passes data suitable for
+ identification of host to the network and should thus not be enabled on untrusted networks, where such
+ identification data should not be made available. Use this option to permit other systems to identify on
+ which interfaces they are connected to this system. The three special values control propagation of the
+ LLDP packets. The <literal>nearest-bridge</literal> setting permits propagation only to the nearest
+ connected bridge, <literal>non-tpmr-bridge</literal> permits propagation across Two-Port MAC Relays, but
+ not any other bridges, and <literal>customer-bridge</literal> permits propagation until a customer bridge
+ is reached. For details about these concepts, see <ulink
+ url="http://standards.ieee.org/getieee802/download/802.1AB-2009.pdf">IEEE 802.1AB-2009</ulink>. Note that
+ configuring this setting to true is equivalent to <literal>nearest-bridge</literal>, the recommended and
+ most restricted level of propagation. See <varname>LLDP=</varname> above for an option to enable LLDP
+ reception.</para>
</listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml
index 15360078ef..3683412c14 100644
--- a/man/systemd.nspawn.xml
+++ b/man/systemd.nspawn.xml
@@ -420,6 +420,16 @@
</varlistentry>
<varlistentry>
+ <term><varname>Zone=</varname></term>
+
+ <listitem><para>Takes a network zone name. This setting implies <varname>VirtualEthernet=yes</varname> and
+ <varname>Private=yes</varname> and has the effect that the host side of the created virtual Ethernet link is
+ connected to an automatically managed bridge interface named after the passed argument, prefixed with
+ <literal>vz-</literal>. This option corresponds to the <option>--network-zone=</option> command line
+ switch. This option is privileged (see above).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>Port=</varname></term>
<listitem><para>Exposes a TCP or UDP port of the container on
diff --git a/network/80-container-host0.network b/network/80-container-host0.network
index f0626f81b0..b012cf98cb 100644
--- a/network/80-container-host0.network
+++ b/network/80-container-host0.network
@@ -5,6 +5,10 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
+# This network file matches the container-side of the virtual Ethernet link
+# created by systemd-nspawn's --network-veth switch. See systemd-nspawn(1) for
+# details.
+
[Match]
Virtualization=container
Name=host0
@@ -13,7 +17,7 @@ Name=host0
DHCP=yes
LinkLocalAddressing=yes
LLDP=yes
-EmitLLDP=yes
+EmitLLDP=customer-bridge
[DHCP]
UseTimezone=yes
diff --git a/network/80-container-ve.network b/network/80-container-ve.network
index abb265ddc4..ac796bfb07 100644
--- a/network/80-container-ve.network
+++ b/network/80-container-ve.network
@@ -5,6 +5,10 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
+# This network file matches the host-side of the virtual Ethernet link
+# created by systemd-nspawn's --network-veth switch. See systemd-nspawn(1) for
+# details.
+
[Match]
Name=ve-*
Driver=veth
@@ -16,4 +20,4 @@ LinkLocalAddressing=yes
DHCPServer=yes
IPMasquerade=yes
LLDP=yes
-EmitLLDP=yes
+EmitLLDP=customer-bridge
diff --git a/network/80-container-vz.network b/network/80-container-vz.network
new file mode 100644
index 0000000000..3d532d6f60
--- /dev/null
+++ b/network/80-container-vz.network
@@ -0,0 +1,22 @@
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+# This network file matches the bridge interface created by systemd-nspawn's
+# --network-zone= switch. See systemd-nspawn(1) for details.
+
+[Match]
+Name=vz-*
+Driver=bridge
+
+[Network]
+# Default to using a /24 prefix, giving up to 253 addresses per virtual network.
+Address=0.0.0.0/24
+LinkLocalAddressing=yes
+DHCPServer=yes
+IPMasquerade=yes
+LLDP=yes
+EmitLLDP=customer-bridge
diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in
index ef7dc6285a..6f2b3f122c 100644
--- a/shell-completion/bash/systemctl.in
+++ b/shell-completion/bash/systemctl.in
@@ -96,7 +96,7 @@ _systemctl () {
local i verb comps mode
local -A OPTS=(
- [STANDALONE]='--all -a --reverse --after --before --defaults --failed --force -f --full -l --global
+ [STANDALONE]='--all -a --reverse --after --before --defaults --force -f --full -l --global
--help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall
--quiet -q --privileged -P --system --user --version --runtime --recursive -r --firmware-setup
--show-types -i --ignore-inhibitors --plain'
diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in
index 667243eb53..44c31b7833 100644
--- a/shell-completion/zsh/_systemctl.in
+++ b/shell-completion/zsh/_systemctl.in
@@ -156,7 +156,7 @@ _systemctl_restartable_units(){
{ while read -r a b; do echo -E - " $a"; done; } )) )
}
-_systemctl_failed_units() {_sys_failed_units=( ${${(f)"$(__systemctl list-units --failed)"}%% *} ) }
+_systemctl_failed_units() {_sys_failed_units=( ${${(f)"$(__systemctl list-units --state=failed)"}%% *} ) }
_systemctl_unit_state() { typeset -gA _sys_unit_state; _sys_unit_state=( $(__systemctl list-unit-files) ) }
local fun
@@ -364,7 +364,6 @@ _arguments -s \
'--reverse[Show reverse dependencies]' \
'--after[Show units ordered after]' \
'--before[Show units ordered before]' \
- '--failed[Show only failed units]' \
{-l,--full}"[Don't ellipsize unit names on output]" \
'--show-types[When showing sockets, show socket type]' \
{-i,--ignore-inhibitors}'[When executing a job, ignore jobs dependencies]' \
diff --git a/src/basic/io-util.c b/src/basic/io-util.c
index 0037a37f2a..cc6dfa8c1b 100644
--- a/src/basic/io-util.c
+++ b/src/basic/io-util.c
@@ -33,6 +33,11 @@ int flush_fd(int fd) {
.events = POLLIN,
};
+ /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
+ * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read
+ * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used
+ * was set to non-blocking too. */
+
for (;;) {
char buf[LINE_MAX];
ssize_t l;
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 0f38f9a0f3..c8769a54f4 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -23,6 +23,7 @@
#include <net/if.h>
#include <netdb.h>
#include <netinet/ip.h>
+#include <poll.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -42,7 +43,9 @@
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "strv.h"
#include "user-util.h"
+#include "utf8.h"
#include "util.h"
int socket_address_parse(SocketAddress *a, const char *s) {
@@ -794,6 +797,42 @@ static const char* const ip_tos_table[] = {
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
+bool ifname_valid(const char *p) {
+ bool numeric = true;
+
+ /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources
+ * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We
+ * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */
+
+ if (isempty(p))
+ return false;
+
+ if (strlen(p) >= IFNAMSIZ)
+ return false;
+
+ if (STR_IN_SET(p, ".", ".."))
+ return false;
+
+ while (*p) {
+ if ((unsigned char) *p >= 127U)
+ return false;
+
+ if ((unsigned char) *p <= 32U)
+ return false;
+
+ if (*p == ':' || *p == '/')
+ return false;
+
+ numeric = numeric && (*p >= '0' && *p <= '9');
+ p++;
+ }
+
+ if (numeric)
+ return false;
+
+ return true;
+}
+
int getpeercred(int fd, struct ucred *ucred) {
socklen_t n = sizeof(struct ucred);
struct ucred u;
@@ -970,3 +1009,42 @@ fallback:
return (ssize_t) k;
}
+
+int flush_accept(int fd) {
+
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
+ int r;
+
+
+ /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */
+
+ for (;;) {
+ int cfd;
+
+ r = poll(&pollfd, 1, 0);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return -errno;
+
+ } else if (r == 0)
+ return 0;
+
+ cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+ if (cfd < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN)
+ return 0;
+
+ return -errno;
+ }
+
+ close(cfd);
+ }
+}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index daa4b24a37..e9230e4a9f 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -123,6 +123,8 @@ int fd_inc_rcvbuf(int fd, size_t n);
int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s);
+bool ifname_valid(const char *p);
+
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
@@ -135,6 +137,8 @@ int receive_one_fd(int transport_fd, int flags);
ssize_t next_datagram_size_fd(int fd);
+int flush_accept(int fd);
+
#define CMSG_FOREACH(cmsg, mh) \
for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 1a8c03904c..a12dd38c60 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -732,16 +732,17 @@ int config_parse_exec(
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
-int config_parse_socket_bindtodevice(const char* unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_socket_bindtodevice(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Socket *s = data;
char *n;
@@ -752,6 +753,11 @@ int config_parse_socket_bindtodevice(const char* unit,
assert(data);
if (rvalue[0] && !streq(rvalue, "*")) {
+ if (!ifname_valid(rvalue)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is invalid, ignoring: %s", rvalue);
+ return 0;
+ }
+
n = strdup(rvalue);
if (!n)
return log_oom();
diff --git a/src/core/socket.c b/src/core/socket.c
index d4b409ef53..f6204d04bf 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -28,7 +28,6 @@
#include <unistd.h>
#include <linux/sctp.h>
-#include "sd-event.h"
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
@@ -38,6 +37,7 @@
#include "exit-status.h"
#include "fd-util.h"
#include "formats-util.h"
+#include "io-util.h"
#include "label.h"
#include "log.h"
#include "missing.h"
@@ -1247,6 +1247,45 @@ fail:
return r;
}
+static int socket_determine_selinux_label(Socket *s, char **ret) {
+ ExecCommand *c;
+ int r;
+
+ assert(s);
+ assert(ret);
+
+ if (s->selinux_context_from_net) {
+ /* If this is requested, get label from the network label */
+
+ r = mac_selinux_get_our_label(ret);
+ if (r == -EOPNOTSUPP)
+ goto no_label;
+
+ } else {
+ /* Otherwise, get it from the executable we are about to start */
+ r = socket_instantiate_service(s);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_ISSET(s->service))
+ goto no_label;
+
+ c = SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START];
+ if (!c)
+ goto no_label;
+
+ r = mac_selinux_get_create_label_from_exe(c->path, ret);
+ if (r == -EPERM || r == -EOPNOTSUPP)
+ goto no_label;
+ }
+
+ return r;
+
+no_label:
+ *ret = NULL;
+ return 0;
+}
+
static int socket_open_fds(Socket *s) {
_cleanup_(mac_selinux_freep) char *label = NULL;
bool know_label = false;
@@ -1265,48 +1304,28 @@ static int socket_open_fds(Socket *s) {
case SOCKET_SOCKET:
if (!know_label) {
- /* Figure out label, if we don't it know
- * yet. We do it once, for the first
- * socket where we need this and
- * remember it for the rest. */
-
- if (s->selinux_context_from_net) {
- /* Get it from the network label */
-
- r = mac_selinux_get_our_label(&label);
- if (r < 0 && r != -EOPNOTSUPP)
- goto rollback;
-
- } else {
- /* Get it from the executable we are about to start */
-
- r = socket_instantiate_service(s);
- if (r < 0)
- goto rollback;
+ /* Figure out label, if we don't it know yet. We do it once, for the first socket where
+ * we need this and remember it for the rest. */
- if (UNIT_ISSET(s->service) &&
- SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) {
- r = mac_selinux_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label);
- if (r < 0 && r != -EPERM && r != -EOPNOTSUPP)
- goto rollback;
- }
- }
+ r = socket_determine_selinux_label(s, &label);
+ if (r < 0)
+ goto rollback;
know_label = true;
}
/* Apply the socket protocol */
- switch(p->address.type) {
+ switch (p->address.type) {
case SOCK_STREAM:
case SOCK_SEQPACKET:
- if (p->socket->socket_protocol == IPPROTO_SCTP)
- p->address.protocol = p->socket->socket_protocol;
+ if (s->socket_protocol == IPPROTO_SCTP)
+ p->address.protocol = s->socket_protocol;
break;
case SOCK_DGRAM:
- if (p->socket->socket_protocol == IPPROTO_UDPLITE)
- p->address.protocol = p->socket->socket_protocol;
+ if (s->socket_protocol == IPPROTO_UDPLITE)
+ p->address.protocol = s->socket_protocol;
break;
}
@@ -1450,6 +1469,34 @@ fail:
return r;
}
+enum {
+ SOCKET_OPEN_NONE,
+ SOCKET_OPEN_SOME,
+ SOCKET_OPEN_ALL,
+};
+
+static int socket_check_open(Socket *s) {
+ bool have_open = false, have_closed = false;
+ SocketPort *p;
+
+ assert(s);
+
+ LIST_FOREACH(port, p, s->ports) {
+ if (p->fd < 0)
+ have_closed = true;
+ else
+ have_open = true;
+
+ if (have_open && have_closed)
+ return SOCKET_OPEN_SOME;
+ }
+
+ if (have_open)
+ return SOCKET_OPEN_ALL;
+
+ return SOCKET_OPEN_NONE;
+}
+
static void socket_set_state(Socket *s, SocketState state) {
SocketState old_state;
assert(s);
@@ -1529,14 +1576,24 @@ static int socket_coldplug(Unit *u) {
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_LISTENING,
- SOCKET_RUNNING,
- SOCKET_STOP_PRE,
- SOCKET_STOP_PRE_SIGTERM,
- SOCKET_STOP_PRE_SIGKILL)) {
-
- r = socket_open_fds(s);
- if (r < 0)
- return r;
+ SOCKET_RUNNING)) {
+
+ /* Originally, we used to simply reopen all sockets here that we didn't have file descriptors
+ * for. However, this is problematic, as we won't traverse throught the SOCKET_START_CHOWN state for
+ * them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open,
+ * and if there's a mismatch, warn loudly. */
+
+ r = socket_check_open(s);
+ if (r == SOCKET_OPEN_NONE)
+ log_unit_warning(UNIT(s),
+ "Socket unit configuration has changed while unit has been running, "
+ "no open socket file descriptor left. "
+ "The socket unit is not functional until restarted.");
+ else if (r == SOCKET_OPEN_SOME)
+ log_unit_warning(UNIT(s),
+ "Socket unit configuration has changed while unit has been running, "
+ "and some socket file descriptors have not been opened yet. "
+ "The socket unit is not fully functional until restarted.");
}
if (s->deserialized_state == SOCKET_LISTENING) {
@@ -1909,6 +1966,21 @@ fail:
socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
}
+static void flush_ports(Socket *s) {
+ SocketPort *p;
+
+ /* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy
+ * anymore */
+
+ LIST_FOREACH(port, p, s->ports) {
+ if (p->fd < 0)
+ continue;
+
+ (void) flush_accept(p->fd);
+ (void) flush_fd(p->fd);
+ }
+}
+
static void socket_enter_running(Socket *s, int cfd) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
@@ -1918,31 +1990,15 @@ static void socket_enter_running(Socket *s, int cfd) {
assert(s);
- /* We don't take connections anymore if we are supposed to
- * shut down anyway */
+ /* We don't take connections anymore if we are supposed to shut down anyway */
if (unit_stop_pending(UNIT(s))) {
log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled.");
if (cfd >= 0)
cfd = safe_close(cfd);
- else {
- /* Flush all sockets by closing and reopening them */
- socket_close_fds(s);
-
- r = socket_open_fds(s);
- if (r < 0) {
- log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m");
- socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
- return;
- }
-
- r = socket_watch_fds(s);
- if (r < 0) {
- log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m");
- socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
- }
- }
+ else
+ flush_ports(s);
return;
}
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index 182d08c50d..2badcdff58 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -32,6 +32,7 @@
#include "network-internal.h"
#include "parse-util.h"
#include "siphash24.h"
+#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
#include "utf8.h"
@@ -175,54 +176,17 @@ int config_parse_net_condition(const char *unit,
return 0;
}
-int config_parse_ifname(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char **s = data;
- _cleanup_free_ char *n = NULL;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- n = strdup(rvalue);
- if (!n)
- return log_oom();
-
- if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
- return 0;
- }
-
- free(*s);
- if (*n) {
- *s = n;
- n = NULL;
- } else
- *s = NULL;
-
- return 0;
-}
-
-int config_parse_ifnames(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_ifnames(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
char ***sv = data;
int r;
@@ -236,13 +200,15 @@ int config_parse_ifnames(const char *unit,
_cleanup_free_ char *word = NULL;
r = extract_first_word(&rvalue, &word, NULL, 0);
- if (r < 0)
- return r;
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue);
+ return 0;
+ }
if (r == 0)
break;
- if (!ascii_is_valid(word) || strlen(word) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+ if (!ifname_valid(word)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
return 0;
}
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index 72432774d7..5bcd577167 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -50,10 +50,6 @@ int config_parse_hwaddr(const char *unit, const char *filename, unsigned line,
const char *section, unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_ifname(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
int config_parse_ifnames(const char *unit, const char *filename, unsigned line,
const char *section, unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 8b52a1f742..4eb0d927a6 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -27,6 +27,7 @@
#include "networkd.h"
#include "parse-util.h"
#include "set.h"
+#include "socket-util.h"
#include "string-util.h"
#include "utf8.h"
#include "util.h"
@@ -726,7 +727,8 @@ int config_parse_address(const char *unit,
return 0;
}
-int config_parse_label(const char *unit,
+int config_parse_label(
+ const char *unit,
const char *filename,
unsigned line,
const char *section,
@@ -736,9 +738,9 @@ int config_parse_label(const char *unit,
const char *rvalue,
void *data,
void *userdata) {
- Network *network = userdata;
+
_cleanup_address_free_ Address *n = NULL;
- char *label;
+ Network *network = userdata;
int r;
assert(filename);
@@ -751,23 +753,14 @@ int config_parse_label(const char *unit,
if (r < 0)
return r;
- label = strdup(rvalue);
- if (!label)
- return log_oom();
-
- if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
- free(label);
+ if (!ifname_valid(rvalue)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not valid or too long, ignoring assignment: %s", rvalue);
return 0;
}
- free(n->label);
- if (*label)
- n->label = label;
- else {
- free(label);
- n->label = NULL;
- }
+ r = free_and_strdup(&n->label, rvalue);
+ if (r < 0)
+ return log_oom();
n = NULL;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index c646af1f1a..4e3f62cf51 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -131,7 +131,7 @@ static bool link_lldp_rx_enabled(Link *link) {
return link->network->lldp_mode != LLDP_MODE_NO;
}
-static bool link_lldp_tx_enabled(Link *link) {
+static bool link_lldp_emit_enabled(Link *link) {
assert(link);
if (link->flags & IFF_LOOPBACK)
@@ -143,7 +143,7 @@ static bool link_lldp_tx_enabled(Link *link) {
if (!link->network)
return false;
- return link->network->lldp_emit;
+ return link->network->lldp_emit != LLDP_EMIT_NO;
}
static bool link_ipv4_forward_enabled(Link *link) {
@@ -491,7 +491,7 @@ static void link_free(Link *link) {
sd_dhcp_client_unref(link->dhcp_client);
sd_dhcp_lease_unref(link->dhcp_lease);
- link_lldp_tx_stop(link);
+ link_lldp_emit_stop(link);
free(link->lease_file);
@@ -618,7 +618,7 @@ static int link_stop_clients(Link *link) {
r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m");
}
- link_lldp_tx_stop(link);
+ link_lldp_emit_stop(link);
return r;
}
@@ -1411,12 +1411,12 @@ static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n
(void) link_lldp_save(link);
- if (link_lldp_tx_enabled(link) && event == SD_LLDP_EVENT_ADDED) {
+ if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) {
/* If we received information about a new neighbor, restart the LLDP "fast" logic */
log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission.");
- r = link_lldp_tx_start(link);
+ r = link_lldp_emit_start(link);
if (r < 0)
log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m");
}
@@ -1501,8 +1501,8 @@ static int link_acquire_conf(Link *link) {
return r;
}
- if (link_lldp_tx_enabled(link)) {
- r = link_lldp_tx_start(link);
+ if (link_lldp_emit_enabled(link)) {
+ r = link_lldp_emit_start(link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m");
}
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 86139be557..14c4a02c7e 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -121,7 +121,7 @@ typedef struct Link {
/* This is about LLDP transmission */
unsigned lldp_tx_fast; /* The LLDP txFast counter (See 802.1ab-2009, section 9.2.5.18) */
- sd_event_source *lldp_tx_event_source;
+ sd_event_source *lldp_emit_event_source;
Hashmap *bound_by_links;
Hashmap *bound_to_links;
diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c
index 03b694c3f1..3aa768388b 100644
--- a/src/network/networkd-lldp-tx.c
+++ b/src/network/networkd-lldp-tx.c
@@ -25,16 +25,14 @@
#include "fd-util.h"
#include "fileio.h"
#include "hostname-util.h"
+#include "networkd-lldp-tx.h"
+#include "networkd.h"
+#include "parse-util.h"
#include "random-util.h"
#include "socket-util.h"
#include "string-util.h"
#include "unaligned.h"
-#include "networkd.h"
-#include "networkd-lldp-tx.h"
-
-#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
-
/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
#define LLDP_TX_FAST_INIT 4U
@@ -50,6 +48,12 @@
/* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */
#define LLDP_FAST_TX_USEC (1U * USEC_PER_SEC - LLDP_JITTER_USEC / 2)
+static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = {
+ [LLDP_EMIT_NEAREST_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }},
+ [LLDP_EMIT_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }},
+ [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }},
+};
+
static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) {
assert(p);
@@ -66,6 +70,7 @@ static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) {
}
static int lldp_make_packet(
+ LLDPEmit mode,
const struct ether_addr *hwaddr,
const char *machine_id,
const char *ifname,
@@ -84,6 +89,8 @@ static int lldp_make_packet(
size_t l;
int r;
+ assert(mode > LLDP_EMIT_NO);
+ assert(mode < _LLDP_EMIT_MAX);
assert(hwaddr);
assert(machine_id);
assert(ifname);
@@ -132,7 +139,7 @@ static int lldp_make_packet(
h = (struct ether_header*) packet;
h->ether_type = htobe16(ETHERTYPE_LLDP);
- memcpy(h->ether_dhost, &(struct ether_addr) { LLDP_MULTICAST_ADDR }, ETH_ALEN);
+ memcpy(h->ether_dhost, lldp_multicast_addr + mode, ETH_ALEN);
memcpy(h->ether_shost, hwaddr, ETH_ALEN);
p = (uint8_t*) packet + sizeof(struct ether_header);
@@ -197,22 +204,28 @@ static int lldp_make_packet(
return 0;
}
-static int lldp_send_packet(int ifindex, const void *packet, size_t packet_size) {
+static int lldp_send_packet(
+ int ifindex,
+ const struct ether_addr *address,
+ const void *packet,
+ size_t packet_size) {
union sockaddr_union sa = {
.ll.sll_family = AF_PACKET,
.ll.sll_protocol = htobe16(ETHERTYPE_LLDP),
.ll.sll_ifindex = ifindex,
.ll.sll_halen = ETH_ALEN,
- .ll.sll_addr = LLDP_MULTICAST_ADDR,
};
_cleanup_close_ int fd = -1;
ssize_t l;
assert(ifindex > 0);
+ assert(address);
assert(packet || packet_size <= 0);
+ memcpy(sa.ll.sll_addr, address, ETH_ALEN);
+
fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW);
if (fd < 0)
return -errno;
@@ -237,6 +250,13 @@ static int link_send_lldp(Link *link) {
usec_t ttl;
int r;
+ assert(link);
+
+ if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO)
+ return 0;
+
+ assert(link->network->lldp_emit < _LLDP_EMIT_MAX);
+
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return r;
@@ -251,7 +271,8 @@ static int link_send_lldp(Link *link) {
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER :
SD_LLDP_SYSTEM_CAPABILITIES_STATION;
- r = lldp_make_packet(&link->mac,
+ r = lldp_make_packet(link->network->lldp_emit,
+ &link->mac,
sd_id128_to_string(machine_id, machine_id_string),
link->ifname,
(uint16_t) ttl,
@@ -264,7 +285,7 @@ static int link_send_lldp(Link *link) {
if (r < 0)
return r;
- return lldp_send_packet(link->ifindex, packet, packet_size);
+ return lldp_send_packet(link->ifindex, lldp_multicast_addr + link->network->lldp_emit, packet, packet_size);
}
static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) {
@@ -300,12 +321,17 @@ static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) {
return 0;
}
-int link_lldp_tx_start(Link *link) {
+int link_lldp_emit_start(Link *link) {
usec_t next;
int r;
assert(link);
+ if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) {
+ link_lldp_emit_stop(link);
+ return 0;
+ }
+
/* Starts the LLDP transmission in "fast" mode. If it is already started, turns "fast" mode back on again. */
link->lldp_tx_fast = LLDP_TX_FAST_INIT;
@@ -313,22 +339,22 @@ int link_lldp_tx_start(Link *link) {
next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC),
(usec_t) random_u64() % LLDP_JITTER_USEC);
- if (link->lldp_tx_event_source) {
+ if (link->lldp_emit_event_source) {
usec_t old;
/* Lower the timeout, maybe */
- r = sd_event_source_get_time(link->lldp_tx_event_source, &old);
+ r = sd_event_source_get_time(link->lldp_emit_event_source, &old);
if (r < 0)
return r;
if (old <= next)
return 0;
- return sd_event_source_set_time(link->lldp_tx_event_source, next);
+ return sd_event_source_set_time(link->lldp_emit_event_source, next);
} else {
r = sd_event_add_time(
link->manager->event,
- &link->lldp_tx_event_source,
+ &link->lldp_emit_event_source,
clock_boottime_or_monotonic(),
next,
0,
@@ -337,14 +363,54 @@ int link_lldp_tx_start(Link *link) {
if (r < 0)
return r;
- (void) sd_event_source_set_description(link->lldp_tx_event_source, "lldp-tx");
+ (void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx");
}
return 0;
}
-void link_lldp_tx_stop(Link *link) {
+void link_lldp_emit_stop(Link *link) {
assert(link);
- link->lldp_tx_event_source = sd_event_source_unref(link->lldp_tx_event_source);
+ link->lldp_emit_event_source = sd_event_source_unref(link->lldp_emit_event_source);
+}
+
+int config_parse_lldp_emit(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ LLDPEmit *emit = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue))
+ *emit = LLDP_EMIT_NO;
+ else if (streq(rvalue, "nearest-bridge"))
+ *emit = LLDP_EMIT_NEAREST_BRIDGE;
+ else if (streq(rvalue, "non-tpmr-bridge"))
+ *emit = LLDP_EMIT_NON_TPMR_BRIDGE;
+ else if (streq(rvalue, "customer-bridge"))
+ *emit = LLDP_EMIT_CUSTOMER_BRIDGE;
+ else {
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *emit = r ? LLDP_EMIT_NEAREST_BRIDGE : LLDP_EMIT_NO;
+ }
+
+ return 0;
}
diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h
index 8c7f403005..4680c9d950 100644
--- a/src/network/networkd-lldp-tx.h
+++ b/src/network/networkd-lldp-tx.h
@@ -21,5 +21,15 @@
#include "networkd-link.h"
-int link_lldp_tx_start(Link *link);
-void link_lldp_tx_stop(Link *link);
+typedef enum LLDPEmit {
+ LLDP_EMIT_NO,
+ LLDP_EMIT_NEAREST_BRIDGE,
+ LLDP_EMIT_NON_TPMR_BRIDGE,
+ LLDP_EMIT_CUSTOMER_BRIDGE,
+ _LLDP_EMIT_MAX,
+} LLDPEmit;
+
+int link_lldp_emit_start(Link *link);
+void link_lldp_emit_stop(Link *link);
+
+int config_parse_lldp_emit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index a9a541559e..4425ee4e2f 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -42,7 +42,7 @@ Network.LinkLocalAddressing, config_parse_address_family_boolean,
Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route)
Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token)
Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode)
-Network.EmitLLDP, config_parse_bool, 0, offsetof(Network, lldp_emit)
+Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit)
Network.Address, config_parse_address, 0, 0
Network.Gateway, config_parse_gateway, 0, 0
Network.Domains, config_parse_domains, 0, 0
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index ff2414efdd..4cd0fa4ab8 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -29,6 +29,7 @@
#include "networkd-address.h"
#include "networkd-fdb.h"
+#include "networkd-lldp-tx.h"
#include "networkd-netdev.h"
#include "networkd-route.h"
#include "networkd-util.h"
@@ -161,7 +162,7 @@ struct Network {
DUID duid;
LLDPMode lldp_mode; /* LLDP reception */
- bool lldp_emit; /* LLDP transmission */
+ LLDPEmit lldp_emit; /* LLDP transmission */
LIST_HEAD(Address, static_addresses);
LIST_HEAD(Route, static_routes);
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
index 34e1310e29..2b5d452662 100644
--- a/src/nspawn/nspawn-gperf.gperf
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -39,5 +39,6 @@ Network.MACVLAN, config_parse_strv, 0, offsetof(Settings,
Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan)
Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth)
Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0
-Network.Bridge, config_parse_string, 0, offsetof(Settings, network_bridge)
+Network.Bridge, config_parse_ifname, 0, offsetof(Settings, network_bridge)
+Network.Zone, config_parse_network_zone, 0, 0
Network.Port, config_parse_expose_port, 0, 0
diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c
index f2b7e4dd79..8da47a2ca6 100644
--- a/src/nspawn/nspawn-network.c
+++ b/src/nspawn/nspawn-network.c
@@ -26,9 +26,12 @@
#include "alloc-util.h"
#include "ether-addr-util.h"
+#include "lockfile-util.h"
#include "netlink-util.h"
#include "nspawn-network.h"
#include "siphash24.h"
+#include "socket-util.h"
+#include "stat-util.h"
#include "string-util.h"
#include "udev-util.h"
#include "util.h"
@@ -39,6 +42,30 @@
#define VETH_EXTRA_CONTAINER_HASH_KEY SD_ID128_MAKE(af,50,17,61,ce,f9,4d,35,84,0d,2b,20,54,be,ce,59)
#define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f)
+static int remove_one_link(sd_netlink *rtnl, const char *name) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ int r;
+
+ if (isempty(name))
+ return 0;
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+ r = sd_netlink_call(rtnl, m, 0, NULL);
+ if (r == -ENODEV) /* Already gone */
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to remove interface %s: %m", name);
+
+ return 1;
+}
+
static int generate_mac(
const char *machine_name,
struct ether_addr *mac,
@@ -238,45 +265,149 @@ int setup_veth_extra(
return 0;
}
-int setup_bridge(const char *veth_name, const char *bridge_name) {
+static int join_bridge(sd_netlink *rtnl, const char *veth_name, const char *bridge_name) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int r, bridge_ifi;
+ assert(rtnl);
assert(veth_name);
assert(bridge_name);
bridge_ifi = (int) if_nametoindex(bridge_name);
if (bridge_ifi <= 0)
- return log_error_errno(errno, "Failed to resolve interface %s: %m", bridge_name);
-
- r = sd_netlink_open(&rtnl);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to netlink: %m");
+ return -errno;
r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0);
if (r < 0)
- return log_error_errno(r, "Failed to allocate netlink message: %m");
+ return r;
r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP);
if (r < 0)
- return log_error_errno(r, "Failed to set IFF_UP flag: %m");
+ return r;
r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name);
if (r < 0)
- return log_error_errno(r, "Failed to add netlink interface name field: %m");
+ return r;
r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge_ifi);
if (r < 0)
- return log_error_errno(r, "Failed to add netlink master field: %m");
+ return r;
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to add veth interface to bridge: %m");
+ return r;
return bridge_ifi;
}
+static int create_bridge(sd_netlink *rtnl, const char *bridge_name) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ int r;
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, bridge_name);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "bridge");
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(rtnl, m, 0, NULL);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int setup_bridge(const char *veth_name, const char *bridge_name, bool create) {
+ _cleanup_release_lock_file_ LockFile bridge_lock = LOCK_FILE_INIT;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ int r, bridge_ifi;
+ unsigned n = 0;
+
+ assert(veth_name);
+ assert(bridge_name);
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ if (create) {
+ /* We take a system-wide lock here, so that we can safely check whether there's still a member in the
+ * bridge before removing it, without risking interferance from other nspawn instances. */
+
+ r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock);
+ if (r < 0)
+ return log_error_errno(r, "Failed to take network zone lock: %m");
+ }
+
+ for (;;) {
+ bridge_ifi = join_bridge(rtnl, veth_name, bridge_name);
+ if (bridge_ifi >= 0)
+ return bridge_ifi;
+ if (bridge_ifi != -ENODEV || !create || n > 10)
+ return log_error_errno(bridge_ifi, "Failed to add interface %s to bridge %s: %m", veth_name, bridge_name);
+
+ /* Count attempts, so that we don't enter an endless loop here. */
+ n++;
+
+ /* The bridge doesn't exist yet. Let's create it */
+ r = create_bridge(rtnl, bridge_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bridge interface %s: %m", bridge_name);
+
+ /* Try again, now that the bridge exists */
+ }
+}
+
+int remove_bridge(const char *bridge_name) {
+ _cleanup_release_lock_file_ LockFile bridge_lock = LOCK_FILE_INIT;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ const char *path;
+ int r;
+
+ /* Removes the specified bridge, but only if it is currently empty */
+
+ if (isempty(bridge_name))
+ return 0;
+
+ r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock);
+ if (r < 0)
+ return log_error_errno(r, "Failed to take network zone lock: %m");
+
+ path = strjoina("/sys/class/net/", bridge_name, "/brif");
+
+ r = dir_is_empty(path);
+ if (r == -ENOENT) /* Already gone? */
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Can't detect if bridge %s is empty: %m", bridge_name);
+ if (r == 0) /* Still populated, leave it around */
+ return 0;
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ return remove_one_link(rtnl, bridge_name);
+}
+
static int parse_interface(struct udev *udev, const char *name) {
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
char ifi_str[2 + DECIMAL_STR_MAX(int)];
@@ -515,13 +646,13 @@ int veth_extra_parse(char ***l, const char *p) {
r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
- if (r == 0 || isempty(a))
+ if (r == 0 || !ifname_valid(a))
return -EINVAL;
r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
- if (r == 0 || isempty(b)) {
+ if (r == 0 || !ifname_valid(b)) {
free(b);
b = strdup(a);
if (!b)
@@ -539,30 +670,6 @@ int veth_extra_parse(char ***l, const char *p) {
return 0;
}
-static int remove_one_veth_link(sd_netlink *rtnl, const char *name) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- int r;
-
- if (isempty(name))
- return 0;
-
- r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate netlink message: %m");
-
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, name);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink interface name: %m");
-
- r = sd_netlink_call(rtnl, m, 0, NULL);
- if (r == -ENODEV) /* Already gone */
- return 0;
- if (r < 0)
- return log_error_errno(r, "Failed to remove veth interface %s: %m", name);
-
- return 1;
-}
-
int remove_veth_links(const char *primary, char **pairs) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char **a, **b;
@@ -578,10 +685,10 @@ int remove_veth_links(const char *primary, char **pairs) {
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- remove_one_veth_link(rtnl, primary);
+ remove_one_link(rtnl, primary);
STRV_FOREACH_PAIR(a, b, pairs)
- remove_one_veth_link(rtnl, *a);
+ remove_one_link(rtnl, *a);
return 0;
}
diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h
index c5036ab470..3d8861e1e5 100644
--- a/src/nspawn/nspawn-network.h
+++ b/src/nspawn/nspawn-network.h
@@ -26,7 +26,8 @@
int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge);
int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs);
-int setup_bridge(const char *veth_name, const char *bridge_name);
+int setup_bridge(const char *veth_name, const char *bridge_name, bool create);
+int remove_bridge(const char *bridge_name);
int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces);
int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces);
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
index b98a79fd09..5f1522cfb6 100644
--- a/src/nspawn/nspawn-settings.c
+++ b/src/nspawn/nspawn-settings.c
@@ -24,10 +24,11 @@
#include "nspawn-settings.h"
#include "parse-util.h"
#include "process-util.h"
+#include "socket-util.h"
+#include "string-util.h"
#include "strv.h"
#include "user-util.h"
#include "util.h"
-#include "string-util.h"
int settings_load(FILE *f, const char *path, Settings **ret) {
_cleanup_(settings_freep) Settings *s = NULL;
@@ -96,6 +97,7 @@ Settings* settings_free(Settings *s) {
strv_free(s->network_ipvlan);
strv_free(s->network_veth_extra);
free(s->network_bridge);
+ free(s->network_zone);
expose_port_free_all(s->expose_ports);
custom_mount_free_all(s->custom_mounts, s->n_custom_mounts);
@@ -111,6 +113,7 @@ bool settings_private_network(Settings *s) {
s->private_network > 0 ||
s->network_veth > 0 ||
s->network_bridge ||
+ s->network_zone ||
s->network_interfaces ||
s->network_macvlan ||
s->network_ipvlan ||
@@ -122,7 +125,8 @@ bool settings_network_veth(Settings *s) {
return
s->network_veth > 0 ||
- s->network_bridge;
+ s->network_bridge ||
+ s->network_zone;
}
DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode");
@@ -319,6 +323,38 @@ int config_parse_veth_extra(
return 0;
}
+int config_parse_network_zone(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Settings *settings = data;
+ _cleanup_free_ char *j = NULL;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ j = strappend("vz-", rvalue);
+ if (!ifname_valid(j)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue);
+ return 0;
+ }
+
+ free(settings->network_zone);
+ settings->network_zone = j;
+ j = NULL;
+
+ return 0;
+}
+
int config_parse_boot(
const char *unit,
const char *filename,
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index e12e91b886..1c47e37912 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -85,6 +85,7 @@ typedef struct Settings {
int private_network;
int network_veth;
char *network_bridge;
+ char *network_zone;
char **network_interfaces;
char **network_macvlan;
char **network_ipvlan;
@@ -109,6 +110,7 @@ int config_parse_volatile_mode(const char *unit, const char *filename, unsigned
int config_parse_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_veth_extra(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_network_zone(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_boot(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_pid2(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_private_users(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 3fc6cc955c..0479389682 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -176,6 +176,7 @@ static char **arg_network_ipvlan = NULL;
static bool arg_network_veth = false;
static char **arg_network_veth_extra = NULL;
static char *arg_network_bridge = NULL;
+static char *arg_network_zone = NULL;
static unsigned long arg_personality = PERSONALITY_INVALID;
static char *arg_image = NULL;
static VolatileMode arg_volatile_mode = VOLATILE_NO;
@@ -234,6 +235,8 @@ static void help(void) {
" Add a virtual Ethernet connection between host\n"
" and container and add it to an existing bridge on\n"
" the host\n"
+ " --network-zone=NAME Add a virtual Ethernet connection to the container,\n"
+ " and add it to an automatically managed bridge interface\n"
" -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n"
" Expose a container IP port on the host\n"
" -Z --selinux-context=SECLABEL\n"
@@ -357,6 +360,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NETWORK_MACVLAN,
ARG_NETWORK_IPVLAN,
ARG_NETWORK_BRIDGE,
+ ARG_NETWORK_ZONE,
ARG_NETWORK_VETH_EXTRA,
ARG_PERSONALITY,
ARG_VOLATILE,
@@ -404,6 +408,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "network-veth", no_argument, NULL, 'n' },
{ "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA},
{ "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE },
+ { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE },
{ "personality", required_argument, NULL, ARG_PERSONALITY },
{ "image", required_argument, NULL, 'i' },
{ "volatile", optional_argument, NULL, ARG_VOLATILE },
@@ -466,7 +471,35 @@ static int parse_argv(int argc, char *argv[]) {
arg_settings_mask |= SETTING_USER;
break;
+ case ARG_NETWORK_ZONE: {
+ char *j;
+
+ j = strappend("vz-", optarg);
+ if (!j)
+ return log_oom();
+
+ if (!ifname_valid(j)) {
+ log_error("Network zone name not valid: %s", j);
+ free(j);
+ return -EINVAL;
+ }
+
+ free(arg_network_zone);
+ arg_network_zone = j;
+
+ arg_network_veth = true;
+ arg_private_network = true;
+ arg_settings_mask |= SETTING_NETWORK;
+ break;
+ }
+
case ARG_NETWORK_BRIDGE:
+
+ if (!ifname_valid(optarg)) {
+ log_error("Bridge interface name not valid: %s", optarg);
+ return -EINVAL;
+ }
+
r = free_and_strdup(&arg_network_bridge, optarg);
if (r < 0)
return log_oom();
@@ -489,6 +522,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NETWORK_INTERFACE:
+
+ if (!ifname_valid(optarg)) {
+ log_error("Network interface name not valid: %s", optarg);
+ return -EINVAL;
+ }
+
if (strv_extend(&arg_network_interfaces, optarg) < 0)
return log_oom();
@@ -497,6 +536,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NETWORK_MACVLAN:
+
+ if (!ifname_valid(optarg)) {
+ log_error("MACVLAN network interface name not valid: %s", optarg);
+ return -EINVAL;
+ }
+
if (strv_extend(&arg_network_macvlan, optarg) < 0)
return log_oom();
@@ -505,6 +550,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NETWORK_IPVLAN:
+
+ if (!ifname_valid(optarg)) {
+ log_error("IPVLAN network interface name not valid: %s", optarg);
+ return -EINVAL;
+ }
+
if (strv_extend(&arg_network_ipvlan, optarg) < 0)
return log_oom();
@@ -1003,6 +1054,11 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (arg_network_bridge && arg_network_zone) {
+ log_error("--network-bridge= and --network-zone= may not be combined.");
+ return -EINVAL;
+ }
+
if (argc > optind) {
arg_parameters = strv_copy(argv + optind);
if (!arg_parameters)
@@ -3271,6 +3327,7 @@ static int load_settings(void) {
(settings->private_network >= 0 ||
settings->network_veth >= 0 ||
settings->network_bridge ||
+ settings->network_zone ||
settings->network_interfaces ||
settings->network_macvlan ||
settings->network_ipvlan ||
@@ -3301,6 +3358,10 @@ static int load_settings(void) {
free(arg_network_bridge);
arg_network_bridge = settings->network_bridge;
settings->network_bridge = NULL;
+
+ free(arg_network_zone);
+ arg_network_zone = settings->network_zone;
+ settings->network_zone = NULL;
}
}
@@ -3346,7 +3407,7 @@ int main(int argc, char *argv[]) {
int ret = EXIT_SUCCESS;
union in_addr_union exposed = {};
_cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
- bool interactive;
+ bool interactive, veth_created = false;
log_parse_environment();
log_open();
@@ -3800,14 +3861,23 @@ int main(int argc, char *argv[]) {
goto finish;
if (arg_network_veth) {
- r = setup_veth(arg_machine, pid, veth_name, !!arg_network_bridge);
+ r = setup_veth(arg_machine, pid, veth_name,
+ arg_network_bridge || arg_network_zone);
if (r < 0)
goto finish;
else if (r > 0)
ifi = r;
if (arg_network_bridge) {
- r = setup_bridge(veth_name, arg_network_bridge);
+ /* Add the interface to a bridge */
+ r = setup_bridge(veth_name, arg_network_bridge, false);
+ if (r < 0)
+ goto finish;
+ if (r > 0)
+ ifi = r;
+ } else if (arg_network_zone) {
+ /* Add the interface to a bridge, possibly creating it */
+ r = setup_bridge(veth_name, arg_network_zone, true);
if (r < 0)
goto finish;
if (r > 0)
@@ -3819,6 +3889,12 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
+ /* We created the primary and extra veth links now; let's remember this, so that we know to
+ remove them later on. Note that we don't bother with removing veth links that were created
+ here when their setup failed half-way, because in that case the kernel should be able to
+ remove them on its own, since they cannot be referenced by anything yet. */
+ veth_created = true;
+
r = setup_macvlan(arg_machine, pid, arg_network_macvlan);
if (r < 0)
goto finish;
@@ -3981,7 +4057,9 @@ int main(int argc, char *argv[]) {
}
expose_port_flush(arg_expose_ports, &exposed);
+
(void) remove_veth_links(veth_name, arg_network_veth_extra);
+ veth_created = false;
}
finish:
@@ -4014,7 +4092,10 @@ finish:
}
expose_port_flush(arg_expose_ports, &exposed);
- (void) remove_veth_links(veth_name, arg_network_veth_extra);
+
+ if (veth_created)
+ (void) remove_veth_links(veth_name, arg_network_veth_extra);
+ (void) remove_bridge(arg_network_zone);
free(arg_directory);
free(arg_template);
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 1141f9964f..83be79a4f5 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -37,6 +37,7 @@
#include "path-util.h"
#include "process-util.h"
#include "signal-util.h"
+#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
#include "syslog-util.h"
@@ -873,3 +874,40 @@ int config_parse_personality(
*personality = p;
return 0;
}
+
+int config_parse_ifname(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char **s = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ *s = mfree(*s);
+ return 0;
+ }
+
+ if (!ifname_valid(rvalue)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ r = free_and_strdup(s, rvalue);
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 73fb132413..f6964e3fd4 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -125,6 +125,7 @@ int config_parse_log_facility(const char *unit, const char *filename, unsigned l
int config_parse_log_level(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_signal(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_personality(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_ifname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \
int function(const char *unit, \
diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c
index ade2de7727..97865eac4a 100644
--- a/src/shared/firewall-util.c
+++ b/src/shared/firewall-util.c
@@ -44,6 +44,7 @@
#include "firewall-util.h"
#include "in-addr-util.h"
#include "macro.h"
+#include "socket-util.h"
DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free);
@@ -59,10 +60,9 @@ static int entry_fill_basics(
assert(entry);
- if (out_interface && strlen(out_interface) >= IFNAMSIZ)
+ if (out_interface && !ifname_valid(out_interface))
return -EINVAL;
-
- if (in_interface && strlen(in_interface) >= IFNAMSIZ)
+ if (in_interface && !ifname_valid(in_interface))
return -EINVAL;
entry->ip.proto = protocol;
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
index 9e01f3afd4..b480fdaa9c 100644
--- a/src/test/test-socket-util.c
+++ b/src/test/test-socket-util.c
@@ -27,6 +27,29 @@
#include "string-util.h"
#include "util.h"
+static void test_ifname_valid(void) {
+ assert(ifname_valid("foo"));
+ assert(ifname_valid("eth0"));
+
+ assert(!ifname_valid("0"));
+ assert(!ifname_valid("99"));
+ assert(ifname_valid("a99"));
+ assert(ifname_valid("99a"));
+
+ assert(!ifname_valid(NULL));
+ assert(!ifname_valid(""));
+ assert(!ifname_valid(" "));
+ assert(!ifname_valid(" foo"));
+ assert(!ifname_valid("bar\n"));
+ assert(!ifname_valid("."));
+ assert(!ifname_valid(".."));
+ assert(ifname_valid("foo.bar"));
+ assert(!ifname_valid("x:y"));
+
+ assert(ifname_valid("xxxxxxxxxxxxxxx"));
+ assert(!ifname_valid("xxxxxxxxxxxxxxxx"));
+}
+
static void test_socket_address_parse(void) {
SocketAddress a;
@@ -362,6 +385,8 @@ int main(int argc, char *argv[]) {
log_set_max_level(LOG_DEBUG);
+ test_ifname_valid();
+
test_socket_address_parse();
test_socket_address_parse_netlink();
test_socket_address_equal();
diff --git a/test/TEST-01-BASIC/test.sh b/test/TEST-01-BASIC/test.sh
index 6963d8c88d..21eed9b22a 100755
--- a/test/TEST-01-BASIC/test.sh
+++ b/test/TEST-01-BASIC/test.sh
@@ -53,7 +53,7 @@ Description=Testsuite service
After=multi-user.target
[Service]
-ExecStart=/bin/sh -x -c 'systemctl --failed --no-legend --no-pager > /failed ; echo OK > /testok'
+ExecStart=/bin/sh -x -c 'systemctl --state=failed --no-legend --no-pager > /failed ; echo OK > /testok'
Type=oneshot
EOF
diff --git a/test/TEST-02-CRYPTSETUP/test.sh b/test/TEST-02-CRYPTSETUP/test.sh
index 242090c761..aea0fc53f6 100755
--- a/test/TEST-02-CRYPTSETUP/test.sh
+++ b/test/TEST-02-CRYPTSETUP/test.sh
@@ -59,7 +59,7 @@ Description=Testsuite service
After=multi-user.target
[Service]
-ExecStart=/bin/sh -x -c 'systemctl --failed --no-legend --no-pager > /failed ; echo OK > /testok'
+ExecStart=/bin/sh -x -c 'systemctl --state=failed --no-legend --no-pager > /failed ; echo OK > /testok'
Type=oneshot
EOF
diff --git a/test/TEST-08-ISSUE-2730/test.sh b/test/TEST-08-ISSUE-2730/test.sh
index 92e70b828b..409140157a 100755
--- a/test/TEST-08-ISSUE-2730/test.sh
+++ b/test/TEST-08-ISSUE-2730/test.sh
@@ -67,6 +67,20 @@ WantedBy=local-fs.target
Alias=root.mount
EOF
+ cat >$initdir/etc/systemd/system/systemd-remount-fs.service <<EOF
+[Unit]
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=systemd-fsck-root.service
+Before=local-fs-pre.target local-fs.target shutdown.target
+Wants=local-fs-pre.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/bin/systemctl reload /
+EOF
+
setup_testsuite
) || return 1
diff --git a/test/test-functions b/test/test-functions
index b07c500339..e2e07a833c 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -64,7 +64,6 @@ run_qemu() {
find_qemu_bin || return 1
KERNEL_APPEND="root=/dev/sda1 \
-systemd.log_level=debug \
raid=noautodetect \
loglevel=2 \
init=$ROOTLIBDIR/systemd \
@@ -79,6 +78,7 @@ $KERNEL_APPEND \
-m 512M \
-nographic \
-kernel $KERNEL_BIN \
+-drive format=raw,cache=unsafe,file=${TESTDIR}/rootdisk.img \
"
if [[ "$INITRD" && "$SKIP_INITRD" != "yes" ]]; then
@@ -93,7 +93,7 @@ $KERNEL_APPEND \
QEMU_BIN="timeout --foreground $QEMU_TIMEOUT $QEMU_BIN"
fi
( set -x
- $QEMU_BIN $QEMU_OPTIONS -append "$KERNEL_APPEND" $TESTDIR/rootdisk.img ) || return 1
+ $QEMU_BIN $QEMU_OPTIONS -append "$KERNEL_APPEND" ) || return 1
}
run_nspawn() {
@@ -244,6 +244,9 @@ install_systemd() {
# we strip binaries since debug symbols increase binaries size a lot
# and it could fill the available space
strip_binaries
+
+ # enable debug logging in PID1
+ echo LogLevel=debug >> $initdir/etc/systemd/system.conf
}
install_missing_libraries() {
@@ -415,6 +418,9 @@ install_pam() {
[[ "$LOOKS_LIKE_DEBIAN" ]] &&
cp /etc/pam.d/systemd-user $initdir/etc/pam.d/
+
+ # set empty root password for easy debugging
+ sed -i 's/^root:x:/root::/' $initdir/etc/passwd
}
install_keymaps() {
diff --git a/units/systemd-fsck@.service.in b/units/systemd-fsck@.service.in
index 0468392dc4..6ca6b07e9e 100644
--- a/units/systemd-fsck@.service.in
+++ b/units/systemd-fsck@.service.in
@@ -11,7 +11,7 @@ Documentation=man:systemd-fsck@.service(8)
DefaultDependencies=no
BindsTo=%i.device
After=%i.device systemd-fsck-root.service local-fs-pre.target
-Before=shutdown.target
+Before=systemd-quotacheck.service shutdown.target
[Service]
Type=oneshot