1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* (C) 1999-2001 Paul `Rusty' Russell |
3 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> |
4 | */ |
5 | |
6 | #include <linux/module.h> |
7 | #include <net/ip.h> |
8 | #include <net/tcp.h> |
9 | #include <net/route.h> |
10 | #include <net/dst.h> |
11 | #include <net/netfilter/ipv4/nf_reject.h> |
12 | #include <linux/netfilter_ipv4.h> |
13 | #include <linux/netfilter_bridge.h> |
14 | |
15 | static int nf_reject_iphdr_validate(struct sk_buff *skb) |
16 | { |
17 | struct iphdr *iph; |
18 | u32 len; |
19 | |
20 | if (!pskb_may_pull(skb, len: sizeof(struct iphdr))) |
21 | return 0; |
22 | |
23 | iph = ip_hdr(skb); |
24 | if (iph->ihl < 5 || iph->version != 4) |
25 | return 0; |
26 | |
27 | len = ntohs(iph->tot_len); |
28 | if (skb->len < len) |
29 | return 0; |
30 | else if (len < (iph->ihl*4)) |
31 | return 0; |
32 | |
33 | if (!pskb_may_pull(skb, len: iph->ihl*4)) |
34 | return 0; |
35 | |
36 | return 1; |
37 | } |
38 | |
39 | struct sk_buff *nf_reject_skb_v4_tcp_reset(struct net *net, |
40 | struct sk_buff *oldskb, |
41 | const struct net_device *dev, |
42 | int hook) |
43 | { |
44 | const struct tcphdr *oth; |
45 | struct sk_buff *nskb; |
46 | struct iphdr *niph; |
47 | struct tcphdr _oth; |
48 | |
49 | if (!nf_reject_iphdr_validate(skb: oldskb)) |
50 | return NULL; |
51 | |
52 | oth = nf_reject_ip_tcphdr_get(oldskb, oth: &_oth, hook); |
53 | if (!oth) |
54 | return NULL; |
55 | |
56 | nskb = alloc_skb(size: sizeof(struct iphdr) + sizeof(struct tcphdr) + |
57 | LL_MAX_HEADER, GFP_ATOMIC); |
58 | if (!nskb) |
59 | return NULL; |
60 | |
61 | nskb->dev = (struct net_device *)dev; |
62 | |
63 | skb_reserve(skb: nskb, LL_MAX_HEADER); |
64 | niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, |
65 | READ_ONCE(net->ipv4.sysctl_ip_default_ttl)); |
66 | nf_reject_ip_tcphdr_put(nskb, oldskb, oth); |
67 | niph->tot_len = htons(nskb->len); |
68 | ip_send_check(ip: niph); |
69 | |
70 | return nskb; |
71 | } |
72 | EXPORT_SYMBOL_GPL(nf_reject_skb_v4_tcp_reset); |
73 | |
74 | struct sk_buff *nf_reject_skb_v4_unreach(struct net *net, |
75 | struct sk_buff *oldskb, |
76 | const struct net_device *dev, |
77 | int hook, u8 code) |
78 | { |
79 | struct sk_buff *nskb; |
80 | struct iphdr *niph; |
81 | struct icmphdr *icmph; |
82 | unsigned int len; |
83 | int dataoff; |
84 | __wsum csum; |
85 | u8 proto; |
86 | |
87 | if (!nf_reject_iphdr_validate(skb: oldskb)) |
88 | return NULL; |
89 | |
90 | /* IP header checks: fragment. */ |
91 | if (ip_hdr(skb: oldskb)->frag_off & htons(IP_OFFSET)) |
92 | return NULL; |
93 | |
94 | /* RFC says return as much as we can without exceeding 576 bytes. */ |
95 | len = min_t(unsigned int, 536, oldskb->len); |
96 | |
97 | if (!pskb_may_pull(skb: oldskb, len)) |
98 | return NULL; |
99 | |
100 | if (pskb_trim_rcsum(skb: oldskb, ntohs(ip_hdr(oldskb)->tot_len))) |
101 | return NULL; |
102 | |
103 | dataoff = ip_hdrlen(skb: oldskb); |
104 | proto = ip_hdr(skb: oldskb)->protocol; |
105 | |
106 | if (!skb_csum_unnecessary(skb: oldskb) && |
107 | nf_reject_verify_csum(skb: oldskb, dataoff, proto) && |
108 | nf_ip_checksum(skb: oldskb, hook, dataoff: ip_hdrlen(skb: oldskb), protocol: proto)) |
109 | return NULL; |
110 | |
111 | nskb = alloc_skb(size: sizeof(struct iphdr) + sizeof(struct icmphdr) + |
112 | LL_MAX_HEADER + len, GFP_ATOMIC); |
113 | if (!nskb) |
114 | return NULL; |
115 | |
116 | nskb->dev = (struct net_device *)dev; |
117 | |
118 | skb_reserve(skb: nskb, LL_MAX_HEADER); |
119 | niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_ICMP, |
120 | READ_ONCE(net->ipv4.sysctl_ip_default_ttl)); |
121 | |
122 | skb_reset_transport_header(skb: nskb); |
123 | icmph = skb_put_zero(skb: nskb, len: sizeof(struct icmphdr)); |
124 | icmph->type = ICMP_DEST_UNREACH; |
125 | icmph->code = code; |
126 | |
127 | skb_put_data(skb: nskb, data: skb_network_header(skb: oldskb), len); |
128 | |
129 | csum = csum_partial(buff: (void *)icmph, len: len + sizeof(struct icmphdr), sum: 0); |
130 | icmph->checksum = csum_fold(sum: csum); |
131 | |
132 | niph->tot_len = htons(nskb->len); |
133 | ip_send_check(ip: niph); |
134 | |
135 | return nskb; |
136 | } |
137 | EXPORT_SYMBOL_GPL(nf_reject_skb_v4_unreach); |
138 | |
139 | const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, |
140 | struct tcphdr *_oth, int hook) |
141 | { |
142 | const struct tcphdr *oth; |
143 | |
144 | /* IP header checks: fragment. */ |
145 | if (ip_hdr(skb: oldskb)->frag_off & htons(IP_OFFSET)) |
146 | return NULL; |
147 | |
148 | if (ip_hdr(skb: oldskb)->protocol != IPPROTO_TCP) |
149 | return NULL; |
150 | |
151 | oth = skb_header_pointer(skb: oldskb, offset: ip_hdrlen(skb: oldskb), |
152 | len: sizeof(struct tcphdr), buffer: _oth); |
153 | if (oth == NULL) |
154 | return NULL; |
155 | |
156 | /* No RST for RST. */ |
157 | if (oth->rst) |
158 | return NULL; |
159 | |
160 | /* Check checksum */ |
161 | if (nf_ip_checksum(skb: oldskb, hook, dataoff: ip_hdrlen(skb: oldskb), IPPROTO_TCP)) |
162 | return NULL; |
163 | |
164 | return oth; |
165 | } |
166 | EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_get); |
167 | |
168 | struct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb, |
169 | const struct sk_buff *oldskb, |
170 | __u8 protocol, int ttl) |
171 | { |
172 | struct iphdr *niph, *oiph = ip_hdr(skb: oldskb); |
173 | |
174 | skb_reset_network_header(skb: nskb); |
175 | niph = skb_put(skb: nskb, len: sizeof(struct iphdr)); |
176 | niph->version = 4; |
177 | niph->ihl = sizeof(struct iphdr) / 4; |
178 | niph->tos = 0; |
179 | niph->id = 0; |
180 | niph->frag_off = htons(IP_DF); |
181 | niph->protocol = protocol; |
182 | niph->check = 0; |
183 | niph->saddr = oiph->daddr; |
184 | niph->daddr = oiph->saddr; |
185 | niph->ttl = ttl; |
186 | |
187 | nskb->protocol = htons(ETH_P_IP); |
188 | |
189 | return niph; |
190 | } |
191 | EXPORT_SYMBOL_GPL(nf_reject_iphdr_put); |
192 | |
193 | void nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, |
194 | const struct tcphdr *oth) |
195 | { |
196 | struct iphdr *niph = ip_hdr(skb: nskb); |
197 | struct tcphdr *tcph; |
198 | |
199 | skb_reset_transport_header(skb: nskb); |
200 | tcph = skb_put_zero(skb: nskb, len: sizeof(struct tcphdr)); |
201 | tcph->source = oth->dest; |
202 | tcph->dest = oth->source; |
203 | tcph->doff = sizeof(struct tcphdr) / 4; |
204 | |
205 | if (oth->ack) { |
206 | tcph->seq = oth->ack_seq; |
207 | } else { |
208 | tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + |
209 | oldskb->len - ip_hdrlen(oldskb) - |
210 | (oth->doff << 2)); |
211 | tcph->ack = 1; |
212 | } |
213 | |
214 | tcph->rst = 1; |
215 | tcph->check = ~tcp_v4_check(len: sizeof(struct tcphdr), saddr: niph->saddr, |
216 | daddr: niph->daddr, base: 0); |
217 | nskb->ip_summed = CHECKSUM_PARTIAL; |
218 | nskb->csum_start = (unsigned char *)tcph - nskb->head; |
219 | nskb->csum_offset = offsetof(struct tcphdr, check); |
220 | } |
221 | EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put); |
222 | |
223 | static int nf_reject_fill_skb_dst(struct sk_buff *skb_in) |
224 | { |
225 | struct dst_entry *dst = NULL; |
226 | struct flowi fl; |
227 | |
228 | memset(&fl, 0, sizeof(struct flowi)); |
229 | fl.u.ip4.daddr = ip_hdr(skb: skb_in)->saddr; |
230 | nf_ip_route(net: dev_net(dev: skb_in->dev), dst: &dst, fl: &fl, strict: false); |
231 | if (!dst) |
232 | return -1; |
233 | |
234 | skb_dst_set(skb: skb_in, dst); |
235 | return 0; |
236 | } |
237 | |
238 | /* Send RST reply */ |
239 | void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb, |
240 | int hook) |
241 | { |
242 | struct net_device *br_indev __maybe_unused; |
243 | struct sk_buff *nskb; |
244 | struct iphdr *niph; |
245 | const struct tcphdr *oth; |
246 | struct tcphdr _oth; |
247 | |
248 | oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook); |
249 | if (!oth) |
250 | return; |
251 | |
252 | if ((hook == NF_INET_PRE_ROUTING || hook == NF_INET_INGRESS) && |
253 | nf_reject_fill_skb_dst(skb_in: oldskb) < 0) |
254 | return; |
255 | |
256 | if (skb_rtable(skb: oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) |
257 | return; |
258 | |
259 | nskb = alloc_skb(size: sizeof(struct iphdr) + sizeof(struct tcphdr) + |
260 | LL_MAX_HEADER, GFP_ATOMIC); |
261 | if (!nskb) |
262 | return; |
263 | |
264 | /* ip_route_me_harder expects skb->dst to be set */ |
265 | skb_dst_set_noref(skb: nskb, dst: skb_dst(skb: oldskb)); |
266 | |
267 | nskb->mark = IP4_REPLY_MARK(net, oldskb->mark); |
268 | |
269 | skb_reserve(skb: nskb, LL_MAX_HEADER); |
270 | niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, |
271 | ip4_dst_hoplimit(dst: skb_dst(skb: nskb))); |
272 | nf_reject_ip_tcphdr_put(nskb, oldskb, oth); |
273 | if (ip_route_me_harder(net, sk, skb: nskb, addr_type: RTN_UNSPEC)) |
274 | goto free_nskb; |
275 | |
276 | niph = ip_hdr(skb: nskb); |
277 | |
278 | /* "Never happens" */ |
279 | if (nskb->len > dst_mtu(dst: skb_dst(skb: nskb))) |
280 | goto free_nskb; |
281 | |
282 | nf_ct_attach(nskb, oldskb); |
283 | nf_ct_set_closing(nfct: skb_nfct(skb: oldskb)); |
284 | |
285 | #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) |
286 | /* If we use ip_local_out for bridged traffic, the MAC source on |
287 | * the RST will be ours, instead of the destination's. This confuses |
288 | * some routers/firewalls, and they drop the packet. So we need to |
289 | * build the eth header using the original destination's MAC as the |
290 | * source, and send the RST packet directly. |
291 | */ |
292 | br_indev = nf_bridge_get_physindev(skb: oldskb); |
293 | if (br_indev) { |
294 | struct ethhdr *oeth = eth_hdr(skb: oldskb); |
295 | |
296 | nskb->dev = br_indev; |
297 | niph->tot_len = htons(nskb->len); |
298 | ip_send_check(ip: niph); |
299 | if (dev_hard_header(skb: nskb, dev: nskb->dev, ntohs(nskb->protocol), |
300 | daddr: oeth->h_source, saddr: oeth->h_dest, len: nskb->len) < 0) |
301 | goto free_nskb; |
302 | dev_queue_xmit(skb: nskb); |
303 | } else |
304 | #endif |
305 | ip_local_out(net, sk: nskb->sk, skb: nskb); |
306 | |
307 | return; |
308 | |
309 | free_nskb: |
310 | kfree_skb(skb: nskb); |
311 | } |
312 | EXPORT_SYMBOL_GPL(nf_send_reset); |
313 | |
314 | void nf_send_unreach(struct sk_buff *skb_in, int code, int hook) |
315 | { |
316 | struct iphdr *iph = ip_hdr(skb: skb_in); |
317 | int dataoff = ip_hdrlen(skb: skb_in); |
318 | u8 proto = iph->protocol; |
319 | |
320 | if (iph->frag_off & htons(IP_OFFSET)) |
321 | return; |
322 | |
323 | if ((hook == NF_INET_PRE_ROUTING || hook == NF_INET_INGRESS) && |
324 | nf_reject_fill_skb_dst(skb_in) < 0) |
325 | return; |
326 | |
327 | if (skb_csum_unnecessary(skb: skb_in) || |
328 | !nf_reject_verify_csum(skb: skb_in, dataoff, proto)) { |
329 | icmp_send(skb_in, ICMP_DEST_UNREACH, code, info: 0); |
330 | return; |
331 | } |
332 | |
333 | if (nf_ip_checksum(skb: skb_in, hook, dataoff, protocol: proto) == 0) |
334 | icmp_send(skb_in, ICMP_DEST_UNREACH, code, info: 0); |
335 | } |
336 | EXPORT_SYMBOL_GPL(nf_send_unreach); |
337 | |
338 | MODULE_LICENSE("GPL" ); |
339 | |