diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/netfilter/Kconfig | 10 | ||||
-rw-r--r-- | net/ipv6/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_REJECT.c | 284 |
3 files changed, 295 insertions, 0 deletions
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index cd1551983c63..8a10c2d0d154 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig | |||
@@ -199,6 +199,16 @@ config IP6_NF_TARGET_LOG | |||
199 | 199 | ||
200 | To compile it as a module, choose M here. If unsure, say N. | 200 | To compile it as a module, choose M here. If unsure, say N. |
201 | 201 | ||
202 | config IP6_NF_TARGET_REJECT | ||
203 | tristate "REJECT target support" | ||
204 | depends on IP6_NF_FILTER | ||
205 | help | ||
206 | The REJECT target allows a filtering rule to specify that an ICMPv6 | ||
207 | error should be issued in response to an incoming packet, rather | ||
208 | than silently being dropped. | ||
209 | |||
210 | To compile it as a module, choose M here. If unsure, say N. | ||
211 | |||
202 | # if [ "$CONFIG_IP6_NF_FILTER" != "n" ]; then | 212 | # if [ "$CONFIG_IP6_NF_FILTER" != "n" ]; then |
203 | # dep_tristate ' REJECT target support' CONFIG_IP6_NF_TARGET_REJECT $CONFIG_IP6_NF_FILTER | 213 | # dep_tristate ' REJECT target support' CONFIG_IP6_NF_TARGET_REJECT $CONFIG_IP6_NF_FILTER |
204 | # if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then | 214 | # if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then |
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 847651dbcd2a..70f6ba610102 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile | |||
@@ -24,4 +24,5 @@ obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o | |||
24 | obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o | 24 | obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o |
25 | obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o | 25 | obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o |
26 | obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o | 26 | obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o |
27 | obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o | ||
27 | obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += ip6t_NFQUEUE.o | 28 | obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += ip6t_NFQUEUE.o |
diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c new file mode 100644 index 000000000000..14316c3ebde4 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_REJECT.c | |||
@@ -0,0 +1,284 @@ | |||
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 | |||
18 | #include <linux/config.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/skbuff.h> | ||
21 | #include <linux/icmpv6.h> | ||
22 | #include <linux/netdevice.h> | ||
23 | #include <net/ipv6.h> | ||
24 | #include <net/tcp.h> | ||
25 | #include <net/icmp.h> | ||
26 | #include <net/ip6_checksum.h> | ||
27 | #include <net/ip6_fib.h> | ||
28 | #include <net/ip6_route.h> | ||
29 | #include <net/flow.h> | ||
30 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
31 | #include <linux/netfilter_ipv6/ip6t_REJECT.h> | ||
32 | |||
33 | MODULE_AUTHOR("Yasuyuki KOZAKAI <yasuyuki.kozakai@toshiba.co.jp>"); | ||
34 | MODULE_DESCRIPTION("IP6 tables REJECT target module"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | |||
37 | #if 0 | ||
38 | #define DEBUGP printk | ||
39 | #else | ||
40 | #define DEBUGP(format, args...) | ||
41 | #endif | ||
42 | |||
43 | /* Send RST reply */ | ||
44 | static void send_reset(struct sk_buff *oldskb) | ||
45 | { | ||
46 | struct sk_buff *nskb; | ||
47 | struct tcphdr otcph, *tcph; | ||
48 | unsigned int otcplen, hh_len; | ||
49 | int tcphoff, needs_ack; | ||
50 | struct ipv6hdr *oip6h = oldskb->nh.ipv6h, *ip6h; | ||
51 | struct dst_entry *dst = NULL; | ||
52 | u8 proto; | ||
53 | struct flowi fl; | ||
54 | |||
55 | if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || | ||
56 | (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { | ||
57 | DEBUGP("ip6t_REJECT: addr is not unicast.\n"); | ||
58 | return; | ||
59 | } | ||
60 | |||
61 | proto = oip6h->nexthdr; | ||
62 | tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto); | ||
63 | |||
64 | if ((tcphoff < 0) || (tcphoff > oldskb->len)) { | ||
65 | DEBUGP("ip6t_REJECT: Can't get TCP header.\n"); | ||
66 | return; | ||
67 | } | ||
68 | |||
69 | otcplen = oldskb->len - tcphoff; | ||
70 | |||
71 | /* IP header checks: fragment, too short. */ | ||
72 | if ((proto != IPPROTO_TCP) || (otcplen < sizeof(struct tcphdr))) { | ||
73 | DEBUGP("ip6t_REJECT: proto(%d) != IPPROTO_TCP, or too short. otcplen = %d\n", | ||
74 | proto, otcplen); | ||
75 | return; | ||
76 | } | ||
77 | |||
78 | if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) | ||
79 | BUG(); | ||
80 | |||
81 | /* No RST for RST. */ | ||
82 | if (otcph.rst) { | ||
83 | DEBUGP("ip6t_REJECT: RST is set\n"); | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | /* Check checksum. */ | ||
88 | if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP, | ||
89 | skb_checksum(oldskb, tcphoff, otcplen, 0))) { | ||
90 | DEBUGP("ip6t_REJECT: TCP checksum is invalid\n"); | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | memset(&fl, 0, sizeof(fl)); | ||
95 | fl.proto = IPPROTO_TCP; | ||
96 | ipv6_addr_copy(&fl.fl6_src, &oip6h->daddr); | ||
97 | ipv6_addr_copy(&fl.fl6_dst, &oip6h->saddr); | ||
98 | fl.fl_ip_sport = otcph.dest; | ||
99 | fl.fl_ip_dport = otcph.source; | ||
100 | dst = ip6_route_output(NULL, &fl); | ||
101 | if (dst == NULL) | ||
102 | return; | ||
103 | if (dst->error || | ||
104 | xfrm_lookup(&dst, &fl, NULL, 0)) { | ||
105 | dst_release(dst); | ||
106 | return; | ||
107 | } | ||
108 | |||
109 | hh_len = (dst->dev->hard_header_len + 15)&~15; | ||
110 | nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) | ||
111 | + sizeof(struct tcphdr) + dst->trailer_len, | ||
112 | GFP_ATOMIC); | ||
113 | |||
114 | if (!nskb) { | ||
115 | if (net_ratelimit()) | ||
116 | printk("ip6t_REJECT: Can't alloc skb\n"); | ||
117 | dst_release(dst); | ||
118 | return; | ||
119 | } | ||
120 | |||
121 | nskb->dst = dst; | ||
122 | |||
123 | skb_reserve(nskb, hh_len + dst->header_len); | ||
124 | |||
125 | ip6h = nskb->nh.ipv6h = (struct ipv6hdr *) | ||
126 | skb_put(nskb, sizeof(struct ipv6hdr)); | ||
127 | ip6h->version = 6; | ||
128 | ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT); | ||
129 | ip6h->nexthdr = IPPROTO_TCP; | ||
130 | ip6h->payload_len = htons(sizeof(struct tcphdr)); | ||
131 | ipv6_addr_copy(&ip6h->saddr, &oip6h->daddr); | ||
132 | ipv6_addr_copy(&ip6h->daddr, &oip6h->saddr); | ||
133 | |||
134 | tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); | ||
135 | /* Truncate to length (no data) */ | ||
136 | tcph->doff = sizeof(struct tcphdr)/4; | ||
137 | tcph->source = otcph.dest; | ||
138 | tcph->dest = otcph.source; | ||
139 | |||
140 | if (otcph.ack) { | ||
141 | needs_ack = 0; | ||
142 | tcph->seq = otcph.ack_seq; | ||
143 | tcph->ack_seq = 0; | ||
144 | } else { | ||
145 | needs_ack = 1; | ||
146 | tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin | ||
147 | + otcplen - (otcph.doff<<2)); | ||
148 | tcph->seq = 0; | ||
149 | } | ||
150 | |||
151 | /* Reset flags */ | ||
152 | ((u_int8_t *)tcph)[13] = 0; | ||
153 | tcph->rst = 1; | ||
154 | tcph->ack = needs_ack; | ||
155 | tcph->window = 0; | ||
156 | tcph->urg_ptr = 0; | ||
157 | tcph->check = 0; | ||
158 | |||
159 | /* Adjust TCP checksum */ | ||
160 | tcph->check = csum_ipv6_magic(&nskb->nh.ipv6h->saddr, | ||
161 | &nskb->nh.ipv6h->daddr, | ||
162 | sizeof(struct tcphdr), IPPROTO_TCP, | ||
163 | csum_partial((char *)tcph, | ||
164 | sizeof(struct tcphdr), 0)); | ||
165 | |||
166 | NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, nskb, NULL, nskb->dst->dev, | ||
167 | dst_output); | ||
168 | } | ||
169 | |||
170 | static inline void | ||
171 | send_unreach(struct sk_buff *skb_in, unsigned char code, unsigned int hooknum) | ||
172 | { | ||
173 | if (hooknum == NF_IP6_LOCAL_OUT && skb_in->dev == NULL) | ||
174 | skb_in->dev = &loopback_dev; | ||
175 | |||
176 | icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0, NULL); | ||
177 | } | ||
178 | |||
179 | static unsigned int reject6_target(struct sk_buff **pskb, | ||
180 | const struct net_device *in, | ||
181 | const struct net_device *out, | ||
182 | unsigned int hooknum, | ||
183 | const void *targinfo, | ||
184 | void *userinfo) | ||
185 | { | ||
186 | const struct ip6t_reject_info *reject = targinfo; | ||
187 | |||
188 | DEBUGP(KERN_DEBUG "%s: medium point\n", __FUNCTION__); | ||
189 | /* WARNING: This code causes reentry within ip6tables. | ||
190 | This means that the ip6tables jump stack is now crap. We | ||
191 | must return an absolute verdict. --RR */ | ||
192 | switch (reject->with) { | ||
193 | case IP6T_ICMP6_NO_ROUTE: | ||
194 | send_unreach(*pskb, ICMPV6_NOROUTE, hooknum); | ||
195 | break; | ||
196 | case IP6T_ICMP6_ADM_PROHIBITED: | ||
197 | send_unreach(*pskb, ICMPV6_ADM_PROHIBITED, hooknum); | ||
198 | break; | ||
199 | case IP6T_ICMP6_NOT_NEIGHBOUR: | ||
200 | send_unreach(*pskb, ICMPV6_NOT_NEIGHBOUR, hooknum); | ||
201 | break; | ||
202 | case IP6T_ICMP6_ADDR_UNREACH: | ||
203 | send_unreach(*pskb, ICMPV6_ADDR_UNREACH, hooknum); | ||
204 | break; | ||
205 | case IP6T_ICMP6_PORT_UNREACH: | ||
206 | send_unreach(*pskb, ICMPV6_PORT_UNREACH, hooknum); | ||
207 | break; | ||
208 | case IP6T_ICMP6_ECHOREPLY: | ||
209 | /* Do nothing */ | ||
210 | break; | ||
211 | case IP6T_TCP_RESET: | ||
212 | send_reset(*pskb); | ||
213 | break; | ||
214 | default: | ||
215 | if (net_ratelimit()) | ||
216 | printk(KERN_WARNING "ip6t_REJECT: case %u not handled yet\n", reject->with); | ||
217 | break; | ||
218 | } | ||
219 | |||
220 | return NF_DROP; | ||
221 | } | ||
222 | |||
223 | static int check(const char *tablename, | ||
224 | const struct ip6t_entry *e, | ||
225 | void *targinfo, | ||
226 | unsigned int targinfosize, | ||
227 | unsigned int hook_mask) | ||
228 | { | ||
229 | const struct ip6t_reject_info *rejinfo = targinfo; | ||
230 | |||
231 | if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_reject_info))) { | ||
232 | DEBUGP("ip6t_REJECT: targinfosize %u != 0\n", targinfosize); | ||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | /* Only allow these for packet filtering. */ | ||
237 | if (strcmp(tablename, "filter") != 0) { | ||
238 | DEBUGP("ip6t_REJECT: bad table `%s'.\n", tablename); | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | if ((hook_mask & ~((1 << NF_IP6_LOCAL_IN) | ||
243 | | (1 << NF_IP6_FORWARD) | ||
244 | | (1 << NF_IP6_LOCAL_OUT))) != 0) { | ||
245 | DEBUGP("ip6t_REJECT: bad hook mask %X\n", hook_mask); | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | if (rejinfo->with == IP6T_ICMP6_ECHOREPLY) { | ||
250 | printk("ip6t_REJECT: ECHOREPLY is not supported.\n"); | ||
251 | return 0; | ||
252 | } else if (rejinfo->with == IP6T_TCP_RESET) { | ||
253 | /* Must specify that it's a TCP packet */ | ||
254 | if (e->ipv6.proto != IPPROTO_TCP | ||
255 | || (e->ipv6.invflags & IP6T_INV_PROTO)) { | ||
256 | DEBUGP("ip6t_REJECT: TCP_RESET illegal for non-tcp\n"); | ||
257 | return 0; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | return 1; | ||
262 | } | ||
263 | |||
264 | static struct ip6t_target ip6t_reject_reg = { | ||
265 | .name = "REJECT", | ||
266 | .target = reject6_target, | ||
267 | .checkentry = check, | ||
268 | .me = THIS_MODULE | ||
269 | }; | ||
270 | |||
271 | static int __init init(void) | ||
272 | { | ||
273 | if (ip6t_register_target(&ip6t_reject_reg)) | ||
274 | return -EINVAL; | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static void __exit fini(void) | ||
279 | { | ||
280 | ip6t_unregister_target(&ip6t_reject_reg); | ||
281 | } | ||
282 | |||
283 | module_init(init); | ||
284 | module_exit(fini); | ||