diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
commit | 19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch) | |
tree | 40b1cb534496a7f1ca0f5c314a523c69f1fee464 /net/ipv4/ip_sockglue.c | |
parent | 7206675c40394c78a90e74812bbdbf8cf3cca1be (diff) |
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'net/ipv4/ip_sockglue.c')
-rw-r--r-- | net/ipv4/ip_sockglue.c | 640 |
1 files changed, 396 insertions, 244 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 7337fc08c..1689159ed 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -36,102 +36,155 @@ #include <asm/uaccess.h> -#ifdef CONFIG_IP_MULTICAST - /* - * Write an multicast group list table for the IGMP daemon to - * read. + * SOL_IP control messages. */ - -int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dummy) + +static void ip_cmsg_recv_rxinfo(struct msghdr *msg, struct sk_buff *skb) { - off_t pos=0, begin=0; - struct ip_mc_list *im; - unsigned long flags; - int len=0; - struct device *dev; - - len=sprintf(buffer,"Device : Count\tGroup Users Timer\n"); - save_flags(flags); - cli(); - - for(dev = dev_base; dev; dev = dev->next) - { - if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)) - { - len+=sprintf(buffer+len,"%-10s: %5d\n", - dev->name, dev->mc_count); - for(im = dev->ip_mc_list; im; im = im->next) - { - len+=sprintf(buffer+len, - "\t\t\t%08lX %5d %d:%08lX\n", - im->multiaddr, im->users, - im->tm_running, im->timer.expires-jiffies); - pos=begin+len; - if(pos<offset) - { - len=0; - begin=pos; - } - if(pos>offset+length) - break; - } - } + struct in_pktinfo info; + struct rtable *rt = (struct rtable *)skb->dst; + + info.ipi_ifindex = skb->dev->ifindex; + info.ipi_addr.s_addr = skb->nh.iph->daddr; + info.ipi_spec_dst.s_addr = rt->rt_spec_dst; + + put_cmsg(msg, SOL_IP, IP_RXINFO, sizeof(info), &info); +} + +static void ip_cmsg_recv_localaddr(struct msghdr *msg, struct sk_buff *skb, int local) +{ + struct in_addr addr; + + addr.s_addr = skb->nh.iph->daddr; + + if (local) { + struct rtable *rt = (struct rtable *)skb->dst; + addr.s_addr = rt->rt_spec_dst; } - restore_flags(flags); - *start=buffer+(offset-begin); - len-=(offset-begin); - if(len>length) - len=length; - return len; + put_cmsg(msg, SOL_IP, local ? IP_LOCALADDR : IP_RECVDSTADDR, + sizeof(addr), &addr); } +static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb) +{ + if (IPCB(skb)->opt.optlen == 0) + return; + + put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1); +} -/* - * Socket option code for IP. This is the end of the line after any TCP,UDP etc options on - * an IP socket. - * - * We implement IP_TOS (type of service), IP_TTL (time to live). - */ -static struct device *ip_mc_find_devfor(unsigned long addr) +void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb) { - struct device *dev; - for(dev = dev_base; dev; dev = dev->next) - { - if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&& - (dev->pa_addr==addr)) - return dev; + unsigned char optbuf[sizeof(struct ip_options) + 40]; + struct ip_options * opt = (struct ip_options*)optbuf; + + if (IPCB(skb)->opt.optlen == 0) + return; + + if (ip_options_echo(opt, skb)) { + msg->msg_flags |= MSG_CTRUNC; + return; } + ip_options_undo(opt); - return NULL; + put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data); } -#endif + +void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) +{ + unsigned flags = skb->sk->ip_cmsg_flags; + + /* Ordered by supposed usage frequency */ + if (flags & 1) + ip_cmsg_recv_rxinfo(msg, skb); + if ((flags>>=1) == 0) + return; + if (flags & 1) + ip_cmsg_recv_localaddr(msg, skb, 1); + if ((flags>>=1) == 0) + return; + if (flags & 1) + ip_cmsg_recv_opts(msg, skb); + if ((flags>>=1) == 0) + return; + if (flags & 1) + ip_cmsg_recv_retopts(msg, skb); + if ((flags>>=1) == 0) + return; + if (flags & 1) + ip_cmsg_recv_localaddr(msg, skb, 0); +} + +int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc, struct device **devp) +{ + int err; + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level != SOL_IP) + continue; + switch (cmsg->cmsg_type) + { + case IP_LOCALADDR: + if (cmsg->cmsg_len < sizeof(struct in_addr)+sizeof(*cmsg)) + return -EINVAL; + memcpy(&ipc->addr, cmsg->cmsg_data, 4); + break; + case IP_RETOPTS: + err = cmsg->cmsg_len - sizeof(*cmsg); + err = ip_options_get(&ipc->opt, cmsg->cmsg_data, + err < 40 ? err : 40, 0); + if (err) + return err; + break; + case IP_TXINFO: + { + struct in_pktinfo *info; + if (cmsg->cmsg_len < sizeof(*info)+sizeof(*cmsg)) + return -EINVAL; + info = (struct in_pktinfo*)cmsg->cmsg_data; + if (info->ipi_ifindex && !devp) + return -EINVAL; + if ((*devp = dev_get_by_index(info->ipi_ifindex)) == NULL) + return -ENODEV; + ipc->addr = info->ipi_spec_dst.s_addr; + break; + } + default: + return -EINVAL; + } + } + return 0; +} + +/* + * Socket option code for IP. This is the end of the line after any TCP,UDP etc options on + * an IP socket. + * + * We implement IP_TOS (type of service), IP_TTL (time to live). + */ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) { - int val,err; - unsigned char ucval; + int val=0,err; + unsigned char ucval = 0; #if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT) struct ip_fw tmp_fw; #endif - if (optval == NULL) - { - val=0; - ucval=0; - } - else - { - err = get_user(val, (int *) optval); - if (!err) - err = get_user(ucval, (unsigned char *) optval); - if (err) - return err; + + if(optlen>=sizeof(int)) { + if(get_user(val, (int *) optval)) + return -EFAULT; + } else if(optlen>=sizeof(char)) { + if(get_user(ucval, (unsigned char *) optval)) + return -EFAULT; } if(level!=SOL_IP) - return -EOPNOTSUPP; + return -ENOPROTOOPT; #ifdef CONFIG_IP_MROUTE if(optname>=MRT_BASE && optname <=MRT_BASE+10) { @@ -142,110 +195,149 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt switch(optname) { case IP_OPTIONS: - { - struct options * opt = NULL; - struct options * old_opt; - if (optlen > 40 || optlen < 0) - return -EINVAL; - opt = kmalloc(sizeof(struct options)+((optlen+3)&~3), GFP_KERNEL); - if (!opt) - return -ENOMEM; - memset(opt, 0, sizeof(struct options)); - if (optlen) - { - err = copy_from_user(opt->__data, optval, optlen); - if (err) - { - kfree_s(opt, sizeof(struct options) + ((optlen+3)&~3)); - return -EFAULT; - } - } - - while (optlen & 3) - opt->__data[optlen++] = IPOPT_END; - opt->optlen = optlen; - opt->is_data = 1; - opt->is_setbyuser = 1; - if (optlen && ip_options_compile(opt, NULL)) - { - kfree_s(opt, sizeof(struct options) + optlen); - return -EINVAL; - } - /* - * ANK: I'm afraid that receive handler may change - * options from under us. - */ - cli(); - old_opt = sk->opt; - sk->opt = opt; - sti(); - if (old_opt) - kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen); - return 0; - } - case IP_TOS: /* This sets both TOS and Precedence */ - if (val & ~0xfe) /* Reject setting of unused bits */ + { + struct ip_options * opt = NULL; + struct ip_options * old_opt; + if (optlen > 40 || optlen < 0) + return -EINVAL; + err = ip_options_get(&opt, optval, optlen, 1); + if (err) + return err; + /* + * ANK: I'm afraid that receive handler may change + * options from under us. + */ + cli(); + old_opt = sk->opt; + sk->opt = opt; + sti(); + if (old_opt) + kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen); + return 0; + } + case IP_RXINFO: + if (optlen<4) + return -EINVAL; + if (val) + sk->ip_cmsg_flags |= 1; + else + sk->ip_cmsg_flags &= ~1; + return 0; + case IP_LOCALADDR: + if (optlen<4) + return -EINVAL; + if (val) + sk->ip_cmsg_flags |= 2; + else + sk->ip_cmsg_flags &= ~2; + return 0; + case IP_RECVOPTS: + if (optlen<4) + return -EINVAL; + if (val) + sk->ip_cmsg_flags |= 4; + else + sk->ip_cmsg_flags &= ~4; + return 0; + case IP_RETOPTS: + if (optlen<4) + return -EINVAL; + if (val) + sk->ip_cmsg_flags |= 8; + else + sk->ip_cmsg_flags &= ~8; + return 0; + case IP_RECVDSTADDR: + if (optlen<4) + return -EINVAL; + if (val) + sk->ip_cmsg_flags |= 0x10; + else + sk->ip_cmsg_flags &= ~0x10; + return 0; + case IP_TOS: /* This sets both TOS and Precedence */ + /* Reject setting of unused bits */ + if (optlen<4) + return -EINVAL; + if (val & ~(IPTOS_TOS_MASK|IPTOS_PREC_MASK)) return -EINVAL; - if ((val>>5) > 4 && !suser()) /* Only root can set Prec>4 */ + if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && !suser()) return -EPERM; sk->ip_tos=val; - switch (val & 0x1E) { - case IPTOS_LOWDELAY: - sk->priority=SOPRI_INTERACTIVE; - break; - case IPTOS_THROUGHPUT: - case IPTOS_MINCOST: - sk->priority=SOPRI_BACKGROUND; - break; - default: - sk->priority=SOPRI_NORMAL; - break; - } + sk->priority = rt_tos2priority(val); return 0; case IP_TTL: + if (optlen<4) + return -EINVAL; if(val<1||val>255) return -EINVAL; sk->ip_ttl=val; return 0; case IP_HDRINCL: + if (optlen<4) + return -EINVAL; if(sk->type!=SOCK_RAW) return -ENOPROTOOPT; sk->ip_hdrincl=val?1:0; return 0; -#ifdef CONFIG_IP_MULTICAST + case IP_PMTUDISC: + if (optlen<4) + return -EINVAL; + if (val<0 || val>2) + return -EINVAL; + sk->ip_pmtudisc = val; + return 0; + case IP_RECVERR: + if (optlen<4) + return -EINVAL; + if (sk->type==SOCK_STREAM) + return -ENOPROTOOPT; + lock_sock(sk); + if (sk->ip_recverr && !val) { + struct sk_buff *skb; + /* Drain queued errors */ + while((skb=skb_dequeue(&sk->error_queue))!=NULL) + kfree_skb(skb, FREE_READ); + } + sk->ip_recverr = val?1:0; + release_sock(sk); + return 0; case IP_MULTICAST_TTL: - { + if (optlen<1) + return -EINVAL; sk->ip_mc_ttl=(int)ucval; return 0; - } case IP_MULTICAST_LOOP: - { + if (optlen<1) + return -EINVAL; if(ucval!=0 && ucval!=1) return -EINVAL; sk->ip_mc_loop=(int)ucval; return 0; - } case IP_MULTICAST_IF: { struct in_addr addr; - struct device *dev=NULL; + struct device *dev = NULL; /* * Check the arguments are allowable */ + + if(optlen<sizeof(addr)) + return -EINVAL; - err = copy_from_user(&addr,optval,sizeof(addr)); - if (err) + if(copy_from_user(&addr,optval,sizeof(addr))) return -EFAULT; + /* * What address has been requested */ - if(addr.s_addr==INADDR_ANY) /* Default */ + if (addr.s_addr==INADDR_ANY) /* Default */ { - sk->ip_mc_name[0]=0; + sk->ip_mc_index = 0; return 0; } @@ -253,7 +345,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt * Find the device */ - dev=ip_mc_find_devfor(addr.s_addr); + dev=ip_dev_find(addr.s_addr, NULL); /* * Did we find one @@ -261,11 +353,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt if(dev) { - strcpy(sk->ip_mc_name,dev->name); + sk->ip_mc_index = dev->ifindex; return 0; } return -EADDRNOTAVAIL; } + case IP_ADD_MEMBERSHIP: { @@ -280,35 +373,24 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt /* * Check the arguments. */ - - err = copy_from_user(&mreq,optval,sizeof(mreq)); - if (err) + + if(optlen<sizeof(mreq)) + return -EINVAL; + if(copy_from_user(&mreq,optval,sizeof(mreq))) return -EFAULT; /* * Get device for use later */ - if(mreq.imr_interface.s_addr==INADDR_ANY) - { - /* - * Not set so scan. - */ - if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL) - { - dev=rt->rt_dev; - atomic_dec(&rt->rt_use); - ip_rt_put(rt); - } - } - else - { - /* - * Find a suitable device. - */ - - dev=ip_mc_find_devfor(mreq.imr_interface.s_addr); - } + if (mreq.imr_interface.s_addr==INADDR_ANY) { + err = ip_route_output(&rt, mreq.imr_multiaddr.s_addr, 0, 1, NULL); + if (err) + return err; + dev = rt->u.dst.dev; + ip_rt_put(rt); + } else + dev = ip_dev_find(mreq.imr_interface.s_addr, NULL); /* * No device, no cookies. @@ -334,28 +416,23 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt * Check the arguments */ - err = copy_from_user(&mreq,optval,sizeof(mreq)); - if (err) + if(optlen<sizeof(mreq)) + return -EINVAL; + if(copy_from_user(&mreq,optval,sizeof(mreq))) return -EFAULT; /* * Get device for use later */ - if(mreq.imr_interface.s_addr==INADDR_ANY) - { - if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL) - { - dev=rt->rt_dev; - atomic_dec(&rt->rt_use); - ip_rt_put(rt); - } - } - else - { - - dev=ip_mc_find_devfor(mreq.imr_interface.s_addr); - } + if (mreq.imr_interface.s_addr==INADDR_ANY) { + err = ip_route_output(&rt, mreq.imr_multiaddr.s_addr, 0, 1, NULL); + if (err) + return err; + dev = rt->u.dst.dev; + ip_rt_put(rt); + } else + dev = ip_dev_find(mreq.imr_interface.s_addr, NULL); /* * Did we find a suitable device. @@ -370,7 +447,69 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr); } -#endif + + case IP_MULTICAST_IFN: + { + struct ip_mreqn mreq; + struct device *dev = NULL; + + if(optlen<sizeof(mreq)) + return -EINVAL; + if(copy_from_user(&mreq,optval,sizeof(mreq))) + return -EFAULT; + + if (!mreq.imr_ifindex) { + if (!mreq.imr_address.s_addr) { + sk->ip_mc_index = 0; + sk->ip_mc_addr = 0; + return 0; + } + dev = ip_dev_find(mreq.imr_address.s_addr, NULL); + } else + dev = dev_get_by_index(mreq.imr_ifindex); + + if (!dev) + return -ENODEV; + + sk->ip_mc_index = mreq.imr_ifindex; + sk->ip_mc_addr = mreq.imr_address.s_addr; + return 0; + } + case IP_ADD_MEMBERSHIPN: + { + struct ip_mreqn mreq; + struct device *dev = NULL; + + if(optlen<sizeof(mreq)) + return -EINVAL; + if(copy_from_user(&mreq,optval,sizeof(mreq))) + return -EFAULT; + dev = dev_get_by_index(mreq.imr_ifindex); + if (!dev) + return -ENODEV; + return ip_mc_join_group(sk,dev,mreq.imr_multiaddr.s_addr); + } + + case IP_DROP_MEMBERSHIPN: + { + struct ip_mreqn mreq; + struct device *dev=NULL; + + /* + * Check the arguments + */ + + if(optlen<sizeof(mreq)) + return -EINVAL; + if(copy_from_user(&mreq,optval,sizeof(mreq))) + return -EFAULT; + + dev=dev_get_by_index(mreq.imr_ifindex); + if(!dev) + return -ENODEV; + + return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr); + } #ifdef CONFIG_IP_FIREWALL case IP_FW_INSERT_IN: case IP_FW_INSERT_OUT: @@ -395,12 +534,11 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt case IP_FW_POLICY_FWD: case IP_FW_MASQ_TIMEOUTS: if(!suser()) - return -EPERM; + return -EACCES; if(optlen>sizeof(tmp_fw) || optlen<1) return -EINVAL; - err = copy_from_user(&tmp_fw,optval,optlen); - if (err) - return -EFAULT; + if(copy_from_user(&tmp_fw,optval,optlen)) + return -EFAULT; err=ip_fw_ctl(optname, &tmp_fw,optlen); return -err; /* -0 is 0 after all */ @@ -412,16 +550,14 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt case IP_ACCT_FLUSH: case IP_ACCT_ZERO: if(!suser()) - return -EPERM; + return -EACCES; if(optlen>sizeof(tmp_fw) || optlen<1) return -EINVAL; - err = copy_from_user(&tmp_fw, optval,optlen); - if (err) + if(copy_from_user(&tmp_fw, optval,optlen)) return -EFAULT; err=ip_acct_ctl(optname, &tmp_fw,optlen); return -err; /* -0 is 0 after all */ #endif - /* IP_OPTIONS and friends go here eventually */ default: return(-ENOPROTOOPT); } @@ -435,9 +571,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) { int val,err; -#ifdef CONFIG_IP_MULTICAST int len; -#endif if(level!=SOL_IP) return -EOPNOTSUPP; @@ -449,59 +583,46 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op } #endif + if(get_user(len,optlen)) + return -EFAULT; + switch(optname) { case IP_OPTIONS: { - unsigned char optbuf[sizeof(struct options)+40]; - struct options * opt = (struct options*)optbuf; - + unsigned char optbuf[sizeof(struct ip_options)+40]; + struct ip_options * opt = (struct ip_options*)optbuf; cli(); opt->optlen = 0; if (sk->opt) - memcpy(optbuf, sk->opt, sizeof(struct options)+sk->opt->optlen); + memcpy(optbuf, sk->opt, sizeof(struct ip_options)+sk->opt->optlen); sti(); if (opt->optlen == 0) - { return put_user(0, optlen); - } -/* - * Now we should undo all the changes done by ip_options_compile(). - */ - if (opt->srr) - { - unsigned char * optptr = opt->__data+opt->srr-sizeof(struct iphdr); - memmove(optptr+7, optptr+3, optptr[1]-7); - memcpy(optptr+3, &opt->faddr, 4); - } - if (opt->rr_needaddr) - { - unsigned char * optptr = opt->__data+opt->rr-sizeof(struct iphdr); - memset(&optptr[optptr[2]-1], 0, 4); - optptr[2] -= 4; - } - if (opt->ts) - { - unsigned char * optptr = opt->__data+opt->ts-sizeof(struct iphdr); - if (opt->ts_needtime) - { - memset(&optptr[optptr[2]-1], 0, 4); - optptr[2] -= 4; - } - if (opt->ts_needaddr) - { - memset(&optptr[optptr[2]-1], 0, 4); - optptr[2] -= 4; - } - } - err = put_user(opt->optlen, optlen); - if (!err) - { - if(copy_to_user(optval, opt->__data, opt->optlen)) - err = -EFAULT; - } - return err; + + ip_options_undo(opt); + + len=min(len, opt->optlen); + if(put_user(len, optlen)) + return -EFAULT; + if(copy_to_user(optval, opt->__data, len)) + return -EFAULT; + return 0; } + case IP_RXINFO: + val = (sk->ip_cmsg_flags & 1) != 0; + return 0; + case IP_LOCALADDR: + val = (sk->ip_cmsg_flags & 2) != 0; + return 0; + case IP_RECVOPTS: + val = (sk->ip_cmsg_flags & 4) != 0; + return 0; + case IP_RETOPTS: + val = (sk->ip_cmsg_flags & 8) != 0; + return 0; + case IP_RECVDSTADDR: + val = (sk->ip_cmsg_flags & 0x10) != 0; return 0; case IP_TOS: val=sk->ip_tos; @@ -512,29 +633,60 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op case IP_HDRINCL: val=sk->ip_hdrincl; break; -#ifdef CONFIG_IP_MULTICAST + case IP_PMTUDISC: + val=sk->ip_pmtudisc; + return 0; + case IP_RECVERR: + val=sk->ip_recverr; + return 0; case IP_MULTICAST_TTL: val=sk->ip_mc_ttl; break; case IP_MULTICAST_LOOP: val=sk->ip_mc_loop; break; + case IP_MULTICAST_IFN: + { + struct ip_mreqn mreq; + len = min(len,sizeof(struct ip_mreqn)); + if(put_user(len, optlen)) + return -EFAULT; + mreq.imr_ifindex = sk->ip_mc_index; + mreq.imr_address.s_addr = sk->ip_mc_addr; + mreq.imr_multiaddr.s_addr = 0; + if(copy_to_user((void *)optval, &mreq, len)) + return -EFAULT; + return 0; + } case IP_MULTICAST_IF: - len=strlen(sk->ip_mc_name); + { + struct device *dev = dev_get_by_index(sk->ip_mc_index); + if (dev == NULL) + { + len = 0; + return put_user(len, optlen); + } + dev_lock_list(); + len = min(len,strlen(dev->name)); err = put_user(len, optlen); if (!err) { - err = copy_to_user((void *)optval,sk->ip_mc_name, len); - if (err) - err = -EFAULT; + err = copy_to_user((void *)optval,dev->name, len); + if(err) + err=-EFAULT; } + dev_unlock_list(); return err; -#endif + } default: return(-ENOPROTOOPT); } - err = put_user(sizeof(int), optlen); - if (err) - return err; - return put_user(val,(int *) optval); + + len=min(sizeof(int),len); + + if(put_user(len, optlen)) + return -EFAULT; + if(copy_to_user(optval,&val,len)) + return -EFAULT; + return 0; } |