libnetfilter_cttimeout  1.0.0
libnetfilter_cttimeout.c
00001 /*
00002  * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
00003  *
00004  * This program is free software; you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License as published
00006  * by the Free Software Foundation; either version 2 of the License, or
00007  * (at your option) any later version.
00008  *
00009  * This code has been sponsored by Vyatta Inc. <http://www.vyatta.com>
00010  */
00011 #include "internal.h"
00012 
00013 #include <time.h>
00014 #include <endian.h>
00015 #include <stdint.h>
00016 #include <stdlib.h>
00017 #include <string.h>
00018 #include <netinet/in.h>
00019 
00020 #include <libmnl/libmnl.h>
00021 #include <linux/netfilter/nfnetlink.h>
00022 #include <linux/netfilter/nfnetlink_cttimeout.h>
00023 
00024 #include <libnetfilter_cttimeout/libnetfilter_cttimeout.h>
00025 
00026 static const char *const tcp_state_to_name[] = {
00027         [NFCT_TIMEOUT_ATTR_TCP_SYN_SENT]        = "SYN_SENT",
00028         [NFCT_TIMEOUT_ATTR_TCP_SYN_RECV]        = "SYN_RECV",
00029         [NFCT_TIMEOUT_ATTR_TCP_ESTABLISHED]     = "ESTABLISHED",
00030         [NFCT_TIMEOUT_ATTR_TCP_FIN_WAIT]        = "FIN_WAIT",
00031         [NFCT_TIMEOUT_ATTR_TCP_CLOSE_WAIT]      = "CLOSE_WAIT",
00032         [NFCT_TIMEOUT_ATTR_TCP_LAST_ACK]        = "LAST_ACK",
00033         [NFCT_TIMEOUT_ATTR_TCP_TIME_WAIT]       = "TIME_WAIT",
00034         [NFCT_TIMEOUT_ATTR_TCP_CLOSE]           = "CLOSE",
00035         [NFCT_TIMEOUT_ATTR_TCP_SYN_SENT2]       = "SYN_SENT2",
00036         [NFCT_TIMEOUT_ATTR_TCP_RETRANS]         = "RETRANS",
00037         [NFCT_TIMEOUT_ATTR_TCP_UNACK]           = "UNACKNOWLEDGED",
00038 };
00039 
00040 static const char *const generic_state_to_name[] = {
00041         [NFCT_TIMEOUT_ATTR_GENERIC]             = "TIMEOUT",
00042 };
00043 
00044 static const char *const udp_state_to_name[] = {
00045         [NFCT_TIMEOUT_ATTR_UDP_UNREPLIED]       = "UNREPLIED",
00046         [NFCT_TIMEOUT_ATTR_UDP_REPLIED]         = "REPLIED",
00047 };
00048 
00049 static const char *const sctp_state_to_name[] = {
00050         [NFCT_TIMEOUT_ATTR_SCTP_CLOSED]                 = "CLOSED",
00051         [NFCT_TIMEOUT_ATTR_SCTP_COOKIE_WAIT]            = "COOKIE_WAIT",
00052         [NFCT_TIMEOUT_ATTR_SCTP_COOKIE_ECHOED]          = "COOKIE_ECHOED",
00053         [NFCT_TIMEOUT_ATTR_SCTP_ESTABLISHED]            = "ESTABLISHED",
00054         [NFCT_TIMEOUT_ATTR_SCTP_SHUTDOWN_SENT]          = "SHUTDOWN_SENT",
00055         [NFCT_TIMEOUT_ATTR_SCTP_SHUTDOWN_RECD]          = "SHUTDOWN_RECD",
00056         [NFCT_TIMEOUT_ATTR_SCTP_SHUTDOWN_ACK_SENT]      = "SHUTDOWN_ACK_SENT",
00057 };
00058 
00059 static const char *const dccp_state_to_name[] = {
00060         [NFCT_TIMEOUT_ATTR_DCCP_REQUEST]        = "REQUEST",
00061         [NFCT_TIMEOUT_ATTR_DCCP_RESPOND]        = "RESPOND",
00062         [NFCT_TIMEOUT_ATTR_DCCP_PARTOPEN]       = "PARTOPEN",
00063         [NFCT_TIMEOUT_ATTR_DCCP_OPEN]           = "OPEN",
00064         [NFCT_TIMEOUT_ATTR_DCCP_CLOSEREQ]       = "CLOSEREQ",
00065         [NFCT_TIMEOUT_ATTR_DCCP_CLOSING]        = "CLOSING",
00066         [NFCT_TIMEOUT_ATTR_DCCP_TIMEWAIT]       = "TIMEWAIT",
00067 };
00068 
00069 static const char *const icmp_state_to_name[] = {
00070         [NFCT_TIMEOUT_ATTR_ICMP]                = "TIMEOUT",
00071 };
00072 
00073 static const char *const icmpv6_state_to_name[] = {
00074         [NFCT_TIMEOUT_ATTR_ICMPV6]              = "TIMEOUT",
00075 };
00076 
00077 static struct {
00078         uint32_t nlattr_max;
00079         uint32_t attr_max;
00080         const char *const *state_to_name;
00081 } timeout_protocol[IPPROTO_MAX] = {
00082         [IPPROTO_ICMP]  = {
00083                 .nlattr_max     = __CTA_TIMEOUT_ICMP_MAX,
00084                 .attr_max       = NFCT_TIMEOUT_ATTR_ICMP_MAX,
00085                 .state_to_name  = icmp_state_to_name,
00086         },
00087         [IPPROTO_TCP]   = {
00088                 .nlattr_max     = __CTA_TIMEOUT_TCP_MAX,
00089                 .attr_max       = NFCT_TIMEOUT_ATTR_TCP_MAX,
00090                 .state_to_name  = tcp_state_to_name,
00091         },
00092         [IPPROTO_UDP]   = {
00093                 .nlattr_max     = __CTA_TIMEOUT_UDP_MAX,
00094                 .attr_max       = NFCT_TIMEOUT_ATTR_UDP_MAX,
00095                 .state_to_name  = udp_state_to_name,
00096         },
00097         [IPPROTO_GRE]   = {
00098                 .nlattr_max     = __CTA_TIMEOUT_GRE_MAX,
00099                 .attr_max       = NFCT_TIMEOUT_ATTR_GRE_MAX,
00100                 .state_to_name  = udp_state_to_name,
00101         },
00102         [IPPROTO_SCTP]  = {
00103                 .nlattr_max     = __CTA_TIMEOUT_SCTP_MAX,
00104                 .attr_max       = NFCT_TIMEOUT_ATTR_SCTP_MAX,
00105                 .state_to_name  = sctp_state_to_name,
00106         },
00107         [IPPROTO_DCCP]  = {
00108                 .nlattr_max     = __CTA_TIMEOUT_DCCP_MAX,
00109                 .attr_max       = NFCT_TIMEOUT_ATTR_DCCP_MAX,
00110                 .state_to_name  = dccp_state_to_name,
00111         },
00112         [IPPROTO_UDPLITE]       = {
00113                 .nlattr_max     = __CTA_TIMEOUT_UDPLITE_MAX,
00114                 .attr_max       = NFCT_TIMEOUT_ATTR_UDPLITE_MAX,
00115                 .state_to_name  = udp_state_to_name,
00116         },
00117         [IPPROTO_ICMPV6]        = {
00118                 .nlattr_max     = __CTA_TIMEOUT_ICMPV6_MAX,
00119                 .attr_max       = NFCT_TIMEOUT_ATTR_ICMPV6_MAX,
00120                 .state_to_name  = icmpv6_state_to_name,
00121         },
00122         /* add your new supported protocol tracker here. */
00123         [IPPROTO_RAW]   = {
00124                 .nlattr_max     = __CTA_TIMEOUT_GENERIC_MAX,
00125                 .attr_max       = NFCT_TIMEOUT_ATTR_GENERIC_MAX,
00126                 .state_to_name  = generic_state_to_name,
00127         },
00128 };
00129 
00130 
00131 struct nfct_timeout {
00132         char                            name[32];       /* object name. */
00133         uint16_t                        l3num;          /* AF_INET, ... */
00134         uint8_t                         l4num;          /* UDP, TCP, ... */
00135         uint16_t                        attrset;
00136 
00137         uint32_t                        *timeout;       /* array of timeout. */
00138         uint16_t                        polset;
00139 };
00140 
00190 struct nfct_timeout *nfct_timeout_alloc(void)
00191 {
00192         struct nfct_timeout *t;
00193 
00194         t = calloc(1, sizeof(struct nfct_timeout));
00195         if (t == NULL)
00196                 return NULL;
00197 
00198         return t;
00199 }
00200 EXPORT_SYMBOL(nfct_timeout_alloc);
00201 
00206 void nfct_timeout_free(struct nfct_timeout *t)
00207 {
00208         if (t->timeout)
00209                 free(t->timeout);
00210         free(t);
00211 }
00212 EXPORT_SYMBOL(nfct_timeout_free);
00213 
00220 int
00221 nfct_timeout_attr_set(struct nfct_timeout *t, uint32_t type, const void *data)
00222 {
00223         switch(type) {
00224         case NFCT_TIMEOUT_ATTR_NAME:
00225                 strncpy(t->name, data, sizeof(t->name));
00226                 t->name[sizeof(t->name)-1] = '\0';
00227                 break;
00228         case NFCT_TIMEOUT_ATTR_L3PROTO:
00229                 t->l3num = *((uint16_t *) data);
00230                 break;
00231         case NFCT_TIMEOUT_ATTR_L4PROTO:
00232                 t->l4num = *((uint8_t *) data);
00233                 break;
00234         /* NFCT_TIMEOUT_ATTR_POLICY is set by nfct_timeout_policy_attr_set. */
00235         }
00236         t->attrset |= (1 << type);
00237         return 0;
00238 }
00239 EXPORT_SYMBOL(nfct_timeout_attr_set);
00240 
00247 int
00248 nfct_timeout_attr_set_u8(struct nfct_timeout *t, uint32_t type, uint8_t data)
00249 {
00250         return nfct_timeout_attr_set(t, type, &data);
00251 }
00252 EXPORT_SYMBOL(nfct_timeout_attr_set_u8);
00253 
00260 int
00261 nfct_timeout_attr_set_u16(struct nfct_timeout *t, uint32_t type, uint16_t data)
00262 {
00263         return nfct_timeout_attr_set(t, type, &data);
00264 }
00265 EXPORT_SYMBOL(nfct_timeout_attr_set_u16);
00266 
00272 void nfct_timeout_attr_unset(struct nfct_timeout *t, uint32_t type)
00273 {
00274         t->attrset &= ~(1 << type);
00275 }
00276 EXPORT_SYMBOL(nfct_timeout_attr_unset);
00277 
00284 int
00285 nfct_timeout_policy_attr_set_u32(struct nfct_timeout *t,
00286                                  uint32_t type, uint32_t data)
00287 {
00288         size_t timeout_array_size;
00289 
00290         /* Layer 4 protocol needs to be already set. */
00291         if (!(t->attrset & (1 << NFCT_TIMEOUT_ATTR_L4PROTO)))
00292                 return -1;
00293 
00294         if (t->timeout == NULL) {
00295                 /* if not supported, default to generic protocol tracker. */
00296                 if (timeout_protocol[t->l4num].attr_max != 0) {
00297                         timeout_array_size =
00298                                 sizeof(uint32_t) *
00299                                         timeout_protocol[t->l4num].attr_max;
00300                 } else {
00301                         timeout_array_size =
00302                                 sizeof(uint32_t) *
00303                                         timeout_protocol[IPPROTO_RAW].attr_max;
00304                 }
00305                 t->timeout = calloc(1, timeout_array_size);
00306                 if (t->timeout == NULL)
00307                         return -1;
00308         }
00309 
00310         /* this state does not exists in this protocol tracker. */
00311         if (type > timeout_protocol[t->l4num].attr_max)
00312                 return -1;
00313 
00314         t->timeout[type] = data;
00315         t->polset |= (1 << type);
00316 
00317         if (!(t->attrset & (1 << NFCT_TIMEOUT_ATTR_POLICY)))
00318                 t->attrset |= (1 << NFCT_TIMEOUT_ATTR_POLICY);
00319 
00320         return 0;
00321 }
00322 EXPORT_SYMBOL(nfct_timeout_policy_attr_set_u32);
00323 
00329 void nfct_timeout_policy_attr_unset(struct nfct_timeout *t, uint32_t type)
00330 {
00331         t->attrset &= ~(1 << type);
00332 }
00333 EXPORT_SYMBOL(nfct_timeout_policy_attr_unset);
00334 
00343 const char *nfct_timeout_policy_attr_to_name(uint8_t l4proto, uint32_t state)
00344 {
00345         if (timeout_protocol[l4proto].state_to_name == NULL) {
00346                 printf("no array state name\n");
00347                 return NULL;
00348         }
00349 
00350         if (timeout_protocol[l4proto].state_to_name[state] == NULL) {
00351                 printf("state %d does not exists\n", state);
00352                 return NULL;
00353         }
00354 
00355         return timeout_protocol[l4proto].state_to_name[state];
00356 }
00357 EXPORT_SYMBOL(nfct_timeout_policy_attr_to_name);
00358 
00368 static int
00369 nfct_timeout_snprintf_default(char *buf, size_t size,
00370                               const struct nfct_timeout *t,
00371                               unsigned int flags)
00372 {
00373         int ret = 0;
00374         unsigned int offset = 0;
00375 
00376         if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_NAME)) {
00377                 ret = snprintf(buf+offset, size, ".%s = {\n", t->name);
00378                 offset += ret;
00379                 size -= ret;
00380         }
00381         if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_L3PROTO)) {
00382                 ret = snprintf(buf+offset, size, "\t.l3proto = %u,\n",
00383                                 t->l3num);
00384                 offset += ret;
00385                 size -= ret;
00386         }
00387         if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_L4PROTO)) {
00388                 ret = snprintf(buf+offset, size, "\t.l4proto = %u,\n",
00389                                 t->l4num);
00390                 offset += ret;
00391                 size -= ret;
00392         }
00393         if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_POLICY)) {
00394                 uint8_t l4num = t->l4num;
00395                 int i;
00396 
00397                 /* default to generic protocol tracker. */
00398                 if (timeout_protocol[t->l4num].attr_max == 0)
00399                         l4num = IPPROTO_RAW;
00400 
00401                 ret = snprintf(buf+offset, size, "\t.policy = {\n");
00402                 offset += ret;
00403                 size -= ret;
00404 
00405                 for (i=0; i<timeout_protocol[l4num].attr_max; i++) {
00406                         const char *state_name =
00407                                 timeout_protocol[l4num].state_to_name[i][0] ?
00408                                 timeout_protocol[l4num].state_to_name[i] :
00409                                 "UNKNOWN";
00410 
00411                         ret = snprintf(buf+offset, size,
00412                                 "\t\t.%s = %u,\n", state_name, t->timeout[i]);
00413                         offset += ret;
00414                         size -= ret;
00415                 }
00416 
00417                 ret = snprintf(buf+offset, size, "\t},\n");
00418                 offset += ret;
00419                 size -= ret;
00420         }
00421         ret = snprintf(buf+offset, size, "};");
00422         offset += ret;
00423         size -= ret;
00424 
00425         buf[offset]='\0';
00426 
00427         return ret;
00428 }
00429 
00441 int nfct_timeout_snprintf(char *buf, size_t size, const struct nfct_timeout *t,
00442                           unsigned int type, unsigned int flags)
00443 {
00444         int ret = 0;
00445 
00446         switch(type) {
00447         case NFCT_TIMEOUT_O_DEFAULT:
00448                 ret = nfct_timeout_snprintf_default(buf, size, t, flags);
00449                 break;
00450         /* add your new output here. */
00451         default:
00452                 break;
00453         }
00454 
00455         return ret;
00456 }
00457 EXPORT_SYMBOL(nfct_timeout_snprintf);
00458 
00480 struct nlmsghdr *
00481 nfct_timeout_nlmsg_build_hdr(char *buf, uint8_t cmd,
00482                              uint16_t flags, uint32_t seq)
00483 {
00484         struct nlmsghdr *nlh;
00485         struct nfgenmsg *nfh;
00486 
00487         nlh = mnl_nlmsg_put_header(buf);
00488         nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8) | cmd;
00489         nlh->nlmsg_flags = NLM_F_REQUEST | flags;
00490         nlh->nlmsg_seq = seq;
00491 
00492         nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
00493         nfh->nfgen_family = AF_UNSPEC;
00494         nfh->version = NFNETLINK_V0;
00495         nfh->res_id = 0;
00496 
00497         return nlh;
00498 }
00499 EXPORT_SYMBOL(nfct_timeout_nlmsg_build_hdr);
00500 
00506 void
00507 nfct_timeout_nlmsg_build_payload(struct nlmsghdr *nlh,
00508                                  const struct nfct_timeout *t)
00509 {
00510         int i;
00511         struct nlattr *nest;
00512 
00513         if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_NAME))
00514                 mnl_attr_put_strz(nlh, CTA_TIMEOUT_NAME, t->name);
00515 
00516         if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_L3PROTO))
00517                 mnl_attr_put_u16(nlh, CTA_TIMEOUT_L3PROTO, htons(t->l3num));
00518 
00519         if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_L4PROTO))
00520                 mnl_attr_put_u8(nlh, CTA_TIMEOUT_L4PROTO, t->l4num);
00521 
00522         if (t->attrset & (1 << NFCT_TIMEOUT_ATTR_POLICY) && t->polset) {
00523                 nest = mnl_attr_nest_start(nlh, CTA_TIMEOUT_DATA);
00524 
00525                 for (i=0; i<timeout_protocol[t->l4num].attr_max; i++) {
00526                         if (t->polset & (1 << i)) {
00527                                 mnl_attr_put_u32(nlh, i+1,
00528                                                  htonl(t->timeout[i]));
00529                         }
00530                 }
00531                 mnl_attr_nest_end(nlh, nest);
00532         }
00533 
00534 }
00535 EXPORT_SYMBOL(nfct_timeout_nlmsg_build_payload);
00536 
00537 static int
00538 timeout_nlmsg_parse_attr_cb(const struct nlattr *attr, void *data)
00539 {
00540         const struct nlattr **tb = data;
00541         uint16_t type = mnl_attr_get_type(attr);
00542 
00543         if (mnl_attr_type_valid(attr, CTA_TIMEOUT_MAX) < 0)
00544                 return MNL_CB_OK;
00545 
00546         switch(type) {
00547         case CTA_TIMEOUT_NAME:
00548                 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
00549                         perror("mnl_attr_validate");
00550                         return MNL_CB_ERROR;
00551                 }
00552                 break;
00553         case CTA_TIMEOUT_L3PROTO:
00554                 if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
00555                         perror("mnl_attr_validate");
00556                         return MNL_CB_ERROR;
00557                 }
00558                 break;
00559         case CTA_TIMEOUT_L4PROTO:
00560                 if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) {
00561                         perror("mnl_attr_validate");
00562                         return MNL_CB_ERROR;
00563                 }
00564                 break;
00565         case CTA_TIMEOUT_DATA:
00566                 if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
00567                         perror("mnl_attr_validate");
00568                         return MNL_CB_ERROR;
00569                 }
00570                 break;
00571         }
00572         tb[type] = attr;
00573         return MNL_CB_OK;
00574 }
00575 
00576 struct _container_policy_cb {
00577         unsigned int nlattr_max;
00578         void *tb;
00579 };
00580 
00581 static int
00582 parse_timeout_attr_policy_cb(const struct nlattr *attr, void *data)
00583 {
00584         struct _container_policy_cb *data_cb = data;
00585         const struct nlattr **tb = data_cb->tb;
00586         uint16_t type = mnl_attr_get_type(attr);
00587 
00588         if (mnl_attr_type_valid(attr, data_cb->nlattr_max) < 0)
00589                 return MNL_CB_OK;
00590 
00591         if (type <= data_cb->nlattr_max) {
00592                 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
00593                         perror("mnl_attr_validate");
00594                         return MNL_CB_ERROR;
00595                 }
00596                 tb[type] = attr;
00597         }
00598         return MNL_CB_OK;
00599 }
00600 
00601 static void
00602 timeout_parse_attr_data(struct nfct_timeout *t, const struct nlattr *nest)
00603 {
00604         unsigned int nlattr_max = timeout_protocol[t->l4num].nlattr_max;
00605         struct nlattr *tb[nlattr_max];
00606         struct _container_policy_cb cnt = {
00607                 .nlattr_max = nlattr_max,
00608                 .tb = tb,
00609         };
00610         unsigned int i;
00611 
00612         memset(tb, 0, sizeof(struct nlattr *) * nlattr_max);
00613 
00614         mnl_attr_parse_nested(nest, parse_timeout_attr_policy_cb, &cnt);
00615 
00616         for (i=1; i<nlattr_max; i++) {
00617                 if (tb[i]) {
00618                         nfct_timeout_policy_attr_set_u32(t, i-1,
00619                                 ntohl(mnl_attr_get_u32(tb[i])));
00620                 }
00621         }
00622 }
00623 
00632 int
00633 nfct_timeout_nlmsg_parse_payload(const struct nlmsghdr *nlh,
00634                                  struct nfct_timeout *t)
00635 {
00636         struct nlattr *tb[CTA_TIMEOUT_MAX+1] = {};
00637         struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
00638 
00639         mnl_attr_parse(nlh, sizeof(*nfg), timeout_nlmsg_parse_attr_cb, tb);
00640         if (tb[CTA_TIMEOUT_NAME]) {
00641                 nfct_timeout_attr_set(t, NFCT_TIMEOUT_ATTR_NAME,
00642                         mnl_attr_get_str(tb[CTA_TIMEOUT_NAME]));
00643         }
00644         if (tb[CTA_TIMEOUT_L3PROTO]) {
00645                 nfct_timeout_attr_set_u16(t, NFCT_TIMEOUT_ATTR_L3PROTO,
00646                         ntohs(mnl_attr_get_u16(tb[CTA_TIMEOUT_L3PROTO])));
00647         }
00648         if (tb[CTA_TIMEOUT_L4PROTO]) {
00649                 nfct_timeout_attr_set_u8(t, NFCT_TIMEOUT_ATTR_L4PROTO,
00650                         mnl_attr_get_u8(tb[CTA_TIMEOUT_L4PROTO]));
00651         }
00652         if (tb[CTA_TIMEOUT_DATA]) {
00653                 timeout_parse_attr_data(t, tb[CTA_TIMEOUT_DATA]);
00654         }
00655         return 0;
00656 }
00657 EXPORT_SYMBOL(nfct_timeout_nlmsg_parse_payload);
00658