12 #include <arpa/inet.h>
13 #include <netinet/in.h>
16 #include <sys/select.h>
18 #include <libmnl/libmnl.h>
19 #include <linux/netlink.h>
20 #include <linux/netfilter/nfnetlink.h>
21 #include <linux/netfilter/nfnetlink_conntrack.h>
23 #include <sys/queue.h>
37 static LIST_HEAD(nstats_head,
nstats) nstats_head;
39 static
int parse_counters_cb(const struct nlattr *attr,
void *data)
41 const struct nlattr **tb = data;
42 int type = mnl_attr_get_type(attr);
44 if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0)
48 case CTA_COUNTERS_PACKETS:
49 case CTA_COUNTERS_BYTES:
50 if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {
51 perror(
"mnl_attr_validate");
60 static void parse_counters(
const struct nlattr *nest,
struct nstats *ns)
62 struct nlattr *tb[CTA_COUNTERS_MAX+1] = {};
64 mnl_attr_parse_nested(nest, parse_counters_cb, tb);
65 if (tb[CTA_COUNTERS_PACKETS])
66 ns->pkts += be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS]));
68 if (tb[CTA_COUNTERS_BYTES])
69 ns->bytes += be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES]));
72 static int parse_ip_cb(
const struct nlattr *attr,
void *data)
74 const struct nlattr **tb = data;
75 int type = mnl_attr_get_type(attr);
77 if (mnl_attr_type_valid(attr, CTA_IP_MAX) < 0)
83 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
84 perror(
"mnl_attr_validate");
90 if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
91 sizeof(
struct in6_addr)) < 0) {
92 perror(
"mnl_attr_validate2");
101 static void parse_ip(
const struct nlattr *nest,
struct nstats *ns)
103 struct nlattr *tb[CTA_IP_MAX+1] = {};
105 mnl_attr_parse_nested(nest, parse_ip_cb, tb);
106 if (tb[CTA_IP_V4_SRC]) {
107 struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_SRC]);
109 ns->family = AF_INET;
111 if (tb[CTA_IP_V6_SRC]) {
112 struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_SRC]);
114 ns->family = AF_INET6;
118 static int parse_tuple_cb(
const struct nlattr *attr,
void *data)
120 const struct nlattr **tb = data;
121 int type = mnl_attr_get_type(attr);
123 if (mnl_attr_type_valid(attr, CTA_TUPLE_MAX) < 0)
128 if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
129 perror(
"mnl_attr_validate");
138 static void parse_tuple(
const struct nlattr *nest,
struct nstats *ns)
140 struct nlattr *tb[CTA_TUPLE_MAX+1] = {};
142 mnl_attr_parse_nested(nest, parse_tuple_cb, tb);
143 if (tb[CTA_TUPLE_IP])
144 parse_ip(tb[CTA_TUPLE_IP], ns);
147 static int data_attr_cb(
const struct nlattr *attr,
void *data)
149 const struct nlattr **tb = data;
150 int type = mnl_attr_get_type(attr);
152 if (mnl_attr_type_valid(attr, CTA_MAX) < 0)
157 case CTA_COUNTERS_ORIG:
158 case CTA_COUNTERS_REPLY:
159 if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
160 perror(
"mnl_attr_validate");
169 static int data_cb(
const struct nlmsghdr *nlh,
void *data)
171 struct nlattr *tb[CTA_MAX+1] = {};
172 struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
173 struct nstats ns = {}, *cur, *
new;
175 mnl_attr_parse(nlh,
sizeof(*nfg), data_attr_cb, tb);
176 if (tb[CTA_TUPLE_ORIG])
177 parse_tuple(tb[CTA_TUPLE_ORIG], &ns);
179 if (tb[CTA_COUNTERS_ORIG])
180 parse_counters(tb[CTA_COUNTERS_ORIG], &ns);
182 if (tb[CTA_COUNTERS_REPLY])
183 parse_counters(tb[CTA_COUNTERS_REPLY], &ns);
186 LIST_FOREACH(cur, &nstats_head, list) {
187 if (memcmp(&ns.ip6, &cur->ip6,
sizeof(
struct in6_addr)) == 0) {
189 cur->pkts += ns.pkts;
190 cur->bytes += ns.bytes;
196 new = calloc(1,
sizeof(
struct nstats));
200 new->family = ns.family;
203 new->bytes = ns.bytes;
205 LIST_INSERT_HEAD(&nstats_head,
new, list);
212 char buf[MNL_SOCKET_BUFFER_SIZE];
215 ret = mnl_socket_recvfrom(nl, buf,
sizeof(buf));
220 if (errno == ENOBUFS) {
221 fprintf(stderr,
"The daemon has hit ENOBUFS, you can "
222 "increase the size of your receiver "
223 "buffer to mitigate this or enable "
224 "reliable delivery.\n");
226 perror(
"mnl_socket_recvfrom");
231 ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL);
233 perror(
"mnl_cb_run");
235 }
else if (ret <= MNL_CB_STOP)
241 int main(
int argc,
char *argv[])
244 char buf[MNL_SOCKET_BUFFER_SIZE];
245 struct nlmsghdr *nlh;
246 struct nfgenmsg *nfh;
248 struct timeval tv = {};
249 int ret, secs, on = 1, buffersize = (1 << 22);
252 printf(
"Usage: %s <poll-secs>\n", argv[0]);
255 secs = atoi(argv[1]);
257 LIST_INIT(&nstats_head);
259 printf(
"Polling every %d seconds from kernel...\n", secs);
268 nl = mnl_socket_open(NETLINK_NETFILTER);
270 perror(
"mnl_socket_open");
277 if (mnl_socket_bind(nl, NF_NETLINK_CONNTRACK_DESTROY,
278 MNL_SOCKET_AUTOPID) < 0) {
279 perror(
"mnl_socket_bind");
284 setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE,
285 &buffersize,
sizeof(socklen_t));
298 mnl_socket_setsockopt(nl, NETLINK_BROADCAST_ERROR, &on,
sizeof(
int));
299 mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &on,
sizeof(
int));
301 nlh = mnl_nlmsg_put_header(buf);
303 nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) |
304 IPCTNL_MSG_CT_GET_CTRZERO;
305 nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
307 nfh = mnl_nlmsg_put_extra_header(nlh,
sizeof(
struct nfgenmsg));
308 nfh->nfgen_family = AF_INET;
309 nfh->version = NFNETLINK_V0;
313 mnl_attr_put_u32(nlh, CTA_MARK, htonl(0));
314 mnl_attr_put_u32(nlh, CTA_MARK_MASK, htonl(0xffffffff));
317 int fd_max = mnl_socket_get_fd(nl);
321 if (tv.tv_sec == 0 && tv.tv_usec == 0) {
323 ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
325 perror(
"mnl_socket_sendto");
332 LIST_FOREACH(cur, &nstats_head, list) {
333 char out[INET6_ADDRSTRLEN];
335 if (inet_ntop(cur->family, &cur->ip, out,
sizeof(out)))
336 printf(
"src=%s ", out);
338 printf(
"counters %"PRIu64
" %"PRIu64
"\n",
339 cur->pkts, cur->bytes);
344 FD_SET(mnl_socket_get_fd(nl), &readfds);
346 ret = select(fd_max+1, &readfds, NULL, NULL, &tv);
356 if (FD_ISSET(mnl_socket_get_fd(nl), &readfds)) {
362 mnl_socket_close(nl);