Blame view

net/ipv6/ipv6_sockglue.c 31.9 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
  /*
   *	IPv6 BSD socket options interface
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
4
   *	Linux INET6 implementation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
   *
   *	Authors:
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
7
   *	Pedro Roque		<roque@di.fc.ul.pt>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
   *
   *	Based on linux/net/ipv4/ip_sockglue.c
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
   *	FIXME: Make the setsockopt code POSIX compliant: That is
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
17
18
19
20
21
   *	o	Truncate getsockopt returns
   *	o	Return an optlen of the truncated length if need be
   *
   *	Changes:
   *	David L Stevens <dlstevens@us.ibm.com>:
   *		- added multicast source filtering API for MLDv2
   */
  
  #include <linux/module.h>
4fc268d24   Randy Dunlap   [PATCH] capable/c...
22
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/socket.h>
  #include <linux/sockios.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
  #include <linux/net.h>
  #include <linux/in6.h>
7bc570c8b   YOSHIFUJI Hideaki   [IPV6] MROUTE: Su...
29
  #include <linux/mroute6.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
  #include <linux/netdevice.h>
  #include <linux/if_arp.h>
  #include <linux/init.h>
  #include <linux/sysctl.h>
  #include <linux/netfilter.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
35
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
41
42
43
44
45
46
47
  
  #include <net/sock.h>
  #include <net/snmp.h>
  #include <net/ipv6.h>
  #include <net/ndisc.h>
  #include <net/protocol.h>
  #include <net/transp_v6.h>
  #include <net/ip6_route.h>
  #include <net/addrconf.h>
  #include <net/inet_common.h>
  #include <net/tcp.h>
  #include <net/udp.h>
ba4e58eca   Gerrit Renker   [NET]: Supporting...
48
  #include <net/udplite.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
  #include <net/xfrm.h>
dae502954   David L Stevens   ipv4/ipv6 compat:...
50
  #include <net/compat.h>
a149e7c7c   David Lebrun   ipv6: sr: add sup...
51
  #include <net/seg6.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52

7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
53
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
  struct ip6_ra_chain *ip6_ra_chain;
  DEFINE_RWLOCK(ip6_ra_lock);
725a8ff04   Denis V. Lunev   ipv6: remove unus...
57
  int ip6_ra_control(struct sock *sk, int sel)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
61
  {
  	struct ip6_ra_chain *ra, *new_ra, **rap;
  
  	/* RA packet may be delivered ONLY to IPPROTO_RAW socket */
c720c7e83   Eric Dumazet   inet: rename some...
62
  	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->inet_num != IPPROTO_RAW)
1717699cd   YOSHIFUJI Hideaki   ipv6: Fail with a...
63
  		return -ENOPROTOOPT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64

67ba4152e   Ian Morris   ipv6: White-space...
65
  	new_ra = (sel >= 0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
95baa60a0   Gen Zhang   ipv6_sockglue: Fi...
66
67
  	if (sel >= 0 && !new_ra)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
  
  	write_lock_bh(&ip6_ra_lock);
67ba4152e   Ian Morris   ipv6: White-space...
70
  	for (rap = &ip6_ra_chain; (ra = *rap) != NULL; rap = &ra->next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
  		if (ra->sk == sk) {
67ba4152e   Ian Morris   ipv6: White-space...
72
  			if (sel >= 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  				write_unlock_bh(&ip6_ra_lock);
a51482bde   Jesper Juhl   [NET]: kfree cleanup
74
  				kfree(new_ra);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
79
  				return -EADDRINUSE;
  			}
  
  			*rap = ra->next;
  			write_unlock_bh(&ip6_ra_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
84
  			sock_put(sk);
  			kfree(ra);
  			return 0;
  		}
  	}
63159f29b   Ian Morris   ipv6: coding styl...
85
  	if (!new_ra) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
90
  		write_unlock_bh(&ip6_ra_lock);
  		return -ENOBUFS;
  	}
  	new_ra->sk = sk;
  	new_ra->sel = sel;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
95
96
  	new_ra->next = ra;
  	*rap = new_ra;
  	sock_hold(sk);
  	write_unlock_bh(&ip6_ra_lock);
  	return 0;
  }
e7712f1a7   YOSHIFUJI Hideaki   [IPV6]: Share com...
97
98
99
100
101
102
  struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
  					   struct ipv6_txoptions *opt)
  {
  	if (inet_sk(sk)->is_icsk) {
  		if (opt &&
  		    !((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) &&
c720c7e83   Eric Dumazet   inet: rename some...
103
  		    inet_sk(sk)->inet_daddr != LOOPBACK4_IPV6) {
e7712f1a7   YOSHIFUJI Hideaki   [IPV6]: Share com...
104
105
106
107
  			struct inet_connection_sock *icsk = inet_csk(sk);
  			icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen;
  			icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
  		}
e7712f1a7   YOSHIFUJI Hideaki   [IPV6]: Share com...
108
  	}
45f6fad84   Eric Dumazet   ipv6: add complet...
109
110
  	opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt,
  		   opt);
e7712f1a7   YOSHIFUJI Hideaki   [IPV6]: Share com...
111
112
113
114
  	sk_dst_reset(sk);
  
  	return opt;
  }
baf606d9c   Marcelo Ricardo Leitner   ipv4,ipv6: grab r...
115
116
117
  static bool setsockopt_needs_rtnl(int optname)
  {
  	switch (optname) {
8651be8f1   WANG Cong   ipv6: fix a poten...
118
  	case IPV6_ADDRFORM:
baf606d9c   Marcelo Ricardo Leitner   ipv4,ipv6: grab r...
119
120
  	case IPV6_ADD_MEMBERSHIP:
  	case IPV6_DROP_MEMBERSHIP:
c4a6853d8   Marcelo Ricardo Leitner   ipv6: invert join...
121
122
  	case IPV6_JOIN_ANYCAST:
  	case IPV6_LEAVE_ANYCAST:
baf606d9c   Marcelo Ricardo Leitner   ipv4,ipv6: grab r...
123
124
  	case MCAST_JOIN_GROUP:
  	case MCAST_LEAVE_GROUP:
54ff9ef36   Marcelo Ricardo Leitner   ipv4, ipv6: kill ...
125
126
127
128
129
  	case MCAST_JOIN_SOURCE_GROUP:
  	case MCAST_LEAVE_SOURCE_GROUP:
  	case MCAST_BLOCK_SOURCE:
  	case MCAST_UNBLOCK_SOURCE:
  	case MCAST_MSFILTER:
baf606d9c   Marcelo Ricardo Leitner   ipv4,ipv6: grab r...
130
131
132
133
  		return true;
  	}
  	return false;
  }
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
134
135
  static int copy_group_source_from_sockptr(struct group_source_req *greqs,
  		sockptr_t optval, int optlen)
3021ad529   Christoph Hellwig   net/ipv6: remove ...
136
137
138
139
140
141
  {
  	if (in_compat_syscall()) {
  		struct compat_group_source_req gr32;
  
  		if (optlen < sizeof(gr32))
  			return -EINVAL;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
142
  		if (copy_from_sockptr(&gr32, optval, sizeof(gr32)))
3021ad529   Christoph Hellwig   net/ipv6: remove ...
143
144
145
146
147
148
149
  			return -EFAULT;
  		greqs->gsr_interface = gr32.gsr_interface;
  		greqs->gsr_group = gr32.gsr_group;
  		greqs->gsr_source = gr32.gsr_source;
  	} else {
  		if (optlen < sizeof(*greqs))
  			return -EINVAL;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
150
  		if (copy_from_sockptr(greqs, optval, sizeof(*greqs)))
3021ad529   Christoph Hellwig   net/ipv6: remove ...
151
152
153
154
155
  			return -EFAULT;
  	}
  
  	return 0;
  }
fcfa0b09d   Al Viro   ipv6: take handli...
156
  static int do_ipv6_mcast_group_source(struct sock *sk, int optname,
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
157
  		sockptr_t optval, int optlen)
