]>
Commit | Line | Data |
---|---|---|
e97150df | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
dd705072 PNA |
2 | /* |
3 | * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> | |
4 | * (C) 2012 by Vyatta Inc. <http://www.vyatta.com> | |
dd705072 PNA |
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> | |
40d102cd | 22 | #include <net/netfilter/nf_conntrack_l4proto.h> |
dd705072 PNA |
23 | #include <net/netfilter/nf_conntrack_timeout.h> |
24 | ||
6c1fd7dc | 25 | struct nf_ct_timeout * |
19576c94 | 26 | (*nf_ct_timeout_find_get_hook)(struct net *net, const char *name) __read_mostly; |
dd705072 PNA |
27 | EXPORT_SYMBOL_GPL(nf_ct_timeout_find_get_hook); |
28 | ||
6c1fd7dc | 29 | void (*nf_ct_timeout_put_hook)(struct nf_ct_timeout *timeout) __read_mostly; |
dd705072 PNA |
30 | EXPORT_SYMBOL_GPL(nf_ct_timeout_put_hook); |
31 | ||
4e665afb HS |
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 | ||
6c1fd7dc | 43 | void nf_ct_untimeout(struct net *net, struct nf_ct_timeout *timeout) |
4e665afb HS |
44 | { |
45 | nf_ct_iterate_cleanup_net(net, untimeout, timeout, 0, 0); | |
46 | } | |
47 | EXPORT_SYMBOL_GPL(nf_ct_untimeout); | |
48 | ||
717700d1 YHW |
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 | ||
23f671a1 | 138 | static const struct nf_ct_ext_type timeout_extend = { |
dd705072 PNA |
139 | .len = sizeof(struct nf_conn_timeout), |
140 | .align = __alignof__(struct nf_conn_timeout), | |
141 | .id = NF_CT_EXT_TIMEOUT, | |
142 | }; | |
143 | ||
8684094c | 144 | int nf_conntrack_timeout_init(void) |
dd705072 | 145 | { |
8684094c G |
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; | |
dd705072 PNA |
150 | } |
151 | ||
8684094c | 152 | void nf_conntrack_timeout_fini(void) |
dd705072 | 153 | { |
8684094c | 154 | nf_ct_extend_unregister(&timeout_extend); |
dd705072 | 155 | } |