File Index
Files and directories
Sign in
FILE

nf_conntrack_proto_icmpv6.c

File preview with syntax highlighting.

linux-6.19.14/net/netfilter/nf_conntrack_proto_icmpv6.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C)2003,2004 USAGI/WIDE Project
  4 *
  5 * Author:
  6 *	Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
  7 */
  8
  9#include <linux/types.h>
 10#include <linux/timer.h>
 11#include <linux/module.h>
 12#include <linux/netfilter.h>
 13#include <linux/in6.h>
 14#include <linux/icmpv6.h>
 15#include <linux/ipv6.h>
 16#include <net/ipv6.h>
 17#include <net/ip6_checksum.h>
 18#include <linux/seq_file.h>
 19#include <linux/netfilter_ipv6.h>
 20#include <net/netfilter/nf_conntrack_tuple.h>
 21#include <net/netfilter/nf_conntrack_l4proto.h>
 22#include <net/netfilter/nf_conntrack_core.h>
 23#include <net/netfilter/nf_conntrack_timeout.h>
 24#include <net/netfilter/nf_conntrack_zones.h>
 25#include <net/netfilter/nf_log.h>
 26
 27#include "nf_internals.h"
 28
 29static const unsigned int nf_ct_icmpv6_timeout = 30*HZ;
 30
 31bool icmpv6_pkt_to_tuple(const struct sk_buff *skb,
 32			 unsigned int dataoff,
 33			 struct net *net,
 34			 struct nf_conntrack_tuple *tuple)
 35{
 36	const struct icmp6hdr *hp;
 37	struct icmp6hdr _hdr;
 38
 39	hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
 40	if (hp == NULL)
 41		return false;
 42	tuple->dst.u.icmp.type = hp->icmp6_type;
 43	tuple->src.u.icmp.id = hp->icmp6_identifier;
 44	tuple->dst.u.icmp.code = hp->icmp6_code;
 45
 46	return true;
 47}
 48
 49/* Add 1; spaces filled with 0. */
 50static const u_int8_t invmap[] = {
 51	[ICMPV6_ECHO_REQUEST - 128]	= ICMPV6_ECHO_REPLY + 1,
 52	[ICMPV6_ECHO_REPLY - 128]	= ICMPV6_ECHO_REQUEST + 1,
 53	[ICMPV6_NI_QUERY - 128]		= ICMPV6_NI_REPLY + 1,
 54	[ICMPV6_NI_REPLY - 128]		= ICMPV6_NI_QUERY + 1
 55};
 56
 57static const u_int8_t noct_valid_new[] = {
 58	[ICMPV6_MGM_QUERY - 130] = 1,
 59	[ICMPV6_MGM_REPORT - 130] = 1,
 60	[ICMPV6_MGM_REDUCTION - 130] = 1,
 61	[NDISC_ROUTER_SOLICITATION - 130] = 1,
 62	[NDISC_ROUTER_ADVERTISEMENT - 130] = 1,
 63	[NDISC_NEIGHBOUR_SOLICITATION - 130] = 1,
 64	[NDISC_NEIGHBOUR_ADVERTISEMENT - 130] = 1,
 65	[ICMPV6_MLD2_REPORT - 130] = 1,
 66	[ICMPV6_MRDISC_ADV - 130] = 1,
 67	[ICMPV6_MRDISC_SOL - 130] = 1
 68};
 69
 70bool nf_conntrack_invert_icmpv6_tuple(struct nf_conntrack_tuple *tuple,
 71				      const struct nf_conntrack_tuple *orig)
 72{
 73	int type = orig->dst.u.icmp.type - 128;
 74	if (type < 0 || type >= sizeof(invmap) || !invmap[type])
 75		return false;
 76
 77	tuple->src.u.icmp.id   = orig->src.u.icmp.id;
 78	tuple->dst.u.icmp.type = invmap[type] - 1;
 79	tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
 80	return true;
 81}
 82
 83static unsigned int *icmpv6_get_timeouts(struct net *net)
 84{
 85	return &nf_icmpv6_pernet(net)->timeout;
 86}
 87
 88/* Returns verdict for packet, or -1 for invalid. */
 89int nf_conntrack_icmpv6_packet(struct nf_conn *ct,
 90			       struct sk_buff *skb,
 91			       enum ip_conntrack_info ctinfo,
 92			       const struct nf_hook_state *state)
 93{
 94	unsigned int *timeout = nf_ct_timeout_lookup(ct);
 95	static const u8 valid_new[] = {
 96		[ICMPV6_ECHO_REQUEST - 128] = 1,
 97		[ICMPV6_NI_QUERY - 128] = 1
 98	};
 99
100	if (state->pf != NFPROTO_IPV6)
101		return -NF_ACCEPT;
102
103	if (!nf_ct_is_confirmed(ct)) {
104		int type = ct->tuplehash[0].tuple.dst.u.icmp.type - 128;
105
106		if (type < 0 || type >= sizeof(valid_new) || !valid_new[type]) {
107			/* Can't create a new ICMPv6 `conn' with this. */
108			pr_debug("icmpv6: can't create new conn with type %u\n",
109				 type + 128);
110			nf_ct_dump_tuple_ipv6(&ct->tuplehash[0].tuple);
111			return -NF_ACCEPT;
112		}
113	}
114
115	if (!timeout)
116		timeout = icmpv6_get_timeouts(nf_ct_net(ct));
117
118	/* Do not immediately delete the connection after the first
119	   successful reply to avoid excessive conntrackd traffic
120	   and also to handle correctly ICMP echo reply duplicates. */
121	nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
122
123	return NF_ACCEPT;
124}
125
126
127static void icmpv6_error_log(const struct sk_buff *skb,
128			     const struct nf_hook_state *state,
129			     const char *msg)
130{
131	nf_l4proto_log_invalid(skb, state, IPPROTO_ICMPV6, "%s", msg);
132}
133
134static noinline_for_stack int
135nf_conntrack_icmpv6_redirect(struct nf_conn *tmpl, struct sk_buff *skb,
136			     unsigned int dataoff,
137			     const struct nf_hook_state *state)
138{
139	u8 hl = ipv6_hdr(skb)->hop_limit;
140	union nf_inet_addr outer_daddr;
141	union {
142		struct nd_opt_hdr nd_opt;
143		struct rd_msg rd_msg;
144	} tmp;
145	const struct nd_opt_hdr *nd_opt;
146	const struct rd_msg *rd_msg;
147
148	rd_msg = skb_header_pointer(skb, dataoff, sizeof(*rd_msg), &tmp.rd_msg);
149	if (!rd_msg) {
150		icmpv6_error_log(skb, state, "short redirect");
151		return -NF_ACCEPT;
152	}
153
154	if (rd_msg->icmph.icmp6_code != 0)
155		return NF_ACCEPT;
156
157	if (hl != 255 || !(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
158		icmpv6_error_log(skb, state, "invalid saddr or hoplimit for redirect");
159		return -NF_ACCEPT;
160	}
161
162	dataoff += sizeof(*rd_msg);
163
164	/* warning: rd_msg no longer usable after this call */
165	nd_opt = skb_header_pointer(skb, dataoff, sizeof(*nd_opt), &tmp.nd_opt);
166	if (!nd_opt || nd_opt->nd_opt_len == 0) {
167		icmpv6_error_log(skb, state, "redirect without options");
168		return -NF_ACCEPT;
169	}
170
171	/* We could call ndisc_parse_options(), but it would need
172	 * skb_linearize() and a bit more work.
173	 */
174	if (nd_opt->nd_opt_type != ND_OPT_REDIRECT_HDR)
175		return NF_ACCEPT;
176
177	memcpy(&outer_daddr.ip6, &ipv6_hdr(skb)->daddr,
178	       sizeof(outer_daddr.ip6));
179	dataoff += 8;
180	return nf_conntrack_inet_error(tmpl, skb, dataoff, state,
181				       IPPROTO_ICMPV6, &outer_daddr);
182}
183
184int nf_conntrack_icmpv6_error(struct nf_conn *tmpl,
185			      struct sk_buff *skb,
186			      unsigned int dataoff,
187			      const struct nf_hook_state *state)
188{
189	union nf_inet_addr outer_daddr;
190	const struct icmp6hdr *icmp6h;
191	struct icmp6hdr _ih;
192	int type;
193
194	icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih);
195	if (icmp6h == NULL) {
196		icmpv6_error_log(skb, state, "short packet");
197		return -NF_ACCEPT;
198	}
199
200	if (state->hook == NF_INET_PRE_ROUTING &&
201	    state->net->ct.sysctl_checksum &&
202	    nf_ip6_checksum(skb, state->hook, dataoff, IPPROTO_ICMPV6)) {
203		icmpv6_error_log(skb, state, "ICMPv6 checksum failed");
204		return -NF_ACCEPT;
205	}
206
207	type = icmp6h->icmp6_type - 130;
208	if (type >= 0 && type < sizeof(noct_valid_new) &&
209	    noct_valid_new[type]) {
210		nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
211		return NF_ACCEPT;
212	}
213
214	if (icmp6h->icmp6_type == NDISC_REDIRECT)
215		return nf_conntrack_icmpv6_redirect(tmpl, skb, dataoff, state);
216
217	/* is not error message ? */
218	if (icmp6h->icmp6_type >= 128)
219		return NF_ACCEPT;
220
221	memcpy(&outer_daddr.ip6, &ipv6_hdr(skb)->daddr,
222	       sizeof(outer_daddr.ip6));
223	dataoff += sizeof(*icmp6h);
224	return nf_conntrack_inet_error(tmpl, skb, dataoff, state,
225				       IPPROTO_ICMPV6, &outer_daddr);
226}
227
228#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
229
230#include <linux/netfilter/nfnetlink.h>
231#include <linux/netfilter/nfnetlink_conntrack.h>
232static int icmpv6_tuple_to_nlattr(struct sk_buff *skb,
233				  const struct nf_conntrack_tuple *t)
234{
235	if (nla_put_be16(skb, CTA_PROTO_ICMPV6_ID, t->src.u.icmp.id) ||
236	    nla_put_u8(skb, CTA_PROTO_ICMPV6_TYPE, t->dst.u.icmp.type) ||
237	    nla_put_u8(skb, CTA_PROTO_ICMPV6_CODE, t->dst.u.icmp.code))
238		goto nla_put_failure;
239	return 0;
240
241nla_put_failure:
242	return -1;
243}
244
245static const struct nla_policy icmpv6_nla_policy[CTA_PROTO_MAX+1] = {
246	[CTA_PROTO_ICMPV6_TYPE]	= { .type = NLA_U8 },
247	[CTA_PROTO_ICMPV6_CODE]	= { .type = NLA_U8 },
248	[CTA_PROTO_ICMPV6_ID]	= { .type = NLA_U16 },
249};
250
251static int icmpv6_nlattr_to_tuple(struct nlattr *tb[],
252				struct nf_conntrack_tuple *tuple,
253				u_int32_t flags)
254{
255	if (flags & CTA_FILTER_FLAG(CTA_PROTO_ICMPV6_TYPE)) {
256		if (!tb[CTA_PROTO_ICMPV6_TYPE])
257			return -EINVAL;
258
259		tuple->dst.u.icmp.type = nla_get_u8(tb[CTA_PROTO_ICMPV6_TYPE]);
260		if (tuple->dst.u.icmp.type < 128 ||
261		    tuple->dst.u.icmp.type - 128 >= sizeof(invmap) ||
262		    !invmap[tuple->dst.u.icmp.type - 128])
263			return -EINVAL;
264	}
265
266	if (flags & CTA_FILTER_FLAG(CTA_PROTO_ICMPV6_CODE)) {
267		if (!tb[CTA_PROTO_ICMPV6_CODE])
268			return -EINVAL;
269
270		tuple->dst.u.icmp.code = nla_get_u8(tb[CTA_PROTO_ICMPV6_CODE]);
271	}
272
273	if (flags & CTA_FILTER_FLAG(CTA_PROTO_ICMPV6_ID)) {
274		if (!tb[CTA_PROTO_ICMPV6_ID])
275			return -EINVAL;
276
277		tuple->src.u.icmp.id = nla_get_be16(tb[CTA_PROTO_ICMPV6_ID]);
278	}
279
280	return 0;
281}
282
283static unsigned int icmpv6_nlattr_tuple_size(void)
284{
285	static unsigned int size __read_mostly;
286
287	if (!size)
288		size = nla_policy_len(icmpv6_nla_policy, CTA_PROTO_MAX + 1);
289
290	return size;
291}
292#endif
293
294#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
295
296#include <linux/netfilter/nfnetlink.h>
297#include <linux/netfilter/nfnetlink_cttimeout.h>
298
299static int icmpv6_timeout_nlattr_to_obj(struct nlattr *tb[],
300					struct net *net, void *data)
301{
302	unsigned int *timeout = data;
303	struct nf_icmp_net *in = nf_icmpv6_pernet(net);
304
305	if (!timeout)
306		timeout = icmpv6_get_timeouts(net);
307	if (tb[CTA_TIMEOUT_ICMPV6_TIMEOUT]) {
308		*timeout =
309		    ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMPV6_TIMEOUT])) * HZ;
310	} else {
311		/* Set default ICMPv6 timeout. */
312		*timeout = in->timeout;
313	}
314	return 0;
315}
316
317static int
318icmpv6_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
319{
320	const unsigned int *timeout = data;
321
322	if (nla_put_be32(skb, CTA_TIMEOUT_ICMPV6_TIMEOUT, htonl(*timeout / HZ)))
323		goto nla_put_failure;
324	return 0;
325
326nla_put_failure:
327	return -ENOSPC;
328}
329
330static const struct nla_policy
331icmpv6_timeout_nla_policy[CTA_TIMEOUT_ICMPV6_MAX+1] = {
332	[CTA_TIMEOUT_ICMPV6_TIMEOUT]	= { .type = NLA_U32 },
333};
334#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
335
336void nf_conntrack_icmpv6_init_net(struct net *net)
337{
338	struct nf_icmp_net *in = nf_icmpv6_pernet(net);
339
340	in->timeout = nf_ct_icmpv6_timeout;
341}
342
343const struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 =
344{
345	.l4proto		= IPPROTO_ICMPV6,
346#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
347	.tuple_to_nlattr	= icmpv6_tuple_to_nlattr,
348	.nlattr_tuple_size	= icmpv6_nlattr_tuple_size,
349	.nlattr_to_tuple	= icmpv6_nlattr_to_tuple,
350	.nla_policy		= icmpv6_nla_policy,
351#endif
352#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
353	.ctnl_timeout		= {
354		.nlattr_to_obj	= icmpv6_timeout_nlattr_to_obj,
355		.obj_to_nlattr	= icmpv6_timeout_obj_to_nlattr,
356		.nlattr_max	= CTA_TIMEOUT_ICMP_MAX,
357		.obj_size	= sizeof(unsigned int),
358		.nla_policy	= icmpv6_timeout_nla_policy,
359	},
360#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
361};
~/ Read-only file preview.