Merge pull request #3209 from poettering/nspawn-network-zones

introduce simple "network zones" concept to nspawn
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2016-05-09 14:34:05 -04:00
commit 36c9a0728d
30 changed files with 657 additions and 204 deletions

View file

@ -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 \

5
TODO
View file

@ -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

View file

@ -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,15 +561,42 @@
<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>

View file

@ -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>

View file

@ -419,6 +419,16 @@
option is privileged (see above).</para></listitem>
</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>

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -43,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) {
@ -795,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;

View file

@ -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);

View file

@ -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();

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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");
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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,43 +265,147 @@ 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);
return -errno;
r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0);
if (r < 0)
return r;
r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP);
if (r < 0)
return r;
r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name);
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge_ifi);
if (r < 0)
return r;
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
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");
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");
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 = 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");
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");
}
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");
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);
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");
/* Count attempts, so that we don't enter an endless loop here. */
n++;
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
return log_error_errno(r, "Failed to add veth interface to bridge: %m");
/* 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);
return bridge_ifi;
/* 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) {
@ -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;
}

View file

@ -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);

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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, \

View file

@ -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;

View file

@ -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();