<term><varname>VXLAN=</varname></term>
<term><varname>Xfrm=</varname></term>
<listitem>
- <para>The name of an IPoIB, IPVLAN, IPVTAP, MACsec, MACVLAN, MACVTAP, tunnel, VLAN,
- VXLAN, or Xfrm to be created on the link. See
+ <para>Takes a space-separated list of stacked netdevs to be created on the interface. See
<citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
- This option may be specified more than once.</para>
+ This option may be specified more than once. If an empty string is specified, previous assignments
+ for all netdev types are cleared.</para>
<xi:include href="version-info.xml" xpointer="v211"/>
</listitem>
#include "condition.h"
#include "conf-files.h"
#include "conf-parser.h"
+#include "extract-word.h"
#include "in-addr-util.h"
#include "iovec-util.h"
#include "net-condition.h"
void *data,
void *userdata) {
- _cleanup_free_ char *name = NULL;
NetDevKind kind = ltype;
Hashmap **h = ASSERT_PTR(data);
int r;
NETDEV_KIND_XFRM,
_NETDEV_KIND_TUNNEL));
- if (!ifname_valid(rvalue)) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
+ if (isempty(rvalue)) {
+ *h = hashmap_free(*h);
return 0;
}
- name = strdup(rvalue);
- if (!name)
- return log_oom();
+ for (const char *p = rvalue;;) {
+ _cleanup_free_ char *name = NULL;
- r = hashmap_ensure_put(h, &string_hash_ops_free, name, INT_TO_PTR(kind));
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
- else if (r == 0)
- log_syntax(unit, LOG_DEBUG, filename, line, r,
- "NetDev '%s' specified twice, ignoring.", name);
- else
- TAKE_PTR(name);
+ r = extract_first_word(&p, &name, /* separators= */ NULL, /* flags= */ 0);
+ if (r < 0)
+ return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
+ if (r == 0)
+ return 0;
- return 0;
+ if (!ifname_valid(name)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, name);
+ continue;
+ }
+
+ r = hashmap_ensure_put(h, &string_hash_ops_free, name, INT_TO_PTR(kind));
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
+ else if (r == 0)
+ log_syntax(unit, LOG_DEBUG, filename, line, r,
+ "NetDev '%s' specified twice, ignoring.", name);
+ else
+ TAKE_PTR(name);
+ }
}
int config_parse_required_for_online(
test_config_parse_multipath_route_one("@wg0 abc", 0, 0); /* Non-numeric */
}
+TEST(config_parse_stacked_netdev) {
+ _cleanup_hashmap_free_ Hashmap *netdevs = NULL;
+ ASSERT_OK(config_parse_stacked_netdev("network", "filename", 1, "section", 1, "VLAN", NETDEV_KIND_VLAN, "foo bar baz invalid:name", &netdevs, NULL));
+ ASSERT_OK(config_parse_stacked_netdev("network", "filename", 1, "section", 1, "VXLAN", NETDEV_KIND_VXLAN, "aaa 321 foo bbb", &netdevs, NULL));
+
+ static const struct {
+ const char *name;
+ NetDevKind kind;
+ } expected[] = {
+ { "foo", NETDEV_KIND_VLAN },
+ { "bar", NETDEV_KIND_VLAN },
+ { "baz", NETDEV_KIND_VLAN },
+ { "aaa", NETDEV_KIND_VXLAN },
+ { "bbb", NETDEV_KIND_VXLAN },
+ };
+
+ ASSERT_EQ(hashmap_size(netdevs), ELEMENTSOF(expected));
+ FOREACH_ELEMENT(i, expected) {
+ void *p = ASSERT_NOT_NULL(hashmap_get(netdevs, i->name));
+ ASSERT_EQ(PTR_TO_INT(p), i->kind);
+ }
+
+ ASSERT_OK(config_parse_stacked_netdev("network", "filename", 1, "section", 1, "VXLAN", NETDEV_KIND_VXLAN, "", &netdevs, NULL));
+ ASSERT_NULL(netdevs);
+}
+
DEFINE_TEST_MAIN(LOG_INFO);