fcfa0b09d   Al Viro   ipv6: take handli...
158
  {
3021ad529   Christoph Hellwig   net/ipv6: remove ...
159
  	struct group_source_req greqs;
fcfa0b09d   Al Viro   ipv6: take handli...
160
  	int omode, add;
3021ad529   Christoph Hellwig   net/ipv6: remove ...
161
  	int ret;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
162
  	ret = copy_group_source_from_sockptr(&greqs, optval, optlen);
3021ad529   Christoph Hellwig   net/ipv6: remove ...
163
164
  	if (ret)
  		return ret;
fcfa0b09d   Al Viro   ipv6: take handli...
165

3021ad529   Christoph Hellwig   net/ipv6: remove ...
166
167
  	if (greqs.gsr_group.ss_family != AF_INET6 ||
  	    greqs.gsr_source.ss_family != AF_INET6)
fcfa0b09d   Al Viro   ipv6: take handli...
168
169
170
171
172
173
174
175
176
177
178
  		return -EADDRNOTAVAIL;
  
  	if (optname == MCAST_BLOCK_SOURCE) {
  		omode = MCAST_EXCLUDE;
  		add = 1;
  	} else if (optname == MCAST_UNBLOCK_SOURCE) {
  		omode = MCAST_EXCLUDE;
  		add = 0;
  	} else if (optname == MCAST_JOIN_SOURCE_GROUP) {
  		struct sockaddr_in6 *psin6;
  		int retv;
3021ad529   Christoph Hellwig   net/ipv6: remove ...
179
180
  		psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
  		retv = ipv6_sock_mc_join_ssm(sk, greqs.gsr_interface,
fcfa0b09d   Al Viro   ipv6: take handli...
181
182
183
184
185
186
187
188
189
190
191
  					     &psin6->sin6_addr,
  					     MCAST_INCLUDE);
  		/* prior join w/ different source is ok */
  		if (retv && retv != -EADDRINUSE)
  			return retv;
  		omode = MCAST_INCLUDE;
  		add = 1;
  	} else /* MCAST_LEAVE_SOURCE_GROUP */ {
  		omode = MCAST_INCLUDE;
  		add = 0;
  	}
3021ad529   Christoph Hellwig   net/ipv6: remove ...
192
  	return ip6_mc_source(add, omode, sk, &greqs);
fcfa0b09d   Al Viro   ipv6: take handli...
193
  }
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
194
  static int ipv6_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
ca0e65eb2   Christoph Hellwig   net/ipv6: factor ...
195
196
197
198
199
200
201
202
203
  		int optlen)
  {
  	struct group_filter *gsf;
  	int ret;
  
  	if (optlen < GROUP_FILTER_SIZE(0))
  		return -EINVAL;
  	if (optlen > sysctl_optmem_max)
  		return -ENOBUFS;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
204
  	gsf = memdup_sockptr(optval, optlen);
ca0e65eb2   Christoph Hellwig   net/ipv6: factor ...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  	if (IS_ERR(gsf))
  		return PTR_ERR(gsf);
  
  	/* numsrc >= (4G-140)/128 overflow in 32 bits */
  	ret = -ENOBUFS;
  	if (gsf->gf_numsrc >= 0x1ffffffU ||
  	    gsf->gf_numsrc > sysctl_mld_max_msf)
  		goto out_free_gsf;
  
  	ret = -EINVAL;
  	if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen)
  		goto out_free_gsf;
  
  	ret = ip6_mc_msfilter(sk, gsf, gsf->gf_slist);
  out_free_gsf:
  	kfree(gsf);
  	return ret;
  }
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
223
  static int compat_ipv6_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
ca0e65eb2   Christoph Hellwig   net/ipv6: factor ...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
  		int optlen)
  {
  	const int size0 = offsetof(struct compat_group_filter, gf_slist);
  	struct compat_group_filter *gf32;
  	void *p;
  	int ret;
  	int n;
  
  	if (optlen < size0)
  		return -EINVAL;
  	if (optlen > sysctl_optmem_max - 4)
  		return -ENOBUFS;
  
  	p = kmalloc(optlen + 4, GFP_KERNEL);
  	if (!p)
  		return -ENOMEM;
  
  	gf32 = p + 4; /* we want ->gf_group and ->gf_slist aligned */
  	ret = -EFAULT;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
243
  	if (copy_from_sockptr(gf32, optval, optlen))
ca0e65eb2   Christoph Hellwig   net/ipv6: factor ...
244
245
246
247
248
249
250
251
252
253
254
  		goto out_free_p;
  
  	/* numsrc >= (4G-140)/128 overflow in 32 bits */
  	ret = -ENOBUFS;
  	n = gf32->gf_numsrc;
  	if (n >= 0x1ffffffU || n > sysctl_mld_max_msf)
  		goto out_free_p;
  
  	ret = -EINVAL;
  	if (offsetof(struct compat_group_filter, gf_slist[n]) > optlen)
  		goto out_free_p;
ca0e65eb2   Christoph Hellwig   net/ipv6: factor ...
255
256
257
258
259
  	ret = ip6_mc_msfilter(sk, &(struct group_filter){
  			.gf_interface = gf32->gf_interface,
  			.gf_group = gf32->gf_group,
  			.gf_fmode = gf32->gf_fmode,
  			.gf_numsrc = gf32->gf_numsrc}, gf32->gf_slist);
ca0e65eb2   Christoph Hellwig   net/ipv6: factor ...
260
261
262
263
264
  
  out_free_p:
  	kfree(p);
  	return ret;
  }
ca0e65eb2   Christoph Hellwig   net/ipv6: factor ...
265

fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
266
  static int ipv6_mcast_join_leave(struct sock *sk, int optname,
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
267
  		sockptr_t optval, int optlen)
fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
268
269
270
271
272
273
  {
  	struct sockaddr_in6 *psin6;
  	struct group_req greq;
  
  	if (optlen < sizeof(greq))
  		return -EINVAL;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
274
  	if (copy_from_sockptr(&greq, optval, sizeof(greq)))
fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
275
276
277
278
279
280
281
282
283
284
  		return -EFAULT;
  
  	if (greq.gr_group.ss_family != AF_INET6)
  		return -EADDRNOTAVAIL;
  	psin6 = (struct sockaddr_in6 *)&greq.gr_group;
  	if (optname == MCAST_JOIN_GROUP)
  		return ipv6_sock_mc_join(sk, greq.gr_interface,
  					 &psin6->sin6_addr);
  	return ipv6_sock_mc_drop(sk, greq.gr_interface, &psin6->sin6_addr);
  }
fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
285
  static int compat_ipv6_mcast_join_leave(struct sock *sk, int optname,
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
286
  		sockptr_t optval, int optlen)
fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
287
288
289
  {
  	struct compat_group_req gr32;
  	struct sockaddr_in6 *psin6;
fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
290
291
292
  
  	if (optlen < sizeof(gr32))
  		return -EINVAL;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
293
  	if (copy_from_sockptr(&gr32, optval, sizeof(gr32)))
fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
294
295
296
297
  		return -EFAULT;
  
  	if (gr32.gr_group.ss_family != AF_INET6)
  		return -EADDRNOTAVAIL;
fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
298
299
  	psin6 = (struct sockaddr_in6 *)&gr32.gr_group;
  	if (optname == MCAST_JOIN_GROUP)
3021ad529   Christoph Hellwig   net/ipv6: remove ...
300
  		return ipv6_sock_mc_join(sk, gr32.gr_interface,
fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
301
  					&psin6->sin6_addr);
3021ad529   Christoph Hellwig   net/ipv6: remove ...
302
  	return ipv6_sock_mc_drop(sk, gr32.gr_interface, &psin6->sin6_addr);
fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
303
  }
fdf5bdd87   Christoph Hellwig   net/ipv6: factor ...
304

894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
305
  static int ipv6_set_opt_hdr(struct sock *sk, int optname, sockptr_t optval,
b84d2b73a   Christoph Hellwig   net/ipv6: factor ...
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
  		int optlen)
  {
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	struct ipv6_opt_hdr *new = NULL;
  	struct net *net = sock_net(sk);
  	struct ipv6_txoptions *opt;
  	int err;
  
  	/* hop-by-hop / destination options are privileged option */
  	if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW))
  		return -EPERM;
  
  	/* remove any sticky options header with a zero option
  	 * length, per RFC3542.
  	 */
  	if (optlen > 0) {
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
322
  		if (sockptr_is_null(optval))
b84d2b73a   Christoph Hellwig   net/ipv6: factor ...
323
324
325
326
327
  			return -EINVAL;
  		if (optlen < sizeof(struct ipv6_opt_hdr) ||
  		    optlen & 0x7 ||
  		    optlen > 8 * 255)
  			return -EINVAL;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
328
  		new = memdup_sockptr(optval, optlen);
b84d2b73a   Christoph Hellwig   net/ipv6: factor ...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
  		if (IS_ERR(new))
  			return PTR_ERR(new);
  		if (unlikely(ipv6_optlen(new) > optlen)) {
  			kfree(new);
  			return -EINVAL;
  		}
  	}
  
  	opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk));
  	opt = ipv6_renew_options(sk, opt, optname, new);
  	kfree(new);
  	if (IS_ERR(opt))
  		return PTR_ERR(opt);
  
  	/* routing header option needs extra check */
  	err = -EINVAL;
  	if (optname == IPV6_RTHDR && opt && opt->srcrt) {
  		struct ipv6_rt_hdr *rthdr = opt->srcrt;
  		switch (rthdr->type) {
  #if IS_ENABLED(CONFIG_IPV6_MIP6)
  		case IPV6_SRCRT_TYPE_2:
  			if (rthdr->hdrlen != 2 || rthdr->segments_left != 1)
  				goto sticky_done;
  			break;
  #endif
  		case IPV6_SRCRT_TYPE_4:
  		{
  			struct ipv6_sr_hdr *srh =
  				(struct ipv6_sr_hdr *)opt->srcrt;
  
  			if (!seg6_validate_srh(srh, optlen, false))
  				goto sticky_done;
  			break;
  		}
  		default:
  			goto sticky_done;
  		}
  	}
  
  	err = 0;
  	opt = ipv6_update_options(sk, opt);
  sticky_done:
  	if (opt) {
  		atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
  		txopt_put(opt);
  	}
  	return err;
  }
