diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:35:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:35:18 +0000 |
commit | b750101eb236130cf056c675997decbac904cc49 (patch) | |
tree | a5df1a06754bdd014cb975c051c83b01c9a97532 /src/network/netdev/wlan.c | |
parent | Initial commit. (diff) | |
download | systemd-upstream.tar.xz systemd-upstream.zip |
Adding upstream version 252.22.upstream/252.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/network/netdev/wlan.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/src/network/netdev/wlan.c b/src/network/netdev/wlan.c new file mode 100644 index 0000000..816e106 --- /dev/null +++ b/src/network/netdev/wlan.c @@ -0,0 +1,258 @@ +/* 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 = ASSERT_PTR(userdata); + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + 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 = ASSERT_PTR(data); + + assert(filename); + assert(lvalue); + assert(rvalue); + + 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 "WLAN\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, +}; |