chiark / gitweb /
networkd: add basic support for MACVLANs
authorTom Gundersen <teg@jklm.no>
Tue, 25 Feb 2014 20:16:17 +0000 (21:16 +0100)
committerTom Gundersen <teg@jklm.no>
Tue, 25 Feb 2014 20:19:08 +0000 (21:19 +0100)
man/systemd.netdev.xml
man/systemd.network.xml
src/network/networkd-link.c
src/network/networkd-netdev-gperf.gperf
src/network/networkd-netdev.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd.h

index 26cc6158c95bc6fae7dda531a318a6a9a4b8a63f..5d8b41a3bc229b73f74ed5c3143a7b54857020a4 100644 (file)
                                         <term><varname>Kind=</varname></term>
                                         <listitem>
                                                 <para>The netdev kind. Currently, <literal>bridge</literal>,
-                                                <literal>bond</literal> and <literal>vlan</literal>
-                                                are supported. This option is compulsory.</para>
+                                                <literal>bond</literal>, <literal>vlan</literal> and
+                                                <literal>macvlan</literal> are supported. This option
+                                                is compulsory.</para>
                                         </listitem>
                                 </varlistentry>
                         </variablelist>
                                 </varlistentry>
                         </variablelist>
 
+                        <para>The <literal>[MACVLAN]</literal> section only applies for netdevs of kind
+                        <literal>macvlan</literal>, and accepts the following key:</para>
+
+                        <variablelist class='network-directives'>
+                                <varlistentry>
+                                        <term><varname>Mode=</varname></term>
+                                        <listitem>
+                                                <para>The MACVLAN mode to use. The supported options are
+                                                <literal>private</literal>, <literal>vepa</literal>,
+                                                <literal>bridge</literal> and <literal>passthru</literal>.
+                                                </para>
+                                        </listitem>
+                                </varlistentry>
+                        </variablelist>
+
         </refsect1>
 
         <refsect1>
index 1ba4f4a971652d70a4b1878efdbeff03efcd94c9..fcf48c6d46cf2f662a2ca15fe054811953d903c3 100644 (file)
                                                 may be specified more than once.</para>
                                         </listitem>
                                 </varlistentry>
+                                <varlistentry>
+                                        <term><varname>MACVLAN=</varname></term>
+                                        <listitem>
+                                                <para>The name of a MACVLAN to create on the link. This option
+                                                may be specified more than once.</para>
+                                        </listitem>
+                                </varlistentry>
                         </variablelist>
 
                         <para>An <literal>[Address]</literal> section accepts the following keys.
