]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: add support to create wlan virtual interface 22502/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 14 Feb 2022 17:09:21 +0000 (02:09 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 15 Feb 2022 14:24:55 +0000 (23:24 +0900)
Closes #18977.

man/systemd.netdev.xml
src/network/meson.build
src/network/netdev/netdev-gperf.gperf
src/network/netdev/netdev.c
src/network/netdev/netdev.h
src/network/netdev/wlan.c [new file with mode: 0644]
src/network/netdev/wlan.h [new file with mode: 0644]
test/fuzz/fuzz-netdev-parser/directives.netdev

index 2207852ae307465869052120d8c7b9d16fc67b89..1f94e3d599f4128f4e6dd8f311f6aa9c43636815 100644 (file)
 
           <row><entry><varname>ipoib</varname></entry>
           <entry>An IP over Infiniband subinterface.</entry></row>
+
+          <row><entry><varname>virtual-wlan</varname></entry>
+          <entry>A virtual local wireless network (WLAN) interface.</entry></row>
         </tbody>
       </tgroup>
     </table>
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>[VirtualWLAN] Section Options</title>
+    <para>The [VirtualWLAN] section only applies to virtual WLAN interfaces, and accepts the following
+    keys:</para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>PhysicalDevice=</varname></term>
+        <listitem>
+          <para>Specifies the name or index of the WLAN physical WLAN device (e.g. <literal>0</literal>
+          or <literal>phy0</literal>). The list of the physical WLAN devices that exist os the host can
+          be obtained by <command>iw phy</command> command. This option is mandatory.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Type=</varname></term>
+        <listitem>
+          <para>Specifies the type of the interface. Takes one of the <literal>ad-hoc</literal>,
+          <literal>station</literal>, <literal>ap</literal>, <literal>ap-vlan</literal>,
+          <literal>wds</literal>, <literal>monitor</literal>, <literal>mesh-point</literal>,
+          <literal>p2p-client</literal>, <literal>p2p-go</literal>, <literal>p2p-device</literal>,
+          <literal>ocb</literal>, and <literal>nan</literal>. This option is mandatory.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>WDS=</varname></term>
+        <listitem>
+          <para>Enables the Wireless Distribution System (WDS) mode on the interface. The mode is also
+          known as the <literal>4 address mode</literal>. Takes a boolean value. Defaults to unset, and
+          the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>Examples</title>
     <example>
index 1b8aa80cfe2dc152c0db0f23f3a2ca7649dbdb49..ca48acae91c35fc98e683eb048612ba593dc8f13 100644 (file)
@@ -53,6 +53,8 @@ sources = files('''
         netdev/vxcan.h
         netdev/wireguard.c
         netdev/wireguard.h
+        netdev/wlan.c
+        netdev/wlan.h
         netdev/xfrm.c
         netdev/xfrm.h
         networkd-address-generation.c
index 0b87e350870255985d4c9b89be5d5e4271973c18..2fec1da06bdd5cae55fc81661fd88f9104e3409c 100644 (file)
@@ -27,6 +27,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "vxcan.h"
 #include "vxlan.h"
 #include "wireguard.h"
+#include "wlan.h"
 #include "xfrm.h"
 %}
 struct ConfigPerfItem;
@@ -258,3 +259,6 @@ BatmanAdvanced.RoutingAlgorithm,          config_parse_batadv_routing_algorithm,
 IPoIB.PartitionKey,                       config_parse_ipoib_pkey,                   0,                             offsetof(IPoIB, pkey)
 IPoIB.Mode,                               config_parse_ipoib_mode,                   0,                             offsetof(IPoIB, mode)
 IPoIB.IgnoreUserspaceMulticastGroups,     config_parse_tristate,                     0,                             offsetof(IPoIB, umcast)
+VirtualWLAN.PhysicalDevice,               config_parse_wiphy,                        0,                             0
+VirtualWLAN.Type,                         config_parse_wlan_iftype,                  0,                             offsetof(WLan, iftype)
+VirtualWLAN.WDS,                          config_parse_tristate,                     0,                             offsetof(WLan, wds)
index 48b263eaa63370f0b60f244ac5404b5e524e466c..8f68a50297feb7483ea8d915fb8a848af8e67c46 100644 (file)
@@ -47,6 +47,7 @@
 #include "vxcan.h"
 #include "vxlan.h"
 #include "wireguard.h"
+#include "wlan.h"
 #include "xfrm.h"
 
 const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
@@ -86,6 +87,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
         [NETDEV_KIND_VXCAN]     = &vxcan_vtable,
         [NETDEV_KIND_VXLAN]     = &vxlan_vtable,
         [NETDEV_KIND_WIREGUARD] = &wireguard_vtable,
+        [NETDEV_KIND_WLAN]      = &wlan_vtable,
         [NETDEV_KIND_XFRM]      = &xfrm_vtable,
 };
 
@@ -126,6 +128,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
         [NETDEV_KIND_VXCAN]     = "vxcan",
         [NETDEV_KIND_VXLAN]     = "vxlan",
         [NETDEV_KIND_WIREGUARD] = "wireguard",
+        [NETDEV_KIND_WLAN]      = "virtual-wlan",
         [NETDEV_KIND_XFRM]      = "xfrm",
 };
 
index 6382d4e620bd10e2ac45b61fde7def462b97fe67..be26d1969ddca08038f57f4c21b86234566be44d 100644 (file)
@@ -43,6 +43,7 @@
         "-VXLAN\0"                                \
         "-WireGuard\0"                            \
         "-WireGuardPeer\0"                        \
+        "-VirtualWLAN\0"                          \
         "-Xfrm\0"
 
 typedef enum NetDevKind {
@@ -82,6 +83,7 @@ typedef enum NetDevKind {
         NETDEV_KIND_VXCAN,
         NETDEV_KIND_VXLAN,
         NETDEV_KIND_WIREGUARD,
+        NETDEV_KIND_WLAN,
         NETDEV_KIND_XFRM,
         _NETDEV_KIND_MAX,
         _NETDEV_KIND_TUNNEL, /* Used by config_parse_stacked_netdev() */
diff --git a/src/network/netdev/wlan.c b/src/network/netdev/wlan.c
new file mode 100644 (file)
index 0000000..17fff4d
--- /dev/null
@@ -0,0 +1,260 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <net/if_arp.h>
+
+#include "sd-netlink.h"
+
+#include "netlink-util.h"
+#include "networkd-manager.h"
+#include "networkd-wiphy.h"
+#include "parse-util.h"
+#include "wifi-util.h"
+#include "wlan.h"
+
+static void wlan_done(NetDev *netdev) {
+        WLan *w;
+
+        assert(netdev);
+
+        w = WLAN(netdev);
+
+        assert(w);
+
+        w->wiphy_name = mfree(w->wiphy_name);
+}
+
+static void wlan_init(NetDev *netdev) {
+        WLan *w;
+
+        assert(netdev);
+
+        w = WLAN(netdev);
+
+        assert(w);
+
+        w->wiphy_index = UINT32_MAX;
+        w->wds = -1;
+}
+
+static int wlan_get_wiphy(NetDev *netdev, Wiphy **ret) {
+        WLan *w;
+
+        assert(netdev);
+
+        w = WLAN(netdev);
+
+        assert(w);
+
+        if (w->wiphy_name)
+                return wiphy_get_by_name(netdev->manager, w->wiphy_name, ret);
+
+        return wiphy_get_by_index(netdev->manager, w->wiphy_index, ret);
+}
+
+static int wlan_is_ready_to_create(NetDev *netdev, Link *link) {
+        return wlan_get_wiphy(netdev, NULL) >= 0;
+}
+
+static int wlan_fill_message(NetDev *netdev, sd_netlink_message *m) {
+        Wiphy *wiphy;
+        WLan *w;
+        int r;
+
+        assert(netdev);
+        assert(m);
+
+        w = WLAN(netdev);
+
+        assert(w);
+
+        r = wlan_get_wiphy(netdev, &wiphy);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_append_u32(m, NL80211_ATTR_WIPHY, wiphy->index);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_append_string(m, NL80211_ATTR_IFNAME, netdev->ifname);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_append_u32(m, NL80211_ATTR_IFTYPE, w->iftype);
+        if (r < 0)
+                return r;
+
+        if (!hw_addr_is_null(&netdev->hw_addr) && netdev->hw_addr.length == ETH_ALEN) {
+                r = sd_netlink_message_append_ether_addr(m, NL80211_ATTR_MAC, &netdev->hw_addr.ether);
+                if (r < 0)
+                        return r;
+        }
+
+        if (w->wds >= 0) {
+                r = sd_netlink_message_append_u8(m, NL80211_ATTR_4ADDR, w->wds);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int wlan_create_handler(sd_netlink *genl, sd_netlink_message *m, NetDev *netdev) {
+        int r;
+
+        assert(netdev);
+        assert(netdev->state != _NETDEV_STATE_INVALID);
+
+        r = sd_netlink_message_get_errno(m);
+        if (IN_SET(r, -EEXIST, -ENFILE))
+                /* Unlike the other netdevs, the kernel may return -ENFILE. See dev_alloc_name(). */
+                log_netdev_info(netdev, "WLAN interface exists, using existing without changing its parameters.");
+        else if (r < 0) {
+                log_netdev_warning_errno(netdev, r, "WLAN interface could not be created: %m");
+                netdev_enter_failed(netdev);
+
+                return 1;
+        }
+
+        log_netdev_debug(netdev, "WLAN interface is created.");
+        return 1;
+}
+
+static int wlan_create(NetDev *netdev) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+        int r;
+
+        assert(netdev);
+        assert(netdev->manager);
+        assert(netdev->manager->genl);
+
+        r = sd_genl_message_new(netdev->manager->genl, NL80211_GENL_NAME, NL80211_CMD_NEW_INTERFACE, &m);
+        if (r < 0)
+                return log_netdev_warning_errno(netdev, r, "Failed to allocate netlink message: %m");
+
+        r = wlan_fill_message(netdev, m);
+        if (r < 0)
+                return log_netdev_warning_errno(netdev, r, "Failed to fill netlink message: %m");
+
+        r = netlink_call_async(netdev->manager->genl, NULL, m, wlan_create_handler,
+                               netdev_destroy_callback, netdev);
+        if (r < 0)
+                return log_netdev_warning_errno(netdev, r, "Failed to send netlink message: %m");
+
+        netdev_ref(netdev);
+        return 0;
+}
+
+static int wlan_verify(NetDev *netdev, const char *filename) {
+        WLan *w;
+
+        assert(netdev);
+        assert(filename);
+
+        w = WLAN(netdev);
+
+        assert(w);
+
+        if (w->iftype == NL80211_IFTYPE_UNSPECIFIED)
+                return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+                                                "%s: WLAN interface type is not specified, ignoring.",
+                                                filename);
+
+        if (w->wiphy_index == UINT32_MAX && isempty(w->wiphy_name))
+                return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+                                                "%s: physical WLAN device is not specified, ignoring.",
+                                                filename);
+
+        return 0;
+}
+
+int config_parse_wiphy(
+                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) {
+
+        WLan *w = userdata;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(userdata);
+
+        if (isempty(rvalue)) {
+                w->wiphy_name = mfree(w->wiphy_name);
+                w->wiphy_index = UINT32_MAX;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &w->wiphy_index);
+        if (r >= 0) {
+                w->wiphy_name = mfree(w->wiphy_name);
+                return 0;
+        }
+
+        r = free_and_strdup_warn(&w->wiphy_name, rvalue);
+        if (r < 0)
+                return r;
+
+        w->wiphy_index = UINT32_MAX;
+        return 0;
+}
+
+int config_parse_wlan_iftype(
+                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) {
+
+        enum nl80211_iftype t, *iftype = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                *iftype = NL80211_IFTYPE_UNSPECIFIED;
+                return 0;
+        }
+
+        t = nl80211_iftype_from_string(rvalue);
+        /* We reuse the kernel provided enum which does not contain negative value. So, the cast
+         * below is mandatory. Otherwise, the check below always passes. */
+        if ((int) t < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, t,
+                           "Failed to parse wlan interface type, ignoring assignment: %s",
+                           rvalue);
+                return 0;
+        }
+
+        *iftype = t;
+        return 0;
+}
+
+const NetDevVTable wlan_vtable = {
+        .object_size = sizeof(WLan),
+        .init = wlan_init,
+        .done = wlan_done,
+        .sections = NETDEV_COMMON_SECTIONS "VirtualWLAN\0",
+        .is_ready_to_create = wlan_is_ready_to_create,
+        .create = wlan_create,
+        .create_type = NETDEV_CREATE_INDEPENDENT,
+        .config_verify = wlan_verify,
+        .iftype = ARPHRD_ETHER,
+        .generate_mac = true,
+        .skip_netdev_kind_check = true,
+};
diff --git a/src/network/netdev/wlan.h b/src/network/netdev/wlan.h
new file mode 100644 (file)
index 0000000..bcc2dbc
--- /dev/null
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <linux/nl80211.h>
+
+#include "conf-parser.h"
+#include "netdev.h"
+
+typedef struct WLan {
+        NetDev meta;
+
+        char *wiphy_name;
+        uint32_t wiphy_index;
+        enum nl80211_iftype iftype;
+        int wds; /* tristate */
+} WLan;
+
+DEFINE_NETDEV_CAST(WLAN, WLan);
+extern const NetDevVTable wlan_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_wiphy);
+CONFIG_PARSER_PROTOTYPE(config_parse_wlan_iftype);
index 584c1c21361f8a160f17491647ee02fd844a053d..1ba273232c44512580be50c2a23e38370f770b7c 100644 (file)
@@ -246,3 +246,7 @@ RoutingAlgorithm=
 PartitionKey=
 Mode=
 IgnoreUserspaceMulticastGroups=
+[VirtualWLAN]
+PhysicalDevice=
+Type=
+WDS=