--- a/include/libnetfilter_acct/libnetfilter_acct.h +++ b/include/libnetfilter_acct/libnetfilter_acct.h @@ -11,6 +11,27 @@ NFACCT_ATTR_NAME = 0, NFACCT_ATTR_PKTS, NFACCT_ATTR_BYTES, + NFACCT_ATTR_FMT, +}; + +enum nfacct_format { + FMT_DEFAULT=0, /* 00001048576 */ + FMT_TRIPLETS, /* 1,048,576 - locale-dependent */ + FMT_IEC, /* 133.012MiB - dynamic */ + FMT_IEC_KIBIBYTE, /* 1,145.178KiB - fixed */ + FMT_IEC_MEBIBYTE, /* 1,145.178MiB - fixed */ + FMT_IEC_GIBIBYTE, /* 1,145.178GiB - fixed */ + FMT_IEC_TEBIBYTE, /* 1,145.178TiB - fixed */ + FMT_IEC_PEBIBYTE, /* 1,145.178PiB - fixed */ + FMT_IEC_EXBIBYTE, /* 1,145.178EiB - fixed */ + FMT_SI, /* 133.012MB - dynamic */ + FMT_SI_KILOBYTE, /* 1,145.178KB - fixed */ + FMT_SI_MEGABYTE, /* 1,145.178MB - fixed */ + FMT_SI_GIGABYTE, /* 1,145.178GB - fixed */ + FMT_SI_TERABYTE, /* 1,145.178TB - fixed */ + FMT_SI_PETABYTE, /* 1,145.178PB - fixed */ + FMT_SI_EXABYTE, /* 1,145.178EB - fixed */ + FMT_MAX, }; struct nfacct *nfacct_alloc(void); @@ -19,11 +40,13 @@ void nfacct_attr_set(struct nfacct *nfacct, enum nfacct_attr_type type, const void *data); void nfacct_attr_set_str(struct nfacct *nfacct, enum nfacct_attr_type type, const char *name); void nfacct_attr_set_u64(struct nfacct *nfacct, enum nfacct_attr_type type, uint64_t value); +void nfacct_attr_set_u32(struct nfacct *nfacct, enum nfacct_attr_type type, uint32_t value); void nfacct_attr_unset(struct nfacct *nfacct, enum nfacct_attr_type type); const void *nfacct_attr_get(struct nfacct *nfacct, enum nfacct_attr_type type); const char *nfacct_attr_get_str(struct nfacct *nfacct, enum nfacct_attr_type type); uint64_t nfacct_attr_get_u64(struct nfacct *nfacct, enum nfacct_attr_type type); +uint32_t nfacct_attr_get_u32(struct nfacct *nfacct, enum nfacct_attr_type type); struct nlmsghdr; --- a/include/linux/netfilter/nfnetlink_acct.h +++ b/include/linux/netfilter/nfnetlink_acct.h @@ -18,6 +18,7 @@ NFACCT_NAME, NFACCT_PKTS, NFACCT_BYTES, + NFACCT_FMT, NFACCT_USE, __NFACCT_MAX }; --- a/src/libnetfilter_acct.c +++ b/src/libnetfilter_acct.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,7 @@ char name[NFACCT_NAME_MAX]; uint64_t pkts; uint64_t bytes; + uint32_t fmt; uint32_t bitset; }; @@ -113,6 +115,10 @@ nfacct->bytes = *((uint64_t *) data); nfacct->bitset |= (1 << NFACCT_ATTR_BYTES); break; + case NFACCT_ATTR_FMT: + nfacct->fmt = *((uint32_t *) data); + nfacct->bitset |= (1 << NFACCT_ATTR_FMT); + break; } } EXPORT_SYMBOL(nfacct_attr_set); @@ -146,6 +152,20 @@ EXPORT_SYMBOL(nfacct_attr_set_u64); /** + * nfacct_attr_set_u32 - set one attribute the accounting object + * \param nfacct pointer to the accounting object + * \param type attribute type you want to set + * \param value unsigned 32-bit integer + */ +void +nfacct_attr_set_u32(struct nfacct *nfacct, enum nfacct_attr_type type, + uint32_t value) +{ + nfacct_attr_set(nfacct, type, &value); +} +EXPORT_SYMBOL(nfacct_attr_set_u32); + +/** * nfacct_attr_unset - unset one attribute the accounting object * \param nfacct pointer to the accounting object * \param type attribute type you want to set @@ -163,6 +183,9 @@ case NFACCT_ATTR_BYTES: nfacct->bitset &= ~(1 << NFACCT_ATTR_BYTES); break; + case NFACCT_ATTR_FMT: + nfacct->bitset &= ~(1 << NFACCT_ATTR_FMT); + break; } } EXPORT_SYMBOL(nfacct_attr_unset); @@ -192,6 +215,10 @@ if (nfacct->bitset & (1 << NFACCT_ATTR_BYTES)) ret = &nfacct->bytes; break; + case NFACCT_ATTR_FMT: + if (nfacct->bitset & (1 << NFACCT_ATTR_FMT)) + ret = &nfacct->fmt; + break; } return ret; } @@ -227,19 +254,180 @@ } EXPORT_SYMBOL(nfacct_attr_get_u64); +/** + * nfacct_attr_get_u32 - get one attribute the accounting object + * \param nfacct pointer to the accounting object + * \param type attribute type you want to get + * + * This function returns a unsigned 32-bits integer. If the attribute is + * unsupported, this returns NULL. + */ +uint32_t nfacct_attr_get_u32(struct nfacct *nfacct, enum nfacct_attr_type type) +{ + const void *ret = nfacct_attr_get(nfacct, type); + return ret ? *((uint32_t *)ret) : 0; +} +EXPORT_SYMBOL(nfacct_attr_get_u32); + +#define KiB ((unsigned long long) 1 << 10) +#define MiB ((unsigned long long) 1 << 20) +#define GiB ((unsigned long long) 1 << 30) +#define TiB ((unsigned long long) 1 << 40) +#define PiB ((unsigned long long) 1 << 50) +#define EiB ((unsigned long long) 1 << 60) +#define KB ((unsigned long long) 1*1000) +#define MB ((unsigned long long) KB*1000) +#define GB ((unsigned long long) MB*1000) +#define TB ((unsigned long long) GB*1000) +#define PB ((unsigned long long) TB*1000) +#define EB ((unsigned long long) PB*1000) + +#define STR_FMT_PLAIN "{ pkts = %s, bytes = %s } = %s;" +#define STR_FMT_XML "%s" \ + "%s" \ + "%s" +#define STR_FMT_DEFAULT "%020.0f%s" +#define STR_FMT_TRIPLETS "%'26.0f%s" +#define STR_FMT_SI_IEC "%'26.3f%s" +#define STR_FMT_XML_DEFAULT STR_FMT_DEFAULT +#define STR_FMT_XML_TRIPLETS "%'.0f%s" +#define STR_FMT_XML_SI_IEC "%'.3f%s" + +struct nfacct_number { + float value; + enum nfacct_format fmt; + char *fmt_str; +}; + +struct fmt_key { + unsigned long long num; + char name[4]; +}; + +static struct fmt_key fmt_keys[] = { + [FMT_DEFAULT] = { .num = 0, .name = "" }, + [FMT_TRIPLETS] = { .num = 0, .name = "" }, + [FMT_IEC] = { .num = 0, .name = "" }, + [FMT_IEC_KIBIBYTE] = { .num = KiB, .name = "KiB" }, + [FMT_IEC_MEBIBYTE] = { .num = MiB, .name = "MiB" }, + [FMT_IEC_GIBIBYTE] = { .num = GiB, .name = "GiB" }, + [FMT_IEC_TEBIBYTE] = { .num = TiB, .name = "TiB" }, + [FMT_IEC_PEBIBYTE] = { .num = PiB, .name = "PiB" }, + [FMT_IEC_EXBIBYTE] = { .num = EiB, .name = "EiB" }, + [FMT_SI] = { .num = 0, .name = "" }, + [FMT_SI_KILOBYTE] = { .num = KB, .name = "KB" }, + [FMT_SI_MEGABYTE] = { .num = MB, .name = "MB" }, + [FMT_SI_GIGABYTE] = { .num = GB, .name = "GB" }, + [FMT_SI_TERABYTE] = { .num = TB, .name = "TB" }, + [FMT_SI_PETABYTE] = { .num = PB, .name = "PB" }, + [FMT_SI_EXABYTE] = { .num = EB, .name = "EB" }, +}; + +#define SET_RET(x) \ + ret.value /= fmt_keys[x].num; \ + ret.fmt = x; + +#define SET_RET_FMT(x) \ + ret.fmt = fmt; \ + ret.fmt_str = xml ? STR_FMT_XML_##x : STR_FMT_##x; + +static struct nfacct_number +format_number(const unsigned long long val, const enum nfacct_format fmt, + const int xml) +{ + struct nfacct_number ret; + ret.value = (float) val; + SET_RET_FMT(SI_IEC); + switch (fmt) { + case FMT_IEC: + if (ret.value >= EiB) { + SET_RET(FMT_IEC_EXBIBYTE); + } else if (ret.value >= PiB) { + SET_RET(FMT_IEC_PEBIBYTE); + } else if (ret.value >= TiB) { + SET_RET(FMT_IEC_TEBIBYTE); + } else if (ret.value >= GiB) { + SET_RET(FMT_IEC_GIBIBYTE); + } else if (ret.value >= MiB) { + SET_RET(FMT_IEC_MEBIBYTE); + } else if (ret.value >= KiB) { + SET_RET(FMT_IEC_KIBIBYTE); + } + break; + case FMT_SI: + if (ret.value >= EB) { + SET_RET(FMT_SI_EXABYTE); + } else if (ret.value >= PB) { + SET_RET(FMT_SI_PETABYTE); + } else if (ret.value >= TB) { + SET_RET(FMT_SI_TERABYTE); + } else if (ret.value >= GB) { + SET_RET(FMT_SI_GIGABYTE); + } else if (ret.value >= MB) { + SET_RET(FMT_SI_MEGABYTE); + } else if (ret.value >= KB) { + SET_RET(FMT_SI_KILOBYTE); + } + break; + case FMT_IEC_EXBIBYTE: + case FMT_IEC_PEBIBYTE: + case FMT_IEC_TEBIBYTE: + case FMT_IEC_GIBIBYTE: + case FMT_IEC_MEBIBYTE: + case FMT_IEC_KIBIBYTE: + case FMT_SI_EXABYTE: + case FMT_SI_PETABYTE: + case FMT_SI_TERABYTE: + case FMT_SI_GIGABYTE: + case FMT_SI_MEGABYTE: + case FMT_SI_KILOBYTE: + SET_RET(fmt); + break; + case FMT_DEFAULT: + SET_RET_FMT(DEFAULT); + break; + case FMT_TRIPLETS: + SET_RET_FMT(TRIPLETS); + break; + } + return ret; +} + +#define DEFAULT_LOCALE "en_GB" + +static void init_locale(void) { + char *lang; + char *env = "LANG"; + lang = getenv(env); + setlocale(LC_ALL,(lang == NULL ? DEFAULT_LOCALE : lang)); +} + static int nfacct_snprintf_plain(char *buf, size_t rem, struct nfacct *nfacct, uint16_t flags) { int ret; + char fmt_str[sizeof(STR_FMT_PLAIN) + + sizeof(STR_FMT_DEFAULT) * 2 + 10]; + struct nfacct_number p; + struct nfacct_number b; + uint32_t fmt; if (flags & NFACCT_SNPRINTF_F_FULL) { - ret = snprintf(buf, rem, - "{ pkts = %.20llu, bytes = %.20llu } = %s;", - (unsigned long long) + fmt = nfacct_attr_get_u32(nfacct, NFACCT_ATTR_FMT); + if (((fmt & 240) >> 4) || (fmt & 15)) // locale-dependent + init_locale(); + + p = format_number((unsigned long long) nfacct_attr_get_u64(nfacct, NFACCT_ATTR_PKTS), - (unsigned long long) + ((fmt & 240) >> 4), 0); + b = format_number((unsigned long long) nfacct_attr_get_u64(nfacct, NFACCT_ATTR_BYTES), + (fmt & 15), 0); + sprintf(fmt_str, STR_FMT_PLAIN, p.fmt_str, b.fmt_str, "%s"); + ret = snprintf(buf, rem, fmt_str, + p.value, fmt_keys[p.fmt].name, + b.value, fmt_keys[b.fmt].name, nfacct_attr_get_str(nfacct, NFACCT_ATTR_NAME)); } else { ret = snprintf(buf, rem, "%s\n", @@ -293,16 +481,26 @@ { int ret = 0; unsigned int size = 0, offset = 0; + char fmt_str[sizeof(STR_FMT_XML) + sizeof(STR_FMT_DEFAULT) * 2 + 10]; + struct nfacct_number p; + struct nfacct_number b; + uint32_t fmt; + + fmt = nfacct_attr_get_u32(nfacct, NFACCT_ATTR_FMT); + if (((fmt & 240) >> 4) || (fmt & 15)) // locale-dependent + init_locale(); - ret = snprintf(buf, rem, - "%s" - "%.20llu" - "%.20llu", - nfacct_attr_get_str(nfacct, NFACCT_ATTR_NAME), - (unsigned long long) + p = format_number((unsigned long long) + nfacct_attr_get_u64(nfacct, NFACCT_ATTR_PKTS), + ((fmt & 240) >> 4), 1); + b = format_number((unsigned long long) nfacct_attr_get_u64(nfacct, NFACCT_ATTR_BYTES), - (unsigned long long) - nfacct_attr_get_u64(nfacct, NFACCT_ATTR_PKTS)); + (fmt & 15), 1); + sprintf(fmt_str, STR_FMT_XML, "%s", p.fmt_str, b.fmt_str); + ret = snprintf(buf, rem, fmt_str, + nfacct_attr_get_str(nfacct, NFACCT_ATTR_NAME), + p.value, fmt_keys[p.fmt].name, + b.value, fmt_keys[b.fmt].name); BUFFER_SIZE(ret, size, rem, offset); if (flags & NFACCT_SNPRINTF_F_TIME) { @@ -427,6 +625,9 @@ if (nfacct->bitset & (1 << NFACCT_ATTR_BYTES)) mnl_attr_put_u64(nlh, NFACCT_BYTES, htobe64(nfacct->bytes)); + + if (nfacct->bitset & (1 << NFACCT_ATTR_FMT)) + mnl_attr_put_u32(nlh, NFACCT_FMT, htobe32(nfacct->fmt)); } EXPORT_SYMBOL(nfacct_nlmsg_build_payload); @@ -452,6 +653,12 @@ return MNL_CB_ERROR; } break; + case NFACCT_FMT: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; } tb[type] = attr; return MNL_CB_OK; @@ -481,6 +688,9 @@ be64toh(mnl_attr_get_u64(tb[NFACCT_PKTS]))); nfacct_attr_set_u64(nfacct, NFACCT_ATTR_BYTES, be64toh(mnl_attr_get_u64(tb[NFACCT_BYTES]))); + if (tb[NFACCT_FMT]) + nfacct_attr_set_u32(nfacct, NFACCT_ATTR_FMT, + be32toh(mnl_attr_get_u32(tb[NFACCT_FMT]))); return 0; }