index 1f495b38bbc579f4951bc039123f15d40af1bbc0..b217123ecc3efb193662c8c03b97a5a28cd1b01e 100644 (file)
@@ -890,7 +890,7 @@ static int enslave_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
 }
 
 static int link_enter_enslave(Link *link) {
-        NetDev *vlan;
+        NetDev *vlan, *macvlan;
         Iterator i;
         int r;
 
@@ -901,7 +901,8 @@ static int link_enter_enslave(Link *link) {
         link->state = LINK_STATE_ENSLAVING;
 
         if (!link->network->bridge && !link->network->bond &&
-            hashmap_isempty(link->network->vlans))
+            hashmap_isempty(link->network->vlans) &&
+            hashmap_isempty(link->network->macvlans))
                 return link_enslaved(link);
 
         if (link->network->bridge) {
@@ -943,6 +944,24 @@ static int link_enter_enslave(Link *link) {
                 link->enslaving ++;
         }
 
+        HASHMAP_FOREACH(macvlan, link->network->macvlans, i) {
+                log_struct_link(LOG_DEBUG, link,
+                                "MESSAGE=%s: enslaving by '%s'",
+                                link->ifname, macvlan->name, NETDEV(macvlan), NULL);
+
+                r = netdev_enslave(macvlan, link, &enslave_handler);
+                if (r < 0) {
+                        log_struct_link(LOG_WARNING, link,
+                                        "MESSAGE=%s: could not enslave by '%s': %s",
+                                        link->ifname, macvlan->name, strerror(-r),
+                                        NETDEV(macvlan), NULL);
+                        link_enter_failed(link);
+                        return r;
+                }
+
+                link->enslaving ++;
+        }
+
         return 0;
 }
 
index 7dd47f971fe137a163a18f1c53220b6030be46be..2793d77a79329a484de6c591ff331b6dafaa70b5 100644 (file)
@@ -23,3 +23,4 @@ NetDev.Description,      config_parse_string,                0,
 NetDev.Name,             config_parse_ifname,                0,                             offsetof(NetDev, name)
 NetDev.Kind,             config_parse_netdev_kind,           0,                             offsetof(NetDev, kind)
 VLAN.Id,                 config_parse_uint64,                0,                             offsetof(NetDev, vlanid)
+MACVLAN.Mode,            config_parse_macvlan_mode,          0,                             offsetof(NetDev, macvlan_mode)
index 05f21fa87426272c08c51c695063ec73569a597e..9f580aeab9dd8d7425e800115d4db920cbd94576 100644 (file)
@@ -32,11 +32,22 @@ static const char* const netdev_kind_table[] = {
         [NETDEV_KIND_BRIDGE] = "bridge",
         [NETDEV_KIND_BOND] = "bond",
         [NETDEV_KIND_VLAN] = "vlan",
+        [NETDEV_KIND_MACVLAN] = "macvlan",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
 
+static const char* const macvlan_mode_table[] = {
+        [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
+        [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
+        [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
+        [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
+
 void netdev_free(NetDev *netdev) {
         netdev_enslave_callback *callback;
 
@@ -166,8 +177,8 @@ static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t c
         int r;
 
         assert(netdev);
-        assert(!(netdev->kind == NETDEV_KIND_VLAN) ||
-               (link && callback && netdev->vlanid <= VLANID_MAX));
+        assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
+               (link && callback));
         assert(netdev->name);
         assert(netdev->manager);
         assert(netdev->manager->rtnl);
@@ -220,7 +231,7 @@ static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t c
                 return r;
         }
 
-        if (netdev->vlanid <= VLANID_MAX) {
+        if (netdev->vlanid <= VLANID_MAX || netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
                 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
                 if (r < 0) {
                         log_error_netdev(netdev,
@@ -229,12 +240,24 @@ static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t c
                         return r;
                 }
 
-                r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
-                if (r < 0) {
-                        log_error_netdev(netdev,
-                                         "Could not append IFLA_VLAN_ID attribute: %s",
-                                         strerror(-r));
-                        return r;
+                if (netdev->vlanid <= VLANID_MAX) {
+                        r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
+                        if (r < 0) {
+                                log_error_netdev(netdev,
+                                                 "Could not append IFLA_VLAN_ID attribute: %s",
+                                                 strerror(-r));
+                                return r;
+                        }
+                }
+
+                if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
+                        r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
+                        if (r < 0) {
+                                log_error_netdev(netdev,
+                                                 "Could not append IFLA_MACVLAN_MODE attribute: %s",
+                                                 strerror(-r));
+                                return r;
+                        }
                 }
 
                 r = sd_rtnl_message_close_container(req);
@@ -272,7 +295,7 @@ static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t c
 }
 
 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
-        if (netdev->kind == NETDEV_KIND_VLAN)
+        if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
                 return netdev_create(netdev, link, callback);
 
         if (netdev->state == NETDEV_STATE_READY) {
@@ -335,10 +358,12 @@ static int netdev_load_one(Manager *manager, const char *filename) {
         netdev->manager = manager;
         netdev->state = _NETDEV_STATE_INVALID;
         netdev->kind = _NETDEV_KIND_INVALID;
+        netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
         netdev->vlanid = VLANID_MAX + 1;
 
-        r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0", config_item_perf_lookup,
-                        (void*) network_netdev_gperf_lookup, false, false, netdev);
+        r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
+                         config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
+                         false, false, netdev);
         if (r < 0) {
                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
                 return r;
@@ -359,6 +384,19 @@ static int netdev_load_one(Manager *manager, const char *filename) {
                 return 0;
         }
 
+        if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
+                log_warning("VLAN Id configured for a %s in %s. Ignoring",
+                            netdev_kind_to_string(netdev->kind), filename);
+                return 0;
+        }
+
+        if (netdev->kind != NETDEV_KIND_MACVLAN &&
+            netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
+                log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
+                            netdev_kind_to_string(netdev->kind), filename);
+                return 0;
+        }
+
         netdev->filename = strdup(filename);
         if (!netdev->filename)
                 return log_oom();
@@ -375,7 +413,8 @@ static int netdev_load_one(Manager *manager, const char *filename) {
 
         LIST_HEAD_INIT(netdev->callbacks);
 
-        if (netdev->kind != NETDEV_KIND_VLAN) {
+        if (netdev->kind != NETDEV_KIND_VLAN &&
+            netdev->kind != NETDEV_KIND_MACVLAN) {
                 r = netdev_create(netdev, NULL, NULL);
                 if (r < 0)
                         return r;
index 44aeb9c3ae745a36027fd2301c12a54a778c6c78..6cc186f2a384a2211aea9413b8243a187229cd4a 100644 (file)
@@ -28,6 +28,7 @@ Network.Description,         config_parse_string,                0,
 Network.Bridge,              config_parse_bridge,                0,                             offsetof(Network, bridge)
 Network.Bond,                config_parse_bond,                  0,                             offsetof(Network, bond)
 Network.VLAN,                config_parse_vlan,                  0,                             offsetof(Network, vlans)
+Network.MACVLAN,             config_parse_macvlan,               0,                             offsetof(Network, macvlans)
 Network.DHCP,                config_parse_bool,                  0,                             offsetof(Network, dhcp)
 Network.Address,             config_parse_address,               0,                             0
 Network.Gateway,             config_parse_gateway,               0,                             0
index 14fa92aeaf62d6cce055ef23439696f5a3927c1c..ca79d4ff6d5c796b22ae8e055650c4b53c6c33d2 100644 (file)
@@ -53,10 +53,14 @@ static int network_load_one(Manager *manager, const char *filename) {
         LIST_HEAD_INIT(network->static_addresses);
         LIST_HEAD_INIT(network->static_routes);
 
-        network->vlans = hashmap_new(uint64_hash_func, uint64_compare_func);
+        network->vlans = hashmap_new(string_hash_func, string_compare_func);
         if (!network->vlans)
                 return log_oom();
 
+        network->macvlans = hashmap_new(uint64_hash_func, uint64_compare_func);
+        if (!network->macvlans)
+                return log_oom();
+
         network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
         if (!network->addresses_by_section)
                 return log_oom();
@@ -150,6 +154,8 @@ void network_free(Network *network) {
 
         hashmap_free(network->vlans);
 
+        hashmap_free(network->macvlans);
+
         while ((route = network->static_routes))
                 route_free(route);
 
@@ -330,3 +336,45 @@ int config_parse_vlan(const char *unit,
 
         return 0;
 }
+
+int config_parse_macvlan(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) {
+        Network *network = userdata;
+        NetDev *netdev;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = netdev_get(network->manager, rvalue, &netdev);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "MACVLAN is invalid, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        if (netdev->kind != NETDEV_KIND_MACVLAN) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "NetDev is not a MACVLAN, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        r = hashmap_put(network->macvlans, netdev->name, netdev);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "Can not add MACVLAN to network: %s", rvalue);
+                return 0;
+        }
+
+        return 0;
+}
index 8307bb5b13c2fddc42f90157adecb0c03e2f0923..85c300982fa541f5b319f1edcfda08cbd8459b00 100644 (file)
@@ -50,10 +50,20 @@ struct netdev_enslave_callback {
         LIST_FIELDS(netdev_enslave_callback, callbacks);
 };
 
+typedef enum MacVlanMode {
+        NETDEV_MACVLAN_MODE_PRIVATE = MACVLAN_MODE_PRIVATE,
+        NETDEV_MACVLAN_MODE_VEPA = MACVLAN_MODE_VEPA,
+        NETDEV_MACVLAN_MODE_BRIDGE = MACVLAN_MODE_BRIDGE,
+        NETDEV_MACVLAN_MODE_PASSTHRU = MACVLAN_MODE_PASSTHRU,
+        _NETDEV_MACVLAN_MODE_MAX,
+        _NETDEV_MACVLAN_MODE_INVALID = -1
+} MacVlanMode;
+
 typedef enum NetDevKind {
         NETDEV_KIND_BRIDGE,
         NETDEV_KIND_BOND,
         NETDEV_KIND_VLAN,
+        NETDEV_KIND_MACVLAN,
         _NETDEV_KIND_MAX,
         _NETDEV_KIND_INVALID = -1
 } NetDevKind;
@@ -81,6 +91,7 @@ struct NetDev {
         NetDevKind kind;
 
         uint64_t vlanid;
+        int32_t macvlan_mode;
 
         int ifindex;
         NetDevState state;
@@ -107,6 +118,7 @@ struct Network {
         NetDev *bridge;
         NetDev *bond;
         Hashmap *vlans;
+        Hashmap *macvlans;
         bool dhcp;
         bool dhcp_dns;
         bool dhcp_mtu;
@@ -248,8 +260,13 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t cb);
 const char *netdev_kind_to_string(NetDevKind d) _const_;
 NetDevKind netdev_kind_from_string(const char *d) _pure_;
 
+const char *macvlan_mode_to_string(MacVlanMode d) _const_;
+MacVlanMode macvlan_mode_from_string(const char *d) _pure_;
+
 int config_parse_netdev_kind(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_macvlan_mode(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);
+
 /* gperf */
 const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, unsigned length);
 
@@ -277,6 +294,10 @@ int config_parse_vlan(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_macvlan(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);
+
 /* gperf */
 const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, unsigned length);