vserver 1.9.3
[linux-2.6.git] / net / ipv4 / raw.c
index d7b9238..d73ccfa 100644 (file)
@@ -102,6 +102,27 @@ static void raw_v4_unhash(struct sock *sk)
        write_unlock_bh(&raw_v4_lock);
 }
 
+
+/*
+ *     Check if a given address matches for a socket
+ *
+ *     nxi:            the socket's nx_info if any
+ *     addr:           to be verified address
+ *     saddr/baddr:    socket addresses
+ */
+static inline int raw_addr_match (
+       struct nx_info *nxi,
+       uint32_t addr,
+       uint32_t saddr,
+       uint32_t baddr)
+{
+       if (addr && (saddr == addr || baddr == addr))
+               return 1;
+       if (!saddr)
+               return addr_in_nx_info(nxi, addr);
+       return 0;
+}
+
 struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num,
                             unsigned long raddr, unsigned long laddr,
                             int dif)
@@ -113,7 +134,8 @@ struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num,
 
                if (inet->num == num                                    &&
                    !(inet->daddr && inet->daddr != raddr)              &&
-                   !(inet->rcv_saddr && inet->rcv_saddr != laddr)      &&
+                   raw_addr_match(sk->sk_nx_info, laddr,
+                       inet->rcv_saddr, inet->rcv_saddr2)              &&
                    !(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif))
                        goto found; /* gotcha */
        }
@@ -305,6 +327,10 @@ static int raw_send_hdrinc(struct sock *sk, void *from, int length,
 
                iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
        }
+       err = -EPERM;
+       if (!vx_check(0, VX_ADMIN) && !capable(CAP_NET_RAW)
+               && (!addr_in_nx_info(sk->sk_nx_info, iph->saddr)))
+               goto error;
 
        err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
                      dst_output);
@@ -319,10 +345,55 @@ error_fault:
        err = -EFAULT;
        kfree_skb(skb);
 error:
-       IP_INC_STATS(IpOutDiscards);
+       IP_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
        return err; 
 }
 
+static void raw_probe_proto_opt(struct flowi *fl, struct msghdr *msg)
+{
+       struct iovec *iov;
+       u8 __user *type = NULL;
+       u8 __user *code = NULL;
+       int probed = 0;
+       int i;
+
+       if (!msg->msg_iov)
+               return;
+
+       for (i = 0; i < msg->msg_iovlen; i++) {
+               iov = &msg->msg_iov[i];
+               if (!iov)
+                       continue;
+
+               switch (fl->proto) {
+               case IPPROTO_ICMP:
+                       /* check if one-byte field is readable or not. */
+                       if (iov->iov_base && iov->iov_len < 1)
+                               break;
+
+                       if (!type) {
+                               type = iov->iov_base;
+                               /* check if code field is readable or not. */
+                               if (iov->iov_len > 1)
+                                       code = type + 1;
+                       } else if (!code)
+                               code = iov->iov_base;
+
+                       if (type && code) {
+                               get_user(fl->fl_icmp_type, type);
+                               __get_user(fl->fl_icmp_code, code);
+                               probed = 1;
+                       }
+                       break;
+               default:
+                       probed = 1;
+                       break;
+               }
+               if (probed)
+                       break;
+       }
+}
+
 static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                       size_t len)
 {
@@ -429,6 +500,15 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                                    .proto = inet->hdrincl ? IPPROTO_RAW :
                                                             sk->sk_protocol,
                                  };
+               if (!inet->hdrincl)
+                       raw_probe_proto_opt(&fl, msg);
+
+               if (sk->sk_nx_info) {
+                       err = ip_find_src(sk->sk_nx_info, &rt, &fl);
+
+                       if (err)
+                               goto done;
+               }
                err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT));
        }
        if (err)
@@ -480,7 +560,7 @@ static void raw_close(struct sock *sk, long timeout)
         */
        ip_ra_control(sk, 0, NULL);
 
-       inet_sock_release(sk);
+       sk_common_release(sk);
 }
 
 /* This gets rid of all the nasties in af_inet. -DaveM */
@@ -555,9 +635,11 @@ int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        }
        if (inet->cmsg_flags)
                ip_cmsg_recv(msg, skb);
