Blame view

net/ipv6/icmp.c 21.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   *	Internet Control Message Protocol (ICMPv6)
   *	Linux INET6 implementation
   *
   *	Authors:
   *	Pedro Roque		<roque@di.fc.ul.pt>
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
   *	Based on net/ipv4/icmp.c
   *
   *	RFC 1885
   *
   *	This program is free software; you can redistribute it and/or
   *      modify it under the terms of the GNU General Public License
   *      as published by the Free Software Foundation; either version
   *      2 of the License, or (at your option) any later version.
   */
  
  /*
   *	Changes:
   *
   *	Andi Kleen		:	exception handling
   *	Andi Kleen			add rate limits. never reply to a icmp.
   *					add more length checks and other fixes.
   *	yoshfuji		:	ensure to sent parameter problem for
   *					fragments.
   *	YOSHIFUJI Hideaki @USAGI:	added sysctl for icmp rate limit.
   *	Randy Dunlap and
   *	YOSHIFUJI Hideaki @USAGI:	Per-interface statistics support
   *	Kazunori MIYAZAWA @USAGI:       change output process to use ip6_append_data
   */
  
  #include <linux/module.h>
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/socket.h>
  #include <linux/in.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
41
  #include <linux/sockios.h>
  #include <linux/net.h>
  #include <linux/skbuff.h>
  #include <linux/init.h>
763ecff18   Yasuyuki Kozakai   [NETFILTER]: nf_c...
42
  #include <linux/netfilter.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
43
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  
  #ifdef CONFIG_SYSCTL
  #include <linux/sysctl.h>
  #endif
  
  #include <linux/inet.h>
  #include <linux/netdevice.h>
  #include <linux/icmpv6.h>
  
  #include <net/ip.h>
  #include <net/sock.h>
  
  #include <net/ipv6.h>
  #include <net/ip6_checksum.h>
  #include <net/protocol.h>
  #include <net/raw.h>
  #include <net/rawv6.h>
  #include <net/transp_v6.h>
  #include <net/ip6_route.h>
  #include <net/addrconf.h>
  #include <net/icmp.h>
8b7817f3a   Herbert Xu   [IPSEC]: Add ICMP...
65
  #include <net/xfrm.h>
1ed8516f0   Denis V. Lunev   [IPV6]: Simplify ...
66
  #include <net/inet_common.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
  
  #include <asm/uaccess.h>
  #include <asm/system.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
76
  /*
   *	The ICMP socket(s). This is the most convenient way to flow control
   *	our ICMP output as well as maintain a clean interface throughout
   *	all layers. All Socketless IP sends will soon be gone.
   *
   *	On SMP we have one ICMP socket per-cpu.
   */
