libnetfilter_cttimeout  1.0.1
libnetfilter_cttimeout.c
1 /*
2  * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published
6  * by the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This code has been sponsored by Vyatta Inc. <http://www.vyatta.com>
10  */
11 #include "internal.h"
12 
13 #include <time.h>
14 #include <endian.h>
15 #include <stdint.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <netinet/in.h>
19 
20 #include <libmnl/libmnl.h>
21 #include <linux/netfilter/nfnetlink.h>
22 #include <linux/netfilter/nfnetlink_cttimeout.h>
23 
24 #include <libnetfilter_cttimeout/libnetfilter_cttimeout.h>
25 
26 static const char *const tcp_state_to_name[] = {
27  [NFCT_TIMEOUT_ATTR_TCP_SYN_SENT] = "SYN_SENT",
28  [NFCT_TIMEOUT_ATTR_TCP_SYN_RECV] = "SYN_RECV",
29  [NFCT_TIMEOUT_ATTR_TCP_ESTABLISHED] = "ESTABLISHED",
30  [NFCT_TIMEOUT_ATTR_TCP_FIN_WAIT] = "FIN_WAIT",
31  [NFCT_TIMEOUT_ATTR_TCP_CLOSE_WAIT] = "CLOSE_WAIT",
32  [NFCT_TIMEOUT_ATTR_TCP_LAST_ACK] = "LAST_ACK",
33  [NFCT_TIMEOUT_ATTR_TCP_TIME_WAIT] = "TIME_WAIT",
34  [NFCT_TIMEOUT_ATTR_TCP_CLOSE] = "CLOSE",
35  [NFCT_TIMEOUT_ATTR_TCP_SYN_SENT2] = "SYN_SENT2",
36  [NFCT_TIMEOUT_ATTR_TCP_RETRANS] = "RETRANS",
37  [NFCT_TIMEOUT_ATTR_TCP_UNACK] = "UNACKNOWLEDGED",
38 };
39 
40 static const char *const generic_state_to_name[] = {
41  [NFCT_TIMEOUT_ATTR_GENERIC] = "TIMEOUT",
42 };
43 
44 static const char *const udp_state_to_name[] = {
45  [NFCT_TIMEOUT_ATTR_UDP_UNREPLIED] = "UNREPLIED",
46  [NFCT_TIMEOUT_ATTR_UDP_REPLIED] = "REPLIED",
47 };
48 
49 static const char *const sctp_state_to_name[] = {
50  [NFCT_TIMEOUT_ATTR_SCTP_CLOSED] = "CLOSED",
51  [NFCT_TIMEOUT_ATTR_SCTP_COOKIE_WAIT] = "COOKIE_WAIT",
52  [NFCT_TIMEOUT_ATTR_SCTP_COOKIE_ECHOED] = "COOKIE_ECHOED",
53  [NFCT_TIMEOUT_ATTR_SCTP_ESTABLISHED] = "ESTABLISHED",
54  [NFCT_TIMEOUT_ATTR_SCTP_SHUTDOWN_SENT] = "SHUTDOWN_SENT",
55  [NFCT_TIMEOUT_ATTR_SCTP_SHUTDOWN_RECD] = "SHUTDOWN_RECD",
56  [NFCT_TIMEOUT_ATTR_SCTP_SHUTDOWN_ACK_SENT] = "SHUTDOWN_ACK_SENT",
57 };
58 
59 static const char *const dccp_state_to_name[] = {
60  [NFCT_TIMEOUT_ATTR_DCCP_REQUEST] = "REQUEST",
61  [NFCT_TIMEOUT_ATTR_DCCP_RESPOND] = "RESPOND",
62  [NFCT_TIMEOUT_ATTR_DCCP_PARTOPEN] = "PARTOPEN",
63  [NFCT_TIMEOUT_ATTR_DCCP_OPEN] = "OPEN",
64  [NFCT_TIMEOUT_ATTR_DCCP_CLOSEREQ] = "CLOSEREQ",
65  [NFCT_TIMEOUT_ATTR_DCCP_CLOSING] = "CLOSING",
66  [NFCT_TIMEOUT_ATTR_DCCP_TIMEWAIT] = "TIMEWAIT",
67 };
68 
69 static const char *const icmp_state_to_name[] = {
70  [NFCT_TIMEOUT_ATTR_ICMP] = "TIMEOUT",
71 };
72 
73 static const char *const icmpv6_state_to_name[] = {
74  [NFCT_TIMEOUT_ATTR_ICMPV6] = "TIMEOUT",
75 };
76 
77 static struct {
78  uint32_t nlattr_max;
79  uint32_t attr_max;
80  const char *const *state_to_name;
81 } timeout_protocol[IPPROTO_MAX] = {
82  [IPPROTO_ICMP] = {
83  .nlattr_max = __CTA_TIMEOUT_ICMP_MAX,
84  .attr_max = NFCT_TIMEOUT_ATTR_ICMP_MAX,
85  .state_to_name = icmp_state_to_name,
86  },
87  [IPPROTO_TCP] = {
88  .nlattr_max = __CTA_TIMEOUT_TCP_MAX,
89  .attr_max = NFCT_TIMEOUT_ATTR_TCP_MAX,
90  .state_to_name = tcp_state_to_name,
91  },
92  [IPPROTO_UDP] = {
93  .nlattr_max = __CTA_TIMEOUT_UDP_MAX,
94  .attr_max = NFCT_TIMEOUT_ATTR_UDP_MAX,
95  .state_to_name = udp_state_to_name,
96  },
97  [IPPROTO_GRE] = {
98  .nlattr_max = __CTA_TIMEOUT_GRE_MAX,
99  .attr_max = NFCT_TIMEOUT_ATTR_GRE_MAX,
100  .state_to_name = udp_state_to_name,
101  },
102  [IPPROTO_SCTP] = {
103  .nlattr_max = __CTA_TIMEOUT_SCTP_MAX,
104  .attr_max = NFCT_TIMEOUT_ATTR_SCTP_MAX,
105  .state_to_name = sctp_state_to_name,
106  },
107  [IPPROTO_DCCP] = {
108  .nlattr_max = __CTA_TIMEOUT_DCCP_MAX,
109  .attr_max = NFCT_TIMEOUT_ATTR_DCCP_MAX,
110  .state_to_name = dccp_state_to_name,
111  },
112  [IPPROTO_UDPLITE] = {
113  .nlattr_max = __CTA_TIMEOUT_UDPLITE_MAX,
114  .attr_max = NFCT_TIMEOUT_ATTR_UDPLITE_MAX,
115  .state_to_name = udp_state_to_name,
116  },
117  [IPPROTO_ICMPV6] = {
118  .nlattr_max = __CTA_TIMEOUT_ICMPV6_MAX,
119  .attr_max = NFCT_TIMEOUT_ATTR_ICMPV6_MAX,
120  .state_to_name = icmpv6_state_to_name,
121  },
122  /* add your new supported protocol tracker here. */
123  [IPPROTO_RAW] = {
124  .nlattr_max = __CTA_TIMEOUT_GENERIC_MAX,
125  .attr_max = NFCT_TIMEOUT_ATTR_GENERIC_MAX,
126  .state_to_name = generic_state_to_name,
127  },
128 };
129 
130 
131 struct nfct_timeout {
132  char name[32]; /* object name. */
133  uint16_t l3num; /* AF_INET, ... */
134  uint8_t l4num; /* UDP, TCP, ... */
135  uint16_t attrset;
136 
137  uint32_t *timeout; /* array of timeout. */
138  uint16_t polset;
139 };
140 
190 struct nfct_timeout __EXPORTED *nfct_timeout_alloc(void)
191 {
192  struct nfct_timeout *t;
193 
194  t = calloc(1, sizeof(struct nfct_timeout));
195  if (t == NULL)
196  return NULL;
197 
198  return t;
199 }
200 
205 void __EXPORTED nfct_timeout_free(struct nfct_timeout *t)
206 {
207  if (t->timeout)
208  free(t->timeout);
209  free(t);
210 }
211 
218 int __EXPORTED
219 nfct_timeout_attr_set(struct nfct_timeout *t, uint32_t type, const void *data)
220 {
221  switch(type) {
222  case NFCT_TIMEOUT_ATTR_NAME:
223  strncpy(t->name, data, sizeof(t->name));
224  t->name[sizeof(t->name)-1] = '\0';
225  break;
226  case NFCT_TIMEOUT_ATTR_L3PROTO:
227  t->l3num = *((uint16_t *) data);
228  break;
229  case NFCT_TIMEOUT_ATTR_L4PROTO:
230  t->l4num = *((uint8_t *) data);
231  break;
232  /* NFCT_TIMEOUT_ATTR_POLICY is set by nfct_timeout_policy_attr_set. */
233  }
234  t->attrset |= (1 << type);
235  return 0;
236 }
237 
244 int __EXPORTED
245 nfct_timeout_attr_set_u8(struct nfct_timeout *t, uint32_t type, uint8_t data)
246 {
247  return nfct_timeout_attr_set(t, type, &data);
248 }
249 
256 int __EXPORTED
257 nfct_timeout_attr_set_u16(struct nfct_timeout *t, uint32_t type, uint16_t data)
258 {
259  return nfct_timeout_attr_set(t, type, &data);
260 }
261 
267 void __EXPORTED nfct_timeout_attr_unset(struct nfct_timeout *t, uint32_t type)
268 {
269  t->attrset &= ~(1 << type);
270 }
271 
278 int __EXPORTED
279 nfct_timeout_policy_attr_set_u32(struct nfct_timeout *t,
280  uint32_t type, uint32_t data)
281 {
282  size_t timeout_array_size;
283 
284  /* Layer 4 protocol needs to be already set. */
285  if (!(t->attrset & (1 << NFCT_TIMEOUT_ATTR_L4PROTO)))
286  return -1;
287 
288  if (t->timeout == NULL) {
289  /* if not supported, default to generic protocol tracker. */
290  if (timeout_protocol[t->l4num].attr_max != 0) {
291  timeout_array_size =
292  sizeof(uint32_t) *
293  timeout_protocol[t->l4num].attr_max;
294  } else {
295  timeout_array_size =
296  sizeof(uint32_t) *
297  timeout_protocol[IPPROTO_RAW].attr_max;
298  }
299  t->timeout = calloc(1, timeout_array_size);
300  if (t->timeout == NULL)
301  return -1;
302  }
303 
304  /* this state does not exists in this protocol tracker. */
305  if (type > timeout_protocol[t->l4num].attr_max)
306  return -1;
307 
308  t->timeout[type] = data;
309  t->polset |= (1 << type);
310 
311  if (!(t->attrset & (1 << NFCT_TIMEOUT_ATTR_POLICY)))
312  t->attrset |= (1 << NFCT_TIMEOUT_ATTR_POLICY);
313 
314  return 0;
315 }
316 
322 void __EXPORTED
323 nfct_timeout_policy_attr_unset(struct nfct_timeout *t, uint32_t type)
324 {
325  t->attrset &= ~(1 << type);
326 }
327 
336 const char __EXPORTED *
337 nfct_timeout_policy_attr_to_name(uint8_t l4proto, uint32_t state)
338 {
339  if (timeout_protocol[l4proto].state_to_name == NULL) {
340  printf("no array state name\n");
341  return NULL;
342  }
343 
344  if (timeout_protocol[l4proto].state_to_name[state] == NULL) {
345  printf("state %d does not exists\n", state);
346  return NULL;
347  }
348 
349  return timeout_protocol[l4proto].state_to_name[state];
350 }
351 
361 static int
362 nfct_timeout_snprintf_default(char *buf, size_t size,
363  const struct nfct_timeout *t,
364  unsigned int flags)
365 {
366  int ret = 0;
367  unsigned int offset = 0;
368 
369  if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_NAME)) {
370  ret = snprintf(buf+offset, size, ".%s = {\n", t->name);
371  offset += ret;
372  size -= ret;
373  }
374  if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_L3PROTO)) {
375  ret = snprintf(buf+offset, size, "\t.l3proto = %u,\n",
376  t->l3num);
377  offset += ret;
378  size -= ret;
379  }
380  if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_L4PROTO)) {
381  ret = snprintf(buf+offset, size, "\t.l4proto = %u,\n",
382  t->l4num);
383  offset += ret;
384  size -= ret;
385  }
386  if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_POLICY)) {
387  uint8_t l4num = t->l4num;
388  int i;
389 
390  /* default to generic protocol tracker. */
391  if (timeout_protocol[t->l4num].attr_max == 0)
392  l4num = IPPROTO_RAW;
393 
394  ret = snprintf(buf+offset, size, "\t.policy = {\n");
395  offset += ret;
396  size -= ret;
397 
398  for (i=0; i<timeout_protocol[l4num].attr_max; i++) {
399  const char *state_name =
400  timeout_protocol[l4num].state_to_name[i][0] ?
401  timeout_protocol[l4num].state_to_name[i] :
402  "UNKNOWN";
403 
404  ret = snprintf(buf+offset, size,
405  "\t\t.%s = %u,\n", state_name, t->timeout[i]);
406  offset += ret;
407  size -= ret;
408  }
409 
410  ret = snprintf(buf+offset, size, "\t},\n");
411  offset += ret;
412  size -= ret;
413  }
414  ret = snprintf(buf+offset, size, "};");
415  offset += ret;
416  size -= ret;
417 
418  buf[offset]='\0';
419 
420  return ret;
421 }
422 
434 int __EXPORTED
435 nfct_timeout_snprintf(char *buf, size_t size, const struct nfct_timeout *t,
436  unsigned int type, unsigned int flags)
437 {
438  int ret = 0;
439 
440  switch(type) {
441  case NFCT_TIMEOUT_O_DEFAULT:
442  ret = nfct_timeout_snprintf_default(buf, size, t, flags);
443  break;
444  /* add your new output here. */
445  default:
446  break;
447  }
448 
449  return ret;
450 }
451 
473 struct nlmsghdr __EXPORTED *
474 nfct_timeout_nlmsg_build_hdr(char *buf, uint8_t cmd,
475  uint16_t flags, uint32_t seq)
476 {
477  struct nlmsghdr *nlh;
478  struct nfgenmsg *nfh;
479 
480  nlh = mnl_nlmsg_put_header(buf);
481  nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8) | cmd;
482  nlh->nlmsg_flags = NLM_F_REQUEST | flags;
483  nlh->nlmsg_seq = seq;
484 
485  nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
486  nfh->nfgen_family = AF_UNSPEC;
487  nfh->version = NFNETLINK_V0;
488  nfh->res_id = 0;
489 
490  return nlh;
491 }
492 
498 void __EXPORTED
499 nfct_timeout_nlmsg_build_payload(struct nlmsghdr *nlh,
500  const struct nfct_timeout *t)
501 {
502  int i;
503  struct nlattr *nest;
504 
505  if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_NAME))
506  mnl_attr_put_strz(nlh, CTA_TIMEOUT_NAME, t->name);
507 
508  if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_L3PROTO))
509  mnl_attr_put_u16(nlh, CTA_TIMEOUT_L3PROTO, htons(t->l3num));
510 
511  if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_L4PROTO))
512  mnl_attr_put_u8(nlh, CTA_TIMEOUT_L4PROTO, t->l4num);
513 
514  if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_POLICY) && t->polset) {
515  nest = mnl_attr_nest_start(nlh, CTA_TIMEOUT_DATA);
516 
517  for (i=0; i<timeout_protocol[t->l4num].attr_max; i++) {
518  if (t->polset & (1 << i)) {
519  mnl_attr_put_u32(nlh, i+1,
520  htonl(t->timeout[i]));
521  }
522  }
523  mnl_attr_nest_end(nlh, nest);
524  }
525 
526 }
527 
528 static int
529 timeout_nlmsg_parse_attr_cb(const struct nlattr *attr, void *data)
530 {
531  const struct nlattr **tb = data;
532  uint16_t type = mnl_attr_get_type(attr);
533 
534  if (mnl_attr_type_valid(attr, CTA_TIMEOUT_MAX) < 0)
535  return MNL_CB_OK;
536 
537  switch(type) {
538  case CTA_TIMEOUT_NAME:
539  if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
540  perror("mnl_attr_validate");
541  return MNL_CB_ERROR;
542  }
543  break;
544  case CTA_TIMEOUT_L3PROTO:
545  if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
546  perror("mnl_attr_validate");
547  return MNL_CB_ERROR;
548  }
549  break;
550  case CTA_TIMEOUT_L4PROTO:
551  if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) {
552  perror("mnl_attr_validate");
553  return MNL_CB_ERROR;
554  }
555  break;
556  case CTA_TIMEOUT_DATA:
557  if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
558  perror("mnl_attr_validate");
559  return MNL_CB_ERROR;
560  }
561  break;
562  }
563  tb[type] = attr;
564  return MNL_CB_OK;
565 }
566 
567 struct _container_policy_cb {
568  unsigned int nlattr_max;
569  void *tb;
570 };
571 
572 static int
573 parse_timeout_attr_policy_cb(const struct nlattr *attr, void *data)
574 {
575  struct _container_policy_cb *data_cb = data;
576  const struct nlattr **tb = data_cb->tb;
577  uint16_t type = mnl_attr_get_type(attr);
578 
579  if (mnl_attr_type_valid(attr, data_cb->nlattr_max) < 0)
580  return MNL_CB_OK;
581 
582  if (type <= data_cb->nlattr_max) {
583  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
584  perror("mnl_attr_validate");
585  return MNL_CB_ERROR;
586  }
587  tb[type] = attr;
588  }
589  return MNL_CB_OK;
590 }
591 
592 static void
593 timeout_parse_attr_data(struct nfct_timeout *t, const struct nlattr *nest)
594 {
595  unsigned int nlattr_max = timeout_protocol[t->l4num].nlattr_max;
596  struct nlattr *tb[nlattr_max];
597  struct _container_policy_cb cnt = {
598  .nlattr_max = nlattr_max,
599  .tb = tb,
600  };
601  unsigned int i;
602 
603  memset(tb, 0, sizeof(struct nlattr *) * nlattr_max);
604 
605  mnl_attr_parse_nested(nest, parse_timeout_attr_policy_cb, &cnt);
606 
607  for (i=1; i<nlattr_max; i++) {
608  if (tb[i]) {
610  ntohl(mnl_attr_get_u32(tb[i])));
611  }
612  }
613 }
614 
623 int __EXPORTED
624 nfct_timeout_nlmsg_parse_payload(const struct nlmsghdr *nlh,
625  struct nfct_timeout *t)
626 {
627  struct nlattr *tb[CTA_TIMEOUT_MAX+1] = {};
628  struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
629 
630  mnl_attr_parse(nlh, sizeof(*nfg), timeout_nlmsg_parse_attr_cb, tb);
631  if (tb[CTA_TIMEOUT_NAME]) {
632  nfct_timeout_attr_set(t, NFCT_TIMEOUT_ATTR_NAME,
633  mnl_attr_get_str(tb[CTA_TIMEOUT_NAME]));
634  }
635  if (tb[CTA_TIMEOUT_L3PROTO]) {
636  nfct_timeout_attr_set_u16(t, NFCT_TIMEOUT_ATTR_L3PROTO,
637  ntohs(mnl_attr_get_u16(tb[CTA_TIMEOUT_L3PROTO])));
638  }
639  if (tb[CTA_TIMEOUT_L4PROTO]) {
640  nfct_timeout_attr_set_u8(t, NFCT_TIMEOUT_ATTR_L4PROTO,
641  mnl_attr_get_u8(tb[CTA_TIMEOUT_L4PROTO]));
642  }
643  if (tb[CTA_TIMEOUT_DATA]) {
644  timeout_parse_attr_data(t, tb[CTA_TIMEOUT_DATA]);
645  }
646  return 0;
647 }
648 
int __EXPORTED nfct_timeout_snprintf(char *buf, size_t size, const struct nfct_timeout *t, unsigned int type, unsigned int flags)
const char __EXPORTED * nfct_timeout_policy_attr_to_name(uint8_t l4proto, uint32_t state)
int __EXPORTED nfct_timeout_policy_attr_set_u32(struct nfct_timeout *t, uint32_t type, uint32_t data)
void __EXPORTED nfct_timeout_policy_attr_unset(struct nfct_timeout *t, uint32_t type)
int __EXPORTED nfct_timeout_attr_set_u16(struct nfct_timeout *t, uint32_t type, uint16_t data)
void __EXPORTED nfct_timeout_free(struct nfct_timeout *t)
int __EXPORTED nfct_timeout_attr_set(struct nfct_timeout *t, uint32_t type, const void *data)
struct nfct_timeout __EXPORTED * nfct_timeout_alloc(void)
int __EXPORTED nfct_timeout_attr_set_u8(struct nfct_timeout *t, uint32_t type, uint8_t data)
void __EXPORTED nfct_timeout_attr_unset(struct nfct_timeout *t, uint32_t type)
struct nlmsghdr __EXPORTED * nfct_timeout_nlmsg_build_hdr(char *buf, uint8_t cmd, uint16_t flags, uint32_t seq)
void __EXPORTED nfct_timeout_nlmsg_build_payload(struct nlmsghdr *nlh, const struct nfct_timeout *t)
int __EXPORTED nfct_timeout_nlmsg_parse_payload(const struct nlmsghdr *nlh, struct nfct_timeout *t)