+       if (flags & MSG_TRUNC)
+               copied = skb->len;
 done:
        skb_free_datagram(sk, skb);
-out:   return err ? : copied;
+out:   return err ? err : copied;
 }
 
 static int raw_init(struct sock *sk)
@@ -568,7 +650,7 @@ static int raw_init(struct sock *sk)
        return 0;
 }
 
-static int raw_seticmpfilter(struct sock *sk, char *optval, int optlen)
+static int raw_seticmpfilter(struct sock *sk, char __user *optval, int optlen)
 {
        if (optlen > sizeof(struct icmp_filter))
                optlen = sizeof(struct icmp_filter);
@@ -577,7 +659,7 @@ static int raw_seticmpfilter(struct sock *sk, char *optval, int optlen)
        return 0;
 }
 
-static int raw_geticmpfilter(struct sock *sk, char *optval, int *optlen)
+static int raw_geticmpfilter(struct sock *sk, char __user *optval, int __user *optlen)
 {
        int len, ret = -EFAULT;
 
@@ -597,7 +679,7 @@ out:        return ret;
 }
 
 static int raw_setsockopt(struct sock *sk, int level, int optname, 
-                         char *optval, int optlen)
+                         char __user *optval, int optlen)
 {
        if (level != SOL_RAW)
                return ip_setsockopt(sk, level, optname, optval, optlen);
@@ -612,7 +694,7 @@ static int raw_setsockopt(struct sock *sk, int level, int optname,
 }
 
 static int raw_getsockopt(struct sock *sk, int level, int optname, 
-                         char *optval, int *optlen)
+                         char __user *optval, int __user *optlen)
 {
        if (level != SOL_RAW)
                return ip_getsockopt(sk, level, optname, optval, optlen);
@@ -631,7 +713,7 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg)
        switch (cmd) {
                case SIOCOUTQ: {
                        int amount = atomic_read(&sk->sk_wmem_alloc);
-                       return put_user(amount, (int *)arg);
+                       return put_user(amount, (int __user *)arg);
                }
                case SIOCINQ: {
                        struct sk_buff *skb;
@@ -642,12 +724,12 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg)
                        if (skb != NULL)
                                amount = skb->len;
                        spin_unlock_irq(&sk->sk_receive_queue.lock);
-                       return put_user(amount, (int *)arg);
+                       return put_user(amount, (int __user *)arg);
                }
 
                default:
 #ifdef CONFIG_IP_MROUTE
-                       return ipmr_ioctl(sk, cmd, arg);
+                       return ipmr_ioctl(sk, cmd, (void __user *)arg);
 #else
                        return -ENOIOCTLCMD;
 #endif
@@ -657,7 +739,7 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg)
 struct proto raw_prot = {
        .name =         "RAW",
        .close =        raw_close,
-       .connect =      udp_connect,
+       .connect =      ip4_datagram_connect,
        .disconnect =   udp_disconnect,
        .ioctl =        raw_ioctl,
        .init =         raw_init,
@@ -669,6 +751,7 @@ struct proto raw_prot = {
        .backlog_rcv =  raw_rcv_skb,
        .hash =         raw_v4_hash,
        .unhash =       raw_v4_unhash,
+       .slab_obj_size = sizeof(struct raw_sock),
 };
 
 #ifdef CONFIG_PROC_FS
@@ -687,7 +770,8 @@ static struct sock *raw_get_first(struct seq_file *seq)
                struct hlist_node *node;
 
                sk_for_each(sk, node, &raw_v4_htable[state->bucket])
-                       if (sk->sk_family == PF_INET)
+                       if (sk->sk_family == PF_INET &&
+                               vx_check(sk->sk_xid, VX_WATCH|VX_IDENT))
                                goto found;
        }
        sk = NULL;
@@ -703,7 +787,8 @@ static struct sock *raw_get_next(struct seq_file *seq, struct sock *sk)
                sk = sk_next(sk);
 try_again:
                ;
-       } while (sk && sk->sk_family != PF_INET);
+       } while (sk && (sk->sk_family != PF_INET ||
+               !vx_check(sk->sk_xid, VX_WATCH|VX_IDENT)));
 
        if (!sk && ++state->bucket < RAWV4_HTABLE_SIZE) {
                sk = sk_head(&raw_v4_htable[state->bucket]);