/* RCU-protected (and RTNL for writers) */
LIST_HEAD(cfg80211_rdev_list); int cfg80211_rdev_list_generation;
/* for debugfs */ staticstruct dentry *ieee80211_debugfs_dir;
/* for the cleanup, scan and event works */ struct workqueue_struct *cfg80211_wq;
staticbool cfg80211_disable_40mhz_24ghz;
module_param(cfg80211_disable_40mhz_24ghz, bool, 0644);
MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, "Disable 40MHz support in the 2.4GHz band");
rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); if (!rdev) return NULL; return &rdev->wiphy;
}
staticint cfg80211_dev_check_name(struct cfg80211_registered_device *rdev, constchar *newname)
{ struct cfg80211_registered_device *rdev2; int wiphy_idx, taken = -1, digits;
ASSERT_RTNL();
if (strlen(newname) > NL80211_WIPHY_NAME_MAXLEN) return -EINVAL;
/* prohibit calling the thing phy%d when %d is not its number */
sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) { /* count number of places needed to print wiphy_idx */
digits = 1; while (wiphy_idx /= 10)
digits++; /* * deny the name if it is phy<idx> where <idx> is printed * without leading zeroes. taken == strlen(newname) here
*/ if (taken == strlen(PHY_NAME) + digits) return -EINVAL;
}
/* Ensure another device does not already have this name. */
for_each_rdev(rdev2) if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0) return -EINVAL;
return 0;
}
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname)
{ int result;
if (rv < 0) {
rtnl_unlock(); goto use_default_name;
}
rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name);
rtnl_unlock(); if (rv) goto use_default_name;
} else { int rv;
use_default_name: /* NOTE: This is *probably* safe w/out holding rtnl because of * the restrictions on phy names. Probably this call could * fail if some other part of the kernel (re)named a device * phyX. But, might should add some locking and check return * value, and use a different name if this one exists?
*/
rv = dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); if (rv < 0) {
kfree(rdev); return NULL;
}
}
static int wiphy_verify_iface_combinations(struct wiphy *wiphy, conststruct ieee80211_iface_combination *iface_comb, int n_iface_comb, bool combined_radio)
{ conststruct ieee80211_iface_combination *c; int i, j;
for (i = 0; i < n_iface_comb; i++) {
u32 cnt = 0;
u16 all_iftypes = 0;
c = &iface_comb[i];
/* * Combinations with just one interface aren't real, * however we make an exception for DFS.
*/ if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths)) return -EINVAL;
/* Need at least one channel */ if (WARN_ON(!c->num_different_channels)) return -EINVAL;
/* DFS only works on one channel. Avoid this check * for multi-radio global combination, since it hold * the capabilities of all radio combinations.
*/ if (!combined_radio &&
WARN_ON(c->radar_detect_widths &&
c->num_different_channels > 1)) return -EINVAL;
/* Shouldn't list software iftypes in combinations! */ if (WARN_ON(wiphy->software_iftypes & types)) return -EINVAL;
/* Only a single P2P_DEVICE can be allowed, avoid this * check for multi-radio global combination, since it * hold the capabilities of all radio combinations.
*/ if (!combined_radio &&
WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) &&
c->limits[j].max > 1)) return -EINVAL;
/* Only a single NAN can be allowed, avoid this * check for multi-radio global combination, since it * hold the capabilities of all radio combinations.
*/ if (!combined_radio &&
WARN_ON(types & BIT(NL80211_IFTYPE_NAN) &&
c->limits[j].max > 1)) return -EINVAL;
/* * This isn't well-defined right now. If you have an * IBSS interface, then its beacon interval may change * by joining other networks, and nothing prevents it * from doing that. * So technically we probably shouldn't even allow AP * and IBSS in the same interface, but it seems that * some drivers support that, possibly only with fixed * beacon intervals for IBSS.
*/ if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
c->beacon_int_min_gcd)) { return -EINVAL;
}
cnt += c->limits[j].max; /* * Don't advertise an unsupported type * in a combination.
*/ if (WARN_ON((wiphy->interface_modes & types) != types)) return -EINVAL;
}
if (WARN_ON(all_iftypes & BIT(NL80211_IFTYPE_WDS))) return -EINVAL;
/* You can't even choose that many! */ if (WARN_ON(cnt < c->max_interfaces)) return -EINVAL;
}
return 0;
}
staticint wiphy_verify_combinations(struct wiphy *wiphy)
{ int i, ret; bool combined_radio = false;
if (wiphy->n_radio) { for (i = 0; i < wiphy->n_radio; i++) { conststruct wiphy_radio *radio = &wiphy->radio[i];
ret = wiphy_verify_iface_combinations(wiphy,
radio->iface_combinations,
radio->n_iface_combinations, false); if (ret) return ret;
}
combined_radio = true;
}
ret = wiphy_verify_iface_combinations(wiphy,
wiphy->iface_combinations,
wiphy->n_iface_combinations,
combined_radio);
return ret;
}
int wiphy_register(struct wiphy *wiphy)
{ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); int res; enum nl80211_band band; struct ieee80211_supported_band *sband; bool have_band = false; int i;
u16 ifmodes = wiphy->interface_modes;
if (WARN_ON(wiphy->ap_sme_capa &&
!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) return -EINVAL;
if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) return -EINVAL;
if (WARN_ON(wiphy->addresses &&
!is_zero_ether_addr(wiphy->perm_addr) &&
memcmp(wiphy->perm_addr, wiphy->addresses[0].addr,
ETH_ALEN))) return -EINVAL;
if (WARN_ON(wiphy->max_acl_mac_addrs &&
(!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) ||
!rdev->ops->set_mac_acl))) return -EINVAL;
/* assure only valid behaviours are flagged by driver * hence subtract 2 as bit 0 is invalid.
*/ if (WARN_ON(wiphy->bss_select_support &&
(wiphy->bss_select_support & ~(BIT(__NL80211_BSS_SELECT_ATTR_AFTER_LAST) - 2)))) return -EINVAL;
if (WARN_ON(wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X) &&
(!rdev->ops->set_pmk || !rdev->ops->del_pmk))) return -EINVAL;
if (WARN_ON(!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
rdev->ops->update_connect_params)) return -EINVAL;
if (wiphy->addresses)
memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
sband->band = band; if (WARN_ON(!sband->n_channels)) return -EINVAL; /* * on 60GHz or sub-1Ghz band, there are no legacy rates, so * n_bitrates is 0
*/ if (WARN_ON((band != NL80211_BAND_60GHZ &&
band != NL80211_BAND_S1GHZ) &&
!sband->n_bitrates)) return -EINVAL;
if (WARN_ON(band == NL80211_BAND_6GHZ &&
(sband->ht_cap.ht_supported ||
sband->vht_cap.vht_supported))) return -EINVAL;
/* * Since cfg80211_disable_40mhz_24ghz is global, we can * modify the sband's ht data even if the driver uses a * global structure for that.
*/ if (cfg80211_disable_40mhz_24ghz &&
band == NL80211_BAND_2GHZ &&
sband->ht_cap.ht_supported) {
sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
}
/* * Since we use a u32 for rate bitmaps in * ieee80211_get_response_rate, we cannot * have more than 32 legacy rates.
*/ if (WARN_ON(sband->n_bitrates > 32)) return -EINVAL;
for (i = 0; i < sband->n_channels; i++) {
sband->channels[i].orig_flags =
sband->channels[i].flags;
sband->channels[i].orig_mag = INT_MAX;
sband->channels[i].orig_mpwr =
sband->channels[i].max_power;
sband->channels[i].band = band;
if (WARN_ON(sband->channels[i].freq_offset >= 1000)) return -EINVAL;
}
/* * For EHT 20 MHz STA, the capabilities format differs * but to simplify, don't check 20 MHz but rather check * only if AP and non-AP were mentioned at the same time, * reject if so.
*/ if (WARN_ON(iftd->eht_cap.has_eht &&
has_ap && has_non_ap)) return -EINVAL;
}
if (WARN_ON(!have_he && band == NL80211_BAND_6GHZ)) return -EINVAL;
have_band = true;
}
if (!have_band) {
WARN_ON(1); return -EINVAL;
}
for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { /* * Validate we have a policy (can be explicitly set to * VENDOR_CMD_RAW_DATA which is non-NULL) and also that * we have at least one of doit/dumpit.
*/ if (WARN_ON(!rdev->wiphy.vendor_commands[i].policy)) return -EINVAL; if (WARN_ON(!rdev->wiphy.vendor_commands[i].doit &&
!rdev->wiphy.vendor_commands[i].dumpit)) return -EINVAL;
}
/* Check that nobody globally advertises any capabilities they do not * advertise on all possible interface types.
*/ if (wiphy->extended_capabilities_len &&
wiphy->num_iftype_ext_capab &&
wiphy->iftype_ext_capab) {
u8 supported_on_all, j; conststruct wiphy_iftype_ext_capab *capab;
capab = wiphy->iftype_ext_capab; for (j = 0; j < wiphy->extended_capabilities_len; j++) { if (capab[0].extended_capabilities_len > j)
supported_on_all =
capab[0].extended_capabilities[j]; else
supported_on_all = 0x00; for (i = 1; i < wiphy->num_iftype_ext_capab; i++) { if (j >= capab[i].extended_capabilities_len) {
supported_on_all = 0x00; break;
}
supported_on_all &=
capab[i].extended_capabilities[j];
} if (WARN_ON(wiphy->extended_capabilities[j] &
~supported_on_all)) break;
}
}
rdev->wiphy.registered = true;
rtnl_unlock();
res = rfkill_register(rdev->wiphy.rfkill); if (res) {
rfkill_destroy(rdev->wiphy.rfkill);
rdev->wiphy.rfkill = NULL;
wiphy_unregister(&rdev->wiphy); return res;
}
/* * First remove the hardware from everywhere, this makes * it impossible to find from userspace.
*/
debugfs_remove_recursive(rdev->wiphy.debugfsdir);
list_del_rcu(&rdev->list);
synchronize_rcu();
/* * If this device got a regulatory hint tell core its * free to listen now to a new shiny device regulatory hint
*/
wiphy_regulatory_deregister(wiphy);
/* * The 'regd' can only be non-NULL if we never finished * initializing the wiphy and thus never went through the * unregister path - e.g. in failure scenarios. Thus, it * cannot have been visible to anyone if non-NULL, so we * can just free it here.
*/
kfree(rcu_dereference_raw(rdev->wiphy.regd));
switch (wdev->iftype) { case NL80211_IFTYPE_P2P_DEVICE:
cfg80211_stop_p2p_device(rdev, wdev); break; case NL80211_IFTYPE_NAN:
cfg80211_stop_nan(rdev, wdev); break; default: break;
}
#ifdef CONFIG_CFG80211_WEXT
kfree_sensitive(wdev->wext.keys);
wdev->wext.keys = NULL; #endif
wiphy_work_cancel(wdev->wiphy, &wdev->cqm_rssi_work); /* deleted from the list, so can't be found from nl80211 any more */
cqm_config = rcu_access_pointer(wdev->cqm_config);
kfree_rcu(cqm_config, rcu_head);
RCU_INIT_POINTER(wdev->cqm_config, NULL);
/* * Ensure that all events have been processed and * freed.
*/
cfg80211_process_wdev_events(wdev);
switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC:
cfg80211_leave_ibss(rdev, dev, true); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION:
list_for_each_entry_safe(pos, tmp, &rdev->sched_scan_req_list,
list) { if (dev == pos->dev)
cfg80211_stop_sched_scan_req(rdev, pos, false);
}
#ifdef CONFIG_CFG80211_WEXT
kfree(wdev->wext.ie);
wdev->wext.ie = NULL;
wdev->wext.ie_len = 0;
wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; #endif
cfg80211_disconnect(rdev, dev,
WLAN_REASON_DEAUTH_LEAVING, true); break; case NL80211_IFTYPE_MESH_POINT:
cfg80211_leave_mesh(rdev, dev); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO:
cfg80211_stop_ap(rdev, dev, -1, true); break; case NL80211_IFTYPE_OCB:
cfg80211_leave_ocb(rdev, dev); break; case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: /* cannot happen, has no netdev */ break; case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: /* nothing to do */ break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_WDS: case NUM_NL80211_IFTYPES: /* invalid */ break;
}
}
/* * We get here also when the interface changes network namespaces, * as it's registered into the new one, but we don't want it to * change ID in that case. Checking if the ID is already assigned * works, because 0 isn't considered a valid ID and the memory is * 0-initialized.
*/ if (!wdev->identifier)
wdev->identifier = ++rdev->wdev_id;
list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list);
rdev->devlist_generation++;
wdev->registered = true;
if (wdev->netdev &&
sysfs_create_link(&wdev->netdev->dev.kobj, &rdev->wiphy.dev.kobj, "phy80211"))
pr_err("failed to add phy80211 symlink to netdev!\n");
switch (state) { case NETDEV_POST_INIT:
SET_NETDEV_DEVTYPE(dev, &wiphy_type);
wdev->netdev = dev; /* can only change netns with wiphy */
dev->netns_immutable = true;
cfg80211_init_wdev(wdev); break; case NETDEV_REGISTER: if (!wdev->registered) {
guard(wiphy)(&rdev->wiphy);
cfg80211_register_wdev(rdev, wdev);
} break; case NETDEV_UNREGISTER: /* * It is possible to get NETDEV_UNREGISTER multiple times, * so check wdev->registered.
*/ if (wdev->registered && !wdev->registering) {
guard(wiphy)(&rdev->wiphy);
_cfg80211_unregister_wdev(wdev, false);
} break; case NETDEV_GOING_DOWN:
scoped_guard(wiphy, &rdev->wiphy) {
cfg80211_leave(rdev, wdev);
cfg80211_remove_links(wdev);
} /* since we just did cfg80211_leave() nothing to do there */
cancel_work_sync(&wdev->disconnect_wk);
cancel_work_sync(&wdev->pmsr_free_wk); break; case NETDEV_DOWN:
wiphy_lock(&rdev->wiphy);
cfg80211_update_iface_num(rdev, wdev->iftype, -1); if (rdev->scan_req && rdev->scan_req->req.wdev == wdev) { if (WARN_ON(!rdev->scan_req->notified &&
(!rdev->int_scan_req ||
!rdev->int_scan_req->notified)))
rdev->scan_req->info.aborted = true;
___cfg80211_scan_done(rdev, false);
}
rdev->opencount--;
wiphy_unlock(&rdev->wiphy);
wake_up(&rdev->dev_wait); break; case NETDEV_UP:
wiphy_lock(&rdev->wiphy);
cfg80211_update_iface_num(rdev, wdev->iftype, 1); switch (wdev->iftype) { #ifdef CONFIG_CFG80211_WEXT case NL80211_IFTYPE_ADHOC:
cfg80211_ibss_wext_join(rdev, wdev); break; case NL80211_IFTYPE_STATION:
cfg80211_mgd_wext_connect(rdev, wdev); break; #endif #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT:
{ /* backward compat code... */ struct mesh_setup setup;
memcpy(&setup, &default_mesh_setup, sizeof(setup)); /* back compat only needed for mesh_id */
setup.mesh_id = wdev->u.mesh.id;
setup.mesh_id_len = wdev->u.mesh.id_up_len; if (wdev->u.mesh.id_up_len)
__cfg80211_join_mesh(rdev, dev,
&setup,
&default_mesh_config); break;
} #endif default: break;
}
rdev->opencount++;
/* * Configure power management to the driver here so that its * correctly set also after interface type changes etc.
*/ if ((wdev->iftype == NL80211_IFTYPE_STATION ||
wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
rdev->ops->set_power_mgmt &&
rdev_set_power_mgmt(rdev, dev, wdev->ps,
wdev->ps_timeout)) { /* assume this means it's off */
wdev->ps = false;
}
wiphy_unlock(&rdev->wiphy); break; case NETDEV_PRE_UP: if (!cfg80211_iftype_allowed(wdev->wiphy, wdev->iftype,
wdev->use_4addr, 0)) return notifier_from_errno(-EOPNOTSUPP);
if (rfkill_blocked(rdev->wiphy.rfkill)) return notifier_from_errno(-ERFKILL); break; default: return NOTIFY_DONE;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.