diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /net/ipv4/netfilter | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'net/ipv4/netfilter')
21 files changed, 4848 insertions, 0 deletions
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c new file mode 100644 index 00000000000..e59aabd0eae --- /dev/null +++ b/net/ipv4/netfilter/ip_queue.c | |||
@@ -0,0 +1,637 @@ | |||
1 | /* | ||
2 | * This is a module which is used for queueing IPv4 packets and | ||
3 | * communicating with userspace via netlink. | ||
4 | * | ||
5 | * (C) 2000-2002 James Morris <jmorris@intercode.com.au> | ||
6 | * (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/skbuff.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/ip.h> | ||
16 | #include <linux/notifier.h> | ||
17 | #include <linux/netdevice.h> | ||
18 | #include <linux/netfilter.h> | ||
19 | #include <linux/netfilter_ipv4/ip_queue.h> | ||
20 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
21 | #include <linux/netlink.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/sysctl.h> | ||
24 | #include <linux/proc_fs.h> | ||
25 | #include <linux/seq_file.h> | ||
26 | #include <linux/security.h> | ||
27 | #include <linux/net.h> | ||
28 | #include <linux/mutex.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <net/net_namespace.h> | ||
31 | #include <net/sock.h> | ||
32 | #include <net/route.h> | ||
33 | #include <net/netfilter/nf_queue.h> | ||
34 | #include <net/ip.h> | ||
35 | |||
36 | #define IPQ_QMAX_DEFAULT 1024 | ||
37 | #define IPQ_PROC_FS_NAME "ip_queue" | ||
38 | #define NET_IPQ_QMAX 2088 | ||
39 | #define NET_IPQ_QMAX_NAME "ip_queue_maxlen" | ||
40 | |||
41 | typedef int (*ipq_cmpfn)(struct nf_queue_entry *, unsigned long); | ||
42 | |||
43 | static unsigned char copy_mode __read_mostly = IPQ_COPY_NONE; | ||
44 | static unsigned int queue_maxlen __read_mostly = IPQ_QMAX_DEFAULT; | ||
45 | static DEFINE_SPINLOCK(queue_lock); | ||
46 | static int peer_pid __read_mostly; | ||
47 | static unsigned int copy_range __read_mostly; | ||
48 | static unsigned int queue_total; | ||
49 | static unsigned int queue_dropped = 0; | ||
50 | static unsigned int queue_user_dropped = 0; | ||
51 | static struct sock *ipqnl __read_mostly; | ||
52 | static LIST_HEAD(queue_list); | ||
53 | static DEFINE_MUTEX(ipqnl_mutex); | ||
54 | |||
55 | static inline void | ||
56 | __ipq_enqueue_entry(struct nf_queue_entry *entry) | ||
57 | { | ||
58 | list_add_tail(&entry->list, &queue_list); | ||
59 | queue_total++; | ||
60 | } | ||
61 | |||
62 | static inline int | ||
63 | __ipq_set_mode(unsigned char mode, unsigned int range) | ||
64 | { | ||
65 | int status = 0; | ||
66 | |||
67 | switch(mode) { | ||
68 | case IPQ_COPY_NONE: | ||
69 | case IPQ_COPY_META: | ||
70 | copy_mode = mode; | ||
71 | copy_range = 0; | ||
72 | break; | ||
73 | |||
74 | case IPQ_COPY_PACKET: | ||
75 | if (range > 0xFFFF) | ||
76 | range = 0xFFFF; | ||
77 | copy_range = range; | ||
78 | copy_mode = mode; | ||
79 | break; | ||
80 | |||
81 | default: | ||
82 | status = -EINVAL; | ||
83 | |||
84 | } | ||
85 | return status; | ||
86 | } | ||
87 | |||
88 | static void __ipq_flush(ipq_cmpfn cmpfn, unsigned long data); | ||
89 | |||
90 | static inline void | ||
91 | __ipq_reset(void) | ||
92 | { | ||
93 | peer_pid = 0; | ||
94 | net_disable_timestamp(); | ||
95 | __ipq_set_mode(IPQ_COPY_NONE, 0); | ||
96 | __ipq_flush(NULL, 0); | ||
97 | } | ||
98 | |||
99 | static struct nf_queue_entry * | ||
100 | ipq_find_dequeue_entry(unsigned long id) | ||
101 | { | ||
102 | struct nf_queue_entry *entry = NULL, *i; | ||
103 | |||
104 | spin_lock_bh(&queue_lock); | ||
105 | |||
106 | list_for_each_entry(i, &queue_list, list) { | ||
107 | if ((unsigned long)i == id) { | ||
108 | entry = i; | ||
109 | break; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | if (entry) { | ||
114 | list_del(&entry->list); | ||
115 | queue_total--; | ||
116 | } | ||
117 | |||
118 | spin_unlock_bh(&queue_lock); | ||
119 | return entry; | ||
120 | } | ||
121 | |||
122 | static void | ||
123 | __ipq_flush(ipq_cmpfn cmpfn, unsigned long data) | ||
124 | { | ||
125 | struct nf_queue_entry *entry, *next; | ||
126 | |||
127 | list_for_each_entry_safe(entry, next, &queue_list, list) { | ||
128 | if (!cmpfn || cmpfn(entry, data)) { | ||
129 | list_del(&entry->list); | ||
130 | queue_total--; | ||
131 | nf_reinject(entry, NF_DROP); | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | |||
136 | static void | ||
137 | ipq_flush(ipq_cmpfn cmpfn, unsigned long data) | ||
138 | { | ||
139 | spin_lock_bh(&queue_lock); | ||
140 | __ipq_flush(cmpfn, data); | ||
141 | spin_unlock_bh(&queue_lock); | ||
142 | } | ||
143 | |||
144 | static struct sk_buff * | ||
145 | ipq_build_packet_message(struct nf_queue_entry *entry, int *errp) | ||
146 | { | ||
147 | sk_buff_data_t old_tail; | ||
148 | size_t size = 0; | ||
149 | size_t data_len = 0; | ||
150 | struct sk_buff *skb; | ||
151 | struct ipq_packet_msg *pmsg; | ||
152 | struct nlmsghdr *nlh; | ||
153 | struct timeval tv; | ||
154 | |||
155 | switch (ACCESS_ONCE(copy_mode)) { | ||
156 | case IPQ_COPY_META: | ||
157 | case IPQ_COPY_NONE: | ||
158 | size = NLMSG_SPACE(sizeof(*pmsg)); | ||
159 | break; | ||
160 | |||
161 | case IPQ_COPY_PACKET: | ||
162 | if (entry->skb->ip_summed == CHECKSUM_PARTIAL && | ||
163 | (*errp = skb_checksum_help(entry->skb))) | ||
164 | return NULL; | ||
165 | |||
166 | data_len = ACCESS_ONCE(copy_range); | ||
167 | if (data_len == 0 || data_len > entry->skb->len) | ||
168 | data_len = entry->skb->len; | ||
169 | |||
170 | size = NLMSG_SPACE(sizeof(*pmsg) + data_len); | ||
171 | break; | ||
172 | |||
173 | default: | ||
174 | *errp = -EINVAL; | ||
175 | return NULL; | ||
176 | } | ||
177 | |||
178 | skb = alloc_skb(size, GFP_ATOMIC); | ||
179 | if (!skb) | ||
180 | goto nlmsg_failure; | ||
181 | |||
182 | old_tail = skb->tail; | ||
183 | nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh)); | ||
184 | pmsg = NLMSG_DATA(nlh); | ||
185 | memset(pmsg, 0, sizeof(*pmsg)); | ||
186 | |||
187 | pmsg->packet_id = (unsigned long )entry; | ||
188 | pmsg->data_len = data_len; | ||
189 | tv = ktime_to_timeval(entry->skb->tstamp); | ||
190 | pmsg->timestamp_sec = tv.tv_sec; | ||
191 | pmsg->timestamp_usec = tv.tv_usec; | ||
192 | pmsg->mark = entry->skb->mark; | ||
193 | pmsg->hook = entry->hook; | ||
194 | pmsg->hw_protocol = entry->skb->protocol; | ||
195 | |||
196 | if (entry->indev) | ||
197 | strcpy(pmsg->indev_name, entry->indev->name); | ||
198 | else | ||
199 | pmsg->indev_name[0] = '\0'; | ||
200 | |||
201 | if (entry->outdev) | ||
202 | strcpy(pmsg->outdev_name, entry->outdev->name); | ||
203 | else | ||
204 | pmsg->outdev_name[0] = '\0'; | ||
205 | |||
206 | if (entry->indev && entry->skb->dev && | ||
207 | entry->skb->mac_header != entry->skb->network_header) { | ||
208 | pmsg->hw_type = entry->skb->dev->type; | ||
209 | pmsg->hw_addrlen = dev_parse_header(entry->skb, | ||
210 | pmsg->hw_addr); | ||
211 | } | ||
212 | |||
213 | if (data_len) | ||
214 | if (skb_copy_bits(entry->skb, 0, pmsg->payload, data_len)) | ||
215 | BUG(); | ||
216 | |||
217 | nlh->nlmsg_len = skb->tail - old_tail; | ||
218 | return skb; | ||
219 | |||
220 | nlmsg_failure: | ||
221 | kfree_skb(skb); | ||
222 | *errp = -EINVAL; | ||
223 | printk(KERN_ERR "ip_queue: error creating packet message\n"); | ||
224 | return NULL; | ||
225 | } | ||
226 | |||
227 | static int | ||
228 | ipq_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) | ||
229 | { | ||
230 | int status = -EINVAL; | ||
231 | struct sk_buff *nskb; | ||
232 | |||
233 | if (copy_mode == IPQ_COPY_NONE) | ||
234 | return -EAGAIN; | ||
235 | |||
236 | nskb = ipq_build_packet_message(entry, &status); | ||
237 | if (nskb == NULL) | ||
238 | return status; | ||
239 | |||
240 | spin_lock_bh(&queue_lock); | ||
241 | |||
242 | if (!peer_pid) | ||
243 | goto err_out_free_nskb; | ||
244 | |||
245 | if (queue_total >= queue_maxlen) { | ||
246 | queue_dropped++; | ||
247 | status = -ENOSPC; | ||
248 | if (net_ratelimit()) | ||
249 | printk (KERN_WARNING "ip_queue: full at %d entries, " | ||
250 | "dropping packets(s). Dropped: %d\n", queue_total, | ||
251 | queue_dropped); | ||
252 | goto err_out_free_nskb; | ||
253 | } | ||
254 | |||
255 | /* netlink_unicast will either free the nskb or attach it to a socket */ | ||
256 | status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT); | ||
257 | if (status < 0) { | ||
258 | queue_user_dropped++; | ||
259 | goto err_out_unlock; | ||
260 | } | ||
261 | |||
262 | __ipq_enqueue_entry(entry); | ||
263 | |||
264 | spin_unlock_bh(&queue_lock); | ||
265 | return status; | ||
266 | |||
267 | err_out_free_nskb: | ||
268 | kfree_skb(nskb); | ||
269 | |||
270 | err_out_unlock: | ||
271 | spin_unlock_bh(&queue_lock); | ||
272 | return status; | ||
273 | } | ||
274 | |||
275 | static int | ||
276 | ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e) | ||
277 | { | ||
278 | int diff; | ||
279 | struct iphdr *user_iph = (struct iphdr *)v->payload; | ||
280 | struct sk_buff *nskb; | ||
281 | |||
282 | if (v->data_len < sizeof(*user_iph)) | ||
283 | return 0; | ||
284 | diff = v->data_len - e->skb->len; | ||
285 | if (diff < 0) { | ||
286 | if (pskb_trim(e->skb, v->data_len)) | ||
287 | return -ENOMEM; | ||
288 | } else if (diff > 0) { | ||
289 | if (v->data_len > 0xFFFF) | ||
290 | return -EINVAL; | ||
291 | if (diff > skb_tailroom(e->skb)) { | ||
292 | nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), | ||
293 | diff, GFP_ATOMIC); | ||
294 | if (!nskb) { | ||
295 | printk(KERN_WARNING "ip_queue: error " | ||
296 | "in mangle, dropping packet\n"); | ||
297 | return -ENOMEM; | ||
298 | } | ||
299 | kfree_skb(e->skb); | ||
300 | e->skb = nskb; | ||
301 | } | ||
302 | skb_put(e->skb, diff); | ||
303 | } | ||
304 | if (!skb_make_writable(e->skb, v->data_len)) | ||
305 | return -ENOMEM; | ||
306 | skb_copy_to_linear_data(e->skb, v->payload, v->data_len); | ||
307 | e->skb->ip_summed = CHECKSUM_NONE; | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int | ||
313 | ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len) | ||
314 | { | ||
315 | struct nf_queue_entry *entry; | ||
316 | |||
317 | if (vmsg->value > NF_MAX_VERDICT || vmsg->value == NF_STOLEN) | ||
318 | return -EINVAL; | ||
319 | |||
320 | entry = ipq_find_dequeue_entry(vmsg->id); | ||
321 | if (entry == NULL) | ||
322 | return -ENOENT; | ||
323 | else { | ||
324 | int verdict = vmsg->value; | ||
325 | |||
326 | if (vmsg->data_len && vmsg->data_len == len) | ||
327 | if (ipq_mangle_ipv4(vmsg, entry) < 0) | ||
328 | verdict = NF_DROP; | ||
329 | |||
330 | nf_reinject(entry, verdict); | ||
331 | return 0; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | static int | ||
336 | ipq_set_mode(unsigned char mode, unsigned int range) | ||
337 | { | ||
338 | int status; | ||
339 | |||
340 | spin_lock_bh(&queue_lock); | ||
341 | status = __ipq_set_mode(mode, range); | ||
342 | spin_unlock_bh(&queue_lock); | ||
343 | return status; | ||
344 | } | ||
345 | |||
346 | static int | ||
347 | ipq_receive_peer(struct ipq_peer_msg *pmsg, | ||
348 | unsigned char type, unsigned int len) | ||
349 | { | ||
350 | int status = 0; | ||
351 | |||
352 | if (len < sizeof(*pmsg)) | ||
353 | return -EINVAL; | ||
354 | |||
355 | switch (type) { | ||
356 | case IPQM_MODE: | ||
357 | status = ipq_set_mode(pmsg->msg.mode.value, | ||
358 | pmsg->msg.mode.range); | ||
359 | break; | ||
360 | |||
361 | case IPQM_VERDICT: | ||
362 | status = ipq_set_verdict(&pmsg->msg.verdict, | ||
363 | len - sizeof(*pmsg)); | ||
364 | break; | ||
365 | default: | ||
366 | status = -EINVAL; | ||
367 | } | ||
368 | return status; | ||
369 | } | ||
370 | |||
371 | static int | ||
372 | dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) | ||
373 | { | ||
374 | if (entry->indev) | ||
375 | if (entry->indev->ifindex == ifindex) | ||
376 | return 1; | ||
377 | if (entry->outdev) | ||
378 | if (entry->outdev->ifindex == ifindex) | ||
379 | return 1; | ||
380 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
381 | if (entry->skb->nf_bridge) { | ||
382 | if (entry->skb->nf_bridge->physindev && | ||
383 | entry->skb->nf_bridge->physindev->ifindex == ifindex) | ||
384 | return 1; | ||
385 | if (entry->skb->nf_bridge->physoutdev && | ||
386 | entry->skb->nf_bridge->physoutdev->ifindex == ifindex) | ||
387 | return 1; | ||
388 | } | ||
389 | #endif | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | static void | ||
394 | ipq_dev_drop(int ifindex) | ||
395 | { | ||
396 | ipq_flush(dev_cmp, ifindex); | ||
397 | } | ||
398 | |||
399 | #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) | ||
400 | |||
401 | static inline void | ||
402 | __ipq_rcv_skb(struct sk_buff *skb) | ||
403 | { | ||
404 | int status, type, pid, flags; | ||
405 | unsigned int nlmsglen, skblen; | ||
406 | struct nlmsghdr *nlh; | ||
407 | |||
408 | skblen = skb->len; | ||
409 | if (skblen < sizeof(*nlh)) | ||
410 | return; | ||
411 | |||
412 | nlh = nlmsg_hdr(skb); | ||
413 | nlmsglen = nlh->nlmsg_len; | ||
414 | if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) | ||
415 | return; | ||
416 | |||
417 | pid = nlh->nlmsg_pid; | ||
418 | flags = nlh->nlmsg_flags; | ||
419 | |||
420 | if(pid <= 0 || !(flags & NLM_F_REQUEST) || flags & NLM_F_MULTI) | ||
421 | RCV_SKB_FAIL(-EINVAL); | ||
422 | |||
423 | if (flags & MSG_TRUNC) | ||
424 | RCV_SKB_FAIL(-ECOMM); | ||
425 | |||
426 | type = nlh->nlmsg_type; | ||
427 | if (type < NLMSG_NOOP || type >= IPQM_MAX) | ||
428 | RCV_SKB_FAIL(-EINVAL); | ||
429 | |||
430 | if (type <= IPQM_BASE) | ||
431 | return; | ||
432 | |||
433 | if (security_netlink_recv(skb, CAP_NET_ADMIN)) | ||
434 | RCV_SKB_FAIL(-EPERM); | ||
435 | |||
436 | spin_lock_bh(&queue_lock); | ||
437 | |||
438 | if (peer_pid) { | ||
439 | if (peer_pid != pid) { | ||
440 | spin_unlock_bh(&queue_lock); | ||
441 | RCV_SKB_FAIL(-EBUSY); | ||
442 | } | ||
443 | } else { | ||
444 | net_enable_timestamp(); | ||
445 | peer_pid = pid; | ||
446 | } | ||
447 | |||
448 | spin_unlock_bh(&queue_lock); | ||
449 | |||
450 | status = ipq_receive_peer(NLMSG_DATA(nlh), type, | ||
451 | nlmsglen - NLMSG_LENGTH(0)); | ||
452 | if (status < 0) | ||
453 | RCV_SKB_FAIL(status); | ||
454 | |||
455 | if (flags & NLM_F_ACK) | ||
456 | netlink_ack(skb, nlh, 0); | ||
457 | } | ||
458 | |||
459 | static void | ||
460 | ipq_rcv_skb(struct sk_buff *skb) | ||
461 | { | ||
462 | mutex_lock(&ipqnl_mutex); | ||
463 | __ipq_rcv_skb(skb); | ||
464 | mutex_unlock(&ipqnl_mutex); | ||
465 | } | ||
466 | |||
467 | static int | ||
468 | ipq_rcv_dev_event(struct notifier_block *this, | ||
469 | unsigned long event, void *ptr) | ||
470 | { | ||
471 | struct net_device *dev = ptr; | ||
472 | |||
473 | if (!net_eq(dev_net(dev), &init_net)) | ||
474 | return NOTIFY_DONE; | ||
475 | |||
476 | /* Drop any packets associated with the downed device */ | ||
477 | if (event == NETDEV_DOWN) | ||
478 | ipq_dev_drop(dev->ifindex); | ||
479 | return NOTIFY_DONE; | ||
480 | } | ||
481 | |||
482 | static struct notifier_block ipq_dev_notifier = { | ||
483 | .notifier_call = ipq_rcv_dev_event, | ||
484 | }; | ||
485 | |||
486 | static int | ||
487 | ipq_rcv_nl_event(struct notifier_block *this, | ||
488 | unsigned long event, void *ptr) | ||
489 | { | ||
490 | struct netlink_notify *n = ptr; | ||
491 | |||
492 | if (event == NETLINK_URELEASE && n->protocol == NETLINK_FIREWALL) { | ||
493 | spin_lock_bh(&queue_lock); | ||
494 | if ((net_eq(n->net, &init_net)) && (n->pid == peer_pid)) | ||
495 | __ipq_reset(); | ||
496 | spin_unlock_bh(&queue_lock); | ||
497 | } | ||
498 | return NOTIFY_DONE; | ||
499 | } | ||
500 | |||
501 | static struct notifier_block ipq_nl_notifier = { | ||
502 | .notifier_call = ipq_rcv_nl_event, | ||
503 | }; | ||
504 | |||
505 | #ifdef CONFIG_SYSCTL | ||
506 | static struct ctl_table_header *ipq_sysctl_header; | ||
507 | |||
508 | static ctl_table ipq_table[] = { | ||
509 | { | ||
510 | .procname = NET_IPQ_QMAX_NAME, | ||
511 | .data = &queue_maxlen, | ||
512 | .maxlen = sizeof(queue_maxlen), | ||
513 | .mode = 0644, | ||
514 | .proc_handler = proc_dointvec | ||
515 | }, | ||
516 | { } | ||
517 | }; | ||
518 | #endif | ||
519 | |||
520 | #ifdef CONFIG_PROC_FS | ||
521 | static int ip_queue_show(struct seq_file *m, void *v) | ||
522 | { | ||
523 | spin_lock_bh(&queue_lock); | ||
524 | |||
525 | seq_printf(m, | ||
526 | "Peer PID : %d\n" | ||
527 | "Copy mode : %hu\n" | ||
528 | "Copy range : %u\n" | ||
529 | "Queue length : %u\n" | ||
530 | "Queue max. length : %u\n" | ||
531 | "Queue dropped : %u\n" | ||
532 | "Netlink dropped : %u\n", | ||
533 | peer_pid, | ||
534 | copy_mode, | ||
535 | copy_range, | ||
536 | queue_total, | ||
537 | queue_maxlen, | ||
538 | queue_dropped, | ||
539 | queue_user_dropped); | ||
540 | |||
541 | spin_unlock_bh(&queue_lock); | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static int ip_queue_open(struct inode *inode, struct file *file) | ||
546 | { | ||
547 | return single_open(file, ip_queue_show, NULL); | ||
548 | } | ||
549 | |||
550 | static const struct file_operations ip_queue_proc_fops = { | ||
551 | .open = ip_queue_open, | ||
552 | .read = seq_read, | ||
553 | .llseek = seq_lseek, | ||
554 | .release = single_release, | ||
555 | .owner = THIS_MODULE, | ||
556 | }; | ||
557 | #endif | ||
558 | |||
559 | static const struct nf_queue_handler nfqh = { | ||
560 | .name = "ip_queue", | ||
561 | .outfn = &ipq_enqueue_packet, | ||
562 | }; | ||
563 | |||
564 | static int __init ip_queue_init(void) | ||
565 | { | ||
566 | int status = -ENOMEM; | ||
567 | struct proc_dir_entry *proc __maybe_unused; | ||
568 | |||
569 | netlink_register_notifier(&ipq_nl_notifier); | ||
570 | ipqnl = netlink_kernel_create(&init_net, NETLINK_FIREWALL, 0, | ||
571 | ipq_rcv_skb, NULL, THIS_MODULE); | ||
572 | if (ipqnl == NULL) { | ||
573 | printk(KERN_ERR "ip_queue: failed to create netlink socket\n"); | ||
574 | goto cleanup_netlink_notifier; | ||
575 | } | ||
576 | |||
577 | #ifdef CONFIG_PROC_FS | ||
578 | proc = proc_create(IPQ_PROC_FS_NAME, 0, init_net.proc_net, | ||
579 | &ip_queue_proc_fops); | ||
580 | if (!proc) { | ||
581 | printk(KERN_ERR "ip_queue: failed to create proc entry\n"); | ||
582 | goto cleanup_ipqnl; | ||
583 | } | ||
584 | #endif | ||
585 | register_netdevice_notifier(&ipq_dev_notifier); | ||
586 | #ifdef CONFIG_SYSCTL | ||
587 | ipq_sysctl_header = register_sysctl_paths(net_ipv4_ctl_path, ipq_table); | ||
588 | #endif | ||
589 | status = nf_register_queue_handler(NFPROTO_IPV4, &nfqh); | ||
590 | if (status < 0) { | ||
591 | printk(KERN_ERR "ip_queue: failed to register queue handler\n"); | ||
592 | goto cleanup_sysctl; | ||
593 | } | ||
594 | return status; | ||
595 | |||
596 | cleanup_sysctl: | ||
597 | #ifdef CONFIG_SYSCTL | ||
598 | unregister_sysctl_table(ipq_sysctl_header); | ||
599 | #endif | ||
600 | unregister_netdevice_notifier(&ipq_dev_notifier); | ||
601 | proc_net_remove(&init_net, IPQ_PROC_FS_NAME); | ||
602 | cleanup_ipqnl: __maybe_unused | ||
603 | netlink_kernel_release(ipqnl); | ||
604 | mutex_lock(&ipqnl_mutex); | ||
605 | mutex_unlock(&ipqnl_mutex); | ||
606 | |||
607 | cleanup_netlink_notifier: | ||
608 | netlink_unregister_notifier(&ipq_nl_notifier); | ||
609 | return status; | ||
610 | } | ||
611 | |||
612 | static void __exit ip_queue_fini(void) | ||
613 | { | ||
614 | nf_unregister_queue_handlers(&nfqh); | ||
615 | |||
616 | ipq_flush(NULL, 0); | ||
617 | |||
618 | #ifdef CONFIG_SYSCTL | ||
619 | unregister_sysctl_table(ipq_sysctl_header); | ||
620 | #endif | ||
621 | unregister_netdevice_notifier(&ipq_dev_notifier); | ||
622 | proc_net_remove(&init_net, IPQ_PROC_FS_NAME); | ||
623 | |||
624 | netlink_kernel_release(ipqnl); | ||
625 | mutex_lock(&ipqnl_mutex); | ||
626 | mutex_unlock(&ipqnl_mutex); | ||
627 | |||
628 | netlink_unregister_notifier(&ipq_nl_notifier); | ||
629 | } | ||
630 | |||
631 | MODULE_DESCRIPTION("IPv4 packet queue handler"); | ||
632 | MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); | ||
633 | MODULE_LICENSE("GPL"); | ||
634 | MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_FIREWALL); | ||
635 | |||
636 | module_init(ip_queue_init); | ||
637 | module_exit(ip_queue_fini); | ||
diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c new file mode 100644 index 00000000000..d76d6c9ed94 --- /dev/null +++ b/net/ipv4/netfilter/ipt_LOG.c | |||
@@ -0,0 +1,516 @@ | |||
1 | /* | ||
2 | * This is a module which is used for logging packets. | ||
3 | */ | ||
4 | |||
5 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
6 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <linux/skbuff.h> | ||
16 | #include <linux/if_arp.h> | ||
17 | #include <linux/ip.h> | ||
18 | #include <net/icmp.h> | ||
19 | #include <net/udp.h> | ||
20 | #include <net/tcp.h> | ||
21 | #include <net/route.h> | ||
22 | |||
23 | #include <linux/netfilter.h> | ||
24 | #include <linux/netfilter/x_tables.h> | ||
25 | #include <linux/netfilter_ipv4/ipt_LOG.h> | ||
26 | #include <net/netfilter/nf_log.h> | ||
27 | #include <net/netfilter/xt_log.h> | ||
28 | |||
29 | MODULE_LICENSE("GPL"); | ||
30 | MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); | ||
31 | MODULE_DESCRIPTION("Xtables: IPv4 packet logging to syslog"); | ||
32 | |||
33 | /* One level of recursion won't kill us */ | ||
34 | static void dump_packet(struct sbuff *m, | ||
35 | const struct nf_loginfo *info, | ||
36 | const struct sk_buff *skb, | ||
37 | unsigned int iphoff) | ||
38 | { | ||
39 | struct iphdr _iph; | ||
40 | const struct iphdr *ih; | ||
41 | unsigned int logflags; | ||
42 | |||
43 | if (info->type == NF_LOG_TYPE_LOG) | ||
44 | logflags = info->u.log.logflags; | ||
45 | else | ||
46 | logflags = NF_LOG_MASK; | ||
47 | |||
48 | ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); | ||
49 | if (ih == NULL) { | ||
50 | sb_add(m, "TRUNCATED"); | ||
51 | return; | ||
52 | } | ||
53 | |||
54 | /* Important fields: | ||
55 | * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ | ||
56 | /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ | ||
57 | sb_add(m, "SRC=%pI4 DST=%pI4 ", | ||
58 | &ih->saddr, &ih->daddr); | ||
59 | |||
60 | /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ | ||
61 | sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", | ||
62 | ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, | ||
63 | ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); | ||
64 | |||
65 | /* Max length: 6 "CE DF MF " */ | ||
66 | if (ntohs(ih->frag_off) & IP_CE) | ||
67 | sb_add(m, "CE "); | ||
68 | if (ntohs(ih->frag_off) & IP_DF) | ||
69 | sb_add(m, "DF "); | ||
70 | if (ntohs(ih->frag_off) & IP_MF) | ||
71 | sb_add(m, "MF "); | ||
72 | |||
73 | /* Max length: 11 "FRAG:65535 " */ | ||
74 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
75 | sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); | ||
76 | |||
77 | if ((logflags & IPT_LOG_IPOPT) && | ||
78 | ih->ihl * 4 > sizeof(struct iphdr)) { | ||
79 | const unsigned char *op; | ||
80 | unsigned char _opt[4 * 15 - sizeof(struct iphdr)]; | ||
81 | unsigned int i, optsize; | ||
82 | |||
83 | optsize = ih->ihl * 4 - sizeof(struct iphdr); | ||
84 | op = skb_header_pointer(skb, iphoff+sizeof(_iph), | ||
85 | optsize, _opt); | ||
86 | if (op == NULL) { | ||
87 | sb_add(m, "TRUNCATED"); | ||
88 | return; | ||
89 | } | ||
90 | |||
91 | /* Max length: 127 "OPT (" 15*4*2chars ") " */ | ||
92 | sb_add(m, "OPT ("); | ||
93 | for (i = 0; i < optsize; i++) | ||
94 | sb_add(m, "%02X", op[i]); | ||
95 | sb_add(m, ") "); | ||
96 | } | ||
97 | |||
98 | switch (ih->protocol) { | ||
99 | case IPPROTO_TCP: { | ||
100 | struct tcphdr _tcph; | ||
101 | const struct tcphdr *th; | ||
102 | |||
103 | /* Max length: 10 "PROTO=TCP " */ | ||
104 | sb_add(m, "PROTO=TCP "); | ||
105 | |||
106 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
107 | break; | ||
108 | |||
109 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
110 | th = skb_header_pointer(skb, iphoff + ih->ihl * 4, | ||
111 | sizeof(_tcph), &_tcph); | ||
112 | if (th == NULL) { | ||
113 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
114 | skb->len - iphoff - ih->ihl*4); | ||
115 | break; | ||
116 | } | ||
117 | |||
118 | /* Max length: 20 "SPT=65535 DPT=65535 " */ | ||
119 | sb_add(m, "SPT=%u DPT=%u ", | ||
120 | ntohs(th->source), ntohs(th->dest)); | ||
121 | /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ | ||
122 | if (logflags & IPT_LOG_TCPSEQ) | ||
123 | sb_add(m, "SEQ=%u ACK=%u ", | ||
124 | ntohl(th->seq), ntohl(th->ack_seq)); | ||
125 | /* Max length: 13 "WINDOW=65535 " */ | ||
126 | sb_add(m, "WINDOW=%u ", ntohs(th->window)); | ||
127 | /* Max length: 9 "RES=0x3F " */ | ||
128 | sb_add(m, "RES=0x%02x ", (u8)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22)); | ||
129 | /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ | ||
130 | if (th->cwr) | ||
131 | sb_add(m, "CWR "); | ||
132 | if (th->ece) | ||
133 | sb_add(m, "ECE "); | ||
134 | if (th->urg) | ||
135 | sb_add(m, "URG "); | ||
136 | if (th->ack) | ||
137 | sb_add(m, "ACK "); | ||
138 | if (th->psh) | ||
139 | sb_add(m, "PSH "); | ||
140 | if (th->rst) | ||
141 | sb_add(m, "RST "); | ||
142 | if (th->syn) | ||
143 | sb_add(m, "SYN "); | ||
144 | if (th->fin) | ||
145 | sb_add(m, "FIN "); | ||
146 | /* Max length: 11 "URGP=65535 " */ | ||
147 | sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); | ||
148 | |||
149 | if ((logflags & IPT_LOG_TCPOPT) && | ||
150 | th->doff * 4 > sizeof(struct tcphdr)) { | ||
151 | unsigned char _opt[4 * 15 - sizeof(struct tcphdr)]; | ||
152 | const unsigned char *op; | ||
153 | unsigned int i, optsize; | ||
154 | |||
155 | optsize = th->doff * 4 - sizeof(struct tcphdr); | ||
156 | op = skb_header_pointer(skb, | ||
157 | iphoff+ih->ihl*4+sizeof(_tcph), | ||
158 | optsize, _opt); | ||
159 | if (op == NULL) { | ||
160 | sb_add(m, "TRUNCATED"); | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | /* Max length: 127 "OPT (" 15*4*2chars ") " */ | ||
165 | sb_add(m, "OPT ("); | ||
166 | for (i = 0; i < optsize; i++) | ||
167 | sb_add(m, "%02X", op[i]); | ||
168 | sb_add(m, ") "); | ||
169 | } | ||
170 | break; | ||
171 | } | ||
172 | case IPPROTO_UDP: | ||
173 | case IPPROTO_UDPLITE: { | ||
174 | struct udphdr _udph; | ||
175 | const struct udphdr *uh; | ||
176 | |||
177 | if (ih->protocol == IPPROTO_UDP) | ||
178 | /* Max length: 10 "PROTO=UDP " */ | ||
179 | sb_add(m, "PROTO=UDP " ); | ||
180 | else /* Max length: 14 "PROTO=UDPLITE " */ | ||
181 | sb_add(m, "PROTO=UDPLITE "); | ||
182 | |||
183 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
184 | break; | ||
185 | |||
186 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
187 | uh = skb_header_pointer(skb, iphoff+ih->ihl*4, | ||
188 | sizeof(_udph), &_udph); | ||
189 | if (uh == NULL) { | ||
190 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
191 | skb->len - iphoff - ih->ihl*4); | ||
192 | break; | ||
193 | } | ||
194 | |||
195 | /* Max length: 20 "SPT=65535 DPT=65535 " */ | ||
196 | sb_add(m, "SPT=%u DPT=%u LEN=%u ", | ||
197 | ntohs(uh->source), ntohs(uh->dest), | ||
198 | ntohs(uh->len)); | ||
199 | break; | ||
200 | } | ||
201 | case IPPROTO_ICMP: { | ||
202 | struct icmphdr _icmph; | ||
203 | const struct icmphdr *ich; | ||
204 | static const size_t required_len[NR_ICMP_TYPES+1] | ||
205 | = { [ICMP_ECHOREPLY] = 4, | ||
206 | [ICMP_DEST_UNREACH] | ||
207 | = 8 + sizeof(struct iphdr), | ||
208 | [ICMP_SOURCE_QUENCH] | ||
209 | = 8 + sizeof(struct iphdr), | ||
210 | [ICMP_REDIRECT] | ||
211 | = 8 + sizeof(struct iphdr), | ||
212 | [ICMP_ECHO] = 4, | ||
213 | [ICMP_TIME_EXCEEDED] | ||
214 | = 8 + sizeof(struct iphdr), | ||
215 | [ICMP_PARAMETERPROB] | ||
216 | = 8 + sizeof(struct iphdr), | ||
217 | [ICMP_TIMESTAMP] = 20, | ||
218 | [ICMP_TIMESTAMPREPLY] = 20, | ||
219 | [ICMP_ADDRESS] = 12, | ||
220 | [ICMP_ADDRESSREPLY] = 12 }; | ||
221 | |||
222 | /* Max length: 11 "PROTO=ICMP " */ | ||
223 | sb_add(m, "PROTO=ICMP "); | ||
224 | |||
225 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
226 | break; | ||
227 | |||
228 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
229 | ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, | ||
230 | sizeof(_icmph), &_icmph); | ||
231 | if (ich == NULL) { | ||
232 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
233 | skb->len - iphoff - ih->ihl*4); | ||
234 | break; | ||
235 | } | ||
236 | |||
237 | /* Max length: 18 "TYPE=255 CODE=255 " */ | ||
238 | sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); | ||
239 | |||
240 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
241 | if (ich->type <= NR_ICMP_TYPES && | ||
242 | required_len[ich->type] && | ||
243 | skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { | ||
244 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
245 | skb->len - iphoff - ih->ihl*4); | ||
246 | break; | ||
247 | } | ||
248 | |||
249 | switch (ich->type) { | ||
250 | case ICMP_ECHOREPLY: | ||
251 | case ICMP_ECHO: | ||
252 | /* Max length: 19 "ID=65535 SEQ=65535 " */ | ||
253 | sb_add(m, "ID=%u SEQ=%u ", | ||
254 | ntohs(ich->un.echo.id), | ||
255 | ntohs(ich->un.echo.sequence)); | ||
256 | break; | ||
257 | |||
258 | case ICMP_PARAMETERPROB: | ||
259 | /* Max length: 14 "PARAMETER=255 " */ | ||
260 | sb_add(m, "PARAMETER=%u ", | ||
261 | ntohl(ich->un.gateway) >> 24); | ||
262 | break; | ||
263 | case ICMP_REDIRECT: | ||
264 | /* Max length: 24 "GATEWAY=255.255.255.255 " */ | ||
265 | sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); | ||
266 | /* Fall through */ | ||
267 | case ICMP_DEST_UNREACH: | ||
268 | case ICMP_SOURCE_QUENCH: | ||
269 | case ICMP_TIME_EXCEEDED: | ||
270 | /* Max length: 3+maxlen */ | ||
271 | if (!iphoff) { /* Only recurse once. */ | ||
272 | sb_add(m, "["); | ||
273 | dump_packet(m, info, skb, | ||
274 | iphoff + ih->ihl*4+sizeof(_icmph)); | ||
275 | sb_add(m, "] "); | ||
276 | } | ||
277 | |||
278 | /* Max length: 10 "MTU=65535 " */ | ||
279 | if (ich->type == ICMP_DEST_UNREACH && | ||
280 | ich->code == ICMP_FRAG_NEEDED) | ||
281 | sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu)); | ||
282 | } | ||
283 | break; | ||
284 | } | ||
285 | /* Max Length */ | ||
286 | case IPPROTO_AH: { | ||
287 | struct ip_auth_hdr _ahdr; | ||
288 | const struct ip_auth_hdr *ah; | ||
289 | |||
290 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
291 | break; | ||
292 | |||
293 | /* Max length: 9 "PROTO=AH " */ | ||
294 | sb_add(m, "PROTO=AH "); | ||
295 | |||
296 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
297 | ah = skb_header_pointer(skb, iphoff+ih->ihl*4, | ||
298 | sizeof(_ahdr), &_ahdr); | ||
299 | if (ah == NULL) { | ||
300 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
301 | skb->len - iphoff - ih->ihl*4); | ||
302 | break; | ||
303 | } | ||
304 | |||
305 | /* Length: 15 "SPI=0xF1234567 " */ | ||
306 | sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); | ||
307 | break; | ||
308 | } | ||
309 | case IPPROTO_ESP: { | ||
310 | struct ip_esp_hdr _esph; | ||
311 | const struct ip_esp_hdr *eh; | ||
312 | |||
313 | /* Max length: 10 "PROTO=ESP " */ | ||
314 | sb_add(m, "PROTO=ESP "); | ||
315 | |||
316 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
317 | break; | ||
318 | |||
319 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
320 | eh = skb_header_pointer(skb, iphoff+ih->ihl*4, | ||
321 | sizeof(_esph), &_esph); | ||
322 | if (eh == NULL) { | ||
323 | sb_add(m, "INCOMPLETE [%u bytes] ", | ||
324 | skb->len - iphoff - ih->ihl*4); | ||
325 | break; | ||
326 | } | ||
327 | |||
328 | /* Length: 15 "SPI=0xF1234567 " */ | ||
329 | sb_add(m, "SPI=0x%x ", ntohl(eh->spi)); | ||
330 | break; | ||
331 | } | ||
332 | /* Max length: 10 "PROTO 255 " */ | ||
333 | default: | ||
334 | sb_add(m, "PROTO=%u ", ih->protocol); | ||
335 | } | ||
336 | |||
337 | /* Max length: 15 "UID=4294967295 " */ | ||
338 | if ((logflags & IPT_LOG_UID) && !iphoff && skb->sk) { | ||
339 | read_lock_bh(&skb->sk->sk_callback_lock); | ||
340 | if (skb->sk->sk_socket && skb->sk->sk_socket->file) | ||
341 | sb_add(m, "UID=%u GID=%u ", | ||
342 | skb->sk->sk_socket->file->f_cred->fsuid, | ||
343 | skb->sk->sk_socket->file->f_cred->fsgid); | ||
344 | read_unlock_bh(&skb->sk->sk_callback_lock); | ||
345 | } | ||
346 | |||
347 | /* Max length: 16 "MARK=0xFFFFFFFF " */ | ||
348 | if (!iphoff && skb->mark) | ||
349 | sb_add(m, "MARK=0x%x ", skb->mark); | ||
350 | |||
351 | /* Proto Max log string length */ | ||
352 | /* IP: 40+46+6+11+127 = 230 */ | ||
353 | /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ | ||
354 | /* UDP: 10+max(25,20) = 35 */ | ||
355 | /* UDPLITE: 14+max(25,20) = 39 */ | ||
356 | /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ | ||
357 | /* ESP: 10+max(25)+15 = 50 */ | ||
358 | /* AH: 9+max(25)+15 = 49 */ | ||
359 | /* unknown: 10 */ | ||
360 | |||
361 | /* (ICMP allows recursion one level deep) */ | ||
362 | /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ | ||
363 | /* maxlen = 230+ 91 + 230 + 252 = 803 */ | ||
364 | } | ||
365 | |||
366 | static void dump_mac_header(struct sbuff *m, | ||
367 | const struct nf_loginfo *info, | ||
368 | const struct sk_buff *skb) | ||
369 | { | ||
370 | struct net_device *dev = skb->dev; | ||
371 | unsigned int logflags = 0; | ||
372 | |||
373 | if (info->type == NF_LOG_TYPE_LOG) | ||
374 | logflags = info->u.log.logflags; | ||
375 | |||
376 | if (!(logflags & IPT_LOG_MACDECODE)) | ||
377 | goto fallback; | ||
378 | |||
379 | switch (dev->type) { | ||
380 | case ARPHRD_ETHER: | ||
381 | sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", | ||
382 | eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, | ||
383 | ntohs(eth_hdr(skb)->h_proto)); | ||
384 | return; | ||
385 | default: | ||
386 | break; | ||
387 | } | ||
388 | |||
389 | fallback: | ||
390 | sb_add(m, "MAC="); | ||
391 | if (dev->hard_header_len && | ||
392 | skb->mac_header != skb->network_header) { | ||
393 | const unsigned char *p = skb_mac_header(skb); | ||
394 | unsigned int i; | ||
395 | |||
396 | sb_add(m, "%02x", *p++); | ||
397 | for (i = 1; i < dev->hard_header_len; i++, p++) | ||
398 | sb_add(m, ":%02x", *p); | ||
399 | } | ||
400 | sb_add(m, " "); | ||
401 | } | ||
402 | |||
403 | static struct nf_loginfo default_loginfo = { | ||
404 | .type = NF_LOG_TYPE_LOG, | ||
405 | .u = { | ||
406 | .log = { | ||
407 | .level = 5, | ||
408 | .logflags = NF_LOG_MASK, | ||
409 | }, | ||
410 | }, | ||
411 | }; | ||
412 | |||
413 | static void | ||
414 | ipt_log_packet(u_int8_t pf, | ||
415 | unsigned int hooknum, | ||
416 | const struct sk_buff *skb, | ||
417 | const struct net_device *in, | ||
418 | const struct net_device *out, | ||
419 | const struct nf_loginfo *loginfo, | ||
420 | const char *prefix) | ||
421 | { | ||
422 | struct sbuff *m = sb_open(); | ||
423 | |||
424 | if (!loginfo) | ||
425 | loginfo = &default_loginfo; | ||
426 | |||
427 | sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, | ||
428 | prefix, | ||
429 | in ? in->name : "", | ||
430 | out ? out->name : ""); | ||
431 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
432 | if (skb->nf_bridge) { | ||
433 | const struct net_device *physindev; | ||
434 | const struct net_device *physoutdev; | ||
435 | |||
436 | physindev = skb->nf_bridge->physindev; | ||
437 | if (physindev && in != physindev) | ||
438 | sb_add(m, "PHYSIN=%s ", physindev->name); | ||
439 | physoutdev = skb->nf_bridge->physoutdev; | ||
440 | if (physoutdev && out != physoutdev) | ||
441 | sb_add(m, "PHYSOUT=%s ", physoutdev->name); | ||
442 | } | ||
443 | #endif | ||
444 | |||
445 | if (in != NULL) | ||
446 | dump_mac_header(m, loginfo, skb); | ||
447 | |||
448 | dump_packet(m, loginfo, skb, 0); | ||
449 | |||
450 | sb_close(m); | ||
451 | } | ||
452 | |||
453 | static unsigned int | ||
454 | log_tg(struct sk_buff *skb, const struct xt_action_param *par) | ||
455 | { | ||
456 | const struct ipt_log_info *loginfo = par->targinfo; | ||
457 | struct nf_loginfo li; | ||
458 | |||
459 | li.type = NF_LOG_TYPE_LOG; | ||
460 | li.u.log.level = loginfo->level; | ||
461 | li.u.log.logflags = loginfo->logflags; | ||
462 | |||
463 | ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, par->out, &li, | ||
464 | loginfo->prefix); | ||
465 | return XT_CONTINUE; | ||
466 | } | ||
467 | |||
468 | static int log_tg_check(const struct xt_tgchk_param *par) | ||
469 | { | ||
470 | const struct ipt_log_info *loginfo = par->targinfo; | ||
471 | |||
472 | if (loginfo->level >= 8) { | ||
473 | pr_debug("level %u >= 8\n", loginfo->level); | ||
474 | return -EINVAL; | ||
475 | } | ||
476 | if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { | ||
477 | pr_debug("prefix is not null-terminated\n"); | ||
478 | return -EINVAL; | ||
479 | } | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | static struct xt_target log_tg_reg __read_mostly = { | ||
484 | .name = "LOG", | ||
485 | .family = NFPROTO_IPV4, | ||
486 | .target = log_tg, | ||
487 | .targetsize = sizeof(struct ipt_log_info), | ||
488 | .checkentry = log_tg_check, | ||
489 | .me = THIS_MODULE, | ||
490 | }; | ||
491 | |||
492 | static struct nf_logger ipt_log_logger __read_mostly = { | ||
493 | .name = "ipt_LOG", | ||
494 | .logfn = &ipt_log_packet, | ||
495 | .me = THIS_MODULE, | ||
496 | }; | ||
497 | |||
498 | static int __init log_tg_init(void) | ||
499 | { | ||
500 | int ret; | ||
501 | |||
502 | ret = xt_register_target(&log_tg_reg); | ||
503 | if (ret < 0) | ||
504 | return ret; | ||
505 | nf_log_register(NFPROTO_IPV4, &ipt_log_logger); | ||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | static void __exit log_tg_exit(void) | ||
510 | { | ||
511 | nf_log_unregister(&ipt_log_logger); | ||
512 | xt_unregister_target(&log_tg_reg); | ||
513 | } | ||
514 | |||
515 | module_init(log_tg_init); | ||
516 | module_exit(log_tg_exit); | ||
diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c new file mode 100644 index 00000000000..6cdb298f103 --- /dev/null +++ b/net/ipv4/netfilter/ipt_NETMAP.c | |||
@@ -0,0 +1,98 @@ | |||
1 | /* NETMAP - static NAT mapping of IP network addresses (1:1). | ||
2 | * The mapping can be applied to source (POSTROUTING), | ||
3 | * destination (PREROUTING), or both (with separate rules). | ||
4 | */ | ||
5 | |||
6 | /* (C) 2000-2001 Svenning Soerensen <svenning@post5.tele.dk> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
13 | #include <linux/ip.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/netdevice.h> | ||
16 | #include <linux/netfilter.h> | ||
17 | #include <linux/netfilter_ipv4.h> | ||
18 | #include <linux/netfilter/x_tables.h> | ||
19 | #include <net/netfilter/nf_nat_rule.h> | ||
20 | |||
21 | MODULE_LICENSE("GPL"); | ||
22 | MODULE_AUTHOR("Svenning Soerensen <svenning@post5.tele.dk>"); | ||
23 | MODULE_DESCRIPTION("Xtables: 1:1 NAT mapping of IPv4 subnets"); | ||
24 | |||
25 | static int netmap_tg_check(const struct xt_tgchk_param *par) | ||
26 | { | ||
27 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
28 | |||
29 | if (!(mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)) { | ||
30 | pr_debug("bad MAP_IPS.\n"); | ||
31 | return -EINVAL; | ||
32 | } | ||
33 | if (mr->rangesize != 1) { | ||
34 | pr_debug("bad rangesize %u.\n", mr->rangesize); | ||
35 | return -EINVAL; | ||
36 | } | ||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static unsigned int | ||
41 | netmap_tg(struct sk_buff *skb, const struct xt_action_param *par) | ||
42 | { | ||
43 | struct nf_conn *ct; | ||
44 | enum ip_conntrack_info ctinfo; | ||
45 | __be32 new_ip, netmask; | ||
46 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
47 | struct nf_nat_range newrange; | ||
48 | |||
49 | NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || | ||
50 | par->hooknum == NF_INET_POST_ROUTING || | ||
51 | par->hooknum == NF_INET_LOCAL_OUT || | ||
52 | par->hooknum == NF_INET_LOCAL_IN); | ||
53 | ct = nf_ct_get(skb, &ctinfo); | ||
54 | |||
55 | netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); | ||
56 | |||
57 | if (par->hooknum == NF_INET_PRE_ROUTING || | ||
58 | par->hooknum == NF_INET_LOCAL_OUT) | ||
59 | new_ip = ip_hdr(skb)->daddr & ~netmask; | ||
60 | else | ||
61 | new_ip = ip_hdr(skb)->saddr & ~netmask; | ||
62 | new_ip |= mr->range[0].min_ip & netmask; | ||
63 | |||
64 | newrange = ((struct nf_nat_range) | ||
65 | { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, | ||
66 | new_ip, new_ip, | ||
67 | mr->range[0].min, mr->range[0].max }); | ||
68 | |||
69 | /* Hand modified range to generic setup. */ | ||
70 | return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum)); | ||
71 | } | ||
72 | |||
73 | static struct xt_target netmap_tg_reg __read_mostly = { | ||
74 | .name = "NETMAP", | ||
75 | .family = NFPROTO_IPV4, | ||
76 | .target = netmap_tg, | ||
77 | .targetsize = sizeof(struct nf_nat_multi_range_compat), | ||
78 | .table = "nat", | ||
79 | .hooks = (1 << NF_INET_PRE_ROUTING) | | ||
80 | (1 << NF_INET_POST_ROUTING) | | ||
81 | (1 << NF_INET_LOCAL_OUT) | | ||
82 | (1 << NF_INET_LOCAL_IN), | ||
83 | .checkentry = netmap_tg_check, | ||
84 | .me = THIS_MODULE | ||
85 | }; | ||
86 | |||
87 | static int __init netmap_tg_init(void) | ||
88 | { | ||
89 | return xt_register_target(&netmap_tg_reg); | ||
90 | } | ||
91 | |||
92 | static void __exit netmap_tg_exit(void) | ||
93 | { | ||
94 | xt_unregister_target(&netmap_tg_reg); | ||
95 | } | ||
96 | |||
97 | module_init(netmap_tg_init); | ||
98 | module_exit(netmap_tg_exit); | ||
diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c new file mode 100644 index 00000000000..18a0656505a --- /dev/null +++ b/net/ipv4/netfilter/ipt_REDIRECT.c | |||
@@ -0,0 +1,110 @@ | |||
1 | /* Redirect. Simple mapping which alters dst to a local IP address. */ | ||
2 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
3 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/ip.h> | ||
12 | #include <linux/timer.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/netfilter.h> | ||
15 | #include <linux/netdevice.h> | ||
16 | #include <linux/if.h> | ||
17 | #include <linux/inetdevice.h> | ||
18 | #include <net/protocol.h> | ||
19 | #include <net/checksum.h> | ||
20 | #include <linux/netfilter_ipv4.h> | ||
21 | #include <linux/netfilter/x_tables.h> | ||
22 | #include <net/netfilter/nf_nat_rule.h> | ||
23 | |||
24 | MODULE_LICENSE("GPL"); | ||
25 | MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); | ||
26 | MODULE_DESCRIPTION("Xtables: Connection redirection to localhost"); | ||
27 | |||
28 | /* FIXME: Take multiple ranges --RR */ | ||
29 | static int redirect_tg_check(const struct xt_tgchk_param *par) | ||
30 | { | ||
31 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
32 | |||
33 | if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { | ||
34 | pr_debug("bad MAP_IPS.\n"); | ||
35 | return -EINVAL; | ||
36 | } | ||
37 | if (mr->rangesize != 1) { | ||
38 | pr_debug("bad rangesize %u.\n", mr->rangesize); | ||
39 | return -EINVAL; | ||
40 | } | ||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | static unsigned int | ||
45 | redirect_tg(struct sk_buff *skb, const struct xt_action_param *par) | ||
46 | { | ||
47 | struct nf_conn *ct; | ||
48 | enum ip_conntrack_info ctinfo; | ||
49 | __be32 newdst; | ||
50 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
51 | struct nf_nat_range newrange; | ||
52 | |||
53 | NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || | ||
54 | par->hooknum == NF_INET_LOCAL_OUT); | ||
55 | |||
56 | ct = nf_ct_get(skb, &ctinfo); | ||
57 | NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); | ||
58 | |||
59 | /* Local packets: make them go to loopback */ | ||
60 | if (par->hooknum == NF_INET_LOCAL_OUT) | ||
61 | newdst = htonl(0x7F000001); | ||
62 | else { | ||
63 | struct in_device *indev; | ||
64 | struct in_ifaddr *ifa; | ||
65 | |||
66 | newdst = 0; | ||
67 | |||
68 | rcu_read_lock(); | ||
69 | indev = __in_dev_get_rcu(skb->dev); | ||
70 | if (indev && (ifa = indev->ifa_list)) | ||
71 | newdst = ifa->ifa_local; | ||
72 | rcu_read_unlock(); | ||
73 | |||
74 | if (!newdst) | ||
75 | return NF_DROP; | ||
76 | } | ||
77 | |||
78 | /* Transfer from original range. */ | ||
79 | newrange = ((struct nf_nat_range) | ||
80 | { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, | ||
81 | newdst, newdst, | ||
82 | mr->range[0].min, mr->range[0].max }); | ||
83 | |||
84 | /* Hand modified range to generic setup. */ | ||
85 | return nf_nat_setup_info(ct, &newrange, IP_NAT_MANIP_DST); | ||
86 | } | ||
87 | |||
88 | static struct xt_target redirect_tg_reg __read_mostly = { | ||
89 | .name = "REDIRECT", | ||
90 | .family = NFPROTO_IPV4, | ||
91 | .target = redirect_tg, | ||
92 | .targetsize = sizeof(struct nf_nat_multi_range_compat), | ||
93 | .table = "nat", | ||
94 | .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT), | ||
95 | .checkentry = redirect_tg_check, | ||
96 | .me = THIS_MODULE, | ||
97 | }; | ||
98 | |||
99 | static int __init redirect_tg_init(void) | ||
100 | { | ||
101 | return xt_register_target(&redirect_tg_reg); | ||
102 | } | ||
103 | |||
104 | static void __exit redirect_tg_exit(void) | ||
105 | { | ||
106 | xt_unregister_target(&redirect_tg_reg); | ||
107 | } | ||
108 | |||
109 | module_init(redirect_tg_init); | ||
110 | module_exit(redirect_tg_exit); | ||
diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c new file mode 100644 index 00000000000..2b57e52c746 --- /dev/null +++ b/net/ipv4/netfilter/ipt_ecn.c | |||
@@ -0,0 +1,127 @@ | |||
1 | /* IP tables module for matching the value of the IPv4 and TCP ECN bits | ||
2 | * | ||
3 | * (C) 2002 by Harald Welte <laforge@gnumonks.org> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
10 | #include <linux/in.h> | ||
11 | #include <linux/ip.h> | ||
12 | #include <net/ip.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/tcp.h> | ||
16 | |||
17 | #include <linux/netfilter/x_tables.h> | ||
18 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
19 | #include <linux/netfilter_ipv4/ipt_ecn.h> | ||
20 | |||
21 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | ||
22 | MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match for IPv4"); | ||
23 | MODULE_LICENSE("GPL"); | ||
24 | |||
25 | static inline bool match_ip(const struct sk_buff *skb, | ||
26 | const struct ipt_ecn_info *einfo) | ||
27 | { | ||
28 | return ((ip_hdr(skb)->tos & IPT_ECN_IP_MASK) == einfo->ip_ect) ^ | ||
29 | !!(einfo->invert & IPT_ECN_OP_MATCH_IP); | ||
30 | } | ||
31 | |||
32 | static inline bool match_tcp(const struct sk_buff *skb, | ||
33 | const struct ipt_ecn_info *einfo, | ||
34 | bool *hotdrop) | ||
35 | { | ||
36 | struct tcphdr _tcph; | ||
37 | const struct tcphdr *th; | ||
38 | |||
39 | /* In practice, TCP match does this, so can't fail. But let's | ||
40 | * be good citizens. | ||
41 | */ | ||
42 | th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph); | ||
43 | if (th == NULL) { | ||
44 | *hotdrop = false; | ||
45 | return false; | ||
46 | } | ||
47 | |||
48 | if (einfo->operation & IPT_ECN_OP_MATCH_ECE) { | ||
49 | if (einfo->invert & IPT_ECN_OP_MATCH_ECE) { | ||
50 | if (th->ece == 1) | ||
51 | return false; | ||
52 | } else { | ||
53 | if (th->ece == 0) | ||
54 | return false; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | if (einfo->operation & IPT_ECN_OP_MATCH_CWR) { | ||
59 | if (einfo->invert & IPT_ECN_OP_MATCH_CWR) { | ||
60 | if (th->cwr == 1) | ||
61 | return false; | ||
62 | } else { | ||
63 | if (th->cwr == 0) | ||
64 | return false; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | return true; | ||
69 | } | ||
70 | |||
71 | static bool ecn_mt(const struct sk_buff *skb, struct xt_action_param *par) | ||
72 | { | ||
73 | const struct ipt_ecn_info *info = par->matchinfo; | ||
74 | |||
75 | if (info->operation & IPT_ECN_OP_MATCH_IP) | ||
76 | if (!match_ip(skb, info)) | ||
77 | return false; | ||
78 | |||
79 | if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) { | ||
80 | if (!match_tcp(skb, info, &par->hotdrop)) | ||
81 | return false; | ||
82 | } | ||
83 | |||
84 | return true; | ||
85 | } | ||
86 | |||
87 | static int ecn_mt_check(const struct xt_mtchk_param *par) | ||
88 | { | ||
89 | const struct ipt_ecn_info *info = par->matchinfo; | ||
90 | const struct ipt_ip *ip = par->entryinfo; | ||
91 | |||
92 | if (info->operation & IPT_ECN_OP_MATCH_MASK) | ||
93 | return -EINVAL; | ||
94 | |||
95 | if (info->invert & IPT_ECN_OP_MATCH_MASK) | ||
96 | return -EINVAL; | ||
97 | |||
98 | if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR) && | ||
99 | (ip->proto != IPPROTO_TCP || ip->invflags & IPT_INV_PROTO)) { | ||
100 | pr_info("cannot match TCP bits in rule for non-tcp packets\n"); | ||
101 | return -EINVAL; | ||
102 | } | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static struct xt_match ecn_mt_reg __read_mostly = { | ||
108 | .name = "ecn", | ||
109 | .family = NFPROTO_IPV4, | ||
110 | .match = ecn_mt, | ||
111 | .matchsize = sizeof(struct ipt_ecn_info), | ||
112 | .checkentry = ecn_mt_check, | ||
113 | .me = THIS_MODULE, | ||
114 | }; | ||
115 | |||
116 | static int __init ecn_mt_init(void) | ||
117 | { | ||
118 | return xt_register_match(&ecn_mt_reg); | ||
119 | } | ||
120 | |||
121 | static void __exit ecn_mt_exit(void) | ||
122 | { | ||
123 | xt_unregister_match(&ecn_mt_reg); | ||
124 | } | ||
125 | |||
126 | module_init(ecn_mt_init); | ||
127 | module_exit(ecn_mt_exit); | ||
diff --git a/net/ipv4/netfilter/nf_nat_amanda.c b/net/ipv4/netfilter/nf_nat_amanda.c new file mode 100644 index 00000000000..703f366fd23 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_amanda.c | |||
@@ -0,0 +1,85 @@ | |||
1 | /* Amanda extension for TCP NAT alteration. | ||
2 | * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca> | ||
3 | * based on a copy of HW's ip_nat_irc.c as well as other modules | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/skbuff.h> | ||
14 | #include <linux/udp.h> | ||
15 | |||
16 | #include <net/netfilter/nf_nat_helper.h> | ||
17 | #include <net/netfilter/nf_nat_rule.h> | ||
18 | #include <net/netfilter/nf_conntrack_helper.h> | ||
19 | #include <net/netfilter/nf_conntrack_expect.h> | ||
20 | #include <linux/netfilter/nf_conntrack_amanda.h> | ||
21 | |||
22 | MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>"); | ||
23 | MODULE_DESCRIPTION("Amanda NAT helper"); | ||
24 | MODULE_LICENSE("GPL"); | ||
25 | MODULE_ALIAS("ip_nat_amanda"); | ||
26 | |||
27 | static unsigned int help(struct sk_buff *skb, | ||
28 | enum ip_conntrack_info ctinfo, | ||
29 | unsigned int matchoff, | ||
30 | unsigned int matchlen, | ||
31 | struct nf_conntrack_expect *exp) | ||
32 | { | ||
33 | char buffer[sizeof("65535")]; | ||
34 | u_int16_t port; | ||
35 | unsigned int ret; | ||
36 | |||
37 | /* Connection comes from client. */ | ||
38 | exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; | ||
39 | exp->dir = IP_CT_DIR_ORIGINAL; | ||
40 | |||
41 | /* When you see the packet, we need to NAT it the same as the | ||
42 | * this one (ie. same IP: it will be TCP and master is UDP). */ | ||
43 | exp->expectfn = nf_nat_follow_master; | ||
44 | |||
45 | /* Try to get same port: if not, try to change it. */ | ||
46 | for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { | ||
47 | int res; | ||
48 | |||
49 | exp->tuple.dst.u.tcp.port = htons(port); | ||
50 | res = nf_ct_expect_related(exp); | ||
51 | if (res == 0) | ||
52 | break; | ||
53 | else if (res != -EBUSY) { | ||
54 | port = 0; | ||
55 | break; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | if (port == 0) | ||
60 | return NF_DROP; | ||
61 | |||
62 | sprintf(buffer, "%u", port); | ||
63 | ret = nf_nat_mangle_udp_packet(skb, exp->master, ctinfo, | ||
64 | matchoff, matchlen, | ||
65 | buffer, strlen(buffer)); | ||
66 | if (ret != NF_ACCEPT) | ||
67 | nf_ct_unexpect_related(exp); | ||
68 | return ret; | ||
69 | } | ||
70 | |||
71 | static void __exit nf_nat_amanda_fini(void) | ||
72 | { | ||
73 | rcu_assign_pointer(nf_nat_amanda_hook, NULL); | ||
74 | synchronize_rcu(); | ||
75 | } | ||
76 | |||
77 | static int __init nf_nat_amanda_init(void) | ||
78 | { | ||
79 | BUG_ON(nf_nat_amanda_hook != NULL); | ||
80 | rcu_assign_pointer(nf_nat_amanda_hook, help); | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | module_init(nf_nat_amanda_init); | ||
85 | module_exit(nf_nat_amanda_fini); | ||
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c new file mode 100644 index 00000000000..3346de5d94d --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_core.c | |||
@@ -0,0 +1,779 @@ | |||
1 | /* NAT for netfilter; shared with compatibility layer. */ | ||
2 | |||
3 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
4 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/timer.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/gfp.h> | ||
16 | #include <net/checksum.h> | ||
17 | #include <net/icmp.h> | ||
18 | #include <net/ip.h> | ||
19 | #include <net/tcp.h> /* For tcp_prot in getorigdst */ | ||
20 | #include <linux/icmp.h> | ||
21 | #include <linux/udp.h> | ||
22 | #include <linux/jhash.h> | ||
23 | |||
24 | #include <linux/netfilter_ipv4.h> | ||
25 | #include <net/netfilter/nf_conntrack.h> | ||
26 | #include <net/netfilter/nf_conntrack_core.h> | ||
27 | #include <net/netfilter/nf_nat.h> | ||
28 | #include <net/netfilter/nf_nat_protocol.h> | ||
29 | #include <net/netfilter/nf_nat_core.h> | ||
30 | #include <net/netfilter/nf_nat_helper.h> | ||
31 | #include <net/netfilter/nf_conntrack_helper.h> | ||
32 | #include <net/netfilter/nf_conntrack_l3proto.h> | ||
33 | #include <net/netfilter/nf_conntrack_l4proto.h> | ||
34 | #include <net/netfilter/nf_conntrack_zones.h> | ||
35 | |||
36 | static DEFINE_SPINLOCK(nf_nat_lock); | ||
37 | |||
38 | static struct nf_conntrack_l3proto *l3proto __read_mostly; | ||
39 | |||
40 | #define MAX_IP_NAT_PROTO 256 | ||
41 | static const struct nf_nat_protocol __rcu *nf_nat_protos[MAX_IP_NAT_PROTO] | ||
42 | __read_mostly; | ||
43 | |||
44 | static inline const struct nf_nat_protocol * | ||
45 | __nf_nat_proto_find(u_int8_t protonum) | ||
46 | { | ||
47 | return rcu_dereference(nf_nat_protos[protonum]); | ||
48 | } | ||
49 | |||
50 | /* We keep an extra hash for each conntrack, for fast searching. */ | ||
51 | static inline unsigned int | ||
52 | hash_by_src(const struct net *net, u16 zone, | ||
53 | const struct nf_conntrack_tuple *tuple) | ||
54 | { | ||
55 | unsigned int hash; | ||
56 | |||
57 | /* Original src, to ensure we map it consistently if poss. */ | ||
58 | hash = jhash_3words((__force u32)tuple->src.u3.ip, | ||
59 | (__force u32)tuple->src.u.all ^ zone, | ||
60 | tuple->dst.protonum, 0); | ||
61 | return ((u64)hash * net->ipv4.nat_htable_size) >> 32; | ||
62 | } | ||
63 | |||
64 | /* Is this tuple already taken? (not by us) */ | ||
65 | int | ||
66 | nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple, | ||
67 | const struct nf_conn *ignored_conntrack) | ||
68 | { | ||
69 | /* Conntrack tracking doesn't keep track of outgoing tuples; only | ||
70 | incoming ones. NAT means they don't have a fixed mapping, | ||
71 | so we invert the tuple and look for the incoming reply. | ||
72 | |||
73 | We could keep a separate hash if this proves too slow. */ | ||
74 | struct nf_conntrack_tuple reply; | ||
75 | |||
76 | nf_ct_invert_tuplepr(&reply, tuple); | ||
77 | return nf_conntrack_tuple_taken(&reply, ignored_conntrack); | ||
78 | } | ||
79 | EXPORT_SYMBOL(nf_nat_used_tuple); | ||
80 | |||
81 | /* If we source map this tuple so reply looks like reply_tuple, will | ||
82 | * that meet the constraints of range. */ | ||
83 | static int | ||
84 | in_range(const struct nf_conntrack_tuple *tuple, | ||
85 | const struct nf_nat_range *range) | ||
86 | { | ||
87 | const struct nf_nat_protocol *proto; | ||
88 | int ret = 0; | ||
89 | |||
90 | /* If we are supposed to map IPs, then we must be in the | ||
91 | range specified, otherwise let this drag us onto a new src IP. */ | ||
92 | if (range->flags & IP_NAT_RANGE_MAP_IPS) { | ||
93 | if (ntohl(tuple->src.u3.ip) < ntohl(range->min_ip) || | ||
94 | ntohl(tuple->src.u3.ip) > ntohl(range->max_ip)) | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | rcu_read_lock(); | ||
99 | proto = __nf_nat_proto_find(tuple->dst.protonum); | ||
100 | if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) || | ||
101 | proto->in_range(tuple, IP_NAT_MANIP_SRC, | ||
102 | &range->min, &range->max)) | ||
103 | ret = 1; | ||
104 | rcu_read_unlock(); | ||
105 | |||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | static inline int | ||
110 | same_src(const struct nf_conn *ct, | ||
111 | const struct nf_conntrack_tuple *tuple) | ||
112 | { | ||
113 | const struct nf_conntrack_tuple *t; | ||
114 | |||
115 | t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; | ||
116 | return (t->dst.protonum == tuple->dst.protonum && | ||
117 | t->src.u3.ip == tuple->src.u3.ip && | ||
118 | t->src.u.all == tuple->src.u.all); | ||
119 | } | ||
120 | |||
121 | /* Only called for SRC manip */ | ||
122 | static int | ||
123 | find_appropriate_src(struct net *net, u16 zone, | ||
124 | const struct nf_conntrack_tuple *tuple, | ||
125 | struct nf_conntrack_tuple *result, | ||
126 | const struct nf_nat_range *range) | ||
127 | { | ||
128 | unsigned int h = hash_by_src(net, zone, tuple); | ||
129 | const struct nf_conn_nat *nat; | ||
130 | const struct nf_conn *ct; | ||
131 | const struct hlist_node *n; | ||
132 | |||
133 | rcu_read_lock(); | ||
134 | hlist_for_each_entry_rcu(nat, n, &net->ipv4.nat_bysource[h], bysource) { | ||
135 | ct = nat->ct; | ||
136 | if (same_src(ct, tuple) && nf_ct_zone(ct) == zone) { | ||
137 | /* Copy source part from reply tuple. */ | ||
138 | nf_ct_invert_tuplepr(result, | ||
139 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple); | ||
140 | result->dst = tuple->dst; | ||
141 | |||
142 | if (in_range(result, range)) { | ||
143 | rcu_read_unlock(); | ||
144 | return 1; | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | rcu_read_unlock(); | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | /* For [FUTURE] fragmentation handling, we want the least-used | ||
153 | src-ip/dst-ip/proto triple. Fairness doesn't come into it. Thus | ||
154 | if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports | ||
155 | 1-65535, we don't do pro-rata allocation based on ports; we choose | ||
156 | the ip with the lowest src-ip/dst-ip/proto usage. | ||
157 | */ | ||
158 | static void | ||
159 | find_best_ips_proto(u16 zone, struct nf_conntrack_tuple *tuple, | ||
160 | const struct nf_nat_range *range, | ||
161 | const struct nf_conn *ct, | ||
162 | enum nf_nat_manip_type maniptype) | ||
163 | { | ||
164 | __be32 *var_ipp; | ||
165 | /* Host order */ | ||
166 | u_int32_t minip, maxip, j; | ||
167 | |||
168 | /* No IP mapping? Do nothing. */ | ||
169 | if (!(range->flags & IP_NAT_RANGE_MAP_IPS)) | ||
170 | return; | ||
171 | |||
172 | if (maniptype == IP_NAT_MANIP_SRC) | ||
173 | var_ipp = &tuple->src.u3.ip; | ||
174 | else | ||
175 | var_ipp = &tuple->dst.u3.ip; | ||
176 | |||
177 | /* Fast path: only one choice. */ | ||
178 | if (range->min_ip == range->max_ip) { | ||
179 | *var_ipp = range->min_ip; | ||
180 | return; | ||
181 | } | ||
182 | |||
183 | /* Hashing source and destination IPs gives a fairly even | ||
184 | * spread in practice (if there are a small number of IPs | ||
185 | * involved, there usually aren't that many connections | ||
186 | * anyway). The consistency means that servers see the same | ||
187 | * client coming from the same IP (some Internet Banking sites | ||
188 | * like this), even across reboots. */ | ||
189 | minip = ntohl(range->min_ip); | ||
190 | maxip = ntohl(range->max_ip); | ||
191 | j = jhash_2words((__force u32)tuple->src.u3.ip, | ||
192 | range->flags & IP_NAT_RANGE_PERSISTENT ? | ||
193 | 0 : (__force u32)tuple->dst.u3.ip ^ zone, 0); | ||
194 | j = ((u64)j * (maxip - minip + 1)) >> 32; | ||
195 | *var_ipp = htonl(minip + j); | ||
196 | } | ||
197 | |||
198 | /* Manipulate the tuple into the range given. For NF_INET_POST_ROUTING, | ||
199 | * we change the source to map into the range. For NF_INET_PRE_ROUTING | ||
200 | * and NF_INET_LOCAL_OUT, we change the destination to map into the | ||
201 | * range. It might not be possible to get a unique tuple, but we try. | ||
202 | * At worst (or if we race), we will end up with a final duplicate in | ||
203 | * __ip_conntrack_confirm and drop the packet. */ | ||
204 | static void | ||
205 | get_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
206 | const struct nf_conntrack_tuple *orig_tuple, | ||
207 | const struct nf_nat_range *range, | ||
208 | struct nf_conn *ct, | ||
209 | enum nf_nat_manip_type maniptype) | ||
210 | { | ||
211 | struct net *net = nf_ct_net(ct); | ||
212 | const struct nf_nat_protocol *proto; | ||
213 | u16 zone = nf_ct_zone(ct); | ||
214 | |||
215 | /* 1) If this srcip/proto/src-proto-part is currently mapped, | ||
216 | and that same mapping gives a unique tuple within the given | ||
217 | range, use that. | ||
218 | |||
219 | This is only required for source (ie. NAT/masq) mappings. | ||
220 | So far, we don't do local source mappings, so multiple | ||
221 | manips not an issue. */ | ||
222 | if (maniptype == IP_NAT_MANIP_SRC && | ||
223 | !(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) { | ||
224 | /* try the original tuple first */ | ||
225 | if (in_range(orig_tuple, range)) { | ||
226 | if (!nf_nat_used_tuple(orig_tuple, ct)) { | ||
227 | *tuple = *orig_tuple; | ||
228 | return; | ||
229 | } | ||
230 | } else if (find_appropriate_src(net, zone, orig_tuple, tuple, | ||
231 | range)) { | ||
232 | pr_debug("get_unique_tuple: Found current src map\n"); | ||
233 | if (!nf_nat_used_tuple(tuple, ct)) | ||
234 | return; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | /* 2) Select the least-used IP/proto combination in the given | ||
239 | range. */ | ||
240 | *tuple = *orig_tuple; | ||
241 | find_best_ips_proto(zone, tuple, range, ct, maniptype); | ||
242 | |||
243 | /* 3) The per-protocol part of the manip is made to map into | ||
244 | the range to make a unique tuple. */ | ||
245 | |||
246 | rcu_read_lock(); | ||
247 | proto = __nf_nat_proto_find(orig_tuple->dst.protonum); | ||
248 | |||
249 | /* Only bother mapping if it's not already in range and unique */ | ||
250 | if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) { | ||
251 | if (range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { | ||
252 | if (proto->in_range(tuple, maniptype, &range->min, | ||
253 | &range->max) && | ||
254 | (range->min.all == range->max.all || | ||
255 | !nf_nat_used_tuple(tuple, ct))) | ||
256 | goto out; | ||
257 | } else if (!nf_nat_used_tuple(tuple, ct)) { | ||
258 | goto out; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | /* Last change: get protocol to try to obtain unique tuple. */ | ||
263 | proto->unique_tuple(tuple, range, maniptype, ct); | ||
264 | out: | ||
265 | rcu_read_unlock(); | ||
266 | } | ||
267 | |||
268 | unsigned int | ||
269 | nf_nat_setup_info(struct nf_conn *ct, | ||
270 | const struct nf_nat_range *range, | ||
271 | enum nf_nat_manip_type maniptype) | ||
272 | { | ||
273 | struct net *net = nf_ct_net(ct); | ||
274 | struct nf_conntrack_tuple curr_tuple, new_tuple; | ||
275 | struct nf_conn_nat *nat; | ||
276 | |||
277 | /* nat helper or nfctnetlink also setup binding */ | ||
278 | nat = nfct_nat(ct); | ||
279 | if (!nat) { | ||
280 | nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | ||
281 | if (nat == NULL) { | ||
282 | pr_debug("failed to add NAT extension\n"); | ||
283 | return NF_ACCEPT; | ||
284 | } | ||
285 | } | ||
286 | |||
287 | NF_CT_ASSERT(maniptype == IP_NAT_MANIP_SRC || | ||
288 | maniptype == IP_NAT_MANIP_DST); | ||
289 | BUG_ON(nf_nat_initialized(ct, maniptype)); | ||
290 | |||
291 | /* What we've got will look like inverse of reply. Normally | ||
292 | this is what is in the conntrack, except for prior | ||
293 | manipulations (future optimization: if num_manips == 0, | ||
294 | orig_tp = | ||
295 | conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */ | ||
296 | nf_ct_invert_tuplepr(&curr_tuple, | ||
297 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple); | ||
298 | |||
299 | get_unique_tuple(&new_tuple, &curr_tuple, range, ct, maniptype); | ||
300 | |||
301 | if (!nf_ct_tuple_equal(&new_tuple, &curr_tuple)) { | ||
302 | struct nf_conntrack_tuple reply; | ||
303 | |||
304 | /* Alter conntrack table so will recognize replies. */ | ||
305 | nf_ct_invert_tuplepr(&reply, &new_tuple); | ||
306 | nf_conntrack_alter_reply(ct, &reply); | ||
307 | |||
308 | /* Non-atomic: we own this at the moment. */ | ||
309 | if (maniptype == IP_NAT_MANIP_SRC) | ||
310 | ct->status |= IPS_SRC_NAT; | ||
311 | else | ||
312 | ct->status |= IPS_DST_NAT; | ||
313 | } | ||
314 | |||
315 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
316 | unsigned int srchash; | ||
317 | |||
318 | srchash = hash_by_src(net, nf_ct_zone(ct), | ||
319 | &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); | ||
320 | spin_lock_bh(&nf_nat_lock); | ||
321 | /* nf_conntrack_alter_reply might re-allocate exntension aera */ | ||
322 | nat = nfct_nat(ct); | ||
323 | nat->ct = ct; | ||
324 | hlist_add_head_rcu(&nat->bysource, | ||
325 | &net->ipv4.nat_bysource[srchash]); | ||
326 | spin_unlock_bh(&nf_nat_lock); | ||
327 | } | ||
328 | |||
329 | /* It's done. */ | ||
330 | if (maniptype == IP_NAT_MANIP_DST) | ||
331 | ct->status |= IPS_DST_NAT_DONE; | ||
332 | else | ||
333 | ct->status |= IPS_SRC_NAT_DONE; | ||
334 | |||
335 | return NF_ACCEPT; | ||
336 | } | ||
337 | EXPORT_SYMBOL(nf_nat_setup_info); | ||
338 | |||
339 | /* Returns true if succeeded. */ | ||
340 | static bool | ||
341 | manip_pkt(u_int16_t proto, | ||
342 | struct sk_buff *skb, | ||
343 | unsigned int iphdroff, | ||
344 | const struct nf_conntrack_tuple *target, | ||
345 | enum nf_nat_manip_type maniptype) | ||
346 | { | ||
347 | struct iphdr *iph; | ||
348 | const struct nf_nat_protocol *p; | ||
349 | |||
350 | if (!skb_make_writable(skb, iphdroff + sizeof(*iph))) | ||
351 | return false; | ||
352 | |||
353 | iph = (void *)skb->data + iphdroff; | ||
354 | |||
355 | /* Manipulate protcol part. */ | ||
356 | |||
357 | /* rcu_read_lock()ed by nf_hook_slow */ | ||
358 | p = __nf_nat_proto_find(proto); | ||
359 | if (!p->manip_pkt(skb, iphdroff, target, maniptype)) | ||
360 | return false; | ||
361 | |||
362 | iph = (void *)skb->data + iphdroff; | ||
363 | |||
364 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
365 | csum_replace4(&iph->check, iph->saddr, target->src.u3.ip); | ||
366 | iph->saddr = target->src.u3.ip; | ||
367 | } else { | ||
368 | csum_replace4(&iph->check, iph->daddr, target->dst.u3.ip); | ||
369 | iph->daddr = target->dst.u3.ip; | ||
370 | } | ||
371 | return true; | ||
372 | } | ||
373 | |||
374 | /* Do packet manipulations according to nf_nat_setup_info. */ | ||
375 | unsigned int nf_nat_packet(struct nf_conn *ct, | ||
376 | enum ip_conntrack_info ctinfo, | ||
377 | unsigned int hooknum, | ||
378 | struct sk_buff *skb) | ||
379 | { | ||
380 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
381 | unsigned long statusbit; | ||
382 | enum nf_nat_manip_type mtype = HOOK2MANIP(hooknum); | ||
383 | |||
384 | if (mtype == IP_NAT_MANIP_SRC) | ||
385 | statusbit = IPS_SRC_NAT; | ||
386 | else | ||
387 | statusbit = IPS_DST_NAT; | ||
388 | |||
389 | /* Invert if this is reply dir. */ | ||
390 | if (dir == IP_CT_DIR_REPLY) | ||
391 | statusbit ^= IPS_NAT_MASK; | ||
392 | |||
393 | /* Non-atomic: these bits don't change. */ | ||
394 | if (ct->status & statusbit) { | ||
395 | struct nf_conntrack_tuple target; | ||
396 | |||
397 | /* We are aiming to look like inverse of other direction. */ | ||
398 | nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); | ||
399 | |||
400 | if (!manip_pkt(target.dst.protonum, skb, 0, &target, mtype)) | ||
401 | return NF_DROP; | ||
402 | } | ||
403 | return NF_ACCEPT; | ||
404 | } | ||
405 | EXPORT_SYMBOL_GPL(nf_nat_packet); | ||
406 | |||
407 | /* Dir is direction ICMP is coming from (opposite to packet it contains) */ | ||
408 | int nf_nat_icmp_reply_translation(struct nf_conn *ct, | ||
409 | enum ip_conntrack_info ctinfo, | ||
410 | unsigned int hooknum, | ||
411 | struct sk_buff *skb) | ||
412 | { | ||
413 | struct { | ||
414 | struct icmphdr icmp; | ||
415 | struct iphdr ip; | ||
416 | } *inside; | ||
417 | const struct nf_conntrack_l4proto *l4proto; | ||
418 | struct nf_conntrack_tuple inner, target; | ||
419 | int hdrlen = ip_hdrlen(skb); | ||
420 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
421 | unsigned long statusbit; | ||
422 | enum nf_nat_manip_type manip = HOOK2MANIP(hooknum); | ||
423 | |||
424 | if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) | ||
425 | return 0; | ||
426 | |||
427 | inside = (void *)skb->data + hdrlen; | ||
428 | |||
429 | /* We're actually going to mangle it beyond trivial checksum | ||
430 | adjustment, so make sure the current checksum is correct. */ | ||
431 | if (nf_ip_checksum(skb, hooknum, hdrlen, 0)) | ||
432 | return 0; | ||
433 | |||
434 | /* Must be RELATED */ | ||
435 | NF_CT_ASSERT(skb->nfctinfo == IP_CT_RELATED || | ||
436 | skb->nfctinfo == IP_CT_RELATED_REPLY); | ||
437 | |||
438 | /* Redirects on non-null nats must be dropped, else they'll | ||
439 | start talking to each other without our translation, and be | ||
440 | confused... --RR */ | ||
441 | if (inside->icmp.type == ICMP_REDIRECT) { | ||
442 | /* If NAT isn't finished, assume it and drop. */ | ||
443 | if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) | ||
444 | return 0; | ||
445 | |||
446 | if (ct->status & IPS_NAT_MASK) | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | if (manip == IP_NAT_MANIP_SRC) | ||
451 | statusbit = IPS_SRC_NAT; | ||
452 | else | ||
453 | statusbit = IPS_DST_NAT; | ||
454 | |||
455 | /* Invert if this is reply dir. */ | ||
456 | if (dir == IP_CT_DIR_REPLY) | ||
457 | statusbit ^= IPS_NAT_MASK; | ||
458 | |||
459 | if (!(ct->status & statusbit)) | ||
460 | return 1; | ||
461 | |||
462 | pr_debug("icmp_reply_translation: translating error %p manip %u " | ||
463 | "dir %s\n", skb, manip, | ||
464 | dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); | ||
465 | |||
466 | /* rcu_read_lock()ed by nf_hook_slow */ | ||
467 | l4proto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol); | ||
468 | |||
469 | if (!nf_ct_get_tuple(skb, hdrlen + sizeof(struct icmphdr), | ||
470 | (hdrlen + | ||
471 | sizeof(struct icmphdr) + inside->ip.ihl * 4), | ||
472 | (u_int16_t)AF_INET, inside->ip.protocol, | ||
473 | &inner, l3proto, l4proto)) | ||
474 | return 0; | ||
475 | |||
476 | /* Change inner back to look like incoming packet. We do the | ||
477 | opposite manip on this hook to normal, because it might not | ||
478 | pass all hooks (locally-generated ICMP). Consider incoming | ||
479 | packet: PREROUTING (DST manip), routing produces ICMP, goes | ||
480 | through POSTROUTING (which must correct the DST manip). */ | ||
481 | if (!manip_pkt(inside->ip.protocol, skb, hdrlen + sizeof(inside->icmp), | ||
482 | &ct->tuplehash[!dir].tuple, !manip)) | ||
483 | return 0; | ||
484 | |||
485 | if (skb->ip_summed != CHECKSUM_PARTIAL) { | ||
486 | /* Reloading "inside" here since manip_pkt inner. */ | ||
487 | inside = (void *)skb->data + hdrlen; | ||
488 | inside->icmp.checksum = 0; | ||
489 | inside->icmp.checksum = | ||
490 | csum_fold(skb_checksum(skb, hdrlen, | ||
491 | skb->len - hdrlen, 0)); | ||
492 | } | ||
493 | |||
494 | /* Change outer to look the reply to an incoming packet | ||
495 | * (proto 0 means don't invert per-proto part). */ | ||
496 | nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); | ||
497 | if (!manip_pkt(0, skb, 0, &target, manip)) | ||
498 | return 0; | ||
499 | |||
500 | return 1; | ||
501 | } | ||
502 | EXPORT_SYMBOL_GPL(nf_nat_icmp_reply_translation); | ||
503 | |||
504 | /* Protocol registration. */ | ||
505 | int nf_nat_protocol_register(const struct nf_nat_protocol *proto) | ||
506 | { | ||
507 | int ret = 0; | ||
508 | |||
509 | spin_lock_bh(&nf_nat_lock); | ||
510 | if (rcu_dereference_protected( | ||
511 | nf_nat_protos[proto->protonum], | ||
512 | lockdep_is_held(&nf_nat_lock) | ||
513 | ) != &nf_nat_unknown_protocol) { | ||
514 | ret = -EBUSY; | ||
515 | goto out; | ||
516 | } | ||
517 | rcu_assign_pointer(nf_nat_protos[proto->protonum], proto); | ||
518 | out: | ||
519 | spin_unlock_bh(&nf_nat_lock); | ||
520 | return ret; | ||
521 | } | ||
522 | EXPORT_SYMBOL(nf_nat_protocol_register); | ||
523 | |||
524 | /* No one stores the protocol anywhere; simply delete it. */ | ||
525 | void nf_nat_protocol_unregister(const struct nf_nat_protocol *proto) | ||
526 | { | ||
527 | spin_lock_bh(&nf_nat_lock); | ||
528 | rcu_assign_pointer(nf_nat_protos[proto->protonum], | ||
529 | &nf_nat_unknown_protocol); | ||
530 | spin_unlock_bh(&nf_nat_lock); | ||
531 | synchronize_rcu(); | ||
532 | } | ||
533 | EXPORT_SYMBOL(nf_nat_protocol_unregister); | ||
534 | |||
535 | /* No one using conntrack by the time this called. */ | ||
536 | static void nf_nat_cleanup_conntrack(struct nf_conn *ct) | ||
537 | { | ||
538 | struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT); | ||
539 | |||
540 | if (nat == NULL || nat->ct == NULL) | ||
541 | return; | ||
542 | |||
543 | NF_CT_ASSERT(nat->ct->status & IPS_SRC_NAT_DONE); | ||
544 | |||
545 | spin_lock_bh(&nf_nat_lock); | ||
546 | hlist_del_rcu(&nat->bysource); | ||
547 | spin_unlock_bh(&nf_nat_lock); | ||
548 | } | ||
549 | |||
550 | static void nf_nat_move_storage(void *new, void *old) | ||
551 | { | ||
552 | struct nf_conn_nat *new_nat = new; | ||
553 | struct nf_conn_nat *old_nat = old; | ||
554 | struct nf_conn *ct = old_nat->ct; | ||
555 | |||
556 | if (!ct || !(ct->status & IPS_SRC_NAT_DONE)) | ||
557 | return; | ||
558 | |||
559 | spin_lock_bh(&nf_nat_lock); | ||
560 | hlist_replace_rcu(&old_nat->bysource, &new_nat->bysource); | ||
561 | spin_unlock_bh(&nf_nat_lock); | ||
562 | } | ||
563 | |||
564 | static struct nf_ct_ext_type nat_extend __read_mostly = { | ||
565 | .len = sizeof(struct nf_conn_nat), | ||
566 | .align = __alignof__(struct nf_conn_nat), | ||
567 | .destroy = nf_nat_cleanup_conntrack, | ||
568 | .move = nf_nat_move_storage, | ||
569 | .id = NF_CT_EXT_NAT, | ||
570 | .flags = NF_CT_EXT_F_PREALLOC, | ||
571 | }; | ||
572 | |||
573 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
574 | |||
575 | #include <linux/netfilter/nfnetlink.h> | ||
576 | #include <linux/netfilter/nfnetlink_conntrack.h> | ||
577 | |||
578 | static const struct nf_nat_protocol * | ||
579 | nf_nat_proto_find_get(u_int8_t protonum) | ||
580 | { | ||
581 | const struct nf_nat_protocol *p; | ||
582 | |||
583 | rcu_read_lock(); | ||
584 | p = __nf_nat_proto_find(protonum); | ||
585 | if (!try_module_get(p->me)) | ||
586 | p = &nf_nat_unknown_protocol; | ||
587 | rcu_read_unlock(); | ||
588 | |||
589 | return p; | ||
590 | } | ||
591 | |||
592 | static void | ||
593 | nf_nat_proto_put(const struct nf_nat_protocol *p) | ||
594 | { | ||
595 | module_put(p->me); | ||
596 | } | ||
597 | |||
598 | static const struct nla_policy protonat_nla_policy[CTA_PROTONAT_MAX+1] = { | ||
599 | [CTA_PROTONAT_PORT_MIN] = { .type = NLA_U16 }, | ||
600 | [CTA_PROTONAT_PORT_MAX] = { .type = NLA_U16 }, | ||
601 | }; | ||
602 | |||
603 | static int nfnetlink_parse_nat_proto(struct nlattr *attr, | ||
604 | const struct nf_conn *ct, | ||
605 | struct nf_nat_range *range) | ||
606 | { | ||
607 | struct nlattr *tb[CTA_PROTONAT_MAX+1]; | ||
608 | const struct nf_nat_protocol *npt; | ||
609 | int err; | ||
610 | |||
611 | err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy); | ||
612 | if (err < 0) | ||
613 | return err; | ||
614 | |||
615 | npt = nf_nat_proto_find_get(nf_ct_protonum(ct)); | ||
616 | if (npt->nlattr_to_range) | ||
617 | err = npt->nlattr_to_range(tb, range); | ||
618 | nf_nat_proto_put(npt); | ||
619 | return err; | ||
620 | } | ||
621 | |||
622 | static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = { | ||
623 | [CTA_NAT_MINIP] = { .type = NLA_U32 }, | ||
624 | [CTA_NAT_MAXIP] = { .type = NLA_U32 }, | ||
625 | }; | ||
626 | |||
627 | static int | ||
628 | nfnetlink_parse_nat(const struct nlattr *nat, | ||
629 | const struct nf_conn *ct, struct nf_nat_range *range) | ||
630 | { | ||
631 | struct nlattr *tb[CTA_NAT_MAX+1]; | ||
632 | int err; | ||
633 | |||
634 | memset(range, 0, sizeof(*range)); | ||
635 | |||
636 | err = nla_parse_nested(tb, CTA_NAT_MAX, nat, nat_nla_policy); | ||
637 | if (err < 0) | ||
638 | return err; | ||
639 | |||
640 | if (tb[CTA_NAT_MINIP]) | ||
641 | range->min_ip = nla_get_be32(tb[CTA_NAT_MINIP]); | ||
642 | |||
643 | if (!tb[CTA_NAT_MAXIP]) | ||
644 | range->max_ip = range->min_ip; | ||
645 | else | ||
646 | range->max_ip = nla_get_be32(tb[CTA_NAT_MAXIP]); | ||
647 | |||
648 | if (range->min_ip) | ||
649 | range->flags |= IP_NAT_RANGE_MAP_IPS; | ||
650 | |||
651 | if (!tb[CTA_NAT_PROTO]) | ||
652 | return 0; | ||
653 | |||
654 | err = nfnetlink_parse_nat_proto(tb[CTA_NAT_PROTO], ct, range); | ||
655 | if (err < 0) | ||
656 | return err; | ||
657 | |||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | static int | ||
662 | nfnetlink_parse_nat_setup(struct nf_conn *ct, | ||
663 | enum nf_nat_manip_type manip, | ||
664 | const struct nlattr *attr) | ||
665 | { | ||
666 | struct nf_nat_range range; | ||
667 | |||
668 | if (nfnetlink_parse_nat(attr, ct, &range) < 0) | ||
669 | return -EINVAL; | ||
670 | if (nf_nat_initialized(ct, manip)) | ||
671 | return -EEXIST; | ||
672 | |||
673 | return nf_nat_setup_info(ct, &range, manip); | ||
674 | } | ||
675 | #else | ||
676 | static int | ||
677 | nfnetlink_parse_nat_setup(struct nf_conn *ct, | ||
678 | enum nf_nat_manip_type manip, | ||
679 | const struct nlattr *attr) | ||
680 | { | ||
681 | return -EOPNOTSUPP; | ||
682 | } | ||
683 | #endif | ||
684 | |||
685 | static int __net_init nf_nat_net_init(struct net *net) | ||
686 | { | ||
687 | /* Leave them the same for the moment. */ | ||
688 | net->ipv4.nat_htable_size = net->ct.htable_size; | ||
689 | net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&net->ipv4.nat_htable_size, 0); | ||
690 | if (!net->ipv4.nat_bysource) | ||
691 | return -ENOMEM; | ||
692 | return 0; | ||
693 | } | ||
694 | |||
695 | /* Clear NAT section of all conntracks, in case we're loaded again. */ | ||
696 | static int clean_nat(struct nf_conn *i, void *data) | ||
697 | { | ||
698 | struct nf_conn_nat *nat = nfct_nat(i); | ||
699 | |||
700 | if (!nat) | ||
701 | return 0; | ||
702 | memset(nat, 0, sizeof(*nat)); | ||
703 | i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK | IPS_SEQ_ADJUST); | ||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | static void __net_exit nf_nat_net_exit(struct net *net) | ||
708 | { | ||
709 | nf_ct_iterate_cleanup(net, &clean_nat, NULL); | ||
710 | synchronize_rcu(); | ||
711 | nf_ct_free_hashtable(net->ipv4.nat_bysource, net->ipv4.nat_htable_size); | ||
712 | } | ||
713 | |||
714 | static struct pernet_operations nf_nat_net_ops = { | ||
715 | .init = nf_nat_net_init, | ||
716 | .exit = nf_nat_net_exit, | ||
717 | }; | ||
718 | |||
719 | static int __init nf_nat_init(void) | ||
720 | { | ||
721 | size_t i; | ||
722 | int ret; | ||
723 | |||
724 | need_ipv4_conntrack(); | ||
725 | |||
726 | ret = nf_ct_extend_register(&nat_extend); | ||
727 | if (ret < 0) { | ||
728 | printk(KERN_ERR "nf_nat_core: Unable to register extension\n"); | ||
729 | return ret; | ||
730 | } | ||
731 | |||
732 | ret = register_pernet_subsys(&nf_nat_net_ops); | ||
733 | if (ret < 0) | ||
734 | goto cleanup_extend; | ||
735 | |||
736 | /* Sew in builtin protocols. */ | ||
737 | spin_lock_bh(&nf_nat_lock); | ||
738 | for (i = 0; i < MAX_IP_NAT_PROTO; i++) | ||
739 | rcu_assign_pointer(nf_nat_protos[i], &nf_nat_unknown_protocol); | ||
740 | rcu_assign_pointer(nf_nat_protos[IPPROTO_TCP], &nf_nat_protocol_tcp); | ||
741 | rcu_assign_pointer(nf_nat_protos[IPPROTO_UDP], &nf_nat_protocol_udp); | ||
742 | rcu_assign_pointer(nf_nat_protos[IPPROTO_ICMP], &nf_nat_protocol_icmp); | ||
743 | spin_unlock_bh(&nf_nat_lock); | ||
744 | |||
745 | /* Initialize fake conntrack so that NAT will skip it */ | ||
746 | nf_ct_untracked_status_or(IPS_NAT_DONE_MASK); | ||
747 | |||
748 | l3proto = nf_ct_l3proto_find_get((u_int16_t)AF_INET); | ||
749 | |||
750 | BUG_ON(nf_nat_seq_adjust_hook != NULL); | ||
751 | rcu_assign_pointer(nf_nat_seq_adjust_hook, nf_nat_seq_adjust); | ||
752 | BUG_ON(nfnetlink_parse_nat_setup_hook != NULL); | ||
753 | rcu_assign_pointer(nfnetlink_parse_nat_setup_hook, | ||
754 | nfnetlink_parse_nat_setup); | ||
755 | BUG_ON(nf_ct_nat_offset != NULL); | ||
756 | rcu_assign_pointer(nf_ct_nat_offset, nf_nat_get_offset); | ||
757 | return 0; | ||
758 | |||
759 | cleanup_extend: | ||
760 | nf_ct_extend_unregister(&nat_extend); | ||
761 | return ret; | ||
762 | } | ||
763 | |||
764 | static void __exit nf_nat_cleanup(void) | ||
765 | { | ||
766 | unregister_pernet_subsys(&nf_nat_net_ops); | ||
767 | nf_ct_l3proto_put(l3proto); | ||
768 | nf_ct_extend_unregister(&nat_extend); | ||
769 | rcu_assign_pointer(nf_nat_seq_adjust_hook, NULL); | ||
770 | rcu_assign_pointer(nfnetlink_parse_nat_setup_hook, NULL); | ||
771 | rcu_assign_pointer(nf_ct_nat_offset, NULL); | ||
772 | synchronize_net(); | ||
773 | } | ||
774 | |||
775 | MODULE_LICENSE("GPL"); | ||
776 | MODULE_ALIAS("nf-nat-ipv4"); | ||
777 | |||
778 | module_init(nf_nat_init); | ||
779 | module_exit(nf_nat_cleanup); | ||
diff --git a/net/ipv4/netfilter/nf_nat_ftp.c b/net/ipv4/netfilter/nf_nat_ftp.c new file mode 100644 index 00000000000..dc73abb3fe2 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_ftp.c | |||
@@ -0,0 +1,137 @@ | |||
1 | /* FTP extension for TCP NAT alteration. */ | ||
2 | |||
3 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
4 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/moduleparam.h> | ||
13 | #include <linux/ip.h> | ||
14 | #include <linux/tcp.h> | ||
15 | #include <linux/netfilter_ipv4.h> | ||
16 | #include <net/netfilter/nf_nat.h> | ||
17 | #include <net/netfilter/nf_nat_helper.h> | ||
18 | #include <net/netfilter/nf_nat_rule.h> | ||
19 | #include <net/netfilter/nf_conntrack_helper.h> | ||
20 | #include <net/netfilter/nf_conntrack_expect.h> | ||
21 | #include <linux/netfilter/nf_conntrack_ftp.h> | ||
22 | |||
23 | MODULE_LICENSE("GPL"); | ||
24 | MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>"); | ||
25 | MODULE_DESCRIPTION("ftp NAT helper"); | ||
26 | MODULE_ALIAS("ip_nat_ftp"); | ||
27 | |||
28 | /* FIXME: Time out? --RR */ | ||
29 | |||
30 | static int nf_nat_ftp_fmt_cmd(enum nf_ct_ftp_type type, | ||
31 | char *buffer, size_t buflen, | ||
32 | __be32 addr, u16 port) | ||
33 | { | ||
34 | switch (type) { | ||
35 | case NF_CT_FTP_PORT: | ||
36 | case NF_CT_FTP_PASV: | ||
37 | return snprintf(buffer, buflen, "%u,%u,%u,%u,%u,%u", | ||
38 | ((unsigned char *)&addr)[0], | ||
39 | ((unsigned char *)&addr)[1], | ||
40 | ((unsigned char *)&addr)[2], | ||
41 | ((unsigned char *)&addr)[3], | ||
42 | port >> 8, | ||
43 | port & 0xFF); | ||
44 | case NF_CT_FTP_EPRT: | ||
45 | return snprintf(buffer, buflen, "|1|%pI4|%u|", &addr, port); | ||
46 | case NF_CT_FTP_EPSV: | ||
47 | return snprintf(buffer, buflen, "|||%u|", port); | ||
48 | } | ||
49 | |||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | /* So, this packet has hit the connection tracking matching code. | ||
54 | Mangle it, and change the expectation to match the new version. */ | ||
55 | static unsigned int nf_nat_ftp(struct sk_buff *skb, | ||
56 | enum ip_conntrack_info ctinfo, | ||
57 | enum nf_ct_ftp_type type, | ||
58 | unsigned int matchoff, | ||
59 | unsigned int matchlen, | ||
60 | struct nf_conntrack_expect *exp) | ||
61 | { | ||
62 | __be32 newip; | ||
63 | u_int16_t port; | ||
64 | int dir = CTINFO2DIR(ctinfo); | ||
65 | struct nf_conn *ct = exp->master; | ||
66 | char buffer[sizeof("|1|255.255.255.255|65535|")]; | ||
67 | unsigned int buflen; | ||
68 | |||
69 | pr_debug("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen); | ||
70 | |||
71 | /* Connection will come from wherever this packet goes, hence !dir */ | ||
72 | newip = ct->tuplehash[!dir].tuple.dst.u3.ip; | ||
73 | exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; | ||
74 | exp->dir = !dir; | ||
75 | |||
76 | /* When you see the packet, we need to NAT it the same as the | ||
77 | * this one. */ | ||
78 | exp->expectfn = nf_nat_follow_master; | ||
79 | |||
80 | /* Try to get same port: if not, try to change it. */ | ||
81 | for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { | ||
82 | int ret; | ||
83 | |||
84 | exp->tuple.dst.u.tcp.port = htons(port); | ||
85 | ret = nf_ct_expect_related(exp); | ||
86 | if (ret == 0) | ||
87 | break; | ||
88 | else if (ret != -EBUSY) { | ||
89 | port = 0; | ||
90 | break; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | if (port == 0) | ||
95 | return NF_DROP; | ||
96 | |||
97 | buflen = nf_nat_ftp_fmt_cmd(type, buffer, sizeof(buffer), newip, port); | ||
98 | if (!buflen) | ||
99 | goto out; | ||
100 | |||
101 | pr_debug("calling nf_nat_mangle_tcp_packet\n"); | ||
102 | |||
103 | if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff, | ||
104 | matchlen, buffer, buflen)) | ||
105 | goto out; | ||
106 | |||
107 | return NF_ACCEPT; | ||
108 | |||
109 | out: | ||
110 | nf_ct_unexpect_related(exp); | ||
111 | return NF_DROP; | ||
112 | } | ||
113 | |||
114 | static void __exit nf_nat_ftp_fini(void) | ||
115 | { | ||
116 | rcu_assign_pointer(nf_nat_ftp_hook, NULL); | ||
117 | synchronize_rcu(); | ||
118 | } | ||
119 | |||
120 | static int __init nf_nat_ftp_init(void) | ||
121 | { | ||
122 | BUG_ON(nf_nat_ftp_hook != NULL); | ||
123 | rcu_assign_pointer(nf_nat_ftp_hook, nf_nat_ftp); | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | /* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ | ||
128 | static int warn_set(const char *val, struct kernel_param *kp) | ||
129 | { | ||
130 | printk(KERN_INFO KBUILD_MODNAME | ||
131 | ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); | ||
132 | return 0; | ||
133 | } | ||
134 | module_param_call(ports, warn_set, NULL, NULL, 0); | ||
135 | |||
136 | module_init(nf_nat_ftp_init); | ||
137 | module_exit(nf_nat_ftp_fini); | ||
diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c new file mode 100644 index 00000000000..ebc5f8894f9 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_helper.c | |||
@@ -0,0 +1,451 @@ | |||
1 | /* ip_nat_helper.c - generic support functions for NAT helpers | ||
2 | * | ||
3 | * (C) 2000-2002 Harald Welte <laforge@netfilter.org> | ||
4 | * (C) 2003-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/gfp.h> | ||
12 | #include <linux/kmod.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/timer.h> | ||
15 | #include <linux/skbuff.h> | ||
16 | #include <linux/tcp.h> | ||
17 | #include <linux/udp.h> | ||
18 | #include <net/checksum.h> | ||
19 | #include <net/tcp.h> | ||
20 | #include <net/route.h> | ||
21 | |||
22 | #include <linux/netfilter_ipv4.h> | ||
23 | #include <net/netfilter/nf_conntrack.h> | ||
24 | #include <net/netfilter/nf_conntrack_helper.h> | ||
25 | #include <net/netfilter/nf_conntrack_ecache.h> | ||
26 | #include <net/netfilter/nf_conntrack_expect.h> | ||
27 | #include <net/netfilter/nf_nat.h> | ||
28 | #include <net/netfilter/nf_nat_protocol.h> | ||
29 | #include <net/netfilter/nf_nat_core.h> | ||
30 | #include <net/netfilter/nf_nat_helper.h> | ||
31 | |||
32 | #define DUMP_OFFSET(x) \ | ||
33 | pr_debug("offset_before=%d, offset_after=%d, correction_pos=%u\n", \ | ||
34 | x->offset_before, x->offset_after, x->correction_pos); | ||
35 | |||
36 | static DEFINE_SPINLOCK(nf_nat_seqofs_lock); | ||
37 | |||
38 | /* Setup TCP sequence correction given this change at this sequence */ | ||
39 | static inline void | ||
40 | adjust_tcp_sequence(u32 seq, | ||
41 | int sizediff, | ||
42 | struct nf_conn *ct, | ||
43 | enum ip_conntrack_info ctinfo) | ||
44 | { | ||
45 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
46 | struct nf_conn_nat *nat = nfct_nat(ct); | ||
47 | struct nf_nat_seq *this_way = &nat->seq[dir]; | ||
48 | |||
49 | pr_debug("adjust_tcp_sequence: seq = %u, sizediff = %d\n", | ||
50 | seq, sizediff); | ||
51 | |||
52 | pr_debug("adjust_tcp_sequence: Seq_offset before: "); | ||
53 | DUMP_OFFSET(this_way); | ||
54 | |||
55 | spin_lock_bh(&nf_nat_seqofs_lock); | ||
56 | |||
57 | /* SYN adjust. If it's uninitialized, or this is after last | ||
58 | * correction, record it: we don't handle more than one | ||
59 | * adjustment in the window, but do deal with common case of a | ||
60 | * retransmit */ | ||
61 | if (this_way->offset_before == this_way->offset_after || | ||
62 | before(this_way->correction_pos, seq)) { | ||
63 | this_way->correction_pos = seq; | ||
64 | this_way->offset_before = this_way->offset_after; | ||
65 | this_way->offset_after += sizediff; | ||
66 | } | ||
67 | spin_unlock_bh(&nf_nat_seqofs_lock); | ||
68 | |||
69 | pr_debug("adjust_tcp_sequence: Seq_offset after: "); | ||
70 | DUMP_OFFSET(this_way); | ||
71 | } | ||
72 | |||
73 | /* Get the offset value, for conntrack */ | ||
74 | s16 nf_nat_get_offset(const struct nf_conn *ct, | ||
75 | enum ip_conntrack_dir dir, | ||
76 | u32 seq) | ||
77 | { | ||
78 | struct nf_conn_nat *nat = nfct_nat(ct); | ||
79 | struct nf_nat_seq *this_way; | ||
80 | s16 offset; | ||
81 | |||
82 | if (!nat) | ||
83 | return 0; | ||
84 | |||
85 | this_way = &nat->seq[dir]; | ||
86 | spin_lock_bh(&nf_nat_seqofs_lock); | ||
87 | offset = after(seq, this_way->correction_pos) | ||
88 | ? this_way->offset_after : this_way->offset_before; | ||
89 | spin_unlock_bh(&nf_nat_seqofs_lock); | ||
90 | |||
91 | return offset; | ||
92 | } | ||
93 | EXPORT_SYMBOL_GPL(nf_nat_get_offset); | ||
94 | |||
95 | /* Frobs data inside this packet, which is linear. */ | ||
96 | static void mangle_contents(struct sk_buff *skb, | ||
97 | unsigned int dataoff, | ||
98 | unsigned int match_offset, | ||
99 | unsigned int match_len, | ||
100 | const char *rep_buffer, | ||
101 | unsigned int rep_len) | ||
102 | { | ||
103 | unsigned char *data; | ||
104 | |||
105 | BUG_ON(skb_is_nonlinear(skb)); | ||
106 | data = skb_network_header(skb) + dataoff; | ||
107 | |||
108 | /* move post-replacement */ | ||
109 | memmove(data + match_offset + rep_len, | ||
110 | data + match_offset + match_len, | ||
111 | skb->tail - (skb->network_header + dataoff + | ||
112 | match_offset + match_len)); | ||
113 | |||
114 | /* insert data from buffer */ | ||
115 | memcpy(data + match_offset, rep_buffer, rep_len); | ||
116 | |||
117 | /* update skb info */ | ||
118 | if (rep_len > match_len) { | ||
119 | pr_debug("nf_nat_mangle_packet: Extending packet by " | ||
120 | "%u from %u bytes\n", rep_len - match_len, skb->len); | ||
121 | skb_put(skb, rep_len - match_len); | ||
122 | } else { | ||
123 | pr_debug("nf_nat_mangle_packet: Shrinking packet from " | ||
124 | "%u from %u bytes\n", match_len - rep_len, skb->len); | ||
125 | __skb_trim(skb, skb->len + rep_len - match_len); | ||
126 | } | ||
127 | |||
128 | /* fix IP hdr checksum information */ | ||
129 | ip_hdr(skb)->tot_len = htons(skb->len); | ||
130 | ip_send_check(ip_hdr(skb)); | ||
131 | } | ||
132 | |||
133 | /* Unusual, but possible case. */ | ||
134 | static int enlarge_skb(struct sk_buff *skb, unsigned int extra) | ||
135 | { | ||
136 | if (skb->len + extra > 65535) | ||
137 | return 0; | ||
138 | |||
139 | if (pskb_expand_head(skb, 0, extra - skb_tailroom(skb), GFP_ATOMIC)) | ||
140 | return 0; | ||
141 | |||
142 | return 1; | ||
143 | } | ||
144 | |||
145 | void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, | ||
146 | __be32 seq, s16 off) | ||
147 | { | ||
148 | if (!off) | ||
149 | return; | ||
150 | set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); | ||
151 | adjust_tcp_sequence(ntohl(seq), off, ct, ctinfo); | ||
152 | nf_conntrack_event_cache(IPCT_NATSEQADJ, ct); | ||
153 | } | ||
154 | EXPORT_SYMBOL_GPL(nf_nat_set_seq_adjust); | ||
155 | |||
156 | static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data, | ||
157 | int datalen, __sum16 *check, int oldlen) | ||
158 | { | ||
159 | struct rtable *rt = skb_rtable(skb); | ||
160 | |||
161 | if (skb->ip_summed != CHECKSUM_PARTIAL) { | ||
162 | if (!(rt->rt_flags & RTCF_LOCAL) && | ||
163 | (!skb->dev || skb->dev->features & NETIF_F_V4_CSUM)) { | ||
164 | skb->ip_summed = CHECKSUM_PARTIAL; | ||
165 | skb->csum_start = skb_headroom(skb) + | ||
166 | skb_network_offset(skb) + | ||
167 | iph->ihl * 4; | ||
168 | skb->csum_offset = (void *)check - data; | ||
169 | *check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, | ||
170 | datalen, iph->protocol, 0); | ||
171 | } else { | ||
172 | *check = 0; | ||
173 | *check = csum_tcpudp_magic(iph->saddr, iph->daddr, | ||
174 | datalen, iph->protocol, | ||
175 | csum_partial(data, datalen, | ||
176 | 0)); | ||
177 | if (iph->protocol == IPPROTO_UDP && !*check) | ||
178 | *check = CSUM_MANGLED_0; | ||
179 | } | ||
180 | } else | ||
181 | inet_proto_csum_replace2(check, skb, | ||
182 | htons(oldlen), htons(datalen), 1); | ||
183 | } | ||
184 | |||
185 | /* Generic function for mangling variable-length address changes inside | ||
186 | * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX | ||
187 | * command in FTP). | ||
188 | * | ||
189 | * Takes care about all the nasty sequence number changes, checksumming, | ||
190 | * skb enlargement, ... | ||
191 | * | ||
192 | * */ | ||
193 | int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, | ||
194 | struct nf_conn *ct, | ||
195 | enum ip_conntrack_info ctinfo, | ||
196 | unsigned int match_offset, | ||
197 | unsigned int match_len, | ||
198 | const char *rep_buffer, | ||
199 | unsigned int rep_len, bool adjust) | ||
200 | { | ||
201 | struct iphdr *iph; | ||
202 | struct tcphdr *tcph; | ||
203 | int oldlen, datalen; | ||
204 | |||
205 | if (!skb_make_writable(skb, skb->len)) | ||
206 | return 0; | ||
207 | |||
208 | if (rep_len > match_len && | ||
209 | rep_len - match_len > skb_tailroom(skb) && | ||
210 | !enlarge_skb(skb, rep_len - match_len)) | ||
211 | return 0; | ||
212 | |||
213 | SKB_LINEAR_ASSERT(skb); | ||
214 | |||
215 | iph = ip_hdr(skb); | ||
216 | tcph = (void *)iph + iph->ihl*4; | ||
217 | |||
218 | oldlen = skb->len - iph->ihl*4; | ||
219 | mangle_contents(skb, iph->ihl*4 + tcph->doff*4, | ||
220 | match_offset, match_len, rep_buffer, rep_len); | ||
221 | |||
222 | datalen = skb->len - iph->ihl*4; | ||
223 | nf_nat_csum(skb, iph, tcph, datalen, &tcph->check, oldlen); | ||
224 | |||
225 | if (adjust && rep_len != match_len) | ||
226 | nf_nat_set_seq_adjust(ct, ctinfo, tcph->seq, | ||
227 | (int)rep_len - (int)match_len); | ||
228 | |||
229 | return 1; | ||
230 | } | ||
231 | EXPORT_SYMBOL(__nf_nat_mangle_tcp_packet); | ||
232 | |||
233 | /* Generic function for mangling variable-length address changes inside | ||
234 | * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX | ||
235 | * command in the Amanda protocol) | ||
236 | * | ||
237 | * Takes care about all the nasty sequence number changes, checksumming, | ||
238 | * skb enlargement, ... | ||
239 | * | ||
240 | * XXX - This function could be merged with nf_nat_mangle_tcp_packet which | ||
241 | * should be fairly easy to do. | ||
242 | */ | ||
243 | int | ||
244 | nf_nat_mangle_udp_packet(struct sk_buff *skb, | ||
245 | struct nf_conn *ct, | ||
246 | enum ip_conntrack_info ctinfo, | ||
247 | unsigned int match_offset, | ||
248 | unsigned int match_len, | ||
249 | const char *rep_buffer, | ||
250 | unsigned int rep_len) | ||
251 | { | ||
252 | struct iphdr *iph; | ||
253 | struct udphdr *udph; | ||
254 | int datalen, oldlen; | ||
255 | |||
256 | /* UDP helpers might accidentally mangle the wrong packet */ | ||
257 | iph = ip_hdr(skb); | ||
258 | if (skb->len < iph->ihl*4 + sizeof(*udph) + | ||
259 | match_offset + match_len) | ||
260 | return 0; | ||
261 | |||
262 | if (!skb_make_writable(skb, skb->len)) | ||
263 | return 0; | ||
264 | |||
265 | if (rep_len > match_len && | ||
266 | rep_len - match_len > skb_tailroom(skb) && | ||
267 | !enlarge_skb(skb, rep_len - match_len)) | ||
268 | return 0; | ||
269 | |||
270 | iph = ip_hdr(skb); | ||
271 | udph = (void *)iph + iph->ihl*4; | ||
272 | |||
273 | oldlen = skb->len - iph->ihl*4; | ||
274 | mangle_contents(skb, iph->ihl*4 + sizeof(*udph), | ||
275 | match_offset, match_len, rep_buffer, rep_len); | ||
276 | |||
277 | /* update the length of the UDP packet */ | ||
278 | datalen = skb->len - iph->ihl*4; | ||
279 | udph->len = htons(datalen); | ||
280 | |||
281 | /* fix udp checksum if udp checksum was previously calculated */ | ||
282 | if (!udph->check && skb->ip_summed != CHECKSUM_PARTIAL) | ||
283 | return 1; | ||
284 | |||
285 | nf_nat_csum(skb, iph, udph, datalen, &udph->check, oldlen); | ||
286 | |||
287 | return 1; | ||
288 | } | ||
289 | EXPORT_SYMBOL(nf_nat_mangle_udp_packet); | ||
290 | |||
291 | /* Adjust one found SACK option including checksum correction */ | ||
292 | static void | ||
293 | sack_adjust(struct sk_buff *skb, | ||
294 | struct tcphdr *tcph, | ||
295 | unsigned int sackoff, | ||
296 | unsigned int sackend, | ||
297 | struct nf_nat_seq *natseq) | ||
298 | { | ||
299 | while (sackoff < sackend) { | ||
300 | struct tcp_sack_block_wire *sack; | ||
301 | __be32 new_start_seq, new_end_seq; | ||
302 | |||
303 | sack = (void *)skb->data + sackoff; | ||
304 | if (after(ntohl(sack->start_seq) - natseq->offset_before, | ||
305 | natseq->correction_pos)) | ||
306 | new_start_seq = htonl(ntohl(sack->start_seq) | ||
307 | - natseq->offset_after); | ||
308 | else | ||
309 | new_start_seq = htonl(ntohl(sack->start_seq) | ||
310 | - natseq->offset_before); | ||
311 | |||
312 | if (after(ntohl(sack->end_seq) - natseq->offset_before, | ||
313 | natseq->correction_pos)) | ||
314 | new_end_seq = htonl(ntohl(sack->end_seq) | ||
315 | - natseq->offset_after); | ||
316 | else | ||
317 | new_end_seq = htonl(ntohl(sack->end_seq) | ||
318 | - natseq->offset_before); | ||
319 | |||
320 | pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", | ||
321 | ntohl(sack->start_seq), new_start_seq, | ||
322 | ntohl(sack->end_seq), new_end_seq); | ||
323 | |||
324 | inet_proto_csum_replace4(&tcph->check, skb, | ||
325 | sack->start_seq, new_start_seq, 0); | ||
326 | inet_proto_csum_replace4(&tcph->check, skb, | ||
327 | sack->end_seq, new_end_seq, 0); | ||
328 | sack->start_seq = new_start_seq; | ||
329 | sack->end_seq = new_end_seq; | ||
330 | sackoff += sizeof(*sack); | ||
331 | } | ||
332 | } | ||
333 | |||
334 | /* TCP SACK sequence number adjustment */ | ||
335 | static inline unsigned int | ||
336 | nf_nat_sack_adjust(struct sk_buff *skb, | ||
337 | struct tcphdr *tcph, | ||
338 | struct nf_conn *ct, | ||
339 | enum ip_conntrack_info ctinfo) | ||
340 | { | ||
341 | unsigned int dir, optoff, optend; | ||
342 | struct nf_conn_nat *nat = nfct_nat(ct); | ||
343 | |||
344 | optoff = ip_hdrlen(skb) + sizeof(struct tcphdr); | ||
345 | optend = ip_hdrlen(skb) + tcph->doff * 4; | ||
346 | |||
347 | if (!skb_make_writable(skb, optend)) | ||
348 | return 0; | ||
349 | |||
350 | dir = CTINFO2DIR(ctinfo); | ||
351 | |||
352 | while (optoff < optend) { | ||
353 | /* Usually: option, length. */ | ||
354 | unsigned char *op = skb->data + optoff; | ||
355 | |||
356 | switch (op[0]) { | ||
357 | case TCPOPT_EOL: | ||
358 | return 1; | ||
359 | case TCPOPT_NOP: | ||
360 | optoff++; | ||
361 | continue; | ||
362 | default: | ||
363 | /* no partial options */ | ||
364 | if (optoff + 1 == optend || | ||
365 | optoff + op[1] > optend || | ||
366 | op[1] < 2) | ||
367 | return 0; | ||
368 | if (op[0] == TCPOPT_SACK && | ||
369 | op[1] >= 2+TCPOLEN_SACK_PERBLOCK && | ||
370 | ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) | ||
371 | sack_adjust(skb, tcph, optoff+2, | ||
372 | optoff+op[1], &nat->seq[!dir]); | ||
373 | optoff += op[1]; | ||
374 | } | ||
375 | } | ||
376 | return 1; | ||
377 | } | ||
378 | |||
379 | /* TCP sequence number adjustment. Returns 1 on success, 0 on failure */ | ||
380 | int | ||
381 | nf_nat_seq_adjust(struct sk_buff *skb, | ||
382 | struct nf_conn *ct, | ||
383 | enum ip_conntrack_info ctinfo) | ||
384 | { | ||
385 | struct tcphdr *tcph; | ||
386 | int dir; | ||
387 | __be32 newseq, newack; | ||
388 | s16 seqoff, ackoff; | ||
389 | struct nf_conn_nat *nat = nfct_nat(ct); | ||
390 | struct nf_nat_seq *this_way, *other_way; | ||
391 | |||
392 | dir = CTINFO2DIR(ctinfo); | ||
393 | |||
394 | this_way = &nat->seq[dir]; | ||
395 | other_way = &nat->seq[!dir]; | ||
396 | |||
397 | if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*tcph))) | ||
398 | return 0; | ||
399 | |||
400 | tcph = (void *)skb->data + ip_hdrlen(skb); | ||
401 | if (after(ntohl(tcph->seq), this_way->correction_pos)) | ||
402 | seqoff = this_way->offset_after; | ||
403 | else | ||
404 | seqoff = this_way->offset_before; | ||
405 | |||
406 | if (after(ntohl(tcph->ack_seq) - other_way->offset_before, | ||
407 | other_way->correction_pos)) | ||
408 | ackoff = other_way->offset_after; | ||
409 | else | ||
410 | ackoff = other_way->offset_before; | ||
411 | |||
412 | newseq = htonl(ntohl(tcph->seq) + seqoff); | ||
413 | newack = htonl(ntohl(tcph->ack_seq) - ackoff); | ||
414 | |||
415 | inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0); | ||
416 | inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0); | ||
417 | |||
418 | pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n", | ||
419 | ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), | ||
420 | ntohl(newack)); | ||
421 | |||
422 | tcph->seq = newseq; | ||
423 | tcph->ack_seq = newack; | ||
424 | |||
425 | return nf_nat_sack_adjust(skb, tcph, ct, ctinfo); | ||
426 | } | ||
427 | |||
428 | /* Setup NAT on this expected conntrack so it follows master. */ | ||
429 | /* If we fail to get a free NAT slot, we'll get dropped on confirm */ | ||
430 | void nf_nat_follow_master(struct nf_conn *ct, | ||
431 | struct nf_conntrack_expect *exp) | ||
432 | { | ||
433 | struct nf_nat_range range; | ||
434 | |||
435 | /* This must be a fresh one. */ | ||
436 | BUG_ON(ct->status & IPS_NAT_DONE_MASK); | ||
437 | |||
438 | /* Change src to where master sends to */ | ||
439 | range.flags = IP_NAT_RANGE_MAP_IPS; | ||
440 | range.min_ip = range.max_ip | ||
441 | = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; | ||
442 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC); | ||
443 | |||
444 | /* For DST manip, map port here to where it's expected. */ | ||
445 | range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); | ||
446 | range.min = range.max = exp->saved_proto; | ||
447 | range.min_ip = range.max_ip | ||
448 | = ct->master->tuplehash[!exp->dir].tuple.src.u3.ip; | ||
449 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST); | ||
450 | } | ||
451 | EXPORT_SYMBOL(nf_nat_follow_master); | ||
diff --git a/net/ipv4/netfilter/nf_nat_irc.c b/net/ipv4/netfilter/nf_nat_irc.c new file mode 100644 index 00000000000..535e1a80235 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_irc.c | |||
@@ -0,0 +1,99 @@ | |||
1 | /* IRC extension for TCP NAT alteration. | ||
2 | * | ||
3 | * (C) 2000-2001 by Harald Welte <laforge@gnumonks.org> | ||
4 | * (C) 2004 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation | ||
5 | * based on a copy of RR's ip_nat_ftp.c | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the License, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/moduleparam.h> | ||
15 | #include <linux/tcp.h> | ||
16 | #include <linux/kernel.h> | ||
17 | |||
18 | #include <net/netfilter/nf_nat.h> | ||
19 | #include <net/netfilter/nf_nat_helper.h> | ||
20 | #include <net/netfilter/nf_nat_rule.h> | ||
21 | #include <net/netfilter/nf_conntrack_helper.h> | ||
22 | #include <net/netfilter/nf_conntrack_expect.h> | ||
23 | #include <linux/netfilter/nf_conntrack_irc.h> | ||
24 | |||
25 | MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); | ||
26 | MODULE_DESCRIPTION("IRC (DCC) NAT helper"); | ||
27 | MODULE_LICENSE("GPL"); | ||
28 | MODULE_ALIAS("ip_nat_irc"); | ||
29 | |||
30 | static unsigned int help(struct sk_buff *skb, | ||
31 | enum ip_conntrack_info ctinfo, | ||
32 | unsigned int matchoff, | ||
33 | unsigned int matchlen, | ||
34 | struct nf_conntrack_expect *exp) | ||
35 | { | ||
36 | char buffer[sizeof("4294967296 65635")]; | ||
37 | u_int32_t ip; | ||
38 | u_int16_t port; | ||
39 | unsigned int ret; | ||
40 | |||
41 | /* Reply comes from server. */ | ||
42 | exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; | ||
43 | exp->dir = IP_CT_DIR_REPLY; | ||
44 | exp->expectfn = nf_nat_follow_master; | ||
45 | |||
46 | /* Try to get same port: if not, try to change it. */ | ||
47 | for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { | ||
48 | int ret; | ||
49 | |||
50 | exp->tuple.dst.u.tcp.port = htons(port); | ||
51 | ret = nf_ct_expect_related(exp); | ||
52 | if (ret == 0) | ||
53 | break; | ||
54 | else if (ret != -EBUSY) { | ||
55 | port = 0; | ||
56 | break; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | if (port == 0) | ||
61 | return NF_DROP; | ||
62 | |||
63 | ip = ntohl(exp->master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip); | ||
64 | sprintf(buffer, "%u %u", ip, port); | ||
65 | pr_debug("nf_nat_irc: inserting '%s' == %pI4, port %u\n", | ||
66 | buffer, &ip, port); | ||
67 | |||
68 | ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo, | ||
69 | matchoff, matchlen, buffer, | ||
70 | strlen(buffer)); | ||
71 | if (ret != NF_ACCEPT) | ||
72 | nf_ct_unexpect_related(exp); | ||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static void __exit nf_nat_irc_fini(void) | ||
77 | { | ||
78 | rcu_assign_pointer(nf_nat_irc_hook, NULL); | ||
79 | synchronize_rcu(); | ||
80 | } | ||
81 | |||
82 | static int __init nf_nat_irc_init(void) | ||
83 | { | ||
84 | BUG_ON(nf_nat_irc_hook != NULL); | ||
85 | rcu_assign_pointer(nf_nat_irc_hook, help); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ | ||
90 | static int warn_set(const char *val, struct kernel_param *kp) | ||
91 | { | ||
92 | printk(KERN_INFO KBUILD_MODNAME | ||
93 | ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); | ||
94 | return 0; | ||
95 | } | ||
96 | module_param_call(ports, warn_set, NULL, NULL, 0); | ||
97 | |||
98 | module_init(nf_nat_irc_init); | ||
99 | module_exit(nf_nat_irc_fini); | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_common.c b/net/ipv4/netfilter/nf_nat_proto_common.c new file mode 100644 index 00000000000..f52d41ea069 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_common.c | |||
@@ -0,0 +1,125 @@ | |||
1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
3 | * (C) 2008 Patrick McHardy <kaber@trash.net> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/random.h> | ||
12 | #include <linux/ip.h> | ||
13 | |||
14 | #include <linux/netfilter.h> | ||
15 | #include <net/secure_seq.h> | ||
16 | #include <net/netfilter/nf_nat.h> | ||
17 | #include <net/netfilter/nf_nat_core.h> | ||
18 | #include <net/netfilter/nf_nat_rule.h> | ||
19 | #include <net/netfilter/nf_nat_protocol.h> | ||
20 | |||
21 | bool nf_nat_proto_in_range(const struct nf_conntrack_tuple *tuple, | ||
22 | enum nf_nat_manip_type maniptype, | ||
23 | const union nf_conntrack_man_proto *min, | ||
24 | const union nf_conntrack_man_proto *max) | ||
25 | { | ||
26 | __be16 port; | ||
27 | |||
28 | if (maniptype == IP_NAT_MANIP_SRC) | ||
29 | port = tuple->src.u.all; | ||
30 | else | ||
31 | port = tuple->dst.u.all; | ||
32 | |||
33 | return ntohs(port) >= ntohs(min->all) && | ||
34 | ntohs(port) <= ntohs(max->all); | ||
35 | } | ||
36 | EXPORT_SYMBOL_GPL(nf_nat_proto_in_range); | ||
37 | |||
38 | void nf_nat_proto_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
39 | const struct nf_nat_range *range, | ||
40 | enum nf_nat_manip_type maniptype, | ||
41 | const struct nf_conn *ct, | ||
42 | u_int16_t *rover) | ||
43 | { | ||
44 | unsigned int range_size, min, i; | ||
45 | __be16 *portptr; | ||
46 | u_int16_t off; | ||
47 | |||
48 | if (maniptype == IP_NAT_MANIP_SRC) | ||
49 | portptr = &tuple->src.u.all; | ||
50 | else | ||
51 | portptr = &tuple->dst.u.all; | ||
52 | |||
53 | /* If no range specified... */ | ||
54 | if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { | ||
55 | /* If it's dst rewrite, can't change port */ | ||
56 | if (maniptype == IP_NAT_MANIP_DST) | ||
57 | return; | ||
58 | |||
59 | if (ntohs(*portptr) < 1024) { | ||
60 | /* Loose convention: >> 512 is credential passing */ | ||
61 | if (ntohs(*portptr) < 512) { | ||
62 | min = 1; | ||
63 | range_size = 511 - min + 1; | ||
64 | } else { | ||
65 | min = 600; | ||
66 | range_size = 1023 - min + 1; | ||
67 | } | ||
68 | } else { | ||
69 | min = 1024; | ||
70 | range_size = 65535 - 1024 + 1; | ||
71 | } | ||
72 | } else { | ||
73 | min = ntohs(range->min.all); | ||
74 | range_size = ntohs(range->max.all) - min + 1; | ||
75 | } | ||
76 | |||
77 | if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) | ||
78 | off = secure_ipv4_port_ephemeral(tuple->src.u3.ip, tuple->dst.u3.ip, | ||
79 | maniptype == IP_NAT_MANIP_SRC | ||
80 | ? tuple->dst.u.all | ||
81 | : tuple->src.u.all); | ||
82 | else | ||
83 | off = *rover; | ||
84 | |||
85 | for (i = 0; ; ++off) { | ||
86 | *portptr = htons(min + off % range_size); | ||
87 | if (++i != range_size && nf_nat_used_tuple(tuple, ct)) | ||
88 | continue; | ||
89 | if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) | ||
90 | *rover = off; | ||
91 | return; | ||
92 | } | ||
93 | return; | ||
94 | } | ||
95 | EXPORT_SYMBOL_GPL(nf_nat_proto_unique_tuple); | ||
96 | |||
97 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
98 | int nf_nat_proto_range_to_nlattr(struct sk_buff *skb, | ||
99 | const struct nf_nat_range *range) | ||
100 | { | ||
101 | NLA_PUT_BE16(skb, CTA_PROTONAT_PORT_MIN, range->min.all); | ||
102 | NLA_PUT_BE16(skb, CTA_PROTONAT_PORT_MAX, range->max.all); | ||
103 | return 0; | ||
104 | |||
105 | nla_put_failure: | ||
106 | return -1; | ||
107 | } | ||
108 | EXPORT_SYMBOL_GPL(nf_nat_proto_nlattr_to_range); | ||
109 | |||
110 | int nf_nat_proto_nlattr_to_range(struct nlattr *tb[], | ||
111 | struct nf_nat_range *range) | ||
112 | { | ||
113 | if (tb[CTA_PROTONAT_PORT_MIN]) { | ||
114 | range->min.all = nla_get_be16(tb[CTA_PROTONAT_PORT_MIN]); | ||
115 | range->max.all = range->min.tcp.port; | ||
116 | range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; | ||
117 | } | ||
118 | if (tb[CTA_PROTONAT_PORT_MAX]) { | ||
119 | range->max.all = nla_get_be16(tb[CTA_PROTONAT_PORT_MAX]); | ||
120 | range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; | ||
121 | } | ||
122 | return 0; | ||
123 | } | ||
124 | EXPORT_SYMBOL_GPL(nf_nat_proto_range_to_nlattr); | ||
125 | #endif | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_dccp.c b/net/ipv4/netfilter/nf_nat_proto_dccp.c new file mode 100644 index 00000000000..570faf2667b --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_dccp.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | * DCCP NAT protocol helper | ||
3 | * | ||
4 | * Copyright (c) 2005, 2006. 2008 Patrick McHardy <kaber@trash.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/skbuff.h> | ||
16 | #include <linux/ip.h> | ||
17 | #include <linux/dccp.h> | ||
18 | |||
19 | #include <net/netfilter/nf_conntrack.h> | ||
20 | #include <net/netfilter/nf_nat.h> | ||
21 | #include <net/netfilter/nf_nat_protocol.h> | ||
22 | |||
23 | static u_int16_t dccp_port_rover; | ||
24 | |||
25 | static void | ||
26 | dccp_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
27 | const struct nf_nat_range *range, | ||
28 | enum nf_nat_manip_type maniptype, | ||
29 | const struct nf_conn *ct) | ||
30 | { | ||
31 | nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, | ||
32 | &dccp_port_rover); | ||
33 | } | ||
34 | |||
35 | static bool | ||
36 | dccp_manip_pkt(struct sk_buff *skb, | ||
37 | unsigned int iphdroff, | ||
38 | const struct nf_conntrack_tuple *tuple, | ||
39 | enum nf_nat_manip_type maniptype) | ||
40 | { | ||
41 | const struct iphdr *iph = (const void *)(skb->data + iphdroff); | ||
42 | struct dccp_hdr *hdr; | ||
43 | unsigned int hdroff = iphdroff + iph->ihl * 4; | ||
44 | __be32 oldip, newip; | ||
45 | __be16 *portptr, oldport, newport; | ||
46 | int hdrsize = 8; /* DCCP connection tracking guarantees this much */ | ||
47 | |||
48 | if (skb->len >= hdroff + sizeof(struct dccp_hdr)) | ||
49 | hdrsize = sizeof(struct dccp_hdr); | ||
50 | |||
51 | if (!skb_make_writable(skb, hdroff + hdrsize)) | ||
52 | return false; | ||
53 | |||
54 | iph = (struct iphdr *)(skb->data + iphdroff); | ||
55 | hdr = (struct dccp_hdr *)(skb->data + hdroff); | ||
56 | |||
57 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
58 | oldip = iph->saddr; | ||
59 | newip = tuple->src.u3.ip; | ||
60 | newport = tuple->src.u.dccp.port; | ||
61 | portptr = &hdr->dccph_sport; | ||
62 | } else { | ||
63 | oldip = iph->daddr; | ||
64 | newip = tuple->dst.u3.ip; | ||
65 | newport = tuple->dst.u.dccp.port; | ||
66 | portptr = &hdr->dccph_dport; | ||
67 | } | ||
68 | |||
69 | oldport = *portptr; | ||
70 | *portptr = newport; | ||
71 | |||
72 | if (hdrsize < sizeof(*hdr)) | ||
73 | return true; | ||
74 | |||
75 | inet_proto_csum_replace4(&hdr->dccph_checksum, skb, oldip, newip, 1); | ||
76 | inet_proto_csum_replace2(&hdr->dccph_checksum, skb, oldport, newport, | ||
77 | 0); | ||
78 | return true; | ||
79 | } | ||
80 | |||
81 | static const struct nf_nat_protocol nf_nat_protocol_dccp = { | ||
82 | .protonum = IPPROTO_DCCP, | ||
83 | .me = THIS_MODULE, | ||
84 | .manip_pkt = dccp_manip_pkt, | ||
85 | .in_range = nf_nat_proto_in_range, | ||
86 | .unique_tuple = dccp_unique_tuple, | ||
87 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
88 | .range_to_nlattr = nf_nat_proto_range_to_nlattr, | ||
89 | .nlattr_to_range = nf_nat_proto_nlattr_to_range, | ||
90 | #endif | ||
91 | }; | ||
92 | |||
93 | static int __init nf_nat_proto_dccp_init(void) | ||
94 | { | ||
95 | return nf_nat_protocol_register(&nf_nat_protocol_dccp); | ||
96 | } | ||
97 | |||
98 | static void __exit nf_nat_proto_dccp_fini(void) | ||
99 | { | ||
100 | nf_nat_protocol_unregister(&nf_nat_protocol_dccp); | ||
101 | } | ||
102 | |||
103 | module_init(nf_nat_proto_dccp_init); | ||
104 | module_exit(nf_nat_proto_dccp_fini); | ||
105 | |||
106 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||
107 | MODULE_DESCRIPTION("DCCP NAT protocol helper"); | ||
108 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_sctp.c b/net/ipv4/netfilter/nf_nat_proto_sctp.c new file mode 100644 index 00000000000..756331d4266 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_sctp.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/ip.h> | ||
12 | #include <linux/sctp.h> | ||
13 | #include <net/sctp/checksum.h> | ||
14 | |||
15 | #include <net/netfilter/nf_nat_protocol.h> | ||
16 | |||
17 | static u_int16_t nf_sctp_port_rover; | ||
18 | |||
19 | static void | ||
20 | sctp_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
21 | const struct nf_nat_range *range, | ||
22 | enum nf_nat_manip_type maniptype, | ||
23 | const struct nf_conn *ct) | ||
24 | { | ||
25 | nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, | ||
26 | &nf_sctp_port_rover); | ||
27 | } | ||
28 | |||
29 | static bool | ||
30 | sctp_manip_pkt(struct sk_buff *skb, | ||
31 | unsigned int iphdroff, | ||
32 | const struct nf_conntrack_tuple *tuple, | ||
33 | enum nf_nat_manip_type maniptype) | ||
34 | { | ||
35 | const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); | ||
36 | struct sk_buff *frag; | ||
37 | sctp_sctphdr_t *hdr; | ||
38 | unsigned int hdroff = iphdroff + iph->ihl*4; | ||
39 | __be32 oldip, newip; | ||
40 | __be32 crc32; | ||
41 | |||
42 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | ||
43 | return false; | ||
44 | |||
45 | iph = (struct iphdr *)(skb->data + iphdroff); | ||
46 | hdr = (struct sctphdr *)(skb->data + hdroff); | ||
47 | |||
48 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
49 | /* Get rid of src ip and src pt */ | ||
50 | oldip = iph->saddr; | ||
51 | newip = tuple->src.u3.ip; | ||
52 | hdr->source = tuple->src.u.sctp.port; | ||
53 | } else { | ||
54 | /* Get rid of dst ip and dst pt */ | ||
55 | oldip = iph->daddr; | ||
56 | newip = tuple->dst.u3.ip; | ||
57 | hdr->dest = tuple->dst.u.sctp.port; | ||
58 | } | ||
59 | |||
60 | crc32 = sctp_start_cksum((u8 *)hdr, skb_headlen(skb) - hdroff); | ||
61 | skb_walk_frags(skb, frag) | ||
62 | crc32 = sctp_update_cksum((u8 *)frag->data, skb_headlen(frag), | ||
63 | crc32); | ||
64 | crc32 = sctp_end_cksum(crc32); | ||
65 | hdr->checksum = crc32; | ||
66 | |||
67 | return true; | ||
68 | } | ||
69 | |||
70 | static const struct nf_nat_protocol nf_nat_protocol_sctp = { | ||
71 | .protonum = IPPROTO_SCTP, | ||
72 | .me = THIS_MODULE, | ||
73 | .manip_pkt = sctp_manip_pkt, | ||
74 | .in_range = nf_nat_proto_in_range, | ||
75 | .unique_tuple = sctp_unique_tuple, | ||
76 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
77 | .range_to_nlattr = nf_nat_proto_range_to_nlattr, | ||
78 | .nlattr_to_range = nf_nat_proto_nlattr_to_range, | ||
79 | #endif | ||
80 | }; | ||
81 | |||
82 | static int __init nf_nat_proto_sctp_init(void) | ||
83 | { | ||
84 | return nf_nat_protocol_register(&nf_nat_protocol_sctp); | ||
85 | } | ||
86 | |||
87 | static void __exit nf_nat_proto_sctp_exit(void) | ||
88 | { | ||
89 | nf_nat_protocol_unregister(&nf_nat_protocol_sctp); | ||
90 | } | ||
91 | |||
92 | module_init(nf_nat_proto_sctp_init); | ||
93 | module_exit(nf_nat_proto_sctp_exit); | ||
94 | |||
95 | MODULE_LICENSE("GPL"); | ||
96 | MODULE_DESCRIPTION("SCTP NAT protocol helper"); | ||
97 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_tcp.c b/net/ipv4/netfilter/nf_nat_proto_tcp.c new file mode 100644 index 00000000000..aa460a595d5 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_tcp.c | |||
@@ -0,0 +1,92 @@ | |||
1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/ip.h> | ||
12 | #include <linux/tcp.h> | ||
13 | |||
14 | #include <linux/netfilter.h> | ||
15 | #include <linux/netfilter/nfnetlink_conntrack.h> | ||
16 | #include <net/netfilter/nf_nat.h> | ||
17 | #include <net/netfilter/nf_nat_rule.h> | ||
18 | #include <net/netfilter/nf_nat_protocol.h> | ||
19 | #include <net/netfilter/nf_nat_core.h> | ||
20 | |||
21 | static u_int16_t tcp_port_rover; | ||
22 | |||
23 | static void | ||
24 | tcp_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
25 | const struct nf_nat_range *range, | ||
26 | enum nf_nat_manip_type maniptype, | ||
27 | const struct nf_conn *ct) | ||
28 | { | ||
29 | nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, &tcp_port_rover); | ||
30 | } | ||
31 | |||
32 | static bool | ||
33 | tcp_manip_pkt(struct sk_buff *skb, | ||
34 | unsigned int iphdroff, | ||
35 | const struct nf_conntrack_tuple *tuple, | ||
36 | enum nf_nat_manip_type maniptype) | ||
37 | { | ||
38 | const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); | ||
39 | struct tcphdr *hdr; | ||
40 | unsigned int hdroff = iphdroff + iph->ihl*4; | ||
41 | __be32 oldip, newip; | ||
42 | __be16 *portptr, newport, oldport; | ||
43 | int hdrsize = 8; /* TCP connection tracking guarantees this much */ | ||
44 | |||
45 | /* this could be a inner header returned in icmp packet; in such | ||
46 | cases we cannot update the checksum field since it is outside of | ||
47 | the 8 bytes of transport layer headers we are guaranteed */ | ||
48 | if (skb->len >= hdroff + sizeof(struct tcphdr)) | ||
49 | hdrsize = sizeof(struct tcphdr); | ||
50 | |||
51 | if (!skb_make_writable(skb, hdroff + hdrsize)) | ||
52 | return false; | ||
53 | |||
54 | iph = (struct iphdr *)(skb->data + iphdroff); | ||
55 | hdr = (struct tcphdr *)(skb->data + hdroff); | ||
56 | |||
57 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
58 | /* Get rid of src ip and src pt */ | ||
59 | oldip = iph->saddr; | ||
60 | newip = tuple->src.u3.ip; | ||
61 | newport = tuple->src.u.tcp.port; | ||
62 | portptr = &hdr->source; | ||
63 | } else { | ||
64 | /* Get rid of dst ip and dst pt */ | ||
65 | oldip = iph->daddr; | ||
66 | newip = tuple->dst.u3.ip; | ||
67 | newport = tuple->dst.u.tcp.port; | ||
68 | portptr = &hdr->dest; | ||
69 | } | ||
70 | |||
71 | oldport = *portptr; | ||
72 | *portptr = newport; | ||
73 | |||
74 | if (hdrsize < sizeof(*hdr)) | ||
75 | return true; | ||
76 | |||
77 | inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); | ||
78 | inet_proto_csum_replace2(&hdr->check, skb, oldport, newport, 0); | ||
79 | return true; | ||
80 | } | ||
81 | |||
82 | const struct nf_nat_protocol nf_nat_protocol_tcp = { | ||
83 | .protonum = IPPROTO_TCP, | ||
84 | .me = THIS_MODULE, | ||
85 | .manip_pkt = tcp_manip_pkt, | ||
86 | .in_range = nf_nat_proto_in_range, | ||
87 | .unique_tuple = tcp_unique_tuple, | ||
88 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
89 | .range_to_nlattr = nf_nat_proto_range_to_nlattr, | ||
90 | .nlattr_to_range = nf_nat_proto_nlattr_to_range, | ||
91 | #endif | ||
92 | }; | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_udp.c b/net/ipv4/netfilter/nf_nat_proto_udp.c new file mode 100644 index 00000000000..dfe65c7e292 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_udp.c | |||
@@ -0,0 +1,83 @@ | |||
1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/ip.h> | ||
12 | #include <linux/udp.h> | ||
13 | |||
14 | #include <linux/netfilter.h> | ||
15 | #include <net/netfilter/nf_nat.h> | ||
16 | #include <net/netfilter/nf_nat_core.h> | ||
17 | #include <net/netfilter/nf_nat_rule.h> | ||
18 | #include <net/netfilter/nf_nat_protocol.h> | ||
19 | |||
20 | static u_int16_t udp_port_rover; | ||
21 | |||
22 | static void | ||
23 | udp_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
24 | const struct nf_nat_range *range, | ||
25 | enum nf_nat_manip_type maniptype, | ||
26 | const struct nf_conn *ct) | ||
27 | { | ||
28 | nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, &udp_port_rover); | ||
29 | } | ||
30 | |||
31 | static bool | ||
32 | udp_manip_pkt(struct sk_buff *skb, | ||
33 | unsigned int iphdroff, | ||
34 | const struct nf_conntrack_tuple *tuple, | ||
35 | enum nf_nat_manip_type maniptype) | ||
36 | { | ||
37 | const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); | ||
38 | struct udphdr *hdr; | ||
39 | unsigned int hdroff = iphdroff + iph->ihl*4; | ||
40 | __be32 oldip, newip; | ||
41 | __be16 *portptr, newport; | ||
42 | |||
43 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | ||
44 | return false; | ||
45 | |||
46 | iph = (struct iphdr *)(skb->data + iphdroff); | ||
47 | hdr = (struct udphdr *)(skb->data + hdroff); | ||
48 | |||
49 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
50 | /* Get rid of src ip and src pt */ | ||
51 | oldip = iph->saddr; | ||
52 | newip = tuple->src.u3.ip; | ||
53 | newport = tuple->src.u.udp.port; | ||
54 | portptr = &hdr->source; | ||
55 | } else { | ||
56 | /* Get rid of dst ip and dst pt */ | ||
57 | oldip = iph->daddr; | ||
58 | newip = tuple->dst.u3.ip; | ||
59 | newport = tuple->dst.u.udp.port; | ||
60 | portptr = &hdr->dest; | ||
61 | } | ||
62 | if (hdr->check || skb->ip_summed == CHECKSUM_PARTIAL) { | ||
63 | inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); | ||
64 | inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, | ||
65 | 0); | ||
66 | if (!hdr->check) | ||
67 | hdr->check = CSUM_MANGLED_0; | ||
68 | } | ||
69 | *portptr = newport; | ||
70 | return true; | ||
71 | } | ||
72 | |||
73 | const struct nf_nat_protocol nf_nat_protocol_udp = { | ||
74 | .protonum = IPPROTO_UDP, | ||
75 | .me = THIS_MODULE, | ||
76 | .manip_pkt = udp_manip_pkt, | ||
77 | .in_range = nf_nat_proto_in_range, | ||
78 | .unique_tuple = udp_unique_tuple, | ||
79 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
80 | .range_to_nlattr = nf_nat_proto_range_to_nlattr, | ||
81 | .nlattr_to_range = nf_nat_proto_nlattr_to_range, | ||
82 | #endif | ||
83 | }; | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_udplite.c b/net/ipv4/netfilter/nf_nat_proto_udplite.c new file mode 100644 index 00000000000..3cc8c8af39e --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_udplite.c | |||
@@ -0,0 +1,99 @@ | |||
1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
3 | * (C) 2008 Patrick McHardy <kaber@trash.net> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/ip.h> | ||
13 | #include <linux/udp.h> | ||
14 | |||
15 | #include <linux/netfilter.h> | ||
16 | #include <net/netfilter/nf_nat.h> | ||
17 | #include <net/netfilter/nf_nat_protocol.h> | ||
18 | |||
19 | static u_int16_t udplite_port_rover; | ||
20 | |||
21 | static void | ||
22 | udplite_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
23 | const struct nf_nat_range *range, | ||
24 | enum nf_nat_manip_type maniptype, | ||
25 | const struct nf_conn *ct) | ||
26 | { | ||
27 | nf_nat_proto_unique_tuple(tuple, range, maniptype, ct, | ||
28 | &udplite_port_rover); | ||
29 | } | ||
30 | |||
31 | static bool | ||
32 | udplite_manip_pkt(struct sk_buff *skb, | ||
33 | unsigned int iphdroff, | ||
34 | const struct nf_conntrack_tuple *tuple, | ||
35 | enum nf_nat_manip_type maniptype) | ||
36 | { | ||
37 | const struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); | ||
38 | struct udphdr *hdr; | ||
39 | unsigned int hdroff = iphdroff + iph->ihl*4; | ||
40 | __be32 oldip, newip; | ||
41 | __be16 *portptr, newport; | ||
42 | |||
43 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | ||
44 | return false; | ||
45 | |||
46 | iph = (struct iphdr *)(skb->data + iphdroff); | ||
47 | hdr = (struct udphdr *)(skb->data + hdroff); | ||
48 | |||
49 | if (maniptype == IP_NAT_MANIP_SRC) { | ||
50 | /* Get rid of src ip and src pt */ | ||
51 | oldip = iph->saddr; | ||
52 | newip = tuple->src.u3.ip; | ||
53 | newport = tuple->src.u.udp.port; | ||
54 | portptr = &hdr->source; | ||
55 | } else { | ||
56 | /* Get rid of dst ip and dst pt */ | ||
57 | oldip = iph->daddr; | ||
58 | newip = tuple->dst.u3.ip; | ||
59 | newport = tuple->dst.u.udp.port; | ||
60 | portptr = &hdr->dest; | ||
61 | } | ||
62 | |||
63 | inet_proto_csum_replace4(&hdr->check, skb, oldip, newip, 1); | ||
64 | inet_proto_csum_replace2(&hdr->check, skb, *portptr, newport, 0); | ||
65 | if (!hdr->check) | ||
66 | hdr->check = CSUM_MANGLED_0; | ||
67 | |||
68 | *portptr = newport; | ||
69 | return true; | ||
70 | } | ||
71 | |||
72 | static const struct nf_nat_protocol nf_nat_protocol_udplite = { | ||
73 | .protonum = IPPROTO_UDPLITE, | ||
74 | .me = THIS_MODULE, | ||
75 | .manip_pkt = udplite_manip_pkt, | ||
76 | .in_range = nf_nat_proto_in_range, | ||
77 | .unique_tuple = udplite_unique_tuple, | ||
78 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
79 | .range_to_nlattr = nf_nat_proto_range_to_nlattr, | ||
80 | .nlattr_to_range = nf_nat_proto_nlattr_to_range, | ||
81 | #endif | ||
82 | }; | ||
83 | |||
84 | static int __init nf_nat_proto_udplite_init(void) | ||
85 | { | ||
86 | return nf_nat_protocol_register(&nf_nat_protocol_udplite); | ||
87 | } | ||
88 | |||
89 | static void __exit nf_nat_proto_udplite_fini(void) | ||
90 | { | ||
91 | nf_nat_protocol_unregister(&nf_nat_protocol_udplite); | ||
92 | } | ||
93 | |||
94 | module_init(nf_nat_proto_udplite_init); | ||
95 | module_exit(nf_nat_proto_udplite_fini); | ||
96 | |||
97 | MODULE_LICENSE("GPL"); | ||
98 | MODULE_DESCRIPTION("UDP-Lite NAT protocol helper"); | ||
99 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||
diff --git a/net/ipv4/netfilter/nf_nat_proto_unknown.c b/net/ipv4/netfilter/nf_nat_proto_unknown.c new file mode 100644 index 00000000000..a50f2bc1c73 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_proto_unknown.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* The "unknown" protocol. This is what is used for protocols we | ||
2 | * don't understand. It's returned by ip_ct_find_proto(). | ||
3 | */ | ||
4 | |||
5 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
6 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/types.h> | ||
14 | #include <linux/init.h> | ||
15 | |||
16 | #include <linux/netfilter.h> | ||
17 | #include <net/netfilter/nf_nat.h> | ||
18 | #include <net/netfilter/nf_nat_rule.h> | ||
19 | #include <net/netfilter/nf_nat_protocol.h> | ||
20 | |||
21 | static bool unknown_in_range(const struct nf_conntrack_tuple *tuple, | ||
22 | enum nf_nat_manip_type manip_type, | ||
23 | const union nf_conntrack_man_proto *min, | ||
24 | const union nf_conntrack_man_proto *max) | ||
25 | { | ||
26 | return true; | ||
27 | } | ||
28 | |||
29 | static void unknown_unique_tuple(struct nf_conntrack_tuple *tuple, | ||
30 | const struct nf_nat_range *range, | ||
31 | enum nf_nat_manip_type maniptype, | ||
32 | const struct nf_conn *ct) | ||
33 | { | ||
34 | /* Sorry: we can't help you; if it's not unique, we can't frob | ||
35 | anything. */ | ||
36 | return; | ||
37 | } | ||
38 | |||
39 | static bool | ||
40 | unknown_manip_pkt(struct sk_buff *skb, | ||
41 | unsigned int iphdroff, | ||
42 | const struct nf_conntrack_tuple *tuple, | ||
43 | enum nf_nat_manip_type maniptype) | ||
44 | { | ||
45 | return true; | ||
46 | } | ||
47 | |||
48 | const struct nf_nat_protocol nf_nat_unknown_protocol = { | ||
49 | /* .me isn't set: getting a ref to this cannot fail. */ | ||
50 | .manip_pkt = unknown_manip_pkt, | ||
51 | .in_range = unknown_in_range, | ||
52 | .unique_tuple = unknown_unique_tuple, | ||
53 | }; | ||
diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c new file mode 100644 index 00000000000..733c9abc1cb --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_rule.c | |||
@@ -0,0 +1,214 @@ | |||
1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | /* Everything about the rules for NAT. */ | ||
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
11 | #include <linux/types.h> | ||
12 | #include <linux/ip.h> | ||
13 | #include <linux/netfilter.h> | ||
14 | #include <linux/netfilter_ipv4.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/kmod.h> | ||
17 | #include <linux/skbuff.h> | ||
18 | #include <linux/proc_fs.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <net/checksum.h> | ||
21 | #include <net/route.h> | ||
22 | #include <linux/bitops.h> | ||
23 | |||
24 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
25 | #include <net/netfilter/nf_nat.h> | ||
26 | #include <net/netfilter/nf_nat_core.h> | ||
27 | #include <net/netfilter/nf_nat_rule.h> | ||
28 | |||
29 | #define NAT_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \ | ||
30 | (1 << NF_INET_POST_ROUTING) | \ | ||
31 | (1 << NF_INET_LOCAL_OUT) | \ | ||
32 | (1 << NF_INET_LOCAL_IN)) | ||
33 | |||
34 | static const struct xt_table nat_table = { | ||
35 | .name = "nat", | ||
36 | .valid_hooks = NAT_VALID_HOOKS, | ||
37 | .me = THIS_MODULE, | ||
38 | .af = NFPROTO_IPV4, | ||
39 | }; | ||
40 | |||
41 | /* Source NAT */ | ||
42 | static unsigned int | ||
43 | ipt_snat_target(struct sk_buff *skb, const struct xt_action_param *par) | ||
44 | { | ||
45 | struct nf_conn *ct; | ||
46 | enum ip_conntrack_info ctinfo; | ||
47 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
48 | |||
49 | NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING || | ||
50 | par->hooknum == NF_INET_LOCAL_IN); | ||
51 | |||
52 | ct = nf_ct_get(skb, &ctinfo); | ||
53 | |||
54 | /* Connection must be valid and new. */ | ||
55 | NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || | ||
56 | ctinfo == IP_CT_RELATED_REPLY)); | ||
57 | NF_CT_ASSERT(par->out != NULL); | ||
58 | |||
59 | return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_SRC); | ||
60 | } | ||
61 | |||
62 | static unsigned int | ||
63 | ipt_dnat_target(struct sk_buff *skb, const struct xt_action_param *par) | ||
64 | { | ||
65 | struct nf_conn *ct; | ||
66 | enum ip_conntrack_info ctinfo; | ||
67 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
68 | |||
69 | NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || | ||
70 | par->hooknum == NF_INET_LOCAL_OUT); | ||
71 | |||
72 | ct = nf_ct_get(skb, &ctinfo); | ||
73 | |||
74 | /* Connection must be valid and new. */ | ||
75 | NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); | ||
76 | |||
77 | return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_DST); | ||
78 | } | ||
79 | |||
80 | static int ipt_snat_checkentry(const struct xt_tgchk_param *par) | ||
81 | { | ||
82 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
83 | |||
84 | /* Must be a valid range */ | ||
85 | if (mr->rangesize != 1) { | ||
86 | pr_info("SNAT: multiple ranges no longer supported\n"); | ||
87 | return -EINVAL; | ||
88 | } | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static int ipt_dnat_checkentry(const struct xt_tgchk_param *par) | ||
93 | { | ||
94 | const struct nf_nat_multi_range_compat *mr = par->targinfo; | ||
95 | |||
96 | /* Must be a valid range */ | ||
97 | if (mr->rangesize != 1) { | ||
98 | pr_info("DNAT: multiple ranges no longer supported\n"); | ||
99 | return -EINVAL; | ||
100 | } | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static unsigned int | ||
105 | alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) | ||
106 | { | ||
107 | /* Force range to this IP; let proto decide mapping for | ||
108 | per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). | ||
109 | */ | ||
110 | struct nf_nat_range range; | ||
111 | |||
112 | range.flags = 0; | ||
113 | pr_debug("Allocating NULL binding for %p (%pI4)\n", ct, | ||
114 | HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC ? | ||
115 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip : | ||
116 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip); | ||
117 | |||
118 | return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum)); | ||
119 | } | ||
120 | |||
121 | int nf_nat_rule_find(struct sk_buff *skb, | ||
122 | unsigned int hooknum, | ||
123 | const struct net_device *in, | ||
124 | const struct net_device *out, | ||
125 | struct nf_conn *ct) | ||
126 | { | ||
127 | struct net *net = nf_ct_net(ct); | ||
128 | int ret; | ||
129 | |||
130 | ret = ipt_do_table(skb, hooknum, in, out, net->ipv4.nat_table); | ||
131 | |||
132 | if (ret == NF_ACCEPT) { | ||
133 | if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum))) | ||
134 | /* NUL mapping */ | ||
135 | ret = alloc_null_binding(ct, hooknum); | ||
136 | } | ||
137 | return ret; | ||
138 | } | ||
139 | |||
140 | static struct xt_target ipt_snat_reg __read_mostly = { | ||
141 | .name = "SNAT", | ||
142 | .target = ipt_snat_target, | ||
143 | .targetsize = sizeof(struct nf_nat_multi_range_compat), | ||
144 | .table = "nat", | ||
145 | .hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_LOCAL_IN), | ||
146 | .checkentry = ipt_snat_checkentry, | ||
147 | .family = AF_INET, | ||
148 | }; | ||
149 | |||
150 | static struct xt_target ipt_dnat_reg __read_mostly = { | ||
151 | .name = "DNAT", | ||
152 | .target = ipt_dnat_target, | ||
153 | .targetsize = sizeof(struct nf_nat_multi_range_compat), | ||
154 | .table = "nat", | ||
155 | .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT), | ||
156 | .checkentry = ipt_dnat_checkentry, | ||
157 | .family = AF_INET, | ||
158 | }; | ||
159 | |||
160 | static int __net_init nf_nat_rule_net_init(struct net *net) | ||
161 | { | ||
162 | struct ipt_replace *repl; | ||
163 | |||
164 | repl = ipt_alloc_initial_table(&nat_table); | ||
165 | if (repl == NULL) | ||
166 | return -ENOMEM; | ||
167 | net->ipv4.nat_table = ipt_register_table(net, &nat_table, repl); | ||
168 | kfree(repl); | ||
169 | if (IS_ERR(net->ipv4.nat_table)) | ||
170 | return PTR_ERR(net->ipv4.nat_table); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static void __net_exit nf_nat_rule_net_exit(struct net *net) | ||
175 | { | ||
176 | ipt_unregister_table(net, net->ipv4.nat_table); | ||
177 | } | ||
178 | |||
179 | static struct pernet_operations nf_nat_rule_net_ops = { | ||
180 | .init = nf_nat_rule_net_init, | ||
181 | .exit = nf_nat_rule_net_exit, | ||
182 | }; | ||
183 | |||
184 | int __init nf_nat_rule_init(void) | ||
185 | { | ||
186 | int ret; | ||
187 | |||
188 | ret = register_pernet_subsys(&nf_nat_rule_net_ops); | ||
189 | if (ret != 0) | ||
190 | goto out; | ||
191 | ret = xt_register_target(&ipt_snat_reg); | ||
192 | if (ret != 0) | ||
193 | goto unregister_table; | ||
194 | |||
195 | ret = xt_register_target(&ipt_dnat_reg); | ||
196 | if (ret != 0) | ||
197 | goto unregister_snat; | ||
198 | |||
199 | return ret; | ||
200 | |||
201 | unregister_snat: | ||
202 | xt_unregister_target(&ipt_snat_reg); | ||
203 | unregister_table: | ||
204 | unregister_pernet_subsys(&nf_nat_rule_net_ops); | ||
205 | out: | ||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | void nf_nat_rule_cleanup(void) | ||
210 | { | ||
211 | xt_unregister_target(&ipt_dnat_reg); | ||
212 | xt_unregister_target(&ipt_snat_reg); | ||
213 | unregister_pernet_subsys(&nf_nat_rule_net_ops); | ||
214 | } | ||
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c new file mode 100644 index 00000000000..e40cf7816fd --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_sip.c | |||
@@ -0,0 +1,561 @@ | |||
1 | /* SIP extension for NAT alteration. | ||
2 | * | ||
3 | * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> | ||
4 | * based on RR's ip_nat_ftp.c and other modules. | ||
5 | * (C) 2007 United Security Providers | ||
6 | * (C) 2007, 2008 Patrick McHardy <kaber@trash.net> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/ip.h> | ||
16 | #include <net/ip.h> | ||
17 | #include <linux/udp.h> | ||
18 | #include <linux/tcp.h> | ||
19 | |||
20 | #include <net/netfilter/nf_nat.h> | ||
21 | #include <net/netfilter/nf_nat_helper.h> | ||
22 | #include <net/netfilter/nf_nat_rule.h> | ||
23 | #include <net/netfilter/nf_conntrack_helper.h> | ||
24 | #include <net/netfilter/nf_conntrack_expect.h> | ||
25 | #include <linux/netfilter/nf_conntrack_sip.h> | ||
26 | |||
27 | MODULE_LICENSE("GPL"); | ||
28 | MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); | ||
29 | MODULE_DESCRIPTION("SIP NAT helper"); | ||
30 | MODULE_ALIAS("ip_nat_sip"); | ||
31 | |||
32 | |||
33 | static unsigned int mangle_packet(struct sk_buff *skb, unsigned int dataoff, | ||
34 | const char **dptr, unsigned int *datalen, | ||
35 | unsigned int matchoff, unsigned int matchlen, | ||
36 | const char *buffer, unsigned int buflen) | ||
37 | { | ||
38 | enum ip_conntrack_info ctinfo; | ||
39 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
40 | struct tcphdr *th; | ||
41 | unsigned int baseoff; | ||
42 | |||
43 | if (nf_ct_protonum(ct) == IPPROTO_TCP) { | ||
44 | th = (struct tcphdr *)(skb->data + ip_hdrlen(skb)); | ||
45 | baseoff = ip_hdrlen(skb) + th->doff * 4; | ||
46 | matchoff += dataoff - baseoff; | ||
47 | |||
48 | if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo, | ||
49 | matchoff, matchlen, | ||
50 | buffer, buflen, false)) | ||
51 | return 0; | ||
52 | } else { | ||
53 | baseoff = ip_hdrlen(skb) + sizeof(struct udphdr); | ||
54 | matchoff += dataoff - baseoff; | ||
55 | |||
56 | if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, | ||
57 | matchoff, matchlen, | ||
58 | buffer, buflen)) | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | /* Reload data pointer and adjust datalen value */ | ||
63 | *dptr = skb->data + dataoff; | ||
64 | *datalen += buflen - matchlen; | ||
65 | return 1; | ||
66 | } | ||
67 | |||
68 | static int map_addr(struct sk_buff *skb, unsigned int dataoff, | ||
69 | const char **dptr, unsigned int *datalen, | ||
70 | unsigned int matchoff, unsigned int matchlen, | ||
71 | union nf_inet_addr *addr, __be16 port) | ||
72 | { | ||
73 | enum ip_conntrack_info ctinfo; | ||
74 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
75 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
76 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | ||
77 | unsigned int buflen; | ||
78 | __be32 newaddr; | ||
79 | __be16 newport; | ||
80 | |||
81 | if (ct->tuplehash[dir].tuple.src.u3.ip == addr->ip && | ||
82 | ct->tuplehash[dir].tuple.src.u.udp.port == port) { | ||
83 | newaddr = ct->tuplehash[!dir].tuple.dst.u3.ip; | ||
84 | newport = ct->tuplehash[!dir].tuple.dst.u.udp.port; | ||
85 | } else if (ct->tuplehash[dir].tuple.dst.u3.ip == addr->ip && | ||
86 | ct->tuplehash[dir].tuple.dst.u.udp.port == port) { | ||
87 | newaddr = ct->tuplehash[!dir].tuple.src.u3.ip; | ||
88 | newport = ct->tuplehash[!dir].tuple.src.u.udp.port; | ||
89 | } else | ||
90 | return 1; | ||
91 | |||
92 | if (newaddr == addr->ip && newport == port) | ||
93 | return 1; | ||
94 | |||
95 | buflen = sprintf(buffer, "%pI4:%u", &newaddr, ntohs(newport)); | ||
96 | |||
97 | return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
98 | buffer, buflen); | ||
99 | } | ||
100 | |||
101 | static int map_sip_addr(struct sk_buff *skb, unsigned int dataoff, | ||
102 | const char **dptr, unsigned int *datalen, | ||
103 | enum sip_header_types type) | ||
104 | { | ||
105 | enum ip_conntrack_info ctinfo; | ||
106 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
107 | unsigned int matchlen, matchoff; | ||
108 | union nf_inet_addr addr; | ||
109 | __be16 port; | ||
110 | |||
111 | if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL, | ||
112 | &matchoff, &matchlen, &addr, &port) <= 0) | ||
113 | return 1; | ||
114 | return map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
115 | &addr, port); | ||
116 | } | ||
117 | |||
118 | static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, | ||
119 | const char **dptr, unsigned int *datalen) | ||
120 | { | ||
121 | enum ip_conntrack_info ctinfo; | ||
122 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
123 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
124 | unsigned int coff, matchoff, matchlen; | ||
125 | enum sip_header_types hdr; | ||
126 | union nf_inet_addr addr; | ||
127 | __be16 port; | ||
128 | int request, in_header; | ||
129 | |||
130 | /* Basic rules: requests and responses. */ | ||
131 | if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) { | ||
132 | if (ct_sip_parse_request(ct, *dptr, *datalen, | ||
133 | &matchoff, &matchlen, | ||
134 | &addr, &port) > 0 && | ||
135 | !map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
136 | &addr, port)) | ||
137 | return NF_DROP; | ||
138 | request = 1; | ||
139 | } else | ||
140 | request = 0; | ||
141 | |||
142 | if (nf_ct_protonum(ct) == IPPROTO_TCP) | ||
143 | hdr = SIP_HDR_VIA_TCP; | ||
144 | else | ||
145 | hdr = SIP_HDR_VIA_UDP; | ||
146 | |||
147 | /* Translate topmost Via header and parameters */ | ||
148 | if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, | ||
149 | hdr, NULL, &matchoff, &matchlen, | ||
150 | &addr, &port) > 0) { | ||
151 | unsigned int matchend, poff, plen, buflen, n; | ||
152 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | ||
153 | |||
154 | /* We're only interested in headers related to this | ||
155 | * connection */ | ||
156 | if (request) { | ||
157 | if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip || | ||
158 | port != ct->tuplehash[dir].tuple.src.u.udp.port) | ||
159 | goto next; | ||
160 | } else { | ||
161 | if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip || | ||
162 | port != ct->tuplehash[dir].tuple.dst.u.udp.port) | ||
163 | goto next; | ||
164 | } | ||
165 | |||
166 | if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
167 | &addr, port)) | ||
168 | return NF_DROP; | ||
169 | |||
170 | matchend = matchoff + matchlen; | ||
171 | |||
172 | /* The maddr= parameter (RFC 2361) specifies where to send | ||
173 | * the reply. */ | ||
174 | if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, | ||
175 | "maddr=", &poff, &plen, | ||
176 | &addr) > 0 && | ||
177 | addr.ip == ct->tuplehash[dir].tuple.src.u3.ip && | ||
178 | addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) { | ||
179 | buflen = sprintf(buffer, "%pI4", | ||
180 | &ct->tuplehash[!dir].tuple.dst.u3.ip); | ||
181 | if (!mangle_packet(skb, dataoff, dptr, datalen, | ||
182 | poff, plen, buffer, buflen)) | ||
183 | return NF_DROP; | ||
184 | } | ||
185 | |||
186 | /* The received= parameter (RFC 2361) contains the address | ||
187 | * from which the server received the request. */ | ||
188 | if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, | ||
189 | "received=", &poff, &plen, | ||
190 | &addr) > 0 && | ||
191 | addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip && | ||
192 | addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) { | ||
193 | buflen = sprintf(buffer, "%pI4", | ||
194 | &ct->tuplehash[!dir].tuple.src.u3.ip); | ||
195 | if (!mangle_packet(skb, dataoff, dptr, datalen, | ||
196 | poff, plen, buffer, buflen)) | ||
197 | return NF_DROP; | ||
198 | } | ||
199 | |||
200 | /* The rport= parameter (RFC 3581) contains the port number | ||
201 | * from which the server received the request. */ | ||
202 | if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen, | ||
203 | "rport=", &poff, &plen, | ||
204 | &n) > 0 && | ||
205 | htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && | ||
206 | htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { | ||
207 | __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; | ||
208 | buflen = sprintf(buffer, "%u", ntohs(p)); | ||
209 | if (!mangle_packet(skb, dataoff, dptr, datalen, | ||
210 | poff, plen, buffer, buflen)) | ||
211 | return NF_DROP; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | next: | ||
216 | /* Translate Contact headers */ | ||
217 | coff = 0; | ||
218 | in_header = 0; | ||
219 | while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen, | ||
220 | SIP_HDR_CONTACT, &in_header, | ||
221 | &matchoff, &matchlen, | ||
222 | &addr, &port) > 0) { | ||
223 | if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
224 | &addr, port)) | ||
225 | return NF_DROP; | ||
226 | } | ||
227 | |||
228 | if (!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_FROM) || | ||
229 | !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO)) | ||
230 | return NF_DROP; | ||
231 | |||
232 | return NF_ACCEPT; | ||
233 | } | ||
234 | |||
235 | static void ip_nat_sip_seq_adjust(struct sk_buff *skb, s16 off) | ||
236 | { | ||
237 | enum ip_conntrack_info ctinfo; | ||
238 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
239 | const struct tcphdr *th; | ||
240 | |||
241 | if (nf_ct_protonum(ct) != IPPROTO_TCP || off == 0) | ||
242 | return; | ||
243 | |||
244 | th = (struct tcphdr *)(skb->data + ip_hdrlen(skb)); | ||
245 | nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); | ||
246 | } | ||
247 | |||
248 | /* Handles expected signalling connections and media streams */ | ||
249 | static void ip_nat_sip_expected(struct nf_conn *ct, | ||
250 | struct nf_conntrack_expect *exp) | ||
251 | { | ||
252 | struct nf_nat_range range; | ||
253 | |||
254 | /* This must be a fresh one. */ | ||
255 | BUG_ON(ct->status & IPS_NAT_DONE_MASK); | ||
256 | |||
257 | /* For DST manip, map port here to where it's expected. */ | ||
258 | range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); | ||
259 | range.min = range.max = exp->saved_proto; | ||
260 | range.min_ip = range.max_ip = exp->saved_ip; | ||
261 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST); | ||
262 | |||
263 | /* Change src to where master sends to, but only if the connection | ||
264 | * actually came from the same source. */ | ||
265 | if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == | ||
266 | ct->master->tuplehash[exp->dir].tuple.src.u3.ip) { | ||
267 | range.flags = IP_NAT_RANGE_MAP_IPS; | ||
268 | range.min_ip = range.max_ip | ||
269 | = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; | ||
270 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff, | ||
275 | const char **dptr, unsigned int *datalen, | ||
276 | struct nf_conntrack_expect *exp, | ||
277 | unsigned int matchoff, | ||
278 | unsigned int matchlen) | ||
279 | { | ||
280 | enum ip_conntrack_info ctinfo; | ||
281 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
282 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
283 | __be32 newip; | ||
284 | u_int16_t port; | ||
285 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | ||
286 | unsigned buflen; | ||
287 | |||
288 | /* Connection will come from reply */ | ||
289 | if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip) | ||
290 | newip = exp->tuple.dst.u3.ip; | ||
291 | else | ||
292 | newip = ct->tuplehash[!dir].tuple.dst.u3.ip; | ||
293 | |||
294 | /* If the signalling port matches the connection's source port in the | ||
295 | * original direction, try to use the destination port in the opposite | ||
296 | * direction. */ | ||
297 | if (exp->tuple.dst.u.udp.port == | ||
298 | ct->tuplehash[dir].tuple.src.u.udp.port) | ||
299 | port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); | ||
300 | else | ||
301 | port = ntohs(exp->tuple.dst.u.udp.port); | ||
302 | |||
303 | exp->saved_ip = exp->tuple.dst.u3.ip; | ||
304 | exp->tuple.dst.u3.ip = newip; | ||
305 | exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; | ||
306 | exp->dir = !dir; | ||
307 | exp->expectfn = ip_nat_sip_expected; | ||
308 | |||
309 | for (; port != 0; port++) { | ||
310 | int ret; | ||
311 | |||
312 | exp->tuple.dst.u.udp.port = htons(port); | ||
313 | ret = nf_ct_expect_related(exp); | ||
314 | if (ret == 0) | ||
315 | break; | ||
316 | else if (ret != -EBUSY) { | ||
317 | port = 0; | ||
318 | break; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | if (port == 0) | ||
323 | return NF_DROP; | ||
324 | |||
325 | if (exp->tuple.dst.u3.ip != exp->saved_ip || | ||
326 | exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { | ||
327 | buflen = sprintf(buffer, "%pI4:%u", &newip, port); | ||
328 | if (!mangle_packet(skb, dataoff, dptr, datalen, | ||
329 | matchoff, matchlen, buffer, buflen)) | ||
330 | goto err; | ||
331 | } | ||
332 | return NF_ACCEPT; | ||
333 | |||
334 | err: | ||
335 | nf_ct_unexpect_related(exp); | ||
336 | return NF_DROP; | ||
337 | } | ||
338 | |||
339 | static int mangle_content_len(struct sk_buff *skb, unsigned int dataoff, | ||
340 | const char **dptr, unsigned int *datalen) | ||
341 | { | ||
342 | enum ip_conntrack_info ctinfo; | ||
343 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
344 | unsigned int matchoff, matchlen; | ||
345 | char buffer[sizeof("65536")]; | ||
346 | int buflen, c_len; | ||
347 | |||
348 | /* Get actual SDP length */ | ||
349 | if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, | ||
350 | SDP_HDR_VERSION, SDP_HDR_UNSPEC, | ||
351 | &matchoff, &matchlen) <= 0) | ||
352 | return 0; | ||
353 | c_len = *datalen - matchoff + strlen("v="); | ||
354 | |||
355 | /* Now, update SDP length */ | ||
356 | if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH, | ||
357 | &matchoff, &matchlen) <= 0) | ||
358 | return 0; | ||
359 | |||
360 | buflen = sprintf(buffer, "%u", c_len); | ||
361 | return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
362 | buffer, buflen); | ||
363 | } | ||
364 | |||
365 | static int mangle_sdp_packet(struct sk_buff *skb, unsigned int dataoff, | ||
366 | const char **dptr, unsigned int *datalen, | ||
367 | unsigned int sdpoff, | ||
368 | enum sdp_header_types type, | ||
369 | enum sdp_header_types term, | ||
370 | char *buffer, int buflen) | ||
371 | { | ||
372 | enum ip_conntrack_info ctinfo; | ||
373 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
374 | unsigned int matchlen, matchoff; | ||
375 | |||
376 | if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, type, term, | ||
377 | &matchoff, &matchlen) <= 0) | ||
378 | return -ENOENT; | ||
379 | return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
380 | buffer, buflen) ? 0 : -EINVAL; | ||
381 | } | ||
382 | |||
383 | static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, unsigned int dataoff, | ||
384 | const char **dptr, unsigned int *datalen, | ||
385 | unsigned int sdpoff, | ||
386 | enum sdp_header_types type, | ||
387 | enum sdp_header_types term, | ||
388 | const union nf_inet_addr *addr) | ||
389 | { | ||
390 | char buffer[sizeof("nnn.nnn.nnn.nnn")]; | ||
391 | unsigned int buflen; | ||
392 | |||
393 | buflen = sprintf(buffer, "%pI4", &addr->ip); | ||
394 | if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, type, term, | ||
395 | buffer, buflen)) | ||
396 | return 0; | ||
397 | |||
398 | return mangle_content_len(skb, dataoff, dptr, datalen); | ||
399 | } | ||
400 | |||
401 | static unsigned int ip_nat_sdp_port(struct sk_buff *skb, unsigned int dataoff, | ||
402 | const char **dptr, unsigned int *datalen, | ||
403 | unsigned int matchoff, | ||
404 | unsigned int matchlen, | ||
405 | u_int16_t port) | ||
406 | { | ||
407 | char buffer[sizeof("nnnnn")]; | ||
408 | unsigned int buflen; | ||
409 | |||
410 | buflen = sprintf(buffer, "%u", port); | ||
411 | if (!mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, | ||
412 | buffer, buflen)) | ||
413 | return 0; | ||
414 | |||
415 | return mangle_content_len(skb, dataoff, dptr, datalen); | ||
416 | } | ||
417 | |||
418 | static unsigned int ip_nat_sdp_session(struct sk_buff *skb, unsigned int dataoff, | ||
419 | const char **dptr, unsigned int *datalen, | ||
420 | unsigned int sdpoff, | ||
421 | const union nf_inet_addr *addr) | ||
422 | { | ||
423 | char buffer[sizeof("nnn.nnn.nnn.nnn")]; | ||
424 | unsigned int buflen; | ||
425 | |||
426 | /* Mangle session description owner and contact addresses */ | ||
427 | buflen = sprintf(buffer, "%pI4", &addr->ip); | ||
428 | if (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, | ||
429 | SDP_HDR_OWNER_IP4, SDP_HDR_MEDIA, | ||
430 | buffer, buflen)) | ||
431 | return 0; | ||
432 | |||
433 | switch (mangle_sdp_packet(skb, dataoff, dptr, datalen, sdpoff, | ||
434 | SDP_HDR_CONNECTION_IP4, SDP_HDR_MEDIA, | ||
435 | buffer, buflen)) { | ||
436 | case 0: | ||
437 | /* | ||
438 | * RFC 2327: | ||
439 | * | ||
440 | * Session description | ||
441 | * | ||
442 | * c=* (connection information - not required if included in all media) | ||
443 | */ | ||
444 | case -ENOENT: | ||
445 | break; | ||
446 | default: | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | return mangle_content_len(skb, dataoff, dptr, datalen); | ||
451 | } | ||
452 | |||
453 | /* So, this packet has hit the connection tracking matching code. | ||
454 | Mangle it, and change the expectation to match the new version. */ | ||
455 | static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int dataoff, | ||
456 | const char **dptr, unsigned int *datalen, | ||
457 | struct nf_conntrack_expect *rtp_exp, | ||
458 | struct nf_conntrack_expect *rtcp_exp, | ||
459 | unsigned int mediaoff, | ||
460 | unsigned int medialen, | ||
461 | union nf_inet_addr *rtp_addr) | ||
462 | { | ||
463 | enum ip_conntrack_info ctinfo; | ||
464 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | ||
465 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
466 | u_int16_t port; | ||
467 | |||
468 | /* Connection will come from reply */ | ||
469 | if (ct->tuplehash[dir].tuple.src.u3.ip == | ||
470 | ct->tuplehash[!dir].tuple.dst.u3.ip) | ||
471 | rtp_addr->ip = rtp_exp->tuple.dst.u3.ip; | ||
472 | else | ||
473 | rtp_addr->ip = ct->tuplehash[!dir].tuple.dst.u3.ip; | ||
474 | |||
475 | rtp_exp->saved_ip = rtp_exp->tuple.dst.u3.ip; | ||
476 | rtp_exp->tuple.dst.u3.ip = rtp_addr->ip; | ||
477 | rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; | ||
478 | rtp_exp->dir = !dir; | ||
479 | rtp_exp->expectfn = ip_nat_sip_expected; | ||
480 | |||
481 | rtcp_exp->saved_ip = rtcp_exp->tuple.dst.u3.ip; | ||
482 | rtcp_exp->tuple.dst.u3.ip = rtp_addr->ip; | ||
483 | rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; | ||
484 | rtcp_exp->dir = !dir; | ||
485 | rtcp_exp->expectfn = ip_nat_sip_expected; | ||
486 | |||
487 | /* Try to get same pair of ports: if not, try to change them. */ | ||
488 | for (port = ntohs(rtp_exp->tuple.dst.u.udp.port); | ||
489 | port != 0; port += 2) { | ||
490 | int ret; | ||
491 | |||
492 | rtp_exp->tuple.dst.u.udp.port = htons(port); | ||
493 | ret = nf_ct_expect_related(rtp_exp); | ||
494 | if (ret == -EBUSY) | ||
495 | continue; | ||
496 | else if (ret < 0) { | ||
497 | port = 0; | ||
498 | break; | ||
499 | } | ||
500 | rtcp_exp->tuple.dst.u.udp.port = htons(port + 1); | ||
501 | ret = nf_ct_expect_related(rtcp_exp); | ||
502 | if (ret == 0) | ||
503 | break; | ||
504 | else if (ret != -EBUSY) { | ||
505 | nf_ct_unexpect_related(rtp_exp); | ||
506 | port = 0; | ||
507 | break; | ||
508 | } | ||
509 | } | ||
510 | |||
511 | if (port == 0) | ||
512 | goto err1; | ||
513 | |||
514 | /* Update media port. */ | ||
515 | if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port && | ||
516 | !ip_nat_sdp_port(skb, dataoff, dptr, datalen, | ||
517 | mediaoff, medialen, port)) | ||
518 | goto err2; | ||
519 | |||
520 | return NF_ACCEPT; | ||
521 | |||
522 | err2: | ||
523 | nf_ct_unexpect_related(rtp_exp); | ||
524 | nf_ct_unexpect_related(rtcp_exp); | ||
525 | err1: | ||
526 | return NF_DROP; | ||
527 | } | ||
528 | |||
529 | static void __exit nf_nat_sip_fini(void) | ||
530 | { | ||
531 | rcu_assign_pointer(nf_nat_sip_hook, NULL); | ||
532 | rcu_assign_pointer(nf_nat_sip_seq_adjust_hook, NULL); | ||
533 | rcu_assign_pointer(nf_nat_sip_expect_hook, NULL); | ||
534 | rcu_assign_pointer(nf_nat_sdp_addr_hook, NULL); | ||
535 | rcu_assign_pointer(nf_nat_sdp_port_hook, NULL); | ||
536 | rcu_assign_pointer(nf_nat_sdp_session_hook, NULL); | ||
537 | rcu_assign_pointer(nf_nat_sdp_media_hook, NULL); | ||
538 | synchronize_rcu(); | ||
539 | } | ||
540 | |||
541 | static int __init nf_nat_sip_init(void) | ||
542 | { | ||
543 | BUG_ON(nf_nat_sip_hook != NULL); | ||
544 | BUG_ON(nf_nat_sip_seq_adjust_hook != NULL); | ||
545 | BUG_ON(nf_nat_sip_expect_hook != NULL); | ||
546 | BUG_ON(nf_nat_sdp_addr_hook != NULL); | ||
547 | BUG_ON(nf_nat_sdp_port_hook != NULL); | ||
548 | BUG_ON(nf_nat_sdp_session_hook != NULL); | ||
549 | BUG_ON(nf_nat_sdp_media_hook != NULL); | ||
550 | rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip); | ||
551 | rcu_assign_pointer(nf_nat_sip_seq_adjust_hook, ip_nat_sip_seq_adjust); | ||
552 | rcu_assign_pointer(nf_nat_sip_expect_hook, ip_nat_sip_expect); | ||
553 | rcu_assign_pointer(nf_nat_sdp_addr_hook, ip_nat_sdp_addr); | ||
554 | rcu_assign_pointer(nf_nat_sdp_port_hook, ip_nat_sdp_port); | ||
555 | rcu_assign_pointer(nf_nat_sdp_session_hook, ip_nat_sdp_session); | ||
556 | rcu_assign_pointer(nf_nat_sdp_media_hook, ip_nat_sdp_media); | ||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | module_init(nf_nat_sip_init); | ||
561 | module_exit(nf_nat_sip_fini); | ||
diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c new file mode 100644 index 00000000000..a6e606e8482 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_standalone.c | |||
@@ -0,0 +1,326 @@ | |||
1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | #include <linux/types.h> | ||
9 | #include <linux/icmp.h> | ||
10 | #include <linux/gfp.h> | ||
11 | #include <linux/ip.h> | ||
12 | #include <linux/netfilter.h> | ||
13 | #include <linux/netfilter_ipv4.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/skbuff.h> | ||
16 | #include <linux/proc_fs.h> | ||
17 | #include <net/ip.h> | ||
18 | #include <net/checksum.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | |||
21 | #include <net/netfilter/nf_conntrack.h> | ||
22 | #include <net/netfilter/nf_conntrack_core.h> | ||
23 | #include <net/netfilter/nf_conntrack_extend.h> | ||
24 | #include <net/netfilter/nf_nat.h> | ||
25 | #include <net/netfilter/nf_nat_rule.h> | ||
26 | #include <net/netfilter/nf_nat_protocol.h> | ||
27 | #include <net/netfilter/nf_nat_core.h> | ||
28 | #include <net/netfilter/nf_nat_helper.h> | ||
29 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
30 | |||
31 | #ifdef CONFIG_XFRM | ||
32 | static void nat_decode_session(struct sk_buff *skb, struct flowi *fl) | ||
33 | { | ||
34 | struct flowi4 *fl4 = &fl->u.ip4; | ||
35 | const struct nf_conn *ct; | ||
36 | const struct nf_conntrack_tuple *t; | ||
37 | enum ip_conntrack_info ctinfo; | ||
38 | enum ip_conntrack_dir dir; | ||
39 | unsigned long statusbit; | ||
40 | |||
41 | ct = nf_ct_get(skb, &ctinfo); | ||
42 | if (ct == NULL) | ||
43 | return; | ||
44 | dir = CTINFO2DIR(ctinfo); | ||
45 | t = &ct->tuplehash[dir].tuple; | ||
46 | |||
47 | if (dir == IP_CT_DIR_ORIGINAL) | ||
48 | statusbit = IPS_DST_NAT; | ||
49 | else | ||
50 | statusbit = IPS_SRC_NAT; | ||
51 | |||
52 | if (ct->status & statusbit) { | ||
53 | fl4->daddr = t->dst.u3.ip; | ||
54 | if (t->dst.protonum == IPPROTO_TCP || | ||
55 | t->dst.protonum == IPPROTO_UDP || | ||
56 | t->dst.protonum == IPPROTO_UDPLITE || | ||
57 | t->dst.protonum == IPPROTO_DCCP || | ||
58 | t->dst.protonum == IPPROTO_SCTP) | ||
59 | fl4->fl4_dport = t->dst.u.tcp.port; | ||
60 | } | ||
61 | |||
62 | statusbit ^= IPS_NAT_MASK; | ||
63 | |||
64 | if (ct->status & statusbit) { | ||
65 | fl4->saddr = t->src.u3.ip; | ||
66 | if (t->dst.protonum == IPPROTO_TCP || | ||
67 | t->dst.protonum == IPPROTO_UDP || | ||
68 | t->dst.protonum == IPPROTO_UDPLITE || | ||
69 | t->dst.protonum == IPPROTO_DCCP || | ||
70 | t->dst.protonum == IPPROTO_SCTP) | ||
71 | fl4->fl4_sport = t->src.u.tcp.port; | ||
72 | } | ||
73 | } | ||
74 | #endif | ||
75 | |||
76 | static unsigned int | ||
77 | nf_nat_fn(unsigned int hooknum, | ||
78 | struct sk_buff *skb, | ||
79 | const struct net_device *in, | ||
80 | const struct net_device *out, | ||
81 | int (*okfn)(struct sk_buff *)) | ||
82 | { | ||
83 | struct nf_conn *ct; | ||
84 | enum ip_conntrack_info ctinfo; | ||
85 | struct nf_conn_nat *nat; | ||
86 | /* maniptype == SRC for postrouting. */ | ||
87 | enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum); | ||
88 | |||
89 | /* We never see fragments: conntrack defrags on pre-routing | ||
90 | and local-out, and nf_nat_out protects post-routing. */ | ||
91 | NF_CT_ASSERT(!ip_is_fragment(ip_hdr(skb))); | ||
92 | |||
93 | ct = nf_ct_get(skb, &ctinfo); | ||
94 | /* Can't track? It's not due to stress, or conntrack would | ||
95 | have dropped it. Hence it's the user's responsibilty to | ||
96 | packet filter it out, or implement conntrack/NAT for that | ||
97 | protocol. 8) --RR */ | ||
98 | if (!ct) | ||
99 | return NF_ACCEPT; | ||
100 | |||
101 | /* Don't try to NAT if this packet is not conntracked */ | ||
102 | if (nf_ct_is_untracked(ct)) | ||
103 | return NF_ACCEPT; | ||
104 | |||
105 | nat = nfct_nat(ct); | ||
106 | if (!nat) { | ||
107 | /* NAT module was loaded late. */ | ||
108 | if (nf_ct_is_confirmed(ct)) | ||
109 | return NF_ACCEPT; | ||
110 | nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | ||
111 | if (nat == NULL) { | ||
112 | pr_debug("failed to add NAT extension\n"); | ||
113 | return NF_ACCEPT; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | switch (ctinfo) { | ||
118 | case IP_CT_RELATED: | ||
119 | case IP_CT_RELATED_REPLY: | ||
120 | if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { | ||
121 | if (!nf_nat_icmp_reply_translation(ct, ctinfo, | ||
122 | hooknum, skb)) | ||
123 | return NF_DROP; | ||
124 | else | ||
125 | return NF_ACCEPT; | ||
126 | } | ||
127 | /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ | ||
128 | case IP_CT_NEW: | ||
129 | |||
130 | /* Seen it before? This can happen for loopback, retrans, | ||
131 | or local packets.. */ | ||
132 | if (!nf_nat_initialized(ct, maniptype)) { | ||
133 | unsigned int ret; | ||
134 | |||
135 | ret = nf_nat_rule_find(skb, hooknum, in, out, ct); | ||
136 | if (ret != NF_ACCEPT) | ||
137 | return ret; | ||
138 | } else | ||
139 | pr_debug("Already setup manip %s for ct %p\n", | ||
140 | maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST", | ||
141 | ct); | ||
142 | break; | ||
143 | |||
144 | default: | ||
145 | /* ESTABLISHED */ | ||
146 | NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || | ||
147 | ctinfo == IP_CT_ESTABLISHED_REPLY); | ||
148 | } | ||
149 | |||
150 | return nf_nat_packet(ct, ctinfo, hooknum, skb); | ||
151 | } | ||
152 | |||
153 | static unsigned int | ||
154 | nf_nat_in(unsigned int hooknum, | ||
155 | struct sk_buff *skb, | ||
156 | const struct net_device *in, | ||
157 | const struct net_device *out, | ||
158 | int (*okfn)(struct sk_buff *)) | ||
159 | { | ||
160 | unsigned int ret; | ||
161 | __be32 daddr = ip_hdr(skb)->daddr; | ||
162 | |||
163 | ret = nf_nat_fn(hooknum, skb, in, out, okfn); | ||
164 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
165 | daddr != ip_hdr(skb)->daddr) | ||
166 | skb_dst_drop(skb); | ||
167 | |||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | static unsigned int | ||
172 | nf_nat_out(unsigned int hooknum, | ||
173 | struct sk_buff *skb, | ||
174 | const struct net_device *in, | ||
175 | const struct net_device *out, | ||
176 | int (*okfn)(struct sk_buff *)) | ||
177 | { | ||
178 | #ifdef CONFIG_XFRM | ||
179 | const struct nf_conn *ct; | ||
180 | enum ip_conntrack_info ctinfo; | ||
181 | #endif | ||
182 | unsigned int ret; | ||
183 | |||
184 | /* root is playing with raw sockets. */ | ||
185 | if (skb->len < sizeof(struct iphdr) || | ||
186 | ip_hdrlen(skb) < sizeof(struct iphdr)) | ||
187 | return NF_ACCEPT; | ||
188 | |||
189 | ret = nf_nat_fn(hooknum, skb, in, out, okfn); | ||
190 | #ifdef CONFIG_XFRM | ||
191 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
192 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
193 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
194 | |||
195 | if ((ct->tuplehash[dir].tuple.src.u3.ip != | ||
196 | ct->tuplehash[!dir].tuple.dst.u3.ip) || | ||
197 | (ct->tuplehash[dir].tuple.src.u.all != | ||
198 | ct->tuplehash[!dir].tuple.dst.u.all) | ||
199 | ) | ||
200 | return ip_xfrm_me_harder(skb) == 0 ? ret : NF_DROP; | ||
201 | } | ||
202 | #endif | ||
203 | return ret; | ||
204 | } | ||
205 | |||
206 | static unsigned int | ||
207 | nf_nat_local_fn(unsigned int hooknum, | ||
208 | struct sk_buff *skb, | ||
209 | const struct net_device *in, | ||
210 | const struct net_device *out, | ||
211 | int (*okfn)(struct sk_buff *)) | ||
212 | { | ||
213 | const struct nf_conn *ct; | ||
214 | enum ip_conntrack_info ctinfo; | ||
215 | unsigned int ret; | ||
216 | |||
217 | /* root is playing with raw sockets. */ | ||
218 | if (skb->len < sizeof(struct iphdr) || | ||
219 | ip_hdrlen(skb) < sizeof(struct iphdr)) | ||
220 | return NF_ACCEPT; | ||
221 | |||
222 | ret = nf_nat_fn(hooknum, skb, in, out, okfn); | ||
223 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
224 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
225 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
226 | |||
227 | if (ct->tuplehash[dir].tuple.dst.u3.ip != | ||
228 | ct->tuplehash[!dir].tuple.src.u3.ip) { | ||
229 | if (ip_route_me_harder(skb, RTN_UNSPEC)) | ||
230 | ret = NF_DROP; | ||
231 | } | ||
232 | #ifdef CONFIG_XFRM | ||
233 | else if (ct->tuplehash[dir].tuple.dst.u.all != | ||
234 | ct->tuplehash[!dir].tuple.src.u.all) | ||
235 | if (ip_xfrm_me_harder(skb)) | ||
236 | ret = NF_DROP; | ||
237 | #endif | ||
238 | } | ||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | /* We must be after connection tracking and before packet filtering. */ | ||
243 | |||
244 | static struct nf_hook_ops nf_nat_ops[] __read_mostly = { | ||
245 | /* Before packet filtering, change destination */ | ||
246 | { | ||
247 | .hook = nf_nat_in, | ||
248 | .owner = THIS_MODULE, | ||
249 | .pf = NFPROTO_IPV4, | ||
250 | .hooknum = NF_INET_PRE_ROUTING, | ||
251 | .priority = NF_IP_PRI_NAT_DST, | ||
252 | }, | ||
253 | /* After packet filtering, change source */ | ||
254 | { | ||
255 | .hook = nf_nat_out, | ||
256 | .owner = THIS_MODULE, | ||
257 | .pf = NFPROTO_IPV4, | ||
258 | .hooknum = NF_INET_POST_ROUTING, | ||
259 | .priority = NF_IP_PRI_NAT_SRC, | ||
260 | }, | ||
261 | /* Before packet filtering, change destination */ | ||
262 | { | ||
263 | .hook = nf_nat_local_fn, | ||
264 | .owner = THIS_MODULE, | ||
265 | .pf = NFPROTO_IPV4, | ||
266 | .hooknum = NF_INET_LOCAL_OUT, | ||
267 | .priority = NF_IP_PRI_NAT_DST, | ||
268 | }, | ||
269 | /* After packet filtering, change source */ | ||
270 | { | ||
271 | .hook = nf_nat_fn, | ||
272 | .owner = THIS_MODULE, | ||
273 | .pf = NFPROTO_IPV4, | ||
274 | .hooknum = NF_INET_LOCAL_IN, | ||
275 | .priority = NF_IP_PRI_NAT_SRC, | ||
276 | }, | ||
277 | }; | ||
278 | |||
279 | static int __init nf_nat_standalone_init(void) | ||
280 | { | ||
281 | int ret = 0; | ||
282 | |||
283 | need_ipv4_conntrack(); | ||
284 | |||
285 | #ifdef CONFIG_XFRM | ||
286 | BUG_ON(ip_nat_decode_session != NULL); | ||
287 | rcu_assign_pointer(ip_nat_decode_session, nat_decode_session); | ||
288 | #endif | ||
289 | ret = nf_nat_rule_init(); | ||
290 | if (ret < 0) { | ||
291 | pr_err("nf_nat_init: can't setup rules.\n"); | ||
292 | goto cleanup_decode_session; | ||
293 | } | ||
294 | ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops)); | ||
295 | if (ret < 0) { | ||
296 | pr_err("nf_nat_init: can't register hooks.\n"); | ||
297 | goto cleanup_rule_init; | ||
298 | } | ||
299 | return ret; | ||
300 | |||
301 | cleanup_rule_init: | ||
302 | nf_nat_rule_cleanup(); | ||
303 | cleanup_decode_session: | ||
304 | #ifdef CONFIG_XFRM | ||
305 | rcu_assign_pointer(ip_nat_decode_session, NULL); | ||
306 | synchronize_net(); | ||
307 | #endif | ||
308 | return ret; | ||
309 | } | ||
310 | |||
311 | static void __exit nf_nat_standalone_fini(void) | ||
312 | { | ||
313 | nf_unregister_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops)); | ||
314 | nf_nat_rule_cleanup(); | ||
315 | #ifdef CONFIG_XFRM | ||
316 | rcu_assign_pointer(ip_nat_decode_session, NULL); | ||
317 | synchronize_net(); | ||
318 | #endif | ||
319 | /* Conntrack caches are unregistered in nf_conntrack_cleanup */ | ||
320 | } | ||
321 | |||
322 | module_init(nf_nat_standalone_init); | ||
323 | module_exit(nf_nat_standalone_fini); | ||
324 | |||
325 | MODULE_LICENSE("GPL"); | ||
326 | MODULE_ALIAS("ip_nat"); | ||
diff --git a/net/ipv4/netfilter/nf_nat_tftp.c b/net/ipv4/netfilter/nf_nat_tftp.c new file mode 100644 index 00000000000..7274a43c7a1 --- /dev/null +++ b/net/ipv4/netfilter/nf_nat_tftp.c | |||
@@ -0,0 +1,51 @@ | |||
1 | /* (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu> | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 as | ||
5 | * published by the Free Software Foundation. | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include <linux/udp.h> | ||
10 | |||
11 | #include <net/netfilter/nf_nat_helper.h> | ||
12 | #include <net/netfilter/nf_nat_rule.h> | ||
13 | #include <net/netfilter/nf_conntrack_helper.h> | ||
14 | #include <net/netfilter/nf_conntrack_expect.h> | ||
15 | #include <linux/netfilter/nf_conntrack_tftp.h> | ||
16 | |||
17 | MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>"); | ||
18 | MODULE_DESCRIPTION("TFTP NAT helper"); | ||
19 | MODULE_LICENSE("GPL"); | ||
20 | MODULE_ALIAS("ip_nat_tftp"); | ||
21 | |||
22 | static unsigned int help(struct sk_buff *skb, | ||
23 | enum ip_conntrack_info ctinfo, | ||
24 | struct nf_conntrack_expect *exp) | ||
25 | { | ||
26 | const struct nf_conn *ct = exp->master; | ||
27 | |||
28 | exp->saved_proto.udp.port | ||
29 | = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; | ||
30 | exp->dir = IP_CT_DIR_REPLY; | ||
31 | exp->expectfn = nf_nat_follow_master; | ||
32 | if (nf_ct_expect_related(exp) != 0) | ||
33 | return NF_DROP; | ||
34 | return NF_ACCEPT; | ||
35 | } | ||
36 | |||
37 | static void __exit nf_nat_tftp_fini(void) | ||
38 | { | ||
39 | rcu_assign_pointer(nf_nat_tftp_hook, NULL); | ||
40 | synchronize_rcu(); | ||
41 | } | ||
42 | |||
43 | static int __init nf_nat_tftp_init(void) | ||
44 | { | ||
45 | BUG_ON(nf_nat_tftp_hook != NULL); | ||
46 | rcu_assign_pointer(nf_nat_tftp_hook, help); | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | module_init(nf_nat_tftp_init); | ||
51 | module_exit(nf_nat_tftp_fini); | ||