File Index
Files and directories
Sign in
FILE

nf_reject_ipv6.c

File preview with syntax highlighting.

linux-6.19.14/net/ipv6/netfilter/nf_reject_ipv6.c C
  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/ipv6.h>
  8#include <net/ip6_route.h>
  9#include <net/ip6_fib.h>
 10#include <net/ip6_checksum.h>
 11#include <net/netfilter/ipv6/nf_reject.h>
 12#include <linux/netfilter_ipv6.h>
 13#include <linux/netfilter_bridge.h>
 14
 15static struct ipv6hdr *
 16nf_reject_ip6hdr_put(struct sk_buff *nskb,
 17		     const struct sk_buff *oldskb,
 18		     __u8 protocol, int hoplimit);
 19static void
 20nf_reject_ip6_tcphdr_put(struct sk_buff *nskb,
 21			 const struct sk_buff *oldskb,
 22			 const struct tcphdr *oth, unsigned int otcplen);
 23static const struct tcphdr *
 24nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb,
 25			 struct tcphdr *otcph,
 26			 unsigned int *otcplen, int hook);
 27
 28static bool nf_reject_v6_csum_ok(struct sk_buff *skb, int hook)
 29{
 30	const struct ipv6hdr *ip6h = ipv6_hdr(skb);
 31	int thoff;
 32	__be16 fo;
 33	u8 proto = ip6h->nexthdr;
 34
 35	if (skb_csum_unnecessary(skb))
 36		return true;
 37
 38	if (ip6h->payload_len &&
 39	    pskb_trim_rcsum(skb, ntohs(ip6h->payload_len) + sizeof(*ip6h)))
 40		return false;
 41
 42	ip6h = ipv6_hdr(skb);
 43	thoff = ipv6_skip_exthdr(skb, ((u8*)(ip6h+1) - skb->data), &proto, &fo);
 44	if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0)
 45		return false;
 46
 47	if (!nf_reject_verify_csum(skb, thoff, proto))
 48		return true;
 49
 50	return nf_ip6_checksum(skb, hook, thoff, proto) == 0;
 51}
 52
 53static int nf_reject_ip6hdr_validate(struct sk_buff *skb)
 54{
 55	struct ipv6hdr *hdr;
 56	u32 pkt_len;
 57
 58	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
 59		return 0;
 60
 61	hdr = ipv6_hdr(skb);
 62	if (hdr->version != 6)
 63		return 0;
 64
 65	pkt_len = ntohs(hdr->payload_len);
 66	if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
 67		return 0;
 68
 69	return 1;
 70}
 71
 72struct sk_buff *nf_reject_skb_v6_tcp_reset(struct net *net,
 73					   struct sk_buff *oldskb,
 74					   const struct net_device *dev,
 75					   int hook)
 76{
 77	struct sk_buff *nskb;
 78	const struct tcphdr *oth;
 79	struct tcphdr _oth;
 80	unsigned int otcplen;
 81	struct ipv6hdr *nip6h;
 82
 83	if (!nf_reject_ip6hdr_validate(oldskb))
 84		return NULL;
 85
 86	oth = nf_reject_ip6_tcphdr_get(oldskb, &_oth, &otcplen, hook);
 87	if (!oth)
 88		return NULL;
 89
 90	nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct tcphdr) +
 91			 LL_MAX_HEADER, GFP_ATOMIC);
 92	if (!nskb)
 93		return NULL;
 94
 95	nskb->dev = (struct net_device *)dev;
 96
 97	skb_reserve(nskb, LL_MAX_HEADER);
 98	nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP,
 99				     READ_ONCE(net->ipv6.devconf_all->hop_limit));
