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
26static 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
40static const char *const generic_state_to_name[] = {
41 [NFCT_TIMEOUT_ATTR_GENERIC] = "TIMEOUT",
42};
43
44static const char *const udp_state_to_name[] = {
45 [NFCT_TIMEOUT_ATTR_UDP_UNREPLIED] = "UNREPLIED",
46 [NFCT_TIMEOUT_ATTR_UDP_REPLIED] = "REPLIED",
47};
48
49static 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
59static 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
69static const char *const icmp_state_to_name[] = {
70 [NFCT_TIMEOUT_ATTR_ICMP] = "TIMEOUT",
71};
72
73static const char *const icmpv6_state_to_name[] = {
74 [NFCT_TIMEOUT_ATTR_ICMPV6] = "TIMEOUT",
75};
76
77static 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
131struct 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
190struct 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
205void __EXPORTED nfct_timeout_free(struct nfct_timeout *t)
206{
207 if (t->timeout)
208 free(t->timeout);
209 free(t);
210}
211
218int __EXPORTED
219nfct_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
244int __EXPORTED
245nfct_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
256int __EXPORTED
257nfct_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
267void __EXPORTED nfct_timeout_attr_unset(struct nfct_timeout *t, uint32_t type)
268{
269 t->attrset &= ~(1 << type);
270}
271
278int __EXPORTED
279nfct_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
322void __EXPORTED
323nfct_timeout_policy_attr_unset(struct nfct_timeout *t, uint32_t type)
324{
325 t->attrset &= ~(1 << type);
326}
327
336const char __EXPORTED *
337nfct_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
361static int
362nfct_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
434int __EXPORTED
435nfct_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
473struct nlmsghdr __EXPORTED *
474nfct_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
498void __EXPORTED
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
528static int
529timeout_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
567struct _container_policy_cb {
568 unsigned int nlattr_max;
569 void *tb;
570};
571
572static int
573parse_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
592static void
593timeout_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
623int __EXPORTED
624nfct_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)
int __EXPORTED nfct_timeout_policy_attr_set_u32(struct nfct_timeout *t, uint32_t type, uint32_t data)
const char __EXPORTED * nfct_timeout_policy_attr_to_name(uint8_t l4proto, uint32_t state)
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)
struct nfct_timeout __EXPORTED * nfct_timeout_alloc(void)
int __EXPORTED nfct_timeout_attr_set(struct nfct_timeout *t, uint32_t type, const void *data)
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)