From d3ae677639d84714602fd02e6803a7e6c5c45ecc Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 19 May 2015 22:21:41 +0200 Subject: [PATCH] nfacct: Add patches for direct JSON output format Rather than parsing the existing XML or standard output of the nfacct utility, and then piping that into a perl/python script for formatting to JSON, we teach nfacct to generate JSON directly. This seems like the proper way to do it. The patches will be submitted mainline. --- .../files/0001-add-JSON-output-format.patch | 95 ++++++++ .../libnetfilter/libnetfilter-acct_git.bb | 16 ++ ...-output-formatting-to-nfacct-utility.patch | 213 ++++++++++++++++++ recipes-extra/nfacct/nfacct_git.bb | 17 ++ 4 files changed, 341 insertions(+) create mode 100644 recipes-extra/libnetfilter/files/0001-add-JSON-output-format.patch create mode 100644 recipes-extra/libnetfilter/libnetfilter-acct_git.bb create mode 100644 recipes-extra/nfacct/files/0001-Add-JSON-output-formatting-to-nfacct-utility.patch create mode 100644 recipes-extra/nfacct/nfacct_git.bb diff --git a/recipes-extra/libnetfilter/files/0001-add-JSON-output-format.patch b/recipes-extra/libnetfilter/files/0001-add-JSON-output-format.patch new file mode 100644 index 0000000..5f82d6b --- /dev/null +++ b/recipes-extra/libnetfilter/files/0001-add-JSON-output-format.patch @@ -0,0 +1,95 @@ +From 1ac986e0bee1cbfc8039c259617bb9bdba1d6e75 Mon Sep 17 00:00:00 2001 +From: Harald Welte +Date: Tue, 19 May 2015 22:09:45 +0200 +Subject: [PATCH] add JSON output format + +the original output format looks a bit like JSON, but isn't. The XML +output is useful if you deal with XML, but a lot of applications prefer +more lightweight formats like YAML/JSON. + +This adds the JSON output format to libnetfilter_acct, which will +subsequently be used by a similar change in the nfacct utility. +--- + include/libnetfilter_acct/libnetfilter_acct.h | 1 + + src/libnetfilter_acct.c | 46 +++++++++++++++++++++++++++ + 2 files changed, 47 insertions(+) + +diff --git a/include/libnetfilter_acct/libnetfilter_acct.h b/include/libnetfilter_acct/libnetfilter_acct.h +index c6ed858..647490f 100644 +--- a/include/libnetfilter_acct/libnetfilter_acct.h ++++ b/include/libnetfilter_acct/libnetfilter_acct.h +@@ -41,6 +41,7 @@ int nfacct_nlmsg_parse_payload(const struct nlmsghdr *nlh, struct nfacct *nfacct + + #define NFACCT_SNPRINTF_T_PLAIN 0 + #define NFACCT_SNPRINTF_T_XML 1 ++#define NFACCT_SNPRINTF_T_JSON 2 + + int nfacct_snprintf(char *buf, size_t size, struct nfacct *nfacct, uint16_t type, uint16_t flags); + +diff --git a/src/libnetfilter_acct.c b/src/libnetfilter_acct.c +index bb15c68..b0bcf67 100644 +--- a/src/libnetfilter_acct.c ++++ b/src/libnetfilter_acct.c +@@ -297,6 +297,49 @@ nfacct_snprintf_plain(char *buf, size_t rem, struct nfacct *nfacct, + return len; + } + ++static int ++nfacct_snprintf_json(char *buf, size_t rem, struct nfacct *nfacct, ++ uint16_t flags) ++{ ++ int ret = 0, offset = 0, len = 0; ++ ++ if (flags & NFACCT_SNPRINTF_F_FULL) { ++ ret = snprintf(buf, rem, ++ " { \"pkts\" : %"PRIu64", \"bytes\" : %"PRIu64"", ++ nfacct_attr_get_u64(nfacct, NFACCT_ATTR_PKTS), ++ nfacct_attr_get_u64(nfacct, NFACCT_ATTR_BYTES)); ++ SNPRINTF_CHECK(ret, rem, offset, len); ++ ++ if (nfacct->flags) { ++ uint32_t mode; ++ char *mode_name; ++ ++ mode = nfacct_attr_get_u64(nfacct, NFACCT_ATTR_FLAGS); ++ if (mode & NFACCT_F_QUOTA_PKTS) ++ mode_name = "packet"; ++ else if (mode & NFACCT_F_QUOTA_BYTES) ++ mode_name = "byte"; ++ else ++ mode_name = "unknown"; ++ ++ ret = snprintf(buf + offset, rem, ++ ", \"quota\" : %"PRIu64", \"mode\" = \"%s\""\ ++ ", \"overquota\" = %u", ++ nfacct_attr_get_u64(nfacct, NFACCT_ATTR_QUOTA), ++ mode_name, ++ mode & NFACCT_F_OVERQUOTA ? 1 : 0); ++ SNPRINTF_CHECK(ret, rem, offset, len); ++ } ++ ++ ret = snprintf(buf + offset, rem, ", \"name\" : \"%s\" }", ++ nfacct_attr_get_str(nfacct, NFACCT_ATTR_NAME)); ++ } ++ /* non-F_FULL doesn't seem to make sense in JSON */ ++ SNPRINTF_CHECK(ret, rem, offset, len); ++ ++ return len; ++} ++ + #define BUFFER_SIZE(ret, size, rem, offset) \ + size += ret; \ + if (ret > rem) \ +@@ -393,6 +436,9 @@ int nfacct_snprintf(char *buf, size_t size, struct nfacct *nfacct, + case NFACCT_SNPRINTF_T_XML: + ret = nfacct_snprintf_xml(buf, size, nfacct, flags); + break; ++ case NFACCT_SNPRINTF_T_JSON: ++ ret = nfacct_snprintf_json(buf, size, nfacct, flags); ++ break; + default: + ret = -1; + break; +-- +2.1.4 + diff --git a/recipes-extra/libnetfilter/libnetfilter-acct_git.bb b/recipes-extra/libnetfilter/libnetfilter-acct_git.bb new file mode 100644 index 0000000..48d1555 --- /dev/null +++ b/recipes-extra/libnetfilter/libnetfilter-acct_git.bb @@ -0,0 +1,16 @@ +SUMMARY = "libnetfilter_acct is the userspace library providing interface to extended accounting infrastructure." +HOMEPAGE = "http://www.netfilter.org/projects/libnetfilter_acct/index.html" +LICENSE = "LGPLv2.1+" +LIC_FILES_CHKSUM = "file://COPYING;md5=4fbd65380cdd255951079008b364516c" +PV="1.0.2+git${SRCPV}" +PR = "r0" + +DEPENDS = "libmnl" + +SRC_URI = "git://git.netfilter.org/libnetfilter_acct;branch=master \ + file://0001-add-JSON-output-format.patch \ + " +SRCREV = "a9fea38024e6bde9118cc12bc8417b207ffc4da9" +S = "${WORKDIR}/git" + +inherit autotools pkgconfig diff --git a/recipes-extra/nfacct/files/0001-Add-JSON-output-formatting-to-nfacct-utility.patch b/recipes-extra/nfacct/files/0001-Add-JSON-output-formatting-to-nfacct-utility.patch new file mode 100644 index 0000000..f956d11 --- /dev/null +++ b/recipes-extra/nfacct/files/0001-Add-JSON-output-formatting-to-nfacct-utility.patch @@ -0,0 +1,213 @@ +From aefba0f03444345dd5384631203f61647637175d Mon Sep 17 00:00:00 2001 +From: Harald Welte +Date: Tue, 19 May 2015 22:12:06 +0200 +Subject: [PATCH] Add JSON output formatting to nfacct utility + +This is based on the JSON support patch of libnetfilter_acct. +--- + nfacct.8 | 2 ++ + src/nfacct.c | 87 ++++++++++++++++++++++++++++++++++++++++++++---------------- + 2 files changed, 66 insertions(+), 23 deletions(-) + +diff --git a/nfacct.8 b/nfacct.8 +index 0c3249c..bf563ae 100644 +--- a/nfacct.8 ++++ b/nfacct.8 +@@ -47,6 +47,8 @@ That atomically obtains and resets the counters. + .TP + .BI "xml " + That displays the output in XML format. ++.BI "json " ++That displays the output in JSON format. + .PP + .SH SEE ALSO + .BR iptables (8) +diff --git a/src/nfacct.c b/src/nfacct.c +index 2546a6e..a55655a 100644 +--- a/src/nfacct.c ++++ b/src/nfacct.c +@@ -137,13 +137,13 @@ int main(int argc, char *argv[]) + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } + +-static bool xml_header = false; ++static bool fmt_header = false; + + static int nfacct_cb(const struct nlmsghdr *nlh, void *data) + { + struct nfacct *nfacct; + char buf[4096]; +- bool *xml = (bool *)data; ++ int *fmt = (int *)data; + + nfacct = nfacct_alloc(); + if (nfacct == NULL) { +@@ -156,17 +156,36 @@ static int nfacct_cb(const struct nlmsghdr *nlh, void *data) + goto err_free; + } + +- if (*xml && !xml_header) { +- printf("\n" +- "\n"); +- xml_header = true; ++ switch (*fmt) { ++ case NFACCT_SNPRINTF_T_XML: ++ if (!fmt_header) { ++ printf("\n" ++ "\n"); ++ fmt_header = true; ++ } ++ break; ++ case NFACCT_SNPRINTF_T_JSON: ++ if (!fmt_header) { ++ printf("{ \"nfacct_counters\" : [\n"); ++ fmt_header = true; ++ } else ++ printf(",\n"); ++ break; ++ default: ++ break; + } + +- nfacct_snprintf(buf, sizeof(buf), nfacct, +- *xml ? NFACCT_SNPRINTF_T_XML : +- NFACCT_SNPRINTF_T_PLAIN, ++ nfacct_snprintf(buf, sizeof(buf), nfacct, *fmt, + NFACCT_SNPRINTF_F_FULL); +- printf("%s\n", buf); ++ printf("%s", buf); ++ switch (*fmt) { ++ case NFACCT_SNPRINTF_T_JSON: ++ /* no newline in JSON case */ ++ break; ++ default: ++ putchar('\n'); ++ break; ++ } + + err_free: + nfacct_free(nfacct); +@@ -174,9 +193,26 @@ err: + return MNL_CB_OK; + } + ++static void _print_footer(int fmt) ++{ ++ if (fmt_header) { ++ switch (fmt) { ++ case NFACCT_SNPRINTF_T_XML: ++ printf("\n"); ++ break; ++ case NFACCT_SNPRINTF_T_JSON: ++ printf("\n] }\n"); ++ break; ++ default: ++ break; ++ } ++ } ++} ++ + static int nfacct_cmd_list(int argc, char *argv[]) + { +- bool zeroctr = false, xml = false; ++ bool zeroctr = false; ++ int fmt = NFACCT_SNPRINTF_T_PLAIN; + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; +@@ -190,9 +226,13 @@ static int nfacct_cmd_list(int argc, char *argv[]) + duparg(argv[i]); + zeroctr = true; + } else if (strncmp(argv[i], "xml", strlen(argv[i])) == 0) { +- if (xml) ++ if (fmt) ++ duparg(argv[i]); ++ fmt = NFACCT_SNPRINTF_T_XML; ++ } else if (strncmp(argv[i], "json", strlen(argv[i])) == 0) { ++ if (fmt) + duparg(argv[i]); +- xml = true; ++ fmt = NFACCT_SNPRINTF_T_JSON; + } else if (strncmp(argv[i], "counters", strlen(argv[i])) == 0) { + if (mask || value) + duparg(argv[i]); +@@ -251,7 +291,7 @@ static int nfacct_cmd_list(int argc, char *argv[]) + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, nfacct_cb, &xml); ++ ret = mnl_cb_run(buf, ret, seq, portid, nfacct_cb, &fmt); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +@@ -262,8 +302,7 @@ static int nfacct_cmd_list(int argc, char *argv[]) + } + mnl_socket_close(nl); + +- if (xml_header) +- printf("\n"); ++ _print_footer(fmt); + + return 0; + } +@@ -444,7 +483,8 @@ static int nfacct_cmd_delete(int argc, char *argv[]) + + static int nfacct_cmd_get(int argc, char *argv[]) + { +- bool zeroctr = false, xml = false; ++ bool zeroctr = false; ++ int fmt = NFACCT_SNPRINTF_T_PLAIN; + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; +@@ -460,7 +500,9 @@ static int nfacct_cmd_get(int argc, char *argv[]) + if (strncmp(argv[i], "reset", strlen(argv[i])) == 0) { + zeroctr = true; + } else if (strncmp(argv[i], "xml", strlen(argv[i])) == 0) { +- xml = true; ++ fmt = NFACCT_SNPRINTF_T_XML; ++ } else if (strncmp(argv[i], "json", strlen(argv[i])) == 0) { ++ fmt = NFACCT_SNPRINTF_T_JSON; + } else { + nfacct_perror("unknown argument"); + return -1; +@@ -503,7 +545,7 @@ static int nfacct_cmd_get(int argc, char *argv[]) + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, nfacct_cb, &xml); ++ ret = mnl_cb_run(buf, ret, seq, portid, nfacct_cb, &fmt); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +@@ -514,8 +556,7 @@ static int nfacct_cmd_get(int argc, char *argv[]) + } + mnl_socket_close(nl); + +- if (xml_header) +- printf("\n"); ++ _print_footer(fmt); + + return 0; + } +@@ -660,7 +701,7 @@ static int nfacct_cmd_restore(int argc, char *argv[]) + static int nfacct_cmd_monitor(int argc, char *argv[]) + { + struct mnl_socket *nl; +- bool xml = false; ++ int fmt = NFACCT_SNPRINTF_T_PLAIN; + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret, option = NFNLGRP_ACCT_QUOTA; + +@@ -685,7 +726,7 @@ static int nfacct_cmd_monitor(int argc, char *argv[]) + break; + } + +- ret = mnl_cb_run(buf, ret, 0, 0, nfacct_cb, &xml); ++ ret = mnl_cb_run(buf, ret, 0, 0, nfacct_cb, &fmt); + if (ret <= 0) + break; + } +-- +2.1.4 + diff --git a/recipes-extra/nfacct/nfacct_git.bb b/recipes-extra/nfacct/nfacct_git.bb new file mode 100644 index 0000000..f7b5e58 --- /dev/null +++ b/recipes-extra/nfacct/nfacct_git.bb @@ -0,0 +1,17 @@ +SUMMARY = "nfacct is the command line tool to create/retrieve/delete accounting objects" +HOMEPAGE = "http://www.netfilter.org/projects/nfacct/downloads.html" +LICENSE = "GPLv2+" +LIC_FILES_CHKSUM = "file://COPYING;md5=8ca43cbc842c2336e835926c2166c28b" +PV="1.0.1+git${SRCPV}" + +DEPENDS = "libmnl libnetfilter-acct" + +SRC_URI = "git://git.netfilter.org/nfacct;branch=master \ + file://0001-Add-JSON-output-formatting-to-nfacct-utility.patch \ + " +SRCREV = "4437682babe86de7435d4fc839437f99e998b79c" +S = "${WORKDIR}/git" + +RDEPENDS_${PN} += "kernel-module-xt-nfacct kernel-module-nfnetlink-acct" + +inherit autotools pkgconfig