3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-12-23 06:02:37 +01:00

p2putil: Builders for management frames P2P payloads

This commit is contained in:
Andrew Zaborowski 2019-07-09 03:24:41 +02:00 committed by Denis Kenzior
parent 67f91605eb
commit cd3c0ad155
2 changed files with 638 additions and 0 deletions

View File

@ -1547,3 +1547,628 @@ void p2p_free_presence_resp(struct p2p_presence_resp *data)
{ {
p2p_free_notice_of_absence_attr(&data->notice_of_absence); p2p_free_notice_of_absence_attr(&data->notice_of_absence);
} }
struct p2p_attr_builder {
size_t capacity;
uint8_t *buf;
uint16_t offset;
uint16_t curlen;
};
static void p2p_attr_builder_grow(struct p2p_attr_builder *builder)
{
builder->buf = l_realloc(builder->buf, builder->capacity * 2);
builder->capacity *= 2;
}
/* Section 4.1.1 */
static void p2p_attr_builder_start_attr(struct p2p_attr_builder *builder,
enum p2p_attr type)
{
/* Record previous attribute's length */
if (builder->curlen || builder->offset) {
l_put_le16(builder->curlen, builder->buf + builder->offset + 1);
builder->offset += 3 + builder->curlen;
builder->curlen = 0;
}
if (builder->offset + 3u >= builder->capacity)
p2p_attr_builder_grow(builder);
builder->buf[builder->offset] = type;
}
static void p2p_attr_builder_put_u8(struct p2p_attr_builder *builder, uint8_t v)
{
if (builder->offset + 3u + builder->curlen + 1u >= builder->capacity)
p2p_attr_builder_grow(builder);
builder->buf[builder->offset + 3 + builder->curlen] = v;
builder->curlen += 1;
}
static void p2p_attr_builder_put_u16(struct p2p_attr_builder *builder,
uint16_t v)
{
if (builder->offset + 3u + builder->curlen + 2u >= builder->capacity)
p2p_attr_builder_grow(builder);
l_put_le16(v, builder->buf + builder->offset + 3 + builder->curlen);
builder->curlen += 2;
}
static void p2p_attr_builder_put_be16(struct p2p_attr_builder *builder,
uint16_t v)
{
if (builder->offset + 3u + builder->curlen + 2u >= builder->capacity)
p2p_attr_builder_grow(builder);
l_put_be16(v, builder->buf + builder->offset + 3 + builder->curlen);
builder->curlen += 2;
}
static void p2p_attr_builder_put_u32(struct p2p_attr_builder *builder,
uint32_t v)
{
if (builder->offset + 3u + builder->curlen + 4u >= builder->capacity)
p2p_attr_builder_grow(builder);
l_put_le32(v, builder->buf + builder->offset + 3 + builder->curlen);
builder->curlen += 4;
}
static void p2p_attr_builder_put_bytes(struct p2p_attr_builder *builder,
const void *bytes, size_t size)
{
while (builder->offset + 3u + builder->curlen + size >=
builder->capacity)
p2p_attr_builder_grow(builder);
memcpy(builder->buf + builder->offset + 3 + builder->curlen,
bytes, size);
builder->curlen += size;
}
static void p2p_attr_builder_put_oui(struct p2p_attr_builder *builder,
const uint8_t *oui)
{
if (builder->offset + 3u + builder->curlen + 3u >= builder->capacity)
p2p_attr_builder_grow(builder);
memcpy(builder->buf + builder->offset + 3 + builder->curlen, oui, 3);
builder->curlen += 3;
}
static struct p2p_attr_builder *p2p_attr_builder_new(size_t initial_capacity)
{
struct p2p_attr_builder *builder;
if (initial_capacity == 0)
return NULL;
builder = l_new(struct p2p_attr_builder, 1);
builder->buf = l_malloc(initial_capacity);
builder->capacity = initial_capacity;
return builder;
}
static uint8_t *p2p_attr_builder_free(struct p2p_attr_builder *builder,
bool free_contents, size_t *out_size)
{
uint8_t *ret;
if (builder->curlen > 0 || builder->offset) {
l_put_le16(builder->curlen, builder->buf + builder->offset + 1);
builder->offset += 3 + builder->curlen;
}
if (free_contents) {
l_free(builder->buf);
ret = NULL;
} else
ret = builder->buf;
if (out_size)
*out_size = builder->offset;
l_free(builder);
return ret;
}
static void p2p_build_u8_attr(struct p2p_attr_builder *builder,
enum p2p_attr type, uint8_t value)
{
p2p_attr_builder_start_attr(builder, type);
p2p_attr_builder_put_u8(builder, value);
}
/* Section 4.1.4 */
static void p2p_build_capability(struct p2p_attr_builder *builder,
const struct p2p_capability_attr *attr)
{
/* Always required */
p2p_attr_builder_start_attr(builder, P2P_ATTR_P2P_CAPABILITY);
p2p_attr_builder_put_u8(builder, attr->device_caps);
p2p_attr_builder_put_u8(builder, attr->group_caps);
}
static const uint8_t zero_addr[6];
/* Section 4.1.5, 4.1.9, 4.1.11, ... */
static void p2p_build_addr(struct p2p_attr_builder *builder, bool optional,
enum p2p_attr type, const uint8_t *addr)
{
if (optional && !memcmp(addr, zero_addr, 6))
return;
p2p_attr_builder_start_attr(builder, type);
p2p_attr_builder_put_bytes(builder, addr, 6);
}
/* Section 4.1.6 */
static void p2p_build_go_intent(struct p2p_attr_builder *builder,
uint8_t intent, bool tie_breaker)
{
/* Always required */
p2p_attr_builder_start_attr(builder, P2P_ATTR_GO_INTENT);
p2p_attr_builder_put_u8(builder, tie_breaker | (intent << 1));
}
/* Section 4.1.7 */
static void p2p_build_config_timeout(struct p2p_attr_builder *builder,
bool optional,
const struct p2p_config_timeout_attr *attr)
{
if (optional && !attr->go_config_timeout &&
!attr->client_config_timeout)
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_CONFIGURATION_TIMEOUT);
p2p_attr_builder_put_u8(builder, attr->go_config_timeout);
p2p_attr_builder_put_u8(builder, attr->client_config_timeout);
}
/* Section 4.1.8, 4.1.19, ... */
static void p2p_build_channel(struct p2p_attr_builder *builder, bool optional,
enum p2p_attr type,
const struct p2p_channel_attr *attr)
{
if (optional && !attr->country[0])
return;
p2p_attr_builder_start_attr(builder, type);
p2p_attr_builder_put_bytes(builder, attr->country, 3);
p2p_attr_builder_put_u8(builder, attr->oper_class);
p2p_attr_builder_put_u8(builder, attr->channel_num);
}
/* Section 4.1.10 */
static void p2p_build_extended_listen_timing(struct p2p_attr_builder *builder,
const struct p2p_extended_listen_timing_attr *attr)
{
/* Always optional */
if (!attr->avail_period_ms && !attr->avail_interval_ms)
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_EXTENDED_LISTEN_TIMING);
p2p_attr_builder_put_u16(builder, attr->avail_period_ms);
p2p_attr_builder_put_u16(builder, attr->avail_interval_ms);
}
/* Section 4.1.13 */
static void p2p_build_channel_list(struct p2p_attr_builder *builder,
bool optional,
const struct p2p_channel_list_attr *attr)
{
const struct l_queue_entry *entry;
if (optional && !attr->channel_entries)
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_CHANNEL_LIST);
p2p_attr_builder_put_bytes(builder, attr->country, 3);
for (entry = l_queue_get_entries(attr->channel_entries); entry;
entry = entry->next) {
const struct p2p_channel_entries *entries = entry->data;
p2p_attr_builder_put_u8(builder, entries->oper_class);
p2p_attr_builder_put_u8(builder, entries->n_channels);
p2p_attr_builder_put_bytes(builder, entries->channels,
entries->n_channels);
}
}
/* Section 4.1.14 */
static void p2p_build_notice_of_absence_attr(struct p2p_attr_builder *builder,
bool optional,
const struct p2p_notice_of_absence_attr *attr)
{
const struct l_queue_entry *entry;
if (optional && !attr->ct_window && !attr->descriptors)
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_NOTICE_OF_ABSENCE);
p2p_attr_builder_put_u8(builder, attr->index);
p2p_attr_builder_put_u8(builder,
attr->ct_window | (attr->opp_ps ? 0x80 : 0));
for (entry = l_queue_get_entries(attr->descriptors); entry;
entry = entry->next) {
const struct p2p_notice_of_absence_desc *desc = entry->data;
p2p_attr_builder_put_u8(builder, desc->count_type);
p2p_attr_builder_put_u32(builder, desc->duration);
p2p_attr_builder_put_u32(builder, desc->interval);
p2p_attr_builder_put_u32(builder, desc->start_time);
}
}
static void p2p_build_wsc_device_type(struct p2p_attr_builder *builder,
const struct wsc_primary_device_type *pdt)
{
p2p_attr_builder_put_be16(builder, pdt->category);
p2p_attr_builder_put_oui(builder, pdt->oui);
p2p_attr_builder_put_u8(builder, pdt->oui_type);
p2p_attr_builder_put_be16(builder, pdt->subcategory);
}
/* Section 4.1.15 */
static void p2p_build_device_info(struct p2p_attr_builder *builder,
bool optional,
const struct p2p_device_info_attr *attr)
{
const struct l_queue_entry *entry;
if (optional && !memcmp(attr->device_addr, zero_addr, 6))
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_P2P_DEVICE_INFO);
p2p_attr_builder_put_bytes(builder, attr->device_addr, 6);
p2p_attr_builder_put_be16(builder, attr->wsc_config_methods);
p2p_build_wsc_device_type(builder, &attr->primary_device_type);
p2p_attr_builder_put_u8(builder,
l_queue_length(attr->secondary_device_types));
for (entry = l_queue_get_entries(attr->secondary_device_types); entry;
entry = entry->next)
p2p_build_wsc_device_type(builder, entry->data);
p2p_attr_builder_put_be16(builder, WSC_ATTR_DEVICE_NAME);
p2p_attr_builder_put_be16(builder, strlen(attr->device_name));
p2p_attr_builder_put_bytes(builder, attr->device_name,
strlen(attr->device_name));
}
/* Section 4.1.16 */
static void p2p_build_group_info(struct p2p_attr_builder *builder,
struct l_queue *clients)
{
const struct l_queue_entry *entry;
/* Always optional */
if (!clients)
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_P2P_GROUP_INFO);
for (entry = l_queue_get_entries(clients); entry; entry = entry->next) {
const struct l_queue_entry *entry2;
const struct p2p_client_info_descriptor *desc = entry->data;
p2p_attr_builder_put_bytes(builder, desc->device_addr, 6);
p2p_attr_builder_put_bytes(builder, desc->interface_addr, 6);
p2p_attr_builder_put_u8(builder, desc->device_caps);
p2p_attr_builder_put_be16(builder, desc->wsc_config_methods);
p2p_build_wsc_device_type(builder, &desc->primary_device_type);
p2p_attr_builder_put_u8(builder,
l_queue_length(desc->secondary_device_types));
for (entry2 = l_queue_get_entries(desc->secondary_device_types);
entry2; entry2 = entry->next)
p2p_build_wsc_device_type(builder, entry2->data);
p2p_attr_builder_put_be16(builder, WSC_ATTR_DEVICE_NAME);
p2p_attr_builder_put_be16(builder, strlen(desc->device_name));
p2p_attr_builder_put_bytes(builder, desc->device_name,
strlen(desc->device_name));
}
}
/* Section 4.1.17, 4.1.29 */
static void p2p_build_group_id(struct p2p_attr_builder *builder, bool optional,
enum p2p_attr type,
const struct p2p_group_id_attr *attr)
{
if (optional && !memcmp(attr->device_addr, zero_addr, 6))
return;
p2p_attr_builder_start_attr(builder, type);
p2p_attr_builder_put_bytes(builder, attr->device_addr, 6);
p2p_attr_builder_put_bytes(builder, attr->ssid, strlen(attr->ssid));
}
/* Section 4.1.18 */
static void p2p_build_interface(struct p2p_attr_builder *builder,
const struct p2p_interface_attr *attr)
{
const struct l_queue_entry *entry;
/* Always optional */
if (!memcmp(attr->device_addr, zero_addr, 6))
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_P2P_INTERFACE);
p2p_attr_builder_put_bytes(builder, attr->device_addr, 6);
p2p_attr_builder_put_u8(builder, l_queue_length(attr->interface_addrs));
for (entry = l_queue_get_entries(attr->interface_addrs); entry;
entry = entry->next)
p2p_attr_builder_put_bytes(builder, entry->data, 6);
}
/* Section 4.1.22 */
static void p2p_build_svc_hash(struct p2p_attr_builder *builder,
struct l_queue *service_hashes)
{
const struct l_queue_entry *entry;
/* Always optional */
if (!service_hashes)
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_SVC_HASH);
for (entry = l_queue_get_entries(service_hashes); entry;
entry = entry->next)
p2p_attr_builder_put_bytes(builder, entry->data, 6);
}
/* Section 4.1.23 */
static void p2p_build_session_data(struct p2p_attr_builder *builder,
const struct p2p_session_info_data_attr *attr)
{
/* Always optional */
if (!attr->data_len)
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_SESSION_INFO_DATA_INFO);
p2p_attr_builder_put_bytes(builder, attr->data, attr->data_len);
}
/* Section 4.1.25 */
static void p2p_build_advertisement_id(struct p2p_attr_builder *builder,
bool optional,
const struct p2p_advertisement_id_info_attr *attr)
{
if (optional && !memcmp(attr->service_mac_addr, zero_addr, 6))
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_ADVERTISEMENT_ID_INFO);
p2p_attr_builder_put_u32(builder, attr->advertisement_id);
p2p_attr_builder_put_bytes(builder, attr->service_mac_addr, 6);
}
/* Section 4.1.26 */
static void p2p_build_advertised_service_info(struct p2p_attr_builder *builder,
struct l_queue *services)
{
const struct l_queue_entry *entry;
/* Always optional */
if (!services)
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_ADVERTISED_SVC_INFO);
for (entry = l_queue_get_entries(services); entry;
entry = entry->next) {
const struct p2p_advertised_service_descriptor *desc =
entry->data;
p2p_attr_builder_put_u32(builder, desc->advertisement_id);
p2p_attr_builder_put_be16(builder, desc->wsc_config_methods);
p2p_attr_builder_put_u8(builder, strlen(desc->service_name));
p2p_attr_builder_put_bytes(builder, desc->service_name,
strlen(desc->service_name));
}
}
/* Section 4.1.27 */
static void p2p_build_session_id(struct p2p_attr_builder *builder,
bool optional,
const struct p2p_session_id_info_attr *attr)
{
if (optional && !memcmp(attr->session_mac_addr, zero_addr, 6))
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_SESSION_ID_INFO);
p2p_attr_builder_put_u32(builder, attr->session_id);
p2p_attr_builder_put_bytes(builder, attr->session_mac_addr, 6);
}
/* Section 4.1.28 */
static void p2p_build_feature_capability(struct p2p_attr_builder *builder,
bool optional,
enum p2p_asp_coordination_transport_protocol attr)
{
if (optional && attr == P2P_ASP_TRANSPORT_UNKNOWN)
return;
p2p_attr_builder_start_attr(builder, P2P_ATTR_FEATURE_CAPABILITY);
p2p_attr_builder_put_u8(builder, 0x01); /* P2P_ASP_TRANSPORT_UDP */
p2p_attr_builder_put_u8(builder, 0x00); /* Reserved */
}
/* Section 4.2.1 */
uint8_t *p2p_build_beacon(const struct p2p_beacon *data, size_t *out_len)
{
struct p2p_attr_builder *builder;
uint8_t *ret;
uint8_t *tlv;
size_t tlv_len;
builder = p2p_attr_builder_new(512);
p2p_build_capability(builder, &data->capability);
p2p_build_addr(builder, false, P2P_ATTR_P2P_DEVICE_ID,
data->device_addr);
p2p_build_notice_of_absence_attr(builder, true,
&data->notice_of_absence);
tlv = p2p_attr_builder_free(builder, false, &tlv_len);
ret = ie_tlv_encapsulate_p2p_payload(tlv, tlv_len, out_len);
l_free(tlv);
return ret;
}
/* Section 4.2.2 */
uint8_t *p2p_build_probe_req(const struct p2p_probe_req *data, size_t *out_len)
{
struct p2p_attr_builder *builder;
uint8_t *ret;
uint8_t *tlv;
size_t tlv_len;
builder = p2p_attr_builder_new(512);
p2p_build_capability(builder, &data->capability);
p2p_build_addr(builder, true, P2P_ATTR_P2P_DEVICE_ID,
data->device_addr);
p2p_build_channel(builder, true, P2P_ATTR_LISTEN_CHANNEL,
&data->listen_channel);
p2p_build_extended_listen_timing(builder, &data->listen_availability);
p2p_build_device_info(builder, true, &data->device_info);
p2p_build_channel(builder, true, P2P_ATTR_OPERATING_CHANNEL,
&data->operating_channel);
p2p_build_svc_hash(builder, data->service_hashes);
tlv = p2p_attr_builder_free(builder, false, &tlv_len);
ret = ie_tlv_encapsulate_p2p_payload(tlv, tlv_len, out_len);
l_free(tlv);
return ret;
}
/* Section 4.2.3 */
uint8_t *p2p_build_probe_resp(const struct p2p_probe_resp *data,
size_t *out_len)
{
struct p2p_attr_builder *builder;
uint8_t *ret;
uint8_t *tlv;
size_t tlv_len;
builder = p2p_attr_builder_new(512);
p2p_build_capability(builder, &data->capability);
p2p_build_extended_listen_timing(builder, &data->listen_availability);
p2p_build_notice_of_absence_attr(builder, true,
&data->notice_of_absence);
p2p_build_device_info(builder, false, &data->device_info);
p2p_build_group_info(builder, data->group_clients);
p2p_build_advertised_service_info(builder, data->advertised_svcs);
tlv = p2p_attr_builder_free(builder, false, &tlv_len);
ret = ie_tlv_encapsulate_p2p_payload(tlv, tlv_len, out_len);
l_free(tlv);
return ret;
}
/* Section 4.2.4 */
uint8_t *p2p_build_association_req(const struct p2p_association_req *data,
size_t *out_len)
{
struct p2p_attr_builder *builder;
uint8_t *ret;
uint8_t *tlv;
size_t tlv_len;
builder = p2p_attr_builder_new(512);
p2p_build_capability(builder, &data->capability);
p2p_build_extended_listen_timing(builder, &data->listen_availability);
p2p_build_device_info(builder, true, &data->device_info);
p2p_build_interface(builder, &data->interface);
tlv = p2p_attr_builder_free(builder, false, &tlv_len);
ret = ie_tlv_encapsulate_p2p_payload(tlv, tlv_len, out_len);
l_free(tlv);
return ret;
}
/* Section 4.2.5 */
uint8_t *p2p_build_association_resp(const struct p2p_association_resp *data,
size_t *out_len)
{
struct p2p_attr_builder *builder;
uint8_t *ret;
uint8_t *tlv;
size_t tlv_len;
builder = p2p_attr_builder_new(32);
/*
* 4.2.5: "The Status attribute shall be present [...] when a
* (Re) association Request frame is denied."
*
* Note the P2P IE may end up being empty but is required for
* this frame type nevertheless.
*/
if (data->status != P2P_STATUS_SUCCESS &&
data->status != P2P_STATUS_SUCCESS_ACCEPTED_BY_USER)
p2p_build_u8_attr(builder, P2P_ATTR_STATUS, data->status);
p2p_build_extended_listen_timing(builder, &data->listen_availability);
tlv = p2p_attr_builder_free(builder, false, &tlv_len);
ret = ie_tlv_encapsulate_p2p_payload(tlv, tlv_len, out_len);
l_free(tlv);
return ret;
}
/* Section 4.2.6 */
uint8_t *p2p_build_deauthentication(const struct p2p_deauthentication *data,
size_t *out_len)
{
struct p2p_attr_builder *builder;
uint8_t *ret;
uint8_t *tlv;
size_t tlv_len;
if (!data->minor_reason_code) {
*out_len = 0;
return (uint8_t *) "";
}
builder = p2p_attr_builder_new(512);
p2p_build_u8_attr(builder, P2P_ATTR_MINOR_REASON_CODE,
data->minor_reason_code);
tlv = p2p_attr_builder_free(builder, false, &tlv_len);
ret = ie_tlv_encapsulate_p2p_payload(tlv, tlv_len, out_len);
l_free(tlv);
return ret;
}
/* Section 4.2.7 */
uint8_t *p2p_build_disassociation(const struct p2p_disassociation *data,
size_t *out_len)
{
struct p2p_attr_builder *builder;
uint8_t *ret;
uint8_t *tlv;
size_t tlv_len;
if (!data->minor_reason_code) {
*out_len = 0;
return (uint8_t *) "";
}
builder = p2p_attr_builder_new(512);
p2p_build_u8_attr(builder, P2P_ATTR_MINOR_REASON_CODE,
data->minor_reason_code);
tlv = p2p_attr_builder_free(builder, false, &tlv_len);
ret = ie_tlv_encapsulate_p2p_payload(tlv, tlv_len, out_len);
l_free(tlv);
return ret;
}

View File

@ -478,3 +478,16 @@ void p2p_free_provision_disc_resp(struct p2p_provision_discovery_resp *data);
void p2p_free_notice_of_absence(struct p2p_notice_of_absence *data); void p2p_free_notice_of_absence(struct p2p_notice_of_absence *data);
void p2p_free_presence_req(struct p2p_presence_req *data); void p2p_free_presence_req(struct p2p_presence_req *data);
void p2p_free_presence_resp(struct p2p_presence_resp *data); void p2p_free_presence_resp(struct p2p_presence_resp *data);
uint8_t *p2p_build_beacon(const struct p2p_beacon *data, size_t *out_len);
uint8_t *p2p_build_probe_req(const struct p2p_probe_req *data, size_t *out_len);
uint8_t *p2p_build_probe_resp(const struct p2p_probe_resp *data,
size_t *out_len);
uint8_t *p2p_build_association_req(const struct p2p_association_req *data,
size_t *out_len);
uint8_t *p2p_build_association_resp(const struct p2p_association_resp *data,
size_t *out_len);
uint8_t *p2p_build_deauthentication(const struct p2p_deauthentication *data,
size_t *out_len);
uint8_t *p2p_build_disassociation(const struct p2p_disassociation *data,
size_t *out_len);