summaryrefslogtreecommitdiffstats
path: root/netlink/bitset.c
diff options
context:
space:
mode:
Diffstat (limited to 'netlink/bitset.c')
-rw-r--r--netlink/bitset.c259
1 files changed, 259 insertions, 0 deletions
diff --git a/netlink/bitset.c b/netlink/bitset.c
new file mode 100644
index 0000000..10ce8e9
--- /dev/null
+++ b/netlink/bitset.c
@@ -0,0 +1,259 @@
+/*
+ * 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;
+}