[NETFILTER] nf_conntrack: clean up to reduce size of 'struct nf_conn'
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / ipv6 / netfilter / nf_conntrack_l3proto_ipv6.c
CommitLineData
9fb9cbb1
YK
1/*
2 * Copyright (C)2004 USAGI/WIDE Project
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 * Author:
9 * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
10 *
11 * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
12 * - support Layer 3 protocol independent connection tracking.
13 * Based on the original ip_conntrack code which had the following
14 * copyright information:
15 * (C) 1999-2001 Paul `Rusty' Russell
16 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
17 *
18 * 23 Mar 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
19 * - add get_features() to support various size of conntrack
20 * structures.
21 */
22
23#include <linux/config.h>
24#include <linux/types.h>
25#include <linux/ipv6.h>
26#include <linux/in6.h>
27#include <linux/netfilter.h>
28#include <linux/module.h>
29#include <linux/skbuff.h>
30#include <linux/icmp.h>
31#include <linux/sysctl.h>
32#include <net/ipv6.h>
33
34#include <linux/netfilter_ipv6.h>
35#include <net/netfilter/nf_conntrack.h>
36#include <net/netfilter/nf_conntrack_helper.h>
37#include <net/netfilter/nf_conntrack_protocol.h>
38#include <net/netfilter/nf_conntrack_l3proto.h>
39#include <net/netfilter/nf_conntrack_core.h>
40
41#if 0
42#define DEBUGP printk
43#else
44#define DEBUGP(format, args...)
45#endif
46
47DECLARE_PER_CPU(struct ip_conntrack_stat, nf_conntrack_stat);
48
49static int ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
50 struct nf_conntrack_tuple *tuple)
51{
52 u_int32_t _addrs[8], *ap;
53
54 ap = skb_header_pointer(skb, nhoff + offsetof(struct ipv6hdr, saddr),
55 sizeof(_addrs), _addrs);
56 if (ap == NULL)
57 return 0;
58
59 memcpy(tuple->src.u3.ip6, ap, sizeof(tuple->src.u3.ip6));
60 memcpy(tuple->dst.u3.ip6, ap + 4, sizeof(tuple->dst.u3.ip6));
61
62 return 1;
63}
64
65static int ipv6_invert_tuple(struct nf_conntrack_tuple *tuple,
66 const struct nf_conntrack_tuple *orig)
67{
68 memcpy(tuple->src.u3.ip6, orig->dst.u3.ip6, sizeof(tuple->src.u3.ip6));
69 memcpy(tuple->dst.u3.ip6, orig->src.u3.ip6, sizeof(tuple->dst.u3.ip6));
70
71 return 1;
72}
73
74static int ipv6_print_tuple(struct seq_file *s,
75 const struct nf_conntrack_tuple *tuple)
76{
46b86a2d 77 return seq_printf(s, "src=" NIP6_FMT " dst=" NIP6_FMT " ",
9fb9cbb1
YK
78 NIP6(*((struct in6_addr *)tuple->src.u3.ip6)),
79 NIP6(*((struct in6_addr *)tuple->dst.u3.ip6)));
80}
81
82static int ipv6_print_conntrack(struct seq_file *s,
83 const struct nf_conn *conntrack)
84{
85 return 0;
86}
87
88/*
89 * Based on ipv6_skip_exthdr() in net/ipv6/exthdr.c
90 *
91 * This function parses (probably truncated) exthdr set "hdr"
92 * of length "len". "nexthdrp" initially points to some place,
93 * where type of the first header can be found.
94 *
95 * It skips all well-known exthdrs, and returns pointer to the start
96 * of unparsable area i.e. the first header with unknown type.
97 * if success, *nexthdr is updated by type/protocol of this header.
98 *
99 * NOTES: - it may return pointer pointing beyond end of packet,
100 * if the last recognized header is truncated in the middle.
101 * - if packet is truncated, so that all parsed headers are skipped,
102 * it returns -1.
103 * - if packet is fragmented, return pointer of the fragment header.
104 * - ESP is unparsable for now and considered like
105 * normal payload protocol.
106 * - Note also special handling of AUTH header. Thanks to IPsec wizards.
107 */
108
109int nf_ct_ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp,
110 int len)
111{
112 u8 nexthdr = *nexthdrp;
113
114 while (ipv6_ext_hdr(nexthdr)) {
115 struct ipv6_opt_hdr hdr;
116 int hdrlen;
117
118 if (len < (int)sizeof(struct ipv6_opt_hdr))
119 return -1;
120 if (nexthdr == NEXTHDR_NONE)
121 break;
122 if (nexthdr == NEXTHDR_FRAGMENT)
123 break;
124 if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
125 BUG();
126 if (nexthdr == NEXTHDR_AUTH)
127 hdrlen = (hdr.hdrlen+2)<<2;
128 else
129 hdrlen = ipv6_optlen(&hdr);
130
131 nexthdr = hdr.nexthdr;
132 len -= hdrlen;
133 start += hdrlen;
134 }
135
136 *nexthdrp = nexthdr;
137 return start;
138}
139
140static int
141ipv6_prepare(struct sk_buff **pskb, unsigned int hooknum, unsigned int *dataoff,
142 u_int8_t *protonum)
143{
144 unsigned int extoff;
145 unsigned char pnum;
146 int protoff;
147
148 extoff = (u8*)((*pskb)->nh.ipv6h + 1) - (*pskb)->data;
149 pnum = (*pskb)->nh.ipv6h->nexthdr;
150
151 protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum,
152 (*pskb)->len - extoff);
153
154 /*
155 * (protoff == (*pskb)->len) mean that the packet doesn't have no data
156 * except of IPv6 & ext headers. but it's tracked anyway. - YK
157 */
158 if ((protoff < 0) || (protoff > (*pskb)->len)) {
159 DEBUGP("ip6_conntrack_core: can't find proto in pkt\n");
160 NF_CT_STAT_INC(error);
161 NF_CT_STAT_INC(invalid);
162 return -NF_ACCEPT;
163 }
164
165 *dataoff = protoff;
166 *protonum = pnum;
167 return NF_ACCEPT;
168}
169
170static u_int32_t ipv6_get_features(const struct nf_conntrack_tuple *tuple)
171{
172 return NF_CT_F_BASIC;
173}
174
175static unsigned int ipv6_confirm(unsigned int hooknum,
176 struct sk_buff **pskb,
177 const struct net_device *in,
178 const struct net_device *out,
179 int (*okfn)(struct sk_buff *))
180{
181 struct nf_conn *ct;
dc808fe2 182 struct nf_conn_help *help;
9fb9cbb1 183 enum ip_conntrack_info ctinfo;
dc808fe2
HW
184 unsigned int ret, protoff;
185 unsigned int extoff = (u8*)((*pskb)->nh.ipv6h + 1)
186 - (*pskb)->data;
187 unsigned char pnum = (*pskb)->nh.ipv6h->nexthdr;
188
9fb9cbb1
YK
189
190 /* This is where we call the helper: as the packet goes out. */
191 ct = nf_ct_get(*pskb, &ctinfo);
dc808fe2
HW
192 if (!ct)
193 goto out;
194
195 help = nfct_help(ct);
196 if (!help || !help->helper)
197 goto out;
198
199 protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum,
200 (*pskb)->len - extoff);
201 if (protoff < 0 || protoff > (*pskb)->len ||
202 pnum == NEXTHDR_FRAGMENT) {
203 DEBUGP("proto header not found\n");
204 return NF_ACCEPT;
9fb9cbb1
YK
205 }
206
dc808fe2
HW
207 ret = help->helper->help(pskb, protoff, ct, ctinfo);
208 if (ret != NF_ACCEPT)
209 return ret;
210out:
9fb9cbb1 211 /* We've seen it coming out the other side: confirm it */
9fb9cbb1
YK
212 return nf_conntrack_confirm(pskb);
213}
214
215extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb);
216extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
217 struct net_device *in,
218 struct net_device *out,
219 int (*okfn)(struct sk_buff *));
220static unsigned int ipv6_defrag(unsigned int hooknum,
221 struct sk_buff **pskb,
222 const struct net_device *in,
223 const struct net_device *out,
224 int (*okfn)(struct sk_buff *))
225{
226 struct sk_buff *reasm;
227
228 /* Previously seen (loopback)? */
229 if ((*pskb)->nfct)
230 return NF_ACCEPT;
231
232 reasm = nf_ct_frag6_gather(*pskb);
233
234 /* queued */
235 if (reasm == NULL)
236 return NF_STOLEN;
237
238 /* error occured or not fragmented */
239 if (reasm == *pskb)
240 return NF_ACCEPT;
241
242 nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in,
243 (struct net_device *)out, okfn);
244
245 return NF_STOLEN;
246}
247
248static unsigned int ipv6_conntrack_in(unsigned int hooknum,
249 struct sk_buff **pskb,
250 const struct net_device *in,
251 const struct net_device *out,
252 int (*okfn)(struct sk_buff *))
253{
254 struct sk_buff *reasm = (*pskb)->nfct_reasm;
255
256 /* This packet is fragmented and has reassembled packet. */
257 if (reasm) {
258 /* Reassembled packet isn't parsed yet ? */
259 if (!reasm->nfct) {
260 unsigned int ret;
261
262 ret = nf_conntrack_in(PF_INET6, hooknum, &reasm);
263 if (ret != NF_ACCEPT)
264 return ret;
265 }
266 nf_conntrack_get(reasm->nfct);
267 (*pskb)->nfct = reasm->nfct;
268 return NF_ACCEPT;
269 }
270
271 return nf_conntrack_in(PF_INET6, hooknum, pskb);
272}
273
274static unsigned int ipv6_conntrack_local(unsigned int hooknum,
275 struct sk_buff **pskb,
276 const struct net_device *in,
277 const struct net_device *out,
278 int (*okfn)(struct sk_buff *))
279{
280 /* root is playing with raw sockets. */
281 if ((*pskb)->len < sizeof(struct ipv6hdr)) {
282 if (net_ratelimit())
283 printk("ipv6_conntrack_local: packet too short\n");
284 return NF_ACCEPT;
285 }
286 return ipv6_conntrack_in(hooknum, pskb, in, out, okfn);
287}
288
289/* Connection tracking may drop packets, but never alters them, so
290 make it the first hook. */
291static struct nf_hook_ops ipv6_conntrack_defrag_ops = {
292 .hook = ipv6_defrag,
293 .owner = THIS_MODULE,
294 .pf = PF_INET6,
295 .hooknum = NF_IP6_PRE_ROUTING,
296 .priority = NF_IP6_PRI_CONNTRACK_DEFRAG,
297};
298
299static struct nf_hook_ops ipv6_conntrack_in_ops = {
300 .hook = ipv6_conntrack_in,
301 .owner = THIS_MODULE,
302 .pf = PF_INET6,
303 .hooknum = NF_IP6_PRE_ROUTING,
304 .priority = NF_IP6_PRI_CONNTRACK,
305};
306
307static struct nf_hook_ops ipv6_conntrack_local_out_ops = {
308 .hook = ipv6_conntrack_local,
309 .owner = THIS_MODULE,
310 .pf = PF_INET6,
311 .hooknum = NF_IP6_LOCAL_OUT,
312 .priority = NF_IP6_PRI_CONNTRACK,
313};
314
315static struct nf_hook_ops ipv6_conntrack_defrag_local_out_ops = {
316 .hook = ipv6_defrag,
317 .owner = THIS_MODULE,
318 .pf = PF_INET6,
319 .hooknum = NF_IP6_LOCAL_OUT,
320 .priority = NF_IP6_PRI_CONNTRACK_DEFRAG,
321};
322
323/* Refragmenter; last chance. */
324static struct nf_hook_ops ipv6_conntrack_out_ops = {
325 .hook = ipv6_confirm,
326 .owner = THIS_MODULE,
327 .pf = PF_INET6,
328 .hooknum = NF_IP6_POST_ROUTING,
329 .priority = NF_IP6_PRI_LAST,
330};
331
332static struct nf_hook_ops ipv6_conntrack_local_in_ops = {
333 .hook = ipv6_confirm,
334 .owner = THIS_MODULE,
335 .pf = PF_INET6,
336 .hooknum = NF_IP6_LOCAL_IN,
337 .priority = NF_IP6_PRI_LAST-1,
338};
339
340#ifdef CONFIG_SYSCTL
341
342/* From nf_conntrack_proto_icmpv6.c */
babbdb1a 343extern unsigned int nf_ct_icmpv6_timeout;
9fb9cbb1
YK
344
345/* From nf_conntrack_frag6.c */
babbdb1a 346extern unsigned int nf_ct_frag6_timeout;
7686a02c
YK
347extern unsigned int nf_ct_frag6_low_thresh;
348extern unsigned int nf_ct_frag6_high_thresh;
9fb9cbb1
YK
349
350static struct ctl_table_header *nf_ct_ipv6_sysctl_header;
351
352static ctl_table nf_ct_sysctl_table[] = {
353 {
354 .ctl_name = NET_NF_CONNTRACK_ICMPV6_TIMEOUT,
355 .procname = "nf_conntrack_icmpv6_timeout",
356 .data = &nf_ct_icmpv6_timeout,
357 .maxlen = sizeof(unsigned int),
358 .mode = 0644,
359 .proc_handler = &proc_dointvec_jiffies,
360 },
361 {
362 .ctl_name = NET_NF_CONNTRACK_FRAG6_TIMEOUT,
363 .procname = "nf_conntrack_frag6_timeout",
364 .data = &nf_ct_frag6_timeout,
365 .maxlen = sizeof(unsigned int),
366 .mode = 0644,
367 .proc_handler = &proc_dointvec_jiffies,
368 },
369 {
370 .ctl_name = NET_NF_CONNTRACK_FRAG6_LOW_THRESH,
371 .procname = "nf_conntrack_frag6_low_thresh",
372 .data = &nf_ct_frag6_low_thresh,
373 .maxlen = sizeof(unsigned int),
374 .mode = 0644,
7686a02c 375 .proc_handler = &proc_dointvec,
9fb9cbb1
YK
376 },
377 {
378 .ctl_name = NET_NF_CONNTRACK_FRAG6_HIGH_THRESH,
379 .procname = "nf_conntrack_frag6_high_thresh",
380 .data = &nf_ct_frag6_high_thresh,
381 .maxlen = sizeof(unsigned int),
382 .mode = 0644,
7686a02c 383 .proc_handler = &proc_dointvec,
9fb9cbb1
YK
384 },
385 { .ctl_name = 0 }
386};
387
388static ctl_table nf_ct_netfilter_table[] = {
389 {
390 .ctl_name = NET_NETFILTER,
391 .procname = "netfilter",
392 .mode = 0555,
393 .child = nf_ct_sysctl_table,
394 },
395 { .ctl_name = 0 }
396};
397
398static ctl_table nf_ct_net_table[] = {
399 {
400 .ctl_name = CTL_NET,
401 .procname = "net",
402 .mode = 0555,
403 .child = nf_ct_netfilter_table,
404 },
405 { .ctl_name = 0 }
406};
407#endif
408
c1d10adb
PNA
409#if defined(CONFIG_NF_CT_NETLINK) || \
410 defined(CONFIG_NF_CT_NETLINK_MODULE)
411
412#include <linux/netfilter/nfnetlink.h>
413#include <linux/netfilter/nfnetlink_conntrack.h>
414
415static int ipv6_tuple_to_nfattr(struct sk_buff *skb,
416 const struct nf_conntrack_tuple *tuple)
417{
418 NFA_PUT(skb, CTA_IP_V6_SRC, sizeof(u_int32_t) * 4,
419 &tuple->src.u3.ip6);
420 NFA_PUT(skb, CTA_IP_V6_DST, sizeof(u_int32_t) * 4,
421 &tuple->dst.u3.ip6);
422 return 0;
423
424nfattr_failure:
425 return -1;
426}
427
428static const size_t cta_min_ip[CTA_IP_MAX] = {
429 [CTA_IP_V6_SRC-1] = sizeof(u_int32_t)*4,
430 [CTA_IP_V6_DST-1] = sizeof(u_int32_t)*4,
431};
432
433static int ipv6_nfattr_to_tuple(struct nfattr *tb[],
434 struct nf_conntrack_tuple *t)
435{
436 if (!tb[CTA_IP_V6_SRC-1] || !tb[CTA_IP_V6_DST-1])
437 return -EINVAL;
438
439 if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip))
440 return -EINVAL;
441
442 memcpy(&t->src.u3.ip6, NFA_DATA(tb[CTA_IP_V6_SRC-1]),
443 sizeof(u_int32_t) * 4);
444 memcpy(&t->dst.u3.ip6, NFA_DATA(tb[CTA_IP_V6_DST-1]),
445 sizeof(u_int32_t) * 4);
446
447 return 0;
448}
449#endif
450
9fb9cbb1
YK
451struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 = {
452 .l3proto = PF_INET6,
453 .name = "ipv6",
454 .pkt_to_tuple = ipv6_pkt_to_tuple,
455 .invert_tuple = ipv6_invert_tuple,
456 .print_tuple = ipv6_print_tuple,
457 .print_conntrack = ipv6_print_conntrack,
458 .prepare = ipv6_prepare,
c1d10adb
PNA
459#if defined(CONFIG_NF_CT_NETLINK) || \
460 defined(CONFIG_NF_CT_NETLINK_MODULE)
461 .tuple_to_nfattr = ipv6_tuple_to_nfattr,
462 .nfattr_to_tuple = ipv6_nfattr_to_tuple,
463#endif
9fb9cbb1
YK
464 .get_features = ipv6_get_features,
465 .me = THIS_MODULE,
466};
467
468extern struct nf_conntrack_protocol nf_conntrack_protocol_tcp6;
469extern struct nf_conntrack_protocol nf_conntrack_protocol_udp6;
470extern struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6;
471extern int nf_ct_frag6_init(void);
472extern void nf_ct_frag6_cleanup(void);
473static int init_or_cleanup(int init)
474{
475 int ret = 0;
476
477 if (!init) goto cleanup;
478
479 ret = nf_ct_frag6_init();
480 if (ret < 0) {
481 printk("nf_conntrack_ipv6: can't initialize frag6.\n");
482 goto cleanup_nothing;
483 }
484 ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_tcp6);
485 if (ret < 0) {
486 printk("nf_conntrack_ipv6: can't register tcp.\n");
487 goto cleanup_frag6;
488 }
489
490 ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_udp6);
491 if (ret < 0) {
492 printk("nf_conntrack_ipv6: can't register udp.\n");
493 goto cleanup_tcp;
494 }
495
496 ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_icmpv6);
497 if (ret < 0) {
498 printk("nf_conntrack_ipv6: can't register icmpv6.\n");
499 goto cleanup_udp;
500 }
501
502 ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6);
503 if (ret < 0) {
504 printk("nf_conntrack_ipv6: can't register ipv6\n");
505 goto cleanup_icmpv6;
506 }
507
508 ret = nf_register_hook(&ipv6_conntrack_defrag_ops);
509 if (ret < 0) {
510 printk("nf_conntrack_ipv6: can't register pre-routing defrag "
511 "hook.\n");
512 goto cleanup_ipv6;
513 }
514
515 ret = nf_register_hook(&ipv6_conntrack_defrag_local_out_ops);
516 if (ret < 0) {
517 printk("nf_conntrack_ipv6: can't register local_out defrag "
518 "hook.\n");
519 goto cleanup_defragops;
520 }
521
522 ret = nf_register_hook(&ipv6_conntrack_in_ops);
523 if (ret < 0) {
524 printk("nf_conntrack_ipv6: can't register pre-routing hook.\n");
525 goto cleanup_defraglocalops;
526 }
527
528 ret = nf_register_hook(&ipv6_conntrack_local_out_ops);
529 if (ret < 0) {
530 printk("nf_conntrack_ipv6: can't register local out hook.\n");
531 goto cleanup_inops;
532 }
533
534 ret = nf_register_hook(&ipv6_conntrack_out_ops);
535 if (ret < 0) {
536 printk("nf_conntrack_ipv6: can't register post-routing hook.\n");
537 goto cleanup_inandlocalops;
538 }
539
540 ret = nf_register_hook(&ipv6_conntrack_local_in_ops);
541 if (ret < 0) {
542 printk("nf_conntrack_ipv6: can't register local in hook.\n");
543 goto cleanup_inoutandlocalops;
544 }
545
546#ifdef CONFIG_SYSCTL
547 nf_ct_ipv6_sysctl_header = register_sysctl_table(nf_ct_net_table, 0);
548 if (nf_ct_ipv6_sysctl_header == NULL) {
549 printk("nf_conntrack: can't register to sysctl.\n");
550 ret = -ENOMEM;
551 goto cleanup_localinops;
552 }
553#endif
554 return ret;
555
556 cleanup:
557 synchronize_net();
558#ifdef CONFIG_SYSCTL
559 unregister_sysctl_table(nf_ct_ipv6_sysctl_header);
560 cleanup_localinops:
561#endif
562 nf_unregister_hook(&ipv6_conntrack_local_in_ops);
563 cleanup_inoutandlocalops:
564 nf_unregister_hook(&ipv6_conntrack_out_ops);
565 cleanup_inandlocalops:
566 nf_unregister_hook(&ipv6_conntrack_local_out_ops);
567 cleanup_inops:
568 nf_unregister_hook(&ipv6_conntrack_in_ops);
569 cleanup_defraglocalops:
570 nf_unregister_hook(&ipv6_conntrack_defrag_local_out_ops);
571 cleanup_defragops:
572 nf_unregister_hook(&ipv6_conntrack_defrag_ops);
573 cleanup_ipv6:
574 nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
575 cleanup_icmpv6:
576 nf_conntrack_protocol_unregister(&nf_conntrack_protocol_icmpv6);
577 cleanup_udp:
578 nf_conntrack_protocol_unregister(&nf_conntrack_protocol_udp6);
579 cleanup_tcp:
580 nf_conntrack_protocol_unregister(&nf_conntrack_protocol_tcp6);
581 cleanup_frag6:
582 nf_ct_frag6_cleanup();
583 cleanup_nothing:
584 return ret;
585}
586
587MODULE_LICENSE("GPL");
588MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
589
590static int __init init(void)
591{
2e4e6a17 592 need_conntrack();
9fb9cbb1
YK
593 return init_or_cleanup(1);
594}
595
596static void __exit fini(void)
597{
598 init_or_cleanup(0);
599}
600
601module_init(init);
602module_exit(fini);