/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #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 "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, };