linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / net / ipv4 / ip_sockglue.c
index 83579b5..2bf8d78 100644 (file)
 #include <linux/skbuff.h>
 #include <linux/ip.h>
 #include <linux/icmp.h>
+#include <linux/inetdevice.h>
 #include <linux/netdevice.h>
 #include <net/sock.h>
 #include <net/ip.h>
 #include <net/icmp.h>
-#include <net/tcp.h>
-#include <linux/tcp.h>
+#include <net/tcp_states.h>
 #include <linux/udp.h>
 #include <linux/igmp.h>
 #include <linux/netfilter.h>
@@ -92,7 +92,7 @@ static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
 }
 
 
-void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
+static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
 {
        unsigned char optbuf[sizeof(struct ip_options) + 40];
        struct ip_options * opt = (struct ip_options*)optbuf;
@@ -112,7 +112,7 @@ void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
 
 void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
 {
-       struct inet_opt *inet = inet_sk(skb->sk);
+       struct inet_sock *inet = inet_sk(skb->sk);
        unsigned flags = inet->cmsg_flags;
 
        /* Ordered by supposed usage frequency */
@@ -146,17 +146,14 @@ int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc)
        struct cmsghdr *cmsg;
 
        for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
-               if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
-                   (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
-                                   + cmsg->cmsg_len) > msg->msg_controllen) {
+               if (!CMSG_OK(msg, cmsg))
                        return -EINVAL;
-               }
                if (cmsg->cmsg_level != SOL_IP)
                        continue;
                switch (cmsg->cmsg_type) {
                case IP_RETOPTS:
                        err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));
-                       err = ip_options_get(&ipc->opt, CMSG_DATA(cmsg), err < 40 ? err : 40, 0);
+                       err = ip_options_get(&ipc->opt, CMSG_DATA(cmsg), err < 40 ? err : 40);
                        if (err)
                                return err;
                        break;
@@ -189,7 +186,7 @@ int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc)
    sent to multicast group to reach destination designated router.
  */
 struct ip_ra_chain *ip_ra_chain;
-rwlock_t ip_ra_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(ip_ra_lock);
 
 int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *))
 {
@@ -205,8 +202,7 @@ int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct s
                if (ra->sk == sk) {
                        if (on) {
                                write_unlock_bh(&ip_ra_lock);
-                               if (new_ra)
-                                       kfree(new_ra);
+                               kfree(new_ra);
                                return -EADDRINUSE;
                        }
                        *rap = ra->next;
@@ -237,7 +233,7 @@ int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct s
 void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 
                   u16 port, u32 info, u8 *payload)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct sock_exterr_skb *serr;
 
        if (!inet->recverr)
@@ -266,7 +262,7 @@ void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
 
 void ip_local_error(struct sock *sk, int err, u32 daddr, u16 port, u32 info)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct sock_exterr_skb *serr;
        struct iphdr *iph;
        struct sk_buff *skb;
@@ -345,7 +341,7 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len)
        sin = &errhdr.offender;
        sin->sin_family = AF_UNSPEC;
        if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
-               struct inet_opt *inet = inet_sk(sk);
+               struct inet_sock *inet = inet_sk(sk);
 
                sin->sin_family = AF_INET;
                sin->sin_addr.s_addr = skb->nh.iph->saddr;
@@ -363,14 +359,14 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len)
        err = copied;
 
        /* Reset and regenerate socket error */
-       spin_lock_irq(&sk->sk_error_queue.lock);
+       spin_lock_bh(&sk->sk_error_queue.lock);
        sk->sk_err = 0;
        if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) {
                sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno;
-               spin_unlock_irq(&sk->sk_error_queue.lock);
+               spin_unlock_bh(&sk->sk_error_queue.lock);
                sk->sk_error_report(sk);
        } else
-               spin_unlock_irq(&sk->sk_error_queue.lock);
+               spin_unlock_bh(&sk->sk_error_queue.lock);
 
 out_free_skb:  
        kfree_skb(skb);
@@ -384,9 +380,9 @@ out:
  *     an IP socket.
  */
 
-int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
+int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        int val=0,err;
 
        if (level != SOL_IP)
