diff --git a/src/basic/linux/nl80211.h b/src/basic/linux/nl80211.h index db474994fa..c2efea98e0 100644 --- a/src/basic/linux/nl80211.h +++ b/src/basic/linux/nl80211.h @@ -1185,6 +1185,21 @@ * passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used to * specify the wiphy index to be applied to. * + * @NL80211_CMD_OBSS_COLOR_COLLISION: This notification is sent out whenever + * mac80211/drv detects a bss color collision. + * + * @NL80211_CMD_COLOR_CHANGE_REQUEST: This command is used to indicate that + * userspace wants to change the BSS color. + * + * @NL80211_CMD_COLOR_CHANGE_STARTED: Notify userland, that a color change has + * started + * + * @NL80211_CMD_COLOR_CHANGE_ABORTED: Notify userland, that the color change has + * been aborted + * + * @NL80211_CMD_COLOR_CHANGE_COMPLETED: Notify userland that the color change + * has completed + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1417,6 +1432,14 @@ enum nl80211_commands { NL80211_CMD_SET_SAR_SPECS, + NL80211_CMD_OBSS_COLOR_COLLISION, + + NL80211_CMD_COLOR_CHANGE_REQUEST, + + NL80211_CMD_COLOR_CHANGE_STARTED, + NL80211_CMD_COLOR_CHANGE_ABORTED, + NL80211_CMD_COLOR_CHANGE_COMPLETED, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2560,6 +2583,16 @@ enum nl80211_commands { * disassoc events to indicate that an immediate reconnect to the AP * is desired. * + * @NL80211_ATTR_OBSS_COLOR_BITMAP: bitmap of the u64 BSS colors for the + * %NL80211_CMD_OBSS_COLOR_COLLISION event. + * + * @NL80211_ATTR_COLOR_CHANGE_COUNT: u8 attribute specifying the number of TBTT's + * until the color switch event. + * @NL80211_ATTR_COLOR_CHANGE_COLOR: u8 attribute specifying the color that we are + * switching to + * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE + * information for the time while performing a color switch. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3057,6 +3090,12 @@ enum nl80211_attrs { NL80211_ATTR_DISABLE_HE, + NL80211_ATTR_OBSS_COLOR_BITMAP, + + NL80211_ATTR_COLOR_CHANGE_COUNT, + NL80211_ATTR_COLOR_CHANGE_COLOR, + NL80211_ATTR_COLOR_CHANGE_ELEMS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -5953,6 +5992,9 @@ enum nl80211_feature_flags { * frame protection for all management frames exchanged during the * negotiation and range measurement procedure. * + * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision + * detection and change announcemnts. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -6017,6 +6059,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_SECURE_LTF, NL80211_EXT_FEATURE_SECURE_RTT, NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE, + NL80211_EXT_FEATURE_BSS_COLOR, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/src/basic/missing_network.h b/src/basic/missing_network.h index 24017df871..6e71b26afd 100644 --- a/src/basic/missing_network.h +++ b/src/basic/missing_network.h @@ -44,3 +44,8 @@ #ifndef BOND_MAX_ARP_TARGETS #define BOND_MAX_ARP_TARGETS 16 #endif + +/* Not exposed but defined in include/linux/ieee80211.h */ +#ifndef IEEE80211_MAX_SSID_LEN +#define IEEE80211_MAX_SSID_LEN 32 +#endif diff --git a/src/libsystemd/sd-netlink/netlink-genl.c b/src/libsystemd/sd-netlink/netlink-genl.c index f6f13ae239..8c907f2836 100644 --- a/src/libsystemd/sd-netlink/netlink-genl.c +++ b/src/libsystemd/sd-netlink/netlink-genl.c @@ -53,6 +53,42 @@ void genl_clear_family(sd_netlink *nl) { nl->genl_family_by_id = hashmap_free_with_destructor(nl->genl_family_by_id, genl_family_free); } +static int genl_family_new_unsupported( + sd_netlink *nl, + const char *family_name, + const NLTypeSystem *type_system) { + + _cleanup_(genl_family_freep) GenericNetlinkFamily *f = NULL; + int r; + + assert(nl); + assert(family_name); + assert(type_system); + + /* Kernel does not support the genl family? To prevent from resolving the family name again, + * let's store the family with zero id to indicate that. */ + + f = new(GenericNetlinkFamily, 1); + if (!f) + return -ENOMEM; + + *f = (GenericNetlinkFamily) { + .type_system = type_system, + }; + + f->name = strdup(family_name); + if (!f->name) + return -ENOMEM; + + r = hashmap_ensure_put(&nl->genl_family_by_name, &string_hash_ops, f->name, f); + if (r < 0) + return r; + + f->genl = nl; + TAKE_PTR(f); + return 0; +} + static int genl_family_new( sd_netlink *nl, const char *expected_family_name, @@ -79,28 +115,6 @@ static int genl_family_new( .type_system = type_system, }; - if (sd_netlink_message_is_error(message)) { - int e; - - /* Kernel does not support the genl family? To prevent from resolving the family name - * again, let's store the family with zero id to indicate that. */ - - e = sd_netlink_message_get_errno(message); - if (e >= 0) /* Huh? */ - e = -EOPNOTSUPP; - - f->name = strdup(expected_family_name); - if (!f->name) - return e; - - if (hashmap_ensure_put(&nl->genl_family_by_name, &string_hash_ops, f->name, f) < 0) - return e; - - f->genl = nl; - TAKE_PTR(f); - return e; - } - r = sd_genl_message_get_family_name(nl, message, &family_name); if (r < 0) return r; @@ -261,9 +275,10 @@ static int genl_family_get_by_name_internal( if (r < 0) return r; - r = sd_netlink_call(nl, req, 0, &reply); - if (r < 0) - return r; + if (sd_netlink_call(nl, req, 0, &reply) < 0) { + (void) genl_family_new_unsupported(nl, name, type_system); + return -EOPNOTSUPP; + } return genl_family_new(nl, name, type_system, reply, ret); } diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h index 299b7d660b..80d28258e2 100644 --- a/src/libsystemd/sd-netlink/netlink-internal.h +++ b/src/libsystemd/sd-netlink/netlink-internal.h @@ -123,8 +123,8 @@ struct sd_netlink_message { struct nlmsghdr *hdr; struct netlink_container containers[NETLINK_CONTAINER_DEPTH]; unsigned n_containers; /* number of containers */ + uint32_t multicast_group; bool sealed:1; - bool broadcast:1; sd_netlink_message *next; /* next in a chain of multi-part messages */ }; diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 2bfaff7ce0..874fffe9d9 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -116,8 +116,8 @@ int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_n int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { assert_return(m, -EINVAL); assert_return(m->hdr, -EINVAL); - - assert_return(IN_SET(m->hdr->nlmsg_type, + assert_return(m->protocol != NETLINK_ROUTE || + IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETLINKPROP, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP), -EINVAL); @@ -166,7 +166,7 @@ int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) { int sd_netlink_message_is_broadcast(sd_netlink_message *m) { assert_return(m, -EINVAL); - return m->broadcast; + return m->multicast_group != 0; } /* If successful the updated message will be correctly aligned, if @@ -751,7 +751,7 @@ int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t s } int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) { - void *attr_data, *data; + void *attr_data; int r; assert_return(m, -EINVAL); @@ -761,6 +761,8 @@ int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, siz return r; if (ret_data) { + void *data; + data = memdup(attr_data, r); if (!data) return -ENOMEM; @@ -774,9 +776,34 @@ int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, siz return r; } +int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) { + void *attr_data; + int r; + + assert_return(m, -EINVAL); + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + + if (ret_data) { + void *data; + + data = memdup_suffix0(attr_data, r); + if (!data) + return -ENOMEM; + + *ret_data = data; + } + + if (ret_size) + *ret_size = r; + + return r; +} + int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data) { void *attr_data; - char *str; int r; assert_return(m, -EINVAL); @@ -790,6 +817,8 @@ int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short return r; if (data) { + char *str; + str = strndup(attr_data, r); if (!str) return -ENOMEM; @@ -801,8 +830,8 @@ int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short } int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) { - int r; void *attr_data; + int r; assert_return(m, -EINVAL); @@ -813,7 +842,8 @@ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, c r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; - else if (strnlen(attr_data, r) >= (size_t) r) + + if (strnlen(attr_data, r) >= (size_t) r) return -EIO; if (data) @@ -823,8 +853,8 @@ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, c } int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) { - int r; void *attr_data; + int r; assert_return(m, -EINVAL); @@ -835,7 +865,8 @@ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8 r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; - else if ((size_t) r < sizeof(uint8_t)) + + if ((size_t) r < sizeof(uint8_t)) return -EIO; if (data) @@ -858,7 +889,8 @@ int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); if (r < 0) return r; - else if ((size_t) r < sizeof(uint16_t)) + + if ((size_t) r < sizeof(uint16_t)) return -EIO; if (data) { @@ -885,7 +917,8 @@ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); if (r < 0) return r; - else if ((size_t) r < sizeof(uint32_t)) + + if ((size_t) r < sizeof(uint32_t)) return -EIO; if (data) { @@ -899,8 +932,8 @@ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint } int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) { - int r; void *attr_data; + int r; assert_return(m, -EINVAL); @@ -911,7 +944,8 @@ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short typ r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; - else if ((size_t) r < sizeof(struct ether_addr)) + + if ((size_t) r < sizeof(struct ether_addr)) return -EIO; if (data) @@ -921,8 +955,8 @@ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short typ } int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, struct hw_addr_data *data) { - int r; void *attr_data; + int r; assert_return(m, -EINVAL); @@ -933,7 +967,8 @@ int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, str r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; - else if (r > HW_ADDR_MAX_SIZE) + + if (r > HW_ADDR_MAX_SIZE) return -EIO; if (data) { @@ -945,8 +980,8 @@ int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, str } int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) { - int r; void *attr_data; + int r; assert_return(m, -EINVAL); @@ -957,7 +992,8 @@ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short typ r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; - else if ((size_t) r < sizeof(struct ifa_cacheinfo)) + + if ((size_t) r < sizeof(struct ifa_cacheinfo)) return -EIO; if (info) @@ -980,7 +1016,8 @@ int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short typ r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; - else if ((size_t) r < FAMILY_ADDRESS_SIZE(family)) + + if ((size_t) r < FAMILY_ADDRESS_SIZE(family)) return -EIO; if (data) diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index 15c2789bee..718073bb8f 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -81,13 +81,11 @@ int socket_bind(sd_netlink *nl) { addrlen = sizeof(nl->sockaddr); - r = bind(nl->fd, &nl->sockaddr.sa, addrlen); /* ignore EINVAL to allow binding an already bound socket */ - if (r < 0 && errno != EINVAL) + if (bind(nl->fd, &nl->sockaddr.sa, addrlen) < 0 && errno != EINVAL) return -errno; - r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen); - if (r < 0) + if (getsockname(nl->fd, &nl->sockaddr.sa, &addrlen) < 0) return -errno; return broadcast_groups_get(nl); @@ -328,7 +326,7 @@ int socket_read_message(sd_netlink *nl) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; size_t size; - if (!group && new_msg->nlmsg_pid != nl->sockaddr.nl.nl_pid) + if (group == 0 && new_msg->nlmsg_pid != nl->sockaddr.nl.nl_pid) /* not broadcast and not for us */ continue; @@ -365,8 +363,7 @@ int socket_read_message(sd_netlink *nl) { if (r < 0) return r; - m->broadcast = !!group; - + m->multicast_group = group; m->hdr = memdup(new_msg, new_msg->nlmsg_len); if (!m->hdr) return -ENOMEM; diff --git a/src/libsystemd/sd-netlink/netlink-types-genl.c b/src/libsystemd/sd-netlink/netlink-types-genl.c index b2f768181f..bdd5700c6e 100644 --- a/src/libsystemd/sd-netlink/netlink-types-genl.c +++ b/src/libsystemd/sd-netlink/netlink-types-genl.c @@ -11,6 +11,7 @@ #include #include +#include "missing_network.h" #include "netlink-genl.h" #include "netlink-types-internal.h" @@ -179,10 +180,15 @@ static const NLType genl_macsec_types[] = { /***************** genl nl80211 type systems *****************/ static const NLType genl_nl80211_types[] = { - [NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 }, - [NL80211_ATTR_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR }, - [NL80211_ATTR_SSID] = { .type = NETLINK_TYPE_STRING }, - [NL80211_ATTR_IFTYPE] = { .type = NETLINK_TYPE_U32 }, + [NL80211_ATTR_WIPHY] = { .type = NETLINK_TYPE_U32 }, + [NL80211_ATTR_WIPHY_NAME] = { .type = NETLINK_TYPE_STRING }, + [NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 }, + [NL80211_ATTR_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 }, + [NL80211_ATTR_IFTYPE] = { .type = NETLINK_TYPE_U32 }, + [NL80211_ATTR_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN }, + [NL80211_ATTR_SSID] = { .type = NETLINK_TYPE_BINARY, .size = IEEE80211_MAX_SSID_LEN }, + [NL80211_ATTR_STATUS_CODE] = { .type = NETLINK_TYPE_U16 }, + [NL80211_ATTR_4ADDR] = { .type = NETLINK_TYPE_U8 }, }; /***************** genl wireguard type systems *****************/ diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index 5208dd94e4..13b284ee64 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -116,6 +116,16 @@ int rtnl_log_create_error(int r); userdata, description); \ }) +#define genl_add_match(nl, ret_slot, family, group, cmd, callback, destroy_callback, userdata, description) \ + ({ \ + int (*_callback_)(sd_netlink *, sd_netlink_message *, typeof(userdata)) = callback; \ + void (*_destroy_)(typeof(userdata)) = destroy_callback; \ + sd_genl_add_match(nl, ret_slot, family, group, cmd, \ + (sd_netlink_message_handler_t) _callback_, \ + (sd_netlink_destroy_t) _destroy_, \ + userdata, description); \ + }) + int netlink_message_append_hw_addr(sd_netlink_message *m, unsigned short type, const struct hw_addr_data *data); int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data); int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data); diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 2d0c940b5a..2bf26523bd 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -81,8 +81,7 @@ int sd_netlink_new_from_fd(sd_netlink **ret, int fd) { addrlen = sizeof(nl->sockaddr); - r = getsockname(fd, &nl->sockaddr.sa, &addrlen); - if (r < 0) + if (getsockname(fd, &nl->sockaddr.sa, &addrlen) < 0) return -errno; if (nl->sockaddr.nl.nl_family != AF_NETLINK) @@ -446,12 +445,22 @@ static int process_match(sd_netlink *nl, sd_netlink_message *m) { LIST_FOREACH(match_callbacks, c, nl->match_callbacks) { sd_netlink_slot *slot; + bool found = false; if (c->type != type) continue; if (c->cmd != 0 && c->cmd != cmd) continue; + for (size_t i = 0; i < c->n_groups; i++) + if (c->groups[i] == m->multicast_group) { + found = true; + break; + } + + if (!found) + continue; + slot = container_of(c, sd_netlink_slot, match_callback); r = c->callback(nl, m, slot->userdata); @@ -483,15 +492,12 @@ static int process_running(sd_netlink *nl, sd_netlink_message **ret) { if (!m) goto null_message; - if (sd_netlink_message_is_broadcast(m)) { + if (sd_netlink_message_is_broadcast(m)) r = process_match(nl, m); - if (r != 0) - goto null_message; - } else { + else r = process_reply(nl, m); - if (r != 0) - goto null_message; - } + if (r != 0) + goto null_message; if (ret) { *ret = TAKE_PTR(m); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 327815def5..43428182e6 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -53,7 +53,6 @@ #include "networkd-sriov.h" #include "networkd-state-file.h" #include "networkd-sysctl.h" -#include "networkd-wifi.h" #include "set.h" #include "socket-util.h" #include "stat-util.h" @@ -1295,28 +1294,15 @@ static int link_reconfigure_impl(Link *link, bool force) { return 1; } -static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force, bool update_wifi) { - bool link_was_lower_up; +static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force) { int r; assert(link); - link_was_lower_up = link->flags & IFF_LOWER_UP; - r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state"); if (r <= 0) return r; - if (update_wifi && link_was_lower_up && link->flags & IFF_LOWER_UP) { - /* If the interface's L1 was not up, then wifi_get_info() is already called in - * link_update_flags(). So, it is not necessary to re-call here. */ - r = wifi_get_info(link); - if (r < 0) { - link_enter_failed(link); - return 0; - } - } - r = link_reconfigure_impl(link, force); if (r < 0) { link_enter_failed(link); @@ -1327,11 +1313,11 @@ static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_messag } static int link_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false, /* update_wifi = */ false); + return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false); } static int link_force_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ true, /* update_wifi = */ false); + return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ true); } static int link_reconfigure_after_sleep_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -1339,7 +1325,7 @@ static int link_reconfigure_after_sleep_handler(sd_netlink *rtnl, sd_netlink_mes assert(link); - r = link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false, /* update_wifi = */ true); + r = link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false); if (r != 0) return r; @@ -1421,10 +1407,6 @@ static int link_initialized_and_synced(Link *link) { return r; if (!link->network) { - r = wifi_get_info(link); - if (r < 0) - return r; - r = link_get_network(link, &network); if (r == -ENOENT) { link_set_state(link, LINK_STATE_UNMANAGED); @@ -1893,7 +1875,7 @@ void link_update_operstate(Link *link, bool also_update_master) { : "") static int link_update_flags(Link *link, sd_netlink_message *message) { - bool link_was_lower_up, link_was_admin_up, had_carrier; + bool link_was_admin_up, had_carrier; uint8_t operstate; unsigned flags; int r; @@ -1955,7 +1937,6 @@ static int link_update_flags(Link *link, sd_netlink_message *message) { log_link_debug(link, "Unknown link flags lost, ignoring: %#.5x", unknown_flags_removed); } - link_was_lower_up = link->flags & IFF_LOWER_UP; link_was_admin_up = link->flags & IFF_UP; had_carrier = link_has_carrier(link); @@ -1964,19 +1945,6 @@ static int link_update_flags(Link *link, sd_netlink_message *message) { link_update_operstate(link, true); - if (!link_was_lower_up && (link->flags & IFF_LOWER_UP)) { - r = wifi_get_info(link); - if (r < 0) - return r; - if (r > 0) { - /* All link information is up-to-date. So, it is not necessary to call - * RTM_GETLINK netlink method again. */ - r = link_reconfigure_impl(link, /* force = */ false); - if (r < 0) - return r; - } - } - if (!link_was_admin_up && (link->flags & IFF_UP)) { log_link_info(link, "Link UP"); @@ -1994,6 +1962,15 @@ static int link_update_flags(Link *link, sd_netlink_message *message) { if (!had_carrier && link_has_carrier(link)) { log_link_info(link, "Gained carrier"); + if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) { + /* At this stage, both wlan and link information should be up-to-date. Hence, + * it is not necessary to call RTM_GETLINK, NL80211_CMD_GET_INTERFACE, or + * NL80211_CMD_GET_STATION commands, and simply call link_reconfigure_impl(). */ + r = link_reconfigure_impl(link, /* force = */ false); + if (r < 0) + return r; + } + r = link_carrier_gained(link); if (r < 0) return r; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 9fb12a07f8..f9cfb64512 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "sd-daemon.h" #include "sd-netlink.h" @@ -38,6 +39,7 @@ #include "networkd-routing-policy-rule.h" #include "networkd-speed-meter.h" #include "networkd-state-file.h" +#include "networkd-wifi.h" #include "ordered-set.h" #include "path-lookup.h" #include "path-util.h" @@ -109,13 +111,11 @@ static int on_connected(sd_bus_message *message, void *userdata, sd_bus_error *r return 0; } -int manager_connect_bus(Manager *m) { +static int manager_connect_bus(Manager *m) { int r; assert(m); - - if (m->bus) - return 0; + assert(!m->bus); r = bus_open_system_watch_bind_with_description(&m->bus, "bus-api-network"); if (r < 0) @@ -248,6 +248,16 @@ static int manager_connect_genl(Manager *m) { if (r < 0) return r; + r = genl_add_match(m->genl, NULL, NL80211_GENL_NAME, NL80211_MULTICAST_GROUP_CONFIG, 0, + &manager_genl_process_nl80211_config, NULL, m, "network-genl_process_nl80211_config"); + if (r < 0 && r != -EOPNOTSUPP) + return r; + + r = genl_add_match(m->genl, NULL, NL80211_GENL_NAME, NL80211_MULTICAST_GROUP_MLME, 0, + &manager_genl_process_nl80211_mlme, NULL, m, "network-genl_process_nl80211_mlme"); + if (r < 0 && r != -EOPNOTSUPP) + return r; + return 0; } @@ -372,28 +382,10 @@ static int signal_restart_callback(sd_event_source *s, const struct signalfd_sig return sd_event_exit(sd_event_source_get_event(s), 0); } -int manager_new(Manager **ret) { - _cleanup_(manager_freep) Manager *m = NULL; +int manager_setup(Manager *m, bool test_mode) { int r; - m = new(Manager, 1); - if (!m) - return -ENOMEM; - - *m = (Manager) { - .speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL, - .online_state = _LINK_ONLINE_STATE_INVALID, - .manage_foreign_routes = true, - .manage_foreign_rules = true, - .ethtool_fd = -1, - .dhcp_duid.type = DUID_TYPE_EN, - .dhcp6_duid.type = DUID_TYPE_EN, - .duid_product_uuid.type = DUID_TYPE_UUID, - }; - - m->state_file = strdup("/run/systemd/netif/state"); - if (!m->state_file) - return -ENOMEM; + assert(m); r = sd_event_default(&m->event); if (r < 0) @@ -422,6 +414,13 @@ int manager_new(Manager **ret) { if (r < 0) return r; + if (test_mode) + return 0; + + r = manager_connect_bus(m); + if (r < 0) + return r; + r = manager_connect_udev(m); if (r < 0) return r; @@ -438,11 +437,35 @@ int manager_new(Manager **ret) { if (r < 0) return r; - *ret = TAKE_PTR(m); + m->state_file = strdup("/run/systemd/netif/state"); + if (!m->state_file) + return -ENOMEM; return 0; } +int manager_new(Manager **ret) { + _cleanup_(manager_freep) Manager *m = NULL; + + m = new(Manager, 1); + if (!m) + return -ENOMEM; + + *m = (Manager) { + .speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL, + .online_state = _LINK_ONLINE_STATE_INVALID, + .manage_foreign_routes = true, + .manage_foreign_rules = true, + .ethtool_fd = -1, + .dhcp_duid.type = DUID_TYPE_EN, + .dhcp6_duid.type = DUID_TYPE_EN, + .duid_product_uuid.type = DUID_TYPE_UUID, + }; + + *ret = TAKE_PTR(m); + return 0; +} + Manager* manager_free(Manager *m) { Link *link; @@ -556,15 +579,15 @@ bool manager_should_reload(Manager *m) { static int manager_enumerate_internal( Manager *m, + sd_netlink *nl, sd_netlink_message *req, - int (*process)(sd_netlink *, sd_netlink_message *, Manager *), - const char *name) { + int (*process)(sd_netlink *, sd_netlink_message *, Manager *)) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *reply = NULL; - int r; + int k, r; assert(m); - assert(m->rtnl); + assert(nl); assert(req); assert(process); @@ -572,22 +595,14 @@ static int manager_enumerate_internal( if (r < 0) return r; - r = sd_netlink_call(m->rtnl, req, 0, &reply); - if (r < 0) { - if (name && (r == -EOPNOTSUPP || (r == -EINVAL && mac_selinux_enforcing()))) { - log_debug_errno(r, "%s are not supported by the kernel. Ignoring.", name); - return 0; - } - + r = sd_netlink_call(nl, req, 0, &reply); + if (r < 0) return r; - } for (sd_netlink_message *reply_one = reply; reply_one; reply_one = sd_netlink_message_next(reply_one)) { - int k; - m->enumerating = true; - k = process(m->rtnl, reply_one, m); + k = process(nl, reply_one, m); if (k < 0 && r >= 0) r = k; @@ -608,7 +623,7 @@ static int manager_enumerate_links(Manager *m) { if (r < 0) return r; - return manager_enumerate_internal(m, req, manager_rtnl_process_link, NULL); + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_link); } static int manager_enumerate_addresses(Manager *m) { @@ -622,7 +637,7 @@ static int manager_enumerate_addresses(Manager *m) { if (r < 0) return r; - return manager_enumerate_internal(m, req, manager_rtnl_process_address, NULL); + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_address); } static int manager_enumerate_neighbors(Manager *m) { @@ -636,7 +651,7 @@ static int manager_enumerate_neighbors(Manager *m) { if (r < 0) return r; - return manager_enumerate_internal(m, req, manager_rtnl_process_neighbor, NULL); + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_neighbor); } static int manager_enumerate_routes(Manager *m) { @@ -653,7 +668,7 @@ static int manager_enumerate_routes(Manager *m) { if (r < 0) return r; - return manager_enumerate_internal(m, req, manager_rtnl_process_route, NULL); + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_route); } static int manager_enumerate_rules(Manager *m) { @@ -670,7 +685,7 @@ static int manager_enumerate_rules(Manager *m) { if (r < 0) return r; - return manager_enumerate_internal(m, req, manager_rtnl_process_rule, "Routing policy rules"); + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_rule); } static int manager_enumerate_nexthop(Manager *m) { @@ -684,7 +699,50 @@ static int manager_enumerate_nexthop(Manager *m) { if (r < 0) return r; - return manager_enumerate_internal(m, req, manager_rtnl_process_nexthop, "Nexthop rules"); + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_nexthop); +} + +static int manager_enumerate_nl80211_config(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_INTERFACE, &req); + if (r < 0) + return r; + + return manager_enumerate_internal(m, m->genl, req, manager_genl_process_nl80211_config); +} + +static int manager_enumerate_nl80211_mlme(Manager *m) { + Link *link; + int r; + + assert(m); + assert(m->genl); + + HASHMAP_FOREACH(link, m->links_by_index) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + + if (link->wlan_iftype != NL80211_IFTYPE_STATION) + continue; + + r = sd_genl_message_new(m->genl, NL80211_GENL_NAME, NL80211_CMD_GET_STATION, &req); + if (r < 0) + return r; + + r = sd_netlink_message_append_u32(req, NL80211_ATTR_IFINDEX, link->ifindex); + if (r < 0) + return r; + + r = manager_enumerate_internal(m, m->genl, req, manager_genl_process_nl80211_mlme); + if (r < 0) + return r; + } + + return 0; } int manager_enumerate(Manager *m) { @@ -702,18 +760,37 @@ int manager_enumerate(Manager *m) { if (r < 0) return log_error_errno(r, "Could not enumerate neighbors: %m"); + /* NextHop support is added in kernel v5.3 (65ee00a9409f751188a8cdc0988167858eb4a536), + * and older kernels return -EOPNOTSUPP, or -EINVAL if SELinux is enabled. */ r = manager_enumerate_nexthop(m); - if (r < 0) - return log_error_errno(r, "Could not enumerate nexthop rules: %m"); + if (r == -EOPNOTSUPP || (r == -EINVAL && mac_selinux_enforcing())) + log_debug_errno(r, "Could not enumerate nexthops, ignoring: %m"); + else if (r < 0) + return log_error_errno(r, "Could not enumerate nexthops: %m"); r = manager_enumerate_routes(m); if (r < 0) return log_error_errno(r, "Could not enumerate routes: %m"); + /* If kernel is built with CONFIG_FIB_RULES=n, it returns -EOPNOTSUPP. */ r = manager_enumerate_rules(m); - if (r < 0) + if (r == -EOPNOTSUPP) + log_debug_errno(r, "Could not enumerate routing policy rules, ignoring: %m"); + else if (r < 0) return log_error_errno(r, "Could not enumerate routing policy rules: %m"); + r = manager_enumerate_nl80211_config(m); + if (r == -EOPNOTSUPP) + log_debug_errno(r, "Could not enumerate wireless LAN interfaces, ignoring: %m"); + else if (r < 0) + return log_error_errno(r, "Could not enumerate wireless LAN interfaces: %m"); + + r = manager_enumerate_nl80211_mlme(m); + if (r == -EOPNOTSUPP) + log_debug_errno(r, "Could not enumerate wireless LAN stations, ignoring: %m"); + else if (r < 0) + return log_error_errno(r, "Could not enumerate wireless LAN stations: %m"); + return 0; } diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index c80095544a..9fce86bb04 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -99,7 +99,7 @@ struct Manager { int manager_new(Manager **ret); Manager* manager_free(Manager *m); -int manager_connect_bus(Manager *m); +int manager_setup(Manager *m, bool test_mode); int manager_start(Manager *m); int manager_load_config(Manager *m); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 6168446be4..29bb5b3b73 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -49,7 +49,7 @@ Match.PermanentMACAddress, config_parse_hwaddrs, Match.Path, config_parse_match_strv, 0, offsetof(Network, match.path) Match.Driver, config_parse_match_strv, 0, offsetof(Network, match.driver) Match.Type, config_parse_match_strv, 0, offsetof(Network, match.iftype) -Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match.wifi_iftype) +Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match.wlan_iftype) Match.SSID, config_parse_match_strv, 0, offsetof(Network, match.ssid) Match.BSSID, config_parse_hwaddrs, 0, offsetof(Network, match.bssid) Match.Name, config_parse_match_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(Network, match.ifname) diff --git a/src/network/networkd-wifi.c b/src/network/networkd-wifi.c index 37700afb81..1475218286 100644 --- a/src/network/networkd-wifi.c +++ b/src/network/networkd-wifi.c @@ -3,11 +3,7 @@ #include #include -#include "sd-bus.h" - -#include "bus-util.h" #include "ether-addr-util.h" -#include "netlink-internal.h" #include "netlink-util.h" #include "networkd-link.h" #include "networkd-manager.h" @@ -15,56 +11,275 @@ #include "string-util.h" #include "wifi-util.h" -int wifi_get_info(Link *link) { - _cleanup_free_ char *ssid = NULL; - enum nl80211_iftype iftype; - bool updated = false; - const char *type; +static int link_get_wlan_interface(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; int r; assert(link); - if (!link->sd_device) - return 0; - - r = sd_device_get_devtype(link->sd_device, &type); - if (r == -ENOENT) - return 0; - else if (r < 0) - return r; - - if (!streq(type, "wlan")) - return 0; - - r = wifi_get_interface(link->manager->genl, link->ifindex, &iftype, &ssid); + r = sd_genl_message_new(link->manager->genl, NL80211_GENL_NAME, NL80211_CMD_GET_INTERFACE, &req); if (r < 0) - return r; - if (r == 0) - iftype = link->wlan_iftype; /* Assume iftype is not changed. */ + return log_link_debug_errno(link, r, "Failed to create generic netlink message: %m"); - if (iftype == NL80211_IFTYPE_STATION) { - struct ether_addr bssid; + r = sd_netlink_message_append_u32(req, NL80211_ATTR_IFINDEX, link->ifindex); + if (r < 0) + return log_link_debug_errno(link, r, "Could not append NL80211_ATTR_IFINDEX attribute: %m"); - r = wifi_get_station(link->manager->genl, link->ifindex, &bssid); - if (r < 0) - return r; - - updated = !ether_addr_equal(&link->bssid, &bssid); - link->bssid = bssid; + r = sd_netlink_call(link->manager->genl, req, 0, &reply); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to request information about wlan interface: %m"); + if (!reply) { + log_link_debug(link, "No reply received to request for information about wifi interface, ignoring."); + return 0; } - updated = updated || link->wlan_iftype != iftype; - link->wlan_iftype = iftype; - updated = updated || !streq_ptr(link->ssid, ssid); - free_and_replace(link->ssid, ssid); + return manager_genl_process_nl80211_config(link->manager->genl, reply, link->manager); +} + +int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *message, Manager *manager) { + _cleanup_free_ char *ssid = NULL; + uint32_t ifindex, wlan_iftype; + const char *family, *ifname; + uint8_t cmd; + size_t len; + Link *link; + 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; + } + 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); + return 0; + } + + r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFINDEX, &ifindex); + if (r < 0) { + log_debug_errno(r, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + r = link_get_by_index(manager, ifindex, &link); + if (r < 0) { + log_debug_errno(r, "nl80211: received %s(%u) message for link '%"PRIu32"' we don't know about, ignoring.", + strna(nl80211_cmd_to_string(cmd)), cmd, ifindex); + return 0; + } + + r = sd_netlink_message_read_string(message, NL80211_ATTR_IFNAME, &ifname); + if (r < 0) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid interface name, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + if (!streq(ifname, link->ifname)) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message with invalid interface name '%s', ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd, ifname); + return 0; + } + + r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFTYPE, &wlan_iftype); + if (r < 0) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid wlan interface type, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + r = sd_netlink_message_read_data_suffix0(message, NL80211_ATTR_SSID, &len, (void**) &ssid); + if (r < 0 && r != -ENODATA) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid SSID, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + if (r >= 0) { + if (len == 0) { + log_link_debug(link, "nl80211: received SSID has zero length, ignoring the received SSID: %m"); + ssid = mfree(ssid); + } else if (strlen_ptr(ssid) != len) { + log_link_debug(link, "nl80211: received SSID contains NUL character(s), ignoring the received SSID."); + ssid = mfree(ssid); + } + } + + log_link_debug(link, "nl80211: received %s(%u) message: iftype=%s, ssid=%s", + strna(nl80211_cmd_to_string(cmd)), cmd, + strna(nl80211_iftype_to_string(wlan_iftype)), ssid); + + switch(cmd) { + case NL80211_CMD_SET_INTERFACE: + case NL80211_CMD_NEW_INTERFACE: + link->wlan_iftype = wlan_iftype; + free_and_replace(link->ssid, ssid); + break; + + case NL80211_CMD_DEL_INTERFACE: + link->wlan_iftype = NL80211_IFTYPE_UNSPECIFIED; + link->ssid = mfree(link->ssid); + break; + + default: + assert_not_reached(); + } + + return 0; +} + +int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *message, Manager *manager) { + const char *family; + uint32_t ifindex; + uint8_t cmd; + Link *link; + 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_IFINDEX, &ifindex); + if (r < 0) { + log_debug_errno(r, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + r = link_get_by_index(manager, ifindex, &link); + if (r < 0) { + log_debug_errno(r, "nl80211: received %s(%u) message for link '%"PRIu32"' we don't know about, ignoring.", + strna(nl80211_cmd_to_string(cmd)), cmd, ifindex); + return 0; + } + + switch(cmd) { + case NL80211_CMD_NEW_STATION: + case NL80211_CMD_DEL_STATION: { + struct ether_addr bssid; + + r = sd_netlink_message_read_ether_addr(message, NL80211_ATTR_MAC, &bssid); + if (r < 0) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid BSSID, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + log_link_debug(link, "nl80211: received %s(%u) message: bssid=%s", + strna(nl80211_cmd_to_string(cmd)), cmd, ETHER_ADDR_TO_STR(&bssid)); + + if (cmd == NL80211_CMD_DEL_STATION) { + link->bssid = ETHER_ADDR_NULL; + return 0; + } + + link->bssid = bssid; + + if (manager->enumerating && + link->wlan_iftype == NL80211_IFTYPE_STATION && link->ssid) + log_link_info(link, "Connected WiFi access point: %s (%s)", + link->ssid, ETHER_ADDR_TO_STR(&link->bssid)); + break; + } + case NL80211_CMD_CONNECT: { + struct ether_addr bssid; + uint16_t status_code; + + r = sd_netlink_message_read_ether_addr(message, NL80211_ATTR_MAC, &bssid); + if (r < 0 && r != -ENODATA) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid BSSID, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + r = sd_netlink_message_read_u16(message, NL80211_ATTR_STATUS_CODE, &status_code); + if (r < 0) { + log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid status code, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + log_link_debug(link, "nl80211: received %s(%u) message: status=%u, bssid=%s", + strna(nl80211_cmd_to_string(cmd)), cmd, status_code, ETHER_ADDR_TO_STR(&bssid)); + + if (status_code != 0) + return 0; + + link->bssid = bssid; + + if (!manager->enumerating) { + r = link_get_wlan_interface(link); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to update wireless LAN interface: %m"); + link_enter_failed(link); + return 0; + } + } - if (updated) { if (link->wlan_iftype == NL80211_IFTYPE_STATION && link->ssid) log_link_info(link, "Connected WiFi access point: %s (%s)", link->ssid, ETHER_ADDR_TO_STR(&link->bssid)); + break; + } + case NL80211_CMD_DISCONNECT: + log_link_debug(link, "nl80211: received %s(%u) message.", + strna(nl80211_cmd_to_string(cmd)), cmd); - return 1; /* Some information is updated. */ + link->bssid = ETHER_ADDR_NULL; + link->ssid = mfree(link->ssid); + break; + + default: + log_link_debug(link, "nl80211: received %s(%u) message.", + strna(nl80211_cmd_to_string(cmd)), cmd); } - return 0; /* No new information. */ + return 0; } diff --git a/src/network/networkd-wifi.h b/src/network/networkd-wifi.h index ab868eba27..a4ca21e42d 100644 --- a/src/network/networkd-wifi.h +++ b/src/network/networkd-wifi.h @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include "sd-bus.h" +#include "sd-netlink.h" -typedef struct Link Link; +typedef struct Manager Manager; -int wifi_get_info(Link *link); +int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *message, Manager *manager); +int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *message, Manager *manager); diff --git a/src/network/networkd.c b/src/network/networkd.c index ff3380c82c..b79cac1418 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -77,9 +77,9 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Could not create manager: %m"); - r = manager_connect_bus(m); + r = manager_setup(m, /* test_mode = */ false); if (r < 0) - return log_error_errno(r, "Could not connect to bus: %m"); + return log_error_errno(r, "Could not setup manager: %m"); r = manager_parse_config_file(m); if (r < 0) diff --git a/src/network/test-network.c b/src/network/test-network.c index d04a7e35e8..5498d9ef04 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -270,6 +270,7 @@ int main(void) { test_dhcp_hostname_shorten_overlong(); assert_se(manager_new(&manager) >= 0); + assert_se(manager_setup(manager, /* test_mode = */ true) >= 0); test_route_tables(manager); diff --git a/src/shared/net-condition.c b/src/shared/net-condition.c index 4742f37447..1da60810ee 100644 --- a/src/shared/net-condition.c +++ b/src/shared/net-condition.c @@ -10,6 +10,7 @@ #include "socket-util.h" #include "string-table.h" #include "strv.h" +#include "wifi-util.h" void net_match_clear(NetMatch *match) { if (!match) @@ -22,7 +23,7 @@ void net_match_clear(NetMatch *match) { match->iftype = strv_free(match->iftype); match->ifname = strv_free(match->ifname); match->property = strv_free(match->property); - match->wifi_iftype = strv_free(match->wifi_iftype); + match->wlan_iftype = strv_free(match->wlan_iftype); match->ssid = strv_free(match->ssid); match->bssid = set_free_free(match->bssid); } @@ -38,7 +39,7 @@ bool net_match_is_empty(const NetMatch *match) { strv_isempty(match->iftype) && strv_isempty(match->ifname) && strv_isempty(match->property) && - strv_isempty(match->wifi_iftype) && + strv_isempty(match->wlan_iftype) && strv_isempty(match->ssid) && set_isempty(match->bssid); } @@ -117,23 +118,6 @@ static int net_condition_test_property(char * const *match_property, sd_device * return true; } -static const char *const wifi_iftype_table[NL80211_IFTYPE_MAX+1] = { - [NL80211_IFTYPE_ADHOC] = "ad-hoc", - [NL80211_IFTYPE_STATION] = "station", - [NL80211_IFTYPE_AP] = "ap", - [NL80211_IFTYPE_AP_VLAN] = "ap-vlan", - [NL80211_IFTYPE_WDS] = "wds", - [NL80211_IFTYPE_MONITOR] = "monitor", - [NL80211_IFTYPE_MESH_POINT] = "mesh-point", - [NL80211_IFTYPE_P2P_CLIENT] = "p2p-client", - [NL80211_IFTYPE_P2P_GO] = "p2p-go", - [NL80211_IFTYPE_P2P_DEVICE] = "p2p-device", - [NL80211_IFTYPE_OCB] = "ocb", - [NL80211_IFTYPE_NAN] = "nan", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(wifi_iftype, enum nl80211_iftype); - int net_match_config( const NetMatch *match, sd_device *device, @@ -143,7 +127,7 @@ int net_match_config( unsigned short iftype, const char *ifname, char * const *alternative_names, - enum nl80211_iftype wifi_iftype, + enum nl80211_iftype wlan_iftype, const char *ssid, const struct ether_addr *bssid) { @@ -194,7 +178,7 @@ int net_match_config( if (!net_condition_test_property(match->property, device)) return false; - if (!net_condition_test_strv(match->wifi_iftype, wifi_iftype_to_string(wifi_iftype))) + if (!net_condition_test_strv(match->wlan_iftype, nl80211_iftype_to_string(wlan_iftype))) return false; if (!net_condition_test_strv(match->ssid, ssid)) diff --git a/src/shared/net-condition.h b/src/shared/net-condition.h index d61537e5e3..4f8e30a271 100644 --- a/src/shared/net-condition.h +++ b/src/shared/net-condition.h @@ -18,7 +18,7 @@ typedef struct NetMatch { char **iftype; char **ifname; char **property; - char **wifi_iftype; + char **wlan_iftype; char **ssid; Set *bssid; } NetMatch; @@ -35,7 +35,7 @@ int net_match_config( unsigned short iftype, const char *ifname, char * const *alternative_names, - enum nl80211_iftype wifi_iftype, + enum nl80211_iftype wlan_iftype, const char *ssid, const struct ether_addr *bssid); diff --git a/src/shared/wifi-util.c b/src/shared/wifi-util.c index 5891208076..cf0f562550 100644 --- a/src/shared/wifi-util.c +++ b/src/shared/wifi-util.c @@ -1,12 +1,16 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "log.h" +#include "string-table.h" #include "string-util.h" #include "wifi-util.h" -int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftype, char **ssid) { +int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *ret_iftype, char **ret_ssid) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL; + _cleanup_free_ char *ssid = NULL; const char *family; + uint32_t iftype; + size_t len; int r; assert(genl); @@ -47,41 +51,47 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp goto nodata; } - if (iftype) { - uint32_t t; + r = sd_netlink_message_read_u32(reply, NL80211_ATTR_IFTYPE, &iftype); + if (r < 0) + return log_debug_errno(r, "Failed to get NL80211_ATTR_IFTYPE attribute: %m"); - r = sd_netlink_message_read_u32(reply, NL80211_ATTR_IFTYPE, &t); - if (r < 0) - return log_debug_errno(r, "Failed to get NL80211_ATTR_IFTYPE attribute: %m"); - *iftype = t; + r = sd_netlink_message_read_data_suffix0(reply, NL80211_ATTR_SSID, &len, (void**) &ssid); + if (r < 0 && r != -ENODATA) + return log_debug_errno(r, "Failed to get NL80211_ATTR_SSID attribute: %m"); + if (r >= 0) { + if (len == 0) { + log_debug("SSID has zero length, ignoring the received SSID."); + ssid = mfree(ssid); + } else if (strlen_ptr(ssid) != len) { + log_debug("SSID contains NUL character(s), ignoring the received SSID."); + ssid = mfree(ssid); + } } - if (ssid) { - r = sd_netlink_message_read_string_strdup(reply, NL80211_ATTR_SSID, ssid); - if (r == -ENODATA) - *ssid = NULL; - else if (r < 0) - return log_debug_errno(r, "Failed to get NL80211_ATTR_SSID attribute: %m"); - } + if (ret_iftype) + *ret_iftype = iftype; + + if (ret_ssid) + *ret_ssid = TAKE_PTR(ssid); return 1; nodata: - if (iftype) - *iftype = 0; - if (ssid) - *ssid = NULL; + if (ret_iftype) + *ret_iftype = 0; + if (ret_ssid) + *ret_ssid = NULL; return 0; } -int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) { +int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *ret_bssid) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL; const char *family; int r; assert(genl); assert(ifindex > 0); - assert(bssid); + assert(ret_bssid); r = sd_genl_message_new(genl, NL80211_GENL_NAME, NL80211_CMD_GET_STATION, &m); if (r < 0) @@ -115,7 +125,7 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) { goto nodata; } - r = sd_netlink_message_read_ether_addr(reply, NL80211_ATTR_MAC, bssid); + r = sd_netlink_message_read_ether_addr(reply, NL80211_ATTR_MAC, ret_bssid); if (r == -ENODATA) goto nodata; if (r < 0) @@ -124,6 +134,173 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) { return 1; nodata: - *bssid = (struct ether_addr) {}; + *ret_bssid = ETHER_ADDR_NULL; return 0; } + +static const char * const nl80211_iftype_table[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_ADHOC] = "ad-hoc", + [NL80211_IFTYPE_STATION] = "station", + [NL80211_IFTYPE_AP] = "ap", + [NL80211_IFTYPE_AP_VLAN] = "ap-vlan", + [NL80211_IFTYPE_WDS] = "wds", + [NL80211_IFTYPE_MONITOR] = "monitor", + [NL80211_IFTYPE_MESH_POINT] = "mesh-point", + [NL80211_IFTYPE_P2P_CLIENT] = "p2p-client", + [NL80211_IFTYPE_P2P_GO] = "p2p-go", + [NL80211_IFTYPE_P2P_DEVICE] = "p2p-device", + [NL80211_IFTYPE_OCB] = "ocb", + [NL80211_IFTYPE_NAN] = "nan", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(nl80211_iftype, enum nl80211_iftype); + +static const char * const nl80211_cmd_table[__NL80211_CMD_AFTER_LAST] = { + [NL80211_CMD_GET_WIPHY] = "get_wiphy", + [NL80211_CMD_SET_WIPHY] = "set_wiphy", + [NL80211_CMD_NEW_WIPHY] = "new_wiphy", + [NL80211_CMD_DEL_WIPHY] = "del_wiphy", + [NL80211_CMD_GET_INTERFACE] = "get_interface", + [NL80211_CMD_SET_INTERFACE] = "set_interface", + [NL80211_CMD_NEW_INTERFACE] = "new_interface", + [NL80211_CMD_DEL_INTERFACE] = "del_interface", + [NL80211_CMD_GET_KEY] = "get_key", + [NL80211_CMD_SET_KEY] = "set_key", + [NL80211_CMD_NEW_KEY] = "new_key", + [NL80211_CMD_DEL_KEY] = "del_key", + [NL80211_CMD_GET_BEACON] = "get_beacon", + [NL80211_CMD_SET_BEACON] = "set_beacon", + [NL80211_CMD_START_AP] = "start_ap", + [NL80211_CMD_STOP_AP] = "stop_ap", + [NL80211_CMD_GET_STATION] = "get_station", + [NL80211_CMD_SET_STATION] = "set_station", + [NL80211_CMD_NEW_STATION] = "new_station", + [NL80211_CMD_DEL_STATION] = "del_station", + [NL80211_CMD_GET_MPATH] = "get_mpath", + [NL80211_CMD_SET_MPATH] = "set_mpath", + [NL80211_CMD_NEW_MPATH] = "new_mpath", + [NL80211_CMD_DEL_MPATH] = "del_mpath", + [NL80211_CMD_SET_BSS] = "set_bss", + [NL80211_CMD_SET_REG] = "set_reg", + [NL80211_CMD_REQ_SET_REG] = "req_set_reg", + [NL80211_CMD_GET_MESH_CONFIG] = "get_mesh_config", + [NL80211_CMD_SET_MESH_CONFIG] = "set_mesh_config", + [NL80211_CMD_SET_MGMT_EXTRA_IE] = "set_mgmt_extra_ie", + [NL80211_CMD_GET_REG] = "get_reg", + [NL80211_CMD_GET_SCAN] = "get_scan", + [NL80211_CMD_TRIGGER_SCAN] = "trigger_scan", + [NL80211_CMD_NEW_SCAN_RESULTS] = "new_scan_results", + [NL80211_CMD_SCAN_ABORTED] = "scan_aborted", + [NL80211_CMD_REG_CHANGE] = "reg_change", + [NL80211_CMD_AUTHENTICATE] = "authenticate", + [NL80211_CMD_ASSOCIATE] = "associate", + [NL80211_CMD_DEAUTHENTICATE] = "deauthenticate", + [NL80211_CMD_DISASSOCIATE] = "disassociate", + [NL80211_CMD_MICHAEL_MIC_FAILURE] = "michael_mic_failure", + [NL80211_CMD_REG_BEACON_HINT] = "reg_beacon_hint", + [NL80211_CMD_JOIN_IBSS] = "join_ibss", + [NL80211_CMD_LEAVE_IBSS] = "leave_ibss", + [NL80211_CMD_TESTMODE] = "testmode", + [NL80211_CMD_CONNECT] = "connect", + [NL80211_CMD_ROAM] = "roam", + [NL80211_CMD_DISCONNECT] = "disconnect", + [NL80211_CMD_SET_WIPHY_NETNS] = "set_wiphy_netns", + [NL80211_CMD_GET_SURVEY] = "get_survey", + [NL80211_CMD_NEW_SURVEY_RESULTS] = "new_survey_results", + [NL80211_CMD_SET_PMKSA] = "set_pmksa", + [NL80211_CMD_DEL_PMKSA] = "del_pmksa", + [NL80211_CMD_FLUSH_PMKSA] = "flush_pmksa", + [NL80211_CMD_REMAIN_ON_CHANNEL] = "remain_on_channel", + [NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL] = "cancel_remain_on_channel", + [NL80211_CMD_SET_TX_BITRATE_MASK] = "set_tx_bitrate_mask", + [NL80211_CMD_REGISTER_FRAME] = "register_frame", + [NL80211_CMD_FRAME] = "frame", + [NL80211_CMD_FRAME_TX_STATUS] = "frame_tx_status", + [NL80211_CMD_SET_POWER_SAVE] = "set_power_save", + [NL80211_CMD_GET_POWER_SAVE] = "get_power_save", + [NL80211_CMD_SET_CQM] = "set_cqm", + [NL80211_CMD_NOTIFY_CQM] = "notify_cqm", + [NL80211_CMD_SET_CHANNEL] = "set_channel", + [NL80211_CMD_SET_WDS_PEER] = "set_wds_peer", + [NL80211_CMD_FRAME_WAIT_CANCEL] = "frame_wait_cancel", + [NL80211_CMD_JOIN_MESH] = "join_mesh", + [NL80211_CMD_LEAVE_MESH] = "leave_mesh", + [NL80211_CMD_UNPROT_DEAUTHENTICATE] = "unprot_deauthenticate", + [NL80211_CMD_UNPROT_DISASSOCIATE] = "unprot_disassociate", + [NL80211_CMD_NEW_PEER_CANDIDATE] = "new_peer_candidate", + [NL80211_CMD_GET_WOWLAN] = "get_wowlan", + [NL80211_CMD_SET_WOWLAN] = "set_wowlan", + [NL80211_CMD_START_SCHED_SCAN] = "start_sched_scan", + [NL80211_CMD_STOP_SCHED_SCAN] = "stop_sched_scan", + [NL80211_CMD_SCHED_SCAN_RESULTS] = "sched_scan_results", + [NL80211_CMD_SCHED_SCAN_STOPPED] = "sched_scan_stopped", + [NL80211_CMD_SET_REKEY_OFFLOAD] = "set_rekey_offload", + [NL80211_CMD_PMKSA_CANDIDATE] = "pmksa_candidate", + [NL80211_CMD_TDLS_OPER] = "tdls_oper", + [NL80211_CMD_TDLS_MGMT] = "tdls_mgmt", + [NL80211_CMD_UNEXPECTED_FRAME] = "unexpected_frame", + [NL80211_CMD_PROBE_CLIENT] = "probe_client", + [NL80211_CMD_REGISTER_BEACONS] = "register_beacons", + [NL80211_CMD_UNEXPECTED_4ADDR_FRAME] = "unexpected_4addr_frame", + [NL80211_CMD_SET_NOACK_MAP] = "set_noack_map", + [NL80211_CMD_CH_SWITCH_NOTIFY] = "ch_switch_notify", + [NL80211_CMD_START_P2P_DEVICE] = "start_p2p_device", + [NL80211_CMD_STOP_P2P_DEVICE] = "stop_p2p_device", + [NL80211_CMD_CONN_FAILED] = "conn_failed", + [NL80211_CMD_SET_MCAST_RATE] = "set_mcast_rate", + [NL80211_CMD_SET_MAC_ACL] = "set_mac_acl", + [NL80211_CMD_RADAR_DETECT] = "radar_detect", + [NL80211_CMD_GET_PROTOCOL_FEATURES] = "get_protocol_features", + [NL80211_CMD_UPDATE_FT_IES] = "update_ft_ies", + [NL80211_CMD_FT_EVENT] = "ft_event", + [NL80211_CMD_CRIT_PROTOCOL_START] = "crit_protocol_start", + [NL80211_CMD_CRIT_PROTOCOL_STOP] = "crit_protocol_stop", + [NL80211_CMD_GET_COALESCE] = "get_coalesce", + [NL80211_CMD_SET_COALESCE] = "set_coalesce", + [NL80211_CMD_CHANNEL_SWITCH] = "channel_switch", + [NL80211_CMD_VENDOR] = "vendor", + [NL80211_CMD_SET_QOS_MAP] = "set_qos_map", + [NL80211_CMD_ADD_TX_TS] = "add_tx_ts", + [NL80211_CMD_DEL_TX_TS] = "del_tx_ts", + [NL80211_CMD_GET_MPP] = "get_mpp", + [NL80211_CMD_JOIN_OCB] = "join_ocb", + [NL80211_CMD_LEAVE_OCB] = "leave_ocb", + [NL80211_CMD_CH_SWITCH_STARTED_NOTIFY] = "ch_switch_started_notify", + [NL80211_CMD_TDLS_CHANNEL_SWITCH] = "tdls_channel_switch", + [NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH] = "tdls_cancel_channel_switch", + [NL80211_CMD_WIPHY_REG_CHANGE] = "wiphy_reg_change", + [NL80211_CMD_ABORT_SCAN] = "abort_scan", + [NL80211_CMD_START_NAN] = "start_nan", + [NL80211_CMD_STOP_NAN] = "stop_nan", + [NL80211_CMD_ADD_NAN_FUNCTION] = "add_nan_function", + [NL80211_CMD_DEL_NAN_FUNCTION] = "del_nan_function", + [NL80211_CMD_CHANGE_NAN_CONFIG] = "change_nan_config", + [NL80211_CMD_NAN_MATCH] = "nan_match", + [NL80211_CMD_SET_MULTICAST_TO_UNICAST] = "set_multicast_to_unicast", + [NL80211_CMD_UPDATE_CONNECT_PARAMS] = "update_connect_params", + [NL80211_CMD_SET_PMK] = "set_pmk", + [NL80211_CMD_DEL_PMK] = "del_pmk", + [NL80211_CMD_PORT_AUTHORIZED] = "port_authorized", + [NL80211_CMD_RELOAD_REGDB] = "reload_regdb", + [NL80211_CMD_EXTERNAL_AUTH] = "external_auth", + [NL80211_CMD_STA_OPMODE_CHANGED] = "sta_opmode_changed", + [NL80211_CMD_CONTROL_PORT_FRAME] = "control_port_frame", + [NL80211_CMD_GET_FTM_RESPONDER_STATS] = "get_ftm_responder_stats", + [NL80211_CMD_PEER_MEASUREMENT_START] = "peer_measurement_start", + [NL80211_CMD_PEER_MEASUREMENT_RESULT] = "peer_measurement_result", + [NL80211_CMD_PEER_MEASUREMENT_COMPLETE] = "peer_measurement_complete", + [NL80211_CMD_NOTIFY_RADAR] = "notify_radar", + [NL80211_CMD_UPDATE_OWE_INFO] = "update_owe_info", + [NL80211_CMD_PROBE_MESH_LINK] = "probe_mesh_link", + [NL80211_CMD_SET_TID_CONFIG] = "set_tid_config", + [NL80211_CMD_UNPROT_BEACON] = "unprot_beacon", + [NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS] = "control_port_frame_tx_status", + [NL80211_CMD_SET_SAR_SPECS] = "set_sar_specs", + [NL80211_CMD_OBSS_COLOR_COLLISION] = "obss_color_collision", + [NL80211_CMD_COLOR_CHANGE_REQUEST] = "color_change_request", + [NL80211_CMD_COLOR_CHANGE_STARTED] = "color_change_started", + [NL80211_CMD_COLOR_CHANGE_ABORTED] = "color_change_aborted", + [NL80211_CMD_COLOR_CHANGE_COMPLETED] = "color_change_completed", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(nl80211_cmd, int); diff --git a/src/shared/wifi-util.h b/src/shared/wifi-util.h index 0ce41372e2..8afd1a4ec3 100644 --- a/src/shared/wifi-util.h +++ b/src/shared/wifi-util.h @@ -3,9 +3,13 @@ #pragma once #include -#include #include "sd-netlink.h" -int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftype, char **ssid); -int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid); +#include "ether-addr-util.h" + +int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *ret_iftype, char **ret_ssid); +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_; +const char *nl80211_cmd_to_string(int cmd) _const_; diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index ee1b32fddb..fdf802041a 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -95,6 +95,7 @@ int sd_netlink_message_close_container(sd_netlink_message *m); int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data); int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data); +int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data); int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data); int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data); int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret);