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 && cur != last)
 224                        continue;
 225
 226                if (ctnl_timeout_fill_info(skb, NETLINK_CB(cb->skb).portid,
 227                                           cb->nlh->nlmsg_seq,
 228                                           NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
 229                                           IPCTNL_MSG_TIMEOUT_NEW, cur) < 0) {
 230                        cb->args[1] = (unsigned long)cur;
 231                        break;
 232                }
 233        }
 234        if (!cb->args[1])
 235                cb->args[2] = 1;
 236        rcu_read_unlock();
 237        return skb->len;
 238}
 239
 240static int
 241cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb,
 242                      const struct nlmsghdr *nlh,
 243                      const struct nlattr * const cda[])
 244{
 245        int ret = -ENOENT;
 246        char *name;
 247        struct ctnl_timeout *cur;
 248
 249        if (nlh->nlmsg_flags & NLM_F_DUMP) {
 250                struct netlink_dump_control c = {
 251                        .dump = ctnl_timeout_dump,
 252                };
 253                return netlink_dump_start(ctnl, skb, nlh, &c);
 254        }
 255
 256        if (!cda[CTA_TIMEOUT_NAME])
 257                return -EINVAL;
 258        name = nla_data(cda[CTA_TIMEOUT_NAME]);
 259
 260        list_for_each_entry(cur, &cttimeout_list, head) {
 261                struct sk_buff *skb2;
 262
 263                if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
 264                        continue;
 265
 266                skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 267                if (skb2 == NULL) {
 268                        ret = -ENOMEM;
 269                        break;
 270                }
 271
 272                ret = ctnl_timeout_fill_info(skb2, NETLINK_CB(skb).portid,
 273                                             nlh->nlmsg_seq,
 274                                             NFNL_MSG_TYPE(nlh->nlmsg_type),
 275                                             IPCTNL_MSG_TIMEOUT_NEW, cur);
 276                if (ret <= 0) {
 277                        kfree_skb(skb2);
 278                        break;
 279                }
 280                ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid,
 281                                        MSG_DONTWAIT);
 282                if (ret > 0)
 283                        ret = 0;
 284
 285                /* this avoids a loop in nfnetlink. */
 286                return ret == -EAGAIN ? -ENOBUFS : ret;
 287        }
 288        return ret;
 289}
 290
 291/* try to delete object, fail if it is still in use. */
 292static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
 293{
 294        int ret = 0;
 295
 296        /* we want to avoid races with nf_ct_timeout_find_get. */
 297        if (atomic_dec_and_test(&timeout->refcnt)) {
 298                /* We are protected by nfnl mutex. */
 299                list_del_rcu(&timeout->head);
 300                nf_ct_l4proto_put(timeout->l4proto);
 301                kfree_rcu(timeout, rcu_head);
 302        } else {
 303                /* still in use, restore reference counter. */
 304                atomic_inc(&timeout->refcnt);
 305                ret = -EBUSY;
 306        }
 307        return ret;
 308}
 309
 310static int
 311cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
 312                      const struct nlmsghdr *nlh,
 313                      const struct nlattr * const cda[])
 314{
 315        char *name;
 316        struct ctnl_timeout *cur;
 317        int ret = -ENOENT;
 318
 319        if (!cda[CTA_TIMEOUT_NAME]) {
 320                list_for_each_entry(cur, &cttimeout_list, head)
 321                        ctnl_timeout_try_del(cur);
 322
 323                return 0;
 324        }
 325        name = nla_data(cda[CTA_TIMEOUT_NAME]);
 326
 327        list_for_each_entry(cur, &cttimeout_list, head) {
 328                if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
 329                        continue;
 330
 331                ret = ctnl_timeout_try_del(cur);
 332                if (ret < 0)
 333                        return ret;
 334
 335                break;
 336        }
 337        return ret;
 338}
 339
 340#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 341static struct ctnl_timeout *ctnl_timeout_find_get(const char *name)
 342{
 343        struct ctnl_timeout *timeout, *matching = NULL;
 344
 345        rcu_read_lock();
 346        list_for_each_entry_rcu(timeout, &cttimeout_list, head) {
 347                if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
 348                        continue;
 349
 350                if (!try_module_get(THIS_MODULE))
 351                        goto err;
 352
 353                if (!atomic_inc_not_zero(&timeout->refcnt)) {
 354                        module_put(THIS_MODULE);
 355                        goto err;
 356                }
 357                matching = timeout;
 358                break;
 359        }
 360err:
 361        rcu_read_unlock();
 362        return matching;
 363}
 364
 365static void ctnl_timeout_put(struct ctnl_timeout *timeout)
 366{
 367        atomic_dec(&timeout->refcnt);
 368        module_put(THIS_MODULE);
 369}
 370#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 371
 372static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = {
 373        [IPCTNL_MSG_TIMEOUT_NEW]        = { .call = cttimeout_new_timeout,
 374                                            .attr_count = CTA_TIMEOUT_MAX,
 375                                            .policy = cttimeout_nla_policy },
 376        [IPCTNL_MSG_TIMEOUT_GET]        = { .call = cttimeout_get_timeout,
 377                                            .attr_count = CTA_TIMEOUT_MAX,
 378                                            .policy = cttimeout_nla_policy },
 379        [IPCTNL_MSG_TIMEOUT_DELETE]     = { .call = cttimeout_del_timeout,
 380                                            .attr_count = CTA_TIMEOUT_MAX,
 381                                            .policy = cttimeout_nla_policy },
 382};
 383
 384static const struct nfnetlink_subsystem cttimeout_subsys = {
 385        .name                           = "conntrack_timeout",
 386        .subsys_id                      = NFNL_SUBSYS_CTNETLINK_TIMEOUT,
 387        .cb_count                       = IPCTNL_MSG_TIMEOUT_MAX,
 388        .cb                             = cttimeout_cb,
 389};
 390
 391MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_TIMEOUT);
 392
 393static int __init cttimeout_init(void)
 394{
 395        int ret;
 396
 397        ret = nfnetlink_subsys_register(&cttimeout_subsys);
 398        if (ret < 0) {
 399                pr_err("cttimeout_init: cannot register cttimeout with "
 400                        "nfnetlink.\n");
 401                goto err_out;
 402        }
 403#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 404        RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, ctnl_timeout_find_get);
 405        RCU_INIT_POINTER(nf_ct_timeout_put_hook, ctnl_timeout_put);
 406#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 407        return 0;
 408
 409err_out:
 410        return ret;
 411}
 412
 413static void __exit cttimeout_exit(void)
 414{
 415        struct ctnl_timeout *cur, *tmp;
 416
 417        pr_info("cttimeout: unregistering from nfnetlink.\n");
 418
 419        nfnetlink_subsys_unregister(&cttimeout_subsys);
 420        list_for_each_entry_safe(cur, tmp, &cttimeout_list, head) {
 421                list_del_rcu(&cur->head);
 422                /* We are sure that our objects have no clients at this point,
 423                 * it's safe to release them all without checking refcnt.
 424                 */
 425                nf_ct_l4proto_put(cur->l4proto);
 426                kfree_rcu(cur, rcu_head);
 427        }
 428#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 429        RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
 430        RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
 431#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 432}
 433
 434module_init(cttimeout_init);
 435module_exit(cttimeout_exit);
 436
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.