Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * This is a module which is used for rejecting packets. | |
1da177e4 LT |
3 | */ |
4 | ||
5 | /* (C) 1999-2001 Paul `Rusty' Russell | |
6 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
1da177e4 LT |
13 | #include <linux/module.h> |
14 | #include <linux/skbuff.h> | |
15 | #include <linux/ip.h> | |
16 | #include <linux/udp.h> | |
17 | #include <linux/icmp.h> | |
18 | #include <net/icmp.h> | |
19 | #include <net/ip.h> | |
20 | #include <net/tcp.h> | |
21 | #include <net/route.h> | |
22 | #include <net/dst.h> | |
6709dbbb | 23 | #include <linux/netfilter/x_tables.h> |
1da177e4 LT |
24 | #include <linux/netfilter_ipv4/ip_tables.h> |
25 | #include <linux/netfilter_ipv4/ipt_REJECT.h> | |
26 | #ifdef CONFIG_BRIDGE_NETFILTER | |
27 | #include <linux/netfilter_bridge.h> | |
28 | #endif | |
29 | ||
30 | MODULE_LICENSE("GPL"); | |
31 | MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); | |
32 | MODULE_DESCRIPTION("iptables REJECT target module"); | |
33 | ||
1da177e4 LT |
34 | /* Send RST reply */ |
35 | static void send_reset(struct sk_buff *oldskb, int hook) | |
36 | { | |
37 | struct sk_buff *nskb; | |
eddc9ec5 | 38 | struct iphdr *niph; |
1da177e4 | 39 | struct tcphdr _otcph, *oth, *tcph; |
6a19d614 AV |
40 | __be16 tmp_port; |
41 | __be32 tmp_addr; | |
1da177e4 | 42 | int needs_ack; |
9d02002d | 43 | unsigned int addr_type; |
1da177e4 LT |
44 | |
45 | /* IP header checks: fragment. */ | |
eddc9ec5 | 46 | if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) |
1da177e4 LT |
47 | return; |
48 | ||
c9bdd4b5 | 49 | oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), |
1da177e4 LT |
50 | sizeof(_otcph), &_otcph); |
51 | if (oth == NULL) | |
e905a9ed | 52 | return; |
1da177e4 LT |
53 | |
54 | /* No RST for RST. */ | |
55 | if (oth->rst) | |
56 | return; | |
57 | ||
6150bacf | 58 | /* Check checksum */ |
c9bdd4b5 | 59 | if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) |
6150bacf PM |
60 | return; |
61 | ||
1da177e4 LT |
62 | /* We need a linear, writeable skb. We also need to expand |
63 | headroom in case hh_len of incoming interface < hh_len of | |
64 | outgoing interface */ | |
9d02002d | 65 | nskb = skb_copy_expand(oldskb, LL_MAX_HEADER, skb_tailroom(oldskb), |
1da177e4 | 66 | GFP_ATOMIC); |
9d02002d | 67 | if (!nskb) |
1da177e4 | 68 | return; |
1da177e4 LT |
69 | |
70 | /* This packet will not be the same as the other: clear nf fields */ | |
71 | nf_reset(nskb); | |
82e91ffe | 72 | nskb->mark = 0; |
984bc16c | 73 | skb_init_secmark(nskb); |
1da177e4 | 74 | |
bbf4a6bc HX |
75 | skb_shinfo(nskb)->gso_size = 0; |
76 | skb_shinfo(nskb)->gso_segs = 0; | |
77 | skb_shinfo(nskb)->gso_type = 0; | |
78 | ||
c9bdd4b5 | 79 | tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); |
1da177e4 LT |
80 | |
81 | /* Swap source and dest */ | |
eddc9ec5 ACM |
82 | niph = ip_hdr(nskb); |
83 | tmp_addr = niph->saddr; | |
84 | niph->saddr = niph->daddr; | |
85 | niph->daddr = tmp_addr; | |
1da177e4 LT |
86 | tmp_port = tcph->source; |
87 | tcph->source = tcph->dest; | |
88 | tcph->dest = tmp_port; | |
89 | ||
90 | /* Truncate to length (no data) */ | |
91 | tcph->doff = sizeof(struct tcphdr)/4; | |
c9bdd4b5 | 92 | skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); |
1da177e4 LT |
93 | |
94 | if (tcph->ack) { | |
95 | needs_ack = 0; | |
96 | tcph->seq = oth->ack_seq; | |
97 | tcph->ack_seq = 0; | |
98 | } else { | |
99 | needs_ack = 1; | |
c9bdd4b5 ACM |
100 | tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + |
101 | oldskb->len - ip_hdrlen(oldskb) - | |
102 | (oth->doff << 2)); | |
1da177e4 LT |
103 | tcph->seq = 0; |
104 | } | |
105 | ||
106 | /* Reset flags */ | |
107 | ((u_int8_t *)tcph)[13] = 0; | |
108 | tcph->rst = 1; | |
109 | tcph->ack = needs_ack; | |
110 | ||
111 | tcph->window = 0; | |
112 | tcph->urg_ptr = 0; | |
113 | ||
af443b6d PM |
114 | /* Adjust TCP checksum */ |
115 | tcph->check = 0; | |
ba7808ea | 116 | tcph->check = tcp_v4_check(sizeof(struct tcphdr), |
eddc9ec5 | 117 | niph->saddr, niph->daddr, |
a47362a2 | 118 | csum_partial(tcph, |
af443b6d PM |
119 | sizeof(struct tcphdr), 0)); |
120 | ||
9d02002d | 121 | /* Set DF, id = 0 */ |
eddc9ec5 ACM |
122 | niph->frag_off = htons(IP_DF); |
123 | niph->id = 0; | |
9d02002d PM |
124 | |
125 | addr_type = RTN_UNSPEC; | |
6e23ae2a | 126 | if (hook != NF_INET_FORWARD |
9d02002d PM |
127 | #ifdef CONFIG_BRIDGE_NETFILTER |
128 | || (nskb->nf_bridge && nskb->nf_bridge->mask & BRNF_BRIDGED) | |
129 | #endif | |
130 | ) | |
131 | addr_type = RTN_LOCAL; | |
132 | ||
3db05fea | 133 | if (ip_route_me_harder(nskb, addr_type)) |
9d02002d PM |
134 | goto free_nskb; |
135 | ||
4cf411de | 136 | nskb->ip_summed = CHECKSUM_NONE; |
af443b6d | 137 | |
9d02002d | 138 | /* Adjust IP TTL */ |
eddc9ec5 | 139 | niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); |
1da177e4 | 140 | |
1da177e4 LT |
141 | /* "Never happens" */ |
142 | if (nskb->len > dst_mtu(nskb->dst)) | |
143 | goto free_nskb; | |
144 | ||
145 | nf_ct_attach(nskb, oldskb); | |
146 | ||
c439cb2e | 147 | ip_local_out(nskb); |
1da177e4 LT |
148 | return; |
149 | ||
150 | free_nskb: | |
151 | kfree_skb(nskb); | |
152 | } | |
153 | ||
154 | static inline void send_unreach(struct sk_buff *skb_in, int code) | |
155 | { | |
156 | icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); | |
e905a9ed | 157 | } |
1da177e4 | 158 | |
d3c5ee6d JE |
159 | static unsigned int |
160 | reject_tg(struct sk_buff *skb, const struct net_device *in, | |
161 | const struct net_device *out, unsigned int hooknum, | |
162 | const struct xt_target *target, const void *targinfo) | |
1da177e4 LT |
163 | { |
164 | const struct ipt_reject_info *reject = targinfo; | |
165 | ||
166 | /* Our naive response construction doesn't deal with IP | |
e905a9ed | 167 | options, and probably shouldn't try. */ |
3db05fea | 168 | if (ip_hdrlen(skb) != sizeof(struct iphdr)) |
1da177e4 LT |
169 | return NF_DROP; |
170 | ||
171 | /* WARNING: This code causes reentry within iptables. | |
172 | This means that the iptables jump stack is now crap. We | |
173 | must return an absolute verdict. --RR */ | |
e905a9ed YH |
174 | switch (reject->with) { |
175 | case IPT_ICMP_NET_UNREACHABLE: | |
3db05fea | 176 | send_unreach(skb, ICMP_NET_UNREACH); |
e905a9ed YH |
177 | break; |
178 | case IPT_ICMP_HOST_UNREACHABLE: | |
3db05fea | 179 | send_unreach(skb, ICMP_HOST_UNREACH); |
e905a9ed YH |
180 | break; |
181 | case IPT_ICMP_PROT_UNREACHABLE: | |
3db05fea | 182 | send_unreach(skb, ICMP_PROT_UNREACH); |
e905a9ed YH |
183 | break; |
184 | case IPT_ICMP_PORT_UNREACHABLE: | |
3db05fea | 185 | send_unreach(skb, ICMP_PORT_UNREACH); |
e905a9ed YH |
186 | break; |
187 | case IPT_ICMP_NET_PROHIBITED: | |
3db05fea | 188 | send_unreach(skb, ICMP_NET_ANO); |
e905a9ed | 189 | break; |
1da177e4 | 190 | case IPT_ICMP_HOST_PROHIBITED: |
3db05fea | 191 | send_unreach(skb, ICMP_HOST_ANO); |
e905a9ed YH |
192 | break; |
193 | case IPT_ICMP_ADMIN_PROHIBITED: | |
3db05fea | 194 | send_unreach(skb, ICMP_PKT_FILTERED); |
1da177e4 LT |
195 | break; |
196 | case IPT_TCP_RESET: | |
3db05fea | 197 | send_reset(skb, hooknum); |
1da177e4 LT |
198 | case IPT_ICMP_ECHOREPLY: |
199 | /* Doesn't happen. */ | |
200 | break; | |
201 | } | |
202 | ||
203 | return NF_DROP; | |
204 | } | |
205 | ||
d3c5ee6d JE |
206 | static bool |
207 | reject_tg_check(const char *tablename, const void *e_void, | |
208 | const struct xt_target *target, void *targinfo, | |
209 | unsigned int hook_mask) | |
1da177e4 | 210 | { |
e905a9ed | 211 | const struct ipt_reject_info *rejinfo = targinfo; |
2e4e6a17 | 212 | const struct ipt_entry *e = e_void; |
1da177e4 | 213 | |
1da177e4 | 214 | if (rejinfo->with == IPT_ICMP_ECHOREPLY) { |
0d53778e | 215 | printk("ipt_REJECT: ECHOREPLY no longer supported.\n"); |
e1931b78 | 216 | return false; |
1da177e4 LT |
217 | } else if (rejinfo->with == IPT_TCP_RESET) { |
218 | /* Must specify that it's a TCP packet */ | |
219 | if (e->ip.proto != IPPROTO_TCP | |
6709dbbb | 220 | || (e->ip.invflags & XT_INV_PROTO)) { |
0d53778e | 221 | printk("ipt_REJECT: TCP_RESET invalid for non-tcp\n"); |
e1931b78 | 222 | return false; |
1da177e4 LT |
223 | } |
224 | } | |
e1931b78 | 225 | return true; |
1da177e4 LT |
226 | } |
227 | ||
d3c5ee6d | 228 | static struct xt_target reject_tg_reg __read_mostly = { |
1da177e4 | 229 | .name = "REJECT", |
6709dbbb | 230 | .family = AF_INET, |
d3c5ee6d | 231 | .target = reject_tg, |
1d5cd909 PM |
232 | .targetsize = sizeof(struct ipt_reject_info), |
233 | .table = "filter", | |
6e23ae2a PM |
234 | .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | |
235 | (1 << NF_INET_LOCAL_OUT), | |
d3c5ee6d | 236 | .checkentry = reject_tg_check, |
1da177e4 LT |
237 | .me = THIS_MODULE, |
238 | }; | |
239 | ||
d3c5ee6d | 240 | static int __init reject_tg_init(void) |
1da177e4 | 241 | { |
d3c5ee6d | 242 | return xt_register_target(&reject_tg_reg); |
1da177e4 LT |
243 | } |
244 | ||
d3c5ee6d | 245 | static void __exit reject_tg_exit(void) |
1da177e4 | 246 | { |
d3c5ee6d | 247 | xt_unregister_target(&reject_tg_reg); |
1da177e4 LT |
248 | } |
249 | ||
d3c5ee6d JE |
250 | module_init(reject_tg_init); |
251 | module_exit(reject_tg_exit); |