struct cfg80211_dscp_range up[8];
};
+/**
+ * DOC: Neighbor Awareness Networking (NAN)
+ *
+ * NAN uses two interface types:
+ *
+ * - %NL80211_IFTYPE_NAN: a non-netdev interface. This has two roles: (1) holds
+ * the configuration of all NAN activities (DE parameters, synchronisation
+ * parameters, local schedule, etc.), and (2) uses as the NAN Management
+ * Interface (NMI), which is used for NAN management communication.
+ *
+ * - %NL80211_IFTYPE_NAN_DATA: The NAN Data Interface (NDI), used for data
+ * communication with NAN peers.
+ *
+ * An NDI interface can only be started (IFF_UP) if the NMI one is running and
+ * NAN is started. Before NAN is stopped, all associated NDI interfaces
+ * must be stopped first.
+ *
+ * The local schedule specifies which channels the device is available on and
+ * when. Must be cancelled before NAN is stopped.
+ */
+
/**
* struct cfg80211_nan_band_config - NAN band specific configuration
*
* @NL80211_IFTYPE_OCB: Outside Context of a BSS
* This mode corresponds to the MIB variable dot11OCBActivated=true
* @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev)
+ * @NL80211_IFTYPE_NAN_DATA: NAN data interface type (netdev); NAN data
+ * interfaces can only be brought up (IFF_UP) when a NAN interface
+ * already exists and NAN has been started (using %NL80211_CMD_START_NAN).
* @NL80211_IFTYPE_MAX: highest interface type number currently defined
* @NUM_NL80211_IFTYPES: number of defined interface types
*
NL80211_IFTYPE_P2P_DEVICE,
NL80211_IFTYPE_OCB,
NL80211_IFTYPE_NAN,
+ NL80211_IFTYPE_NAN_DATA,
/* keep last */
NUM_NL80211_IFTYPES,
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_NAN_DATA:
/* shouldn't happen */
WARN_ON_ONCE(1);
break;
case NUM_NL80211_IFTYPES:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_NAN_DATA:
WARN_ON_ONCE(1);
break;
}
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
case NUM_NL80211_IFTYPES:
WARN_ON(1);
break;
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
/* no special treatment */
break;
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_P2P_DEVICE:
sdata->vif.bss_conf.bssid = sdata->vif.addr;
break;
+ case NL80211_IFTYPE_NAN_DATA:
+ break;
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_WDS:
case NUM_NL80211_IFTYPES:
(ieee80211_is_public_action(hdr, skb->len) ||
(ieee80211_is_auth(hdr->frame_control) &&
ether_addr_equal(sdata->vif.addr, hdr->addr1)));
+ case NL80211_IFTYPE_NAN_DATA:
+ return false;
default:
break;
}
return res;
}
break;
+ case NL80211_IFTYPE_NAN_DATA:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_NAN_DATA:
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_P2P_DEVICE:
/* Can NAN type be considered as beaconing interface? */
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
break;
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_WDS:
ASSERT_RTNL();
+ /*
+ * Some netdev interfaces need to be closed before some non-netdev
+ * ones, i.e. NAN_DATA interfaces need to be closed before the NAN
+ * interface
+ */
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (wdev->netdev) {
dev_close(wdev->netdev);
continue;
}
+ }
- /* otherwise, check iftype */
-
- guard(wiphy)(wiphy);
+ guard(wiphy)(wiphy);
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
switch (wdev->iftype) {
case NL80211_IFTYPE_P2P_DEVICE:
cfg80211_stop_p2p_device(rdev, wdev);
list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) {
if (wdev->nl_owner_dead) {
+ cfg80211_close_dependents(rdev, wdev);
+
if (wdev->netdev)
dev_close(wdev->netdev);
}
}
+void cfg80211_close_dependents(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ ASSERT_RTNL();
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ return;
+
+ /* Close all NAN DATA interfaces */
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (wdev->iftype == NL80211_IFTYPE_NAN_DATA)
+ dev_close(wdev->netdev);
+ }
+}
+
static void cfg80211_destroy_iface_wk(struct work_struct *work)
{
struct cfg80211_registered_device *rdev;
rdev->num_running_monitor_ifaces += num;
}
-void cfg80211_leave(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev,
- int link_id)
+void cfg80211_leave_locked(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int link_id)
{
struct net_device *dev = wdev->netdev;
struct cfg80211_sched_scan_request *pos, *tmp;
break;
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_NAN_DATA:
/* nothing to do */
break;
case NL80211_IFTYPE_UNSPECIFIED:
}
}
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int link_id)
+{
+ ASSERT_RTNL();
+
+ /* NAN_DATA interfaces must be closed before stopping NAN */
+ cfg80211_close_dependents(rdev, wdev);
+
+ guard(wiphy)(&rdev->wiphy);
+
+ cfg80211_leave_locked(rdev, wdev, link_id);
+}
+
void cfg80211_stop_link(struct wiphy *wiphy, struct wireless_dev *wdev,
int link_id, gfp_t gfp)
{
trace_cfg80211_stop_link(wiphy, wdev, link_id);
+ if (wdev->iftype == NL80211_IFTYPE_NAN)
+ return;
+
ev = kzalloc_obj(*ev, gfp);
if (!ev)
return;
}
break;
case NETDEV_GOING_DOWN:
- scoped_guard(wiphy, &rdev->wiphy) {
- cfg80211_leave(rdev, wdev, -1);
+ cfg80211_leave(rdev, wdev, -1);
+ scoped_guard(wiphy, &rdev->wiphy)
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);
if (rfkill_blocked(rdev->wiphy.rfkill))
return notifier_from_errno(-ERFKILL);
+
+ /* NAN_DATA interfaces require a running NAN interface */
+ if (wdev->iftype == NL80211_IFTYPE_NAN_DATA) {
+ struct wireless_dev *iter;
+ bool nan_started = false;
+
+ list_for_each_entry(iter, &rdev->wiphy.wdev_list, list) {
+ if (iter->iftype == NL80211_IFTYPE_NAN &&
+ wdev_running(iter)) {
+ nan_started = true;
+ break;
+ }
+ }
+
+ if (!nan_started)
+ return notifier_from_errno(-ENOLINK);
+ }
break;
default:
return NOTIFY_DONE;
void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
+void cfg80211_close_dependents(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev);
+
/* free object */
void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype, int num);
+void cfg80211_leave_locked(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int link_id);
+
void cfg80211_leave(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
int link_id);
return 0;
return -ENOLINK;
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
if (wiphy_ext_feature_isset(wdev->wiphy,
NL80211_EXT_FEATURE_SECURE_NAN))
return 0;
else
dev_close(wdev->netdev);
+ cfg80211_close_dependents(rdev, wdev);
+
mutex_lock(&rdev->wiphy.mtx);
return cfg80211_remove_virtual_intf(rdev, wdev);
if (wdev->iftype != NL80211_IFTYPE_NAN)
return -EOPNOTSUPP;
+ cfg80211_close_dependents(rdev, wdev);
+
+ guard(wiphy)(&rdev->wiphy);
+
cfg80211_stop_nan(rdev, wdev);
return 0;
NL80211_FLAG_NEED_RTNL) \
SELECTOR(__sel, WIPHY_CLEAR, \
NL80211_FLAG_NEED_WIPHY | \
- NL80211_FLAG_CLEAR_SKB)
+ NL80211_FLAG_CLEAR_SKB) \
+ SELECTOR(__sel, WDEV_UP_RTNL_NOMTX, \
+ NL80211_FLAG_NEED_WDEV_UP | \
+ NL80211_FLAG_NO_WIPHY_MTX | \
+ NL80211_FLAG_NEED_RTNL)
enum nl80211_internal_flags_selector {
#define SELECTOR(_, name, value) NL80211_IFL_SEL_##name,
.doit = nl80211_stop_nan,
.flags = GENL_ADMIN_PERM,
.internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NO_WIPHY_MTX |
NL80211_FLAG_NEED_RTNL),
},
{
continue;
chandef = wdev->u.ocb.chandef;
break;
+ case NL80211_IFTYPE_NAN_DATA:
+ /* NAN channels are checked in NL80211_IFTYPE_NAN interface */
+ break;
default:
/* others not implemented for now */
WARN_ON_ONCE(1);
struct wireless_dev *wdev;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- guard(wiphy)(wiphy);
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ bool valid;
- list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
- if (!reg_wdev_chan_valid(wiphy, wdev))
+ scoped_guard(wiphy, wiphy)
+ valid = reg_wdev_chan_valid(wiphy, wdev);
+ if (!valid)
cfg80211_leave(rdev, wdev, -1);
+ }
}
static void reg_check_chans_work(struct work_struct *work)
if (!rdev->wiphy.registered)
goto out_unlock_rtnl;
- wiphy_lock(&rdev->wiphy);
if (rdev->wiphy.wowlan_config) {
- cfg80211_process_wiphy_works(rdev, NULL);
- if (rdev->ops->suspend)
- ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config);
- if (ret <= 0)
- goto out_unlock_wiphy;
+ scoped_guard(wiphy, &rdev->wiphy) {
+ cfg80211_process_wiphy_works(rdev, NULL);
+ if (rdev->ops->suspend)
+ ret = rdev_suspend(rdev,
+ rdev->wiphy.wowlan_config);
+ if (ret <= 0)
+ goto out_unlock_rtnl;
+ }
}
/* Driver refused to configure wowlan (ret = 1) or no wowlan */
cfg80211_leave_all(rdev);
- cfg80211_process_rdev_events(rdev);
- cfg80211_process_wiphy_works(rdev, NULL);
- if (rdev->ops->suspend)
- ret = rdev_suspend(rdev, NULL);
-
-out_unlock_wiphy:
- wiphy_unlock(&rdev->wiphy);
+ scoped_guard(wiphy, &rdev->wiphy) {
+ cfg80211_process_rdev_events(rdev);
+ cfg80211_process_wiphy_works(rdev, NULL);
+ if (rdev->ops->suspend)
+ ret = rdev_suspend(rdev, NULL);
+ }
out_unlock_rtnl:
if (ret == 0)
rdev->suspended = true;
ev->ij.channel);
break;
case EVENT_STOPPED:
- cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev,
- ev->link_id);
+ /*
+ * for NAN interfaces cfg80211_leave must be called but
+ * locking here doesn't allow this.
+ */
+ if (WARN_ON(wdev->iftype == NL80211_IFTYPE_NAN))
+ break;
+
+ cfg80211_leave_locked(wiphy_to_rdev(wdev->wiphy), wdev,
+ ev->link_id);
break;
case EVENT_PORT_AUTHORIZED:
__cfg80211_port_authorized(wdev, ev->pa.peer_addr,
if (otype == NL80211_IFTYPE_AP_VLAN)
return -EOPNOTSUPP;
+ /*
+ * for NAN interfaces cfg80211_leave must be called for leaving,
+ * but locking here doesn't allow this.
+ */
+ if (otype == NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
/* cannot change into P2P device or NAN */
if (ntype == NL80211_IFTYPE_P2P_DEVICE ||
ntype == NL80211_IFTYPE_NAN)
dev->ieee80211_ptr->use_4addr = false;
rdev_set_qos_map(rdev, dev, NULL);
- cfg80211_leave(rdev, dev->ieee80211_ptr, -1);
+ cfg80211_leave_locked(rdev, dev->ieee80211_ptr, -1);
cfg80211_process_rdev_events(rdev);
cfg80211_mlme_purge_registrations(dev->ieee80211_ptr);
case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_NAN_DATA:
dev->priv_flags |= IFF_DONT_BRIDGE;
break;
case NL80211_IFTYPE_P2P_GO: