nl80211: support sending TDLS commands/frames
authorArik Nemtsov <arik@wizery.com>
Wed, 28 Sep 2011 11:12:50 +0000 (14:12 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 30 Sep 2011 19:57:05 +0000 (15:57 -0400)
Add support for sending high-level TDLS commands and TDLS frames via
NL80211_CMD_TDLS_OPER and NL80211_CMD_TDLS_MGMT, respectively. Add
appropriate cfg80211 callbacks for lower level drivers.

Add wiphy capability flags for TDLS support and advertise them via
nl80211.

Signed-off-by: Arik Nemtsov <arik@wizery.com>
Cc: Kalyan C Gaddam <chakkal@iit.edu>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/linux/nl80211.h
include/net/cfg80211.h
net/wireless/nl80211.c

index c73582f..a5ab23d 100644 (file)
  * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
  *     of PMKSA caching dandidates.
  *
+ * @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_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -632,6 +635,9 @@ enum nl80211_commands {
 
        NL80211_CMD_PMKSA_CANDIDATE,
 
+       NL80211_CMD_TDLS_OPER,
+       NL80211_CMD_TDLS_MGMT,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1089,6 +1095,20 @@ enum nl80211_commands {
  *     This attribute is used with %NL80211_CMD_TRIGGER_SCAN and
  *     %NL80211_CMD_FRAME commands.
  *
+ * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup
+ *     request, link setup confirm, link teardown, etc.). Values are
+ *     described in the TDLS (802.11z) specification.
+ * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a
+ *     TDLS conversation between two devices.
+ * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see
+ *     &enum nl80211_tdls_operation, represented as a u8.
+ * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate
+ *     as a TDLS peer sta.
+ * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown
+ *     procedures should be performed by sending TDLS packets via
+ *     %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be
+ *     used for asking the driver to perform a TDLS operation.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1311,6 +1331,12 @@ enum nl80211_attrs {
 
        NL80211_ATTR_TX_NO_CCK_RATE,
 
+       NL80211_ATTR_TDLS_ACTION,
+       NL80211_ATTR_TDLS_DIALOG_TOKEN,
+       NL80211_ATTR_TDLS_OPERATION,
+       NL80211_ATTR_TDLS_SUPPORT,
+       NL80211_ATTR_TDLS_EXTERNAL_SETUP,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2604,4 +2630,20 @@ enum nl80211_pmksa_candidate_attr {
        MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1
 };
 
+/**
+ * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION
+ * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request
+ * @NL80211_TDLS_SETUP: Setup TDLS link
+ * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established
+ * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link
+ * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link
+ */
+enum nl80211_tdls_operation {
+       NL80211_TDLS_DISCOVERY_REQ,
+       NL80211_TDLS_SETUP,
+       NL80211_TDLS_TEARDOWN,
+       NL80211_TDLS_ENABLE_LINK,
+       NL80211_TDLS_DISABLE_LINK,
+};
+
 #endif /* __LINUX_NL80211_H */
index 34b8f26..74f4f85 100644 (file)
@@ -1422,6 +1422,9 @@ struct cfg80211_gtk_rekey_data {
  * @set_ringparam: Set tx and rx ring sizes.
  *
  * @get_ringparam: Get tx and rx ring current and maximum sizes.
+ *
+ * @tdls_mgmt: Transmit a TDLS management frame.
+ * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup).
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -1605,6 +1608,12 @@ struct cfg80211_ops {
 
        int     (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev,
                                  struct cfg80211_gtk_rekey_data *data);
+
+       int     (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
+                            u8 *peer, u8 action_code,  u8 dialog_token,
+                            u16 status_code, const u8 *buf, size_t len);
+       int     (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
+                            u8 *peer, enum nl80211_tdls_operation oper);
 };
 
 /*
@@ -1657,6 +1666,12 @@ struct cfg80211_ops {
  * @WIPHY_FLAG_SUPPORTS_FW_ROAM: The device supports roaming feature in the
  *     firmware.
  * @WIPHY_FLAG_AP_UAPSD: The device supports uapsd on AP.
+ * @WIPHY_FLAG_SUPPORTS_TDLS: The device supports TDLS (802.11z) operation.
+ * @WIPHY_FLAG_TDLS_EXTERNAL_SETUP: The device does not handle TDLS (802.11z)
+ *     link setup/discovery operations internally. Setup, discovery and
+ *     teardown packets should be sent through the @NL80211_CMD_TDLS_MGMT
+ *     command. When this flag is not set, @NL80211_CMD_TDLS_OPER should be
+ *     used for asking the driver/firmware to perform a TDLS operation.
  */
 enum wiphy_flags {
        WIPHY_FLAG_CUSTOM_REGULATORY            = BIT(0),
@@ -1673,6 +1688,8 @@ enum wiphy_flags {
        WIPHY_FLAG_ENFORCE_COMBINATIONS         = BIT(12),
        WIPHY_FLAG_SUPPORTS_FW_ROAM             = BIT(13),
        WIPHY_FLAG_AP_UAPSD                     = BIT(14),
+       WIPHY_FLAG_SUPPORTS_TDLS                = BIT(15),
+       WIPHY_FLAG_TDLS_EXTERNAL_SETUP          = BIT(16),
 };
 
 /**
index 3799623..25a37fc 100644 (file)
@@ -192,6 +192,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
        [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
        [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
+       [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 },
+       [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 },
+       [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
+       [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
+       [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -732,9 +737,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH);
        if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD)
                NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD);
-
        if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)
                NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT);
+       if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT);
+       if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP);
 
        NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
                sizeof(u32) * dev->wiphy.n_cipher_suites,
@@ -877,6 +885,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        }
        CMD(set_channel, SET_CHANNEL);
        CMD(set_wds_peer, SET_WDS_PEER);
+       if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
+               CMD(tdls_mgmt, TDLS_MGMT);
+               CMD(tdls_oper, TDLS_OPER);
+       }
        if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
                CMD(sched_scan_start, START_SCHED_SCAN);
 
@@ -4966,6 +4978,57 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
        return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
 }
 
+static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       u8 action_code, dialog_token;
+       u16 status_code;
+       u8 *peer;
+
+       if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
+           !rdev->ops->tdls_mgmt)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
+           !info->attrs[NL80211_ATTR_STATUS_CODE] ||
+           !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
+           !info->attrs[NL80211_ATTR_IE] ||
+           !info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
+       status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
+       dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
+
+       return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
+                                   dialog_token, status_code,
+                                   nla_data(info->attrs[NL80211_ATTR_IE]),
+                                   nla_len(info->attrs[NL80211_ATTR_IE]));
+}
+
+static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       enum nl80211_tdls_operation operation;
+       u8 *peer;
+
+       if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
+           !rdev->ops->tdls_oper)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
+           !info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
+       peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation);
+}
+
 static int nl80211_remain_on_channel(struct sk_buff *skb,
                                     struct genl_info *info)
 {
@@ -6281,6 +6344,22 @@ static struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_TDLS_MGMT,
+               .doit = nl80211_tdls_mgmt,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_TDLS_OPER,
+               .doit = nl80211_tdls_oper,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {