diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/ipv4/ip_sockglue.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/ipv4/ip_sockglue.c')
-rw-r--r-- | net/ipv4/ip_sockglue.c | 1093 |
1 files changed, 1093 insertions, 0 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c new file mode 100644 index 000000000000..47012b93cad2 --- /dev/null +++ b/net/ipv4/ip_sockglue.c | |||
@@ -0,0 +1,1093 @@ | |||
1 | /* | ||
2 | * INET An implementation of the TCP/IP protocol suite for the LINUX | ||
3 | * operating system. INET is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * The IP to API glue. | ||
7 | * | ||
8 | * Version: $Id: ip_sockglue.c,v 1.62 2002/02/01 22:01:04 davem Exp $ | ||
9 | * | ||
10 | * Authors: see ip.c | ||
11 | * | ||
12 | * Fixes: | ||
13 | * Many : Split from ip.c , see ip.c for history. | ||
14 | * Martin Mares : TOS setting fixed. | ||
15 | * Alan Cox : Fixed a couple of oopses in Martin's | ||
16 | * TOS tweaks. | ||
17 | * Mike McLagan : Routing by source | ||
18 | */ | ||
19 | |||
20 | #include <linux/config.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/mm.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/skbuff.h> | ||
26 | #include <linux/ip.h> | ||
27 | #include <linux/icmp.h> | ||
28 | #include <linux/netdevice.h> | ||
29 | #include <net/sock.h> | ||
30 | #include <net/ip.h> | ||
31 | #include <net/icmp.h> | ||
32 | #include <net/tcp.h> | ||
33 | #include <linux/tcp.h> | ||
34 | #include <linux/udp.h> | ||
35 | #include <linux/igmp.h> | ||
36 | #include <linux/netfilter.h> | ||
37 | #include <linux/route.h> | ||
38 | #include <linux/mroute.h> | ||
39 | #include <net/route.h> | ||
40 | #include <net/xfrm.h> | ||
41 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
42 | #include <net/transp_v6.h> | ||
43 | #endif | ||
44 | |||
45 | #include <linux/errqueue.h> | ||
46 | #include <asm/uaccess.h> | ||
47 | |||
48 | #define IP_CMSG_PKTINFO 1 | ||
49 | #define IP_CMSG_TTL 2 | ||
50 | #define IP_CMSG_TOS 4 | ||
51 | #define IP_CMSG_RECVOPTS 8 | ||
52 | #define IP_CMSG_RETOPTS 16 | ||
53 | |||
54 | /* | ||
55 | * SOL_IP control messages. | ||
56 | */ | ||
57 | |||
58 | static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) | ||
59 | { | ||
60 | struct in_pktinfo info; | ||
61 | struct rtable *rt = (struct rtable *)skb->dst; | ||
62 | |||
63 | info.ipi_addr.s_addr = skb->nh.iph->daddr; | ||
64 | if (rt) { | ||
65 | info.ipi_ifindex = rt->rt_iif; | ||
66 | info.ipi_spec_dst.s_addr = rt->rt_spec_dst; | ||
67 | } else { | ||
68 | info.ipi_ifindex = 0; | ||
69 | info.ipi_spec_dst.s_addr = 0; | ||
70 | } | ||
71 | |||
72 | put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); | ||
73 | } | ||
74 | |||
75 | static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb) | ||
76 | { | ||
77 | int ttl = skb->nh.iph->ttl; | ||
78 | put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl); | ||
79 | } | ||
80 | |||
81 | static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb) | ||
82 | { | ||
83 | put_cmsg(msg, SOL_IP, IP_TOS, 1, &skb->nh.iph->tos); | ||
84 | } | ||
85 | |||
86 | static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb) | ||
87 | { | ||
88 | if (IPCB(skb)->opt.optlen == 0) | ||
89 | return; | ||
90 | |||
91 | put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1); | ||
92 | } | ||
93 | |||
94 | |||
95 | static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb) | ||
96 | { | ||
97 | unsigned char optbuf[sizeof(struct ip_options) + 40]; | ||
98 | struct ip_options * opt = (struct ip_options*)optbuf; | ||
99 | |||
100 | if (IPCB(skb)->opt.optlen == 0) | ||
101 | return; | ||
102 | |||
103 | if (ip_options_echo(opt, skb)) { | ||
104 | msg->msg_flags |= MSG_CTRUNC; | ||
105 | return; | ||
106 | } | ||
107 | ip_options_undo(opt); | ||
108 | |||
109 | put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data); | ||
110 | } | ||
111 | |||
112 | |||
113 | void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) | ||
114 | { | ||
115 | struct inet_sock *inet = inet_sk(skb->sk); | ||
116 | unsigned flags = inet->cmsg_flags; | ||
117 | |||
118 | /* Ordered by supposed usage frequency */ | ||
119 | if (flags & 1) | ||
120 | ip_cmsg_recv_pktinfo(msg, skb); | ||
121 | if ((flags>>=1) == 0) | ||
122 | return; | ||
123 | |||
124 | if (flags & 1) | ||
125 | ip_cmsg_recv_ttl(msg, skb); | ||
126 | if ((flags>>=1) == 0) | ||
127 | return; | ||
128 | |||
129 | if (flags & 1) | ||
130 | ip_cmsg_recv_tos(msg, skb); | ||
131 | if ((flags>>=1) == 0) | ||
132 | return; | ||
133 | |||
134 | if (flags & 1) | ||
135 | ip_cmsg_recv_opts(msg, skb); | ||
136 | if ((flags>>=1) == 0) | ||
137 | return; | ||
138 | |||
139 | if (flags & 1) | ||
140 | ip_cmsg_recv_retopts(msg, skb); | ||
141 | } | ||
142 | |||
143 | int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc) | ||
144 | { | ||
145 | int err; | ||
146 | struct cmsghdr *cmsg; | ||
147 | |||
148 | for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { | ||
149 | if (!CMSG_OK(msg, cmsg)) | ||
150 | return -EINVAL; | ||
151 | if (cmsg->cmsg_level != SOL_IP) | ||
152 | continue; | ||
153 | switch (cmsg->cmsg_type) { | ||
154 | case IP_RETOPTS: | ||
155 | err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)); | ||
156 | err = ip_options_get(&ipc->opt, CMSG_DATA(cmsg), err < 40 ? err : 40, 0); | ||
157 | if (err) | ||
158 | return err; | ||
159 | break; | ||
160 | case IP_PKTINFO: | ||
161 | { | ||
162 | struct in_pktinfo *info; | ||
163 | if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo))) | ||
164 | return -EINVAL; | ||
165 | info = (struct in_pktinfo *)CMSG_DATA(cmsg); | ||
166 | ipc->oif = info->ipi_ifindex; | ||
167 | ipc->addr = info->ipi_spec_dst.s_addr; | ||
168 | break; | ||
169 | } | ||
170 | default: | ||
171 | return -EINVAL; | ||
172 | } | ||
173 | } | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | |||
178 | /* Special input handler for packets caught by router alert option. | ||
179 | They are selected only by protocol field, and then processed likely | ||
180 | local ones; but only if someone wants them! Otherwise, router | ||
181 | not running rsvpd will kill RSVP. | ||
182 | |||
183 | It is user level problem, what it will make with them. | ||
184 | I have no idea, how it will masquearde or NAT them (it is joke, joke :-)), | ||
185 | but receiver should be enough clever f.e. to forward mtrace requests, | ||
186 | sent to multicast group to reach destination designated router. | ||
187 | */ | ||
188 | struct ip_ra_chain *ip_ra_chain; | ||
189 | DEFINE_RWLOCK(ip_ra_lock); | ||
190 | |||
191 | int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *)) | ||
192 | { | ||
193 | struct ip_ra_chain *ra, *new_ra, **rap; | ||
194 | |||
195 | if (sk->sk_type != SOCK_RAW || inet_sk(sk)->num == IPPROTO_RAW) | ||
196 | return -EINVAL; | ||
197 | |||
198 | new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; | ||
199 | |||
200 | write_lock_bh(&ip_ra_lock); | ||
201 | for (rap = &ip_ra_chain; (ra=*rap) != NULL; rap = &ra->next) { | ||
202 | if (ra->sk == sk) { | ||
203 | if (on) { | ||
204 | write_unlock_bh(&ip_ra_lock); | ||
205 | if (new_ra) | ||
206 | kfree(new_ra); | ||
207 | return -EADDRINUSE; | ||
208 | } | ||
209 | *rap = ra->next; | ||
210 | write_unlock_bh(&ip_ra_lock); | ||
211 | |||
212 | if (ra->destructor) | ||
213 | ra->destructor(sk); | ||
214 | sock_put(sk); | ||
215 | kfree(ra); | ||
216 | return 0; | ||
217 | } | ||
218 | } | ||
219 | if (new_ra == NULL) { | ||
220 | write_unlock_bh(&ip_ra_lock); | ||
221 | return -ENOBUFS; | ||
222 | } | ||
223 | new_ra->sk = sk; | ||
224 | new_ra->destructor = destructor; | ||
225 | |||
226 | new_ra->next = ra; | ||
227 | *rap = new_ra; | ||
228 | sock_hold(sk); | ||
229 | write_unlock_bh(&ip_ra_lock); | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, | ||
235 | u16 port, u32 info, u8 *payload) | ||
236 | { | ||
237 | struct inet_sock *inet = inet_sk(sk); | ||
238 | struct sock_exterr_skb *serr; | ||
239 | |||
240 | if (!inet->recverr) | ||
241 | return; | ||
242 | |||
243 | skb = skb_clone(skb, GFP_ATOMIC); | ||
244 | if (!skb) | ||
245 | return; | ||
246 | |||
247 | serr = SKB_EXT_ERR(skb); | ||
248 | serr->ee.ee_errno = err; | ||
249 | serr->ee.ee_origin = SO_EE_ORIGIN_ICMP; | ||
250 | serr->ee.ee_type = skb->h.icmph->type; | ||
251 | serr->ee.ee_code = skb->h.icmph->code; | ||
252 | serr->ee.ee_pad = 0; | ||
253 | serr->ee.ee_info = info; | ||
254 | serr->ee.ee_data = 0; | ||
255 | serr->addr_offset = (u8*)&(((struct iphdr*)(skb->h.icmph+1))->daddr) - skb->nh.raw; | ||
256 | serr->port = port; | ||
257 | |||
258 | skb->h.raw = payload; | ||
259 | if (!skb_pull(skb, payload - skb->data) || | ||
260 | sock_queue_err_skb(sk, skb)) | ||
261 | kfree_skb(skb); | ||
262 | } | ||
263 | |||
264 | void ip_local_error(struct sock *sk, int err, u32 daddr, u16 port, u32 info) | ||
265 | { | ||
266 | struct inet_sock *inet = inet_sk(sk); | ||
267 | struct sock_exterr_skb *serr; | ||
268 | struct iphdr *iph; | ||
269 | struct sk_buff *skb; | ||
270 | |||
271 | if (!inet->recverr) | ||
272 | return; | ||
273 | |||
274 | skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC); | ||
275 | if (!skb) | ||
276 | return; | ||
277 | |||
278 | iph = (struct iphdr*)skb_put(skb, sizeof(struct iphdr)); | ||
279 | skb->nh.iph = iph; | ||
280 | iph->daddr = daddr; | ||
281 | |||
282 | serr = SKB_EXT_ERR(skb); | ||
283 | serr->ee.ee_errno = err; | ||
284 | serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL; | ||
285 | serr->ee.ee_type = 0; | ||
286 | serr->ee.ee_code = 0; | ||
287 | serr->ee.ee_pad = 0; | ||
288 | serr->ee.ee_info = info; | ||
289 | serr->ee.ee_data = 0; | ||
290 | serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw; | ||
291 | serr->port = port; | ||
292 | |||
293 | skb->h.raw = skb->tail; | ||
294 | __skb_pull(skb, skb->tail - skb->data); | ||
295 | |||
296 | if (sock_queue_err_skb(sk, skb)) | ||
297 | kfree_skb(skb); | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * Handle MSG_ERRQUEUE | ||
302 | */ | ||
303 | int ip_recv_error(struct sock *sk, struct msghdr *msg, int len) | ||
304 | { | ||
305 | struct sock_exterr_skb *serr; | ||
306 | struct sk_buff *skb, *skb2; | ||
307 | struct sockaddr_in *sin; | ||
308 | struct { | ||
309 | struct sock_extended_err ee; | ||
310 | struct sockaddr_in offender; | ||
311 | } errhdr; | ||
312 | int err; | ||
313 | int copied; | ||
314 | |||
315 | err = -EAGAIN; | ||
316 | skb = skb_dequeue(&sk->sk_error_queue); | ||
317 | if (skb == NULL) | ||
318 | goto out; | ||
319 | |||
320 | copied = skb->len; | ||
321 | if (copied > len) { | ||
322 | msg->msg_flags |= MSG_TRUNC; | ||
323 | copied = len; | ||
324 | } | ||
325 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); | ||
326 | if (err) | ||
327 | goto out_free_skb; | ||
328 | |||
329 | sock_recv_timestamp(msg, sk, skb); | ||
330 | |||
331 | serr = SKB_EXT_ERR(skb); | ||
332 | |||
333 | sin = (struct sockaddr_in *)msg->msg_name; | ||
334 | if (sin) { | ||
335 | sin->sin_family = AF_INET; | ||
336 | sin->sin_addr.s_addr = *(u32*)(skb->nh.raw + serr->addr_offset); | ||
337 | sin->sin_port = serr->port; | ||
338 | memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); | ||
339 | } | ||
340 | |||
341 | memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); | ||
342 | sin = &errhdr.offender; | ||
343 | sin->sin_family = AF_UNSPEC; | ||
344 | if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) { | ||
345 | struct inet_sock *inet = inet_sk(sk); | ||
346 | |||
347 | sin->sin_family = AF_INET; | ||
348 | sin->sin_addr.s_addr = skb->nh.iph->saddr; | ||
349 | sin->sin_port = 0; | ||
350 | memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); | ||
351 | if (inet->cmsg_flags) | ||
352 | ip_cmsg_recv(msg, skb); | ||
353 | } | ||
354 | |||
355 | put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr); | ||
356 | |||
357 | /* Now we could try to dump offended packet options */ | ||
358 | |||
359 | msg->msg_flags |= MSG_ERRQUEUE; | ||
360 | err = copied; | ||
361 | |||
362 | /* Reset and regenerate socket error */ | ||
363 | spin_lock_irq(&sk->sk_error_queue.lock); | ||
364 | sk->sk_err = 0; | ||
365 | if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) { | ||
366 | sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno; | ||
367 | spin_unlock_irq(&sk->sk_error_queue.lock); | ||
368 | sk->sk_error_report(sk); | ||
369 | } else | ||
370 | spin_unlock_irq(&sk->sk_error_queue.lock); | ||
371 | |||
372 | out_free_skb: | ||
373 | kfree_skb(skb); | ||
374 | out: | ||
375 | return err; | ||
376 | } | ||
377 | |||
378 | |||
379 | /* | ||
380 | * Socket option code for IP. This is the end of the line after any TCP,UDP etc options on | ||
381 | * an IP socket. | ||
382 | */ | ||
383 | |||
384 | int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen) | ||
385 | { | ||
386 | struct inet_sock *inet = inet_sk(sk); | ||
387 | int val=0,err; | ||
388 | |||
389 | if (level != SOL_IP) | ||
390 | return -ENOPROTOOPT; | ||
391 | |||
392 | if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) | | ||
393 | (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) | | ||
394 | (1<<IP_RETOPTS) | (1<<IP_TOS) | | ||
395 | (1<<IP_TTL) | (1<<IP_HDRINCL) | | ||
396 | (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | | ||
397 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND))) || | ||
398 | optname == IP_MULTICAST_TTL || | ||
399 | optname == IP_MULTICAST_LOOP) { | ||
400 | if (optlen >= sizeof(int)) { | ||
401 | if (get_user(val, (int __user *) optval)) | ||
402 | return -EFAULT; | ||
403 | } else if (optlen >= sizeof(char)) { | ||
404 | unsigned char ucval; | ||
405 | |||
406 | if (get_user(ucval, (unsigned char __user *) optval)) | ||
407 | return -EFAULT; | ||
408 | val = (int) ucval; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | /* If optlen==0, it is equivalent to val == 0 */ | ||
413 | |||
414 | #ifdef CONFIG_IP_MROUTE | ||
415 | if (optname >= MRT_BASE && optname <= (MRT_BASE + 10)) | ||
416 | return ip_mroute_setsockopt(sk,optname,optval,optlen); | ||
417 | #endif | ||
418 | |||
419 | err = 0; | ||
420 | lock_sock(sk); | ||
421 | |||
422 | switch (optname) { | ||
423 | case IP_OPTIONS: | ||
424 | { | ||
425 | struct ip_options * opt = NULL; | ||
426 | if (optlen > 40 || optlen < 0) | ||
427 | goto e_inval; | ||
428 | err = ip_options_get(&opt, optval, optlen, 1); | ||
429 | if (err) | ||
430 | break; | ||
431 | if (sk->sk_type == SOCK_STREAM) { | ||
432 | struct tcp_sock *tp = tcp_sk(sk); | ||
433 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
434 | if (sk->sk_family == PF_INET || | ||
435 | (!((1 << sk->sk_state) & | ||
436 | (TCPF_LISTEN | TCPF_CLOSE)) && | ||
437 | inet->daddr != LOOPBACK4_IPV6)) { | ||
438 | #endif | ||
439 | if (inet->opt) | ||
440 | tp->ext_header_len -= inet->opt->optlen; | ||
441 | if (opt) | ||
442 | tp->ext_header_len += opt->optlen; | ||
443 | tcp_sync_mss(sk, tp->pmtu_cookie); | ||
444 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
445 | } | ||
446 | #endif | ||
447 | } | ||
448 | opt = xchg(&inet->opt, opt); | ||
449 | if (opt) | ||
450 | kfree(opt); | ||
451 | break; | ||
452 | } | ||
453 | case IP_PKTINFO: | ||
454 | if (val) | ||
455 | inet->cmsg_flags |= IP_CMSG_PKTINFO; | ||
456 | else | ||
457 | inet->cmsg_flags &= ~IP_CMSG_PKTINFO; | ||
458 | break; | ||
459 | case IP_RECVTTL: | ||
460 | if (val) | ||
461 | inet->cmsg_flags |= IP_CMSG_TTL; | ||
462 | else | ||
463 | inet->cmsg_flags &= ~IP_CMSG_TTL; | ||
464 | break; | ||
465 | case IP_RECVTOS: | ||
466 | if (val) | ||
467 | inet->cmsg_flags |= IP_CMSG_TOS; | ||
468 | else | ||
469 | inet->cmsg_flags &= ~IP_CMSG_TOS; | ||
470 | break; | ||
471 | case IP_RECVOPTS: | ||
472 | if (val) | ||
473 | inet->cmsg_flags |= IP_CMSG_RECVOPTS; | ||
474 | else | ||
475 | inet->cmsg_flags &= ~IP_CMSG_RECVOPTS; | ||
476 | break; | ||
477 | case IP_RETOPTS: | ||
478 | if (val) | ||
479 | inet->cmsg_flags |= IP_CMSG_RETOPTS; | ||
480 | else | ||
481 | inet->cmsg_flags &= ~IP_CMSG_RETOPTS; | ||
482 | break; | ||
483 | case IP_TOS: /* This sets both TOS and Precedence */ | ||
484 | if (sk->sk_type == SOCK_STREAM) { | ||
485 | val &= ~3; | ||
486 | val |= inet->tos & 3; | ||
487 | } | ||
488 | if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && | ||
489 | !capable(CAP_NET_ADMIN)) { | ||
490 | err = -EPERM; | ||
491 | break; | ||
492 | } | ||
493 | if (inet->tos != val) { | ||
494 | inet->tos = val; | ||
495 | sk->sk_priority = rt_tos2priority(val); | ||
496 | sk_dst_reset(sk); | ||
497 | } | ||
498 | break; | ||
499 | case IP_TTL: | ||
500 | if (optlen<1) | ||
501 | goto e_inval; | ||
502 | if (val != -1 && (val < 1 || val>255)) | ||
503 | goto e_inval; | ||
504 | inet->uc_ttl = val; | ||
505 | break; | ||
506 | case IP_HDRINCL: | ||
507 | if (sk->sk_type != SOCK_RAW) { | ||
508 | err = -ENOPROTOOPT; | ||
509 | break; | ||
510 | } | ||
511 | inet->hdrincl = val ? 1 : 0; | ||
512 | break; | ||
513 | case IP_MTU_DISCOVER: | ||
514 | if (val<0 || val>2) | ||
515 | goto e_inval; | ||
516 | inet->pmtudisc = val; | ||
517 | break; | ||
518 | case IP_RECVERR: | ||
519 | inet->recverr = !!val; | ||
520 | if (!val) | ||
521 | skb_queue_purge(&sk->sk_error_queue); | ||
522 | break; | ||
523 | case IP_MULTICAST_TTL: | ||
524 | if (sk->sk_type == SOCK_STREAM) | ||
525 | goto e_inval; | ||
526 | if (optlen<1) | ||
527 | goto e_inval; | ||
528 | if (val==-1) | ||
529 | val = 1; | ||
530 | if (val < 0 || val > 255) | ||
531 | goto e_inval; | ||
532 | inet->mc_ttl = val; | ||
533 | break; | ||
534 | case IP_MULTICAST_LOOP: | ||
535 | if (optlen<1) | ||
536 | goto e_inval; | ||
537 | inet->mc_loop = !!val; | ||
538 | break; | ||
539 | case IP_MULTICAST_IF: | ||
540 | { | ||
541 | struct ip_mreqn mreq; | ||
542 | struct net_device *dev = NULL; | ||
543 | |||
544 | if (sk->sk_type == SOCK_STREAM) | ||
545 | goto e_inval; | ||
546 | /* | ||
547 | * Check the arguments are allowable | ||
548 | */ | ||
549 | |||
550 | err = -EFAULT; | ||
551 | if (optlen >= sizeof(struct ip_mreqn)) { | ||
552 | if (copy_from_user(&mreq,optval,sizeof(mreq))) | ||
553 | break; | ||
554 | } else { | ||
555 | memset(&mreq, 0, sizeof(mreq)); | ||
556 | if (optlen >= sizeof(struct in_addr) && | ||
557 | copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr))) | ||
558 | break; | ||
559 | } | ||
560 | |||
561 | if (!mreq.imr_ifindex) { | ||
562 | if (mreq.imr_address.s_addr == INADDR_ANY) { | ||
563 | inet->mc_index = 0; | ||
564 | inet->mc_addr = 0; | ||
565 | err = 0; | ||
566 | break; | ||
567 | } | ||
568 | dev = ip_dev_find(mreq.imr_address.s_addr); | ||
569 | if (dev) { | ||
570 | mreq.imr_ifindex = dev->ifindex; | ||
571 | dev_put(dev); | ||
572 | } | ||
573 | } else | ||
574 | dev = __dev_get_by_index(mreq.imr_ifindex); | ||
575 | |||
576 | |||
577 | err = -EADDRNOTAVAIL; | ||
578 | if (!dev) | ||
579 | break; | ||
580 | |||
581 | err = -EINVAL; | ||
582 | if (sk->sk_bound_dev_if && | ||
583 | mreq.imr_ifindex != sk->sk_bound_dev_if) | ||
584 | break; | ||
585 | |||
586 | inet->mc_index = mreq.imr_ifindex; | ||
587 | inet->mc_addr = mreq.imr_address.s_addr; | ||
588 | err = 0; | ||
589 | break; | ||
590 | } | ||
591 | |||
592 | case IP_ADD_MEMBERSHIP: | ||
593 | case IP_DROP_MEMBERSHIP: | ||
594 | { | ||
595 | struct ip_mreqn mreq; | ||
596 | |||
597 | if (optlen < sizeof(struct ip_mreq)) | ||
598 | goto e_inval; | ||
599 | err = -EFAULT; | ||
600 | if (optlen >= sizeof(struct ip_mreqn)) { | ||
601 | if(copy_from_user(&mreq,optval,sizeof(mreq))) | ||
602 | break; | ||
603 | } else { | ||
604 | memset(&mreq, 0, sizeof(mreq)); | ||
605 | if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq))) | ||
606 | break; | ||
607 | } | ||
608 | |||
609 | if (optname == IP_ADD_MEMBERSHIP) | ||
610 | err = ip_mc_join_group(sk, &mreq); | ||
611 | else | ||
612 | err = ip_mc_leave_group(sk, &mreq); | ||
613 | break; | ||
614 | } | ||
615 | case IP_MSFILTER: | ||
616 | { | ||
617 | extern int sysctl_optmem_max; | ||
618 | extern int sysctl_igmp_max_msf; | ||
619 | struct ip_msfilter *msf; | ||
620 | |||
621 | if (optlen < IP_MSFILTER_SIZE(0)) | ||
622 | goto e_inval; | ||
623 | if (optlen > sysctl_optmem_max) { | ||
624 | err = -ENOBUFS; | ||
625 | break; | ||
626 | } | ||
627 | msf = (struct ip_msfilter *)kmalloc(optlen, GFP_KERNEL); | ||
628 | if (msf == 0) { | ||
629 | err = -ENOBUFS; | ||
630 | break; | ||
631 | } | ||
632 | err = -EFAULT; | ||
633 | if (copy_from_user(msf, optval, optlen)) { | ||
634 | kfree(msf); | ||
635 | break; | ||
636 | } | ||
637 | /* numsrc >= (1G-4) overflow in 32 bits */ | ||
638 | if (msf->imsf_numsrc >= 0x3ffffffcU || | ||
639 | msf->imsf_numsrc > sysctl_igmp_max_msf) { | ||
640 | kfree(msf); | ||
641 | err = -ENOBUFS; | ||
642 | break; | ||
643 | } | ||
644 | if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { | ||
645 | kfree(msf); | ||
646 | err = -EINVAL; | ||
647 | break; | ||
648 | } | ||
649 | err = ip_mc_msfilter(sk, msf, 0); | ||
650 | kfree(msf); | ||
651 | break; | ||
652 | } | ||
653 | case IP_BLOCK_SOURCE: | ||
654 | case IP_UNBLOCK_SOURCE: | ||
655 | case IP_ADD_SOURCE_MEMBERSHIP: | ||
656 | case IP_DROP_SOURCE_MEMBERSHIP: | ||
657 | { | ||
658 | struct ip_mreq_source mreqs; | ||
659 | int omode, add; | ||
660 | |||
661 | if (optlen != sizeof(struct ip_mreq_source)) | ||
662 | goto e_inval; | ||
663 | if (copy_from_user(&mreqs, optval, sizeof(mreqs))) { | ||
664 | err = -EFAULT; | ||
665 | break; | ||
666 | } | ||
667 | if (optname == IP_BLOCK_SOURCE) { | ||
668 | omode = MCAST_EXCLUDE; | ||
669 | add = 1; | ||
670 | } else if (optname == IP_UNBLOCK_SOURCE) { | ||
671 | omode = MCAST_EXCLUDE; | ||
672 | add = 0; | ||
673 | } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) { | ||
674 | struct ip_mreqn mreq; | ||
675 | |||
676 | mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr; | ||
677 | mreq.imr_address.s_addr = mreqs.imr_interface; | ||
678 | mreq.imr_ifindex = 0; | ||
679 | err = ip_mc_join_group(sk, &mreq); | ||
680 | if (err) | ||
681 | break; | ||
682 | omode = MCAST_INCLUDE; | ||
683 | add = 1; | ||
684 | } else /*IP_DROP_SOURCE_MEMBERSHIP */ { | ||
685 | omode = MCAST_INCLUDE; | ||
686 | add = 0; | ||
687 | } | ||
688 | err = ip_mc_source(add, omode, sk, &mreqs, 0); | ||
689 | break; | ||
690 | } | ||
691 | case MCAST_JOIN_GROUP: | ||
692 | case MCAST_LEAVE_GROUP: | ||
693 | { | ||
694 | struct group_req greq; | ||
695 | struct sockaddr_in *psin; | ||
696 | struct ip_mreqn mreq; | ||
697 | |||
698 | if (optlen < sizeof(struct group_req)) | ||
699 | goto e_inval; | ||
700 | err = -EFAULT; | ||
701 | if(copy_from_user(&greq, optval, sizeof(greq))) | ||
702 | break; | ||
703 | psin = (struct sockaddr_in *)&greq.gr_group; | ||
704 | if (psin->sin_family != AF_INET) | ||
705 | goto e_inval; | ||
706 | memset(&mreq, 0, sizeof(mreq)); | ||
707 | mreq.imr_multiaddr = psin->sin_addr; | ||
708 | mreq.imr_ifindex = greq.gr_interface; | ||
709 | |||
710 | if (optname == MCAST_JOIN_GROUP) | ||
711 | err = ip_mc_join_group(sk, &mreq); | ||
712 | else | ||
713 | err = ip_mc_leave_group(sk, &mreq); | ||
714 | break; | ||
715 | } | ||
716 | case MCAST_JOIN_SOURCE_GROUP: | ||
717 | case MCAST_LEAVE_SOURCE_GROUP: | ||
718 | case MCAST_BLOCK_SOURCE: | ||
719 | case MCAST_UNBLOCK_SOURCE: | ||
720 | { | ||
721 | struct group_source_req greqs; | ||
722 | struct ip_mreq_source mreqs; | ||
723 | struct sockaddr_in *psin; | ||
724 | int omode, add; | ||
725 | |||
726 | if (optlen != sizeof(struct group_source_req)) | ||
727 | goto e_inval; | ||
728 | if (copy_from_user(&greqs, optval, sizeof(greqs))) { | ||
729 | err = -EFAULT; | ||
730 | break; | ||
731 | } | ||
732 | if (greqs.gsr_group.ss_family != AF_INET || | ||
733 | greqs.gsr_source.ss_family != AF_INET) { | ||
734 | err = -EADDRNOTAVAIL; | ||
735 | break; | ||
736 | } | ||
737 | psin = (struct sockaddr_in *)&greqs.gsr_group; | ||
738 | mreqs.imr_multiaddr = psin->sin_addr.s_addr; | ||
739 | psin = (struct sockaddr_in *)&greqs.gsr_source; | ||
740 | mreqs.imr_sourceaddr = psin->sin_addr.s_addr; | ||
741 | mreqs.imr_interface = 0; /* use index for mc_source */ | ||
742 | |||
743 | if (optname == MCAST_BLOCK_SOURCE) { | ||
744 | omode = MCAST_EXCLUDE; | ||
745 | add = 1; | ||
746 | } else if (optname == MCAST_UNBLOCK_SOURCE) { | ||
747 | omode = MCAST_EXCLUDE; | ||
748 | add = 0; | ||
749 | } else if (optname == MCAST_JOIN_SOURCE_GROUP) { | ||
750 | struct ip_mreqn mreq; | ||
751 | |||
752 | psin = (struct sockaddr_in *)&greqs.gsr_group; | ||
753 | mreq.imr_multiaddr = psin->sin_addr; | ||
754 | mreq.imr_address.s_addr = 0; | ||
755 | mreq.imr_ifindex = greqs.gsr_interface; | ||
756 | err = ip_mc_join_group(sk, &mreq); | ||
757 | if (err) | ||
758 | break; | ||
759 | greqs.gsr_interface = mreq.imr_ifindex; | ||
760 | omode = MCAST_INCLUDE; | ||
761 | add = 1; | ||
762 | } else /* MCAST_LEAVE_SOURCE_GROUP */ { | ||
763 | omode = MCAST_INCLUDE; | ||
764 | add = 0; | ||
765 | } | ||
766 | err = ip_mc_source(add, omode, sk, &mreqs, | ||
767 | greqs.gsr_interface); | ||
768 | break; | ||
769 | } | ||
770 | case MCAST_MSFILTER: | ||
771 | { | ||
772 | extern int sysctl_optmem_max; | ||
773 | extern int sysctl_igmp_max_msf; | ||
774 | struct sockaddr_in *psin; | ||
775 | struct ip_msfilter *msf = NULL; | ||
776 | struct group_filter *gsf = NULL; | ||
777 | int msize, i, ifindex; | ||
778 | |||
779 | if (optlen < GROUP_FILTER_SIZE(0)) | ||
780 | goto e_inval; | ||
781 | if (optlen > sysctl_optmem_max) { | ||
782 | err = -ENOBUFS; | ||
783 | break; | ||
784 | } | ||
785 | gsf = (struct group_filter *)kmalloc(optlen,GFP_KERNEL); | ||
786 | if (gsf == 0) { | ||
787 | err = -ENOBUFS; | ||
788 | break; | ||
789 | } | ||
790 | err = -EFAULT; | ||
791 | if (copy_from_user(gsf, optval, optlen)) { | ||
792 | goto mc_msf_out; | ||
793 | } | ||
794 | /* numsrc >= (4G-140)/128 overflow in 32 bits */ | ||
795 | if (gsf->gf_numsrc >= 0x1ffffff || | ||
796 | gsf->gf_numsrc > sysctl_igmp_max_msf) { | ||
797 | err = -ENOBUFS; | ||
798 | goto mc_msf_out; | ||
799 | } | ||
800 | if (GROUP_FILTER_SIZE(gsf->gf_numsrc) > optlen) { | ||
801 | err = -EINVAL; | ||
802 | goto mc_msf_out; | ||
803 | } | ||
804 | msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); | ||
805 | msf = (struct ip_msfilter *)kmalloc(msize,GFP_KERNEL); | ||
806 | if (msf == 0) { | ||
807 | err = -ENOBUFS; | ||
808 | goto mc_msf_out; | ||
809 | } | ||
810 | ifindex = gsf->gf_interface; | ||
811 | psin = (struct sockaddr_in *)&gsf->gf_group; | ||
812 | if (psin->sin_family != AF_INET) { | ||
813 | err = -EADDRNOTAVAIL; | ||
814 | goto mc_msf_out; | ||
815 | } | ||
816 | msf->imsf_multiaddr = psin->sin_addr.s_addr; | ||
817 | msf->imsf_interface = 0; | ||
818 | msf->imsf_fmode = gsf->gf_fmode; | ||
819 | msf->imsf_numsrc = gsf->gf_numsrc; | ||
820 | err = -EADDRNOTAVAIL; | ||
821 | for (i=0; i<gsf->gf_numsrc; ++i) { | ||
822 | psin = (struct sockaddr_in *)&gsf->gf_slist[i]; | ||
823 | |||
824 | if (psin->sin_family != AF_INET) | ||
825 | goto mc_msf_out; | ||
826 | msf->imsf_slist[i] = psin->sin_addr.s_addr; | ||
827 | } | ||
828 | kfree(gsf); | ||
829 | gsf = NULL; | ||
830 | |||
831 | err = ip_mc_msfilter(sk, msf, ifindex); | ||
832 | mc_msf_out: | ||
833 | if (msf) | ||
834 | kfree(msf); | ||
835 | if (gsf) | ||
836 | kfree(gsf); | ||
837 | break; | ||
838 | } | ||
839 | case IP_ROUTER_ALERT: | ||
840 | err = ip_ra_control(sk, val ? 1 : 0, NULL); | ||
841 | break; | ||
842 | |||
843 | case IP_FREEBIND: | ||
844 | if (optlen<1) | ||
845 | goto e_inval; | ||
846 | inet->freebind = !!val; | ||
847 | break; | ||
848 | |||
849 | case IP_IPSEC_POLICY: | ||
850 | case IP_XFRM_POLICY: | ||
851 | err = xfrm_user_policy(sk, optname, optval, optlen); | ||
852 | break; | ||
853 | |||
854 | default: | ||
855 | #ifdef CONFIG_NETFILTER | ||
856 | err = nf_setsockopt(sk, PF_INET, optname, optval, | ||
857 | optlen); | ||
858 | #else | ||
859 | err = -ENOPROTOOPT; | ||
860 | #endif | ||
861 | break; | ||
862 | } | ||
863 | release_sock(sk); | ||
864 | return err; | ||
865 | |||
866 | e_inval: | ||
867 | release_sock(sk); | ||
868 | return -EINVAL; | ||
869 | } | ||
870 | |||
871 | /* | ||
872 | * Get the options. Note for future reference. The GET of IP options gets the | ||
873 | * _received_ ones. The set sets the _sent_ ones. | ||
874 | */ | ||
875 | |||
876 | int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) | ||
877 | { | ||
878 | struct inet_sock *inet = inet_sk(sk); | ||
879 | int val; | ||
880 | int len; | ||
881 | |||
882 | if(level!=SOL_IP) | ||
883 | return -EOPNOTSUPP; | ||
884 | |||
885 | #ifdef CONFIG_IP_MROUTE | ||
886 | if(optname>=MRT_BASE && optname <=MRT_BASE+10) | ||
887 | { | ||
888 | return ip_mroute_getsockopt(sk,optname,optval,optlen); | ||
889 | } | ||
890 | #endif | ||
891 | |||
892 | if(get_user(len,optlen)) | ||
893 | return -EFAULT; | ||
894 | if(len < 0) | ||
895 | return -EINVAL; | ||
896 | |||
897 | lock_sock(sk); | ||
898 | |||
899 | switch(optname) { | ||
900 | case IP_OPTIONS: | ||
901 | { | ||
902 | unsigned char optbuf[sizeof(struct ip_options)+40]; | ||
903 | struct ip_options * opt = (struct ip_options*)optbuf; | ||
904 | opt->optlen = 0; | ||
905 | if (inet->opt) | ||
906 | memcpy(optbuf, inet->opt, | ||
907 | sizeof(struct ip_options)+ | ||
908 | inet->opt->optlen); | ||
909 | release_sock(sk); | ||
910 | |||
911 | if (opt->optlen == 0) | ||
912 | return put_user(0, optlen); | ||
913 | |||
914 | ip_options_undo(opt); | ||
915 | |||
916 | len = min_t(unsigned int, len, opt->optlen); | ||
917 | if(put_user(len, optlen)) | ||
918 | return -EFAULT; | ||
919 | if(copy_to_user(optval, opt->__data, len)) | ||
920 | return -EFAULT; | ||
921 | return 0; | ||
922 | } | ||
923 | case IP_PKTINFO: | ||
924 | val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; | ||
925 | break; | ||
926 | case IP_RECVTTL: | ||
927 | val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; | ||
928 | break; | ||
929 | case IP_RECVTOS: | ||
930 | val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; | ||
931 | break; | ||
932 | case IP_RECVOPTS: | ||
933 | val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; | ||
934 | break; | ||
935 | case IP_RETOPTS: | ||
936 | val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; | ||
937 | break; | ||
938 | case IP_TOS: | ||
939 | val = inet->tos; | ||
940 | break; | ||
941 | case IP_TTL: | ||
942 | val = (inet->uc_ttl == -1 ? | ||
943 | sysctl_ip_default_ttl : | ||
944 | inet->uc_ttl); | ||
945 | break; | ||
946 | case IP_HDRINCL: | ||
947 | val = inet->hdrincl; | ||
948 | break; | ||
949 | case IP_MTU_DISCOVER: | ||
950 | val = inet->pmtudisc; | ||
951 | break; | ||
952 | case IP_MTU: | ||
953 | { | ||
954 | struct dst_entry *dst; | ||
955 | val = 0; | ||
956 | dst = sk_dst_get(sk); | ||
957 | if (dst) { | ||
958 | val = dst_mtu(dst); | ||
959 | dst_release(dst); | ||
960 | } | ||
961 | if (!val) { | ||
962 | release_sock(sk); | ||
963 | return -ENOTCONN; | ||
964 | } | ||
965 | break; | ||
966 | } | ||
967 | case IP_RECVERR: | ||
968 | val = inet->recverr; | ||
969 | break; | ||
970 | case IP_MULTICAST_TTL: | ||
971 | val = inet->mc_ttl; | ||
972 | break; | ||
973 | case IP_MULTICAST_LOOP: | ||
974 | val = inet->mc_loop; | ||
975 | break; | ||
976 | case IP_MULTICAST_IF: | ||
977 | { | ||
978 | struct in_addr addr; | ||
979 | len = min_t(unsigned int, len, sizeof(struct in_addr)); | ||
980 | addr.s_addr = inet->mc_addr; | ||
981 | release_sock(sk); | ||
982 | |||
983 | if(put_user(len, optlen)) | ||
984 | return -EFAULT; | ||
985 | if(copy_to_user(optval, &addr, len)) | ||
986 | return -EFAULT; | ||
987 | return 0; | ||
988 | } | ||
989 | case IP_MSFILTER: | ||
990 | { | ||
991 | struct ip_msfilter msf; | ||
992 | int err; | ||
993 | |||
994 | if (len < IP_MSFILTER_SIZE(0)) { | ||
995 | release_sock(sk); | ||
996 | return -EINVAL; | ||
997 | } | ||
998 | if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { | ||
999 | release_sock(sk); | ||
1000 | return -EFAULT; | ||
1001 | } | ||
1002 | err = ip_mc_msfget(sk, &msf, | ||
1003 | (struct ip_msfilter __user *)optval, optlen); | ||
1004 | release_sock(sk); | ||
1005 | return err; | ||
1006 | } | ||
1007 | case MCAST_MSFILTER: | ||
1008 | { | ||
1009 | struct group_filter gsf; | ||
1010 | int err; | ||
1011 | |||
1012 | if (len < GROUP_FILTER_SIZE(0)) { | ||
1013 | release_sock(sk); | ||
1014 | return -EINVAL; | ||
1015 | } | ||
1016 | if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { | ||
1017 | release_sock(sk); | ||
1018 | return -EFAULT; | ||
1019 | } | ||
1020 | err = ip_mc_gsfget(sk, &gsf, | ||
1021 | (struct group_filter __user *)optval, optlen); | ||
1022 | release_sock(sk); | ||
1023 | return err; | ||
1024 | } | ||
1025 | case IP_PKTOPTIONS: | ||
1026 | { | ||
1027 | struct msghdr msg; | ||
1028 | |||
1029 | release_sock(sk); | ||
1030 | |||
1031 | if (sk->sk_type != SOCK_STREAM) | ||
1032 | return -ENOPROTOOPT; | ||
1033 | |||
1034 | msg.msg_control = optval; | ||
1035 | msg.msg_controllen = len; | ||
1036 | msg.msg_flags = 0; | ||
1037 | |||
1038 | if (inet->cmsg_flags & IP_CMSG_PKTINFO) { | ||
1039 | struct in_pktinfo info; | ||
1040 | |||
1041 | info.ipi_addr.s_addr = inet->rcv_saddr; | ||
1042 | info.ipi_spec_dst.s_addr = inet->rcv_saddr; | ||
1043 | info.ipi_ifindex = inet->mc_index; | ||
1044 | put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); | ||
1045 | } | ||
1046 | if (inet->cmsg_flags & IP_CMSG_TTL) { | ||
1047 | int hlim = inet->mc_ttl; | ||
1048 | put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); | ||
1049 | } | ||
1050 | len -= msg.msg_controllen; | ||
1051 | return put_user(len, optlen); | ||
1052 | } | ||
1053 | case IP_FREEBIND: | ||
1054 | val = inet->freebind; | ||
1055 | break; | ||
1056 | default: | ||
1057 | #ifdef CONFIG_NETFILTER | ||
1058 | val = nf_getsockopt(sk, PF_INET, optname, optval, | ||
1059 | &len); | ||
1060 | release_sock(sk); | ||
1061 | if (val >= 0) | ||
1062 | val = put_user(len, optlen); | ||
1063 | return val; | ||
1064 | #else | ||
1065 | release_sock(sk); | ||
1066 | return -ENOPROTOOPT; | ||
1067 | #endif | ||
1068 | } | ||
1069 | release_sock(sk); | ||
1070 | |||
1071 | if (len < sizeof(int) && len > 0 && val>=0 && val<255) { | ||
1072 | unsigned char ucval = (unsigned char)val; | ||
1073 | len = 1; | ||
1074 | if(put_user(len, optlen)) | ||
1075 | return -EFAULT; | ||
1076 | if(copy_to_user(optval,&ucval,1)) | ||
1077 | return -EFAULT; | ||
1078 | } else { | ||
1079 | len = min_t(unsigned int, sizeof(int), len); | ||
1080 | if(put_user(len, optlen)) | ||
1081 | return -EFAULT; | ||
1082 | if(copy_to_user(optval,&val,len)) | ||
1083 | return -EFAULT; | ||
1084 | } | ||
1085 | return 0; | ||
1086 | } | ||
1087 | |||
1088 | EXPORT_SYMBOL(ip_cmsg_recv); | ||
1089 | |||
1090 | #ifdef CONFIG_IP_SCTP_MODULE | ||
1091 | EXPORT_SYMBOL(ip_getsockopt); | ||
1092 | EXPORT_SYMBOL(ip_setsockopt); | ||
1093 | #endif | ||