12 #include <arpa/inet.h>
13 #include <netinet/in.h>
17 #include <libmnl/libmnl.h>
18 #include <linux/netlink.h>
19 #include <linux/netfilter/nfnetlink.h>
20 #include <linux/netfilter/nfnetlink_conntrack.h>
22 #include <sys/queue.h>
36 static LIST_HEAD(nstats_head,
nstats) nstats_head;
38 static
int parse_counters_cb(const struct nlattr *attr,
void *data)
40 const struct nlattr **tb = data;
41 int type = mnl_attr_get_type(attr);
43 if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0)
47 case CTA_COUNTERS_PACKETS:
48 case CTA_COUNTERS_BYTES:
49 if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {
50 perror(
"mnl_attr_validate");
59 static void parse_counters(
const struct nlattr *nest,
struct nstats *ns)
61 struct nlattr *tb[CTA_COUNTERS_MAX+1] = {};
63 mnl_attr_parse_nested(nest, parse_counters_cb, tb);
64 if (tb[CTA_COUNTERS_PACKETS])
65 ns->pkts += be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS]));
67 if (tb[CTA_COUNTERS_BYTES])
68 ns->bytes += be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES]));
71 static int parse_ip_cb(
const struct nlattr *attr,
void *data)
73 const struct nlattr **tb = data;
74 int type = mnl_attr_get_type(attr);
76 if (mnl_attr_type_valid(attr, CTA_IP_MAX) < 0)
82 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
83 perror(
"mnl_attr_validate");
89 if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
90 sizeof(
struct in6_addr)) < 0) {
91 perror(
"mnl_attr_validate2");
100 static void parse_ip(
const struct nlattr *nest,
struct nstats *ns)
102 struct nlattr *tb[CTA_IP_MAX+1] = {};
104 mnl_attr_parse_nested(nest, parse_ip_cb, tb);
105 if (tb[CTA_IP_V4_SRC]) {
106 struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_SRC]);
108 ns->family = AF_INET;
110 if (tb[CTA_IP_V6_SRC]) {
111 struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_SRC]);
113 ns->family = AF_INET6;
117 static int parse_tuple_cb(
const struct nlattr *attr,
void *data)
119 const struct nlattr **tb = data;
120 int type = mnl_attr_get_type(attr);
122 if (mnl_attr_type_valid(attr, CTA_TUPLE_MAX) < 0)
127 if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
128 perror(
"mnl_attr_validate");
137 static void parse_tuple(
const struct nlattr *nest,
struct nstats *ns)
139 struct nlattr *tb[CTA_TUPLE_MAX+1] = {};
141 mnl_attr_parse_nested(nest, parse_tuple_cb, tb);
142 if (tb[CTA_TUPLE_IP])
143 parse_ip(tb[CTA_TUPLE_IP], ns);
146 static int data_attr_cb(
const struct nlattr *attr,
void *data)
148 const struct nlattr **tb = data;
149 int type = mnl_attr_get_type(attr);
151 if (mnl_attr_type_valid(attr, CTA_MAX) < 0)
156 case CTA_COUNTERS_ORIG:
157 case CTA_COUNTERS_REPLY:
158 if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
159 perror(
"mnl_attr_validate");
168 static int data_cb(
const struct nlmsghdr *nlh,
void *data)
170 struct nlattr *tb[CTA_MAX+1] = {};
171 struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
172 struct nstats ns = {}, *cur, *
new;
174 mnl_attr_parse(nlh,
sizeof(*nfg), data_attr_cb, tb);
175 if (tb[CTA_TUPLE_ORIG])
176 parse_tuple(tb[CTA_TUPLE_ORIG], &ns);
178 if (tb[CTA_COUNTERS_ORIG])
179 parse_counters(tb[CTA_COUNTERS_ORIG], &ns);
181 if (tb[CTA_COUNTERS_REPLY])
182 parse_counters(tb[CTA_COUNTERS_REPLY], &ns);
185 LIST_FOREACH(cur, &nstats_head, list) {
186 if (memcmp(&ns.ip6, &cur->ip6,
sizeof(
struct in6_addr)) == 0) {
188 cur->pkts += ns.pkts;
189 cur->bytes += ns.bytes;
195 new = calloc(1,
sizeof(
struct nstats));
199 new->family = ns.family;
202 new->bytes = ns.bytes;
204 LIST_INSERT_HEAD(&nstats_head,
new, list);
211 char buf[MNL_SOCKET_BUFFER_SIZE];
214 ret = mnl_socket_recvfrom(nl, buf,
sizeof(buf));
219 if (errno == ENOBUFS) {
220 fprintf(stderr,
"The daemon has hit ENOBUFS, you can "
221 "increase the size of your receiver "
222 "buffer to mitigate this or enable "
223 "reliable delivery.\n");
225 perror(
"mnl_socket_recvfrom");
230 ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL);
232 perror(
"mnl_cb_run");
234 }
else if (ret <= MNL_CB_STOP)
240 int main(
int argc,
char *argv[])
243 char buf[MNL_SOCKET_BUFFER_SIZE];
244 struct nlmsghdr *nlh;
245 struct nfgenmsg *nfh;
247 struct timeval tv = {};
248 int ret, secs, on = 1, buffersize = (1 << 22);
251 printf(
"Usage: %s <poll-secs>\n", argv[0]);
254 secs = atoi(argv[1]);
256 LIST_INIT(&nstats_head);
258 printf(
"Polling every %d seconds from kernel...\n", secs);
267 nl = mnl_socket_open(NETLINK_NETFILTER);
269 perror(
"mnl_socket_open");
276 if (mnl_socket_bind(nl, NF_NETLINK_CONNTRACK_DESTROY,
277 MNL_SOCKET_AUTOPID) < 0) {
278 perror(
"mnl_socket_bind");
283 setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE,
284 &buffersize,
sizeof(socklen_t));
297 mnl_socket_setsockopt(nl, NETLINK_BROADCAST_ERROR, &on,
sizeof(
int));
298 mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &on,
sizeof(
int));
300 nlh = mnl_nlmsg_put_header(buf);
302 nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) |
303 IPCTNL_MSG_CT_GET_CTRZERO;
304 nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
306 nfh = mnl_nlmsg_put_extra_header(nlh,
sizeof(
struct nfgenmsg));
307 nfh->nfgen_family = AF_INET;
308 nfh->version = NFNETLINK_V0;
312 mnl_attr_put_u32(nlh, CTA_MARK, htonl(0));
313 mnl_attr_put_u32(nlh, CTA_MARK_MASK, htonl(0xffffffff));
316 int fd_max = mnl_socket_get_fd(nl);
320 if (tv.tv_sec == 0 && tv.tv_usec == 0) {
322 ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
324 perror(
"mnl_socket_sendto");
331 LIST_FOREACH(cur, &nstats_head, list) {
332 char out[INET6_ADDRSTRLEN];
334 if (inet_ntop(cur->family, &cur->ip, out,
sizeof(out)))
335 printf(
"src=%s ", out);
337 printf(
"counters %"PRIu64
" %"PRIu64
"\n",
338 cur->pkts, cur->bytes);
343 FD_SET(mnl_socket_get_fd(nl), &readfds);
345 ret = select(fd_max+1, &readfds, NULL, NULL, &tv);
355 if (FD_ISSET(mnl_socket_get_fd(nl), &readfds)) {
361 mnl_socket_close(nl);