blob: e80fe0b1a363d03081473fad3bb696249b72b891 [file] [log] [blame]
/*
* Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC : wlan_hdd_disa.c
*
* WLAN Host Device Driver file for DISA certification
*
*/
#include "wlan_hdd_disa.h"
#include "wlan_disa_ucfg_api.h"
#include "wlan_hdd_request_manager.h"
#include "sme_api.h"
#include <qca_vendor.h>
#define WLAN_WAIT_TIME_ENCRYPT_DECRYPT 1000
/**
* hdd_encrypt_decrypt_msg_context - hdd encrypt/decrypt message context
* @status: status of response. 0: no error, -ENOMEM: unable to allocate
* memory for the response payload
* @request: encrypt/decrypt request
* @response: encrypt/decrypt response
*/
struct hdd_encrypt_decrypt_msg_context {
int status;
struct disa_encrypt_decrypt_req_params request;
struct disa_encrypt_decrypt_resp_params response;
};
/**
* hdd_encrypt_decrypt_msg_cb () - encrypt/decrypt response message handler
* @cookie: hdd request cookie
* @resp: encrypt/decrypt response parameters
*
* Return: none
*/
static void hdd_encrypt_decrypt_msg_cb(void *cookie,
struct disa_encrypt_decrypt_resp_params *resp)
{
struct hdd_request *request;
struct hdd_encrypt_decrypt_msg_context *context;
ENTER();
if (!resp) {
hdd_err("rsp params is NULL");
return;
}
request = hdd_request_get(cookie);
if (!request) {
hdd_err("Obsolete request");
return;
}
print_hex_dump(KERN_INFO, "Data in hdd_encrypt_decrypt_msg_cb: ",
DUMP_PREFIX_NONE, 16, 1,
resp->data,
resp->data_len, 0);
hdd_debug("vdev_id: %d status:%d data_length: %d",
resp->vdev_id,
resp->status,
resp->data_len);
context = hdd_request_priv(request);
context->response = *resp;
context->status = 0;
if (resp->data_len) {
context->response.data =
qdf_mem_malloc(sizeof(uint8_t) *
resp->data_len);
if (!context->response.data) {
hdd_err("memory allocation failed");
context->status = -ENOMEM;
} else {
qdf_mem_copy(context->response.data,
resp->data,
resp->data_len);
}
} else {
/* make sure we don't have a rogue pointer */
context->response.data = NULL;
}
hdd_request_complete(request);
hdd_request_put(request);
EXIT();
}
/**
* hdd_post_encrypt_decrypt_msg_rsp () - send encrypt/decrypt data to user space
* @encrypt_decrypt_rsp_params: encrypt/decrypt response parameters
*
* Return: none
*/
static int hdd_post_encrypt_decrypt_msg_rsp(struct hdd_context *hdd_ctx,
struct disa_encrypt_decrypt_resp_params *resp)
{
struct sk_buff *skb;
uint32_t nl_buf_len;
ENTER();
nl_buf_len = resp->data_len + NLA_HDRLEN;
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
return -ENOMEM;
}
if (resp->data_len) {
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA,
resp->data_len, resp->data)) {
hdd_err("put fail");
goto nla_put_failure;
}
}
cfg80211_vendor_cmd_reply(skb);
EXIT();
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
static const struct nla_policy
encrypt_decrypt_policy[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION] = {
.type = NLA_FLAG},
[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID] = {
.type = NLA_U8},
};
/**
* hdd_fill_encrypt_decrypt_params () - parses data from user space
* and fills encrypt/decrypt parameters
* @encrypt_decrypt_params: encrypt/decrypt request parameters
* @adapter : adapter context
* @data: Pointer to data
* @data_len: Data length
*
Return: 0 on success, negative errno on failure
*/
static int
hdd_fill_encrypt_decrypt_params(struct disa_encrypt_decrypt_req_params
*encrypt_decrypt_params,
struct hdd_adapter *adapter,
const void *data,
int data_len)
{
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX + 1];
uint8_t len, mac_hdr_len;
uint8_t *tmp;
uint8_t fc[2];
if (hdd_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX,
data, data_len, encrypt_decrypt_policy)) {
hdd_err("Invalid ATTR");
return -EINVAL;
}
encrypt_decrypt_params->vdev_id = adapter->session_id;
hdd_debug("vdev_id: %d", encrypt_decrypt_params->vdev_id);
if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION]) {
hdd_err("attr flag NEEDS_DECRYPTION not present");
encrypt_decrypt_params->key_flag = WMI_ENCRYPT;
} else {
hdd_err("attr flag NEEDS_DECRYPTION present");
encrypt_decrypt_params->key_flag = WMI_DECRYPT;
}
hdd_debug("Key flag: %d", encrypt_decrypt_params->key_flag);
if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID]) {
hdd_err("attr key id failed");
return -EINVAL;
}
encrypt_decrypt_params->key_idx = nla_get_u8(tb
[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID]);
hdd_debug("Key Idx: %d", encrypt_decrypt_params->key_idx);
if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER]) {
hdd_err("attr Cipher failed");
return -EINVAL;
}
encrypt_decrypt_params->key_cipher = nla_get_u32(tb
[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER]);
hdd_debug("key_cipher: %d", encrypt_decrypt_params->key_cipher);
if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK]) {
hdd_err("attr TK failed");
return -EINVAL;
}
encrypt_decrypt_params->key_len =
nla_len(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK]);
if (!encrypt_decrypt_params->key_len) {
hdd_err("Invalid TK length");
return -EINVAL;
}
hdd_debug("Key len: %d", encrypt_decrypt_params->key_len);
if (encrypt_decrypt_params->key_len > SIR_MAC_MAX_KEY_LENGTH)
encrypt_decrypt_params->key_len = SIR_MAC_MAX_KEY_LENGTH;
tmp = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK]);
qdf_mem_copy(encrypt_decrypt_params->key_data, tmp,
encrypt_decrypt_params->key_len);
print_hex_dump(KERN_INFO, "Key : ", DUMP_PREFIX_NONE, 16, 1,
&encrypt_decrypt_params->key_data,
encrypt_decrypt_params->key_len, 0);
if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN]) {
hdd_err("attr PN failed");
return -EINVAL;
}
len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN]);
if (!len || len > sizeof(encrypt_decrypt_params->pn)) {
hdd_err("Invalid PN length %u", len);
return -EINVAL;
}
tmp = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN]);
qdf_mem_copy(encrypt_decrypt_params->pn, tmp, len);
print_hex_dump(KERN_INFO, "PN received : ", DUMP_PREFIX_NONE, 16, 1,
&encrypt_decrypt_params->pn, len, 0);
if (!tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA]) {
hdd_err("attr header failed");
return -EINVAL;
}
len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA]);
if (len < MIN_MAC_HEADER_LEN) {
hdd_err("Invalid header and payload length %u", len);
return -EINVAL;
}
hdd_debug("Header and Payload length: %d", len);
tmp = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA]);
print_hex_dump(KERN_INFO, "Header and Payload received: ",
DUMP_PREFIX_NONE, 16, 1,
tmp, len, 0);
mac_hdr_len = MIN_MAC_HEADER_LEN;
/*
* Check to find out address 4. Address 4 is present if ToDS and FromDS
* are 1 and data representation is little endian.
*/
fc[1] = *tmp;
fc[0] = *(tmp + 1);
if ((fc[0] & 0x03) == 0x03) {
hdd_err("Address 4 is present");
mac_hdr_len += IEEE80211_ADDR_LEN;
}
/*
* Check to find out Qos control field. Qos control field is present
* if msb of subtype field is 1 and data representation is
* little endian.
*/
if (fc[1] & 0x80) {
hdd_err("Qos control is present");
mac_hdr_len += QOS_CONTROL_LEN;
}
hdd_debug("mac_hdr_len: %d", mac_hdr_len);
if (len < mac_hdr_len) {
hdd_err("Invalid header and payload length %u", len);
return -EINVAL;
}
qdf_mem_copy(encrypt_decrypt_params->mac_header,
tmp, mac_hdr_len);
print_hex_dump(KERN_INFO, "Header received in request: ",
DUMP_PREFIX_NONE, 16, 1,
encrypt_decrypt_params->mac_header,
mac_hdr_len, 0);
encrypt_decrypt_params->data_len =
len - mac_hdr_len;
hdd_debug("Payload length: %d", encrypt_decrypt_params->data_len);
if (encrypt_decrypt_params->data_len) {
encrypt_decrypt_params->data =
qdf_mem_malloc(sizeof(uint8_t) *
encrypt_decrypt_params->data_len);
if (encrypt_decrypt_params->data == NULL) {
hdd_err("memory allocation failed");
return -ENOMEM;
}
qdf_mem_copy(encrypt_decrypt_params->data,
tmp + mac_hdr_len,
encrypt_decrypt_params->data_len);
print_hex_dump(KERN_INFO, "Data received in request: ",
DUMP_PREFIX_NONE, 16, 1,
encrypt_decrypt_params->data,
encrypt_decrypt_params->data_len, 0);
}
return 0;
}
static void hdd_encrypt_decrypt_context_dealloc(void *priv)
{
struct hdd_encrypt_decrypt_msg_context *context = priv;
qdf_mem_free(context->request.data);
qdf_mem_free(context->response.data);
}
/**
* hdd_encrypt_decrypt_msg () - process encrypt/decrypt message
* @adapter : adapter context
* @hdd_ctx: hdd context
* @data: Pointer to data
* @data_len: Data length
*
Return: 0 on success, negative errno on failure
*/
static int hdd_encrypt_decrypt_msg(struct hdd_adapter *adapter,
struct hdd_context *hdd_ctx,
const void *data,
int data_len)
{
QDF_STATUS qdf_status;
int ret;
void *cookie;
struct hdd_request *request;
struct hdd_encrypt_decrypt_msg_context *context;
static const struct hdd_request_params params = {
.priv_size = sizeof(*context),
.timeout_ms = WLAN_WAIT_TIME_ENCRYPT_DECRYPT,
.dealloc = hdd_encrypt_decrypt_context_dealloc,
};
request = hdd_request_alloc(&params);
if (!request) {
hdd_err("Request allocation failure");
return -ENOMEM;
}
context = hdd_request_priv(request);
ret = hdd_fill_encrypt_decrypt_params(&context->request, adapter,
data, data_len);
if (ret)
goto cleanup;
cookie = hdd_request_cookie(request);
qdf_status = ucfg_disa_encrypt_decrypt_req(hdd_ctx->hdd_psoc,
&context->request,
hdd_encrypt_decrypt_msg_cb,
cookie);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("Unable to post encrypt/decrypt message");
ret = -EINVAL;
goto cleanup;
}
ret = hdd_request_wait_for_response(request);
if (ret) {
hdd_err("Target response timed out");
goto cleanup;
}
ret = context->status;
if (ret) {
hdd_err("Target response processing failed");
goto cleanup;
}
ret = hdd_post_encrypt_decrypt_msg_rsp(hdd_ctx, &context->response);
if (ret)
hdd_err("Failed to post encrypt/decrypt message response");
cleanup:
hdd_request_put(request);
EXIT();
return ret;
}
/**
* __wlan_hdd_cfg80211_encrypt_decrypt_msg () - Encrypt/Decrypt msg
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_cfg80211_encrypt_decrypt_msg(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = NULL;
int ret;
ENTER_DEV(dev);
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
adapter = WLAN_HDD_GET_PRIV_PTR(dev);
ret = hdd_encrypt_decrypt_msg(adapter, hdd_ctx, data, data_len);
return ret;
}
/**
* wlan_hdd_cfg80211_encrypt_decrypt_msg () - Encrypt/Decrypt msg
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
int wlan_hdd_cfg80211_encrypt_decrypt_msg(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_encrypt_decrypt_msg(wiphy, wdev,
data, data_len);
cds_ssr_unprotect(__func__);
return ret;
}