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 | const struct nf_ct_timeout_hooks __rcu *nf_ct_timeout_hook __read_mostly; |
26 | EXPORT_SYMBOL_GPL(nf_ct_timeout_hook); |
27 | |
28 | static int untimeout(struct nf_conn *ct, void *timeout) |
29 | { |
30 | struct nf_conn_timeout *timeout_ext = nf_ct_timeout_find(ct); |
31 | |
32 | if (timeout_ext) { |
33 | const struct nf_ct_timeout *t; |
34 | |
35 | t = rcu_access_pointer(timeout_ext->timeout); |
36 | |
37 | if (!timeout || t == timeout) |
38 | RCU_INIT_POINTER(timeout_ext->timeout, NULL); |
39 | } |
40 | |
41 | /* We are not intended to delete this conntrack. */ |
42 | return 0; |
43 | } |
44 | |
45 | void nf_ct_untimeout(struct net *net, struct nf_ct_timeout *timeout) |
46 | { |
47 | struct nf_ct_iter_data iter_data = { |
48 | .net = net, |
49 | .data = timeout, |
50 | }; |
51 | |
52 | nf_ct_iterate_cleanup_net(iter: untimeout, iter_data: &iter_data); |
53 | } |
54 | EXPORT_SYMBOL_GPL(nf_ct_untimeout); |
55 | |
56 | static void __nf_ct_timeout_put(struct nf_ct_timeout *timeout) |
57 | { |
58 | const struct nf_ct_timeout_hooks *h = rcu_dereference(nf_ct_timeout_hook); |
59 | |
60 | if (h) |
61 | h->timeout_put(timeout); |
62 | } |
63 | |
64 | int nf_ct_set_timeout(struct net *net, struct nf_conn *ct, |
65 | u8 l3num, u8 l4num, const char *timeout_name) |
66 | { |
67 | const struct nf_ct_timeout_hooks *h; |
68 | struct nf_ct_timeout *timeout; |
69 | struct nf_conn_timeout *timeout_ext; |
70 | const char *errmsg = NULL; |
71 | int ret = 0; |
72 | |
73 | rcu_read_lock(); |
74 | h = rcu_dereference(nf_ct_timeout_hook); |
75 | if (!h) { |
76 | ret = -ENOENT; |
77 | errmsg = "Timeout policy base is empty" ; |
78 | goto out; |
79 | } |
80 | |
81 | timeout = h->timeout_find_get(net, timeout_name); |
82 | if (!timeout) { |
83 | ret = -ENOENT; |
84 | pr_info_ratelimited("No such timeout policy \"%s\"\n" , |
85 | timeout_name); |
86 | goto out; |
87 | } |
88 | |
89 | if (timeout->l3num != l3num) { |
90 | ret = -EINVAL; |
91 | pr_info_ratelimited("Timeout policy `%s' can only be used by " |
92 | "L%d protocol number %d\n" , |
93 | timeout_name, 3, timeout->l3num); |
94 | goto err_put_timeout; |
95 | } |
96 | /* Make sure the timeout policy matches any existing protocol tracker, |
97 | * otherwise default to generic. |
98 | */ |
99 | if (timeout->l4proto->l4proto != l4num) { |
100 | ret = -EINVAL; |
101 | pr_info_ratelimited("Timeout policy `%s' can only be used by " |
102 | "L%d protocol number %d\n" , |
103 | timeout_name, 4, timeout->l4proto->l4proto); |
104 | goto err_put_timeout; |
105 | } |
106 | timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC); |
107 | if (!timeout_ext) { |
108 | ret = -ENOMEM; |
109 | goto err_put_timeout; |
110 | } |
111 | |
112 | rcu_read_unlock(); |
113 | return ret; |
114 | |
115 | err_put_timeout: |
116 | __nf_ct_timeout_put(timeout); |
117 | out: |
118 | rcu_read_unlock(); |
119 | if (errmsg) |
120 | pr_info_ratelimited("%s\n" , errmsg); |
121 | return ret; |
122 | } |
123 | EXPORT_SYMBOL_GPL(nf_ct_set_timeout); |
124 | |
125 | void nf_ct_destroy_timeout(struct nf_conn *ct) |
126 | { |
127 | struct nf_conn_timeout *timeout_ext; |
128 | const struct nf_ct_timeout_hooks *h; |
129 | |
130 | rcu_read_lock(); |
131 | h = rcu_dereference(nf_ct_timeout_hook); |
132 | |
133 | if (h) { |
134 | timeout_ext = nf_ct_timeout_find(ct); |
135 | if (timeout_ext) { |
136 | struct nf_ct_timeout *t; |
137 | |
138 | t = rcu_dereference(timeout_ext->timeout); |
139 | if (t) |
140 | h->timeout_put(t); |
141 | RCU_INIT_POINTER(timeout_ext->timeout, NULL); |
142 | } |
143 | } |
144 | rcu_read_unlock(); |
145 | } |
146 | EXPORT_SYMBOL_GPL(nf_ct_destroy_timeout); |
147 | |