Hi John,
This patchset is an RFC for a new PMKSA API. This is needed for fullmac
devices that run firmwares which set the re-association frames IEs. Those
devices need to get a correct PMKID from wpa_supplicant in order to do faster
WPA2 roaming. mac80211 based drivers wont need to use this API, wpa_supplicant
does all the work for them.
The patchset defines the nl80211 API, adds a wext compatibility handler, and
finally provides the corresponding iwmc3200wifi implementation.
Samuel Ortiz (3):
nl80211: PMKSA caching support
cfg80211: Add PMKSA wext compatibility handler
iwmc3200wifi: Implement cfg80211 PMKSA API
drivers/net/wireless/iwmc3200wifi/cfg80211.c | 30 +++++++
drivers/net/wireless/iwmc3200wifi/commands.c | 22 +++++
drivers/net/wireless/iwmc3200wifi/commands.h | 13 +++
drivers/net/wireless/iwmc3200wifi/umac.h | 1 +
include/linux/ieee80211.h | 2 +
include/linux/nl80211.h | 6 ++
include/net/cfg80211.h | 26 ++++++
net/wireless/nl80211.c | 117 ++++++++++++++++++++++++++
net/wireless/wext-compat.c | 42 +++++++++
9 files changed, 259 insertions(+), 0 deletions(-)
We need to implement the PMKSA API for proper WPA2 pre-auth and fast
re-association. Our fullmac device generates all (re-)assoc IEs, and thus it
needs the right PMKIDs. With this implementation we now get them from
wpa_supplicant.
Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/net/wireless/iwmc3200wifi/cfg80211.c | 30 ++++++++++++++++++++++++++
drivers/net/wireless/iwmc3200wifi/commands.c | 22 +++++++++++++++++++
drivers/net/wireless/iwmc3200wifi/commands.h | 13 +++++++++++
drivers/net/wireless/iwmc3200wifi/umac.h | 1 +
4 files changed, 66 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index 7cfc2c0..c74f9f9 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -725,6 +725,33 @@ static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
CFG_POWER_INDEX, iwm->conf.power_index);
}
+int iwm_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+
+ return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD);
+}
+
+int iwm_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+
+ return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL);
+}
+
+int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
+{
+ struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+ struct cfg80211_pmksa pmksa;
+
+ memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
+
+ return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH);
+}
+
+
static struct cfg80211_ops iwm_cfg80211_ops = {
.change_virtual_intf = iwm_cfg80211_change_iface,
.add_key = iwm_cfg80211_add_key,
@@ -741,6 +768,9 @@ static struct cfg80211_ops iwm_cfg80211_ops = {
.set_tx_power = iwm_cfg80211_set_txpower,
.get_tx_power = iwm_cfg80211_get_txpower,
.set_power_mgmt = iwm_cfg80211_set_power_mgmt,
+ .set_pmksa = iwm_cfg80211_set_pmksa,
+ .del_pmksa = iwm_cfg80211_del_pmksa,
+ .flush_pmksa = iwm_cfg80211_flush_pmksa,
};
static const u32 cipher_suites[] = {
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
index 46ca7c5..bd06307 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.c
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -960,3 +960,25 @@ int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm,
sizeof(struct iwm_umac_cmd_stop_resume_tx));
}
+
+int iwm_send_pmkid_update(struct iwm_priv *iwm,
+ struct cfg80211_pmksa *pmksa, u32 command)
+{
+ struct iwm_umac_pmkid_update update;
+ int ret;
+
+ memset(&update, 0, sizeof(struct iwm_umac_pmkid_update));
+
+ update.command = cpu_to_le32(command);
+ memcpy(&update.bssid, pmksa->bssid, ETH_ALEN);
+ memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
+
+ ret = iwm_send_wifi_if_cmd(iwm, &update,
+ sizeof(struct iwm_umac_pmkid_update), 0);
+ if (ret) {
+ IWM_ERR(iwm, "PMKID update command failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h
index 95cdf94..06af055 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.h
+++ b/drivers/net/wireless/iwmc3200wifi/commands.h
@@ -458,6 +458,17 @@ struct iwm_umac_cmd_stop_resume_tx {
u16 reserved;
} __attribute__ ((packed));
+#define IWM_CMD_PMKID_ADD 1
+#define IWM_CMD_PMKID_DEL 2
+#define IWM_CMD_PMKID_FLUSH 3
+
+struct iwm_umac_pmkid_update {
+ __le32 command;
+ u8 bssid[ETH_ALEN];
+ __le16 reserved;
+ u8 pmkid[WLAN_PMKID_LEN];
+} __attribute__ ((packed));
+
/* LMAC commands */
int iwm_read_mac(struct iwm_priv *iwm, u8 *mac);
int iwm_send_prio_table(struct iwm_priv *iwm);
@@ -488,6 +499,8 @@ int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len);
int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm,
struct iwm_umac_notif_stop_resume_tx *ntf);
+int iwm_send_pmkid_update(struct iwm_priv *iwm,
+ struct cfg80211_pmksa *pmksa, u32 command);
/* UDMA commands */
int iwm_target_reset(struct iwm_priv *iwm);
diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h
index 70094bf..24c7c7a 100644
--- a/drivers/net/wireless/iwmc3200wifi/umac.h
+++ b/drivers/net/wireless/iwmc3200wifi/umac.h
@@ -298,6 +298,7 @@ struct iwm_udma_out_wifi_hdr {
#define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID 0x1B
#define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE 0x1C
#define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS 0x1E
+#define UMAC_WIFI_IF_CMD_PMKID_UPDATE 0x1F
#define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER 0x20
/* UMAC WiFi interface ports */
--
1.6.3.3
This is an interface to set, delete and flush PMKIDs through nl80211.
Main users would be fullmac devices which firmwares are capable of generating
the RSN IEs for the re-association requests, e.g. iwmc3200wifi.
Signed-off-by: Samuel Ortiz <[email protected]>
---
include/linux/ieee80211.h | 2 +
include/linux/nl80211.h | 6 ++
include/net/cfg80211.h | 26 ++++++++++
net/wireless/nl80211.c | 117 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 151 insertions(+), 0 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index afa8e0a..d9724a2 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1266,6 +1266,8 @@ enum ieee80211_sa_query_action {
#define WLAN_MAX_KEY_LEN 32
+#define WLAN_PMKID_LEN 16
+
/**
* ieee80211_get_qos_ctl - get pointer to qos control bytes
* @hdr: the frame
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 45db17f..3b85d76 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -349,6 +349,10 @@ enum nl80211_commands {
NL80211_CMD_GET_SURVEY,
NL80211_CMD_NEW_SURVEY_RESULTS,
+ NL80211_CMD_SET_PMKSA,
+ NL80211_CMD_DEL_PMKSA,
+ NL80211_CMD_FLUSH_PMKSA,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -732,6 +736,8 @@ enum nl80211_attrs {
NL80211_ATTR_SURVEY_INFO,
+ NL80211_ATTR_PMKID,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index a6492e9..6211372 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -871,6 +871,19 @@ struct cfg80211_bitrate_mask {
u32 fixed; /* fixed bitrate, 0 == not fixed */
u32 maxrate; /* in kbps, 0 == no limit */
};
+/**
+ * struct cfg80211_pmksa - PMK Security Association
+ *
+ * This structure is passed to the set/del_pmksa() method for PMKSA
+ * caching.
+ *
+ * @bssid: The AP's BSSID.
+ * @pmkid: The PMK material itself.
+ */
+struct cfg80211_pmksa {
+ u8 *bssid;
+ u8 *pmkid;
+};
/**
* struct cfg80211_ops - backend description for wireless configuration
@@ -976,6 +989,13 @@ struct cfg80211_bitrate_mask {
* @dump_survey: get site survey information.
*
* @testmode_cmd: run a test mode command
+ *
+ * @set_pmksa: Cache a PMKID for a BSSID. This is mostly useful for fullmac
+ * devices running firmwares capable of generating the (re) association
+ * RSN IE. It allows for faster roaming between WPA2 BSSIDs.
+ * @del_pmksa: Delete a cached PMKID.
+ * @flush_pmksa: Flush all cached PMKIDs.
+ *
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy);
@@ -1097,6 +1117,12 @@ struct cfg80211_ops {
int (*dump_survey)(struct wiphy *wiphy, struct net_device *netdev,
int idx, struct survey_info *info);
+ int (*set_pmksa)(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa);
+ int (*del_pmksa)(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa);
+ int (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev);
+
/* some temporary stuff to finish wext */
int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 149539a..e3be689 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -139,6 +139,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
[NL80211_ATTR_PID] = { .type = NLA_U32 },
[NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
+ [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
+ .len = WLAN_PMKID_LEN },
};
/* policy for the attributes */
@@ -561,6 +563,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
CMD(deauth, DEAUTHENTICATE);
CMD(disassoc, DISASSOCIATE);
CMD(join_ibss, JOIN_IBSS);
+ CMD(set_pmksa, SET_PMKSA);
+ CMD(del_pmksa, DEL_PMKSA);
+ CMD(flush_pmksa, FLUSH_PMKSA);
if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
i++;
NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
@@ -4221,6 +4226,99 @@ static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev;
+ int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_pmksa *pmksa) = NULL;
+ int err;
+ struct net_device *dev;
+ struct cfg80211_pmksa pmksa;
+
+ memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_PMKID])
+ return -EINVAL;
+
+ rtnl_lock();
+
+ err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+ if (err)
+ goto out_rtnl;
+
+ pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
+ pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ switch (info->genlhdr->cmd) {
+ case NL80211_CMD_SET_PMKSA:
+ rdev_ops = rdev->ops->set_pmksa;
+ break;
+ case NL80211_CMD_DEL_PMKSA:
+ rdev_ops = rdev->ops->del_pmksa;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ if (!rdev_ops) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = rdev_ops(&rdev->wiphy, dev, &pmksa);
+
+ out:
+ cfg80211_unlock_rdev(rdev);
+ dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
+ return err;
+}
+
+static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev;
+ int err;
+ struct net_device *dev;
+
+ rtnl_lock();
+
+ err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+ if (err)
+ goto out_rtnl;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!rdev->ops->flush_pmksa) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = rdev->ops->flush_pmksa(&rdev->wiphy, dev);
+
+ out:
+ cfg80211_unlock_rdev(rdev);
+ dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
+ return err;
+
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -4465,6 +4563,25 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.dumpit = nl80211_dump_survey,
},
+ {
+ .cmd = NL80211_CMD_SET_PMKSA,
+ .doit = nl80211_setdel_pmksa,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_PMKSA,
+ .doit = nl80211_setdel_pmksa,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_FLUSH_PMKSA,
+ .doit = nl80211_flush_pmksa,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
.name = "mlme",
--
1.6.3.3
Hi Jouni,
On Tue, Nov 24, 2009 at 07:41:05PM +0200, Jouni Malinen wrote:
> On Tue, Nov 24, 2009 at 04:26:52PM +0100, Samuel Ortiz wrote:
> > This is an interface to set, delete and flush PMKIDs through nl80211.
> > Main users would be fullmac devices which firmwares are capable of generating
> > the RSN IEs for the re-association requests, e.g. iwmc3200wifi.
>
> Is there a maximum number of PMKSA cache entries that the driver can
> handle? If so, it might be useful to provide a mechanism to indicate
> that limit (e.g., something similar to NL80211_ATTR_MAX_NUM_SCAN_SSIDS).
Good point. I'll add that to the patch.
Cheers,
Samuel.
> --
> Jouni Malinen PGP id EFC895FA
--
Intel Open Source Technology Centre
http://oss.intel.com/
On Tue, Nov 24, 2009 at 04:26:52PM +0100, Samuel Ortiz wrote:
> This is an interface to set, delete and flush PMKIDs through nl80211.
> Main users would be fullmac devices which firmwares are capable of generating
> the RSN IEs for the re-association requests, e.g. iwmc3200wifi.
Is there a maximum number of PMKSA cache entries that the driver can
handle? If so, it might be useful to provide a mechanism to indicate
that limit (e.g., something similar to NL80211_ATTR_MAX_NUM_SCAN_SSIDS).
--
Jouni Malinen PGP id EFC895FA
With the addition of the *_pmksa cfg80211 ops, we can now add the
corresponding wireless extensions compatibility handler.
Signed-off-by: Samuel Ortiz <[email protected]>
---
net/wireless/wext-compat.c | 42 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 42 insertions(+), 0 deletions(-)
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 29091ac..7e7985e 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -1401,6 +1401,47 @@ int cfg80211_wext_giwessid(struct net_device *dev,
}
EXPORT_SYMBOL_GPL(cfg80211_wext_giwessid);
+int cfg80211_wext_siwpmksa(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct cfg80211_pmksa cfg_pmksa;
+ struct iw_pmksa* pmksa = (struct iw_pmksa *)extra;
+
+ memset(&cfg_pmksa, 0, sizeof(struct cfg80211_pmksa));
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION)
+ return -EINVAL;
+
+ cfg_pmksa.bssid = pmksa->bssid.sa_data;
+ cfg_pmksa.pmkid = pmksa->pmkid;
+
+ switch (pmksa->cmd) {
+ case IW_PMKSA_ADD:
+ if (!rdev->ops->set_pmksa)
+ return -EOPNOTSUPP;
+
+ return rdev->ops->set_pmksa(&rdev->wiphy, dev, &cfg_pmksa);
+
+ case IW_PMKSA_REMOVE:
+ if (!rdev->ops->del_pmksa)
+ return -EOPNOTSUPP;
+
+ return rdev->ops->del_pmksa(&rdev->wiphy, dev, &cfg_pmksa);
+
+ case IW_PMKSA_FLUSH:
+ if (!rdev->ops->flush_pmksa)
+ return -EOPNOTSUPP;
+
+ return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static const iw_handler cfg80211_handlers[] = {
[IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname,
[IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq,
@@ -1433,6 +1474,7 @@ static const iw_handler cfg80211_handlers[] = {
[IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth,
[IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth,
[IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext,
+ [IW_IOCTL_IDX(SIOCSIWPMKSA)] = (iw_handler) cfg80211_wext_siwpmksa,
};
const struct iw_handler_def cfg80211_wext_handler = {
--
1.6.3.3
Hi Samuel,
> /* policy for the attributes */
> @@ -561,6 +563,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
> CMD(deauth, DEAUTHENTICATE);
> CMD(disassoc, DISASSOCIATE);
> CMD(join_ibss, JOIN_IBSS);
> + CMD(set_pmksa, SET_PMKSA);
> + CMD(del_pmksa, DEL_PMKSA);
> + CMD(flush_pmksa, FLUSH_PMKSA);
once these have been accepted, don't forget to send a patch for iw that
actually prints out the supported command list.
Regards
Marcel
With the addition of the *_pmksa cfg80211 ops, we can now add the
corresponding wireless extensions compatibility handler.
Signed-off-by: Samuel Ortiz <[email protected]>
---
v2: Fixed a coding style issue thanks to johill's eagle eye.
net/wireless/wext-compat.c | 42 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 42 insertions(+), 0 deletions(-)
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 29091ac..584eb48 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -1401,6 +1401,47 @@ int cfg80211_wext_giwessid(struct net_device *dev,
}
EXPORT_SYMBOL_GPL(cfg80211_wext_giwessid);
+int cfg80211_wext_siwpmksa(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct cfg80211_pmksa cfg_pmksa;
+ struct iw_pmksa *pmksa = (struct iw_pmksa *)extra;
+
+ memset(&cfg_pmksa, 0, sizeof(struct cfg80211_pmksa));
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION)
+ return -EINVAL;
+
+ cfg_pmksa.bssid = pmksa->bssid.sa_data;
+ cfg_pmksa.pmkid = pmksa->pmkid;
+
+ switch (pmksa->cmd) {
+ case IW_PMKSA_ADD:
+ if (!rdev->ops->set_pmksa)
+ return -EOPNOTSUPP;
+
+ return rdev->ops->set_pmksa(&rdev->wiphy, dev, &cfg_pmksa);
+
+ case IW_PMKSA_REMOVE:
+ if (!rdev->ops->del_pmksa)
+ return -EOPNOTSUPP;
+
+ return rdev->ops->del_pmksa(&rdev->wiphy, dev, &cfg_pmksa);
+
+ case IW_PMKSA_FLUSH:
+ if (!rdev->ops->flush_pmksa)
+ return -EOPNOTSUPP;
+
+ return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static const iw_handler cfg80211_handlers[] = {
[IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname,
[IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq,
@@ -1433,6 +1474,7 @@ static const iw_handler cfg80211_handlers[] = {
[IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth,
[IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth,
[IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext,
+ [IW_IOCTL_IDX(SIOCSIWPMKSA)] = (iw_handler) cfg80211_wext_siwpmksa,
};
const struct iw_handler_def cfg80211_wext_handler = {
--
1.6.3.3
--
Intel Open Source Technology Centre
http://oss.intel.com/
We need to implement the PMKSA API for proper WPA2 pre-auth and fast
re-association. Our fullmac device generates all (re-)assoc IEs, and thus it
needs the right PMKIDs. With this implementation we now get them from
wpa_supplicant.
Signed-off-by: Samuel Ortiz <[email protected]>
---
v2: Set wiphy->max_num_pmkids
drivers/net/wireless/iwmc3200wifi/cfg80211.c | 31 ++++++++++++++++++++++++++
drivers/net/wireless/iwmc3200wifi/commands.c | 22 ++++++++++++++++++
drivers/net/wireless/iwmc3200wifi/commands.h | 13 +++++++++++
drivers/net/wireless/iwmc3200wifi/umac.h | 2 +
4 files changed, 68 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index 7cfc2c0..7c4f44a 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -725,6 +725,33 @@ static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
CFG_POWER_INDEX, iwm->conf.power_index);
}
+int iwm_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+
+ return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD);
+}
+
+int iwm_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+
+ return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL);
+}
+
+int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
+{
+ struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+ struct cfg80211_pmksa pmksa;
+
+ memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
+
+ return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH);
+}
+
+
static struct cfg80211_ops iwm_cfg80211_ops = {
.change_virtual_intf = iwm_cfg80211_change_iface,
.add_key = iwm_cfg80211_add_key,
@@ -741,6 +768,9 @@ static struct cfg80211_ops iwm_cfg80211_ops = {
.set_tx_power = iwm_cfg80211_set_txpower,
.get_tx_power = iwm_cfg80211_get_txpower,
.set_power_mgmt = iwm_cfg80211_set_power_mgmt,
+ .set_pmksa = iwm_cfg80211_set_pmksa,
+ .del_pmksa = iwm_cfg80211_del_pmksa,
+ .flush_pmksa = iwm_cfg80211_flush_pmksa,
};
static const u32 cipher_suites[] = {
@@ -786,6 +816,7 @@ struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
set_wiphy_dev(wdev->wiphy, dev);
wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
+ wdev->wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS;
wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
index 46ca7c5..bd06307 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.c
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -960,3 +960,25 @@ int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm,
sizeof(struct iwm_umac_cmd_stop_resume_tx));
}
+
+int iwm_send_pmkid_update(struct iwm_priv *iwm,
+ struct cfg80211_pmksa *pmksa, u32 command)
+{
+ struct iwm_umac_pmkid_update update;
+ int ret;
+
+ memset(&update, 0, sizeof(struct iwm_umac_pmkid_update));
+
+ update.command = cpu_to_le32(command);
+ memcpy(&update.bssid, pmksa->bssid, ETH_ALEN);
+ memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
+
+ ret = iwm_send_wifi_if_cmd(iwm, &update,
+ sizeof(struct iwm_umac_pmkid_update), 0);
+ if (ret) {
+ IWM_ERR(iwm, "PMKID update command failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h
index 95cdf94..06af055 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.h
+++ b/drivers/net/wireless/iwmc3200wifi/commands.h
@@ -458,6 +458,17 @@ struct iwm_umac_cmd_stop_resume_tx {
u16 reserved;
} __attribute__ ((packed));
+#define IWM_CMD_PMKID_ADD 1
+#define IWM_CMD_PMKID_DEL 2
+#define IWM_CMD_PMKID_FLUSH 3
+
+struct iwm_umac_pmkid_update {
+ __le32 command;
+ u8 bssid[ETH_ALEN];
+ __le16 reserved;
+ u8 pmkid[WLAN_PMKID_LEN];
+} __attribute__ ((packed));
+
/* LMAC commands */
int iwm_read_mac(struct iwm_priv *iwm, u8 *mac);
int iwm_send_prio_table(struct iwm_priv *iwm);
@@ -488,6 +499,8 @@ int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len);
int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm,
struct iwm_umac_notif_stop_resume_tx *ntf);
+int iwm_send_pmkid_update(struct iwm_priv *iwm,
+ struct cfg80211_pmksa *pmksa, u32 command);
/* UDMA commands */
int iwm_target_reset(struct iwm_priv *iwm);
diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h
index 70094bf..7f54a14 100644
--- a/drivers/net/wireless/iwmc3200wifi/umac.h
+++ b/drivers/net/wireless/iwmc3200wifi/umac.h
@@ -298,6 +298,7 @@ struct iwm_udma_out_wifi_hdr {
#define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID 0x1B
#define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE 0x1C
#define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS 0x1E
+#define UMAC_WIFI_IF_CMD_PMKID_UPDATE 0x1F
#define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER 0x20
/* UMAC WiFi interface ports */
@@ -771,6 +772,7 @@ struct iwm_umac_notif_stop_resume_tx {
__le16 stop_resume_tid_msk; /* tid bitmask */
} __attribute__ ((packed));
+#define UMAC_MAX_NUM_PMKIDS 4
/* WiFi interface wrapper header */
struct iwm_umac_wifi_if {
--
1.6.3.3
--
Intel Open Source Technology Centre
http://oss.intel.com/
This is an interface to set, delete and flush PMKIDs through nl80211.
Main users would be fullmac devices which firmwares are capable of
generating the RSN IEs for the re-association requests, e.g. iwmc3200wifi.
Signed-off-by: Samuel Ortiz <[email protected]>
---
v2: Added NL80211_ATTR_MAX_NUM_PMKIDS attribute
include/linux/ieee80211.h | 2 +
include/linux/nl80211.h | 11 ++++
include/net/cfg80211.h | 28 ++++++++++
net/wireless/nl80211.c | 120 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 161 insertions(+), 0 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index afa8e0a..d9724a2 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1266,6 +1266,8 @@ enum ieee80211_sa_query_action {
#define WLAN_MAX_KEY_LEN 32
+#define WLAN_PMKID_LEN 16
+
/**
* ieee80211_get_qos_ctl - get pointer to qos control bytes
* @hdr: the frame
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 45db17f..da8ea2e 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -349,6 +349,10 @@ enum nl80211_commands {
NL80211_CMD_GET_SURVEY,
NL80211_CMD_NEW_SURVEY_RESULTS,
+ NL80211_CMD_SET_PMKSA,
+ NL80211_CMD_DEL_PMKSA,
+ NL80211_CMD_FLUSH_PMKSA,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -598,6 +602,10 @@ enum nl80211_commands {
* the survey response for %NL80211_CMD_GET_SURVEY, nested attribute
* containing info as possible, see &enum survey_info.
*
+ * @NL80211_ATTR_PMKID: PMK material for PMKSA caching.
+ * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can
+ * cache, a wiphy attribute.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -732,6 +740,9 @@ enum nl80211_attrs {
NL80211_ATTR_SURVEY_INFO,
+ NL80211_ATTR_PMKID,
+ NL80211_ATTR_MAX_NUM_PMKIDS,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index a6492e9..0884b9a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -871,6 +871,19 @@ struct cfg80211_bitrate_mask {
u32 fixed; /* fixed bitrate, 0 == not fixed */
u32 maxrate; /* in kbps, 0 == no limit */
};
+/**
+ * struct cfg80211_pmksa - PMK Security Association
+ *
+ * This structure is passed to the set/del_pmksa() method for PMKSA
+ * caching.
+ *
+ * @bssid: The AP's BSSID.
+ * @pmkid: The PMK material itself.
+ */
+struct cfg80211_pmksa {
+ u8 *bssid;
+ u8 *pmkid;
+};
/**
* struct cfg80211_ops - backend description for wireless configuration
@@ -976,6 +989,13 @@ struct cfg80211_bitrate_mask {
* @dump_survey: get site survey information.
*
* @testmode_cmd: run a test mode command
+ *
+ * @set_pmksa: Cache a PMKID for a BSSID. This is mostly useful for fullmac
+ * devices running firmwares capable of generating the (re) association
+ * RSN IE. It allows for faster roaming between WPA2 BSSIDs.
+ * @del_pmksa: Delete a cached PMKID.
+ * @flush_pmksa: Flush all cached PMKIDs.
+ *
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy);
@@ -1097,6 +1117,12 @@ struct cfg80211_ops {
int (*dump_survey)(struct wiphy *wiphy, struct net_device *netdev,
int idx, struct survey_info *info);
+ int (*set_pmksa)(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa);
+ int (*del_pmksa)(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa);
+ int (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev);
+
/* some temporary stuff to finish wext */
int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout);
@@ -1195,6 +1221,8 @@ struct wiphy {
char fw_version[ETHTOOL_BUSINFO_LEN];
u32 hw_version;
+ u8 max_num_pmkids;
+
/* If multiple wiphys are registered and you're handed e.g.
* a regular netdev with assigned ieee80211_ptr, you won't
* know whether it points to a wiphy your driver has registered
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 149539a..a602843 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -139,6 +139,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
[NL80211_ATTR_PID] = { .type = NLA_U32 },
[NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
+ [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
+ .len = WLAN_PMKID_LEN },
};
/* policy for the attributes */
@@ -450,6 +452,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
sizeof(u32) * dev->wiphy.n_cipher_suites,
dev->wiphy.cipher_suites);
+ NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
+ dev->wiphy.max_num_pmkids);
+
nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
if (!nl_modes)
goto nla_put_failure;
@@ -561,6 +566,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
CMD(deauth, DEAUTHENTICATE);
CMD(disassoc, DISASSOCIATE);
CMD(join_ibss, JOIN_IBSS);
+ CMD(set_pmksa, SET_PMKSA);
+ CMD(del_pmksa, DEL_PMKSA);
+ CMD(flush_pmksa, FLUSH_PMKSA);
if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
i++;
NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
@@ -4221,6 +4229,99 @@ static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev;
+ int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_pmksa *pmksa) = NULL;
+ int err;
+ struct net_device *dev;
+ struct cfg80211_pmksa pmksa;
+
+ memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_PMKID])
+ return -EINVAL;
+
+ rtnl_lock();
+
+ err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+ if (err)
+ goto out_rtnl;
+
+ pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
+ pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ switch (info->genlhdr->cmd) {
+ case NL80211_CMD_SET_PMKSA:
+ rdev_ops = rdev->ops->set_pmksa;
+ break;
+ case NL80211_CMD_DEL_PMKSA:
+ rdev_ops = rdev->ops->del_pmksa;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ if (!rdev_ops) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = rdev_ops(&rdev->wiphy, dev, &pmksa);
+
+ out:
+ cfg80211_unlock_rdev(rdev);
+ dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
+ return err;
+}
+
+static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev;
+ int err;
+ struct net_device *dev;
+
+ rtnl_lock();
+
+ err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+ if (err)
+ goto out_rtnl;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!rdev->ops->flush_pmksa) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = rdev->ops->flush_pmksa(&rdev->wiphy, dev);
+
+ out:
+ cfg80211_unlock_rdev(rdev);
+ dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
+ return err;
+
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -4465,6 +4566,25 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.dumpit = nl80211_dump_survey,
},
+ {
+ .cmd = NL80211_CMD_SET_PMKSA,
+ .doit = nl80211_setdel_pmksa,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_PMKSA,
+ .doit = nl80211_setdel_pmksa,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_FLUSH_PMKSA,
+ .doit = nl80211_flush_pmksa,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
.name = "mlme",
--
1.6.3.3
--
Intel Open Source Technology Centre
http://oss.intel.com/