linux/net/netfilter/nf_conntrack_timeout.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
   4 * (C) 2012 by Vyatta Inc. <http://www.vyatta.com>
   5 */
   6
   7#include <linux/types.h>
   8#include <linux/netfilter.h>
   9#include <linux/skbuff.h>
  10#include <linux/vmalloc.h>
  11#include <linux/stddef.h>
  12#include <linux/err.h>
  13#include <linux/percpu.h>
  14#include <linux/kernel.h>
  15#include <linux/netdevice.h>
  16#include <linux/slab.h>
  17#include <linux/export.h>
  18
  19#include <net/netfilter/nf_conntrack.h>
  20#include <net/netfilter/nf_conntrack_core.h>
  21#include <net/netfilter/nf_conntrack_extend.h>
  22#include <net/netfilter/nf_conntrack_l4proto.h>
  23#include <net/netfilter/nf_conntrack_timeout.h>
  24
  25struct nf_ct_timeout *
  26(*nf_ct_timeout_find_get_hook)(struct net *net, const char *name) __read_mostly;
  27EXPORT_SYMBOL_GPL(nf_ct_timeout_find_get_hook);
  28
  29void (*nf_ct_timeout_put_hook)(struct nf_ct_timeout *timeout) __read_mostly;
  30EXPORT_SYMBOL_GPL(nf_ct_timeout_put_hook);
  31
  32static int untimeout(struct nf_conn *ct, void *timeout)
  33{
  34        struct nf_conn_timeout *timeout_ext = nf_ct_timeout_find(ct);
  35
  36        if (timeout_ext && (!timeout || timeout_ext->timeout == timeout))
  37                RCU_INIT_POINTER(timeout_ext->timeout, NULL);
  38
  39        /* We are not intended to delete this conntrack. */
  40        return 0;
  41}
  42
  43void nf_ct_untimeout(struct net *net, struct nf_ct_timeout *timeout)
  44{
  45        nf_ct_iterate_cleanup_net(net, untimeout, timeout, 0, 0);
  46}
  47EXPORT_SYMBOL_GPL(nf_ct_untimeout);
  48
  49static void __nf_ct_timeout_put(struct nf_ct_timeout *timeout)
  50{
  51        typeof(nf_ct_timeout_put_hook) timeout_put;
  52
  53        timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
  54        if (timeout_put)
  55                timeout_put(timeout);
  56}
  57
  58int nf_ct_set_timeout(struct net *net, struct nf_conn *ct,
  59                      u8 l3num, u8 l4num, const char *timeout_name)
  60{
  61        typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
  62        struct nf_ct_timeout *timeout;
  63        struct nf_conn_timeout *timeout_ext;
  64        const char *errmsg = NULL;
  65        int ret = 0;
  66
  67        rcu_read_lock();
  68        timeout_find_get = rcu_dereference(nf_ct_timeout_find_get_hook);
  69        if (!timeout_find_get) {
  70                ret = -ENOENT;
  71                errmsg = "Timeout policy base is empty";
  72                goto out;
  73        }
  74
  75        timeout = timeout_find_get(net, timeout_name);
  76        if (!timeout) {
  77                ret = -ENOENT;
  78                pr_info_ratelimited("No such timeout policy \"%s\"\n",
  79                                    timeout_name);
  80                goto out;
  81        }
  82
  83        if (timeout->l3num != l3num) {
  84                ret = -EINVAL;
  85                pr_info_ratelimited("Timeout policy `%s' can only be used by "
  86                                    "L%d protocol number %d\n",
  87                                    timeout_name, 3, timeout->l3num);
  88                goto err_put_timeout;
  89        }
  90        /* Make sure the timeout policy matches any existing protocol tracker,
  91         * otherwise default to generic.
  92         */
  93        if (timeout->l4proto->l4proto != l4num) {
  94                ret = -EINVAL;
  95                pr_info_ratelimited("Timeout policy `%s' can only be used by "
  96                                    "L%d protocol number %d\n",
  97                                    timeout_name, 4, timeout->l4proto->l4proto);
  98                goto err_put_timeout;
  99        }
 100        timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC);
 101        if (!timeout_ext) {
 102                ret = -ENOMEM;
 103                goto err_put_timeout;
 104        }
 105
 106        rcu_read_unlock();
 107        return ret;
 108
 109err_put_timeout:
 110        __nf_ct_timeout_put(timeout);
 111out:
 112        rcu_read_unlock();
 113        if (errmsg)
 114                pr_info_ratelimited("%s\n", errmsg);
 115        return ret;
 116}
 117EXPORT_SYMBOL_GPL(nf_ct_set_timeout);
 118
 119void nf_ct_destroy_timeout(struct nf_conn *ct)
 120{
 121        struct nf_conn_timeout *timeout_ext;
 122        typeof(nf_ct_timeout_put_hook) timeout_put;
 123
 124        rcu_read_lock();
 125        timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
 126
 127        if (timeout_put) {
 128                timeout_ext = nf_ct_timeout_find(ct);
 129                if (timeout_ext) {
 130                        timeout_put(timeout_ext->timeout);
 131                        RCU_INIT_POINTER(timeout_ext->timeout, NULL);
 132                }
 133        }
 134        rcu_read_unlock();
 135}
 136EXPORT_SYMBOL_GPL(nf_ct_destroy_timeout);
 137
 138static const struct nf_ct_ext_type timeout_extend = {
 139        .len    = sizeof(struct nf_conn_timeout),
 140        .align  = __alignof__(struct nf_conn_timeout),
 141        .id     = NF_CT_EXT_TIMEOUT,
 142};
 143
 144int nf_conntrack_timeout_init(void)
 145{
 146        int ret = nf_ct_extend_register(&timeout_extend);
 147        if (ret < 0)
 148                pr_err("nf_ct_timeout: Unable to register timeout extension.\n");
 149        return ret;
 150}
 151
 152void nf_conntrack_timeout_fini(void)
 153{
 154        nf_ct_extend_unregister(&timeout_extend);
 155}
 156