Commit | Line | Data |
---|---|---|
f6ebe77f HW |
1 | #include <linux/config.h> |
2 | #include <linux/kernel.h> | |
3 | #include <linux/init.h> | |
4 | #include <linux/module.h> | |
5 | #include <linux/proc_fs.h> | |
6 | #include <linux/skbuff.h> | |
7 | #include <linux/netfilter.h> | |
bbd86b9f | 8 | #include <linux/seq_file.h> |
7a11b984 | 9 | #include <linux/rcupdate.h> |
f6ebe77f HW |
10 | #include <net/protocol.h> |
11 | ||
12 | #include "nf_internals.h" | |
13 | ||
14 | /* | |
15 | * A queue handler may be registered for each protocol. Each is protected by | |
16 | * long term mutex. The handler must provide an an outfn() to accept packets | |
17 | * for queueing and must reinject all packets it receives, no matter what. | |
18 | */ | |
bbd86b9f | 19 | static struct nf_queue_handler *queue_handler[NPROTO]; |
f6ebe77f HW |
20 | |
21 | static DEFINE_RWLOCK(queue_handler_lock); | |
22 | ||
d72367b6 HW |
23 | /* return EBUSY when somebody else is registered, return EEXIST if the |
24 | * same handler is registered, return 0 in case of success. */ | |
bbd86b9f | 25 | int nf_register_queue_handler(int pf, struct nf_queue_handler *qh) |
f6ebe77f HW |
26 | { |
27 | int ret; | |
28 | ||
29 | if (pf >= NPROTO) | |
30 | return -EINVAL; | |
31 | ||
32 | write_lock_bh(&queue_handler_lock); | |
d72367b6 HW |
33 | if (queue_handler[pf] == qh) |
34 | ret = -EEXIST; | |
35 | else if (queue_handler[pf]) | |
f6ebe77f HW |
36 | ret = -EBUSY; |
37 | else { | |
bbd86b9f | 38 | queue_handler[pf] = qh; |
f6ebe77f HW |
39 | ret = 0; |
40 | } | |
41 | write_unlock_bh(&queue_handler_lock); | |
42 | ||
43 | return ret; | |
44 | } | |
45 | EXPORT_SYMBOL(nf_register_queue_handler); | |
46 | ||
47 | /* The caller must flush their queue before this */ | |
48 | int nf_unregister_queue_handler(int pf) | |
49 | { | |
50 | if (pf >= NPROTO) | |
51 | return -EINVAL; | |
52 | ||
53 | write_lock_bh(&queue_handler_lock); | |
bbd86b9f | 54 | queue_handler[pf] = NULL; |
f6ebe77f HW |
55 | write_unlock_bh(&queue_handler_lock); |
56 | ||
57 | return 0; | |
58 | } | |
59 | EXPORT_SYMBOL(nf_unregister_queue_handler); | |
60 | ||
bbd86b9f | 61 | void nf_unregister_queue_handlers(struct nf_queue_handler *qh) |
f6ebe77f HW |
62 | { |
63 | int pf; | |
64 | ||
65 | write_lock_bh(&queue_handler_lock); | |
66 | for (pf = 0; pf < NPROTO; pf++) { | |
bbd86b9f HW |
67 | if (queue_handler[pf] == qh) |
68 | queue_handler[pf] = NULL; | |
f6ebe77f HW |
69 | } |
70 | write_unlock_bh(&queue_handler_lock); | |
71 | } | |
72 | EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers); | |
73 | ||
74 | /* | |
75 | * Any packet that leaves via this function must come back | |
76 | * through nf_reinject(). | |
77 | */ | |
78 | int nf_queue(struct sk_buff **skb, | |
79 | struct list_head *elem, | |
80 | int pf, unsigned int hook, | |
81 | struct net_device *indev, | |
82 | struct net_device *outdev, | |
83 | int (*okfn)(struct sk_buff *), | |
84 | unsigned int queuenum) | |
85 | { | |
86 | int status; | |
87 | struct nf_info *info; | |
88 | #ifdef CONFIG_BRIDGE_NETFILTER | |
89 | struct net_device *physindev = NULL; | |
90 | struct net_device *physoutdev = NULL; | |
91 | #endif | |
bce8032e | 92 | struct nf_afinfo *afinfo; |
f6ebe77f HW |
93 | |
94 | /* QUEUE == DROP if noone is waiting, to be safe. */ | |
95 | read_lock(&queue_handler_lock); | |
e121e9ec | 96 | if (!queue_handler[pf]) { |
f6ebe77f HW |
97 | read_unlock(&queue_handler_lock); |
98 | kfree_skb(*skb); | |
99 | return 1; | |
100 | } | |
101 | ||
bce8032e PM |
102 | afinfo = nf_get_afinfo(pf); |
103 | if (!afinfo) { | |
104 | read_unlock(&queue_handler_lock); | |
105 | kfree_skb(*skb); | |
106 | return 1; | |
107 | } | |
108 | ||
109 | info = kmalloc(sizeof(*info) + afinfo->route_key_size, GFP_ATOMIC); | |
f6ebe77f HW |
110 | if (!info) { |
111 | if (net_ratelimit()) | |
112 | printk(KERN_ERR "OOM queueing packet %p\n", | |
113 | *skb); | |
114 | read_unlock(&queue_handler_lock); | |
115 | kfree_skb(*skb); | |
116 | return 1; | |
117 | } | |
118 | ||
119 | *info = (struct nf_info) { | |
120 | (struct nf_hook_ops *)elem, pf, hook, indev, outdev, okfn }; | |
121 | ||
122 | /* If it's going away, ignore hook. */ | |
123 | if (!try_module_get(info->elem->owner)) { | |
124 | read_unlock(&queue_handler_lock); | |
125 | kfree(info); | |
126 | return 0; | |
127 | } | |
128 | ||
129 | /* Bump dev refs so they don't vanish while packet is out */ | |
130 | if (indev) dev_hold(indev); | |
131 | if (outdev) dev_hold(outdev); | |
132 | ||
133 | #ifdef CONFIG_BRIDGE_NETFILTER | |
134 | if ((*skb)->nf_bridge) { | |
135 | physindev = (*skb)->nf_bridge->physindev; | |
136 | if (physindev) dev_hold(physindev); | |
137 | physoutdev = (*skb)->nf_bridge->physoutdev; | |
138 | if (physoutdev) dev_hold(physoutdev); | |
139 | } | |
140 | #endif | |
bce8032e | 141 | afinfo->saveroute(*skb, info); |
bbd86b9f HW |
142 | status = queue_handler[pf]->outfn(*skb, info, queuenum, |
143 | queue_handler[pf]->data); | |
f6ebe77f | 144 | |
f6ebe77f HW |
145 | read_unlock(&queue_handler_lock); |
146 | ||
147 | if (status < 0) { | |
148 | /* James M doesn't say fuck enough. */ | |
149 | if (indev) dev_put(indev); | |
150 | if (outdev) dev_put(outdev); | |
151 | #ifdef CONFIG_BRIDGE_NETFILTER | |
152 | if (physindev) dev_put(physindev); | |
153 | if (physoutdev) dev_put(physoutdev); | |
154 | #endif | |
155 | module_put(info->elem->owner); | |
156 | kfree(info); | |
157 | kfree_skb(*skb); | |
158 | ||
159 | return 1; | |
160 | } | |
161 | ||
162 | return 1; | |
163 | } | |
164 | ||
165 | void nf_reinject(struct sk_buff *skb, struct nf_info *info, | |
166 | unsigned int verdict) | |
167 | { | |
168 | struct list_head *elem = &info->elem->list; | |
169 | struct list_head *i; | |
bce8032e | 170 | struct nf_afinfo *afinfo; |
f6ebe77f HW |
171 | |
172 | rcu_read_lock(); | |
173 | ||
174 | /* Release those devices we held, or Alexey will kill me. */ | |
175 | if (info->indev) dev_put(info->indev); | |
176 | if (info->outdev) dev_put(info->outdev); | |
177 | #ifdef CONFIG_BRIDGE_NETFILTER | |
178 | if (skb->nf_bridge) { | |
179 | if (skb->nf_bridge->physindev) | |
180 | dev_put(skb->nf_bridge->physindev); | |
181 | if (skb->nf_bridge->physoutdev) | |
182 | dev_put(skb->nf_bridge->physoutdev); | |
183 | } | |
184 | #endif | |
185 | ||
186 | /* Drop reference to owner of hook which queued us. */ | |
187 | module_put(info->elem->owner); | |
188 | ||
189 | list_for_each_rcu(i, &nf_hooks[info->pf][info->hook]) { | |
190 | if (i == elem) | |
191 | break; | |
192 | } | |
193 | ||
45fe4dc0 | 194 | if (i == &nf_hooks[info->pf][info->hook]) { |
f6ebe77f HW |
195 | /* The module which sent it to userspace is gone. */ |
196 | NFDEBUG("%s: module disappeared, dropping packet.\n", | |
197 | __FUNCTION__); | |
198 | verdict = NF_DROP; | |
199 | } | |
200 | ||
201 | /* Continue traversal iff userspace said ok... */ | |
202 | if (verdict == NF_REPEAT) { | |
203 | elem = elem->prev; | |
204 | verdict = NF_ACCEPT; | |
205 | } | |
206 | ||
7a11b984 | 207 | if (verdict == NF_ACCEPT) { |
bce8032e PM |
208 | afinfo = nf_get_afinfo(info->pf); |
209 | if (!afinfo || afinfo->reroute(&skb, info) < 0) | |
7a11b984 PM |
210 | verdict = NF_DROP; |
211 | } | |
212 | ||
f6ebe77f HW |
213 | if (verdict == NF_ACCEPT) { |
214 | next_hook: | |
215 | verdict = nf_iterate(&nf_hooks[info->pf][info->hook], | |
216 | &skb, info->hook, | |
217 | info->indev, info->outdev, &elem, | |
218 | info->okfn, INT_MIN); | |
219 | } | |
220 | ||
221 | switch (verdict & NF_VERDICT_MASK) { | |
222 | case NF_ACCEPT: | |
223 | info->okfn(skb); | |
224 | break; | |
225 | ||
226 | case NF_QUEUE: | |
227 | if (!nf_queue(&skb, elem, info->pf, info->hook, | |
228 | info->indev, info->outdev, info->okfn, | |
229 | verdict >> NF_VERDICT_BITS)) | |
230 | goto next_hook; | |
231 | break; | |
232 | } | |
233 | rcu_read_unlock(); | |
234 | ||
235 | if (verdict == NF_DROP) | |
236 | kfree_skb(skb); | |
237 | ||
238 | kfree(info); | |
239 | return; | |
240 | } | |
241 | EXPORT_SYMBOL(nf_reinject); | |
242 | ||
bbd86b9f HW |
243 | #ifdef CONFIG_PROC_FS |
244 | static void *seq_start(struct seq_file *seq, loff_t *pos) | |
245 | { | |
246 | if (*pos >= NPROTO) | |
247 | return NULL; | |
248 | ||
249 | return pos; | |
250 | } | |
251 | ||
252 | static void *seq_next(struct seq_file *s, void *v, loff_t *pos) | |
253 | { | |
254 | (*pos)++; | |
255 | ||
256 | if (*pos >= NPROTO) | |
257 | return NULL; | |
258 | ||
259 | return pos; | |
260 | } | |
261 | ||
262 | static void seq_stop(struct seq_file *s, void *v) | |
263 | { | |
264 | ||
265 | } | |
266 | ||
267 | static int seq_show(struct seq_file *s, void *v) | |
268 | { | |
269 | int ret; | |
270 | loff_t *pos = v; | |
271 | struct nf_queue_handler *qh; | |
272 | ||
273 | read_lock_bh(&queue_handler_lock); | |
274 | qh = queue_handler[*pos]; | |
275 | if (!qh) | |
276 | ret = seq_printf(s, "%2lld NONE\n", *pos); | |
277 | else | |
278 | ret = seq_printf(s, "%2lld %s\n", *pos, qh->name); | |
279 | read_unlock_bh(&queue_handler_lock); | |
280 | ||
281 | return ret; | |
282 | } | |
283 | ||
284 | static struct seq_operations nfqueue_seq_ops = { | |
285 | .start = seq_start, | |
286 | .next = seq_next, | |
287 | .stop = seq_stop, | |
288 | .show = seq_show, | |
289 | }; | |
290 | ||
291 | static int nfqueue_open(struct inode *inode, struct file *file) | |
292 | { | |
293 | return seq_open(file, &nfqueue_seq_ops); | |
294 | } | |
295 | ||
296 | static struct file_operations nfqueue_file_ops = { | |
297 | .owner = THIS_MODULE, | |
298 | .open = nfqueue_open, | |
299 | .read = seq_read, | |
300 | .llseek = seq_lseek, | |
301 | .release = seq_release, | |
302 | }; | |
303 | #endif /* PROC_FS */ | |
304 | ||
305 | ||
f6ebe77f HW |
306 | int __init netfilter_queue_init(void) |
307 | { | |
bbd86b9f HW |
308 | #ifdef CONFIG_PROC_FS |
309 | struct proc_dir_entry *pde; | |
f6ebe77f | 310 | |
bbd86b9f | 311 | pde = create_proc_entry("nf_queue", S_IRUGO, proc_net_netfilter); |
e02f7d16 | 312 | if (!pde) |
bbd86b9f | 313 | return -1; |
bbd86b9f HW |
314 | pde->proc_fops = &nfqueue_file_ops; |
315 | #endif | |
f6ebe77f HW |
316 | return 0; |
317 | } | |
318 |