linux/net/netfilter/nfnetlink_cttimeout.c
<<
>>
Prefs
   1/*
   2 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
   3 * (C) 2012 by Vyatta Inc. <http://www.vyatta.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation (or any later at your option).
   8 */
   9#include <linux/init.h>
  10#include <linux/module.h>
  11#include <linux/kernel.h>
  12#include <linux/rculist.h>
  13#include <linux/rculist_nulls.h>
  14#include <linux/types.h>
  15#include <linux/timer.h>
  16#include <linux/security.h>
  17#include <linux/skbuff.h>
  18#include <linux/errno.h>
  19#include <linux/netlink.h>
  20#include <linux/spinlock.h>
  21#include <linux/interrupt.h>
  22#include <linux/slab.h>
  23
  24#include <linux/netfilter.h>
  25#include <net/netlink.h>
  26#include <net/sock.h>
  27#include <net/netfilter/nf_conntrack.h>
  28#include <net/netfilter/nf_conntrack_core.h>
  29#include <net/netfilter/nf_conntrack_l3proto.h>
  30#include <net/netfilter/nf_conntrack_l4proto.h>
  31#include <net/netfilter/nf_conntrack_tuple.h>
  32#include <net/netfilter/nf_conntrack_timeout.h>
  33
  34#include <linux/netfilter/nfnetlink.h>
  35#include <linux/netfilter/nfnetlink_cttimeout.h>
  36
  37MODULE_LICENSE("GPL");
  38MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
  39MODULE_DESCRIPTION("cttimeout: Extended Netfilter Connection Tracking timeout tuning");
  40
  41static LIST_HEAD(cttimeout_list);
  42
  43static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
  44        [CTA_TIMEOUT_NAME]      = { .type = NLA_NUL_STRING,
  45                                    .len  = CTNL_TIMEOUT_NAME_MAX - 1},
  46        [CTA_TIMEOUT_L3PROTO]   = { .type = NLA_U16 },
  47        [CTA_TIMEOUT_L4PROTO]   = { .type = NLA_U8 },
  48        [CTA_TIMEOUT_DATA]      = { .type = NLA_NESTED },
  49};
  50
  51static int
  52ctnl_timeout_parse_policy(struct ctnl_timeout *timeout,
  53                          struct nf_conntrack_l4proto *l4proto,
  54                          struct net *net,
  55                          const struct nlattr *attr)
  56{
  57        int ret = 0;
  58
  59        if (likely(l4proto->ctnl_timeout.nlattr_to_obj)) {
  60                struct nlattr *tb[l4proto->ctnl_timeout.nlattr_max+1];
  61
  62                nla_parse_nested(tb, l4proto->ctnl_timeout.nlattr_max,
  63                                 attr, l4proto->ctnl_timeout.nla_policy);
  64
  65                ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net,
  66                                                          &timeout->data);
  67        }
  68        return ret;
  69}
  70
  71static int
  72cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
  73                      const struct nlmsghdr *nlh,
  74                      const struct nlattr * const cda[])
  75{
  76        __u16 l3num;
  77        __u8 l4num;
  78        struct nf_conntrack_l4proto *l4proto;
  79        struct ctnl_timeout *timeout, *matching = NULL;
  80        struct net *net = sock_net(skb->sk);
  81        char *name;
  82        int ret;
  83
  84        if (!cda[CTA_TIMEOUT_NAME] ||
  85            !cda[CTA_TIMEOUT_L3PROTO] ||
  86            !cda[CTA_TIMEOUT_L4PROTO] ||
  87            !cda[CTA_TIMEOUT_DATA])
  88                return -EINVAL;
  89
  90        name = nla_data(cda[CTA_TIMEOUT_NAME]);
  91        l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
  92        l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
  93
  94        list_for_each_entry(timeout, &cttimeout_list, head) {
  95                if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
  96                        continue;
  97
  98                if (nlh->nlmsg_flags & NLM_F_EXCL)
  99                        return -EEXIST;
 100
 101                matching = timeout;
 102                break;
 103        }
 104
 105        l4proto = nf_ct_l4proto_find_get(l3num, l4num);
 106
 107        /* This protocol is not supportted, skip. */
 108        if (l4proto->l4proto != l4num) {
 109                ret = -EOPNOTSUPP;
 110                goto err_proto_put;
 111        }
 112
 113        if (matching) {
 114                if (nlh->nlmsg_flags & NLM_F_REPLACE) {
 115                        /* You cannot replace one timeout policy by another of
 116                         * different kind, sorry.
 117                         */
 118                        if (matching->l3num != l3num ||
 119                            matching->l4proto->l4proto != l4num) {
 120                                ret = -EINVAL;
 121                                goto err_proto_put;
 122                        }
 123
 124                        ret = ctnl_timeout_parse_policy(matching, l4proto, net,
 125                                                        cda[CTA_TIMEOUT_DATA]);
 126                        return ret;
 127                }
 128                ret = -EBUSY;
 129                goto err_proto_put;
 130        }
 131
 132        timeout = kzalloc(sizeof(struct ctnl_timeout) +
 133                          l4proto->ctnl_timeout.obj_size, GFP_KERNEL);
 134        if (timeout == NULL) {
 135                ret = -ENOMEM;
 136                goto err_proto_put;
 137        }
 138
 139        ret = ctnl_timeout_parse_policy(timeout, l4proto, net,
 140                                        cda[CTA_TIMEOUT_DATA]);
 141        if (ret < 0)
 142                goto err;
 143
 144        strcpy(timeout->name, nla_data(cda[CTA_TIMEOUT_NAME]));
 145        timeout->l3num = l3num;
 146        timeout->l4proto = l4proto;
 147        atomic_set(&timeout->refcnt, 1);
 148        list_add_tail_rcu(&timeout->head, &cttimeout_list);
 149
 150        return 0;
 151err:
 152        kfree(timeout);
 153err_proto_put:
 154        nf_ct_l4proto_put(l4proto);
 155        return ret;
 156}
 157
 158static int
 159ctnl_timeout_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
 160                       int event, struct ctnl_timeout *timeout)
 161{
 162        struct nlmsghdr *nlh;
 163        struct nfgenmsg *nfmsg;
 164        unsigned int flags = portid ? NLM_F_MULTI : 0;
 165        struct nf_conntrack_l4proto *l4proto = timeout->l4proto;
 166
 167        event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8;
 168        nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
 169        if (nlh == NULL)
 170                goto nlmsg_failure;
 171
 172        nfmsg = nlmsg_data(nlh);
 173        nfmsg->nfgen_family = AF_UNSPEC;
 174        nfmsg->version = NFNETLINK_V0;
 175        nfmsg->res_id = 0;
 176
 177        if (nla_put_string(skb, CTA_TIMEOUT_NAME, timeout->name) ||
 178            nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, htons(timeout->l3num)) ||
 179            nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, timeout->l4proto->l4proto) ||
 180            nla_put_be32(skb, CTA_TIMEOUT_USE,
 181                         htonl(atomic_read(&timeout->refcnt))))
 182                goto nla_put_failure;
 183
 184        if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) {
 185                struct nlattr *nest_parms;
 186                int ret;
 187
 188                nest_parms = nla_nest_start(skb,
 189                                            CTA_TIMEOUT_DATA | NLA_F_NESTED);
 190                if (!nest_parms)
 191                        goto nla_put_failure;
 192
 193                ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->data);
 194                if (ret < 0)
 195                        goto nla_put_failure;
 196
 197                nla_nest_end(skb, nest_parms);
 198        }
 199
 200        nlmsg_end(skb, nlh);
 201        return skb->len;
 202
 203nlmsg_failure:
 204nla_put_failure:
 205        nlmsg_cancel(skb, nlh);
 206        return -1;
 207}
 208
 209static int
 210ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb)
 211{
 212        struct ctnl_timeout *cur, *last;
 213
 214        if (cb->args[2])
 215                return 0;
 216
 217        last = (struct ctnl_timeout *)cb->args[1];
 218        if (cb->args[1])
 219                cb->args[1] = 0;
 220
 221        rcu_read_lock();
 222        list_for_each_entry_rcu(cur, &cttimeout_list, head) {
 223                if (last) {
 224                        if (cur != last)
 225                                continue;
 226
 227                        last = NULL;
 228                }
 229                if (ctnl_timeout_fill_info(skb, NETLINK_CB(cb->skb).portid,
 230                                           cb->nlh->nlmsg_seq,
 231                                           NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
 232                                           IPCTNL_MSG_TIMEOUT_NEW, cur) < 0) {
 233                        cb->args[1] = (unsigned long)cur;
 234                        break;
 235                }
 236        }
 237        if (!cb->args[1])
 238                cb->args[2] = 1;
 239        rcu_read_unlock();
 240        return skb->len;
 241}
 242
 243static int
 244cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb,
 245                      const struct nlmsghdr *nlh,
 246                      const struct nlattr * const cda[])
 247{
 248        int ret = -ENOENT;
 249        char *name;
 250        struct ctnl_timeout *cur;
 251
 252        if (nlh->nlmsg_flags & NLM_F_DUMP) {
 253                struct netlink_dump_control c = {
 254                        .dump = ctnl_timeout_dump,
 255                };
 256                return netlink_dump_start(ctnl, skb, nlh, &c);
 257        }
 258
 259        if (!cda[CTA_TIMEOUT_NAME])
 260                return -EINVAL;
 261        name = nla_data(cda[CTA_TIMEOUT_NAME]);
 262
 263        list_for_each_entry(cur, &cttimeout_list, head) {
 264                struct sk_buff *skb2;
 265
 266                if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
 267                        continue;
 268
 269                skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 270                if (skb2 == NULL) {
 271                        ret = -ENOMEM;
 272                        break;
 273                }
 274
 275                ret = ctnl_timeout_fill_info(skb2, NETLINK_CB(skb).portid,
 276                                             nlh->nlmsg_seq,
 277                                             NFNL_MSG_TYPE(nlh->nlmsg_type),
 278                                             IPCTNL_MSG_TIMEOUT_NEW, cur);
 279                if (ret <= 0) {
 280                        kfree_skb(skb2);
 281                        break;
 282                }
 283                ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid,
 284                                        MSG_DONTWAIT);
 285                if (ret > 0)
 286                        ret = 0;
 287
 288                /* this avoids a loop in nfnetlink. */
 289                return ret == -EAGAIN ? -ENOBUFS : ret;
 290        }
 291        return ret;
 292}
 293
 294/* try to delete object, fail if it is still in use. */
 295static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
 296{
 297        int ret = 0;
 298
 299        /* we want to avoid races with nf_ct_timeout_find_get. */
 300        if (atomic_dec_and_test(&timeout->refcnt)) {
 301                /* We are protected by nfnl mutex. */
 302                list_del_rcu(&timeout->head);
 303                nf_ct_l4proto_put(timeout->l4proto);
 304                kfree_rcu(timeout, rcu_head);
 305        } else {
 306                /* still in use, restore reference counter. */
 307                atomic_inc(&timeout->refcnt);
 308                ret = -EBUSY;
 309        }
 310        return ret;
 311}
 312
 313static int
 314cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
 315                      const struct nlmsghdr *nlh,
 316                      const struct nlattr * const cda[])
 317{
 318        char *name;
 319        struct ctnl_timeout *cur;
 320        int ret = -ENOENT;
 321
 322        if (!cda[CTA_TIMEOUT_NAME]) {
 323                list_for_each_entry(cur, &cttimeout_list, head)
 324                        ctnl_timeout_try_del(cur);
 325
 326                return 0;
 327        }
 328        name = nla_data(cda[CTA_TIMEOUT_NAME]);
 329
 330        list_for_each_entry(cur, &cttimeout_list, head) {
 331                if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
 332                        continue;
 333
 334                ret = ctnl_timeout_try_del(cur);
 335                if (ret < 0)
 336                        return ret;
 337
 338                break;
 339        }
 340        return ret;
 341}
 342
 343#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 344static struct ctnl_timeout *ctnl_timeout_find_get(const char *name)
 345{
 346        struct ctnl_timeout *timeout, *matching = NULL;
 347
 348        rcu_read_lock();
 349        list_for_each_entry_rcu(timeout, &cttimeout_list, head) {
 350                if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
 351                        continue;
 352
 353                if (!try_module_get(THIS_MODULE))
 354                        goto err;
 355
 356                if (!atomic_inc_not_zero(&timeout->refcnt)) {
 357                        module_put(THIS_MODULE);
 358                        goto err;
 359                }
 360                matching = timeout;
 361                break;
 362        }
 363err:
 364        rcu_read_unlock();
 365        return matching;
 366}
 367
 368static void ctnl_timeout_put(struct ctnl_timeout *timeout)
 369{
 370        atomic_dec(&timeout->refcnt);
 371        module_put(THIS_MODULE);
 372}
 373#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 374
 375static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = {
 376        [IPCTNL_MSG_TIMEOUT_NEW]        = { .call = cttimeout_new_timeout,
 377                                            .attr_count = CTA_TIMEOUT_MAX,
 378                                            .policy = cttimeout_nla_policy },
 379        [IPCTNL_MSG_TIMEOUT_GET]        = { .call = cttimeout_get_timeout,
 380                                            .attr_count = CTA_TIMEOUT_MAX,
 381                                            .policy = cttimeout_nla_policy },
 382        [IPCTNL_MSG_TIMEOUT_DELETE]     = { .call = cttimeout_del_timeout,
 383                                            .attr_count = CTA_TIMEOUT_MAX,
 384                                            .policy = cttimeout_nla_policy },
 385};
 386
 387static const struct nfnetlink_subsystem cttimeout_subsys = {
 388        .name                           = "conntrack_timeout",
 389        .subsys_id                      = NFNL_SUBSYS_CTNETLINK_TIMEOUT,
 390        .cb_count                       = IPCTNL_MSG_TIMEOUT_MAX,
 391        .cb                             = cttimeout_cb,
 392};
 393
 394MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_TIMEOUT);
 395
 396static int __init cttimeout_init(void)
 397{
 398        int ret;
 399
 400        ret = nfnetlink_subsys_register(&cttimeout_subsys);
 401        if (ret < 0) {
 402                pr_err("cttimeout_init: cannot register cttimeout with "
 403                        "nfnetlink.\n");
 404                goto err_out;
 405        }
 406#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 407        RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, ctnl_timeout_find_get);
 408        RCU_INIT_POINTER(nf_ct_timeout_put_hook, ctnl_timeout_put);
 409#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 410        return 0;
 411
 412err_out:
 413        return ret;
 414}
 415
 416static void __exit cttimeout_exit(void)
 417{
 418        struct ctnl_timeout *cur, *tmp;
 419
 420        pr_info("cttimeout: unregistering from nfnetlink.\n");
 421
 422        nfnetlink_subsys_unregister(&cttimeout_subsys);
 423        list_for_each_entry_safe(cur, tmp, &cttimeout_list, head) {
 424                list_del_rcu(&cur->head);
 425                /* We are sure that our objects have no clients at this point,
 426                 * it's safe to release them all without checking refcnt.
 427                 */
 428                nf_ct_l4proto_put(cur->l4proto);
 429                kfree_rcu(cur, rcu_head);
 430        }
 431#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 432        RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
 433        RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
 434#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 435}
 436
 437module_init(cttimeout_init);
 438module_exit(cttimeout_exit);
 439