Commit 28946da7 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

nl80211: allow subscribing to unexpected class3 frames



To implement AP mode without monitor interfaces we
need to be able to send a deauth to stations that
send frames without being associated. Enable this
by adding a new nl80211 event for such frames that
an application can subscribe to.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 665c93a9
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -509,6 +509,16 @@
 * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
 * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame.
 *
 * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP
 *	(or GO) interface (i.e. hostapd) to ask for unexpected frames to
 *	implement sending deauth to stations that send unexpected class 3
 *	frames. Also used as the event sent by the kernel when such a frame
 *	is received.
 *	For the event, the %NL80211_ATTR_MAC attribute carries the TA and
 *	other attributes like the interface index are present.
 *	If used as the command it must have an interface index and you can
 *	only unsubscribe from the event by closing the socket.
 *
 * @NL80211_CMD_MAX: highest used command number
 * @__NL80211_CMD_AFTER_LAST: internal use
 */
@@ -638,6 +648,8 @@ enum nl80211_commands {
	NL80211_CMD_TDLS_OPER,
	NL80211_CMD_TDLS_MGMT,

	NL80211_CMD_UNEXPECTED_FRAME,

	/* add new commands above here */

	/* used to define NL80211_CMD_MAX below */
+17 −0
Original line number Diff line number Diff line
@@ -2183,6 +2183,8 @@ struct wireless_dev {

	int beacon_interval;

	u32 ap_unexpected_nlpid;

#ifdef CONFIG_CFG80211_WEXT
	/* wext data */
	struct {
@@ -3193,6 +3195,21 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
				     const u8 *bssid, bool preauth, gfp_t gfp);

/**
 * cfg80211_rx_spurious_frame - inform userspace about a spurious frame
 * @dev: The device the frame matched to
 * @addr: the transmitter address
 * @gfp: context flags
 *
 * This function is used in AP mode (only!) to inform userspace that
 * a spurious class 3 frame was received, to be able to deauth the
 * sender.
 * Returns %true if the frame was passed to userspace (or this failed
 * for a reason other than not having a subscription.)
 */
bool cfg80211_rx_spurious_frame(struct net_device *dev,
				const u8 *addr, gfp_t gfp);

/* Logging, debugging and troubleshooting/diagnostic helpers. */

/* wiphy_printk helpers, similar to dev_printk */
+16 −0
Original line number Diff line number Diff line
@@ -879,6 +879,9 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid)
	}

	spin_unlock_bh(&wdev->mgmt_registrations_lock);

	if (nlpid == wdev->ap_unexpected_nlpid)
		wdev->ap_unexpected_nlpid = 0;
}

void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
@@ -1107,3 +1110,16 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
	nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
}
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);

bool cfg80211_rx_spurious_frame(struct net_device *dev,
				const u8 *addr, gfp_t gfp)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;

	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
		    wdev->iftype != NL80211_IFTYPE_P2P_GO))
		return false;

	return nl80211_unexpected_frame(dev, addr, gfp);
}
EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
+66 −0
Original line number Diff line number Diff line
@@ -5832,6 +5832,23 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
	return err;
}

static int nl80211_register_unexpected_frame(struct sk_buff *skb,
					     struct genl_info *info)
{
	struct net_device *dev = info->user_ptr[1];
	struct wireless_dev *wdev = dev->ieee80211_ptr;

	if (wdev->iftype != NL80211_IFTYPE_AP &&
	    wdev->iftype != NL80211_IFTYPE_P2P_GO)
		return -EINVAL;

	if (wdev->ap_unexpected_nlpid)
		return -EBUSY;

	wdev->ap_unexpected_nlpid = info->snd_pid;
	return 0;
}

#define NL80211_FLAG_NEED_WIPHY		0x01
#define NL80211_FLAG_NEED_NETDEV	0x02
#define NL80211_FLAG_NEED_RTNL		0x04
@@ -6387,6 +6404,14 @@ static struct genl_ops nl80211_ops[] = {
		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
				  NL80211_FLAG_NEED_RTNL,
	},
	{
		.cmd = NL80211_CMD_UNEXPECTED_FRAME,
		.doit = nl80211_register_unexpected_frame,
		.policy = nl80211_policy,
		.flags = GENL_ADMIN_PERM,
		.internal_flags = NL80211_FLAG_NEED_NETDEV |
				  NL80211_FLAG_NEED_RTNL,
	},
};

static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -7171,6 +7196,47 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
	nlmsg_free(msg);
}

bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	struct sk_buff *msg;
	void *hdr;
	int err;
	u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid);

	if (!nlpid)
		return false;

	msg = nlmsg_new(100, gfp);
	if (!msg)
		return true;

	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_UNEXPECTED_FRAME);
	if (!hdr) {
		nlmsg_free(msg);
		return true;
	}

	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);

	err = genlmsg_end(msg, hdr);
	if (err < 0) {
		nlmsg_free(msg);
		return true;
	}

	genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
	return true;

 nla_put_failure:
	genlmsg_cancel(msg, hdr);
	nlmsg_free(msg);
	return true;
}

int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
		      struct net_device *netdev, u32 nlpid,
		      int freq, const u8 *buf, size_t len, gfp_t gfp)
+3 −0
Original line number Diff line number Diff line
@@ -117,4 +117,7 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
				    struct net_device *netdev, int index,
				    const u8 *bssid, bool preauth, gfp_t gfp);

bool nl80211_unexpected_frame(struct net_device *dev,
			      const u8 *addr, gfp_t gfp);

#endif /* __NET_WIRELESS_NL80211_H */