wifi: cfg80211: add support for NAN data interface

This new interface type represents a NAN data interface (NDI).
It is used for data communication with NAN peers.

Note that the existing NL80211_IFTYPE_NAN interface, which is the NAN
Management Interface (NMI), is used for management communication.

An NDI interface is started when a new NAN data path is about to
be established, and is stopped after the NAN data path is terminated.

- An NDI interface can only be started if the NMI is running, and NAN is
  started.
- Before the NMI is stopped, the NDI interfaces will be stopped.

Add the new interface type, handle add/remove operations for it,
and makes sure of the conditions above.

Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260219114327.0d681335c2e2.I92973483e927820ae2297853c141842fdb262747@changeid
Link: https://patch.msgid.link/20260318123926.206536-4-miriam.rachel.korenblit@intel.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Miri Korenblit
2026-03-18 14:39:17 +02:00
committed by Johannes Berg
parent 763a5a580f
commit 0e8ec738a7
14 changed files with 159 additions and 29 deletions

View File

@@ -3980,6 +3980,27 @@ struct cfg80211_qos_map {
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
*

View File

@@ -3749,6 +3749,9 @@ enum nl80211_attrs {
* @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
*
@@ -3770,6 +3773,7 @@ enum nl80211_iftype {
NL80211_IFTYPE_P2P_DEVICE,
NL80211_IFTYPE_OCB,
NL80211_IFTYPE_NAN,
NL80211_IFTYPE_NAN_DATA,
/* keep last */
NUM_NL80211_IFTYPES,

View File

@@ -718,6 +718,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
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;

View File

@@ -495,6 +495,7 @@ ieee80211_get_width_of_link(struct ieee80211_link_data *link)
case NUM_NL80211_IFTYPES:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_NAN_DATA:
WARN_ON_ONCE(1);
break;
}
@@ -1458,6 +1459,7 @@ ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link)
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;

View File

@@ -1368,6 +1368,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
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:
@@ -1945,6 +1946,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
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:

View File

@@ -4607,6 +4607,8 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
(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;
}

View File

@@ -2118,6 +2118,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
return res;
}
break;
case NL80211_IFTYPE_NAN_DATA:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_P2P_DEVICE:

View File

@@ -816,6 +816,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
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:
@@ -939,6 +940,7 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev)
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:

View File

@@ -329,16 +329,21 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
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);
@@ -396,6 +401,8 @@ void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
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);
@@ -406,6 +413,21 @@ void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
}
}
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;
@@ -1419,9 +1441,8 @@ void cfg80211_update_iface_num(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;
@@ -1472,6 +1493,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
break;
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_NAN_DATA:
/* nothing to do */
break;
case NL80211_IFTYPE_UNSPECIFIED:
@@ -1482,6 +1504,19 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
}
}
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)
{
@@ -1497,6 +1532,9 @@ void cfg80211_stop_link(struct wiphy *wiphy, struct wireless_dev *wdev,
trace_cfg80211_stop_link(wiphy, wdev, link_id);
if (wdev->iftype == NL80211_IFTYPE_NAN)
return;
ev = kzalloc_obj(*ev, gfp);
if (!ev)
return;
@@ -1647,10 +1685,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
}
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);
@@ -1731,6 +1768,23 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
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;

View File

@@ -318,6 +318,9 @@ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy,
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);
@@ -541,6 +544,9 @@ int cfg80211_validate_beacon_int(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);

View File

@@ -1764,6 +1764,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
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;
@@ -4921,6 +4922,8 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
else
dev_close(wdev->netdev);
cfg80211_close_dependents(rdev, wdev);
mutex_lock(&rdev->wiphy.mtx);
return cfg80211_remove_virtual_intf(rdev, wdev);
@@ -15964,6 +15967,10 @@ static int nl80211_stop_nan(struct sk_buff *skb, struct genl_info *info)
if (wdev->iftype != NL80211_IFTYPE_NAN)
return -EOPNOTSUPP;
cfg80211_close_dependents(rdev, wdev);
guard(wiphy)(&rdev->wiphy);
cfg80211_stop_nan(rdev, wdev);
return 0;
@@ -18356,7 +18363,11 @@ nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *info)
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,
@@ -19193,6 +19204,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.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),
},
{

View File

@@ -2409,6 +2409,9 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
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);
@@ -2445,11 +2448,14 @@ static void reg_leave_invalid_chans(struct wiphy *wiphy)
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)

View File

@@ -102,25 +102,26 @@ static int wiphy_suspend(struct device *dev)
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;

View File

@@ -1144,8 +1144,15 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
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,
@@ -1184,6 +1191,13 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
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)
@@ -1204,7 +1218,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
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);
@@ -1232,6 +1246,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
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: