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