• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/net/ipv6/netfilter/
1/*
2 * IP6 tables REJECT target module
3 * Linux INET6 implementation
4 *
5 * Copyright (C)2003 USAGI/WIDE Project
6 *
7 * Authors:
8 *	Yasuyuki Kozakai	<yasuyuki.kozakai@toshiba.co.jp>
9 *
10 * Based on net/ipv4/netfilter/ipt_REJECT.c
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
16 */
17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19#include <linux/gfp.h>
20#include <linux/module.h>
21#include <linux/skbuff.h>
22#include <linux/icmpv6.h>
23#include <linux/netdevice.h>
24#include <net/ipv6.h>
25#include <net/tcp.h>
26#include <net/icmp.h>
27#include <net/ip6_checksum.h>
28#include <net/ip6_fib.h>
29#include <net/ip6_route.h>
30#include <net/flow.h>
31#include <linux/netfilter/x_tables.h>
32#include <linux/netfilter_ipv6/ip6_tables.h>
33#include <linux/netfilter_ipv6/ip6t_REJECT.h>
34
35MODULE_AUTHOR("Yasuyuki KOZAKAI <yasuyuki.kozakai@toshiba.co.jp>");
36MODULE_DESCRIPTION("Xtables: packet \"rejection\" target for IPv6");
37MODULE_LICENSE("GPL");
38
39/* Send RST reply */
40static void send_reset(struct net *net, struct sk_buff *oldskb)
41{
42	struct sk_buff *nskb;
43	struct tcphdr otcph, *tcph;
44	unsigned int otcplen, hh_len;
45	int tcphoff, needs_ack;
46	const struct ipv6hdr *oip6h = ipv6_hdr(oldskb);
47	struct ipv6hdr *ip6h;
48	struct dst_entry *dst = NULL;
49	u8 proto;
50	struct flowi fl;
51
52	if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) ||
53	    (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) {
54		pr_debug("addr is not unicast.\n");
55		return;
56	}
57
58	proto = oip6h->nexthdr;
59	tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto);
60
61	if ((tcphoff < 0) || (tcphoff > oldskb->len)) {
62		pr_debug("Cannot get TCP header.\n");
63		return;
64	}
65
66	otcplen = oldskb->len - tcphoff;
67
68	/* IP header checks: fragment, too short. */
69	if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) {
70		pr_debug("proto(%d) != IPPROTO_TCP, "
71			 "or too short. otcplen = %d\n",
72			 proto, otcplen);
73		return;
74	}
75
76	if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr)))
77		BUG();
78
79	/* No RST for RST. */
80	if (otcph.rst) {
81		pr_debug("RST is set\n");
82		return;
83	}
84
85	/* Check checksum. */
86	if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP,
87			    skb_checksum(oldskb, tcphoff, otcplen, 0))) {
88		pr_debug("TCP checksum is invalid\n");
89		return;
90	}
91
92	memset(&fl, 0, sizeof(fl));
93	fl.proto = IPPROTO_TCP;
94	ipv6_addr_copy(&fl.fl6_src, &oip6h->daddr);
95	ipv6_addr_copy(&fl.fl6_dst, &oip6h->saddr);
96	fl.fl_ip_sport = otcph.dest;
97	fl.fl_ip_dport = otcph.source;
98	security_skb_classify_flow(oldskb, &fl);
99	dst = ip6_route_output(net, NULL, &fl);
100	if (dst == NULL || dst->error) {
101		dst_release(dst);
102		return;
103	}
104	if (xfrm_lookup(net, &dst, &fl, NULL, 0))
105		return;
106
107	hh_len = (dst->dev->hard_header_len + 15)&~15;
108	nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr)
109			 + sizeof(struct tcphdr) + dst->trailer_len,
110			 GFP_ATOMIC);
111
112	if (!nskb) {
113		if (net_ratelimit())
114			pr_debug("cannot alloc skb\n");
115		dst_release(dst);
116		return;
117	}
118
119	skb_dst_set(nskb, dst);
120
121	skb_reserve(nskb, hh_len + dst->header_len);
122
123	skb_put(nskb, sizeof(struct ipv6hdr));
124	skb_reset_network_header(nskb);
125	ip6h = ipv6_hdr(nskb);
126	ip6h->version = 6;
127	ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT);
128	ip6h->nexthdr = IPPROTO_TCP;
129	ipv6_addr_copy(&ip6h->saddr, &oip6h->daddr);
130	ipv6_addr_copy(&ip6h->daddr, &oip6h->saddr);
131
132	tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));
133	/* Truncate to length (no data) */
134	tcph->doff = sizeof(struct tcphdr)/4;
135	tcph->source = otcph.dest;
136	tcph->dest = otcph.source;
137
138	if (otcph.ack) {
139		needs_ack = 0;
140		tcph->seq = otcph.ack_seq;
141		tcph->ack_seq = 0;
142	} else {
143		needs_ack = 1;
144		tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin
145				      + otcplen - (otcph.doff<<2));
146		tcph->seq = 0;
147	}
148
149	/* Reset flags */
150	((u_int8_t *)tcph)[13] = 0;
151	tcph->rst = 1;
152	tcph->ack = needs_ack;
153	tcph->window = 0;
154	tcph->urg_ptr = 0;
155	tcph->check = 0;
156
157	/* Adjust TCP checksum */
158	tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr,
159				      &ipv6_hdr(nskb)->daddr,
160				      sizeof(struct tcphdr), IPPROTO_TCP,
161				      csum_partial(tcph,
162						   sizeof(struct tcphdr), 0));
163
164	nf_ct_attach(nskb, oldskb);
165
166	ip6_local_out(nskb);
167}
168
169static inline void
170send_unreach(struct net *net, struct sk_buff *skb_in, unsigned char code,
171	     unsigned int hooknum)
172{
173	if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
174		skb_in->dev = net->loopback_dev;
175
176	icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0);
177}
178
179static unsigned int
180reject_tg6(struct sk_buff *skb, const struct xt_action_param *par)
181{
182	const struct ip6t_reject_info *reject = par->targinfo;
183	struct net *net = dev_net((par->in != NULL) ? par->in : par->out);
184
185	pr_debug("%s: medium point\n", __func__);
186	switch (reject->with) {
187	case IP6T_ICMP6_NO_ROUTE:
188		send_unreach(net, skb, ICMPV6_NOROUTE, par->hooknum);
189		break;
190	case IP6T_ICMP6_ADM_PROHIBITED:
191		send_unreach(net, skb, ICMPV6_ADM_PROHIBITED, par->hooknum);
192		break;
193	case IP6T_ICMP6_NOT_NEIGHBOUR:
194		send_unreach(net, skb, ICMPV6_NOT_NEIGHBOUR, par->hooknum);
195		break;
196	case IP6T_ICMP6_ADDR_UNREACH:
197		send_unreach(net, skb, ICMPV6_ADDR_UNREACH, par->hooknum);
198		break;
199	case IP6T_ICMP6_PORT_UNREACH:
200		send_unreach(net, skb, ICMPV6_PORT_UNREACH, par->hooknum);
201		break;
202	case IP6T_ICMP6_ECHOREPLY:
203		/* Do nothing */
204		break;
205	case IP6T_TCP_RESET:
206		send_reset(net, skb);
207		break;
208	default:
209		if (net_ratelimit())
210			pr_info("case %u not handled yet\n", reject->with);
211		break;
212	}
213
214	return NF_DROP;
215}
216
217static int reject_tg6_check(const struct xt_tgchk_param *par)
218{
219	const struct ip6t_reject_info *rejinfo = par->targinfo;
220	const struct ip6t_entry *e = par->entryinfo;
221
222	if (rejinfo->with == IP6T_ICMP6_ECHOREPLY) {
223		pr_info("ECHOREPLY is not supported.\n");
224		return -EINVAL;
225	} else if (rejinfo->with == IP6T_TCP_RESET) {
226		/* Must specify that it's a TCP packet */
227		if (e->ipv6.proto != IPPROTO_TCP ||
228		    (e->ipv6.invflags & XT_INV_PROTO)) {
229			pr_info("TCP_RESET illegal for non-tcp\n");
230			return -EINVAL;
231		}
232	}
233	return 0;
234}
235
236static struct xt_target reject_tg6_reg __read_mostly = {
237	.name		= "REJECT",
238	.family		= NFPROTO_IPV6,
239	.target		= reject_tg6,
240	.targetsize	= sizeof(struct ip6t_reject_info),
241	.table		= "filter",
242	.hooks		= (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) |
243			  (1 << NF_INET_LOCAL_OUT),
244	.checkentry	= reject_tg6_check,
245	.me		= THIS_MODULE
246};
247
248static int __init reject_tg6_init(void)
249{
250	return xt_register_target(&reject_tg6_reg);
251}
252
253static void __exit reject_tg6_exit(void)
254{
255	xt_unregister_target(&reject_tg6_reg);
256}
257
258module_init(reject_tg6_init);
259module_exit(reject_tg6_exit);
260