#include "common.h"
#ifdef INTERNAL_PLUGIN_NFACCT
#ifdef HAVE_LIBMNL
#include <libmnl/libmnl.h>
static inline size_t mnl_buffer_size() {
long s = MNL_SOCKET_BUFFER_SIZE;
if(s <= 0) return 8192;
return (size_t)s;
}
// ----------------------------------------------------------------------------
// DO_NFSTAT - collect netfilter connection tracker statistics via netlink
// example: https://github.com/formorer/pkg-conntrack-tools/blob/master/src/conntrack.c
#ifdef HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H
#define DO_NFSTAT 1
#define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
#define RRD_TYPE_NET_STAT_CONNTRACK "netlink" // FIXME: should be "conntrack" when merged with the /proc plugin
#include <linux/netfilter/nfnetlink_conntrack.h>
static struct {
int update_every;
char *buf;
size_t buf_size;
struct mnl_socket *mnl;
struct nlmsghdr *nlh;
struct nfgenmsg *nfh;
unsigned int seq;
uint32_t portid;
struct nlattr *tb[CTA_STATS_MAX+1];
const char *attr2name[CTA_STATS_MAX+1];
kernel_uint_t metrics[CTA_STATS_MAX+1];
struct nlattr *tb_exp[CTA_STATS_EXP_MAX+1];
const char *attr2name_exp[CTA_STATS_EXP_MAX+1];
kernel_uint_t metrics_exp[CTA_STATS_EXP_MAX+1];
} nfstat_root = {
.update_every = 1,
.buf = NULL,
.buf_size = 0,
.mnl = NULL,
.nlh = NULL,
.nfh = NULL,
.seq = 0,
.portid = 0,
.tb = {},
.attr2name = {
[CTA_STATS_SEARCHED] = "searched",
[CTA_STATS_FOUND] = "found",
[CTA_STATS_NEW] = "new",
[CTA_STATS_INVALID] = "invalid",
[CTA_STATS_IGNORE] = "ignore",
[CTA_STATS_DELETE] = "delete",
[CTA_STATS_DELETE_LIST] = "delete_list",
[CTA_STATS_INSERT] = "insert",
[CTA_STATS_INSERT_FAILED] = "insert_failed",
[CTA_STATS_DROP] = "drop",
[CTA_STATS_EARLY_DROP] = "early_drop",
[CTA_STATS_ERROR] = "icmp_error",
[CTA_STATS_SEARCH_RESTART] = "search_restart",
},
.metrics = {},
.tb_exp = {},
.attr2name_exp = {
[CTA_STATS_EXP_NEW] = "new",
[CTA_STATS_EXP_CREATE] = "created",
[CTA_STATS_EXP_DELETE] = "deleted",
},
.metrics_exp = {}
};
static int nfstat_init(int update_every) {
nfstat_root.update_every = update_every;
nfstat_root.buf_size = mnl_buffer_size();
nfstat_root.buf = mallocz(nfstat_root.buf_size);
nfstat_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
if(!nfstat_root.mnl) {
error("NFSTAT: mnl_socket_open() failed");
return 1;
}
nfstat_root.seq = (unsigned int)now_realtime_sec() - 1;
if(mnl_socket_bind(nfstat_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
error("NFSTAT: mnl_socket_bind() failed");
return 1;
}
nfstat_root.portid = mnl_socket_get_portid(nfstat_root.mnl);
return 0;
}
static void nfstat_cleanup() {
if(nfstat_root.mnl) {
mnl_socket_close(nfstat_root.mnl);
nfstat_root.mnl = NULL;
}
freez(nfstat_root.buf);
nfstat_root.buf = NULL;
nfstat_root.buf_size = 0;
}
static struct nlmsghdr * nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type, uint8_t family, uint32_t seq) {
struct nlmsghdr *nlh;
struct nfgenmsg *nfh;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = (subsys << 8) | type;
nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
nlh->nlmsg_seq = seq;
nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
nfh->nfgen_family = family;
nfh->version = NFNETLINK_V0;
nfh->res_id = 0;
return nlh;
}
static int nfct_stats_attr_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, CTA_STATS_MAX) < 0)
return MNL_CB_OK;
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
error("NFSTAT: mnl_attr_validate() failed");
return MNL_CB_ERROR;
}
tb[type] = attr;
return MNL_CB_OK;
}
static int nfstat_callback(const struct nlmsghdr *nlh, void *data) {
(void)data;
struct nfgenmsg *nfg