upgrade to fedora-2.6.12-1.1398.FC4 + vserver 2.0.rc7
[linux-2.6.git] / net / ipv4 / raw.c
index d36280a..4591a5b 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Version:    $Id: raw.c,v 1.64 2002/02/01 22:01:04 davem Exp $
  *
- * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
+ * Authors:    Ross Biro
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *
  * Fixes:
@@ -82,7 +82,7 @@
 #include <linux/vs_base.h>
 
 struct hlist_head raw_v4_htable[RAWV4_HTABLE_SIZE];
-rwlock_t raw_v4_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(raw_v4_lock);
 
 static void raw_v4_hash(struct sock *sk)
 {
@@ -103,6 +103,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)
@@ -110,11 +131,12 @@ struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num,
        struct hlist_node *node;
 
        sk_for_each_from(sk, node) {
-               struct inet_opt *inet = inet_sk(sk);
+               struct inet_sock *inet = inet_sk(sk);
 
                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 */
        }
@@ -136,7 +158,7 @@ static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb)
 
        type = skb->h.icmph->type;
        if (type < 32) {
-               __u32 data = raw4_sk(sk)->filter.data;
+               __u32 data = raw_sk(sk)->filter.data;
 
                return ((1 << type) & data) != 0;
        }
@@ -182,7 +204,7 @@ out:
 
 void raw_err (struct sock *sk, struct sk_buff *skb, u32 info)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        int type = skb->h.icmph->type;
        int code = skb->h.icmph->code;
        int err = 0;
@@ -264,7 +286,7 @@ static int raw_send_hdrinc(struct sock *sk, void *from, int length,
                        struct rtable *rt, 
                        unsigned int flags)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        int hh_len;
        struct iphdr *iph;
        struct sk_buff *skb;
@@ -309,6 +331,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);
@@ -375,7 +401,7 @@ static void raw_probe_proto_opt(struct flowi *fl, struct msghdr *msg)
 static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                       size_t len)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct ipcm_cookie ipc;
        struct rtable *rt = NULL;
        int free = 0;
@@ -458,7 +484,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                        daddr = ipc.opt->faddr;
                }
        }
-       tos = RT_TOS(inet->tos) | sk->sk_localroute;
+       tos = RT_CONN_FLAGS(sk);
        if (msg->msg_flags & MSG_DONTROUTE)
                tos |= RTO_ONLINK;
 
@@ -481,6 +507,12 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                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)
@@ -538,7 +570,7 @@ static void raw_close(struct sock *sk, long timeout)
 /* This gets rid of all the nasties in af_inet. -DaveM */
 static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
        int ret = -EINVAL;
        int chk_addr_ret;
@@ -563,10 +595,10 @@ out:      return ret;
  *     we return it, otherwise we block.
  */
 
-int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
-               size_t len, int noblock, int flags, int *addr_len)
+static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+                      size_t len, int noblock, int flags, int *addr_len)
 {
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        size_t copied = 0;
        int err = -EOPNOTSUPP;
        struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
@@ -616,9 +648,10 @@ out:       return err ? err : copied;
 
 static int raw_init(struct sock *sk)
 {
-       struct raw_opt *tp = raw4_sk(sk);
+       struct raw_sock *rp = raw_sk(sk);
+
        if (inet_sk(sk)->num == IPPROTO_ICMP)
-               memset(&tp->filter, 0, sizeof(tp->filter));
+               memset(&rp->filter, 0, sizeof(rp->filter));
        return 0;
 }
 
@@ -626,7 +659,7 @@ static int raw_seticmpfilter(struct sock *sk, char __user *optval, int optlen)
 {
        if (optlen > sizeof(struct icmp_filter))
                optlen = sizeof(struct icmp_filter);
-       if (copy_from_user(&raw4_sk(sk)->filter, optval, optlen))
+       if (copy_from_user(&raw_sk(sk)->filter, optval, optlen))
                return -EFAULT;
        return 0;
 }
@@ -644,7 +677,7 @@ static int raw_geticmpfilter(struct sock *sk, char __user *optval, int __user *o
                len = sizeof(struct icmp_filter);
        ret = -EFAULT;
        if (put_user(len, optlen) ||
-           copy_to_user(optval, &raw4_sk(sk)->filter, len))
+           copy_to_user(optval, &raw_sk(sk)->filter, len))
                goto out;
        ret = 0;
 out:   return ret;
@@ -724,7 +757,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),
+       .obj_size =     sizeof(struct raw_sock),
 };
 
 #ifdef CONFIG_PROC_FS
@@ -744,7 +777,7 @@ static struct sock *raw_get_first(struct seq_file *seq)
 
                sk_for_each(sk, node, &raw_v4_htable[state->bucket])
                        if (sk->sk_family == PF_INET &&
-                               vx_check(sk->sk_xid, VX_WATCH|VX_IDENT))
+                               vx_check(sk->sk_xid, VX_IDENT|VX_WATCH))
                                goto found;
        }
        sk = NULL;
@@ -761,7 +794,7 @@ static struct sock *raw_get_next(struct seq_file *seq, struct sock *sk)
 try_again:
                ;
        } while (sk && (sk->sk_family != PF_INET ||
-               !vx_check(sk->sk_xid, VX_WATCH|VX_IDENT)));
+               !vx_check(sk->sk_xid, VX_IDENT|VX_WATCH)));
 
        if (!sk && ++state->bucket < RAWV4_HTABLE_SIZE) {
                sk = sk_head(&raw_v4_htable[state->bucket]);
@@ -805,7 +838,7 @@ static void raw_seq_stop(struct seq_file *seq, void *v)
 
 static __inline__ char *get_raw_sock(struct sock *sp, char *tmpbuf, int i)
 {
-       struct inet_opt *inet = inet_sk(sp);
+       struct inet_sock *inet = inet_sk(sp);
        unsigned int dest = inet->daddr,
                     src = inet->rcv_saddr;
        __u16 destp = 0,