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 25 struct nf_ct_timeout * 26 (*nf_ct_timeout_find_get_hook)(struct net *net, const char *name) __read_mostly; 27 EXPORT_SYMBOL_GPL(nf_ct_timeout_find_get_hook); 28 29 void (*nf_ct_timeout_put_hook)(struct nf_ct_timeout *timeout) __read_mostly; 30 EXPORT_SYMBOL_GPL(nf_ct_timeout_put_hook); 31 32 static 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 43 void nf_ct_untimeout(struct net *net, struct nf_ct_timeout *timeout) 44 { 45 nf_ct_iterate_cleanup_net(net, untimeout, timeout, 0, 0); 46 } 47 EXPORT_SYMBOL_GPL(nf_ct_untimeout); 48 49 static 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 58 int 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 109 err_put_timeout: 110 __nf_ct_timeout_put(timeout); 111 out: 112 rcu_read_unlock(); 113 if (errmsg) 114 pr_info_ratelimited("%s\n", errmsg); 115 return ret; 116 } 117 EXPORT_SYMBOL_GPL(nf_ct_set_timeout); 118 119 void 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 } 136 EXPORT_SYMBOL_GPL(nf_ct_destroy_timeout); 137