3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
377
  static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
378
  		   sockptr_t optval, unsigned int optlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
  {
  	struct ipv6_pinfo *np = inet6_sk(sk);
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
381
  	struct net *net = sock_net(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
  	int val, valbool;
  	int retv = -ENOPROTOOPT;
baf606d9c   Marcelo Ricardo Leitner   ipv4,ipv6: grab r...
384
  	bool needs_rtnl = setsockopt_needs_rtnl(optname);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385

894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
386
  	if (sockptr_is_null(optval))
67ba4152e   Ian Morris   ipv6: White-space...
387
  		val = 0;
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
388
389
  	else {
  		if (optlen >= sizeof(int)) {
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
390
  			if (copy_from_sockptr(&val, optval, sizeof(val)))
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
391
392
393
394
  				return -EFAULT;
  		} else
  			val = 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395

67ba4152e   Ian Morris   ipv6: White-space...
396
  	valbool = (val != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397

7bc570c8b   YOSHIFUJI Hideaki   [IPV6] MROUTE: Su...
398
  	if (ip6_mroute_opt(optname))
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
399
  		return ip6_mroute_setsockopt(sk, optname, optval, optlen);
7bc570c8b   YOSHIFUJI Hideaki   [IPV6] MROUTE: Su...
400

baf606d9c   Marcelo Ricardo Leitner   ipv4,ipv6: grab r...
401
402
  	if (needs_rtnl)
  		rtnl_lock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
407
  	lock_sock(sk);
  
  	switch (optname) {
  
  	case IPV6_ADDRFORM:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
408
409
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
411
412
  		if (val == PF_INET) {
  			struct ipv6_txoptions *opt;
  			struct sk_buff *pktopt;
49d074f40   Denis V. Lunev   [IPV6]: Do not ch...
413
414
  			if (sk->sk_type == SOCK_RAW)
  				break;
9596cc826   Denis V. Lunev   [IPV6]: Do not ch...
415
416
417
418
419
420
421
  			if (sk->sk_protocol == IPPROTO_UDP ||
  			    sk->sk_protocol == IPPROTO_UDPLITE) {
  				struct udp_sock *up = udp_sk(sk);
  				if (up->pending == AF_INET6) {
  					retv = -EBUSY;
  					break;
  				}
79a1f0ccd   Hangbin Liu   ipv6: fix IPV6_AD...
422
423
424
425
426
427
  			} else if (sk->sk_protocol == IPPROTO_TCP) {
  				if (sk->sk_prot != &tcpv6_prot) {
  					retv = -EBUSY;
  					break;
  				}
  			} else {
b6f611890   Eric Dumazet   ipv6: restrict IP...
428
429
  				break;
  			}
79a1f0ccd   Hangbin Liu   ipv6: fix IPV6_AD...
430

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
433
434
435
436
  			if (sk->sk_state != TCP_ESTABLISHED) {
  				retv = -ENOTCONN;
  				break;
  			}
  
  			if (ipv6_only_sock(sk) ||
efe4208f4   Eric Dumazet   ipv6: make lookup...
437
  			    !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
439
440
441
442
  				retv = -EADDRNOTAVAIL;
  				break;
  			}
  
  			fl6_free_socklist(sk);
8651be8f1   WANG Cong   ipv6: fix a poten...
443
  			__ipv6_sock_mc_close(sk);
8c0de6e96   Cong Wang   ipv6: fix memory ...
444
  			__ipv6_sock_ac_close(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445

e6848976b   Arnaldo Carvalho de Melo   [NET]: Cleanup IN...
446
447
448
449
450
451
  			/*
  			 * Sock is moving from IPv6 to IPv4 (sk_prot), so
  			 * remove it from the refcnt debug socks count in the
  			 * original family...
  			 */
  			sk_refcnt_debug_dec(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
  			if (sk->sk_protocol == IPPROTO_TCP) {
d83d8461f   Arnaldo Carvalho de Melo   [IP_SOCKGLUE]: Re...
453
  				struct inet_connection_sock *icsk = inet_csk(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
  				local_bh_disable();
c29a0bc4d   Pavel Emelyanov   [SOCK][NETNS]: Ad...
455
456
  				sock_prot_inuse_add(net, sk->sk_prot, -1);
  				sock_prot_inuse_add(net, &tcp_prot, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
  				local_bh_enable();
  				sk->sk_prot = &tcp_prot;
d83d8461f   Arnaldo Carvalho de Melo   [IP_SOCKGLUE]: Re...
459
  				icsk->icsk_af_ops = &ipv4_specific;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
  				sk->sk_socket->ops = &inet_stream_ops;
  				sk->sk_family = PF_INET;
d83d8461f   Arnaldo Carvalho de Melo   [IP_SOCKGLUE]: Re...
462
  				tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
  			} else {
ba4e58eca   Gerrit Renker   [NET]: Supporting...
464
  				struct proto *prot = &udp_prot;
db8dac20d   David S. Miller   [UDP]: Revert udp...
465
  				if (sk->sk_protocol == IPPROTO_UDPLITE)
ba4e58eca   Gerrit Renker   [NET]: Supporting...
466
  					prot = &udplite_prot;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
  				local_bh_disable();
c29a0bc4d   Pavel Emelyanov   [SOCK][NETNS]: Ad...
468
469
  				sock_prot_inuse_add(net, sk->sk_prot, -1);
  				sock_prot_inuse_add(net, prot, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
  				local_bh_enable();
ba4e58eca   Gerrit Renker   [NET]: Supporting...
471
  				sk->sk_prot = prot;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
  				sk->sk_socket->ops = &inet_dgram_ops;
  				sk->sk_family = PF_INET;
  			}
45f6fad84   Eric Dumazet   ipv6: add complet...
475
476
477
478
479
480
  			opt = xchg((__force struct ipv6_txoptions **)&np->opt,
  				   NULL);
  			if (opt) {
  				atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
  				txopt_put(opt);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
  			pktopt = xchg(&np->pktoptions, NULL);
800d55f14   Wei Yongjun   ipv6: Remove some...
482
  			kfree_skb(pktopt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483

e6848976b   Arnaldo Carvalho de Melo   [NET]: Cleanup IN...
484
485
486
487
488
  			/*
  			 * ... and add it to the refcnt debug socks count
  			 * in the new family. -acme
  			 */
  			sk_refcnt_debug_inc(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
493
494
495
  			module_put(THIS_MODULE);
  			retv = 0;
  			break;
  		}
  		goto e_inval;
  
  	case IPV6_V6ONLY:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
496
  		if (optlen < sizeof(int) ||
c720c7e83   Eric Dumazet   inet: rename some...
497
  		    inet_sk(sk)->inet_num)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
  			goto e_inval;
9fe516ba3   Eric Dumazet   inet: move ipv6on...
499
  		sk->sk_ipv6only = valbool;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
  		retv = 0;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
502
  	case IPV6_RECVPKTINFO:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
503
504
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
  		np->rxopt.bits.rxinfo = valbool;
  		retv = 0;
  		break;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
508

333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
509
  	case IPV6_2292PKTINFO:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
510
511
  		if (optlen < sizeof(int))
  			goto e_inval;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
512
513
514
  		np->rxopt.bits.rxoinfo = valbool;
  		retv = 0;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515

333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
516
  	case IPV6_RECVHOPLIMIT:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
517
518
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
  		np->rxopt.bits.rxhlim = valbool;
  		retv = 0;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
522
  	case IPV6_2292HOPLIMIT:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
523
524
  		if (optlen < sizeof(int))
  			goto e_inval;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
525
526
527
528
529
  		np->rxopt.bits.rxohlim = valbool;
  		retv = 0;
  		break;
  
  	case IPV6_RECVRTHDR:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
530
531
  		if (optlen < sizeof(int))
  			goto e_inval;
4c752098f   YOSHIFUJI Hideaki   [IPV6]: Make IPV6...
532
  		np->rxopt.bits.srcrt = valbool;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
  		retv = 0;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
535
  	case IPV6_2292RTHDR:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
536
537
  		if (optlen < sizeof(int))
  			goto e_inval;
4c752098f   YOSHIFUJI Hideaki   [IPV6]: Make IPV6...
538
  		np->rxopt.bits.osrcrt = valbool;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
539
540
541
542
  		retv = 0;
  		break;
  
  	case IPV6_RECVHOPOPTS:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
543
544
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
547
  		np->rxopt.bits.hopopts = valbool;
  		retv = 0;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
548
  	case IPV6_2292HOPOPTS:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
549
550
  		if (optlen < sizeof(int))
  			goto e_inval;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
551
552
553
554
555
  		np->rxopt.bits.ohopopts = valbool;
  		retv = 0;
  		break;
  
  	case IPV6_RECVDSTOPTS:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
556
557
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
  		np->rxopt.bits.dstopts = valbool;
  		retv = 0;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
561
  	case IPV6_2292DSTOPTS:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
562
563
  		if (optlen < sizeof(int))
  			goto e_inval;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
564
565
566
  		np->rxopt.bits.odstopts = valbool;
  		retv = 0;
  		break;
41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
567
  	case IPV6_TCLASS:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
568
569
  		if (optlen < sizeof(int))
  			goto e_inval;
d0ee011f7   Rémi Denis-Courmont   [IPV6]: Accept -1...
570
  		if (val < -1 || val > 0xff)
41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
571
  			goto e_inval;
26ced1e4a   Gerrit Renker   inet6: Set defaul...
572
573
574
  		/* RFC 3542, 6.5: default traffic class of 0x0 */
  		if (val == -1)
  			val = 0;
41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
575
576
577
  		np->tclass = val;
  		retv = 0;
  		break;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
578

41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
579
  	case IPV6_RECVTCLASS:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
580
581
  		if (optlen < sizeof(int))
  			goto e_inval;
41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
582
583
584
  		np->rxopt.bits.rxtclass = valbool;
  		retv = 0;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
  	case IPV6_FLOWINFO:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
586
587
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
589
590
  		np->rxopt.bits.rxflow = valbool;
  		retv = 0;
  		break;
793b14731   Brian Haley   IPv6: data struct...
591
592
593
594
595
596
  	case IPV6_RECVPATHMTU:
  		if (optlen < sizeof(int))
  			goto e_inval;
  		np->rxopt.bits.rxpmtu = valbool;
  		retv = 0;
  		break;
6c4686228   Balazs Scheidler   tproxy: added tpr...
597
  	case IPV6_TRANSPARENT:
35fc59c95   Maciej Żenczykowski   net-ipv6: IPV6_TR...
598
599
  		if (valbool && !ns_capable(net->user_ns, CAP_NET_RAW) &&
  		    !ns_capable(net->user_ns, CAP_NET_ADMIN)) {
b889416b5   Balazs Scheidler   tproxy: Add missi...
600
601
602
  			retv = -EPERM;
  			break;
  		}
6c4686228   Balazs Scheidler   tproxy: added tpr...
603
604
605
606
607
608
  		if (optlen < sizeof(int))
  			goto e_inval;
  		/* we don't have a separate transparent bit for IPV6 we use the one in the IPv4 socket */
  		inet_sk(sk)->transparent = valbool;
  		retv = 0;
  		break;
84e14fe35   Maciej Żenczykowski   net-ipv6: add sup...
609
610
611
612
613
614
615
  	case IPV6_FREEBIND:
  		if (optlen < sizeof(int))
  			goto e_inval;
  		/* we also don't have a separate freebind bit for IPV6 */
  		inet_sk(sk)->freebind = valbool;
  		retv = 0;
  		break;
6c4686228   Balazs Scheidler   tproxy: added tpr...
616
617
618
619
620
621
  	case IPV6_RECVORIGDSTADDR:
  		if (optlen < sizeof(int))
  			goto e_inval;
  		np->rxopt.bits.rxorigdstaddr = valbool;
  		retv = 0;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
622
623
624
625
  	case IPV6_HOPOPTS:
  	case IPV6_RTHDRDSTOPTS:
  	case IPV6_RTHDR:
  	case IPV6_DSTOPTS:
b84d2b73a   Christoph Hellwig   net/ipv6: factor ...
626
  		retv = ipv6_set_opt_hdr(sk, optname, optval, optlen);
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
627
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
628

b24a2516d   Yang Hongyang   ipv6: Add IPV6_PK...
629
630
631
632
633
634
  	case IPV6_PKTINFO:
  	{
  		struct in6_pktinfo pkt;
  
  		if (optlen == 0)
  			goto e_inval;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
635
636
  		else if (optlen < sizeof(struct in6_pktinfo) ||
  			 sockptr_is_null(optval))
b24a2516d   Yang Hongyang   ipv6: Add IPV6_PK...
637
  			goto e_inval;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
638
639
640
  		if (copy_from_sockptr(&pkt, optval, sizeof(pkt))) {
  			retv = -EFAULT;
  			break;
b24a2516d   Yang Hongyang   ipv6: Add IPV6_PK...
641
  		}
d839a0ebe   Mike Manning   ipv6: allow ping ...
642
  		if (!sk_dev_equal_l3scope(sk, pkt.ipi6_ifindex))
b24a2516d   Yang Hongyang   ipv6: Add IPV6_PK...
643
644
645
  			goto e_inval;
  
  		np->sticky_pktinfo.ipi6_ifindex = pkt.ipi6_ifindex;
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
646
  		np->sticky_pktinfo.ipi6_addr = pkt.ipi6_addr;
b24a2516d   Yang Hongyang   ipv6: Add IPV6_PK...
647
648
649
  		retv = 0;
  		break;
  	}
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
650
  	case IPV6_2292PKTOPTIONS:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
  	{
  		struct ipv6_txoptions *opt = NULL;
  		struct msghdr msg;
4c9483b2f   David S. Miller   ipv6: Convert to ...
654
  		struct flowi6 fl6;
26879da58   Wei Wang   ipv6: add new str...
655
  		struct ipcm6_cookie ipc6;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656

4c9483b2f   David S. Miller   ipv6: Convert to ...
657
658
659
  		memset(&fl6, 0, sizeof(fl6));
  		fl6.flowi6_oif = sk->sk_bound_dev_if;
  		fl6.flowi6_mark = sk->sk_mark;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
662
663
664
665
666
667
668
669
670
671
672
  
  		if (optlen == 0)
  			goto update;
  
  		/* 1K is probably excessive
  		 * 1K is surely not enough, 2K per standard header is 16K.
  		 */
  		retv = -EINVAL;
  		if (optlen > 64*1024)
  			break;
  
  		opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
  		retv = -ENOBUFS;
63159f29b   Ian Morris   ipv6: coding styl...
673
  		if (!opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
676
  			break;
  
  		memset(opt, 0, sizeof(*opt));
0aeea21ad   Reshetova, Elena   net, ipv6: conver...
677
  		refcount_set(&opt->refcnt, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
679
  		opt->tot_len = sizeof(*opt) + optlen;
  		retv = -EFAULT;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
680
  		if (copy_from_sockptr(opt + 1, optval, optlen))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
  			goto done;
  
  		msg.msg_controllen = optlen;
67ba4152e   Ian Morris   ipv6: White-space...
684
  		msg.msg_control = (void *)(opt+1);
26879da58   Wei Wang   ipv6: add new str...
685
  		ipc6.opt = opt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686

5fdaa88df   Willem de Bruijn   ipv6: fold sockcm...
687
  		retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, &ipc6);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
690
691
  		if (retv)
  			goto done;
  update:
  		retv = 0;
e7712f1a7   YOSHIFUJI Hideaki   [IPV6]: Share com...
692
  		opt = ipv6_update_options(sk, opt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
693
  done:
45f6fad84   Eric Dumazet   ipv6: add complet...
694
695
696
697
  		if (opt) {
  			atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
  			txopt_put(opt);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
699
700
  		break;
  	}
  	case IPV6_UNICAST_HOPS:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
701
702
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
703
704
705
706
707
708
709
710
  		if (val > 255 || val < -1)
  			goto e_inval;
  		np->hop_limit = val;
  		retv = 0;
  		break;
  
  	case IPV6_MULTICAST_HOPS:
  		if (sk->sk_type == SOCK_STREAM)
1717699cd   YOSHIFUJI Hideaki   ipv6: Fail with a...
711
  			break;
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
712
713
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
715
  		if (val > 255 || val < -1)
  			goto e_inval;
2a38e6d5a   Li Wei   ipv6: Set mcast_h...
716
  		np->mcast_hops = (val == -1 ? IPV6_DEFAULT_MCASTHOPS : val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
719
720
  		retv = 0;
  		break;
  
  	case IPV6_MULTICAST_LOOP:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
721
722
  		if (optlen < sizeof(int))
  			goto e_inval;
28d448821   YOSHIFUJI Hideaki   ipv6: Check IPV6_...
723
724
  		if (val != valbool)
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725
726
727
  		np->mc_loop = valbool;
  		retv = 0;
  		break;
c4062dfc4   Erich E. Hoover   ipv6: Implement I...
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
  	case IPV6_UNICAST_IF:
  	{
  		struct net_device *dev = NULL;
  		int ifindex;
  
  		if (optlen != sizeof(int))
  			goto e_inval;
  
  		ifindex = (__force int)ntohl((__force __be32)val);
  		if (ifindex == 0) {
  			np->ucast_oif = 0;
  			retv = 0;
  			break;
  		}
  
  		dev = dev_get_by_index(net, ifindex);
  		retv = -EADDRNOTAVAIL;
  		if (!dev)
  			break;
  		dev_put(dev);
  
  		retv = -EINVAL;
  		if (sk->sk_bound_dev_if)
  			break;
  
  		np->ucast_oif = ifindex;
  		retv = 0;
  		break;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
757
758
  	case IPV6_MULTICAST_IF:
  		if (sk->sk_type == SOCK_STREAM)
1717699cd   YOSHIFUJI Hideaki   ipv6: Fail with a...
759
  			break;
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
760
761
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762

4953f0fcc   Brian Haley   [IPv6]: Update se...
763
  		if (val) {
55b805035   Eric Dumazet   net: Fix IP_MULTI...
764
  			struct net_device *dev;
7bb387c5a   David Ahern   net: Allow IP_MUL...
765
  			int midx;
55b805035   Eric Dumazet   net: Fix IP_MULTI...
766

7bb387c5a   David Ahern   net: Allow IP_MUL...
767
  			rcu_read_lock();
4953f0fcc   Brian Haley   [IPv6]: Update se...
768

7bb387c5a   David Ahern   net: Allow IP_MUL...
769
  			dev = dev_get_by_index_rcu(net, val);
55b805035   Eric Dumazet   net: Fix IP_MULTI...
770
  			if (!dev) {
7bb387c5a   David Ahern   net: Allow IP_MUL...
771
  				rcu_read_unlock();
4953f0fcc   Brian Haley   [IPv6]: Update se...
772
773
774
  				retv = -ENODEV;
  				break;
  			}
7bb387c5a   David Ahern   net: Allow IP_MUL...
775
776
777
778
779
780
781
782
  			midx = l3mdev_master_ifindex_rcu(dev);
  
  			rcu_read_unlock();
  
  			if (sk->sk_bound_dev_if &&
  			    sk->sk_bound_dev_if != val &&
  			    (!midx || midx != sk->sk_bound_dev_if))
  				goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783
784
785
786
787
788
789
790
  		}
  		np->mcast_oif = val;
  		retv = 0;
  		break;
  	case IPV6_ADD_MEMBERSHIP:
  	case IPV6_DROP_MEMBERSHIP:
  	{
  		struct ipv6_mreq mreq;
a28398ba6   Wang Chen   [IPV6]: Check len...
791
792
  		if (optlen < sizeof(struct ipv6_mreq))
  			goto e_inval;
a96fb49be   Flavio Leitner   [NET]: Fix IP_ADD...
793
794
795
  		retv = -EPROTO;
  		if (inet_sk(sk)->is_icsk)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
796
  		retv = -EFAULT;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
797
  		if (copy_from_sockptr(&mreq, optval, sizeof(struct ipv6_mreq)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
799
800
  			break;
  
  		if (optname == IPV6_ADD_MEMBERSHIP)
54ff9ef36   Marcelo Ricardo Leitner   ipv4, ipv6: kill ...
801
  			retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
  		else
54ff9ef36   Marcelo Ricardo Leitner   ipv4, ipv6: kill ...
803
  			retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
805
806
807
808
809
  		break;
  	}
  	case IPV6_JOIN_ANYCAST:
  	case IPV6_LEAVE_ANYCAST:
  	{
  		struct ipv6_mreq mreq;
a28398ba6   Wang Chen   [IPV6]: Check len...
810
  		if (optlen < sizeof(struct ipv6_mreq))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
812
813
  			goto e_inval;
  
  		retv = -EFAULT;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
814
  		if (copy_from_sockptr(&mreq, optval, sizeof(struct ipv6_mreq)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
815
816
817
818
819
820
821
822
  			break;
  
  		if (optname == IPV6_JOIN_ANYCAST)
  			retv = ipv6_sock_ac_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
  		else
  			retv = ipv6_sock_ac_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
  		break;
  	}
15033f045   Andre Naujoks   ipv6: Add sockopt...
823
824
825
826
827
828
  	case IPV6_MULTICAST_ALL:
  		if (optlen < sizeof(int))
  			goto e_inval;
  		np->mc_all = valbool;
  		retv = 0;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
829
830
  	case MCAST_JOIN_GROUP:
  	case MCAST_LEAVE_GROUP:
3021ad529   Christoph Hellwig   net/ipv6: remove ...
831
832
833
834
835
836
  		if (in_compat_syscall())
  			retv = compat_ipv6_mcast_join_leave(sk, optname, optval,
  							    optlen);
  		else
  			retv = ipv6_mcast_join_leave(sk, optname, optval,
  						     optlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
839
840
841
  	case MCAST_JOIN_SOURCE_GROUP:
  	case MCAST_LEAVE_SOURCE_GROUP:
  	case MCAST_BLOCK_SOURCE:
  	case MCAST_UNBLOCK_SOURCE:
3021ad529   Christoph Hellwig   net/ipv6: remove ...
842
  		retv = do_ipv6_mcast_group_source(sk, optname, optval, optlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
  	case MCAST_MSFILTER:
3021ad529   Christoph Hellwig   net/ipv6: remove ...
845
846
847
848
849
  		if (in_compat_syscall())
  			retv = compat_ipv6_set_mcast_msfilter(sk, optval,
  							      optlen);
  		else
  			retv = ipv6_set_mcast_msfilter(sk, optval, optlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
  	case IPV6_ROUTER_ALERT:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
852
853
  		if (optlen < sizeof(int))
  			goto e_inval;
725a8ff04   Denis V. Lunev   ipv6: remove unus...
854
  		retv = ip6_ra_control(sk, val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855
  		break;
9036b2fe0   Francesco Ruggeri   net: ipv6: add so...
856
857
858
859
860
861
  	case IPV6_ROUTER_ALERT_ISOLATE:
  		if (optlen < sizeof(int))
  			goto e_inval;
  		np->rtalert_isolate = valbool;
  		retv = 0;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
  	case IPV6_MTU_DISCOVER:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
863
864
  		if (optlen < sizeof(int))
  			goto e_inval;
0b95227a7   Hannes Frederic Sowa   ipv6: yet another...
865
  		if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_OMIT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866
867
868
869
870
  			goto e_inval;
  		np->pmtudisc = val;
  		retv = 0;
  		break;
  	case IPV6_MTU:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
871
872
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
874
875
876
877
878
  		if (val && val < IPV6_MIN_MTU)
  			goto e_inval;
  		np->frag_size = val;
  		retv = 0;
  		break;
  	case IPV6_RECVERR:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
879
880
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
881
882
883
884
885
886
  		np->recverr = valbool;
  		if (!val)
  			skb_queue_purge(&sk->sk_error_queue);
  		retv = 0;
  		break;
  	case IPV6_FLOWINFO_SEND:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
887
888
  		if (optlen < sizeof(int))
  			goto e_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
891
892
  		np->sndflow = valbool;
  		retv = 0;
  		break;
  	case IPV6_FLOWLABEL_MGR:
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
893
  		retv = ipv6_flowlabel_opt(sk, optval, optlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
896
  		break;
  	case IPV6_IPSEC_POLICY:
  	case IPV6_XFRM_POLICY:
6fc0b4a7a   Herbert Xu   [IPSEC]: Restrict...
897
  		retv = -EPERM;
af31f412c   Eric W. Biederman   net: Allow userns...
898
  		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
6fc0b4a7a   Herbert Xu   [IPSEC]: Restrict...
899
  			break;
894cfbc0c   Christoph Hellwig   net/ipv6: switch ...
900
  		retv = xfrm_user_policy(sk, optname, optval, optlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
  		break;
7cbca67c0   YOSHIFUJI Hideaki   [IPV6]: Support S...
902
  	case IPV6_ADDR_PREFERENCES:
b2a9d7c2f   YOSHIFUJI Hideaki   [IPV6]: Check len...
903
904
  		if (optlen < sizeof(int))
  			goto e_inval;
18d5ad623   Christoph Hellwig   ipv6: add ip6_soc...
905
  		retv = __ip6_sock_set_addr_preferences(sk, val);
7cbca67c0   YOSHIFUJI Hideaki   [IPV6]: Support S...
906
  		break;
e802af9ca   Stephen Hemminger   IPv6: Generic TTL...
907
908
909
910
911
912
  	case IPV6_MINHOPCOUNT:
  		if (optlen < sizeof(int))
  			goto e_inval;
  		if (val < 0 || val > 255)
  			goto e_inval;
  		np->min_hopcount = val;
d4596bad2   Hannes Frederic Sowa   ipv6: setsockopt(...
913
  		retv = 0;
793b14731   Brian Haley   IPv6: data struct...
914
915
916
  		break;
  	case IPV6_DONTFRAG:
  		np->dontfrag = valbool;
e802af9ca   Stephen Hemminger   IPv6: Generic TTL...
917
918
  		retv = 0;
  		break;
cb1ce2ef3   Tom Herbert   ipv6: Implement a...
919
920
  	case IPV6_AUTOFLOWLABEL:
  		np->autoflowlabel = valbool;
513674b5a   Shaohua Li   net: reevalulate ...
921
  		np->autoflowlabel_set = 1;
cb1ce2ef3   Tom Herbert   ipv6: Implement a...
922
923
  		retv = 0;
  		break;
0cc0aa614   Willem de Bruijn   ipv6: add IPV6_RE...
924
925
926
927
  	case IPV6_RECVFRAGSIZE:
  		np->rxopt.bits.recvfragsize = valbool;
  		retv = 0;
  		break;
01370434d   Willem de Bruijn   icmp6: support rf...
928
929
930
931
932
933
934
935
  	case IPV6_RECVERR_RFC4884:
  		if (optlen < sizeof(int))
  			goto e_inval;
  		if (val < 0 || val > 1)
  			goto e_inval;
  		np->recverr_rfc4884 = valbool;
  		retv = 0;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
936
  	}
7cbca67c0   YOSHIFUJI Hideaki   [IPV6]: Support S...
937

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
  	release_sock(sk);
baf606d9c   Marcelo Ricardo Leitner   ipv4,ipv6: grab r...
939
940
  	if (needs_rtnl)
  		rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
941

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
943
944
945
  	return retv;
  
  e_inval:
  	release_sock(sk);
baf606d9c   Marcelo Ricardo Leitner   ipv4,ipv6: grab r...
946
947
  	if (needs_rtnl)
  		rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
948
949
  	return -EINVAL;
  }
a7b75c5a8   Christoph Hellwig   net: pass a sockp...
950
951
  int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
  		    unsigned int optlen)
3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
952
953
954
955
956
957
958
959
  {
  	int err;
  
  	if (level == SOL_IP && sk->sk_type != SOCK_RAW)
  		return udp_prot.setsockopt(sk, level, optname, optval, optlen);
  
  	if (level != SOL_IPV6)
  		return -ENOPROTOOPT;
a7b75c5a8   Christoph Hellwig   net: pass a sockp...
960
  	err = do_ipv6_setsockopt(sk, level, optname, optval, optlen);
3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
961
962
963
  #ifdef CONFIG_NETFILTER
  	/* we need to exclude all possible ENOPROTOOPTs except default case */
  	if (err == -ENOPROTOOPT && optname != IPV6_IPSEC_POLICY &&
3f34cfae1   Paolo Abeni   netfilter: on soc...
964
  			optname != IPV6_XFRM_POLICY)
a7b75c5a8   Christoph Hellwig   net: pass a sockp...
965
  		err = nf_setsockopt(sk, PF_INET6, optname, optval, optlen);
3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
966
967
968
  #endif
  	return err;
  }
7159039a1   YOSHIFUJI Hideaki   [IPV6]: Decentral...
969
  EXPORT_SYMBOL(ipv6_setsockopt);
3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
970

286930797   David S. Miller   [IPV6]: Handle np...
971
  static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,
4c6510a73   YOSHIFUJI Hideaki   [IPV6]: Return co...
972
  				  int optname, char __user *optval, int len)
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
973
  {
286930797   David S. Miller   [IPV6]: Handle np...
974
  	struct ipv6_opt_hdr *hdr;
4c6510a73   YOSHIFUJI Hideaki   [IPV6]: Return co...
975
976
  	if (!opt)
  		return 0;
67ba4152e   Ian Morris   ipv6: White-space...
977
  	switch (optname) {
4c6510a73   YOSHIFUJI Hideaki   [IPV6]: Return co...
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
  	case IPV6_HOPOPTS:
  		hdr = opt->hopopt;
  		break;
  	case IPV6_RTHDRDSTOPTS:
  		hdr = opt->dst0opt;
  		break;
  	case IPV6_RTHDR:
  		hdr = (struct ipv6_opt_hdr *)opt->srcrt;
  		break;
  	case IPV6_DSTOPTS:
  		hdr = opt->dst1opt;
  		break;
  	default:
  		return -EINVAL;	/* should not happen */
  	}
  
  	if (!hdr)
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
995
  		return 0;
286930797   David S. Miller   [IPV6]: Handle np...
996

d2b02ed94   Chris Wright   [IPV6] fix ipv6_g...
997
  	len = min_t(unsigned int, len, ipv6_optlen(hdr));
660adc6e6   Ilpo Järvinen   [IPv6]: Invalid s...
998
  	if (copy_to_user(optval, hdr, len))
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
999
  		return -EFAULT;
95b496b66   Yang Hongyang   [IPV6]: Fix the d...
1000
  	return len;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1001
  }
d5541e85c   Christoph Hellwig   net/ipv6: factor ...
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
  static int ipv6_get_msfilter(struct sock *sk, void __user *optval,
  		int __user *optlen, int len)
  {
  	const int size0 = offsetof(struct group_filter, gf_slist);
  	struct group_filter __user *p = optval;
  	struct group_filter gsf;
  	int num;
  	int err;
  
  	if (len < size0)
  		return -EINVAL;
  	if (copy_from_user(&gsf, p, size0))
  		return -EFAULT;
  	if (gsf.gf_group.ss_family != AF_INET6)
  		return -EADDRNOTAVAIL;
  	num = gsf.gf_numsrc;
  	lock_sock(sk);
  	err = ip6_mc_msfget(sk, &gsf, p->gf_slist);
  	if (!err) {
  		if (num > gsf.gf_numsrc)
  			num = gsf.gf_numsrc;
  		if (put_user(GROUP_FILTER_SIZE(num), optlen) ||
  		    copy_to_user(p, &gsf, size0))
  			err = -EFAULT;
  	}
  	release_sock(sk);
  	return err;
  }
d5541e85c   Christoph Hellwig   net/ipv6: factor ...
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
  static int compat_ipv6_get_msfilter(struct sock *sk, void __user *optval,
  		int __user *optlen)
  {
  	const int size0 = offsetof(struct compat_group_filter, gf_slist);
  	struct compat_group_filter __user *p = optval;
  	struct compat_group_filter gf32;
  	struct group_filter gf;
  	int len, err;
  	int num;
  
  	if (get_user(len, optlen))
  		return -EFAULT;
  	if (len < size0)
  		return -EINVAL;
  
  	if (copy_from_user(&gf32, p, size0))
  		return -EFAULT;
  	gf.gf_interface = gf32.gf_interface;
  	gf.gf_fmode = gf32.gf_fmode;
  	num = gf.gf_numsrc = gf32.gf_numsrc;
  	gf.gf_group = gf32.gf_group;
  
  	if (gf.gf_group.ss_family != AF_INET6)
  		return -EADDRNOTAVAIL;
  
  	lock_sock(sk);
  	err = ip6_mc_msfget(sk, &gf, p->gf_slist);
  	release_sock(sk);
  	if (err)
  		return err;
  	if (num > gf.gf_numsrc)
  		num = gf.gf_numsrc;
  	len = GROUP_FILTER_SIZE(num) - (sizeof(gf)-sizeof(gf32));
  	if (put_user(len, optlen) ||
  	    put_user(gf.gf_fmode, &p->gf_fmode) ||
  	    put_user(gf.gf_numsrc, &p->gf_numsrc))
  		return -EFAULT;
  	return 0;
  }
d5541e85c   Christoph Hellwig   net/ipv6: factor ...
1069

3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
1070
  static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
95c961747   Eric Dumazet   net: cleanup unsi...
1071
  		    char __user *optval, int __user *optlen, unsigned int flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1072
1073
1074
1075
  {
  	struct ipv6_pinfo *np = inet6_sk(sk);
  	int len;
  	int val;
7bc570c8b   YOSHIFUJI Hideaki   [IPV6] MROUTE: Su...
1076
1077
  	if (ip6_mroute_opt(optname))
  		return ip6_mroute_getsockopt(sk, optname, optval, optlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
1081
1082
  	if (get_user(len, optlen))
  		return -EFAULT;
  	switch (optname) {
  	case IPV6_ADDRFORM:
  		if (sk->sk_protocol != IPPROTO_UDP &&
ba4e58eca   Gerrit Renker   [NET]: Supporting...
1083
  		    sk->sk_protocol != IPPROTO_UDPLITE &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1084
  		    sk->sk_protocol != IPPROTO_TCP)
1717699cd   YOSHIFUJI Hideaki   ipv6: Fail with a...
1085
  			return -ENOPROTOOPT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1086
1087
1088
1089
1090
  		if (sk->sk_state != TCP_ESTABLISHED)
  			return -ENOTCONN;
  		val = sk->sk_family;
  		break;
  	case MCAST_MSFILTER:
3021ad529   Christoph Hellwig   net/ipv6: remove ...
1091
1092
  		if (in_compat_syscall())
  			return compat_ipv6_get_msfilter(sk, optval, optlen);
d5541e85c   Christoph Hellwig   net/ipv6: factor ...
1093
  		return ipv6_get_msfilter(sk, optval, optlen, len);
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1094
  	case IPV6_2292PKTOPTIONS:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095
1096
1097
1098
1099
1100
1101
1102
1103
  	{
  		struct msghdr msg;
  		struct sk_buff *skb;
  
  		if (sk->sk_type != SOCK_STREAM)
  			return -ENOPROTOOPT;
  
  		msg.msg_control = optval;
  		msg.msg_controllen = len;
98e77438a   Daniel Baluta   ipv6: Fix ipv6_ge...
1104
  		msg.msg_flags = flags;
1b2f08df0   Christoph Hellwig   ipv6: set msg_con...
1105
  		msg.msg_control_is_user = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1106
1107
1108
1109
  
  		lock_sock(sk);
  		skb = np->pktoptions;
  		if (skb)
4b261c75a   Hannes Frederic Sowa   ipv6: make IPV6_R...
1110
  			ip6_datagram_recv_ctl(sk, &msg, skb);
1dc7b90f7   Eric Dumazet   ipv6: tcp: fix ra...
1111
1112
  		release_sock(sk);
  		if (!skb) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1113
1114
  			if (np->rxopt.bits.rxinfo) {
  				struct in6_pktinfo src_info;
f250dcdac   Yang Hongyang   ipv6: fix the ret...
1115
1116
  				src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
  					np->sticky_pktinfo.ipi6_ifindex;
efe4208f4   Eric Dumazet   ipv6: make lookup...
1117
  				src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr : np->sticky_pktinfo.ipi6_addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1118
1119
1120
1121
1122
1123
  				put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
  			}
  			if (np->rxopt.bits.rxhlim) {
  				int hlim = np->mcast_hops;
  				put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
  			}
4c507d289   Jiri Benc   net: implement IP...
1124
  			if (np->rxopt.bits.rxtclass) {
d76ed22b2   Li RongQing   ipv6: move IPV6_T...
1125
  				int tclass = (int)ip6_tclass(np->rcv_flowinfo);
4c507d289   Jiri Benc   net: implement IP...
1126
1127
  				put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
  			}
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1128
1129
  			if (np->rxopt.bits.rxoinfo) {
  				struct in6_pktinfo src_info;
f250dcdac   Yang Hongyang   ipv6: fix the ret...
1130
1131
  				src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
  					np->sticky_pktinfo.ipi6_ifindex;
efe4208f4   Eric Dumazet   ipv6: make lookup...
1132
1133
  				src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr :
  								     np->sticky_pktinfo.ipi6_addr;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1134
1135
1136
1137
1138
1139
  				put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
  			}
  			if (np->rxopt.bits.rxohlim) {
  				int hlim = np->mcast_hops;
  				put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
  			}
1397ed35f   Florent Fourcot   ipv6: add flowinf...
1140
  			if (np->rxopt.bits.rxflow) {
685360536   Florent Fourcot   ipv6: fix incorre...
1141
  				__be32 flowinfo = np->rcv_flowinfo;
1397ed35f   Florent Fourcot   ipv6: add flowinf...
1142
1143
  				put_cmsg(&msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1144
1145
1146
1147
1148
1149
1150
  		}
  		len -= msg.msg_controllen;
  		return put_user(len, optlen);
  	}
  	case IPV6_MTU:
  	{
  		struct dst_entry *dst;
b6c6712a4   Eric Dumazet   net: sk_dst_cache...
1151

1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1152
  		val = 0;
b6c6712a4   Eric Dumazet   net: sk_dst_cache...
1153
1154
1155
  		rcu_read_lock();
  		dst = __sk_dst_get(sk);
  		if (dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1156
  			val = dst_mtu(dst);
b6c6712a4   Eric Dumazet   net: sk_dst_cache...
1157
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1158
1159
1160
1161
1162
1163
  		if (!val)
  			return -ENOTCONN;
  		break;
  	}
  
  	case IPV6_V6ONLY:
9fe516ba3   Eric Dumazet   inet: move ipv6on...
1164
  		val = sk->sk_ipv6only;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1165
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1166
  	case IPV6_RECVPKTINFO:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1167
1168
  		val = np->rxopt.bits.rxinfo;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1169
1170
1171
1172
1173
  	case IPV6_2292PKTINFO:
  		val = np->rxopt.bits.rxoinfo;
  		break;
  
  	case IPV6_RECVHOPLIMIT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1174
1175
  		val = np->rxopt.bits.rxhlim;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1176
1177
1178
1179
1180
  	case IPV6_2292HOPLIMIT:
  		val = np->rxopt.bits.rxohlim;
  		break;
  
  	case IPV6_RECVRTHDR:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1181
1182
  		val = np->rxopt.bits.srcrt;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1183
1184
1185
  	case IPV6_2292RTHDR:
  		val = np->rxopt.bits.osrcrt;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1186
  	case IPV6_HOPOPTS:
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1187
1188
1189
1190
  	case IPV6_RTHDRDSTOPTS:
  	case IPV6_RTHDR:
  	case IPV6_DSTOPTS:
  	{
45f6fad84   Eric Dumazet   ipv6: add complet...
1191
  		struct ipv6_txoptions *opt;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1192
1193
  
  		lock_sock(sk);
1e1d04e67   Hannes Frederic Sowa   net: introduce lo...
1194
1195
  		opt = rcu_dereference_protected(np->opt,
  						lockdep_sock_is_held(sk));
45f6fad84   Eric Dumazet   ipv6: add complet...
1196
  		len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len);
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1197
  		release_sock(sk);
05335c222   Yang Hongyang   [IPV6]: Fix the r...
1198
1199
1200
  		/* check if ipv6_getsockopt_sticky() returns err code */
  		if (len < 0)
  			return len;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1201
1202
1203
1204
  		return put_user(len, optlen);
  	}
  
  	case IPV6_RECVHOPOPTS:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1205
1206
  		val = np->rxopt.bits.hopopts;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1207
1208
1209
1210
1211
  	case IPV6_2292HOPOPTS:
  		val = np->rxopt.bits.ohopopts;
  		break;
  
  	case IPV6_RECVDSTOPTS:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1212
1213
  		val = np->rxopt.bits.dstopts;
  		break;
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
1214
1215
1216
  	case IPV6_2292DSTOPTS:
  		val = np->rxopt.bits.odstopts;
  		break;
41a1f8ea4   YOSHIFUJI Hideaki   [IPV6]: Support I...
1217
1218
1219
1220
1221
1222
1223
  	case IPV6_TCLASS:
  		val = np->tclass;
  		break;
  
  	case IPV6_RECVTCLASS:
  		val = np->rxopt.bits.rxtclass;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1224
1225
1226
  	case IPV6_FLOWINFO:
  		val = np->rxopt.bits.rxflow;
  		break;
793b14731   Brian Haley   IPv6: data struct...
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
  	case IPV6_RECVPATHMTU:
  		val = np->rxopt.bits.rxpmtu;
  		break;
  
  	case IPV6_PATHMTU:
  	{
  		struct dst_entry *dst;
  		struct ip6_mtuinfo mtuinfo;
  
  		if (len < sizeof(mtuinfo))
  			return -EINVAL;
  
  		len = sizeof(mtuinfo);
  		memset(&mtuinfo, 0, sizeof(mtuinfo));
  
  		rcu_read_lock();
  		dst = __sk_dst_get(sk);
  		if (dst)
  			mtuinfo.ip6m_mtu = dst_mtu(dst);
  		rcu_read_unlock();
  		if (!mtuinfo.ip6m_mtu)
  			return -ENOTCONN;
  
  		if (put_user(len, optlen))
  			return -EFAULT;
  		if (copy_to_user(optval, &mtuinfo, len))
  			return -EFAULT;
  
  		return 0;
793b14731   Brian Haley   IPv6: data struct...
1256
  	}
6c4686228   Balazs Scheidler   tproxy: added tpr...
1257
1258
1259
  	case IPV6_TRANSPARENT:
  		val = inet_sk(sk)->transparent;
  		break;
84e14fe35   Maciej Żenczykowski   net-ipv6: add sup...
1260
1261
1262
  	case IPV6_FREEBIND:
  		val = inet_sk(sk)->freebind;
  		break;
6c4686228   Balazs Scheidler   tproxy: added tpr...
1263
1264
1265
  	case IPV6_RECVORIGDSTADDR:
  		val = np->rxopt.bits.rxorigdstaddr;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1266
  	case IPV6_UNICAST_HOPS:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1267
  	case IPV6_MULTICAST_HOPS:
befffe901   Brian Haley   [IPV6]: Fix IPV6_...
1268
1269
1270
1271
1272
1273
1274
  	{
  		struct dst_entry *dst;
  
  		if (optname == IPV6_UNICAST_HOPS)
  			val = np->hop_limit;
  		else
  			val = np->mcast_hops;
b6c6712a4   Eric Dumazet   net: sk_dst_cache...
1275
1276
1277
1278
  		if (val < 0) {
  			rcu_read_lock();
  			dst = __sk_dst_get(sk);
  			if (dst)
6b75d0908   YOSHIFUJI Hideaki   [IPV6]: Optimize ...
1279
  				val = ip6_dst_hoplimit(dst);
b6c6712a4   Eric Dumazet   net: sk_dst_cache...
1280
  			rcu_read_unlock();
befffe901   Brian Haley   [IPV6]: Fix IPV6_...
1281
  		}
b6c6712a4   Eric Dumazet   net: sk_dst_cache...
1282

befffe901   Brian Haley   [IPV6]: Fix IPV6_...
1283
  		if (val < 0)
53b7997fd   YOSHIFUJI Hideaki   ipv6 netns: Make ...
1284
  			val = sock_net(sk)->ipv6.devconf_all->hop_limit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1285
  		break;
befffe901   Brian Haley   [IPV6]: Fix IPV6_...
1286
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1287
1288
1289
1290
1291
1292
1293
1294
  
  	case IPV6_MULTICAST_LOOP:
  		val = np->mc_loop;
  		break;
  
  	case IPV6_MULTICAST_IF:
  		val = np->mcast_oif;
  		break;
15033f045   Andre Naujoks   ipv6: Add sockopt...
1295
1296
1297
  	case IPV6_MULTICAST_ALL:
  		val = np->mc_all;
  		break;
c4062dfc4   Erich E. Hoover   ipv6: Implement I...
1298
1299
1300
  	case IPV6_UNICAST_IF:
  		val = (__force int)htonl((__u32) np->ucast_oif);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
  	case IPV6_MTU_DISCOVER:
  		val = np->pmtudisc;
  		break;
  
  	case IPV6_RECVERR:
  		val = np->recverr;
  		break;
  
  	case IPV6_FLOWINFO_SEND:
  		val = np->sndflow;
  		break;
3fdfa5ff5   Florent Fourcot   ipv6: enable IPV6...
1312
1313
1314
  	case IPV6_FLOWLABEL_MGR:
  	{
  		struct in6_flowlabel_req freq;
46e5f4017   Florent Fourcot   ipv6: add a flag ...
1315
  		int flags;
3fdfa5ff5   Florent Fourcot   ipv6: enable IPV6...
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
  
  		if (len < sizeof(freq))
  			return -EINVAL;
  
  		if (copy_from_user(&freq, optval, sizeof(freq)))
  			return -EFAULT;
  
  		if (freq.flr_action != IPV6_FL_A_GET)
  			return -EINVAL;
  
  		len = sizeof(freq);
46e5f4017   Florent Fourcot   ipv6: add a flag ...
1327
  		flags = freq.flr_flags;
3fdfa5ff5   Florent Fourcot   ipv6: enable IPV6...
1328
  		memset(&freq, 0, sizeof(freq));
46e5f4017   Florent Fourcot   ipv6: add a flag ...
1329
  		val = ipv6_flowlabel_opt_get(sk, &freq, flags);
3fdfa5ff5   Florent Fourcot   ipv6: enable IPV6...
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
  		if (val < 0)
  			return val;
  
  		if (put_user(len, optlen))
  			return -EFAULT;
  		if (copy_to_user(optval, &freq, len))
  			return -EFAULT;
  
  		return 0;
  	}
7cbca67c0   YOSHIFUJI Hideaki   [IPV6]: Support S...
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
  	case IPV6_ADDR_PREFERENCES:
  		val = 0;
  
  		if (np->srcprefs & IPV6_PREFER_SRC_TMP)
  			val |= IPV6_PREFER_SRC_TMP;
  		else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC)
  			val |= IPV6_PREFER_SRC_PUBLIC;
  		else {
  			/* XXX: should we return system default? */
  			val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT;
  		}
  
  		if (np->srcprefs & IPV6_PREFER_SRC_COA)
  			val |= IPV6_PREFER_SRC_COA;
  		else
  			val |= IPV6_PREFER_SRC_HOME;
  		break;
e802af9ca   Stephen Hemminger   IPv6: Generic TTL...
1357
1358
1359
1360
  
  	case IPV6_MINHOPCOUNT:
  		val = np->min_hopcount;
  		break;
793b14731   Brian Haley   IPv6: data struct...
1361
1362
1363
1364
  
  	case IPV6_DONTFRAG:
  		val = np->dontfrag;
  		break;
7cbca67c0   YOSHIFUJI Hideaki   [IPV6]: Support S...
1365

cb1ce2ef3   Tom Herbert   ipv6: Implement a...
1366
  	case IPV6_AUTOFLOWLABEL:
e9191ffb6   Ben Hutchings   ipv6: Fix getsock...
1367
  		val = ip6_autoflowlabel(sock_net(sk), np);
cb1ce2ef3   Tom Herbert   ipv6: Implement a...
1368
  		break;
0cc0aa614   Willem de Bruijn   ipv6: add IPV6_RE...
1369
1370
1371
  	case IPV6_RECVFRAGSIZE:
  		val = np->rxopt.bits.recvfragsize;
  		break;
9036b2fe0   Francesco Ruggeri   net: ipv6: add so...
1372
1373
1374
  	case IPV6_ROUTER_ALERT_ISOLATE:
  		val = np->rtalert_isolate;
  		break;
01370434d   Willem de Bruijn   icmp6: support rf...
1375
1376
1377
  	case IPV6_RECVERR_RFC4884:
  		val = np->recverr_rfc4884;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1378
  	default:
cf6fc4a92   Wei Yongjun   [IPV6]: Fix the r...
1379
  		return -ENOPROTOOPT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1380
1381
  	}
  	len = min_t(unsigned int, sizeof(int), len);
67ba4152e   Ian Morris   ipv6: White-space...
1382
  	if (put_user(len, optlen))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1383
  		return -EFAULT;
67ba4152e   Ian Morris   ipv6: White-space...
1384
  	if (copy_to_user(optval, &val, len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1385
1386
1387
  		return -EFAULT;
  	return 0;
  }
3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
1388
1389
1390
1391
1392
1393
1394
  int ipv6_getsockopt(struct sock *sk, int level, int optname,
  		    char __user *optval, int __user *optlen)
  {
  	int err;
  
  	if (level == SOL_IP && sk->sk_type != SOCK_RAW)
  		return udp_prot.getsockopt(sk, level, optname, optval, optlen);
67ba4152e   Ian Morris   ipv6: White-space...
1395
  	if (level != SOL_IPV6)
3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
1396
  		return -ENOPROTOOPT;
98e77438a   Daniel Baluta   ipv6: Fix ipv6_ge...
1397
  	err = do_ipv6_getsockopt(sk, level, optname, optval, optlen, 0);
3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
1398
  #ifdef CONFIG_NETFILTER
cf6fc4a92   Wei Yongjun   [IPV6]: Fix the r...
1399
1400
  	/* we need to exclude all possible ENOPROTOOPTs except default case */
  	if (err == -ENOPROTOOPT && optname != IPV6_2292PKTOPTIONS) {
3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
1401
1402
1403
1404
  		int len;
  
  		if (get_user(len, optlen))
  			return -EFAULT;
01ea306f2   Paolo Abeni   netfilter: drop o...
1405
  		err = nf_getsockopt(sk, PF_INET6, optname, optval, &len);
3fdadf7d2   Dmitry Mishin   [NET]: {get|set}s...
1406
1407
1408
1409
1410
1411
  		if (err >= 0)
  			err = put_user(len, optlen);
  	}
  #endif
  	return err;
  }
7159039a1   YOSHIFUJI Hideaki   [IPV6]: Decentral...
1412
  EXPORT_SYMBOL(ipv6_getsockopt);