mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-26 18:59:22 +01:00
ap: Write extra frame IEs from the user
Add an API for the ap.h users to add extra IEs to outgoing management frames: beacons, etc.
This commit is contained in:
parent
f7b5bd4a79
commit
18a63f91fd
109
src/ap.c
109
src/ap.c
@ -467,11 +467,48 @@ static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
|
|||||||
rsn->group_cipher = ap->group_cipher;
|
rsn->group_cipher = ap->group_cipher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t ap_get_extra_ies_len(struct ap_state *ap,
|
||||||
|
enum mpdu_management_subtype type,
|
||||||
|
const struct mmpdu_header *client_frame,
|
||||||
|
size_t client_frame_len)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
if (ap->ops->get_extra_ies_len)
|
||||||
|
len += ap->ops->get_extra_ies_len(type, client_frame,
|
||||||
|
client_frame_len,
|
||||||
|
ap->user_data);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ap_write_extra_ies(struct ap_state *ap,
|
||||||
|
enum mpdu_management_subtype type,
|
||||||
|
const struct mmpdu_header *client_frame,
|
||||||
|
size_t client_frame_len,
|
||||||
|
uint8_t *out_buf)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
if (ap->ops->write_extra_ies)
|
||||||
|
len += ap->ops->write_extra_ies(type,
|
||||||
|
client_frame, client_frame_len,
|
||||||
|
out_buf + len, ap->user_data);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build a Beacon frame or a Probe Response frame's header and body until
|
* Build a Beacon frame or a Probe Response frame's header and body until
|
||||||
* the TIM IE. Except for the optional TIM IE which is inserted by the
|
* the TIM IE. Except for the optional TIM IE which is inserted by the
|
||||||
* kernel when needed, our contents for both frames are the same.
|
* kernel when needed, our contents for both frames are the same.
|
||||||
* See Beacon format in 8.3.3.2 and Probe Response format in 8.3.3.10.
|
* See Beacon format in 8.3.3.2 and Probe Response format in 8.3.3.10.
|
||||||
|
*
|
||||||
|
* 802.11-2016, Section 9.4.2.1:
|
||||||
|
* "The frame body components specified for many management subtypes result
|
||||||
|
* in elements ordered by ascending values of the Element ID field and then
|
||||||
|
* the Element ID Extension field (when present), with the exception of the
|
||||||
|
* MIC Management element (9.4.2.55)."
|
||||||
*/
|
*/
|
||||||
static size_t ap_build_beacon_pr_head(struct ap_state *ap,
|
static size_t ap_build_beacon_pr_head(struct ap_state *ap,
|
||||||
enum mpdu_management_subtype stype,
|
enum mpdu_management_subtype stype,
|
||||||
@ -537,8 +574,10 @@ static size_t ap_build_beacon_pr_head(struct ap_state *ap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Beacon / Probe Response frame portion after the TIM IE */
|
/* Beacon / Probe Response frame portion after the TIM IE */
|
||||||
static size_t ap_build_beacon_pr_tail(struct ap_state *ap, bool pr,
|
static size_t ap_build_beacon_pr_tail(struct ap_state *ap,
|
||||||
uint8_t *out_buf)
|
enum mpdu_management_subtype stype,
|
||||||
|
const struct mmpdu_header *req,
|
||||||
|
size_t req_len, uint8_t *out_buf)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
struct ie_rsn_info rsn;
|
struct ie_rsn_info rsn;
|
||||||
@ -556,7 +595,7 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap, bool pr,
|
|||||||
len = 2 + out_buf[1];
|
len = 2 + out_buf[1];
|
||||||
|
|
||||||
/* WSC IE */
|
/* WSC IE */
|
||||||
if (pr) {
|
if (stype == MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE) {
|
||||||
struct wsc_probe_response wsc_pr = {};
|
struct wsc_probe_response wsc_pr = {};
|
||||||
|
|
||||||
wsc_pr.version2 = true;
|
wsc_pr.version2 = true;
|
||||||
@ -634,6 +673,7 @@ static size_t ap_build_beacon_pr_tail(struct ap_state *ap, bool pr,
|
|||||||
len += wsc_ie_size;
|
len += wsc_ie_size;
|
||||||
l_free(wsc_ie);
|
l_free(wsc_ie);
|
||||||
|
|
||||||
|
len += ap_write_extra_ies(ap, stype, req, req_len, out_buf + len);
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,7 +688,11 @@ static void ap_set_beacon_cb(struct l_genl_msg *msg, void *user_data)
|
|||||||
void ap_update_beacon(struct ap_state *ap)
|
void ap_update_beacon(struct ap_state *ap)
|
||||||
{
|
{
|
||||||
struct l_genl_msg *cmd;
|
struct l_genl_msg *cmd;
|
||||||
uint8_t head[256], tail[256];
|
uint8_t head[256];
|
||||||
|
L_AUTO_FREE_VAR(uint8_t *, tail) =
|
||||||
|
l_malloc(256 + ap_get_extra_ies_len(ap,
|
||||||
|
MPDU_MANAGEMENT_SUBTYPE_BEACON,
|
||||||
|
NULL, 0));
|
||||||
size_t head_len, tail_len;
|
size_t head_len, tail_len;
|
||||||
uint64_t wdev_id = netdev_get_wdev_id(ap->netdev);
|
uint64_t wdev_id = netdev_get_wdev_id(ap->netdev);
|
||||||
static const uint8_t bcast_addr[6] = {
|
static const uint8_t bcast_addr[6] = {
|
||||||
@ -660,7 +704,8 @@ void ap_update_beacon(struct ap_state *ap)
|
|||||||
|
|
||||||
head_len = ap_build_beacon_pr_head(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
|
head_len = ap_build_beacon_pr_head(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
|
||||||
bcast_addr, head, sizeof(head));
|
bcast_addr, head, sizeof(head));
|
||||||
tail_len = ap_build_beacon_pr_tail(ap, false, tail);
|
tail_len = ap_build_beacon_pr_tail(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
|
||||||
|
NULL, 0, tail);
|
||||||
if (L_WARN_ON(!head_len || !tail_len))
|
if (L_WARN_ON(!head_len || !tail_len))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1198,10 +1243,15 @@ static void ap_fail_assoc_resp_cb(int err, void *user_data)
|
|||||||
static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
|
static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
|
||||||
const uint8_t *dest,
|
const uint8_t *dest,
|
||||||
enum mmpdu_reason_code status_code,
|
enum mmpdu_reason_code status_code,
|
||||||
bool reassoc, frame_xchg_cb_t callback)
|
bool reassoc, const struct mmpdu_header *req,
|
||||||
|
size_t req_len, frame_xchg_cb_t callback)
|
||||||
{
|
{
|
||||||
const uint8_t *addr = netdev_get_address(ap->netdev);
|
const uint8_t *addr = netdev_get_address(ap->netdev);
|
||||||
uint8_t mpdu_buf[128];
|
enum mpdu_management_subtype stype = reassoc ?
|
||||||
|
MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE :
|
||||||
|
MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE;
|
||||||
|
L_AUTO_FREE_VAR(uint8_t *, mpdu_buf) =
|
||||||
|
l_malloc(128 + ap_get_extra_ies_len(ap, stype, req, req_len));
|
||||||
struct mmpdu_header *mpdu = (void *) mpdu_buf;
|
struct mmpdu_header *mpdu = (void *) mpdu_buf;
|
||||||
struct mmpdu_association_response *resp;
|
struct mmpdu_association_response *resp;
|
||||||
size_t ies_len = 0;
|
size_t ies_len = 0;
|
||||||
@ -1213,9 +1263,7 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
|
|||||||
/* Header */
|
/* Header */
|
||||||
mpdu->fc.protocol_version = 0;
|
mpdu->fc.protocol_version = 0;
|
||||||
mpdu->fc.type = MPDU_TYPE_MANAGEMENT;
|
mpdu->fc.type = MPDU_TYPE_MANAGEMENT;
|
||||||
mpdu->fc.subtype = reassoc ?
|
mpdu->fc.subtype = stype;
|
||||||
MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE :
|
|
||||||
MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE;
|
|
||||||
memcpy(mpdu->address_1, dest, 6); /* DA */
|
memcpy(mpdu->address_1, dest, 6); /* DA */
|
||||||
memcpy(mpdu->address_2, addr, 6); /* SA */
|
memcpy(mpdu->address_2, addr, 6); /* SA */
|
||||||
memcpy(mpdu->address_3, addr, 6); /* BSSID */
|
memcpy(mpdu->address_3, addr, 6); /* BSSID */
|
||||||
@ -1277,6 +1325,9 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
|
|||||||
l_free(wsc_ie);
|
l_free(wsc_ie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ies_len += ap_write_extra_ies(ap, stype, req, req_len,
|
||||||
|
resp->ies + ies_len);
|
||||||
|
|
||||||
send_frame:
|
send_frame:
|
||||||
return ap_send_mgmt_frame(ap, mpdu, resp->ies + ies_len - mpdu_buf,
|
return ap_send_mgmt_frame(ap, mpdu, resp->ies + ies_len - mpdu_buf,
|
||||||
callback, sta);
|
callback, sta);
|
||||||
@ -1352,8 +1403,9 @@ static int ap_parse_supported_rates(struct ie_tlv_iter *iter,
|
|||||||
*/
|
*/
|
||||||
static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
|
static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
|
||||||
const struct mmpdu_field_capability *capability,
|
const struct mmpdu_field_capability *capability,
|
||||||
uint16_t listen_interval, const uint8_t *ies,
|
uint16_t listen_interval,
|
||||||
size_t ies_len)
|
const uint8_t *ies, size_t ies_len,
|
||||||
|
const struct mmpdu_header *req)
|
||||||
{
|
{
|
||||||
struct ap_state *ap = sta->ap;
|
struct ap_state *ap = sta->ap;
|
||||||
const char *ssid = NULL;
|
const char *ssid = NULL;
|
||||||
@ -1542,6 +1594,8 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, 0, reassoc,
|
sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, 0, reassoc,
|
||||||
|
req, (void *) ies + ies_len -
|
||||||
|
(void *) req,
|
||||||
ap_success_assoc_resp_cb);
|
ap_success_assoc_resp_cb);
|
||||||
if (!sta->assoc_resp_cmd_id)
|
if (!sta->assoc_resp_cmd_id)
|
||||||
l_error("Sending success (Re)Association Response failed");
|
l_error("Sending success (Re)Association Response failed");
|
||||||
@ -1573,6 +1627,7 @@ bad_frame:
|
|||||||
l_free(wsc_data);
|
l_free(wsc_data);
|
||||||
|
|
||||||
if (!ap_assoc_resp(ap, sta, sta->addr, err, reassoc,
|
if (!ap_assoc_resp(ap, sta, sta->addr, err, reassoc,
|
||||||
|
req, (void *) ies + ies_len - (void *) req,
|
||||||
ap_fail_assoc_resp_cb))
|
ap_fail_assoc_resp_cb))
|
||||||
l_error("Sending error (Re)Association Response failed");
|
l_error("Sending error (Re)Association Response failed");
|
||||||
}
|
}
|
||||||
@ -1597,7 +1652,8 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
|
|||||||
if (!sta) {
|
if (!sta) {
|
||||||
if (!ap_assoc_resp(ap, NULL, from,
|
if (!ap_assoc_resp(ap, NULL, from,
|
||||||
MMPDU_REASON_CODE_STA_REQ_ASSOC_WITHOUT_AUTH,
|
MMPDU_REASON_CODE_STA_REQ_ASSOC_WITHOUT_AUTH,
|
||||||
false, ap_fail_assoc_resp_cb))
|
false, hdr, body + body_len - (void *) hdr,
|
||||||
|
ap_fail_assoc_resp_cb))
|
||||||
l_error("Sending error Association Response failed");
|
l_error("Sending error Association Response failed");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1605,7 +1661,7 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
|
|||||||
|
|
||||||
ap_assoc_reassoc(sta, false, &req->capability,
|
ap_assoc_reassoc(sta, false, &req->capability,
|
||||||
L_LE16_TO_CPU(req->listen_interval),
|
L_LE16_TO_CPU(req->listen_interval),
|
||||||
req->ies, body_len - sizeof(*req));
|
req->ies, body_len - sizeof(*req), hdr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 802.11-2016 9.3.3.8 */
|
/* 802.11-2016 9.3.3.8 */
|
||||||
@ -1639,11 +1695,13 @@ static void ap_reassoc_req_cb(const struct mmpdu_header *hdr, const void *body,
|
|||||||
|
|
||||||
ap_assoc_reassoc(sta, true, &req->capability,
|
ap_assoc_reassoc(sta, true, &req->capability,
|
||||||
L_LE16_TO_CPU(req->listen_interval),
|
L_LE16_TO_CPU(req->listen_interval),
|
||||||
req->ies, body_len - sizeof(*req));
|
req->ies, body_len - sizeof(*req), hdr);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bad_frame:
|
bad_frame:
|
||||||
if (!ap_assoc_resp(ap, NULL, from, err, true, ap_fail_assoc_resp_cb))
|
if (!ap_assoc_resp(ap, NULL, from, err, true,
|
||||||
|
hdr, body + body_len - (void *) hdr,
|
||||||
|
ap_fail_assoc_resp_cb))
|
||||||
l_error("Sending error Reassociation Response failed");
|
l_error("Sending error Reassociation Response failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1786,7 +1844,10 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
|
|||||||
struct ie_tlv_iter iter;
|
struct ie_tlv_iter iter;
|
||||||
const uint8_t *bssid = netdev_get_address(ap->netdev);
|
const uint8_t *bssid = netdev_get_address(ap->netdev);
|
||||||
bool match = false;
|
bool match = false;
|
||||||
uint8_t resp[512];
|
L_AUTO_FREE_VAR(uint8_t *, resp) =
|
||||||
|
l_malloc(512 + ap_get_extra_ies_len(ap,
|
||||||
|
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE, hdr,
|
||||||
|
body + body_len - (void *) hdr));
|
||||||
uint8_t *wsc_data;
|
uint8_t *wsc_data;
|
||||||
ssize_t wsc_data_len;
|
ssize_t wsc_data_len;
|
||||||
|
|
||||||
@ -1876,7 +1937,10 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
|
|||||||
len = ap_build_beacon_pr_head(ap,
|
len = ap_build_beacon_pr_head(ap,
|
||||||
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
|
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
|
||||||
hdr->address_2, resp, sizeof(resp));
|
hdr->address_2, resp, sizeof(resp));
|
||||||
len += ap_build_beacon_pr_tail(ap, true, resp + len);
|
len += ap_build_beacon_pr_tail(ap,
|
||||||
|
MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE,
|
||||||
|
hdr, body + body_len - (void *) hdr,
|
||||||
|
resp + len);
|
||||||
|
|
||||||
ap_send_mgmt_frame(ap, (struct mmpdu_header *) resp, len,
|
ap_send_mgmt_frame(ap, (struct mmpdu_header *) resp, len,
|
||||||
ap_probe_resp_cb, NULL);
|
ap_probe_resp_cb, NULL);
|
||||||
@ -2109,7 +2173,11 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
|
|||||||
{
|
{
|
||||||
struct l_genl_msg *cmd;
|
struct l_genl_msg *cmd;
|
||||||
|
|
||||||
uint8_t head[256], tail[256];
|
uint8_t head[256];
|
||||||
|
L_AUTO_FREE_VAR(uint8_t *, tail) =
|
||||||
|
l_malloc(256 + ap_get_extra_ies_len(ap,
|
||||||
|
MPDU_MANAGEMENT_SUBTYPE_BEACON,
|
||||||
|
NULL, 0));
|
||||||
size_t head_len, tail_len;
|
size_t head_len, tail_len;
|
||||||
|
|
||||||
uint32_t dtim_period = 3;
|
uint32_t dtim_period = 3;
|
||||||
@ -2139,7 +2207,8 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
|
|||||||
|
|
||||||
head_len = ap_build_beacon_pr_head(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
|
head_len = ap_build_beacon_pr_head(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
|
||||||
bcast_addr, head, sizeof(head));
|
bcast_addr, head, sizeof(head));
|
||||||
tail_len = ap_build_beacon_pr_tail(ap, false, tail);
|
tail_len = ap_build_beacon_pr_tail(ap, MPDU_MANAGEMENT_SUBTYPE_BEACON,
|
||||||
|
NULL, 0, tail);
|
||||||
|
|
||||||
if (!head_len || !tail_len)
|
if (!head_len || !tail_len)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
23
src/ap.h
23
src/ap.h
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
struct ap_state;
|
struct ap_state;
|
||||||
struct iovec;
|
struct iovec;
|
||||||
|
enum mpdu_management_subtype;
|
||||||
|
|
||||||
enum ap_event_type {
|
enum ap_event_type {
|
||||||
AP_EVENT_START_FAILED,
|
AP_EVENT_START_FAILED,
|
||||||
@ -75,6 +76,28 @@ struct ap_config {
|
|||||||
struct ap_ops {
|
struct ap_ops {
|
||||||
void (*handle_event)(enum ap_event_type type, const void *event_data,
|
void (*handle_event)(enum ap_event_type type, const void *event_data,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
/*
|
||||||
|
* If .write_extra_ies is provided, this callback must return an upper
|
||||||
|
* bound on the buffer space needed for the extra IEs to be sent in
|
||||||
|
* the frame of given type and, if it's not a beacon frame, in
|
||||||
|
* response to a given client frame.
|
||||||
|
*/
|
||||||
|
size_t (*get_extra_ies_len)(enum mpdu_management_subtype type,
|
||||||
|
const struct mmpdu_header *client_frame,
|
||||||
|
size_t client_frame_len,
|
||||||
|
void *user_data);
|
||||||
|
/*
|
||||||
|
* If not null, writes extra IEs to be added to the outgoing frame of
|
||||||
|
* given type and, if it's not a beacon frame, in reponse to a given
|
||||||
|
* client frame. May also react to the extra IEs in that frame.
|
||||||
|
* Returns the number of bytes written which must be less than or
|
||||||
|
* equal to the number returned by .get_extra_ies_len when called
|
||||||
|
* with the same parameters.
|
||||||
|
*/
|
||||||
|
size_t (*write_extra_ies)(enum mpdu_management_subtype type,
|
||||||
|
const struct mmpdu_header *client_frame,
|
||||||
|
size_t client_frame_len,
|
||||||
|
uint8_t *out_buf, void *user_data);
|
||||||
};
|
};
|
||||||
|
|
||||||
void ap_config_free(struct ap_config *config);
|
void ap_config_free(struct ap_config *config);
|
||||||
|
Loading…
Reference in New Issue
Block a user