@@ -401,12 +397,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                optname == IP_MULTICAST_TTL || 
                                optname == IP_MULTICAST_LOOP) { 
                if (optlen >= sizeof(int)) {
-                       if (get_user(val, (int *) optval))
+                       if (get_user(val, (int __user *) optval))
                                return -EFAULT;
                } else if (optlen >= sizeof(char)) {
                        unsigned char ucval;
 
-                       if (get_user(ucval, (unsigned char *) optval))
+                       if (get_user(ucval, (unsigned char __user *) optval))
                                return -EFAULT;
                        val = (int) ucval;
                }
@@ -428,11 +424,11 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                        struct ip_options * opt = NULL;
                        if (optlen > 40 || optlen < 0)
                                goto e_inval;
-                       err = ip_options_get(&opt, optval, optlen, 1);
+                       err = ip_options_get_from_user(&opt, optval, optlen);
                        if (err)
                                break;
-                       if (sk->sk_type == SOCK_STREAM) {
-                               struct tcp_opt *tp = tcp_sk(sk);
+                       if (inet->is_icsk) {
+                               struct inet_connection_sock *icsk = inet_csk(sk);
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
                                if (sk->sk_family == PF_INET ||
                                    (!((1 << sk->sk_state) &
@@ -440,17 +436,16 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                     inet->daddr != LOOPBACK4_IPV6)) {
 #endif
                                        if (inet->opt)
-                                               tp->ext_header_len -= inet->opt->optlen;
+                                               icsk->icsk_ext_hdr_len -= inet->opt->optlen;
                                        if (opt)
-                                               tp->ext_header_len += opt->optlen;
-                                       tcp_sync_mss(sk, tp->pmtu_cookie);
+                                               icsk->icsk_ext_hdr_len += opt->optlen;
+                                       icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
                                }
 #endif
                        }
                        opt = xchg(&inet->opt, opt);
-                       if (opt)
-                               kfree(opt);
+                       kfree(opt);
                        break;
                }
                case IP_PKTINFO:
@@ -617,7 +612,6 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                }
                case IP_MSFILTER:
                {
-                       extern int sysctl_optmem_max;
                        extern int sysctl_igmp_max_msf;
                        struct ip_msfilter *msf;
 
@@ -627,7 +621,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                err = -ENOBUFS;
                                break;
                        }
-                       msf = (struct ip_msfilter *)kmalloc(optlen, GFP_KERNEL);
+                       msf = kmalloc(optlen, GFP_KERNEL);
                        if (msf == 0) {
                                err = -ENOBUFS;
                                break;
@@ -680,11 +674,11 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                mreq.imr_address.s_addr = mreqs.imr_interface;
                                mreq.imr_ifindex = 0;
                                err = ip_mc_join_group(sk, &mreq);
-                               if (err)
+                               if (err && err != -EADDRINUSE)
                                        break;
                                omode = MCAST_INCLUDE;
                                add = 1;
-                       } else /*IP_DROP_SOURCE_MEMBERSHIP */ {
+                       } else /* IP_DROP_SOURCE_MEMBERSHIP */ {
                                omode = MCAST_INCLUDE;
                                add = 0;
                        }
@@ -757,8 +751,9 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                mreq.imr_address.s_addr = 0;
                                mreq.imr_ifindex = greqs.gsr_interface;
                                err = ip_mc_join_group(sk, &mreq);
-                               if (err)
+                               if (err && err != -EADDRINUSE)
                                        break;
+                               greqs.gsr_interface = mreq.imr_ifindex;
                                omode = MCAST_INCLUDE;
                                add = 1;
                        } else /* MCAST_LEAVE_SOURCE_GROUP */ {
@@ -771,11 +766,10 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                }
                case MCAST_MSFILTER:
                {
-                       extern int sysctl_optmem_max;
                        extern int sysctl_igmp_max_msf;
                        struct sockaddr_in *psin;
-                       struct ip_msfilter *msf = 0;
-                       struct group_filter *gsf = 0;
+                       struct ip_msfilter *msf = NULL;
+                       struct group_filter *gsf = NULL;
                        int msize, i, ifindex;
 
                        if (optlen < GROUP_FILTER_SIZE(0))
@@ -784,7 +778,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                err = -ENOBUFS;
                                break;
                        }
-                       gsf = (struct group_filter *)kmalloc(optlen,GFP_KERNEL);
+                       gsf = kmalloc(optlen,GFP_KERNEL);
                        if (gsf == 0) {
                                err = -ENOBUFS;
                                break;
@@ -804,7 +798,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                goto mc_msf_out;
                        }
                        msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
-                       msf = (struct ip_msfilter *)kmalloc(msize,GFP_KERNEL);
+                       msf = kmalloc(msize,GFP_KERNEL);
                        if (msf == 0) {
                                err = -ENOBUFS;
                                goto mc_msf_out;
@@ -828,14 +822,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                msf->imsf_slist[i] = psin->sin_addr.s_addr;
                        }
                        kfree(gsf);
-                       gsf = 0;
+                       gsf = NULL;
 
                        err = ip_mc_msfilter(sk, msf, ifindex);
 mc_msf_out:
-                       if (msf)
-                               kfree(msf);
-                       if (gsf)
-                               kfree(gsf);
+                       kfree(msf);
+                       kfree(gsf);
                        break;
                }
                case IP_ROUTER_ALERT:   
@@ -850,6 +842,9 @@ mc_msf_out:
  
                case IP_IPSEC_POLICY:
                case IP_XFRM_POLICY:
+                       err = -EPERM;
+                       if (!capable(CAP_NET_ADMIN))
+                               break;
                        err = xfrm_user_policy(sk, optname, optval, optlen);
                        break;
 
@@ -875,9 +870,9 @@ e_inval:
  *     _received_ ones. The set sets the _sent_ ones.
  */
 
-int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
+int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        int val;
        int len;
        
@@ -957,7 +952,7 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op
                        val = 0;
                        dst = sk_dst_get(sk);
                        if (dst) {
-                               val = dst_pmtu(dst) - dst->header_len;
+                               val = dst_mtu(dst);
                                dst_release(dst);
                        }
                        if (!val) {
@@ -984,7 +979,7 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op
 
                        if(put_user(len, optlen))
                                return -EFAULT;
-                       if(copy_to_user((void *)optval, &addr, len))
+                       if(copy_to_user(optval, &addr, len))
                                return -EFAULT;
                        return 0;
                }
@@ -1002,7 +997,7 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op
                                return -EFAULT;
                        }
                        err = ip_mc_msfget(sk, &msf,
-                               (struct ip_msfilter *)optval, optlen);
+                               (struct ip_msfilter __user *)optval, optlen);
                        release_sock(sk);
                        return err;
                }
@@ -1020,7 +1015,7 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op
                                return -EFAULT;
                        }
                        err = ip_mc_gsfget(sk, &gsf,
-                               (struct group_filter *)optval, optlen);
+                               (struct group_filter __user *)optval, optlen);
                        release_sock(sk);
                        return err;
                }
@@ -1089,7 +1084,5 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op
 
 EXPORT_SYMBOL(ip_cmsg_recv);
 
-#ifdef CONFIG_IP_SCTP_MODULE
 EXPORT_SYMBOL(ip_getsockopt);
 EXPORT_SYMBOL(ip_setsockopt);
-#endif