/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * (C) 2012 by Pablo Neira Ayuso * * This code has been sponsored by Sophos Astaro */ #include #include #include #include #include #include #include #include #include #include #include #include #include "internal.h" static bool big_endian_host(void) { uint16_t v = 1; return v == htons(v); } static int __reg_value_snprintf(char *buf, size_t remain, uint8_t *data, size_t datalen, bool reverse, const char *pfx) { int offset = 0, ret, i, idx; const char *sep = ""; for (i = 0; i < datalen; i++) { if ((i % 4) == 0) { ret = snprintf(buf + offset, remain, "%s%s", sep, pfx); SNPRINTF_BUFFER_SIZE(ret, remain, offset); sep = " "; } if (reverse) idx = datalen - i - 1; else idx = i; ret = snprintf(buf + offset, remain, "%.2x", data[idx]); SNPRINTF_BUFFER_SIZE(ret, remain, offset); } return offset; } static int nftnl_data_reg_value_snprintf_default(char *buf, size_t remain, const union nftnl_data_reg *reg, uint32_t flags) { uint32_t byteorder = big_endian_host() ? 0 : reg->byteorder; const char *pfx = flags & DATA_F_NOPFX ? "" : "0x"; int offset = 0, ret, i, pos = 0; for (i = 0; i < array_size(reg->sizes); i++) { int curlen = reg->sizes[i] ?: reg->len; bool reverse = byteorder & (1 << i); if (i > 0) { ret = snprintf(buf + offset, remain, " . "); SNPRINTF_BUFFER_SIZE(ret, remain, offset); } ret = __reg_value_snprintf(buf + offset, remain, (void *)®->val[pos], curlen, reverse, pfx); SNPRINTF_BUFFER_SIZE(ret, remain, offset); pos += div_round_up(curlen, sizeof(uint32_t)); if (pos >= reg->len / sizeof(uint32_t)) break; } return offset; } static int nftnl_data_reg_verdict_snprintf_def(char *buf, size_t size, const union nftnl_data_reg *reg, uint32_t flags) { int remain = size, offset = 0, ret = 0; ret = snprintf(buf, size, "%s", nftnl_verdict2str(reg->verdict)); SNPRINTF_BUFFER_SIZE(ret, remain, offset); if (reg->chain != NULL) { ret = snprintf(buf + offset, remain, " -> %s", reg->chain); SNPRINTF_BUFFER_SIZE(ret, remain, offset); } return offset; } int nftnl_data_reg_snprintf(char *buf, size_t size, const union nftnl_data_reg *reg, uint32_t flags, int reg_type) { switch(reg_type) { case DATA_VALUE: return nftnl_data_reg_value_snprintf_default(buf, size, reg, flags); case DATA_VERDICT: case DATA_CHAIN: return nftnl_data_reg_verdict_snprintf_def(buf, size, reg, flags); default: return -1; } } static int nftnl_data_parse_cb(const struct nlattr *attr, void *data) { const struct nlattr **tb = data; int type = mnl_attr_get_type(attr); if (mnl_attr_type_valid(attr, NFTA_DATA_MAX) < 0) return MNL_CB_OK; switch(type) { case NFTA_DATA_VALUE: if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) abi_breakage(); break; case NFTA_DATA_VERDICT: if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) abi_breakage(); break; } tb[type] = attr; return MNL_CB_OK; } static int nftnl_verdict_parse_cb(const struct nlattr *attr, void *data) { const struct nlattr **tb = data; int type = mnl_attr_get_type(attr); if (mnl_attr_type_valid(attr, NFTA_VERDICT_MAX) < 0) return MNL_CB_OK; switch(type) { case NFTA_VERDICT_CODE: case NFTA_VERDICT_CHAIN_ID: if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) abi_breakage(); break; case NFTA_VERDICT_CHAIN: if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) abi_breakage(); break; } tb[type] = attr; return MNL_CB_OK; } static int nftnl_parse_verdict(union nftnl_data_reg *data, const struct nlattr *attr, int *type) { struct nlattr *tb[NFTA_VERDICT_MAX+1]; if (mnl_attr_parse_nested(attr, nftnl_verdict_parse_cb, tb) < 0) return -1; if (!tb[NFTA_VERDICT_CODE]) return -1; data->verdict = ntohl(mnl_attr_get_u32(tb[NFTA_VERDICT_CODE])); switch(data->verdict) { case NF_ACCEPT: case NF_DROP: case NF_QUEUE: case NFT_CONTINUE: case NFT_BREAK: case NFT_RETURN: if (type) *type = DATA_VERDICT; data->len = sizeof(data->verdict); break; case NFT_JUMP: case NFT_GOTO: if (!tb[NFTA_VERDICT_CHAIN]) return -1; data->chain = strdup(mnl_attr_get_str(tb[NFTA_VERDICT_CHAIN])); if (!data->chain) return -1; if (type) *type = DATA_CHAIN; break; default: return -1; } return 0; } static int __nftnl_parse_data(union nftnl_data_reg *data, const struct nlattr *attr) { void *orig = mnl_attr_get_payload(attr); uint32_t data_len = mnl_attr_get_payload_len(attr); if (data_len == 0) return -1; if (data_len > sizeof(data->val)) return -1; memcpy(data->val, orig, data_len); data->len = data_len; return 0; } int nftnl_parse_data(union nftnl_data_reg *data, struct nlattr *attr, int *type) { struct nlattr *tb[NFTA_DATA_MAX+1] = {}; int ret = 0; if (mnl_attr_parse_nested(attr, nftnl_data_parse_cb, tb) < 0) return -1; if (tb[NFTA_DATA_VALUE]) { if (type) *type = DATA_VALUE; ret = __nftnl_parse_data(data, tb[NFTA_DATA_VALUE]); if (ret < 0) return ret; } if (tb[NFTA_DATA_VERDICT]) ret = nftnl_parse_verdict(data, tb[NFTA_DATA_VERDICT], type); return ret; } int nftnl_data_cpy(union nftnl_data_reg *dreg, const void *src, uint32_t len, uint32_t byteorder, uint8_t *sizes) { int ret = 0; if (len > sizeof(dreg->val)) { len = sizeof(dreg->val); ret = -1; } memcpy(dreg->val, src, len); dreg->len = len; dreg->byteorder = byteorder; if (sizes) memcpy(dreg->sizes, sizes, sizeof(dreg->sizes)); else memset(dreg->sizes, 0, sizeof(dreg->sizes)); return ret; }