blob: 10ce8e9def9a0bd3c8965d8e72a8ffaff2804e94 [file] [log] [blame]
/*
* bitset.h - netlink bitset helpers
*
* Functions for easier handling of ethtool netlink bitset attributes.
*/
#include <stdio.h>
#include <errno.h>
#include "../common.h"
#include "netlink.h"
#include "bitset.h"
uint32_t bitset_get_count(const struct nlattr *bitset, int *retptr)
{
const struct nlattr *attr;
mnl_attr_for_each_nested(attr, bitset) {
if (mnl_attr_get_type(attr) != ETHTOOL_A_BITSET_SIZE)
continue;
*retptr = 0;
return mnl_attr_get_u32(attr);
}
*retptr = -EFAULT;
return 0;
}
bool bitset_is_compact(const struct nlattr *bitset)
{
const struct nlattr *attr;
mnl_attr_for_each_nested(attr, bitset) {
switch(mnl_attr_get_type(attr)) {
case ETHTOOL_A_BITSET_BITS:
return 0;
case ETHTOOL_A_BITSET_VALUE:
case ETHTOOL_A_BITSET_MASK:
return 1;
}
}
return false;
}
bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx,
int *retptr)
{
const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {};
DECLARE_ATTR_TB_INFO(bitset_tb);
const struct nlattr *bits;
const struct nlattr *bit;
bool nomask;
int ret;
*retptr = 0;
ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
if (ret < 0)
goto err;
nomask = bitset_tb[ETHTOOL_A_BITSET_NOMASK];
if (mask && nomask) {
/* Trying to determine if a bit is set in the mask of a "no
* mask" bitset doesn't make sense.
*/
ret = -EFAULT;
goto err;
}
bits = mask ? bitset_tb[ETHTOOL_A_BITSET_MASK] :
bitset_tb[ETHTOOL_A_BITSET_VALUE];
if (bits) {
const uint32_t *bitmap =
(const uint32_t *)mnl_attr_get_payload(bits);
if (idx >= 8 * mnl_attr_get_payload_len(bits))
return false;
return bitmap[idx / 32] & (1U << (idx % 32));
}
bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
if (!bits)
goto err;
mnl_attr_for_each_nested(bit, bits) {
const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {};
DECLARE_ATTR_TB_INFO(tb);
unsigned int my_idx;
if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT)
continue;
ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
if (ret < 0)
goto err;
ret = -EFAULT;
if (!tb[ETHTOOL_A_BITSET_BIT_INDEX])
goto err;
my_idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);
if (my_idx == idx)
return mask || nomask || tb[ETHTOOL_A_BITSET_BIT_VALUE];
}
return false;
err:
fprintf(stderr, "malformed netlink message (bitset)\n");
*retptr = ret;
return false;
}
bool bitset_is_empty(const struct nlattr *bitset, bool mask, int *retptr)
{
const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {};
DECLARE_ATTR_TB_INFO(bitset_tb);
const struct nlattr *bits;
const struct nlattr *bit;
int ret;
*retptr = 0;
ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
if (ret < 0)
goto err;
bits = mask ? bitset_tb[ETHTOOL_A_BITSET_MASK] :
bitset_tb[ETHTOOL_A_BITSET_VALUE];
if (bits) {
const uint32_t *bitmap =
(const uint32_t *)mnl_attr_get_payload(bits);
unsigned int n = mnl_attr_get_payload_len(bits);
unsigned int i;
ret = -EFAULT;
if (n % 4)
goto err;
for (i = 0; i < n / 4; i++)
if (bitmap[i])
return false;
return true;
}
bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
if (!bits)
goto err;
mnl_attr_for_each_nested(bit, bits) {
const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {};
DECLARE_ATTR_TB_INFO(tb);
if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT)
continue;
if (mask || bitset_tb[ETHTOOL_A_BITSET_NOMASK])
return false;
ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
if (ret < 0)
goto err;
if (tb[ETHTOOL_A_BITSET_BIT_VALUE])
return false;
}
return true;
err:
fprintf(stderr, "malformed netlink message (bitset)\n");
*retptr = ret;
return true;
}
static uint32_t *get_compact_bitset_attr(const struct nlattr *bitset,
uint16_t type)
{
const struct nlattr *tb[ETHTOOL_A_BITSET_MAX + 1] = {};
DECLARE_ATTR_TB_INFO(tb);
unsigned int count;
int ret;
ret = mnl_attr_parse_nested(bitset, attr_cb, &tb_info);
if (ret < 0)
return NULL;
if (!tb[ETHTOOL_A_BITSET_SIZE] || !tb[ETHTOOL_A_BITSET_VALUE] ||
!tb[type])
return NULL;
count = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_SIZE]);
if (8 * mnl_attr_get_payload_len(tb[type]) < count)
return NULL;
return mnl_attr_get_payload(tb[type]);
}
uint32_t *get_compact_bitset_value(const struct nlattr *bitset)
{
return get_compact_bitset_attr(bitset, ETHTOOL_A_BITSET_VALUE);
}
uint32_t *get_compact_bitset_mask(const struct nlattr *bitset)
{
return get_compact_bitset_attr(bitset, ETHTOOL_A_BITSET_MASK);
}
int walk_bitset(const struct nlattr *bitset, const struct stringset *labels,
bitset_walk_callback cb, void *data)
{
const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {};
DECLARE_ATTR_TB_INFO(bitset_tb);
const struct nlattr *bits;
const struct nlattr *bit;
bool is_list;
int ret;
ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
if (ret < 0)
return ret;
is_list = bitset_tb[ETHTOOL_A_BITSET_NOMASK];
bits = bitset_tb[ETHTOOL_A_BITSET_VALUE];
if (bits) {
const struct nlattr *mask = bitset_tb[ETHTOOL_A_BITSET_MASK];
unsigned int count, nwords, idx;
uint32_t *val_bm;
uint32_t *mask_bm;
if (!bitset_tb[ETHTOOL_A_BITSET_SIZE])
return -EFAULT;
count = mnl_attr_get_u32(bitset_tb[ETHTOOL_A_BITSET_SIZE]);
nwords = (count + 31) / 32;
if ((mnl_attr_get_payload_len(bits) / 4 < nwords) ||
(mask && mnl_attr_get_payload_len(mask) / 4 < nwords))
return -EFAULT;
val_bm = mnl_attr_get_payload(bits);
mask_bm = mask ? mnl_attr_get_payload(mask) : NULL;
for (idx = 0; idx < count; idx++)
if (!mask_bm || (mask_bm[idx / 32] & (1 << (idx % 32))))
cb(idx, get_string(labels, idx),
val_bm[idx / 32] & (1 << (idx % 32)), data);
return 0;
}
bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
if (!bits)
return -EFAULT;
mnl_attr_for_each_nested(bit, bits) {
const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {};
DECLARE_ATTR_TB_INFO(tb);
const char *name;
unsigned int idx;
if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT)
continue;
ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
if (ret < 0 || !tb[ETHTOOL_A_BITSET_BIT_INDEX] ||
!tb[ETHTOOL_A_BITSET_BIT_NAME])
return -EFAULT;
idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);
name = mnl_attr_get_str(tb[ETHTOOL_A_BITSET_BIT_NAME]);
cb(idx, name, is_list || tb[ETHTOOL_A_BITSET_BIT_VALUE], data);
}
return 0;
}