Import changeset
[linux-flexiantxendom0-3.2.10.git] / net / ipv4 / netfilter / ipt_REJECT.c
1 /*
2  * This is a module which is used for rejecting packets.
3  * Added support for customized reject packets (Jozsef Kadlecsik).
4  */
5 #include <linux/config.h>
6 #include <linux/module.h>
7 #include <linux/skbuff.h>
8 #include <linux/ip.h>
9 #include <net/icmp.h>
10 #include <net/ip.h>
11 #include <net/tcp.h>
12 struct in_device;
13 #include <net/route.h>
14 #include <linux/netfilter_ipv4/ip_tables.h>
15 #include <linux/netfilter_ipv4/ipt_REJECT.h>
16
17 #if 0
18 #define DEBUGP printk
19 #else
20 #define DEBUGP(format, args...)
21 #endif
22
23 /* Send RST reply */
24 static void send_reset(struct sk_buff *oldskb, int local)
25 {
26         struct sk_buff *nskb;
27         struct tcphdr *otcph, *tcph;
28         struct rtable *rt;
29         unsigned int otcplen;
30         u_int16_t tmp;
31         int needs_ack;
32
33         /* IP header checks: fragment, too short. */
34         if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)
35             || oldskb->len < (oldskb->nh.iph->ihl<<2) + sizeof(struct tcphdr))
36                 return;
37
38         otcph = (struct tcphdr *)((u_int32_t*)oldskb->nh.iph + oldskb->nh.iph->ihl);
39         otcplen = oldskb->len - oldskb->nh.iph->ihl*4;
40
41         /* No RST for RST. */
42         if (otcph->rst)
43                 return;
44
45         /* Check checksum. */
46         if (tcp_v4_check(otcph, otcplen, oldskb->nh.iph->saddr,
47                          oldskb->nh.iph->daddr,
48                          csum_partial((char *)otcph, otcplen, 0)) != 0)
49                 return;
50
51         /* Copy skb (even if skb is about to be dropped, we can't just
52            clone it because there may be other things, such as tcpdump,
53            interested in it) */
54         nskb = skb_copy(oldskb, GFP_ATOMIC);
55         if (!nskb)
56                 return;
57
58         /* This packet will not be the same as the other: clear nf fields */
59         nf_conntrack_put(nskb->nfct);
60         nskb->nfct = NULL;
61         nskb->nfcache = 0;
62 #ifdef CONFIG_NETFILTER_DEBUG
63         nskb->nf_debug = 0;
64 #endif
65
66         tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
67
68         /* Swap source and dest */
69         nskb->nh.iph->daddr = xchg(&nskb->nh.iph->saddr, nskb->nh.iph->daddr);
70         tmp = tcph->source;
71         tcph->source = tcph->dest;
72         tcph->dest = tmp;
73
74         /* Truncate to length (no data) */
75         tcph->doff = sizeof(struct tcphdr)/4;
76         skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
77         nskb->nh.iph->tot_len = htons(nskb->len);
78
79         if (tcph->ack) {
80                 needs_ack = 0;
81                 tcph->seq = otcph->ack_seq;
82                 tcph->ack_seq = 0;
83         } else {
84                 needs_ack = 1;
85                 tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin
86                                       + otcplen - (otcph->doff<<2));
87                 tcph->seq = 0;
88         }
89
90         /* Reset flags */
91         ((u_int8_t *)tcph)[13] = 0;
92         tcph->rst = 1;
93         tcph->ack = needs_ack;
94
95         tcph->window = 0;
96         tcph->urg_ptr = 0;
97
98         /* Adjust TCP checksum */
99         tcph->check = 0;
100         tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
101                                    nskb->nh.iph->saddr,
102                                    nskb->nh.iph->daddr,
103                                    csum_partial((char *)tcph,
104                                                 sizeof(struct tcphdr), 0));
105
106         /* Adjust IP TTL, DF */
107         nskb->nh.iph->ttl = MAXTTL;
108         /* Set DF, id = 0 */
109         nskb->nh.iph->frag_off = htons(IP_DF);
110         nskb->nh.iph->id = 0;
111
112         /* Adjust IP checksum */
113         nskb->nh.iph->check = 0;
114         nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, 
115                                            nskb->nh.iph->ihl);
116
117         /* Routing: if not headed for us, route won't like source */
118         if (ip_route_output(&rt, nskb->nh.iph->daddr,
119                             local ? nskb->nh.iph->saddr : 0,
120                             RT_TOS(nskb->nh.iph->tos) | RTO_CONN,
121                             0) != 0)
122                 goto free_nskb;
123
124         dst_release(nskb->dst);
125         nskb->dst = &rt->u.dst;
126
127         /* "Never happens" */
128         if (nskb->len > nskb->dst->pmtu)
129                 goto free_nskb;
130
131         NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
132                 ip_finish_output);
133         return;
134
135  free_nskb:
136         kfree_skb(nskb);
137 }
138
139 static unsigned int reject(struct sk_buff **pskb,
140                            unsigned int hooknum,
141                            const struct net_device *in,
142                            const struct net_device *out,
143                            const void *targinfo,
144                            void *userinfo)
145 {
146         const struct ipt_reject_info *reject = targinfo;
147
148         /* WARNING: This code causes reentry within iptables.
149            This means that the iptables jump stack is now crap.  We
150            must return an absolute verdict. --RR */
151         switch (reject->with) {
152         case IPT_ICMP_NET_UNREACHABLE:
153                 icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0);
154                 break;
155         case IPT_ICMP_HOST_UNREACHABLE:
156                 icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
157                 break;
158         case IPT_ICMP_PROT_UNREACHABLE:
159                 icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
160                 break;
161         case IPT_ICMP_PORT_UNREACHABLE:
162                 icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
163                 break;
164         case IPT_ICMP_NET_PROHIBITED:
165                 icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0);
166                 break;
167         case IPT_ICMP_HOST_PROHIBITED:
168                 icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0);
169                 break;
170         case IPT_ICMP_ECHOREPLY: {
171                 struct icmphdr *icmph  = (struct icmphdr *)
172                         ((u_int32_t *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl);
173                 unsigned int datalen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4;
174
175                 /* Not non-head frags, or truncated */
176                 if (((ntohs((*pskb)->nh.iph->frag_off) & IP_OFFSET) == 0)
177                     && datalen >= 4) {
178                         /* Usually I don't like cut & pasting code,
179                            but dammit, my party is starting in 45
180                            mins! --RR */
181                         struct icmp_bxm icmp_param;
182
183                         icmp_param.icmph=*icmph;
184                         icmp_param.icmph.type=ICMP_ECHOREPLY;
185                         icmp_param.data_ptr=(icmph+1);
186                         icmp_param.data_len=datalen;
187                         icmp_reply(&icmp_param, *pskb);
188                 }
189         }
190         break;
191         case IPT_TCP_RESET:
192                 send_reset(*pskb, hooknum == NF_IP_LOCAL_IN);
193                 break;
194         }
195
196         return NF_DROP;
197 }
198
199 static inline int find_ping_match(const struct ipt_entry_match *m)
200 {
201         const struct ipt_icmp *icmpinfo = (const struct ipt_icmp *)m->data;
202
203         if (strcmp(m->u.kernel.match->name, "icmp") == 0
204             && icmpinfo->type == ICMP_ECHO
205             && !(icmpinfo->invflags & IPT_ICMP_INV))
206                 return 1;
207
208         return 0;
209 }
210
211 static int check(const char *tablename,
212                  const struct ipt_entry *e,
213                  void *targinfo,
214                  unsigned int targinfosize,
215                  unsigned int hook_mask)
216 {
217         const struct ipt_reject_info *rejinfo = targinfo;
218
219         if (targinfosize != IPT_ALIGN(sizeof(struct ipt_icmp))) {
220                 DEBUGP("REJECT: targinfosize %u != 0\n", targinfosize);
221                 return 0;
222         }
223
224         /* Only allow these for packet filtering. */
225         if (strcmp(tablename, "filter") != 0) {
226                 DEBUGP("REJECT: bad table `%s'.\n", tablename);
227                 return 0;
228         }
229         if ((hook_mask & ~((1 << NF_IP_LOCAL_IN)
230                            | (1 << NF_IP_FORWARD)
231                            | (1 << NF_IP_LOCAL_OUT))) != 0) {
232                 DEBUGP("REJECT: bad hook mask %X\n", hook_mask);
233                 return 0;
234         }
235
236         if (rejinfo->with == IPT_ICMP_ECHOREPLY) {
237                 /* Must specify that it's an ICMP ping packet. */
238                 if (e->ip.proto != IPPROTO_ICMP
239                     || (e->ip.invflags & IPT_INV_PROTO)) {
240                         DEBUGP("REJECT: ECHOREPLY illegal for non-icmp\n");
241                         return 0;
242                 }
243                 /* Must contain ICMP match. */
244                 if (IPT_MATCH_ITERATE(e, find_ping_match) == 0) {
245                         DEBUGP("REJECT: ECHOREPLY illegal for non-ping\n");
246                         return 0;
247                 }
248         } else if (rejinfo->with == IPT_TCP_RESET) {
249                 /* Must specify that it's a TCP packet */
250                 if (e->ip.proto != IPPROTO_TCP
251                     || (e->ip.invflags & IPT_INV_PROTO)) {
252                         DEBUGP("REJECT: TCP_RESET illegal for non-tcp\n");
253                         return 0;
254                 }
255         }
256
257         return 1;
258 }
259
260 static struct ipt_target ipt_reject_reg
261 = { { NULL, NULL }, "REJECT", reject, check, NULL, THIS_MODULE };
262
263 static int __init init(void)
264 {
265         if (ipt_register_target(&ipt_reject_reg))
266                 return -EINVAL;
267         return 0;
268 }
269
270 static void __exit fini(void)
271 {
272         ipt_unregister_target(&ipt_reject_reg);
273 }
274
275 module_init(init);
276 module_exit(fini);