98c6d1b26   Denis V. Lunev   [NETNS]: Make icm...
77
78
79
80
  static inline struct sock *icmpv6_sk(struct net *net)
  {
  	return net->ipv6.icmp_sk[smp_processor_id()];
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81

e5bbef20e   Herbert Xu   [IPV6]: Replace s...
82
  static int icmpv6_rcv(struct sk_buff *skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83

41135cc83   Alexey Dobriyan   net: constify str...
84
  static const struct inet6_protocol icmpv6_protocol = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  	.handler	=	icmpv6_rcv,
8b7817f3a   Herbert Xu   [IPSEC]: Add ICMP...
86
  	.flags		=	INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  };
fdc0bde90   Denis V. Lunev   icmp: icmp_sk() s...
88
  static __inline__ struct sock *icmpv6_xmit_lock(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  {
fdc0bde90   Denis V. Lunev   icmp: icmp_sk() s...
90
  	struct sock *sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  	local_bh_disable();
fdc0bde90   Denis V. Lunev   icmp: icmp_sk() s...
92
  	sk = icmpv6_sk(net);
405666db8   Denis V. Lunev   [ICMP]: Pass prop...
93
  	if (unlikely(!spin_trylock(&sk->sk_lock.slock))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
  		/* This can happen if the output path (f.e. SIT or
  		 * ip6ip6 tunnel) signals dst_link_failure() for an
  		 * outgoing ICMP6 packet.
  		 */
  		local_bh_enable();
fdc0bde90   Denis V. Lunev   icmp: icmp_sk() s...
99
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
  	}
fdc0bde90   Denis V. Lunev   icmp: icmp_sk() s...
101
  	return sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  }
405666db8   Denis V. Lunev   [ICMP]: Pass prop...
103
  static __inline__ void icmpv6_xmit_unlock(struct sock *sk)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
  {
405666db8   Denis V. Lunev   [ICMP]: Pass prop...
105
  	spin_unlock_bh(&sk->sk_lock.slock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  }
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
107
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
   * Slightly more convenient version of icmpv6_send.
   */
d5fdd6bab   Brian Haley   ipv6: Use correct...
110
  void icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  {
3ffe533c8   Alexey Dobriyan   ipv6: drop unused...
112
  	icmpv6_send(skb, ICMPV6_PARAMPROB, code, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  	kfree_skb(skb);
  }
  
  /*
   * Figure out, may we reply to this packet with icmp error.
   *
   * We do not reply, if:
   *	- it was icmp error message.
   *	- it is truncated, so that it is known, that protocol is ICMPV6
   *	  (i.e. in the middle of some exthdr)
   *
   *	--ANK (980726)
   */
  
  static int is_ineligible(struct sk_buff *skb)
  {
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
129
  	int ptr = (u8 *)(ipv6_hdr(skb) + 1) - skb->data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
  	int len = skb->len - ptr;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
131
  	__u8 nexthdr = ipv6_hdr(skb)->nexthdr;
75f2811c6   Jesse Gross   ipv6: Add fragmen...
132
  	__be16 frag_off;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
  
  	if (len < 0)
  		return 1;
75f2811c6   Jesse Gross   ipv6: Add fragmen...
136
  	ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, &frag_off);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
140
141
142
143
144
145
146
147
148
149
  	if (ptr < 0)
  		return 0;
  	if (nexthdr == IPPROTO_ICMPV6) {
  		u8 _type, *tp;
  		tp = skb_header_pointer(skb,
  			ptr+offsetof(struct icmp6hdr, icmp6_type),
  			sizeof(_type), &_type);
  		if (tp == NULL ||
  		    !(*tp & ICMPV6_INFOMSG_MASK))
  			return 1;
  	}
  	return 0;
  }
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
150
151
  /*
   * Check the ICMP output rate limit
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
   */
92d868292   David S. Miller   inetpeer: Move IC...
153
  static inline bool icmpv6_xrlim_allow(struct sock *sk, u8 type,
4c9483b2f   David S. Miller   ipv6: Convert to ...
154
  				      struct flowi6 *fl6)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
  {
  	struct dst_entry *dst;
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
157
  	struct net *net = sock_net(sk);
92d868292   David S. Miller   inetpeer: Move IC...
158
  	bool res = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
161
  
  	/* Informational messages are not limited. */
  	if (type & ICMPV6_INFOMSG_MASK)
92d868292   David S. Miller   inetpeer: Move IC...
162
  		return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
  
  	/* Do not limit pmtu discovery, it would break it. */
  	if (type == ICMPV6_PKT_TOOBIG)
92d868292   David S. Miller   inetpeer: Move IC...
166
  		return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167

1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
168
  	/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
171
172
  	 * Look up the output route.
  	 * XXX: perhaps the expire for routing entries cloned by
  	 * this lookup should be more aggressive (not longer than timeout).
  	 */
4c9483b2f   David S. Miller   ipv6: Convert to ...
173
  	dst = ip6_route_output(net, sk, fl6);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
  	if (dst->error) {
3bd653c84   Denis V. Lunev   netns: add net pa...
175
  		IP6_INC_STATS(net, ip6_dst_idev(dst),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
176
  			      IPSTATS_MIB_OUTNOROUTES);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  	} else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) {
92d868292   David S. Miller   inetpeer: Move IC...
178
  		res = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
180
  	} else {
  		struct rt6_info *rt = (struct rt6_info *)dst;
9a43b709a   Benjamin Thery   [NETNS][IPV6] icm...
181
  		int tmo = net->ipv6.sysctl.icmpv6_time;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
184
185
  
  		/* Give more bandwidth to wider prefixes. */
  		if (rt->rt6i_dst.plen < 128)
  			tmo >>= ((128 - rt->rt6i_dst.plen)>>5);
92d868292   David S. Miller   inetpeer: Move IC...
186
187
188
  		if (!rt->rt6i_peer)
  			rt6_bind_peer(rt, 1);
  		res = inet_peer_xrlim_allow(rt->rt6i_peer, tmo);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
192
193
194
195
196
  	}
  	dst_release(dst);
  	return res;
  }
  
  /*
   *	an inline helper for the "simple" if statement below
   *	checks if parameter problem report is caused by an
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
197
   *	unrecognized IPv6 option that has the Option Type
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
202
203
   *	highest-order two bits set to 10
   */
  
  static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
  {
  	u8 _optval, *op;
bbe735e42   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
204
  	offset += skb_network_offset(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
209
  	op = skb_header_pointer(skb, offset, sizeof(_optval), &_optval);
  	if (op == NULL)
  		return 1;
  	return (*op & 0xC0) == 0x80;
  }
4c9483b2f   David S. Miller   ipv6: Convert to ...
210
  static int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct icmp6hdr *thdr, int len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
  {
  	struct sk_buff *skb;
  	struct icmp6hdr *icmp6h;
  	int err = 0;
  
  	if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
  		goto out;
cc70ab261   Arnaldo Carvalho de Melo   [ICMP6]: Introduc...
218
  	icmp6h = icmp6_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
221
222
  	memcpy(icmp6h, thdr, sizeof(struct icmp6hdr));
  	icmp6h->icmp6_cksum = 0;
  
  	if (skb_queue_len(&sk->sk_write_queue) == 1) {
07f0757a6   Joe Perches   include/net net/ ...
223
  		skb->csum = csum_partial(icmp6h,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  					sizeof(struct icmp6hdr), skb->csum);
4c9483b2f   David S. Miller   ipv6: Convert to ...
225
226
227
  		icmp6h->icmp6_cksum = csum_ipv6_magic(&fl6->saddr,
  						      &fl6->daddr,
  						      len, fl6->flowi6_proto,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
  						      skb->csum);
  	} else {
868c86bcb   Al Viro   [NET]: annotate c...
230
  		__wsum tmp_csum = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
233
234
  
  		skb_queue_walk(&sk->sk_write_queue, skb) {
  			tmp_csum = csum_add(tmp_csum, skb->csum);
  		}
07f0757a6   Joe Perches   include/net net/ ...
235
  		tmp_csum = csum_partial(icmp6h,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  					sizeof(struct icmp6hdr), tmp_csum);
4c9483b2f   David S. Miller   ipv6: Convert to ...
237
238
239
  		icmp6h->icmp6_cksum = csum_ipv6_magic(&fl6->saddr,
  						      &fl6->daddr,
  						      len, fl6->flowi6_proto,
868c86bcb   Al Viro   [NET]: annotate c...
240
  						      tmp_csum);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
244
245
246
247
248
249
  	ip6_push_pending_frames(sk);
  out:
  	return err;
  }
  
  struct icmpv6_msg {
  	struct sk_buff	*skb;
  	int		offset;
763ecff18   Yasuyuki Kozakai   [NETFILTER]: nf_c...
250
  	uint8_t		type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
254
255
256
  };
  
  static int icmpv6_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb)
  {
  	struct icmpv6_msg *msg = (struct icmpv6_msg *) from;
  	struct sk_buff *org_skb = msg->skb;
5f92a7388   Al Viro   [NET]: Annotate c...
257
  	__wsum csum = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
261
  
  	csum = skb_copy_and_csum_bits(org_skb, msg->offset + offset,
  				      to, len, csum);
  	skb->csum = csum_block_add(skb->csum, csum, odd);
763ecff18   Yasuyuki Kozakai   [NETFILTER]: nf_c...
262
263
  	if (!(msg->type & ICMPV6_INFOMSG_MASK))
  		nf_ct_attach(skb, org_skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
  	return 0;
  }
59fbb3a61   Masahide NAKAMURA   [IPV6] MIP6: Load...
266
  #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
793832361   Masahide NAKAMURA   [IPV6] MIP6: Reve...
267
268
  static void mip6_addr_swap(struct sk_buff *skb)
  {
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
269
  	struct ipv6hdr *iph = ipv6_hdr(skb);
793832361   Masahide NAKAMURA   [IPV6] MIP6: Reve...
270
271
272
273
274
275
276
277
  	struct inet6_skb_parm *opt = IP6CB(skb);
  	struct ipv6_destopt_hao *hao;
  	struct in6_addr tmp;
  	int off;
  
  	if (opt->dsthao) {
  		off = ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO);
  		if (likely(off >= 0)) {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
278
279
  			hao = (struct ipv6_destopt_hao *)
  					(skb_network_header(skb) + off);
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
280
281
282
  			tmp = iph->saddr;
  			iph->saddr = hao->addr;
  			hao->addr = tmp;
793832361   Masahide NAKAMURA   [IPV6] MIP6: Reve...
283
284
285
286
287
288
  		}
  	}
  }
  #else
  static inline void mip6_addr_swap(struct sk_buff *skb) {}
  #endif
b42835dbe   David S. Miller   ipv6: Make icmp r...
289
  static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb,
4c9483b2f   David S. Miller   ipv6: Convert to ...
290
  					     struct sock *sk, struct flowi6 *fl6)
b42835dbe   David S. Miller   ipv6: Make icmp r...
291
292
  {
  	struct dst_entry *dst, *dst2;
4c9483b2f   David S. Miller   ipv6: Convert to ...
293
  	struct flowi6 fl2;
b42835dbe   David S. Miller   ipv6: Make icmp r...
294
  	int err;
4c9483b2f   David S. Miller   ipv6: Convert to ...
295
  	err = ip6_dst_lookup(sk, &dst, fl6);
b42835dbe   David S. Miller   ipv6: Make icmp r...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
  	if (err)
  		return ERR_PTR(err);
  
  	/*
  	 * We won't send icmp if the destination is known
  	 * anycast.
  	 */
  	if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) {
  		LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source
  ");
  		dst_release(dst);
  		return ERR_PTR(-EINVAL);
  	}
  
  	/* No need to clone since we're just using its address. */
  	dst2 = dst;
4c9483b2f   David S. Miller   ipv6: Convert to ...
312
  	dst = xfrm_lookup(net, dst, flowi6_to_flowi(fl6), sk, 0);
452edd598   David S. Miller   xfrm: Return dst ...
313
  	if (!IS_ERR(dst)) {
b42835dbe   David S. Miller   ipv6: Make icmp r...
314
315
  		if (dst != dst2)
  			return dst;
452edd598   David S. Miller   xfrm: Return dst ...
316
317
318
319
320
  	} else {
  		if (PTR_ERR(dst) == -EPERM)
  			dst = NULL;
  		else
  			return dst;
b42835dbe   David S. Miller   ipv6: Make icmp r...
321
  	}
4c9483b2f   David S. Miller   ipv6: Convert to ...
322
  	err = xfrm_decode_session_reverse(skb, flowi6_to_flowi(&fl2), AF_INET6);
b42835dbe   David S. Miller   ipv6: Make icmp r...
323
324
325
326
327
328
  	if (err)
  		goto relookup_failed;
  
  	err = ip6_dst_lookup(sk, &dst2, &fl2);
  	if (err)
  		goto relookup_failed;
4c9483b2f   David S. Miller   ipv6: Convert to ...
329
  	dst2 = xfrm_lookup(net, dst2, flowi6_to_flowi(&fl2), sk, XFRM_LOOKUP_ICMP);
452edd598   David S. Miller   xfrm: Return dst ...
330
  	if (!IS_ERR(dst2)) {
b42835dbe   David S. Miller   ipv6: Make icmp r...
331
332
  		dst_release(dst);
  		dst = dst2;
452edd598   David S. Miller   xfrm: Return dst ...
333
334
335
336
337
338
339
  	} else {
  		err = PTR_ERR(dst2);
  		if (err == -EPERM) {
  			dst_release(dst);
  			return dst2;
  		} else
  			goto relookup_failed;
b42835dbe   David S. Miller   ipv6: Make icmp r...
340
341
342
343
344
345
346
  	}
  
  relookup_failed:
  	if (dst)
  		return dst;
  	return ERR_PTR(err);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
  /*
   *	Send an ICMP message in response to a packet in error
   */
3ffe533c8   Alexey Dobriyan   ipv6: drop unused...
350
  void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  {
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
352
  	struct net *net = dev_net(skb->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
  	struct inet6_dev *idev = NULL;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
354
  	struct ipv6hdr *hdr = ipv6_hdr(skb);
84427d533   YOSHIFUJI Hideaki   [IPV6]: Ensure to...
355
356
  	struct sock *sk;
  	struct ipv6_pinfo *np;
b71d1d426   Eric Dumazet   inet: constify ip...
357
  	const struct in6_addr *saddr = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
  	struct dst_entry *dst;
  	struct icmp6hdr tmp_hdr;
4c9483b2f   David S. Miller   ipv6: Convert to ...
360
  	struct flowi6 fl6;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
  	struct icmpv6_msg msg;
  	int iif = 0;
  	int addr_type = 0;
  	int len;
e651f03af   Gerrit Renker   inet6: Conversion...
365
  	int hlimit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  	int err = 0;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
367
368
  	if ((u8 *)hdr < skb->head ||
  	    (skb->network_header + sizeof(*hdr)) > skb->tail)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
  		return;
  
  	/*
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
372
  	 *	Make sure we respect the rules
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
374
375
376
377
  	 *	i.e. RFC 1885 2.4(e)
  	 *	Rule (e.1) is enforced by not using icmpv6_send
  	 *	in any code that processes icmp errors.
  	 */
  	addr_type = ipv6_addr_type(&hdr->daddr);
9a43b709a   Benjamin Thery   [NETNS][IPV6] icm...
378
  	if (ipv6_chk_addr(net, &hdr->daddr, skb->dev, 0))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
381
382
383
384
385
386
  		saddr = &hdr->daddr;
  
  	/*
  	 *	Dest addr check
  	 */
  
  	if ((addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST)) {
  		if (type != ICMPV6_PKT_TOOBIG &&
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
387
388
  		    !(type == ICMPV6_PARAMPROB &&
  		      code == ICMPV6_UNK_OPTION &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
  		      (opt_unrec(skb, info))))
  			return;
  
  		saddr = NULL;
  	}
  
  	addr_type = ipv6_addr_type(&hdr->saddr);
  
  	/*
  	 *	Source addr check
  	 */
  
  	if (addr_type & IPV6_ADDR_LINKLOCAL)
  		iif = skb->dev->ifindex;
  
  	/*
8de3351e6   YOSHIFUJI Hideaki   [IPV6]: Try not t...
405
406
407
408
  	 *	Must not send error if the source does not uniquely
  	 *	identify a single node (RFC2463 Section 2.4).
  	 *	We check unspecified / multicast addresses here,
  	 *	and anycast addresses will be checked later.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
  	 */
  	if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
64ce20730   Patrick McHardy   [NET]: Make NETDE...
411
412
  		LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: addr_any/mcast source
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
  		return;
  	}
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
415
  	/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
418
  	 *	Never answer to a ICMP packet.
  	 */
  	if (is_ineligible(skb)) {
64ce20730   Patrick McHardy   [NET]: Make NETDE...
419
420
  		LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: no reply to icmp error
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
  		return;
  	}
793832361   Masahide NAKAMURA   [IPV6] MIP6: Reve...
423
  	mip6_addr_swap(skb);
4c9483b2f   David S. Miller   ipv6: Convert to ...
424
425
  	memset(&fl6, 0, sizeof(fl6));
  	fl6.flowi6_proto = IPPROTO_ICMPV6;
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
426
  	fl6.daddr = hdr->saddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
  	if (saddr)
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
428
  		fl6.saddr = *saddr;
4c9483b2f   David S. Miller   ipv6: Convert to ...
429
  	fl6.flowi6_oif = iif;
1958b856c   David S. Miller   net: Put fl6_* ma...
430
431
  	fl6.fl6_icmp_type = type;
  	fl6.fl6_icmp_code = code;
4c9483b2f   David S. Miller   ipv6: Convert to ...
432
  	security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433

fdc0bde90   Denis V. Lunev   icmp: icmp_sk() s...
434
435
  	sk = icmpv6_xmit_lock(net);
  	if (sk == NULL)
405666db8   Denis V. Lunev   [ICMP]: Pass prop...
436
  		return;
fdc0bde90   Denis V. Lunev   icmp: icmp_sk() s...
437
  	np = inet6_sk(sk);
405666db8   Denis V. Lunev   [ICMP]: Pass prop...
438

4c9483b2f   David S. Miller   ipv6: Convert to ...
439
  	if (!icmpv6_xrlim_allow(sk, type, &fl6))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
442
443
444
445
  		goto out;
  
  	tmp_hdr.icmp6_type = type;
  	tmp_hdr.icmp6_code = code;
  	tmp_hdr.icmp6_cksum = 0;
  	tmp_hdr.icmp6_pointer = htonl(info);
4c9483b2f   David S. Miller   ipv6: Convert to ...
446
447
  	if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
  		fl6.flowi6_oif = np->mcast_oif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448

4c9483b2f   David S. Miller   ipv6: Convert to ...
449
  	dst = icmpv6_route_lookup(net, skb, sk, &fl6);
b42835dbe   David S. Miller   ipv6: Make icmp r...
450
  	if (IS_ERR(dst))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
  		goto out;
8de3351e6   YOSHIFUJI Hideaki   [IPV6]: Try not t...
452

4c9483b2f   David S. Miller   ipv6: Convert to ...
453
  	if (ipv6_addr_is_multicast(&fl6.daddr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
  		hlimit = np->mcast_hops;
  	else
  		hlimit = np->hop_limit;
  	if (hlimit < 0)
6b75d0908   YOSHIFUJI Hideaki   [IPV6]: Optimize ...
458
  		hlimit = ip6_dst_hoplimit(dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
  
  	msg.skb = skb;
bbe735e42   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
461
  	msg.offset = skb_network_offset(skb);
763ecff18   Yasuyuki Kozakai   [NETFILTER]: nf_c...
462
  	msg.type = type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
466
  
  	len = skb->len - msg.offset;
  	len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) -sizeof(struct icmp6hdr));
  	if (len < 0) {
64ce20730   Patrick McHardy   [NET]: Make NETDE...
467
468
  		LIMIT_NETDEBUG(KERN_DEBUG "icmp: len problem
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
470
  		goto out_dst_release;
  	}
cfdf76474   Eric Dumazet   ipv6: some RCU co...
471
472
  	rcu_read_lock();
  	idev = __in6_dev_get(skb->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
  
  	err = ip6_append_data(sk, icmpv6_getfrag, &msg,
  			      len + sizeof(struct icmp6hdr),
e651f03af   Gerrit Renker   inet6: Conversion...
476
  			      sizeof(struct icmp6hdr), hlimit,
4c9483b2f   David S. Miller   ipv6: Convert to ...
477
  			      np->tclass, NULL, &fl6, (struct rt6_info*)dst,
13b52cd44   Brian Haley   IPv6: Add dontfra...
478
  			      MSG_DONTWAIT, np->dontfrag);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
  	if (err) {
00d9d6a18   Eric Dumazet   ipv6: fix ICMP6_M...
480
  		ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
  		ip6_flush_pending_frames(sk);
cfdf76474   Eric Dumazet   ipv6: some RCU co...
482
483
484
  	} else {
  		err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
  						 len + sizeof(struct icmp6hdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
  	}
cfdf76474   Eric Dumazet   ipv6: some RCU co...
486
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
488
489
  out_dst_release:
  	dst_release(dst);
  out:
405666db8   Denis V. Lunev   [ICMP]: Pass prop...
490
  	icmpv6_xmit_unlock(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
  }
7159039a1   YOSHIFUJI Hideaki   [IPV6]: Decentral...
492
  EXPORT_SYMBOL(icmpv6_send);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
494
  static void icmpv6_echo_reply(struct sk_buff *skb)
  {
c346dca10   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
495
  	struct net *net = dev_net(skb->dev);
84427d533   YOSHIFUJI Hideaki   [IPV6]: Ensure to...
496
  	struct sock *sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
  	struct inet6_dev *idev;
84427d533   YOSHIFUJI Hideaki   [IPV6]: Ensure to...
498
  	struct ipv6_pinfo *np;
b71d1d426   Eric Dumazet   inet: constify ip...
499
  	const struct in6_addr *saddr = NULL;
cc70ab261   Arnaldo Carvalho de Melo   [ICMP6]: Introduc...
500
  	struct icmp6hdr *icmph = icmp6_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
  	struct icmp6hdr tmp_hdr;
4c9483b2f   David S. Miller   ipv6: Convert to ...
502
  	struct flowi6 fl6;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
504
505
506
  	struct icmpv6_msg msg;
  	struct dst_entry *dst;
  	int err = 0;
  	int hlimit;
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
507
  	saddr = &ipv6_hdr(skb)->daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
510
511
512
513
  
  	if (!ipv6_unicast_destination(skb))
  		saddr = NULL;
  
  	memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr));
  	tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY;
4c9483b2f   David S. Miller   ipv6: Convert to ...
514
515
  	memset(&fl6, 0, sizeof(fl6));
  	fl6.flowi6_proto = IPPROTO_ICMPV6;
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
516
  	fl6.daddr = ipv6_hdr(skb)->saddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
  	if (saddr)
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
518
  		fl6.saddr = *saddr;
4c9483b2f   David S. Miller   ipv6: Convert to ...
519
  	fl6.flowi6_oif = skb->dev->ifindex;
1958b856c   David S. Miller   net: Put fl6_* ma...
520
  	fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
4c9483b2f   David S. Miller   ipv6: Convert to ...
521
  	security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522

fdc0bde90   Denis V. Lunev   icmp: icmp_sk() s...
523
524
  	sk = icmpv6_xmit_lock(net);
  	if (sk == NULL)
405666db8   Denis V. Lunev   [ICMP]: Pass prop...
525
  		return;
fdc0bde90   Denis V. Lunev   icmp: icmp_sk() s...
526
  	np = inet6_sk(sk);
405666db8   Denis V. Lunev   [ICMP]: Pass prop...
527

4c9483b2f   David S. Miller   ipv6: Convert to ...
528
529
  	if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
  		fl6.flowi6_oif = np->mcast_oif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530

4c9483b2f   David S. Miller   ipv6: Convert to ...
531
  	err = ip6_dst_lookup(sk, &dst, &fl6);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
533
  	if (err)
  		goto out;
4c9483b2f   David S. Miller   ipv6: Convert to ...
534
  	dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), sk, 0);
452edd598   David S. Miller   xfrm: Return dst ...
535
  	if (IS_ERR(dst))
e104411b8   Patrick McHardy   [XFRM]: Always re...
536
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537

4c9483b2f   David S. Miller   ipv6: Convert to ...
538
  	if (ipv6_addr_is_multicast(&fl6.daddr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
539
540
541
542
  		hlimit = np->mcast_hops;
  	else
  		hlimit = np->hop_limit;
  	if (hlimit < 0)
6b75d0908   YOSHIFUJI Hideaki   [IPV6]: Optimize ...
543
  		hlimit = ip6_dst_hoplimit(dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544

cfdf76474   Eric Dumazet   ipv6: some RCU co...
545
  	idev = __in6_dev_get(skb->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
548
  
  	msg.skb = skb;
  	msg.offset = 0;
763ecff18   Yasuyuki Kozakai   [NETFILTER]: nf_c...
549
  	msg.type = ICMPV6_ECHO_REPLY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
  
  	err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr),
4c9483b2f   David S. Miller   ipv6: Convert to ...
552
  				sizeof(struct icmp6hdr), hlimit, np->tclass, NULL, &fl6,
13b52cd44   Brian Haley   IPv6: Add dontfra...
553
554
  				(struct rt6_info*)dst, MSG_DONTWAIT,
  				np->dontfrag);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
556
  
  	if (err) {
00d9d6a18   Eric Dumazet   ipv6: fix ICMP6_M...
557
  		ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
  		ip6_flush_pending_frames(sk);
cfdf76474   Eric Dumazet   ipv6: some RCU co...
559
560
561
  	} else {
  		err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
  						 skb->len + sizeof(struct icmp6hdr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
  	dst_release(dst);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
564
  out:
405666db8   Denis V. Lunev   [ICMP]: Pass prop...
565
  	icmpv6_xmit_unlock(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
  }
d5fdd6bab   Brian Haley   ipv6: Use correct...
567
  static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
  {
41135cc83   Alexey Dobriyan   net: constify str...
569
  	const struct inet6_protocol *ipprot;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
572
  	int inner_offset;
  	int hash;
  	u8 nexthdr;
75f2811c6   Jesse Gross   ipv6: Add fragmen...
573
  	__be16 frag_off;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
578
579
580
  
  	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
  		return;
  
  	nexthdr = ((struct ipv6hdr *)skb->data)->nexthdr;
  	if (ipv6_ext_hdr(nexthdr)) {
  		/* now skip over extension headers */
75f2811c6   Jesse Gross   ipv6: Add fragmen...
581
582
  		inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
  						&nexthdr, &frag_off);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
584
585
586
587
588
589
590
591
  		if (inner_offset<0)
  			return;
  	} else {
  		inner_offset = sizeof(struct ipv6hdr);
  	}
  
  	/* Checkin header including 8 bytes of inner protocol header. */
  	if (!pskb_may_pull(skb, inner_offset+8))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
593
594
595
596
597
598
599
600
601
602
603
604
605
  	/* BUGGG_FUTURE: we should try to parse exthdrs in this packet.
  	   Without this we will not able f.e. to make source routed
  	   pmtu discovery.
  	   Corresponding argument (opt) to notifiers is already added.
  	   --ANK (980726)
  	 */
  
  	hash = nexthdr & (MAX_INET_PROTOS - 1);
  
  	rcu_read_lock();
  	ipprot = rcu_dereference(inet6_protos[hash]);
  	if (ipprot && ipprot->err_handler)
  		ipprot->err_handler(skb, NULL, type, code, inner_offset, info);
  	rcu_read_unlock();
69d6da0b0   Pavel Emelyanov   [IPv6] RAW: Compa...
606
  	raw6_icmp_error(skb, nexthdr, type, code, inner_offset, info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
  }
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
608

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
610
611
  /*
   *	Handle icmp messages
   */
e5bbef20e   Herbert Xu   [IPV6]: Replace s...
612
  static int icmpv6_rcv(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614
615
  	struct net_device *dev = skb->dev;
  	struct inet6_dev *idev = __in6_dev_get(dev);
b71d1d426   Eric Dumazet   inet: constify ip...
616
617
  	const struct in6_addr *saddr, *daddr;
  	const struct ipv6hdr *orig_hdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
  	struct icmp6hdr *hdr;
d5fdd6bab   Brian Haley   ipv6: Use correct...
619
  	u8 type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620

aebcf82c1   Herbert Xu   [IPSEC]: Do not l...
621
  	if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
def8b4faf   Alexey Dobriyan   net: reduce struc...
622
  		struct sec_path *sp = skb_sec_path(skb);
8b7817f3a   Herbert Xu   [IPSEC]: Add ICMP...
623
  		int nh;
def8b4faf   Alexey Dobriyan   net: reduce struc...
624
  		if (!(sp && sp->xvec[sp->len - 1]->props.flags &
aebcf82c1   Herbert Xu   [IPSEC]: Do not l...
625
626
  				 XFRM_STATE_ICMP))
  			goto drop_no_count;
8b7817f3a   Herbert Xu   [IPSEC]: Add ICMP...
627
628
629
630
631
632
633
634
635
636
637
  		if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(*orig_hdr)))
  			goto drop_no_count;
  
  		nh = skb_network_offset(skb);
  		skb_set_network_header(skb, sizeof(*hdr));
  
  		if (!xfrm6_policy_check_reverse(NULL, XFRM_POLICY_IN, skb))
  			goto drop_no_count;
  
  		skb_set_network_header(skb, nh);
  	}
e41b5368e   Denis V. Lunev   ipv6: added net a...
638
  	ICMP6_INC_STATS_BH(dev_net(dev), idev, ICMP6_MIB_INMSGS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639

0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
640
641
  	saddr = &ipv6_hdr(skb)->saddr;
  	daddr = &ipv6_hdr(skb)->daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
  
  	/* Perform checksum. */
fb286bb29   Herbert Xu   [NET]: Detect har...
644
  	switch (skb->ip_summed) {
84fa7933a   Patrick McHardy   [NET]: Replace CH...
645
  	case CHECKSUM_COMPLETE:
fb286bb29   Herbert Xu   [NET]: Detect har...
646
647
648
649
650
  		if (!csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,
  				     skb->csum))
  			break;
  		/* fall through */
  	case CHECKSUM_NONE:
868c86bcb   Al Viro   [NET]: annotate c...
651
652
  		skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len,
  					     IPPROTO_ICMPV6, 0));
fb286bb29   Herbert Xu   [NET]: Detect har...
653
  		if (__skb_checksum_complete(skb)) {
5b095d989   Harvey Harrison   net: replace %p6 ...
654
655
  			LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [%pI6 > %pI6]
  ",
0c6ce78ab   Harvey Harrison   net: replace uses...
656
  				       saddr, daddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
658
659
  			goto discard_it;
  		}
  	}
8cf229437   Herbert Xu   [ICMP]: Restore p...
660
661
  	if (!pskb_pull(skb, sizeof(*hdr)))
  		goto discard_it;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
662

cc70ab261   Arnaldo Carvalho de Melo   [ICMP6]: Introduc...
663
  	hdr = icmp6_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
665
  
  	type = hdr->icmp6_type;
55d43808e   Denis V. Lunev   ipv6: added net a...
666
  	ICMP6MSGIN_INC_STATS_BH(dev_net(dev), idev, type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
  
  	switch (type) {
  	case ICMPV6_ECHO_REQUEST:
  		icmpv6_echo_reply(skb);
  		break;
  
  	case ICMPV6_ECHO_REPLY:
  		/* we couldn't care less */
  		break;
  
  	case ICMPV6_PKT_TOOBIG:
  		/* BUGGG_FUTURE: if packet contains rthdr, we cannot update
  		   standard destination cache. Seems, only "advanced"
  		   destination cache will allow to solve this problem
  		   --ANK (980726)
  		 */
  		if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
  			goto discard_it;
cc70ab261   Arnaldo Carvalho de Melo   [ICMP6]: Introduc...
685
  		hdr = icmp6_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
  		orig_hdr = (struct ipv6hdr *) (hdr + 1);
  		rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,
  				   ntohl(hdr->icmp6_mtu));
  
  		/*
  		 *	Drop through to notify
  		 */
  
  	case ICMPV6_DEST_UNREACH:
  	case ICMPV6_TIME_EXCEED:
  	case ICMPV6_PARAMPROB:
  		icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);
  		break;
  
  	case NDISC_ROUTER_SOLICITATION:
  	case NDISC_ROUTER_ADVERTISEMENT:
  	case NDISC_NEIGHBOUR_SOLICITATION:
  	case NDISC_NEIGHBOUR_ADVERTISEMENT:
  	case NDISC_REDIRECT:
  		ndisc_rcv(skb);
  		break;
  
  	case ICMPV6_MGM_QUERY:
  		igmp6_event_query(skb);
  		break;
  
  	case ICMPV6_MGM_REPORT:
  		igmp6_event_report(skb);
  		break;
  
  	case ICMPV6_MGM_REDUCTION:
  	case ICMPV6_NI_QUERY:
  	case ICMPV6_NI_REPLY:
  	case ICMPV6_MLD2_REPORT:
  	case ICMPV6_DHAAD_REQUEST:
  	case ICMPV6_DHAAD_REPLY:
  	case ICMPV6_MOBILE_PREFIX_SOL:
  	case ICMPV6_MOBILE_PREFIX_ADV:
  		break;
  
  	default:
64ce20730   Patrick McHardy   [NET]: Make NETDE...
727
728
  		LIMIT_NETDEBUG(KERN_DEBUG "icmpv6: msg of unknown type
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
729
730
731
732
  
  		/* informational */
  		if (type & ICMPV6_INFOMSG_MASK)
  			break;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
733
734
735
  		/*
  		 * error of unknown type.
  		 * must pass to upper level
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
737
738
  		 */
  
  		icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
739
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
741
742
743
  	kfree_skb(skb);
  	return 0;
  
  discard_it:
e41b5368e   Denis V. Lunev   ipv6: added net a...
744
  	ICMP6_INC_STATS_BH(dev_net(dev), idev, ICMP6_MIB_INERRORS);
8b7817f3a   Herbert Xu   [IPSEC]: Add ICMP...
745
  drop_no_count:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
746
747
748
  	kfree_skb(skb);
  	return 0;
  }
4c9483b2f   David S. Miller   ipv6: Convert to ...
749
  void icmpv6_flow_init(struct sock *sk, struct flowi6 *fl6,
95e41e93e   YOSHIFUJI Hideaki   [IPV6]: Make ndis...
750
751
752
753
754
  		      u8 type,
  		      const struct in6_addr *saddr,
  		      const struct in6_addr *daddr,
  		      int oif)
  {
4c9483b2f   David S. Miller   ipv6: Convert to ...
755
  	memset(fl6, 0, sizeof(*fl6));
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
756
757
  	fl6->saddr = *saddr;
  	fl6->daddr = *daddr;
4c9483b2f   David S. Miller   ipv6: Convert to ...
758
  	fl6->flowi6_proto 	= IPPROTO_ICMPV6;
1958b856c   David S. Miller   net: Put fl6_* ma...
759
760
  	fl6->fl6_icmp_type	= type;
  	fl6->fl6_icmp_code	= 0;
4c9483b2f   David S. Miller   ipv6: Convert to ...
761
762
  	fl6->flowi6_oif		= oif;
  	security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
95e41e93e   YOSHIFUJI Hideaki   [IPV6]: Make ndis...
763
  }
640c41c77   Ingo Molnar   [IPV6] lockdep: a...
764
  /*
b7e729c4b   Denis V. Lunev   [ICMP]: Store soc...
765
   * Special lock-class for __icmpv6_sk:
640c41c77   Ingo Molnar   [IPV6] lockdep: a...
766
767
   */
  static struct lock_class_key icmpv6_socket_sk_dst_lock_key;
98c6d1b26   Denis V. Lunev   [NETNS]: Make icm...
768
  static int __net_init icmpv6_sk_init(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
771
  {
  	struct sock *sk;
  	int err, i, j;
98c6d1b26   Denis V. Lunev   [NETNS]: Make icm...
772
773
774
  	net->ipv6.icmp_sk =
  		kzalloc(nr_cpu_ids * sizeof(struct sock *), GFP_KERNEL);
  	if (net->ipv6.icmp_sk == NULL)
79c911595   Denis V. Lunev   [ICMP]: Allocate ...
775
  		return -ENOMEM;
6f9120422   KAMEZAWA Hiroyuki   [PATCH] for_each_...
776
  	for_each_possible_cpu(i) {
1ed8516f0   Denis V. Lunev   [IPV6]: Simplify ...
777
778
  		err = inet_ctl_sock_create(&sk, PF_INET6,
  					   SOCK_RAW, IPPROTO_ICMPV6, net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
781
782
783
784
785
786
  		if (err < 0) {
  			printk(KERN_ERR
  			       "Failed to initialize the ICMP6 control socket "
  			       "(err %d).
  ",
  			       err);
  			goto fail;
  		}
1ed8516f0   Denis V. Lunev   [IPV6]: Simplify ...
787
  		net->ipv6.icmp_sk[i] = sk;
5c8cafd65   Denis V. Lunev   [NETNS]: icmp(v6)...
788

640c41c77   Ingo Molnar   [IPV6] lockdep: a...
789
790
791
  		/*
  		 * Split off their lock-class, because sk->sk_dst_lock
  		 * gets used from softirqs, which is safe for
b7e729c4b   Denis V. Lunev   [ICMP]: Store soc...
792
  		 * __icmpv6_sk (because those never get directly used
640c41c77   Ingo Molnar   [IPV6] lockdep: a...
793
794
795
796
  		 * via userspace syscalls), but unsafe for normal sockets.
  		 */
  		lockdep_set_class(&sk->sk_dst_lock,
  				  &icmpv6_socket_sk_dst_lock_key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
797
798
799
800
  
  		/* Enough space for 2 64K ICMP packets, including
  		 * sk_buff struct overhead.
  		 */
87fb4b7b5   Eric Dumazet   net: more accurat...
801
  		sk->sk_sndbuf = 2 * SKB_TRUESIZE(64 * 1024);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
803
804
805
  	return 0;
  
   fail:
5c8cafd65   Denis V. Lunev   [NETNS]: icmp(v6)...
806
  	for (j = 0; j < i; j++)
1ed8516f0   Denis V. Lunev   [IPV6]: Simplify ...
807
  		inet_ctl_sock_destroy(net->ipv6.icmp_sk[j]);
98c6d1b26   Denis V. Lunev   [NETNS]: Make icm...
808
  	kfree(net->ipv6.icmp_sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
809
810
  	return err;
  }
98c6d1b26   Denis V. Lunev   [NETNS]: Make icm...
811
  static void __net_exit icmpv6_sk_exit(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
  {
  	int i;
6f9120422   KAMEZAWA Hiroyuki   [PATCH] for_each_...
814
  	for_each_possible_cpu(i) {
1ed8516f0   Denis V. Lunev   [IPV6]: Simplify ...
815
  		inet_ctl_sock_destroy(net->ipv6.icmp_sk[i]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
  	}
98c6d1b26   Denis V. Lunev   [NETNS]: Make icm...
817
818
  	kfree(net->ipv6.icmp_sk);
  }
8ed7edce8   Alexey Dobriyan   ipv6: fix inet6_i...
819
  static struct pernet_operations icmpv6_sk_ops = {
98c6d1b26   Denis V. Lunev   [NETNS]: Make icm...
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
         .init = icmpv6_sk_init,
         .exit = icmpv6_sk_exit,
  };
  
  int __init icmpv6_init(void)
  {
  	int err;
  
  	err = register_pernet_subsys(&icmpv6_sk_ops);
  	if (err < 0)
  		return err;
  
  	err = -EAGAIN;
  	if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0)
  		goto fail;
  	return 0;
  
  fail:
  	printk(KERN_ERR "Failed to register ICMP6 protocol
  ");
  	unregister_pernet_subsys(&icmpv6_sk_ops);
  	return err;
  }
8ed7edce8   Alexey Dobriyan   ipv6: fix inet6_i...
843
  void icmpv6_cleanup(void)
98c6d1b26   Denis V. Lunev   [NETNS]: Make icm...
844
845
  {
  	unregister_pernet_subsys(&icmpv6_sk_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
846
847
  	inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6);
  }
98c6d1b26   Denis V. Lunev   [NETNS]: Make icm...
848

9b5b5cff9   Arjan van de Ven   [NET]: Add const ...
849
  static const struct icmp6_err {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
  	int err;
  	int fatal;
  } tab_unreach[] = {
  	{	/* NOROUTE */
  		.err	= ENETUNREACH,
  		.fatal	= 0,
  	},
  	{	/* ADM_PROHIBITED */
  		.err	= EACCES,
  		.fatal	= 1,
  	},
  	{	/* Was NOT_NEIGHBOUR, now reserved */
  		.err	= EHOSTUNREACH,
  		.fatal	= 0,
  	},
  	{	/* ADDR_UNREACH	*/
  		.err	= EHOSTUNREACH,
  		.fatal	= 0,
  	},
  	{	/* PORT_UNREACH	*/
  		.err	= ECONNREFUSED,
  		.fatal	= 1,
  	},
  };
d5fdd6bab   Brian Haley   ipv6: Use correct...
874
  int icmpv6_err_convert(u8 type, u8 code, int *err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
  {
  	int fatal = 0;
  
  	*err = EPROTO;
  
  	switch (type) {
  	case ICMPV6_DEST_UNREACH:
  		fatal = 1;
  		if (code <= ICMPV6_PORT_UNREACH) {
  			*err  = tab_unreach[code].err;
  			fatal = tab_unreach[code].fatal;
  		}
  		break;
  
  	case ICMPV6_PKT_TOOBIG:
  		*err = EMSGSIZE;
  		break;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
892

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
896
897
898
899
900
  	case ICMPV6_PARAMPROB:
  		*err = EPROTO;
  		fatal = 1;
  		break;
  
  	case ICMPV6_TIME_EXCEED:
  		*err = EHOSTUNREACH;
  		break;
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
901
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
903
904
  
  	return fatal;
  }
7159039a1   YOSHIFUJI Hideaki   [IPV6]: Decentral...
905
  EXPORT_SYMBOL(icmpv6_err_convert);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906
  #ifdef CONFIG_SYSCTL
760f2d018   Daniel Lezcano   [NETNS][IPV6]: Ma...
907
  ctl_table ipv6_icmp_table_template[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
909
  		.procname	= "ratelimit",
41a76906b   Daniel Lezcano   [NETNS][IPV6]: Ma...
910
  		.data		= &init_net.ipv6.sysctl.icmpv6_time,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
912
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
6d9f239a1   Alexey Dobriyan   net: '&' redux
913
  		.proc_handler	= proc_dointvec_ms_jiffies,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
914
  	},
f8572d8f2   Eric W. Biederman   sysctl net: Remov...
915
  	{ },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
916
  };
760f2d018   Daniel Lezcano   [NETNS][IPV6]: Ma...
917

2c8c1e729   Alexey Dobriyan   net: spread __net...
918
  struct ctl_table * __net_init ipv6_icmp_sysctl_init(struct net *net)
760f2d018   Daniel Lezcano   [NETNS][IPV6]: Ma...
919
920
921
922
923
924
  {
  	struct ctl_table *table;
  
  	table = kmemdup(ipv6_icmp_table_template,
  			sizeof(ipv6_icmp_table_template),
  			GFP_KERNEL);
5ee091050   YOSHIFUJI Hideaki   [IPV6] SYSCTL: co...
925
926
927
  
  	if (table)
  		table[0].data = &net->ipv6.sysctl.icmpv6_time;
760f2d018   Daniel Lezcano   [NETNS][IPV6]: Ma...
928
929
  	return table;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
930
  #endif