/* * bitset.h - netlink bitset helpers * * Functions for easier handling of ethtool netlink bitset attributes. */ #include #include #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; }