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