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 | */ | |
ff67e4e4 | 12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
1da177e4 LT |
13 | #include <linux/module.h> |
14 | #include <linux/skbuff.h> | |
5a0e3ad6 | 15 | #include <linux/slab.h> |
1da177e4 LT |
16 | #include <linux/ip.h> |
17 | #include <linux/udp.h> | |
18 | #include <linux/icmp.h> | |
19 | #include <net/icmp.h> | |
20 | #include <net/ip.h> | |
21 | #include <net/tcp.h> | |
22 | #include <net/route.h> | |
23 | #include <net/dst.h> | |
6709dbbb | 24 | #include <linux/netfilter/x_tables.h> |
1da177e4 LT |
25 | #include <linux/netfilter_ipv4/ip_tables.h> |
26 | #include <linux/netfilter_ipv4/ipt_REJECT.h> | |
27 | #ifdef CONFIG_BRIDGE_NETFILTER | |
28 | #include <linux/netfilter_bridge.h> | |
29 | #endif | |
30 | ||
31 | MODULE_LICENSE("GPL"); | |
32 | MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); | |
2ae15b64 | 33 | MODULE_DESCRIPTION("Xtables: packet \"rejection\" target for IPv4"); |
1da177e4 | 34 | |
1da177e4 LT |
35 | /* Send RST reply */ |
36 | static void send_reset(struct sk_buff *oldskb, int hook) | |
37 | { | |
38 | struct sk_buff *nskb; | |
3cf93c96 JE |
39 | const struct iphdr *oiph; |
40 | struct iphdr *niph; | |
41 | const struct tcphdr *oth; | |
42 | struct tcphdr _otcph, *tcph; | |
1da177e4 LT |
43 | |
44 | /* IP header checks: fragment. */ | |
eddc9ec5 | 45 | if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) |
1da177e4 LT |
46 | return; |
47 | ||
c9bdd4b5 | 48 | oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), |
1da177e4 LT |
49 | sizeof(_otcph), &_otcph); |
50 | if (oth == NULL) | |
e905a9ed | 51 | return; |
1da177e4 LT |
52 | |
53 | /* No RST for RST. */ | |
54 | if (oth->rst) | |
55 | return; | |
56 | ||
ed6e4ef8 JA |
57 | if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) |
58 | return; | |
59 | ||
6150bacf | 60 | /* Check checksum */ |
c9bdd4b5 | 61 | if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) |
6150bacf | 62 | return; |
9ba99b0d | 63 | oiph = ip_hdr(oldskb); |
6150bacf | 64 | |
9ba99b0d DV |
65 | nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + |
66 | LL_MAX_HEADER, GFP_ATOMIC); | |
9d02002d | 67 | if (!nskb) |
1da177e4 | 68 | return; |
1da177e4 | 69 | |
9ba99b0d DV |
70 | skb_reserve(nskb, LL_MAX_HEADER); |
71 | ||
72 | skb_reset_network_header(nskb); | |
73 | niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); | |
74 | niph->version = 4; | |
75 | niph->ihl = sizeof(struct iphdr) / 4; | |
76 | niph->tos = 0; | |
77 | niph->id = 0; | |
78 | niph->frag_off = htons(IP_DF); | |
79 | niph->protocol = IPPROTO_TCP; | |
80 | niph->check = 0; | |
81 | niph->saddr = oiph->daddr; | |
82 | niph->daddr = oiph->saddr; | |
83 | ||
c6f40899 | 84 | skb_reset_transport_header(nskb); |
9ba99b0d DV |
85 | tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); |
86 | memset(tcph, 0, sizeof(*tcph)); | |
87 | tcph->source = oth->dest; | |
88 | tcph->dest = oth->source; | |
89 | tcph->doff = sizeof(struct tcphdr) / 4; | |
90 | ||
91 | if (oth->ack) | |
1da177e4 | 92 | tcph->seq = oth->ack_seq; |
9ba99b0d | 93 | else { |
c9bdd4b5 ACM |
94 | tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + |
95 | oldskb->len - ip_hdrlen(oldskb) - | |
96 | (oth->doff << 2)); | |
9ba99b0d | 97 | tcph->ack = 1; |
1da177e4 LT |
98 | } |
99 | ||
9ba99b0d | 100 | tcph->rst = 1; |
98b0e84a CG |
101 | tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), niph->saddr, |
102 | niph->daddr, 0); | |
103 | nskb->ip_summed = CHECKSUM_PARTIAL; | |
104 | nskb->csum_start = (unsigned char *)tcph - nskb->head; | |
105 | nskb->csum_offset = offsetof(struct tcphdr, check); | |
9d02002d | 106 | |
9ba99b0d | 107 | /* ip_route_me_harder expects skb->dst to be set */ |
b13b7125 | 108 | skb_dst_set_noref(nskb, skb_dst(oldskb)); |
9ba99b0d | 109 | |
b46ffb85 | 110 | nskb->protocol = htons(ETH_P_IP); |
ed6e4ef8 | 111 | if (ip_route_me_harder(nskb, RTN_UNSPEC)) |
9d02002d PM |
112 | goto free_nskb; |
113 | ||
323e126f | 114 | niph->ttl = ip4_dst_hoplimit(skb_dst(nskb)); |
af443b6d | 115 | |
1da177e4 | 116 | /* "Never happens" */ |
adf30907 | 117 | if (nskb->len > dst_mtu(skb_dst(nskb))) |
1da177e4 LT |
118 | goto free_nskb; |
119 | ||
120 | nf_ct_attach(nskb, oldskb); | |
121 | ||
c439cb2e | 122 | ip_local_out(nskb); |
1da177e4 LT |
123 | return; |
124 | ||
125 | free_nskb: | |
126 | kfree_skb(nskb); | |
127 | } | |
128 | ||
129 | static inline void send_unreach(struct sk_buff *skb_in, int code) | |
130 | { | |
131 | icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); | |
6fa3eb70 S |
132 | #ifdef CONFIG_IP_NF_TARGET_REJECT_SKERR |
133 | if (skb_in->sk) { | |
134 | skb_in->sk->sk_err = icmp_err_convert[code].errno; | |
135 | skb_in->sk->sk_error_report(skb_in->sk); | |
136 | pr_debug("ipt_REJECT: sk_err=%d for skb=%p sk=%p\n", | |
137 | skb_in->sk->sk_err, skb_in, skb_in->sk); | |
138 | } | |
139 | #endif | |
e905a9ed | 140 | } |
1da177e4 | 141 | |
d3c5ee6d | 142 | static unsigned int |
4b560b44 | 143 | reject_tg(struct sk_buff *skb, const struct xt_action_param *par) |
1da177e4 | 144 | { |
7eb35586 | 145 | const struct ipt_reject_info *reject = par->targinfo; |
1da177e4 | 146 | |
e905a9ed YH |
147 | switch (reject->with) { |
148 | case IPT_ICMP_NET_UNREACHABLE: | |
3db05fea | 149 | send_unreach(skb, ICMP_NET_UNREACH); |
e905a9ed YH |
150 | break; |
151 | case IPT_ICMP_HOST_UNREACHABLE: | |
3db05fea | 152 | send_unreach(skb, ICMP_HOST_UNREACH); |
e905a9ed YH |
153 | break; |
154 | case IPT_ICMP_PROT_UNREACHABLE: | |
3db05fea | 155 | send_unreach(skb, ICMP_PROT_UNREACH); |
e905a9ed YH |
156 | break; |
157 | case IPT_ICMP_PORT_UNREACHABLE: | |
3db05fea | 158 | send_unreach(skb, ICMP_PORT_UNREACH); |
e905a9ed YH |
159 | break; |
160 | case IPT_ICMP_NET_PROHIBITED: | |
3db05fea | 161 | send_unreach(skb, ICMP_NET_ANO); |
e905a9ed | 162 | break; |
1da177e4 | 163 | case IPT_ICMP_HOST_PROHIBITED: |
3db05fea | 164 | send_unreach(skb, ICMP_HOST_ANO); |
e905a9ed YH |
165 | break; |
166 | case IPT_ICMP_ADMIN_PROHIBITED: | |
3db05fea | 167 | send_unreach(skb, ICMP_PKT_FILTERED); |
1da177e4 LT |
168 | break; |
169 | case IPT_TCP_RESET: | |
7eb35586 | 170 | send_reset(skb, par->hooknum); |
1da177e4 LT |
171 | case IPT_ICMP_ECHOREPLY: |
172 | /* Doesn't happen. */ | |
173 | break; | |
174 | } | |
175 | ||
176 | return NF_DROP; | |
177 | } | |
178 | ||
135367b8 | 179 | static int reject_tg_check(const struct xt_tgchk_param *par) |
1da177e4 | 180 | { |
af5d6dc2 JE |
181 | const struct ipt_reject_info *rejinfo = par->targinfo; |
182 | const struct ipt_entry *e = par->entryinfo; | |
1da177e4 | 183 | |
1da177e4 | 184 | if (rejinfo->with == IPT_ICMP_ECHOREPLY) { |
ff67e4e4 | 185 | pr_info("ECHOREPLY no longer supported.\n"); |
d6b00a53 | 186 | return -EINVAL; |
1da177e4 LT |
187 | } else if (rejinfo->with == IPT_TCP_RESET) { |
188 | /* Must specify that it's a TCP packet */ | |
3666ed1c JP |
189 | if (e->ip.proto != IPPROTO_TCP || |
190 | (e->ip.invflags & XT_INV_PROTO)) { | |
ff67e4e4 | 191 | pr_info("TCP_RESET invalid for non-tcp\n"); |
d6b00a53 | 192 | return -EINVAL; |
1da177e4 LT |
193 | } |
194 | } | |
d6b00a53 | 195 | return 0; |
1da177e4 LT |
196 | } |
197 | ||
d3c5ee6d | 198 | static struct xt_target reject_tg_reg __read_mostly = { |
1da177e4 | 199 | .name = "REJECT", |
ee999d8b | 200 | .family = NFPROTO_IPV4, |
d3c5ee6d | 201 | .target = reject_tg, |
1d5cd909 PM |
202 | .targetsize = sizeof(struct ipt_reject_info), |
203 | .table = "filter", | |
6e23ae2a PM |
204 | .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | |
205 | (1 << NF_INET_LOCAL_OUT), | |
d3c5ee6d | 206 | .checkentry = reject_tg_check, |
1da177e4 LT |
207 | .me = THIS_MODULE, |
208 | }; | |
209 | ||
d3c5ee6d | 210 | static int __init reject_tg_init(void) |
1da177e4 | 211 | { |
d3c5ee6d | 212 | return xt_register_target(&reject_tg_reg); |
1da177e4 LT |
213 | } |
214 | ||
d3c5ee6d | 215 | static void __exit reject_tg_exit(void) |
1da177e4 | 216 | { |
d3c5ee6d | 217 | xt_unregister_target(&reject_tg_reg); |
1da177e4 LT |
218 | } |
219 | ||
d3c5ee6d JE |
220 | module_init(reject_tg_init); |
221 | module_exit(reject_tg_exit); |