Merge pull request #22502 from yuwata/udev-net-virtual-wlan-interface

network: add virtual wlan interface support
This commit is contained in:
Yu Watanabe
2022-02-16 23:03:52 +09:00
committed by GitHub
19 changed files with 808 additions and 77 deletions

View File

@@ -192,6 +192,9 @@
<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>
@@ -2179,6 +2182,43 @@
</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>

View File

@@ -11,18 +11,26 @@ sources = files('''
netdev/bridge.h
netdev/dummy.c
netdev/dummy.h
netdev/fou-tunnel.c
netdev/fou-tunnel.h
netdev/ifb.c
netdev/ifb.h
netdev/ipoib.c
netdev/ipoib.h
netdev/ipvlan.c
netdev/ipvlan.h
netdev/l2tp-tunnel.c
netdev/l2tp-tunnel.h
netdev/macsec.c
netdev/macsec.h
netdev/macvlan.c
netdev/macvlan.h
netdev/netdev.c
netdev/netdev-util.c
netdev/netdev-util.h
netdev/netdev.c
netdev/netdev.h
netdev/netdevsim.c
netdev/netdevsim.h
netdev/nlmon.c
netdev/nlmon.h
netdev/tunnel.c
@@ -45,14 +53,8 @@ sources = files('''
netdev/vxcan.h
netdev/wireguard.c
netdev/wireguard.h
netdev/netdevsim.c
netdev/netdevsim.h
netdev/fou-tunnel.c
netdev/fou-tunnel.h
netdev/l2tp-tunnel.c
netdev/l2tp-tunnel.h
netdev/macsec.c
netdev/macsec.h
netdev/wlan.c
netdev/wlan.h
netdev/xfrm.c
netdev/xfrm.h
networkd-address-generation.c
@@ -143,6 +145,8 @@ sources = files('''
networkd-util.h
networkd-wifi.c
networkd-wifi.h
networkd-wiphy.c
networkd-wiphy.h
tc/cake.c
tc/cake.h
tc/codel.c

View 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)

View 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",
};
@@ -352,37 +355,40 @@ int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *message) {
return -EINVAL;
}
r = sd_netlink_message_enter_container(message, IFLA_LINKINFO);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not get LINKINFO: %m");
if (!NETDEV_VTABLE(netdev)->skip_netdev_kind_check) {
r = sd_netlink_message_read_string(message, IFLA_INFO_KIND, &received_kind);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not get KIND: %m");
r = sd_netlink_message_enter_container(message, IFLA_LINKINFO);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not get LINKINFO: %m");
r = sd_netlink_message_exit_container(message);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not exit container: %m");
r = sd_netlink_message_read_string(message, IFLA_INFO_KIND, &received_kind);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not get KIND: %m");
if (netdev->kind == NETDEV_KIND_TAP)
/* the kernel does not distinguish between tun and tap */
kind = "tun";
else {
kind = netdev_kind_to_string(netdev->kind);
if (!kind) {
log_netdev_error(netdev, "Could not get kind");
r = sd_netlink_message_exit_container(message);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not exit container: %m");
if (netdev->kind == NETDEV_KIND_TAP)
/* the kernel does not distinguish between tun and tap */
kind = "tun";
else {
kind = netdev_kind_to_string(netdev->kind);
if (!kind) {
log_netdev_error(netdev, "Could not get kind");
netdev_enter_failed(netdev);
return -EINVAL;
}
}
if (!streq(kind, received_kind)) {
log_netdev_error(netdev, "Received newlink with wrong KIND %s, expected %s",
received_kind, kind);
netdev_enter_failed(netdev);
return -EINVAL;
}
}
if (!streq(kind, received_kind)) {
log_netdev_error(netdev, "Received newlink with wrong KIND %s, expected %s",
received_kind, kind);
netdev_enter_failed(netdev);
return -EINVAL;
}
netdev->ifindex = ifindex;
log_netdev_debug(netdev, "netdev has index %d", netdev->ifindex);
@@ -621,22 +627,23 @@ int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t callb
return 0;
}
static bool netdev_is_ready_to_create(NetDev *netdev, Link *link) {
static int netdev_is_ready_to_create(NetDev *netdev, Link *link) {
assert(netdev);
assert(link);
if (netdev->state != NETDEV_STATE_LOADING)
return false;
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
return false;
if (link) {
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
return false;
if (netdev_get_create_type(netdev) == NETDEV_CREATE_AFTER_CONFIGURED &&
link->state != LINK_STATE_CONFIGURED)
return false;
if (netdev_get_create_type(netdev) == NETDEV_CREATE_AFTER_CONFIGURED &&
link->state != LINK_STATE_CONFIGURED)
return false;
if (link->set_link_messages > 0)
return false;
if (link->set_link_messages > 0)
return false;
}
if (NETDEV_VTABLE(netdev)->is_ready_to_create)
return NETDEV_VTABLE(netdev)->is_ready_to_create(netdev, link);
@@ -649,12 +656,13 @@ int request_process_stacked_netdev(Request *req) {
assert(req);
assert(req->link);
assert(req->type == REQUEST_TYPE_STACKED_NETDEV);
assert(req->type == REQUEST_TYPE_NETDEV_STACKED);
assert(req->netdev);
assert(req->netlink_handler);
if (!netdev_is_ready_to_create(req->netdev, req->link))
return 0;
r = netdev_is_ready_to_create(req->netdev, req->link);
if (r <= 0)
return r;
r = netdev_join(req->netdev, req->link, req->netlink_handler);
if (r < 0)
@@ -731,13 +739,13 @@ int link_request_stacked_netdev(Link *link, NetDev *netdev) {
if (netdev_get_create_type(netdev) == NETDEV_CREATE_STACKED) {
link->stacked_netdevs_created = false;
r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false,
r = link_queue_request(link, REQUEST_TYPE_NETDEV_STACKED, netdev_ref(netdev), true,
&link->create_stacked_netdev_messages,
link_create_stacked_netdev_handler,
NULL);
} else {
link->stacked_netdevs_after_configured_created = false;
r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false,
r = link_queue_request(link, REQUEST_TYPE_NETDEV_STACKED, netdev_ref(netdev), true,
&link->create_stacked_netdev_after_configured_messages,
link_create_stacked_netdev_after_configured_handler,
NULL);
@@ -750,6 +758,44 @@ int link_request_stacked_netdev(Link *link, NetDev *netdev) {
return 0;
}
int request_process_independent_netdev(Request *req) {
int r;
assert(req);
assert(req->type == REQUEST_TYPE_NETDEV_INDEPENDENT);
assert(req->netdev);
r = netdev_is_ready_to_create(req->netdev, NULL);
if (r <= 0)
return r;
r = netdev_create(req->netdev, NULL, NULL);
if (r < 0)
return r;
return 1;
}
static int netdev_request(NetDev *netdev) {
int r;
assert(netdev);
if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_MASTER, NETDEV_CREATE_INDEPENDENT) &&
!netdev_is_stacked_and_independent(netdev))
return 0;
r = netdev_is_ready_to_create(netdev, NULL);
if (r < 0)
return r;
if (r > 0)
/* If the netdev has no dependency, then create it now. */
return netdev_create(netdev, NULL, NULL);
/* Otherwise, wait for the dependencies being resolved. */
return netdev_queue_request(netdev, NULL);
}
int netdev_load_one(Manager *manager, const char *filename) {
_cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL;
const char *dropin_dirname;
@@ -860,20 +906,11 @@ int netdev_load_one(Manager *manager, const char *filename) {
log_netdev_debug(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
if (IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_MASTER, NETDEV_CREATE_INDEPENDENT)) {
r = netdev_create(netdev, NULL, NULL);
if (r < 0)
return r;
}
if (netdev_is_stacked_and_independent(netdev)) {
r = netdev_create(netdev, NULL, NULL);
if (r < 0)
return r;
}
netdev = NULL;
r = netdev_request(netdev);
if (r < 0)
return log_netdev_warning_errno(netdev, r, "Failed to request to create: %m");
TAKE_PTR(netdev);
return 0;
}

View 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() */
@@ -173,6 +175,9 @@ typedef struct NetDevVTable {
/* Generate MAC address when MACAddress= is not specified. */
bool generate_mac;
/* When assigning ifindex to the netdev, skip to check if the netdev kind matches. */
bool skip_netdev_kind_check;
} NetDevVTable;
extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
@@ -210,6 +215,7 @@ int netdev_generate_hw_addr(NetDev *netdev, Link *link, const char *name,
const struct hw_addr_data *hw_addr, struct hw_addr_data *ret);
int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t cb);
int request_process_independent_netdev(Request *req);
int request_process_stacked_netdev(Request *req);
int link_request_stacked_netdev(Link *link, NetDev *netdev);

View File

@@ -657,12 +657,14 @@ static int netdev_tunnel_is_ready_to_create(NetDev *netdev, Link *link) {
Tunnel *t;
assert(netdev);
assert(link);
t = TUNNEL(netdev);
assert(t);
if (t->independent)
return true;
return tunnel_get_local_address(t, link, NULL) >= 0;
}

View File

@@ -427,12 +427,14 @@ static int netdev_vxlan_is_ready_to_create(NetDev *netdev, Link *link) {
VxLan *v;
assert(netdev);
assert(link);
v = VXLAN(netdev);
assert(v);
if (v->independent)
return true;
return vxlan_get_local_address(v, link, NULL, NULL) >= 0;
}

260
src/network/netdev/wlan.c Normal file
View File

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

22
src/network/netdev/wlan.h Normal file
View File

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

View File

@@ -42,6 +42,7 @@
#include "networkd-speed-meter.h"
#include "networkd-state-file.h"
#include "networkd-wifi.h"
#include "networkd-wiphy.h"
#include "ordered-set.h"
#include "path-lookup.h"
#include "path-util.h"
@@ -554,6 +555,9 @@ Manager* manager_free(Manager *m) {
m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
m->wiphy_by_name = hashmap_free(m->wiphy_by_name);
m->wiphy_by_index = hashmap_free_with_destructor(m->wiphy_by_index, wiphy_free);
ordered_set_free_free(m->address_pools);
hashmap_free(m->route_table_names_by_number);
@@ -793,6 +797,20 @@ static int manager_enumerate_nexthop(Manager *m) {
return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_nexthop);
}
static int manager_enumerate_nl80211_wiphy(Manager *m) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(m);
assert(m->genl);
r = sd_genl_message_new(m->genl, NL80211_GENL_NAME, NL80211_CMD_GET_WIPHY, &req);
if (r < 0)
return r;
return manager_enumerate_internal(m, m->genl, req, manager_genl_process_nl80211_wiphy);
}
static int manager_enumerate_nl80211_config(Manager *m) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
@@ -878,6 +896,12 @@ int manager_enumerate(Manager *m) {
else if (r < 0)
return log_error_errno(r, "Could not enumerate routing policy rules: %m");
r = manager_enumerate_nl80211_wiphy(m);
if (r == -EOPNOTSUPP)
log_debug_errno(r, "Could not enumerate wireless LAN phy, ignoring: %m");
else if (r < 0)
return log_error_errno(r, "Could not enumerate wireless LAN phy: %m");
r = manager_enumerate_nl80211_config(m);
if (r == -EOPNOTSUPP)
log_debug_errno(r, "Could not enumerate wireless LAN interfaces, ignoring: %m");

View File

@@ -84,6 +84,10 @@ struct Manager {
Hashmap *route_table_numbers_by_name;
Hashmap *route_table_names_by_number;
/* Wiphy */
Hashmap *wiphy_by_index;
Hashmap *wiphy_by_name;
/* For link speed meter */
bool use_speed_meter;
sd_event_source *speed_meter_event_source;

View File

@@ -46,6 +46,10 @@ static void request_free_object(RequestType type, void *object) {
case REQUEST_TYPE_NEIGHBOR:
neighbor_free(object);
break;
case REQUEST_TYPE_NETDEV_INDEPENDENT:
case REQUEST_TYPE_NETDEV_STACKED:
netdev_unref(object);
break;
case REQUEST_TYPE_NEXTHOP:
nexthop_free(object);
break;
@@ -58,7 +62,6 @@ static void request_free_object(RequestType type, void *object) {
routing_policy_rule_free(object);
break;
case REQUEST_TYPE_SET_LINK:
case REQUEST_TYPE_STACKED_NETDEV:
break;
case REQUEST_TYPE_TRAFFIC_CONTROL:
traffic_control_free(object);
@@ -99,10 +102,12 @@ void request_drop(Request *req) {
static void request_hash_func(const Request *req, struct siphash *state) {
assert(req);
assert(req->link);
assert(state);
siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state);
siphash24_compress_boolean(req->link, state);
if (req->link)
siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state);
siphash24_compress(&req->type, sizeof(req->type), state);
switch (req->type) {
@@ -114,7 +119,8 @@ static void request_hash_func(const Request *req, struct siphash *state) {
case REQUEST_TYPE_ADDRESS_LABEL:
case REQUEST_TYPE_BRIDGE_FDB:
case REQUEST_TYPE_BRIDGE_MDB:
case REQUEST_TYPE_STACKED_NETDEV:
case REQUEST_TYPE_NETDEV_INDEPENDENT:
case REQUEST_TYPE_NETDEV_STACKED:
/* TODO: Currently, these types do not have any specific hash and compare functions.
* Fortunately, all these objects are 'static', thus we can use the trivial functions. */
trivial_hash_func(req->object, state);
@@ -163,13 +169,17 @@ static int request_compare_func(const struct Request *a, const struct Request *b
assert(a);
assert(b);
assert(a->link);
assert(b->link);
r = CMP(a->link->ifindex, b->link->ifindex);
r = CMP(!!a->link, !!b->link);
if (r != 0)
return r;
if (a->link) {
r = CMP(a->link->ifindex, b->link->ifindex);
if (r != 0)
return r;
}
r = CMP(a->type, b->type);
if (r != 0)
return r;
@@ -182,7 +192,8 @@ static int request_compare_func(const struct Request *a, const struct Request *b
case REQUEST_TYPE_ADDRESS_LABEL:
case REQUEST_TYPE_BRIDGE_FDB:
case REQUEST_TYPE_BRIDGE_MDB:
case REQUEST_TYPE_STACKED_NETDEV:
case REQUEST_TYPE_NETDEV_INDEPENDENT:
case REQUEST_TYPE_NETDEV_STACKED:
return trivial_compare_func(a->object, b->object);
case REQUEST_TYPE_DHCP_SERVER:
case REQUEST_TYPE_DHCP4_CLIENT:
@@ -220,6 +231,48 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
request_compare_func,
request_free);
int netdev_queue_request(
NetDev *netdev,
Request **ret) {
_cleanup_(request_freep) Request *req = NULL;
Request *existing;
int r;
assert(netdev);
assert(netdev->manager);
req = new(Request, 1);
if (!req)
return -ENOMEM;
*req = (Request) {
.netdev = netdev_ref(netdev),
.type = REQUEST_TYPE_NETDEV_INDEPENDENT,
.consume_object = true,
};
existing = ordered_set_get(netdev->manager->request_queue, req);
if (existing) {
/* To prevent from removing the existing request. */
req->netdev = netdev_unref(req->netdev);
if (ret)
*ret = existing;
return 0;
}
r = ordered_set_ensure_put(&netdev->manager->request_queue, &request_hash_ops, req);
if (r < 0)
return r;
if (ret)
*ret = req;
TAKE_PTR(req);
return 1;
}
int link_queue_request(
Link *link,
RequestType type,
@@ -340,6 +393,12 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
case REQUEST_TYPE_NEIGHBOR:
r = request_process_neighbor(req);
break;
case REQUEST_TYPE_NETDEV_INDEPENDENT:
r = request_process_independent_netdev(req);
break;
case REQUEST_TYPE_NETDEV_STACKED:
r = request_process_stacked_netdev(req);
break;
case REQUEST_TYPE_NEXTHOP:
r = request_process_nexthop(req);
break;
@@ -355,9 +414,6 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
case REQUEST_TYPE_SET_LINK:
r = request_process_set_link(req);
break;
case REQUEST_TYPE_STACKED_NETDEV:
r = request_process_stacked_netdev(req);
break;
case REQUEST_TYPE_TRAFFIC_CONTROL:
r = request_process_traffic_control(req);
break;
@@ -367,9 +423,10 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
default:
return -EINVAL;
}
if (r < 0)
link_enter_failed(req->link);
if (r > 0) {
if (r < 0) {
if (req->link)
link_enter_failed(req->link);
} else if (r > 0) {
ordered_set_remove(manager->request_queue, req);
request_free(req);
processed = true;

View File

@@ -28,12 +28,13 @@ typedef enum RequestType {
REQUEST_TYPE_IPV6_PROXY_NDP,
REQUEST_TYPE_NDISC,
REQUEST_TYPE_NEIGHBOR,
REQUEST_TYPE_NETDEV_INDEPENDENT,
REQUEST_TYPE_NETDEV_STACKED,
REQUEST_TYPE_NEXTHOP,
REQUEST_TYPE_RADV,
REQUEST_TYPE_ROUTE,
REQUEST_TYPE_ROUTING_POLICY_RULE,
REQUEST_TYPE_SET_LINK,
REQUEST_TYPE_STACKED_NETDEV,
REQUEST_TYPE_TRAFFIC_CONTROL,
REQUEST_TYPE_UP_DOWN,
_REQUEST_TYPE_MAX,
@@ -66,6 +67,10 @@ typedef struct Request {
void request_drop(Request *req);
int netdev_queue_request(
NetDev *netdev,
Request **ret);
int link_queue_request(
Link *link,
RequestType type,

View File

@@ -8,6 +8,7 @@
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-wifi.h"
#include "networkd-wiphy.h"
#include "string-util.h"
#include "wifi-util.h"
@@ -72,6 +73,8 @@ int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *me
log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m");
return 0;
}
if (IN_SET(cmd, NL80211_CMD_NEW_WIPHY, NL80211_CMD_DEL_WIPHY))
return manager_genl_process_nl80211_wiphy(genl, message, manager);
if (!IN_SET(cmd, NL80211_CMD_SET_INTERFACE, NL80211_CMD_NEW_INTERFACE, NL80211_CMD_DEL_INTERFACE)) {
log_debug("nl80211: ignoring nl80211 %s(%u) message.",
strna(nl80211_cmd_to_string(cmd)), cmd);

View File

@@ -0,0 +1,205 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <linux/nl80211.h>
#include "device-util.h"
#include "networkd-manager.h"
#include "networkd-wiphy.h"
#include "parse-util.h"
#include "wifi-util.h"
Wiphy *wiphy_free(Wiphy *w) {
if (!w)
return NULL;
if (w->manager) {
hashmap_remove_value(w->manager->wiphy_by_index, UINT32_TO_PTR(w->index), w);
if (w->name)
hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
}
free(w->name);
return mfree(w);
}
static int wiphy_new(Manager *manager, uint32_t index, Wiphy **ret) {
_cleanup_(wiphy_freep) Wiphy *w = NULL;
int r;
assert(manager);
w = new(Wiphy, 1);
if (!w)
return -ENOMEM;
*w = (Wiphy) {
.index = index,
};
r = hashmap_ensure_put(&manager->wiphy_by_index, NULL, UINT32_TO_PTR(w->index), w);
if (r < 0)
return r;
w->manager = manager;
if (ret)
*ret = w;
TAKE_PTR(w);
return 0;
}
int wiphy_get_by_index(Manager *manager, uint32_t index, Wiphy **ret) {
Wiphy *w;
assert(manager);
w = hashmap_get(manager->wiphy_by_index, UINT32_TO_PTR(index));
if (!w)
return -ENODEV;
if (ret)
*ret = w;
return 0;
}
int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret) {
Wiphy *w;
assert(manager);
assert(name);
w = hashmap_get(manager->wiphy_by_name, name);
if (!w)
return -ENODEV;
if (ret)
*ret = w;
return 0;
}
static int wiphy_update_name(Wiphy *w, sd_netlink_message *message) {
const char *name;
int r;
assert(w);
assert(w->manager);
assert(message);
r = sd_netlink_message_read_string(message, NL80211_ATTR_WIPHY_NAME, &name);
if (r == -ENODATA)
return 0;
if (r < 0)
return r;
if (streq_ptr(w->name, name))
return 0;
if (w->name)
hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
r = free_and_strdup(&w->name, name);
if (r < 0)
return r;
return hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
}
static int wiphy_update(Wiphy *w, sd_netlink_message *message) {
int r;
assert(w);
assert(message);
r = wiphy_update_name(w, message);
if (r < 0)
return log_wiphy_debug_errno(w, r, "Failed to update wiphy name: %m");
return 0;
}
int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *message, Manager *manager) {
const char *family;
uint32_t index;
uint8_t cmd;
Wiphy *w = NULL;
int r;
assert(genl);
assert(message);
assert(manager);
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
log_message_warning_errno(message, r, "nl80211: received error message, ignoring");
return 0;
}
r = sd_genl_message_get_family_name(genl, message, &family);
if (r < 0) {
log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m");
return 0;
}
if (!streq(family, NL80211_GENL_NAME)) {
log_debug("nl80211: Received message of unexpected genl family '%s', ignoring.", family);
return 0;
}
r = sd_genl_message_get_command(genl, message, &cmd);
if (r < 0) {
log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_u32(message, NL80211_ATTR_WIPHY, &index);
if (r < 0) {
log_debug_errno(r, "nl80211: received %s(%u) message without valid index, ignoring: %m",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 0;
}
(void) wiphy_get_by_index(manager, index, &w);
switch (cmd) {
case NL80211_CMD_NEW_WIPHY: {
bool is_new = !w;
if (!w) {
r = wiphy_new(manager, index, &w);
if (r < 0) {
log_warning_errno(r, "Failed to allocate wiphy, ignoring: %m");
return 0;
}
}
r = wiphy_update(w, message);
if (r < 0) {
log_wiphy_warning_errno(w, r, "Failed to update wiphy, ignoring: %m");
return 0;
}
log_wiphy_debug(w, "Received %s phy.", is_new ? "new" : "updated");
break;
}
case NL80211_CMD_DEL_WIPHY:
if (!w) {
log_debug("The kernel removes wiphy we do not know, ignoring: %m");
return 0;
}
log_wiphy_debug(w, "Removed.");
wiphy_free(w);
break;
default:
log_wiphy_debug(w, "nl80211: received %s(%u) message.",
strna(nl80211_cmd_to_string(cmd)), cmd);
}
return 0;
}

View File

@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <inttypes.h>
#include "sd-device.h"
#include "macro.h"
typedef struct Manager Manager;
typedef struct Wiphy {
Manager *manager;
uint32_t index;
char *name;
} Wiphy;
Wiphy *wiphy_free(Wiphy *w);
DEFINE_TRIVIAL_CLEANUP_FUNC(Wiphy*, wiphy_free);
int wiphy_get_by_index(Manager *manager, uint32_t index, Wiphy **ret);
int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret);
int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *message, Manager *manager);
#define log_wiphy_full_errno_zerook(w, level, error, ...) \
({ \
const Wiphy *_w = (w); \
log_interface_full_errno_zerook(_w ? _w->name : NULL, level, error, __VA_ARGS__); \
})
#define log_wiphy_full_errno(w, level, error, ...) \
({ \
int _error = (error); \
ASSERT_NON_ZERO(_error); \
log_wiphy_full_errno_zerook(w, level, _error, __VA_ARGS__); \
})
#define log_wiphy_full(w, level, ...) (void) log_wiphy_full_errno_zerook(w, level, 0, __VA_ARGS__)
#define log_wiphy_debug(w, ...) log_wiphy_full(w, LOG_DEBUG, __VA_ARGS__)
#define log_wiphy_info(w, ...) log_wiphy_full(w, LOG_INFO, __VA_ARGS__)
#define log_wiphy_notice(w, ...) log_wiphy_full(w, LOG_NOTICE, __VA_ARGS__)
#define log_wiphy_warning(w, ...) log_wiphy_full(w, LOG_WARNING, __VA_ARGS__)
#define log_wiphy_error(w, ...) log_wiphy_full(w, LOG_ERR, __VA_ARGS__)
#define log_wiphy_debug_errno(w, error, ...) log_wiphy_full_errno(w, LOG_DEBUG, error, __VA_ARGS__)
#define log_wiphy_info_errno(w, error, ...) log_wiphy_full_errno(w, LOG_INFO, error, __VA_ARGS__)
#define log_wiphy_notice_errno(w, error, ...) log_wiphy_full_errno(w, LOG_NOTICE, error, __VA_ARGS__)
#define log_wiphy_warning_errno(w, error, ...) log_wiphy_full_errno(w, LOG_WARNING, error, __VA_ARGS__)
#define log_wiphy_error_errno(w, error, ...) log_wiphy_full_errno(w, LOG_ERR, error, __VA_ARGS__)

View File

@@ -153,7 +153,7 @@ static const char * const nl80211_iftype_table[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_NAN] = "nan",
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(nl80211_iftype, enum nl80211_iftype);
DEFINE_STRING_TABLE_LOOKUP(nl80211_iftype, enum nl80211_iftype);
static const char * const nl80211_cmd_table[__NL80211_CMD_AFTER_LAST] = {
[NL80211_CMD_GET_WIPHY] = "get_wiphy",

View File

@@ -12,4 +12,5 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *ret_i
int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *ret_bssid);
const char *nl80211_iftype_to_string(enum nl80211_iftype iftype) _const_;
enum nl80211_iftype nl80211_iftype_from_string(const char *s) _pure_;
const char *nl80211_cmd_to_string(int cmd) _const_;

View File

@@ -246,3 +246,7 @@ RoutingAlgorithm=
PartitionKey=
Mode=
IgnoreUserspaceMulticastGroups=
[VirtualWLAN]
PhysicalDevice=
Type=
WDS=