100	nf_reject_ip6_tcphdr_put(nskb, oldskb, oth, otcplen);
101	nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr));
102
103	return nskb;
104}
105EXPORT_SYMBOL_GPL(nf_reject_skb_v6_tcp_reset);
106
107static bool nf_skb_is_icmp6_unreach(const struct sk_buff *skb)
108{
109	const struct ipv6hdr *ip6h = ipv6_hdr(skb);
110	u8 proto = ip6h->nexthdr;
111	u8 _type, *tp;
112	int thoff;
113	__be16 fo;
114
115	thoff = ipv6_skip_exthdr(skb, ((u8 *)(ip6h + 1) - skb->data), &proto, &fo);
116
117	if (thoff < 0 || thoff >= skb->len || fo != 0)
118		return false;
119
120	if (proto != IPPROTO_ICMPV6)
121		return false;
122
123	tp = skb_header_pointer(skb,
124				thoff + offsetof(struct icmp6hdr, icmp6_type),
125				sizeof(_type), &_type);
126
127	if (!tp)
128		return false;
129
130	return *tp == ICMPV6_DEST_UNREACH;
131}
132
133struct sk_buff *nf_reject_skb_v6_unreach(struct net *net,
134					 struct sk_buff *oldskb,
135					 const struct net_device *dev,
136					 int hook, u8 code)
137{
138	struct sk_buff *nskb;
139	struct ipv6hdr *nip6h;
140	struct icmp6hdr *icmp6h;
141	unsigned int len;
142
143	if (!nf_reject_ip6hdr_validate(oldskb))
144		return NULL;
145
146	/* Don't reply to ICMPV6_DEST_UNREACH with ICMPV6_DEST_UNREACH */
147	if (nf_skb_is_icmp6_unreach(oldskb))
148		return NULL;
149
150	/* Include "As much of invoking packet as possible without the ICMPv6
151	 * packet exceeding the minimum IPv6 MTU" in the ICMP payload.
152	 */
153	len = min_t(unsigned int, 1220, oldskb->len);
154
155	if (!pskb_may_pull(oldskb, len))
156		return NULL;
157
158	if (!nf_reject_v6_csum_ok(oldskb, hook))
159		return NULL;
160
161	nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr) +
162			 LL_MAX_HEADER + len, GFP_ATOMIC);
163	if (!nskb)
164		return NULL;
165
166	nskb->dev = (struct net_device *)dev;
167
168	skb_reserve(nskb, LL_MAX_HEADER);
169	nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_ICMPV6,
170				     READ_ONCE(net->ipv6.devconf_all->hop_limit));
171
172	skb_reset_transport_header(nskb);
173	icmp6h = skb_put_zero(nskb, sizeof(struct icmp6hdr));
174	icmp6h->icmp6_type = ICMPV6_DEST_UNREACH;
175	icmp6h->icmp6_code = code;
176
177	skb_put_data(nskb, skb_network_header(oldskb), len);
178	nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr));
179
180	icmp6h->icmp6_cksum =
181		csum_ipv6_magic(&nip6h->saddr, &nip6h->daddr,
182				nskb->len - sizeof(struct ipv6hdr),
183				IPPROTO_ICMPV6,
184				csum_partial(icmp6h,
185					     nskb->len - sizeof(struct ipv6hdr),
186					     0));
187
188	return nskb;
189}
190EXPORT_SYMBOL_GPL(nf_reject_skb_v6_unreach);
191
192static const struct tcphdr *
193nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb,
194			 struct tcphdr *otcph,
195			 unsigned int *otcplen, int hook)
196{
197	const struct ipv6hdr *oip6h = ipv6_hdr(oldskb);
198	u8 proto;
199	__be16 frag_off;
200	int tcphoff;
201
202	proto = oip6h->nexthdr;
203	tcphoff = ipv6_skip_exthdr(oldskb, ((u8 *)(oip6h + 1) - oldskb->data),
204				   &proto, &frag_off);
205
206	if ((tcphoff < 0) || (tcphoff > oldskb->len)) {
207		pr_debug("Cannot get TCP header.\n");
208		return NULL;
209	}
210
211	*otcplen = oldskb->len - tcphoff;
212
213	/* IP header checks: fragment, too short. */
214	if (proto != IPPROTO_TCP || *otcplen < sizeof(struct tcphdr)) {
215		pr_debug("proto(%d) != IPPROTO_TCP or too short (len = %d)\n",
216			 proto, *otcplen);
217		return NULL;
218	}
219
220	otcph = skb_header_pointer(oldskb, tcphoff, sizeof(struct tcphdr),
221				   otcph);
222	if (otcph == NULL)
223		return NULL;
224
225	/* No RST for RST. */
226	if (otcph->rst) {
227		pr_debug("RST is set\n");
228		return NULL;
229	}
230
231	/* Check checksum. */
232	if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) {
233		pr_debug("TCP checksum is invalid\n");
234		return NULL;
235	}
236
237	return otcph;
238}
239
240static struct ipv6hdr *
241nf_reject_ip6hdr_put(struct sk_buff *nskb,
242		     const struct sk_buff *oldskb,
243		     __u8 protocol, int hoplimit)
244{
245	struct ipv6hdr *ip6h;
246	const struct ipv6hdr *oip6h = ipv6_hdr(oldskb);
247#define DEFAULT_TOS_VALUE	0x0U
248	const __u8 tclass = DEFAULT_TOS_VALUE;
249
250	skb_put(nskb, sizeof(struct ipv6hdr));
251	skb_reset_network_header(nskb);
252	ip6h = ipv6_hdr(nskb);
253	ip6_flow_hdr(ip6h, tclass, 0);
254	ip6h->hop_limit = hoplimit;
255	ip6h->nexthdr = protocol;
256	ip6h->saddr = oip6h->daddr;
257	ip6h->daddr = oip6h->saddr;
258
259	nskb->protocol = htons(ETH_P_IPV6);
260
261	return ip6h;
262}
263
264static void
265nf_reject_ip6_tcphdr_put(struct sk_buff *nskb,
266			 const struct sk_buff *oldskb,
267			 const struct tcphdr *oth, unsigned int otcplen)
268{
269	struct tcphdr *tcph;
270
271	skb_reset_transport_header(nskb);
272	tcph = skb_put_zero(nskb, sizeof(struct tcphdr));
273	/* Truncate to length (no data) */
274	tcph->doff = sizeof(struct tcphdr)/4;
275	tcph->source = oth->dest;
276	tcph->dest = oth->source;
277
278	if (oth->ack) {
279		tcph->seq = oth->ack_seq;
280	} else {
281		tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin +
282				      otcplen - (oth->doff<<2));
283		tcph->ack = 1;
284	}
285
286	tcph->rst = 1;
287
288	/* Adjust TCP checksum */
289	tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr,
290				      &ipv6_hdr(nskb)->daddr,
291				      sizeof(struct tcphdr), IPPROTO_TCP,
292				      csum_partial(tcph,
293						   sizeof(struct tcphdr), 0));
294}
295
296static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in)
297{
298	struct dst_entry *dst = NULL;
299	struct flowi fl;
300
301	memset(&fl, 0, sizeof(struct flowi));
302	fl.u.ip6.daddr = ipv6_hdr(skb_in)->saddr;
303	nf_ip6_route(dev_net(skb_in->dev), &dst, &fl, false);
304	if (!dst)
305		return -1;
306
307	skb_dst_set(skb_in, dst);
308	return 0;
309}
310
311void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb,
312		    int hook)
313{
314	const struct ipv6hdr *oip6h = ipv6_hdr(oldskb);
315	struct dst_entry *dst = NULL;
316	const struct tcphdr *otcph;
317	struct sk_buff *nskb;
318	struct tcphdr _otcph;
319	unsigned int otcplen;
320	struct flowi6 fl6;
321
322	if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) ||
323	    (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) {
324		pr_debug("addr is not unicast.\n");
325		return;
326	}
327
328	otcph = nf_reject_ip6_tcphdr_get(oldskb, &_otcph, &otcplen, hook);
329	if (!otcph)
330		return;
331
332	memset(&fl6, 0, sizeof(fl6));
333	fl6.flowi6_proto = IPPROTO_TCP;
334	fl6.saddr = oip6h->daddr;
335	fl6.daddr = oip6h->saddr;
336	fl6.fl6_sport = otcph->dest;
337	fl6.fl6_dport = otcph->source;
338
339	if (!skb_dst(oldskb)) {
340		nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false);
341		if (!dst)
342			return;
343		skb_dst_set(oldskb, dst);
344	}
345
346	fl6.flowi6_oif = l3mdev_master_ifindex(skb_dst_dev(oldskb));
347	fl6.flowi6_mark = IP6_REPLY_MARK(net, oldskb->mark);
348	security_skb_classify_flow(oldskb, flowi6_to_flowi_common(&fl6));
349	dst = ip6_route_output(net, NULL, &fl6);
350	if (dst->error) {
351		dst_release(dst);
352		return;
353	}
354	dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
355	if (IS_ERR(dst))
356		return;
357
358	nskb = alloc_skb(LL_MAX_HEADER + sizeof(struct ipv6hdr) +
359			 sizeof(struct tcphdr) + dst->trailer_len,
360			 GFP_ATOMIC);
361
362	if (!nskb) {
363		net_dbg_ratelimited("cannot alloc skb\n");
364		dst_release(dst);
365		return;
366	}
367
368	skb_dst_set(nskb, dst);
369
370	nskb->mark = fl6.flowi6_mark;
371
372	skb_reserve(nskb, LL_MAX_HEADER);
373	nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, ip6_dst_hoplimit(dst));
374	nf_reject_ip6_tcphdr_put(nskb, oldskb, otcph, otcplen);
375
376	nf_ct_attach(nskb, oldskb);
377	nf_ct_set_closing(skb_nfct(oldskb));
378
379#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
380	/* If we use ip6_local_out for bridged traffic, the MAC source on
381	 * the RST will be ours, instead of the destination's.  This confuses
382	 * some routers/firewalls, and they drop the packet.  So we need to
383	 * build the eth header using the original destination's MAC as the
384	 * source, and send the RST packet directly.
385	 */
386	if (nf_bridge_info_exists(oldskb)) {
387		struct ethhdr *oeth = eth_hdr(oldskb);
388		struct ipv6hdr *ip6h = ipv6_hdr(nskb);
389		struct net_device *br_indev;
390
391		br_indev = nf_bridge_get_physindev(oldskb, net);
392		if (!br_indev) {
393			kfree_skb(nskb);
394			return;
395		}
396
397		nskb->dev = br_indev;
398		nskb->protocol = htons(ETH_P_IPV6);
399		ip6h->payload_len = htons(sizeof(struct tcphdr));
400		if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
401				    oeth->h_source, oeth->h_dest, nskb->len) < 0) {
402			kfree_skb(nskb);
403			return;
404		}
405		dev_queue_xmit(nskb);
406	} else
407#endif
408		ip6_local_out(net, sk, nskb);
409}
410EXPORT_SYMBOL_GPL(nf_send_reset6);
411
412static bool reject6_csum_ok(struct sk_buff *skb, int hook)
413{
414	const struct ipv6hdr *ip6h = ipv6_hdr(skb);
415	int thoff;
416	__be16 fo;
417	u8 proto;
418
419	if (skb_csum_unnecessary(skb))
420		return true;
421
422	proto = ip6h->nexthdr;
423	thoff = ipv6_skip_exthdr(skb, ((u8 *)(ip6h + 1) - skb->data), &proto, &fo);
424
425	if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0)
426		return false;
427
428	if (!nf_reject_verify_csum(skb, thoff, proto))
429		return true;
430
431	return nf_ip6_checksum(skb, hook, thoff, proto) == 0;
432}
433
434void nf_send_unreach6(struct net *net, struct sk_buff *skb_in,
435		      unsigned char code, unsigned int hooknum)
436{
437	if (!reject6_csum_ok(skb_in, hooknum))
438		return;
439
440	if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
441		skb_in->dev = net->loopback_dev;
442
443	if (!skb_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0)
444		return;
445
446	icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0);
447}
448EXPORT_SYMBOL_GPL(nf_send_unreach6);
449
450MODULE_LICENSE("GPL");
451MODULE_DESCRIPTION("IPv6 packet rejection core");
~/ Read-only file preview.