diff options
-rw-r--r-- | include/linux/nl80211.h | 10 | ||||
-rw-r--r-- | include/net/cfg80211.h | 17 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 104 |
3 files changed, 131 insertions, 0 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index ff39e4b234d4..901a70d327d1 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h | |||
@@ -519,6 +519,14 @@ | |||
519 | * If used as the command it must have an interface index and you can | 519 | * If used as the command it must have an interface index and you can |
520 | * only unsubscribe from the event by closing the socket. | 520 | * only unsubscribe from the event by closing the socket. |
521 | * | 521 | * |
522 | * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface | ||
523 | * by sending a null data frame to it and reporting when the frame is | ||
524 | * acknowleged. This is used to allow timing out inactive clients. Uses | ||
525 | * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a | ||
526 | * direct reply with an %NL80211_ATTR_COOKIE that is later used to match | ||
527 | * up the event with the request. The event includes the same data and | ||
528 | * has %NL80211_ATTR_ACK set if the frame was ACKed. | ||
529 | * | ||
522 | * @NL80211_CMD_MAX: highest used command number | 530 | * @NL80211_CMD_MAX: highest used command number |
523 | * @__NL80211_CMD_AFTER_LAST: internal use | 531 | * @__NL80211_CMD_AFTER_LAST: internal use |
524 | */ | 532 | */ |
@@ -650,6 +658,8 @@ enum nl80211_commands { | |||
650 | 658 | ||
651 | NL80211_CMD_UNEXPECTED_FRAME, | 659 | NL80211_CMD_UNEXPECTED_FRAME, |
652 | 660 | ||
661 | NL80211_CMD_PROBE_CLIENT, | ||
662 | |||
653 | /* add new commands above here */ | 663 | /* add new commands above here */ |
654 | 664 | ||
655 | /* used to define NL80211_CMD_MAX below */ | 665 | /* used to define NL80211_CMD_MAX below */ |
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 86d207da6cce..389e85e8c03d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -1432,6 +1432,9 @@ struct cfg80211_gtk_rekey_data { | |||
1432 | * | 1432 | * |
1433 | * @tdls_mgmt: Transmit a TDLS management frame. | 1433 | * @tdls_mgmt: Transmit a TDLS management frame. |
1434 | * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). | 1434 | * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). |
1435 | * | ||
1436 | * @probe_client: probe an associated client, must return a cookie that it | ||
1437 | * later passes to cfg80211_probe_status(). | ||
1435 | */ | 1438 | */ |
1436 | struct cfg80211_ops { | 1439 | struct cfg80211_ops { |
1437 | int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); | 1440 | int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); |
@@ -1621,6 +1624,9 @@ struct cfg80211_ops { | |||
1621 | u16 status_code, const u8 *buf, size_t len); | 1624 | u16 status_code, const u8 *buf, size_t len); |
1622 | int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, | 1625 | int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, |
1623 | u8 *peer, enum nl80211_tdls_operation oper); | 1626 | u8 *peer, enum nl80211_tdls_operation oper); |
1627 | |||
1628 | int (*probe_client)(struct wiphy *wiphy, struct net_device *dev, | ||
1629 | const u8 *peer, u64 *cookie); | ||
1624 | }; | 1630 | }; |
1625 | 1631 | ||
1626 | /* | 1632 | /* |
@@ -3216,6 +3222,17 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, | |||
3216 | bool cfg80211_rx_spurious_frame(struct net_device *dev, | 3222 | bool cfg80211_rx_spurious_frame(struct net_device *dev, |
3217 | const u8 *addr, gfp_t gfp); | 3223 | const u8 *addr, gfp_t gfp); |
3218 | 3224 | ||
3225 | /** | ||
3226 | * cfg80211_probe_status - notify userspace about probe status | ||
3227 | * @dev: the device the probe was sent on | ||
3228 | * @addr: the address of the peer | ||
3229 | * @cookie: the cookie filled in @probe_client previously | ||
3230 | * @acked: indicates whether probe was acked or not | ||
3231 | * @gfp: allocation flags | ||
3232 | */ | ||
3233 | void cfg80211_probe_status(struct net_device *dev, const u8 *addr, | ||
3234 | u64 cookie, bool acked, gfp_t gfp); | ||
3235 | |||
3219 | /* Logging, debugging and troubleshooting/diagnostic helpers. */ | 3236 | /* Logging, debugging and troubleshooting/diagnostic helpers. */ |
3220 | 3237 | ||
3221 | /* wiphy_printk helpers, similar to dev_printk */ | 3238 | /* wiphy_printk helpers, similar to dev_printk */ |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2094c8468d78..a8eda12b46a8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -890,6 +890,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
890 | } | 890 | } |
891 | if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) | 891 | if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) |
892 | CMD(sched_scan_start, START_SCHED_SCAN); | 892 | CMD(sched_scan_start, START_SCHED_SCAN); |
893 | CMD(probe_client, PROBE_CLIENT); | ||
893 | 894 | ||
894 | #undef CMD | 895 | #undef CMD |
895 | 896 | ||
@@ -5853,6 +5854,59 @@ static int nl80211_register_unexpected_frame(struct sk_buff *skb, | |||
5853 | return 0; | 5854 | return 0; |
5854 | } | 5855 | } |
5855 | 5856 | ||
5857 | static int nl80211_probe_client(struct sk_buff *skb, | ||
5858 | struct genl_info *info) | ||
5859 | { | ||
5860 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
5861 | struct net_device *dev = info->user_ptr[1]; | ||
5862 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
5863 | struct sk_buff *msg; | ||
5864 | void *hdr; | ||
5865 | const u8 *addr; | ||
5866 | u64 cookie; | ||
5867 | int err; | ||
5868 | |||
5869 | if (wdev->iftype != NL80211_IFTYPE_AP && | ||
5870 | wdev->iftype != NL80211_IFTYPE_P2P_GO) | ||
5871 | return -EOPNOTSUPP; | ||
5872 | |||
5873 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
5874 | return -EINVAL; | ||
5875 | |||
5876 | if (!rdev->ops->probe_client) | ||
5877 | return -EOPNOTSUPP; | ||
5878 | |||
5879 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
5880 | if (!msg) | ||
5881 | return -ENOMEM; | ||
5882 | |||
5883 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
5884 | NL80211_CMD_PROBE_CLIENT); | ||
5885 | |||
5886 | if (IS_ERR(hdr)) { | ||
5887 | err = PTR_ERR(hdr); | ||
5888 | goto free_msg; | ||
5889 | } | ||
5890 | |||
5891 | addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
5892 | |||
5893 | err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie); | ||
5894 | if (err) | ||
5895 | goto free_msg; | ||
5896 | |||
5897 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
5898 | |||
5899 | genlmsg_end(msg, hdr); | ||
5900 | |||
5901 | return genlmsg_reply(msg, info); | ||
5902 | |||
5903 | nla_put_failure: | ||
5904 | err = -ENOBUFS; | ||
5905 | free_msg: | ||
5906 | nlmsg_free(msg); | ||
5907 | return err; | ||
5908 | } | ||
5909 | |||
5856 | #define NL80211_FLAG_NEED_WIPHY 0x01 | 5910 | #define NL80211_FLAG_NEED_WIPHY 0x01 |
5857 | #define NL80211_FLAG_NEED_NETDEV 0x02 | 5911 | #define NL80211_FLAG_NEED_NETDEV 0x02 |
5858 | #define NL80211_FLAG_NEED_RTNL 0x04 | 5912 | #define NL80211_FLAG_NEED_RTNL 0x04 |
@@ -6416,6 +6470,14 @@ static struct genl_ops nl80211_ops[] = { | |||
6416 | .internal_flags = NL80211_FLAG_NEED_NETDEV | | 6470 | .internal_flags = NL80211_FLAG_NEED_NETDEV | |
6417 | NL80211_FLAG_NEED_RTNL, | 6471 | NL80211_FLAG_NEED_RTNL, |
6418 | }, | 6472 | }, |
6473 | { | ||
6474 | .cmd = NL80211_CMD_PROBE_CLIENT, | ||
6475 | .doit = nl80211_probe_client, | ||
6476 | .policy = nl80211_policy, | ||
6477 | .flags = GENL_ADMIN_PERM, | ||
6478 | .internal_flags = NL80211_FLAG_NEED_NETDEV | | ||
6479 | NL80211_FLAG_NEED_RTNL, | ||
6480 | }, | ||
6419 | }; | 6481 | }; |
6420 | 6482 | ||
6421 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 6483 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
@@ -7478,6 +7540,48 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, | |||
7478 | nlmsg_free(msg); | 7540 | nlmsg_free(msg); |
7479 | } | 7541 | } |
7480 | 7542 | ||
7543 | void cfg80211_probe_status(struct net_device *dev, const u8 *addr, | ||
7544 | u64 cookie, bool acked, gfp_t gfp) | ||
7545 | { | ||
7546 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
7547 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | ||
7548 | struct sk_buff *msg; | ||
7549 | void *hdr; | ||
7550 | int err; | ||
7551 | |||
7552 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
7553 | if (!msg) | ||
7554 | return; | ||
7555 | |||
7556 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT); | ||
7557 | if (!hdr) { | ||
7558 | nlmsg_free(msg); | ||
7559 | return; | ||
7560 | } | ||
7561 | |||
7562 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
7563 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
7564 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); | ||
7565 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
7566 | if (acked) | ||
7567 | NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); | ||
7568 | |||
7569 | err = genlmsg_end(msg, hdr); | ||
7570 | if (err < 0) { | ||
7571 | nlmsg_free(msg); | ||
7572 | return; | ||
7573 | } | ||
7574 | |||
7575 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
7576 | nl80211_mlme_mcgrp.id, gfp); | ||
7577 | return; | ||
7578 | |||
7579 | nla_put_failure: | ||
7580 | genlmsg_cancel(msg, hdr); | ||
7581 | nlmsg_free(msg); | ||
7582 | } | ||
7583 | EXPORT_SYMBOL(cfg80211_probe_status); | ||
7584 | |||
7481 | static int nl80211_netlink_notify(struct notifier_block * nb, | 7585 | static int nl80211_netlink_notify(struct notifier_block * nb, |
7482 | unsigned long state, | 7586 | unsigned long state, |
7483 | void *_notify) | 7587 | void *_notify) |