libnetfilter_acct  1.0.3
libnetfilter_acct.c
1 /*
2  * (C) 2011 by Pablo Neira Ayuso <pablo@netfilter.org>
3  * (C) 2011 by Intra2net AG <http://www.intra2net.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the Lesser GNU General Public License as published
7  * by the Free Software Foundation; either version 2.1 of the License, or
8  * (at your option) any later version.
9  */
10 #include "internal.h"
11 
12 #include <time.h>
13 #include <endian.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <inttypes.h>
17 
18 #include <libmnl/libmnl.h>
19 #include <linux/netfilter/nfnetlink.h>
20 #include <linux/netfilter/nfnetlink_acct.h>
21 
22 #include <libnetfilter_acct/libnetfilter_acct.h>
23 
59 struct nfacct {
60  char name[NFACCT_NAME_MAX];
61  uint64_t pkts;
62  uint64_t bytes;
63  uint32_t bitset;
64  uint32_t flags;
65  uint64_t quota;
66 };
67 
79 struct nfacct *nfacct_alloc(void)
80 {
81  return calloc(1, sizeof(struct nfacct));
82 }
83 EXPORT_SYMBOL(nfacct_alloc);
84 
89 void nfacct_free(struct nfacct *nfacct)
90 {
91  free(nfacct);
92 }
93 EXPORT_SYMBOL(nfacct_free);
94 
101 void
102 nfacct_attr_set(struct nfacct *nfacct, enum nfacct_attr_type type,
103  const void *data)
104 {
105  switch(type) {
106  case NFACCT_ATTR_NAME:
107  strncpy(nfacct->name, data, NFACCT_NAME_MAX);
108  nfacct->name[NFACCT_NAME_MAX-1] = '\0';
109  nfacct->bitset |= (1 << NFACCT_ATTR_NAME);
110  break;
111  case NFACCT_ATTR_PKTS:
112  nfacct->pkts = *((uint64_t *) data);
113  nfacct->bitset |= (1 << NFACCT_ATTR_PKTS);
114  break;
115  case NFACCT_ATTR_BYTES:
116  nfacct->bytes = *((uint64_t *) data);
117  nfacct->bitset |= (1 << NFACCT_ATTR_BYTES);
118  break;
119  case NFACCT_ATTR_FLAGS:
120  nfacct->flags = *((uint32_t *) data);
121  nfacct->bitset |= (1 << NFACCT_ATTR_FLAGS);
122  break;
123  case NFACCT_ATTR_QUOTA:
124  nfacct->quota = *((uint64_t *) data);
125  nfacct->bitset |= (1 << NFACCT_ATTR_QUOTA);
126  break;
127  }
128 }
129 EXPORT_SYMBOL(nfacct_attr_set);
130 
137 void
138 nfacct_attr_set_str(struct nfacct *nfacct, enum nfacct_attr_type type,
139  const char *name)
140 {
141  nfacct_attr_set(nfacct, type, name);
142 }
143 EXPORT_SYMBOL(nfacct_attr_set_str);
144 
151 void
152 nfacct_attr_set_u64(struct nfacct *nfacct, enum nfacct_attr_type type,
153  uint64_t value)
154 {
155  nfacct_attr_set(nfacct, type, &value);
156 }
157 EXPORT_SYMBOL(nfacct_attr_set_u64);
158 
164 void
165 nfacct_attr_unset(struct nfacct *nfacct, enum nfacct_attr_type type)
166 {
167  switch(type) {
168  case NFACCT_ATTR_NAME:
169  nfacct->bitset &= ~(1 << NFACCT_ATTR_NAME);
170  break;
171  case NFACCT_ATTR_PKTS:
172  nfacct->bitset &= ~(1 << NFACCT_ATTR_PKTS);
173  break;
174  case NFACCT_ATTR_BYTES:
175  nfacct->bitset &= ~(1 << NFACCT_ATTR_BYTES);
176  break;
177  case NFACCT_ATTR_FLAGS:
178  nfacct->bitset &= ~(1 << NFACCT_ATTR_FLAGS);
179  break;
180  case NFACCT_ATTR_QUOTA:
181  nfacct->bitset &= ~(1 << NFACCT_ATTR_QUOTA);
182  break;
183  }
184 }
185 EXPORT_SYMBOL(nfacct_attr_unset);
186 
195 const void *nfacct_attr_get(struct nfacct *nfacct, enum nfacct_attr_type type)
196 {
197  const void *ret = NULL;
198 
199  switch(type) {
200  case NFACCT_ATTR_NAME:
201  if (nfacct->bitset & (1 << NFACCT_ATTR_NAME))
202  ret = nfacct->name;
203  break;
204  case NFACCT_ATTR_PKTS:
205  if (nfacct->bitset & (1 << NFACCT_ATTR_PKTS))
206  ret = &nfacct->pkts;
207  break;
208  case NFACCT_ATTR_BYTES:
209  if (nfacct->bitset & (1 << NFACCT_ATTR_BYTES))
210  ret = &nfacct->bytes;
211  break;
212  case NFACCT_ATTR_FLAGS:
213  if (nfacct->bitset & (1 << NFACCT_ATTR_FLAGS))
214  ret = &nfacct->flags;
215  break;
216  case NFACCT_ATTR_QUOTA:
217  if (nfacct->bitset & (1 << NFACCT_ATTR_QUOTA))
218  ret = &nfacct->quota;
219  break;
220  }
221  return ret;
222 }
223 EXPORT_SYMBOL(nfacct_attr_get);
224 
233 const char *
234 nfacct_attr_get_str(struct nfacct *nfacct, enum nfacct_attr_type type)
235 {
236  return nfacct_attr_get(nfacct, type);
237 }
238 EXPORT_SYMBOL(nfacct_attr_get_str);
239 
248 uint64_t nfacct_attr_get_u64(struct nfacct *nfacct, enum nfacct_attr_type type)
249 {
250  const void *ret = nfacct_attr_get(nfacct, type);
251  return ret ? *((uint64_t *)ret) : 0;
252 }
253 EXPORT_SYMBOL(nfacct_attr_get_u64);
254 
255 static int
256 nfacct_snprintf_plain(char *buf, size_t rem, struct nfacct *nfacct,
257  uint16_t flags)
258 {
259  int ret, offset = 0, len = 0;
260 
261  if (flags & NFACCT_SNPRINTF_F_FULL) {
262  ret = snprintf(buf, rem,
263  "{ pkts = %.20"PRIu64", bytes = %.20"PRIu64"",
264  nfacct_attr_get_u64(nfacct, NFACCT_ATTR_PKTS),
265  nfacct_attr_get_u64(nfacct, NFACCT_ATTR_BYTES));
266  SNPRINTF_CHECK(ret, rem, offset, len);
267 
268  if (nfacct->flags) {
269  uint32_t mode;
270  char *mode_name;
271 
272  mode = nfacct_attr_get_u64(nfacct, NFACCT_ATTR_FLAGS);
273  if (mode & NFACCT_F_QUOTA_PKTS)
274  mode_name = "packet";
275  else if (mode & NFACCT_F_QUOTA_BYTES)
276  mode_name = "byte";
277  else
278  mode_name = "unknown";
279 
280  ret = snprintf(buf + offset, rem,
281  ", quota = %.20"PRIu64", mode = %s"\
282  ", overquota = %s",
283  nfacct_attr_get_u64(nfacct, NFACCT_ATTR_QUOTA),
284  mode_name,
285  mode & NFACCT_F_OVERQUOTA ? "yes" : "no");
286  SNPRINTF_CHECK(ret, rem, offset, len);
287  }
288 
289  ret = snprintf(buf + offset, rem, " } = %s;",
290  nfacct_attr_get_str(nfacct, NFACCT_ATTR_NAME));
291  } else {
292  ret = snprintf(buf, rem, "%s\n",
293  nfacct_attr_get_str(nfacct, NFACCT_ATTR_NAME));
294  }
295  SNPRINTF_CHECK(ret, rem, offset, len);
296 
297  return len;
298 }
299 
300 static int
301 nfacct_snprintf_json(char *buf, size_t rem, struct nfacct *nfacct,
302  uint16_t flags)
303 {
304  int ret = 0, offset = 0, len = 0;
305 
306  if (flags & NFACCT_SNPRINTF_F_FULL) {
307  ret = snprintf(buf, rem,
308  " { \"pkts\" : %"PRIu64", \"bytes\" : %"PRIu64"",
309  nfacct_attr_get_u64(nfacct, NFACCT_ATTR_PKTS),
310  nfacct_attr_get_u64(nfacct, NFACCT_ATTR_BYTES));
311  SNPRINTF_CHECK(ret, rem, offset, len);
312 
313  if (nfacct->flags) {
314  uint32_t mode;
315  char *mode_name;
316 
317  mode = nfacct_attr_get_u64(nfacct, NFACCT_ATTR_FLAGS);
318  if (mode & NFACCT_F_QUOTA_PKTS)
319  mode_name = "packet";
320  else if (mode & NFACCT_F_QUOTA_BYTES)
321  mode_name = "byte";
322  else
323  mode_name = "unknown";
324 
325  ret = snprintf(buf + offset, rem,
326  ", \"quota\" : %"PRIu64", \"mode\" = \"%s\""\
327  ", \"overquota\" = %u",
328  nfacct_attr_get_u64(nfacct, NFACCT_ATTR_QUOTA),
329  mode_name,
330  mode & NFACCT_F_OVERQUOTA ? 1 : 0);
331  SNPRINTF_CHECK(ret, rem, offset, len);
332  }
333 
334  ret = snprintf(buf + offset, rem, ", \"name\" : \"%s\" }",
335  nfacct_attr_get_str(nfacct, NFACCT_ATTR_NAME));
336  }
337  /* non-F_FULL doesn't seem to make sense in JSON */
338  SNPRINTF_CHECK(ret, rem, offset, len);
339 
340  return len;
341 }
342 
343 #define BUFFER_SIZE(ret, size, rem, offset) \
344  size += ret; \
345  if (ret > rem) \
346  ret = rem; \
347  offset += ret; \
348  rem -= ret;
349 
350 static int
351 nfacct_snprintf_xml_localtime(char *buf, unsigned int rem, const struct tm *tm)
352 {
353  int ret = 0;
354  unsigned int size = 0, offset = 0;
355 
356  ret = snprintf(buf+offset, rem, "<hour>%d</hour>", tm->tm_hour);
357  BUFFER_SIZE(ret, size, rem, offset);
358 
359  ret = snprintf(buf+offset, rem, "<min>%02d</min>", tm->tm_min);
360  BUFFER_SIZE(ret, size, rem, offset);
361 
362  ret = snprintf(buf+offset, rem, "<sec>%02d</sec>", tm->tm_sec);
363  BUFFER_SIZE(ret, size, rem, offset);
364 
365  ret = snprintf(buf+offset, rem, "<wday>%d</wday>", tm->tm_wday + 1);
366  BUFFER_SIZE(ret, size, rem, offset);
367 
368  ret = snprintf(buf+offset, rem, "<day>%d</day>", tm->tm_mday);
369  BUFFER_SIZE(ret, size, rem, offset);
370 
371  ret = snprintf(buf+offset, rem, "<month>%d</month>", tm->tm_mon + 1);
372  BUFFER_SIZE(ret, size, rem, offset);
373 
374  ret = snprintf(buf+offset, rem, "<year>%d</year>", 1900 + tm->tm_year);
375  BUFFER_SIZE(ret, size, rem, offset);
376 
377  return size;
378 }
379 
380 
381 static int
382 nfacct_snprintf_xml(char *buf, size_t rem, struct nfacct *nfacct,
383  uint16_t flags)
384 {
385  int ret = 0;
386  unsigned int size = 0, offset = 0;
387 
388  ret = snprintf(buf, rem,
389  "<obj><name>%s</name>"
390  "<pkts>%.20"PRIu64"</pkts>"
391  "<bytes>%.20"PRIu64"</bytes>",
392  nfacct_attr_get_str(nfacct, NFACCT_ATTR_NAME),
393  nfacct_attr_get_u64(nfacct, NFACCT_ATTR_PKTS),
394  nfacct_attr_get_u64(nfacct, NFACCT_ATTR_BYTES));
395  BUFFER_SIZE(ret, size, rem, offset);
396 
397  if (flags & NFACCT_SNPRINTF_F_TIME) {
398  time_t t;
399  struct tm tm;
400 
401  t = time(NULL);
402  if (localtime_r(&t, &tm) == NULL)
403  goto err;
404 
405  ret = nfacct_snprintf_xml_localtime(buf+offset, rem, &tm);
406  BUFFER_SIZE(ret, size, rem, offset);
407  }
408 
409  ret = snprintf(buf+offset, rem, "</obj>");
410  BUFFER_SIZE(ret, size, rem, offset);
411 
412 err:
413  return ret;
414 }
415 
427 int nfacct_snprintf(char *buf, size_t size, struct nfacct *nfacct,
428  uint16_t type, uint16_t flags)
429 {
430  int ret = 0;
431 
432  switch(type) {
433  case NFACCT_SNPRINTF_T_PLAIN:
434  ret = nfacct_snprintf_plain(buf, size, nfacct, flags);
435  break;
436  case NFACCT_SNPRINTF_T_XML:
437  ret = nfacct_snprintf_xml(buf, size, nfacct, flags);
438  break;
439  case NFACCT_SNPRINTF_T_JSON:
440  ret = nfacct_snprintf_json(buf, size, nfacct, flags);
441  break;
442  default:
443  ret = -1;
444  break;
445  }
446  return ret;
447 }
448 EXPORT_SYMBOL(nfacct_snprintf);
449 
487 struct nlmsghdr *
488 nfacct_nlmsg_build_hdr(char *buf, uint8_t cmd, uint16_t flags, uint32_t seq)
489 {
490  struct nlmsghdr *nlh;
491  struct nfgenmsg *nfh;
492 
493  nlh = mnl_nlmsg_put_header(buf);
494  nlh->nlmsg_type = (NFNL_SUBSYS_ACCT << 8) | cmd;
495  nlh->nlmsg_flags = NLM_F_REQUEST | flags;
496  nlh->nlmsg_seq = seq;
497 
498  nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
499  nfh->nfgen_family = AF_UNSPEC;
500  nfh->version = NFNETLINK_V0;
501  nfh->res_id = 0;
502 
503  return nlh;
504 }
505 EXPORT_SYMBOL(nfacct_nlmsg_build_hdr);
506 
512 void nfacct_nlmsg_build_payload(struct nlmsghdr *nlh, struct nfacct *nfacct)
513 {
514  if (nfacct->bitset & (1 << NFACCT_ATTR_NAME))
515  mnl_attr_put_strz(nlh, NFACCT_NAME, nfacct->name);
516 
517  if (nfacct->bitset & (1 << NFACCT_ATTR_PKTS))
518  mnl_attr_put_u64(nlh, NFACCT_PKTS, htobe64(nfacct->pkts));
519 
520  if (nfacct->bitset & (1 << NFACCT_ATTR_BYTES))
521  mnl_attr_put_u64(nlh, NFACCT_BYTES, htobe64(nfacct->bytes));
522 
523  if (nfacct->bitset & (1 << NFACCT_ATTR_FLAGS))
524  mnl_attr_put_u32(nlh, NFACCT_FLAGS, htobe32(nfacct->flags));
525 
526  if (nfacct->bitset & (1 << NFACCT_ATTR_QUOTA))
527  mnl_attr_put_u64(nlh, NFACCT_QUOTA, htobe64(nfacct->quota));
528 }
529 EXPORT_SYMBOL(nfacct_nlmsg_build_payload);
530 
531 static int nfacct_nlmsg_parse_attr_cb(const struct nlattr *attr, void *data)
532 {
533  const struct nlattr **tb = data;
534  int type = mnl_attr_get_type(attr);
535 
536  if (mnl_attr_type_valid(attr, NFACCT_MAX) < 0)
537  return MNL_CB_OK;
538 
539  switch(type) {
540  case NFACCT_NAME:
541  if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
542  perror("mnl_attr_validate");
543  return MNL_CB_ERROR;
544  }
545  break;
546  case NFACCT_PKTS:
547  case NFACCT_BYTES:
548  if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {
549  perror("mnl_attr_validate");
550  return MNL_CB_ERROR;
551  }
552  break;
553  }
554  tb[type] = attr;
555  return MNL_CB_OK;
556 }
557 
566 int
567 nfacct_nlmsg_parse_payload(const struct nlmsghdr *nlh, struct nfacct *nfacct)
568 {
569  struct nlattr *tb[NFACCT_MAX+1] = {};
570  struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
571 
572  mnl_attr_parse(nlh, sizeof(*nfg), nfacct_nlmsg_parse_attr_cb, tb);
573  if (!tb[NFACCT_NAME] && !tb[NFACCT_PKTS] && !tb[NFACCT_BYTES])
574  return -1;
575 
576  nfacct_attr_set_str(nfacct, NFACCT_ATTR_NAME,
577  mnl_attr_get_str(tb[NFACCT_NAME]));
578  nfacct_attr_set_u64(nfacct, NFACCT_ATTR_PKTS,
579  be64toh(mnl_attr_get_u64(tb[NFACCT_PKTS])));
580  nfacct_attr_set_u64(nfacct, NFACCT_ATTR_BYTES,
581  be64toh(mnl_attr_get_u64(tb[NFACCT_BYTES])));
582 
583  if (tb[NFACCT_FLAGS] && tb[NFACCT_QUOTA]) {
584  uint32_t flags = be32toh(mnl_attr_get_u32(tb[NFACCT_FLAGS]));
585  nfacct_attr_set(nfacct, NFACCT_ATTR_FLAGS, &flags);
586  nfacct_attr_set_u64(nfacct, NFACCT_ATTR_QUOTA,
587  be64toh(mnl_attr_get_u64(tb[NFACCT_QUOTA])));
588  }
589 
590  return 0;
591 }
592 EXPORT_SYMBOL(nfacct_nlmsg_parse_payload);
593 
const void * nfacct_attr_get(struct nfacct *nfacct, enum nfacct_attr_type type)
void nfacct_nlmsg_build_payload(struct nlmsghdr *nlh, struct nfacct *nfacct)
void nfacct_attr_set(struct nfacct *nfacct, enum nfacct_attr_type type, const void *data)
const char * nfacct_attr_get_str(struct nfacct *nfacct, enum nfacct_attr_type type)
void nfacct_attr_unset(struct nfacct *nfacct, enum nfacct_attr_type type)
void nfacct_free(struct nfacct *nfacct)
uint64_t nfacct_attr_get_u64(struct nfacct *nfacct, enum nfacct_attr_type type)
int nfacct_nlmsg_parse_payload(const struct nlmsghdr *nlh, struct nfacct *nfacct)
struct nlmsghdr * nfacct_nlmsg_build_hdr(char *buf, uint8_t cmd, uint16_t flags, uint32_t seq)
struct nfacct * nfacct_alloc(void)
int nfacct_snprintf(char *buf, size_t size, struct nfacct *nfacct, uint16_t type, uint16_t flags)
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)