• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/net/ipv6/netfilter/
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
12#include <linux/types.h>
13#include <linux/ipv6.h>
14#include <linux/in6.h>
15#include <linux/netfilter.h>
16#include <linux/module.h>
17#include <linux/skbuff.h>
18#include <linux/icmp.h>
19#include <linux/sysctl.h>
20#include <net/ipv6.h>
21#include <net/inet_frag.h>
22
23#include <linux/netfilter_bridge.h>
24#include <linux/netfilter_ipv6.h>
25#include <net/netfilter/nf_conntrack.h>
26#include <net/netfilter/nf_conntrack_helper.h>
27#include <net/netfilter/nf_conntrack_l4proto.h>
28#include <net/netfilter/nf_conntrack_l3proto.h>
29#include <net/netfilter/nf_conntrack_core.h>
30#include <net/netfilter/nf_conntrack_zones.h>
31#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
32#include <net/netfilter/nf_log.h>
33
34#ifdef HNDCTF
35extern void ip_conntrack_ipct_add(struct sk_buff *skb, u_int32_t hooknum,
36	struct nf_conn *ct, enum ip_conntrack_info ci,
37	struct nf_conntrack_tuple *manip);
38#endif /* HNDCTF */
39
40static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
41			      struct nf_conntrack_tuple *tuple)
42{
43	const u_int32_t *ap;
44	u_int32_t _addrs[8];
45
46	ap = skb_header_pointer(skb, nhoff + offsetof(struct ipv6hdr, saddr),
47				sizeof(_addrs), _addrs);
48	if (ap == NULL)
49		return false;
50
51	memcpy(tuple->src.u3.ip6, ap, sizeof(tuple->src.u3.ip6));
52	memcpy(tuple->dst.u3.ip6, ap + 4, sizeof(tuple->dst.u3.ip6));
53
54	return true;
55}
56
57static bool ipv6_invert_tuple(struct nf_conntrack_tuple *tuple,
58			      const struct nf_conntrack_tuple *orig)
59{
60	memcpy(tuple->src.u3.ip6, orig->dst.u3.ip6, sizeof(tuple->src.u3.ip6));
61	memcpy(tuple->dst.u3.ip6, orig->src.u3.ip6, sizeof(tuple->dst.u3.ip6));
62
63	return true;
64}
65
66static int ipv6_print_tuple(struct seq_file *s,
67			    const struct nf_conntrack_tuple *tuple)
68{
69	return seq_printf(s, "src=%pI6 dst=%pI6 ",
70			  tuple->src.u3.ip6, tuple->dst.u3.ip6);
71}
72
73/*
74 * Based on ipv6_skip_exthdr() in net/ipv6/exthdr.c
75 *
76 * This function parses (probably truncated) exthdr set "hdr"
77 * of length "len". "nexthdrp" initially points to some place,
78 * where type of the first header can be found.
79 *
80 * It skips all well-known exthdrs, and returns pointer to the start
81 * of unparsable area i.e. the first header with unknown type.
82 * if success, *nexthdr is updated by type/protocol of this header.
83 *
84 * NOTES: - it may return pointer pointing beyond end of packet,
85 *          if the last recognized header is truncated in the middle.
86 *        - if packet is truncated, so that all parsed headers are skipped,
87 *          it returns -1.
88 *        - if packet is fragmented, return pointer of the fragment header.
89 *        - ESP is unparsable for now and considered like
90 *          normal payload protocol.
91 *        - Note also special handling of AUTH header. Thanks to IPsec wizards.
92 */
93
94static int nf_ct_ipv6_skip_exthdr(const struct sk_buff *skb, int start,
95				  u8 *nexthdrp, int len)
96{
97	u8 nexthdr = *nexthdrp;
98
99	while (ipv6_ext_hdr(nexthdr)) {
100		struct ipv6_opt_hdr hdr;
101		int hdrlen;
102
103		if (len < (int)sizeof(struct ipv6_opt_hdr))
104			return -1;
105		if (nexthdr == NEXTHDR_NONE)
106			break;
107		if (nexthdr == NEXTHDR_FRAGMENT)
108			break;
109		if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
110			BUG();
111		if (nexthdr == NEXTHDR_AUTH)
112			hdrlen = (hdr.hdrlen+2)<<2;
113		else
114			hdrlen = ipv6_optlen(&hdr);
115
116		nexthdr = hdr.nexthdr;
117		len -= hdrlen;
118		start += hdrlen;
119	}
120
121	*nexthdrp = nexthdr;
122	return start;
123}
124
125static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
126			    unsigned int *dataoff, u_int8_t *protonum)
127{
128	unsigned int extoff = nhoff + sizeof(struct ipv6hdr);
129	unsigned char pnum;
130	int protoff;
131
132	if (skb_copy_bits(skb, nhoff + offsetof(struct ipv6hdr, nexthdr),
133			  &pnum, sizeof(pnum)) != 0) {
134		pr_debug("ip6_conntrack_core: can't get nexthdr\n");
135		return -NF_ACCEPT;
136	}
137	protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum, skb->len - extoff);
138	/*
139	 * (protoff == skb->len) mean that the packet doesn't have no data
140	 * except of IPv6 & ext headers. but it's tracked anyway. - YK
141	 */
142	if ((protoff < 0) || (protoff > skb->len)) {
143		pr_debug("ip6_conntrack_core: can't find proto in pkt\n");
144		return -NF_ACCEPT;
145	}
146
147	*dataoff = protoff;
148	*protonum = pnum;
149	return NF_ACCEPT;
150}
151
152static unsigned int ipv6_confirm(unsigned int hooknum,
153				 struct sk_buff *skb,
154				 const struct net_device *in,
155				 const struct net_device *out,
156				 int (*okfn)(struct sk_buff *))
157{
158	struct nf_conn *ct;
159	const struct nf_conn_help *help;
160	const struct nf_conntrack_helper *helper;
161	enum ip_conntrack_info ctinfo;
162	unsigned int ret, protoff;
163	unsigned int extoff = (u8 *)(ipv6_hdr(skb) + 1) - skb->data;
164	unsigned char pnum = ipv6_hdr(skb)->nexthdr;
165
166
167	/* This is where we call the helper: as the packet goes out. */
168	ct = nf_ct_get(skb, &ctinfo);
169	if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)
170		goto out;
171
172	help = nfct_help(ct);
173	if (!help)
174		goto out;
175	/* rcu_read_lock()ed by nf_hook_slow */
176	helper = rcu_dereference(help->helper);
177	if (!helper)
178		goto out;
179
180	protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum,
181					 skb->len - extoff);
182	if (protoff > skb->len || pnum == NEXTHDR_FRAGMENT) {
183		pr_debug("proto header not found\n");
184		return NF_ACCEPT;
185	}
186
187	ret = helper->help(skb, protoff, ct, ctinfo);
188	if (ret != NF_ACCEPT) {
189		nf_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL,
190			      "nf_ct_%s: dropping packet", helper->name);
191		return ret;
192	}
193out:
194	/* We've seen it coming out the other side: confirm it */
195	return nf_conntrack_confirm(skb);
196}
197
198static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum,
199						struct sk_buff *skb)
200{
201	u16 zone = NF_CT_DEFAULT_ZONE;
202
203	if (skb->nfct)
204		zone = nf_ct_zone((struct nf_conn *)skb->nfct);
205
206#ifdef CONFIG_BRIDGE_NETFILTER
207	if (skb->nf_bridge &&
208	    skb->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)
209		return IP6_DEFRAG_CONNTRACK_BRIDGE_IN + zone;
210#endif
211	if (hooknum == NF_INET_PRE_ROUTING)
212		return IP6_DEFRAG_CONNTRACK_IN + zone;
213	else
214		return IP6_DEFRAG_CONNTRACK_OUT + zone;
215
216}
217
218static unsigned int ipv6_defrag(unsigned int hooknum,
219				struct sk_buff *skb,
220				const struct net_device *in,
221				const struct net_device *out,
222				int (*okfn)(struct sk_buff *))
223{
224	struct sk_buff *reasm;
225
226	/* Previously seen (loopback)?  */
227	if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct))
228		return NF_ACCEPT;
229
230	reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb));
231	/* queued */
232	if (reasm == NULL)
233		return NF_STOLEN;
234
235	/* error occured or not fragmented */
236	if (reasm == skb)
237		return NF_ACCEPT;
238
239	nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in,
240			   (struct net_device *)out, okfn);
241
242	return NF_STOLEN;
243}
244
245static unsigned int __ipv6_conntrack_in(struct net *net,
246					unsigned int hooknum,
247					struct sk_buff *skb,
248					int (*okfn)(struct sk_buff *))
249{
250	struct sk_buff *reasm = skb->nfct_reasm;
251	unsigned int ret;
252
253	/* This packet is fragmented and has reassembled packet. */
254	if (reasm) {
255		/* Reassembled packet isn't parsed yet ? */
256		if (!reasm->nfct) {
257			ret = nf_conntrack_in(net, PF_INET6, hooknum, reasm);
258			if (ret != NF_ACCEPT)
259				return ret;
260		}
261		nf_conntrack_get(reasm->nfct);
262		skb->nfct = reasm->nfct;
263		skb->nfctinfo = reasm->nfctinfo;
264		return NF_ACCEPT;
265	}
266
267	ret = nf_conntrack_in(net, PF_INET6, hooknum, skb);
268
269#if defined(HNDCTF)
270	if (ret == NF_ACCEPT) {
271		struct nf_conn *ct;
272		enum ip_conntrack_info ctinfo;
273
274		ct = nf_ct_get(skb, &ctinfo);
275		ip_conntrack_ipct_add(skb, hooknum, ct, ctinfo, NULL);
276	}
277#endif /* HNDCTF */
278
279	return ret;
280}
281
282static unsigned int ipv6_conntrack_in(unsigned int hooknum,
283				      struct sk_buff *skb,
284				      const struct net_device *in,
285				      const struct net_device *out,
286				      int (*okfn)(struct sk_buff *))
287{
288	return __ipv6_conntrack_in(dev_net(in), hooknum, skb, okfn);
289}
290
291static unsigned int ipv6_conntrack_local(unsigned int hooknum,
292					 struct sk_buff *skb,
293					 const struct net_device *in,
294					 const struct net_device *out,
295					 int (*okfn)(struct sk_buff *))
296{
297	/* root is playing with raw sockets. */
298	if (skb->len < sizeof(struct ipv6hdr)) {
299		if (net_ratelimit())
300			pr_notice("ipv6_conntrack_local: packet too short\n");
301		return NF_ACCEPT;
302	}
303	return __ipv6_conntrack_in(dev_net(out), hooknum, skb, okfn);
304}
305
306static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
307	{
308		.hook		= ipv6_defrag,
309		.owner		= THIS_MODULE,
310		.pf		= NFPROTO_IPV6,
311		.hooknum	= NF_INET_PRE_ROUTING,
312		.priority	= NF_IP6_PRI_CONNTRACK_DEFRAG,
313	},
314	{
315		.hook		= ipv6_conntrack_in,
316		.owner		= THIS_MODULE,
317		.pf		= NFPROTO_IPV6,
318		.hooknum	= NF_INET_PRE_ROUTING,
319		.priority	= NF_IP6_PRI_CONNTRACK,
320	},
321	{
322		.hook		= ipv6_conntrack_local,
323		.owner		= THIS_MODULE,
324		.pf		= NFPROTO_IPV6,
325		.hooknum	= NF_INET_LOCAL_OUT,
326		.priority	= NF_IP6_PRI_CONNTRACK,
327	},
328	{
329		.hook		= ipv6_defrag,
330		.owner		= THIS_MODULE,
331		.pf		= NFPROTO_IPV6,
332		.hooknum	= NF_INET_LOCAL_OUT,
333		.priority	= NF_IP6_PRI_CONNTRACK_DEFRAG,
334	},
335	{
336		.hook		= ipv6_confirm,
337		.owner		= THIS_MODULE,
338		.pf		= NFPROTO_IPV6,
339		.hooknum	= NF_INET_POST_ROUTING,
340		.priority	= NF_IP6_PRI_LAST,
341	},
342	{
343		.hook		= ipv6_confirm,
344		.owner		= THIS_MODULE,
345		.pf		= NFPROTO_IPV6,
346		.hooknum	= NF_INET_LOCAL_IN,
347		.priority	= NF_IP6_PRI_LAST-1,
348	},
349};
350
351#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
352
353#include <linux/netfilter/nfnetlink.h>
354#include <linux/netfilter/nfnetlink_conntrack.h>
355
356static int ipv6_tuple_to_nlattr(struct sk_buff *skb,
357				const struct nf_conntrack_tuple *tuple)
358{
359	NLA_PUT(skb, CTA_IP_V6_SRC, sizeof(u_int32_t) * 4,
360		&tuple->src.u3.ip6);
361	NLA_PUT(skb, CTA_IP_V6_DST, sizeof(u_int32_t) * 4,
362		&tuple->dst.u3.ip6);
363	return 0;
364
365nla_put_failure:
366	return -1;
367}
368
369static const struct nla_policy ipv6_nla_policy[CTA_IP_MAX+1] = {
370	[CTA_IP_V6_SRC]	= { .len = sizeof(u_int32_t)*4 },
371	[CTA_IP_V6_DST]	= { .len = sizeof(u_int32_t)*4 },
372};
373
374static int ipv6_nlattr_to_tuple(struct nlattr *tb[],
375				struct nf_conntrack_tuple *t)
376{
377	if (!tb[CTA_IP_V6_SRC] || !tb[CTA_IP_V6_DST])
378		return -EINVAL;
379
380	memcpy(&t->src.u3.ip6, nla_data(tb[CTA_IP_V6_SRC]),
381	       sizeof(u_int32_t) * 4);
382	memcpy(&t->dst.u3.ip6, nla_data(tb[CTA_IP_V6_DST]),
383	       sizeof(u_int32_t) * 4);
384
385	return 0;
386}
387
388static int ipv6_nlattr_tuple_size(void)
389{
390	return nla_policy_len(ipv6_nla_policy, CTA_IP_MAX + 1);
391}
392#endif
393
394struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
395	.l3proto		= PF_INET6,
396	.name			= "ipv6",
397	.pkt_to_tuple		= ipv6_pkt_to_tuple,
398	.invert_tuple		= ipv6_invert_tuple,
399	.print_tuple		= ipv6_print_tuple,
400	.get_l4proto		= ipv6_get_l4proto,
401#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
402	.tuple_to_nlattr	= ipv6_tuple_to_nlattr,
403	.nlattr_tuple_size	= ipv6_nlattr_tuple_size,
404	.nlattr_to_tuple	= ipv6_nlattr_to_tuple,
405	.nla_policy		= ipv6_nla_policy,
406#endif
407#ifdef CONFIG_SYSCTL
408	.ctl_table_path		= nf_net_netfilter_sysctl_path,
409	.ctl_table		= nf_ct_ipv6_sysctl_table,
410#endif
411	.me			= THIS_MODULE,
412};
413
414MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6));
415MODULE_LICENSE("GPL");
416MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
417
418static int __init nf_conntrack_l3proto_ipv6_init(void)
419{
420	int ret = 0;
421
422	need_conntrack();
423
424	ret = nf_ct_frag6_init();
425	if (ret < 0) {
426		pr_err("nf_conntrack_ipv6: can't initialize frag6.\n");
427		return ret;
428	}
429	ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp6);
430	if (ret < 0) {
431		pr_err("nf_conntrack_ipv6: can't register tcp.\n");
432		goto cleanup_frag6;
433	}
434
435	ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp6);
436	if (ret < 0) {
437		pr_err("nf_conntrack_ipv6: can't register udp.\n");
438		goto cleanup_tcp;
439	}
440
441	ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_icmpv6);
442	if (ret < 0) {
443		pr_err("nf_conntrack_ipv6: can't register icmpv6.\n");
444		goto cleanup_udp;
445	}
446
447	ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6);
448	if (ret < 0) {
449		pr_err("nf_conntrack_ipv6: can't register ipv6\n");
450		goto cleanup_icmpv6;
451	}
452
453	ret = nf_register_hooks(ipv6_conntrack_ops,
454				ARRAY_SIZE(ipv6_conntrack_ops));
455	if (ret < 0) {
456		pr_err("nf_conntrack_ipv6: can't register pre-routing defrag "
457		       "hook.\n");
458		goto cleanup_ipv6;
459	}
460	return ret;
461
462 cleanup_ipv6:
463	nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
464 cleanup_icmpv6:
465	nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
466 cleanup_udp:
467	nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6);
468 cleanup_tcp:
469	nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
470 cleanup_frag6:
471	nf_ct_frag6_cleanup();
472	return ret;
473}
474
475static void __exit nf_conntrack_l3proto_ipv6_fini(void)
476{
477	synchronize_net();
478	nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
479	nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
480	nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
481	nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6);
482	nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
483	nf_ct_frag6_cleanup();
484}
485
486module_init(nf_conntrack_l3proto_ipv6_init);
487module_exit(nf_conntrack_l3proto_ipv6_fini);
488