Merge git://oss.sgi.com:8090/xfs/xfs-2.6
[linux-drm-fsl-dcu.git] / net / packet / af_packet.c
index f4ccb90e67392cd8a9821cb947d06cc4ef405783..444550917bc1f6f89a297cf20868f4b31de4933c 100644 (file)
@@ -11,7 +11,7 @@
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *             Alan Cox, <gw4pts@gw4pts.ampr.org>
  *
- * Fixes:      
+ * Fixes:
  *             Alan Cox        :       verify_area() now used correctly
  *             Alan Cox        :       new skbuff lists, look ma no backlogs!
  *             Alan Cox        :       tidied skbuff lists.
  *     Alexey Kuznetsov        :       Untied from IPv4 stack.
  *     Cyrus Durgin            :       Fixed kerneld for kmod.
  *     Michal Ostrowski        :       Module initialization cleanup.
- *         Ulises Alonso        :       Frame number limit removal and 
+ *         Ulises Alonso        :       Frame number limit removal and
  *                                      packet_set_ring memory leak.
  *             Eric Biederman  :       Allow for > 8 byte hardware addresses.
  *                                     The convention is that longer addresses
  *                                     will simply extend the hardware address
- *                                     byte arrays at the end of sockaddr_ll 
+ *                                     byte arrays at the end of sockaddr_ll
  *                                     and packet_mreq.
  *
  *             This program is free software; you can redistribute it and/or
@@ -48,7 +48,7 @@
  *             2 of the License, or (at your option) any later version.
  *
  */
+
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
@@ -60,6 +60,7 @@
 #include <linux/netdevice.h>
 #include <linux/if_packet.h>
 #include <linux/wireless.h>
+#include <linux/kernel.h>
 #include <linux/kmod.h>
 #include <net/ip.h>
 #include <net/protocol.h>
@@ -71,6 +72,7 @@
 #include <asm/uaccess.h>
 #include <asm/ioctls.h>
 #include <asm/page.h>
+#include <asm/cacheflush.h>
 #include <asm/io.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
@@ -122,7 +124,7 @@ Outgoing, dev->hard_header!=NULL
 
 Incoming, dev->hard_header==NULL
    mac.raw -> UNKNOWN position. It is very likely, that it points to ll header.
-              PPP makes it, that is wrong, because introduce assymetry
+             PPP makes it, that is wrong, because introduce assymetry
              between rx and tx paths.
    data    -> data
 
@@ -199,9 +201,10 @@ struct packet_sock {
 #endif
        struct packet_type      prot_hook;
        spinlock_t              bind_lock;
-       char                    running;        /* prot_hook is attached*/
+       unsigned int            running:1,      /* prot_hook is attached*/
+                               auxdata:1;
        int                     ifindex;        /* bound device         */
-       unsigned short          num;
+       __be16                  num;
 #ifdef CONFIG_PACKET_MULTICAST
        struct packet_mclist    *mclist;
 #endif
@@ -213,6 +216,16 @@ struct packet_sock {
 #endif
 };
 
+struct packet_skb_cb {
+       unsigned int origlen;
+       union {
+               struct sockaddr_pkt pkt;
+               struct sockaddr_ll ll;
+       } sa;
+};
+
+#define PACKET_SKB_CB(__skb)   ((struct packet_skb_cb *)((__skb)->cb))
+
 #ifdef CONFIG_PACKET_MMAP
 
 static inline char *packet_lookup_frame(struct packet_sock *po, unsigned int position)
@@ -224,7 +237,7 @@ static inline char *packet_lookup_frame(struct packet_sock *po, unsigned int pos
        frame_offset = position % po->frames_per_block;
 
        frame = po->pg_vec[pg_vec_pos] + (frame_offset * po->frame_size);
-       
+
        return frame;
 }
 #endif
@@ -267,7 +280,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev,  struct
         */
 
        sk = pt->af_packet_priv;
-       
+
        /*
         *      Yank back the headers [hope the device set this
         *      right or kerboom...]
@@ -292,7 +305,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev,  struct
        /* drop conntrack reference */
        nf_reset(skb);
 
-       spkt = (struct sockaddr_pkt*)skb->cb;
+       spkt = &PACKET_SKB_CB(skb)->sa.pkt;
 
        skb_push(skb, skb->data-skb->mac.raw);
 
@@ -323,7 +336,7 @@ oom:
  *     Output a raw packet to a device layer. This bypasses all the other
  *     protocol layers and you must therefore supply it with a complete frame
  */
+
 static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,
                               struct msghdr *msg, size_t len)
 {
@@ -331,11 +344,11 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,
        struct sockaddr_pkt *saddr=(struct sockaddr_pkt *)msg->msg_name;
        struct sk_buff *skb;
        struct net_device *dev;
-       unsigned short proto=0;
+       __be16 proto=0;
        int err;
-       
+
        /*
-        *      Get and verify the address. 
+        *      Get and verify the address.
         */
 
        if (saddr)
@@ -349,7 +362,7 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,
                return(-ENOTCONN);      /* SOCK_PACKET must be sent giving an address */
 
        /*
-        *      Find the device first to size check it 
+        *      Find the device first to size check it
         */
 
        saddr->spkt_device[13] = 0;
@@ -357,12 +370,16 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,
        err = -ENODEV;
        if (dev == NULL)
                goto out_unlock;
-       
+
+       err = -ENETDOWN;
+       if (!(dev->flags & IFF_UP))
+               goto out_unlock;
+
        /*
         *      You may not queue a frame bigger than the mtu. This is the lowest level
         *      raw protocol and you must do your own fragmentation at this level.
         */
-        
+
        err = -EMSGSIZE;
        if (len > dev->mtu + dev->hard_header_len)
                goto out_unlock;
@@ -375,14 +392,14 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,
         *      deal with the problem - do your own algorithmic backoffs. That's far
         *      more flexible.
         */
-        
-       if (skb == NULL) 
+
+       if (skb == NULL)
                goto out_unlock;
 
        /*
-        *      Fill it in 
+        *      Fill it in
         */
-        
+
        /* FIXME: Save some space for broken drivers that write a
         * hard header at transmission time by themselves. PPP is the
         * notable one here. This should really be fixed at the driver level.
@@ -406,10 +423,6 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,
        if (err)
                goto out_free;
 
-       err = -ENETDOWN;
-       if (!(dev->flags & IFF_UP))
-               goto out_free;
-
        /*
         *      Now send it
         */
@@ -427,24 +440,18 @@ out_unlock:
 }
 #endif
 
-static inline int run_filter(struct sk_buff *skb, struct sock *sk,
-                                                       unsigned *snaplen)
+static inline unsigned int run_filter(struct sk_buff *skb, struct sock *sk,
+                                     unsigned int res)
 {
        struct sk_filter *filter;
-       int err = 0;
 
        rcu_read_lock_bh();
        filter = rcu_dereference(sk->sk_filter);
-       if (filter != NULL) {
-               err = sk_run_filter(skb, filter->insns, filter->len);
-               if (!err)
-                       err = -EPERM;
-               else if (*snaplen > err)
-                       *snaplen = err;
-       }
+       if (filter != NULL)
+               res = sk_run_filter(skb, filter->insns, filter->len);
        rcu_read_unlock_bh();
 
-       return err;
+       return res;
 }
 
 /*
@@ -466,7 +473,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
        struct packet_sock *po;
        u8 * skb_head = skb->data;
        int skb_len = skb->len;
-       unsigned snaplen;
+       unsigned int snaplen, res;
 
        if (skb->pkt_type == PACKET_LOOPBACK)
                goto drop;
@@ -494,8 +501,11 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
 
        snaplen = skb->len;
 
-       if (run_filter(skb, sk, &snaplen) < 0)
+       res = run_filter(skb, sk, snaplen);
+       if (!res)
                goto drop_n_restore;
+       if (snaplen > res)
+               snaplen = res;
 
        if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=
            (unsigned)sk->sk_rcvbuf)
@@ -514,7 +524,10 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
                skb = nskb;
        }
 
-       sll = (struct sockaddr_ll*)skb->cb;
+       BUILD_BUG_ON(sizeof(*PACKET_SKB_CB(skb)) + MAX_ADDR_LEN - 8 >
+                    sizeof(skb->cb));
+
+       sll = &PACKET_SKB_CB(skb)->sa.ll;
        sll->sll_family = AF_PACKET;
        sll->sll_hatype = dev->type;
        sll->sll_protocol = skb->protocol;
@@ -525,6 +538,8 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
        if (dev->hard_header_parse)
                sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr);
 
+       PACKET_SKB_CB(skb)->origlen = skb->len;
+
        if (pskb_trim(skb, snaplen))
                goto drop_n_acct;
 
@@ -567,7 +582,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
        struct tpacket_hdr *h;
        u8 * skb_head = skb->data;
        int skb_len = skb->len;
-       unsigned snaplen;
+       unsigned int snaplen, res;
        unsigned long status = TP_STATUS_LOSING|TP_STATUS_USER;
        unsigned short macoff, netoff;
        struct sk_buff *copy_skb = NULL;
@@ -584,15 +599,19 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
                else if (skb->pkt_type == PACKET_OUTGOING) {
                        /* Special case: outgoing packets have ll header at head */
                        skb_pull(skb, skb->nh.raw - skb->data);
-                       if (skb->ip_summed == CHECKSUM_PARTIAL)
-                               status |= TP_STATUS_CSUMNOTREADY;
                }
        }
 
+       if (skb->ip_summed == CHECKSUM_PARTIAL)
+               status |= TP_STATUS_CSUMNOTREADY;
+
        snaplen = skb->len;
 
-       if (run_filter(skb, sk, &snaplen) < 0)
+       res = run_filter(skb, sk, snaplen);
+       if (!res)
                goto drop_n_restore;
+       if (snaplen > res)
+               snaplen = res;
 
        if (sk->sk_type == SOCK_DGRAM) {
                macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16;
@@ -622,7 +641,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
 
        spin_lock(&sk->sk_receive_queue.lock);
        h = (struct tpacket_hdr *)packet_lookup_frame(po, po->head);
-       
+
        if (h->tp_status)
                goto ring_is_full;
        po->head = po->head != po->frame_max ? po->head+1 : 0;
@@ -641,7 +660,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
        h->tp_snaplen = snaplen;
        h->tp_mac = macoff;
        h->tp_net = netoff;
-       if (skb->tstamp.off_sec == 0) { 
+       if (skb->tstamp.off_sec == 0) {
                __net_timestamp(skb);
                sock_enable_timestamp(sk);
        }
@@ -659,7 +678,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
        sll->sll_ifindex = dev->ifindex;
 
        h->tp_status = status;
-       mb();
+       smp_mb();
 
        {
                struct page *p_start, *p_end;
@@ -681,7 +700,7 @@ drop_n_restore:
                skb->len = skb_len;
        }
 drop:
-        kfree_skb(skb);
+       kfree_skb(skb);
        return 0;
 
 ring_is_full:
@@ -704,14 +723,14 @@ static int packet_sendmsg(struct kiocb *iocb, struct socket *sock,
        struct sockaddr_ll *saddr=(struct sockaddr_ll *)msg->msg_name;
        struct sk_buff *skb;
        struct net_device *dev;
-       unsigned short proto;
+       __be16 proto;
        unsigned char *addr;
        int ifindex, err, reserve = 0;
 
        /*
-        *      Get and verify the address. 
+        *      Get and verify the address.
         */
-        
+
        if (saddr == NULL) {
                struct packet_sock *po = pkt_sk(sk);
 
@@ -737,6 +756,10 @@ static int packet_sendmsg(struct kiocb *iocb, struct socket *sock,
        if (sock->type == SOCK_RAW)
                reserve = dev->hard_header_len;
 
+       err = -ENETDOWN;
+       if (!(dev->flags & IFF_UP))
+               goto out_unlock;
+
        err = -EMSGSIZE;
        if (len > dev->mtu+reserve)
                goto out_unlock;
@@ -769,10 +792,6 @@ static int packet_sendmsg(struct kiocb *iocb, struct socket *sock,
        skb->dev = dev;
        skb->priority = sk->sk_priority;
 
-       err = -ENETDOWN;
-       if (!(dev->flags & IFF_UP))
-               goto out_free;
-
        /*
         *      Now send it
         */
@@ -858,7 +877,7 @@ static int packet_release(struct socket *sock)
  *     Attach a packet hook.
  */
 
-static int packet_do_bind(struct sock *sk, struct net_device *dev, int protocol)
+static int packet_do_bind(struct sock *sk, struct net_device *dev, __be16 protocol)
 {
        struct packet_sock *po = pkt_sk(sk);
        /*
@@ -920,11 +939,11 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, int add
        char name[15];
        struct net_device *dev;
        int err = -ENODEV;
-       
+
        /*
         *      Check legality
         */
-        
+
        if (addr_len != sizeof(struct sockaddr))
                return -EINVAL;
        strlcpy(name,uaddr->sa_data,sizeof(name));
@@ -949,7 +968,7 @@ static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len
        /*
         *      Check legality
         */
-        
+
        if (addr_len < sizeof(struct sockaddr_ll))
                return -EINVAL;
        if (sll->sll_family != AF_PACKET)
@@ -976,13 +995,14 @@ static struct proto packet_proto = {
 };
 
 /*
- *     Create a packet of type SOCK_PACKET. 
+ *     Create a packet of type SOCK_PACKET.
  */
 
 static int packet_create(struct socket *sock, int protocol)
 {
        struct sock *sk;
        struct packet_sock *po;
+       __be16 proto = (__force __be16)protocol; /* weird, but documented */
        int err;
 
        if (!capable(CAP_NET_RAW))
@@ -1010,7 +1030,7 @@ static int packet_create(struct socket *sock, int protocol)
 
        po = pkt_sk(sk);
        sk->sk_family = PF_PACKET;
-       po->num = protocol;
+       po->num = proto;
 
        sk->sk_destruct = packet_sock_destruct;
        atomic_inc(&packet_socks_nr);
@@ -1027,8 +1047,8 @@ static int packet_create(struct socket *sock, int protocol)
 #endif
        po->prot_hook.af_packet_priv = sk;
 
-       if (protocol) {
-               po->prot_hook.type = protocol;
+       if (proto) {
+               po->prot_hook.type = proto;
                dev_add_pack(&po->prot_hook);
                sock_hold(sk);
                po->running = 1;
@@ -1077,7 +1097,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
        skb=skb_recv_datagram(sk,flags,flags&MSG_DONTWAIT,&err);
 
        /*
-        *      An error occurred so return it. Because skb_recv_datagram() 
+        *      An error occurred so return it. Because skb_recv_datagram()
         *      handles the blocking we don't see and worry about blocking
         *      retries.
         */
@@ -1090,7 +1110,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
         *      it in now.
         */
 
-       sll = (struct sockaddr_ll*)skb->cb;
+       sll = &PACKET_SKB_CB(skb)->sa.ll;
        if (sock->type == SOCK_PACKET)
                msg->msg_namelen = sizeof(struct sockaddr_pkt);
        else
@@ -1115,7 +1135,22 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
        sock_recv_timestamp(msg, sk, skb);
 
        if (msg->msg_name)
-               memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+               memcpy(msg->msg_name, &PACKET_SKB_CB(skb)->sa,
+                      msg->msg_namelen);
+
+       if (pkt_sk(sk)->auxdata) {
+               struct tpacket_auxdata aux;
+
+               aux.tp_status = TP_STATUS_USER;
+               if (skb->ip_summed == CHECKSUM_PARTIAL)
+                       aux.tp_status |= TP_STATUS_CSUMNOTREADY;
+               aux.tp_len = PACKET_SKB_CB(skb)->origlen;
+               aux.tp_snaplen = skb->len;
+               aux.tp_mac = 0;
+               aux.tp_net = skb->nh.raw - skb->data;
+
+               put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux);
+       }
 
        /*
         *      Free or return the buffer as appropriate. Again this
@@ -1315,6 +1350,7 @@ static int
 packet_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
 {
        struct sock *sk = sock->sk;
+       struct packet_sock *po = pkt_sk(sk);
        int ret;
 
        if (level != SOL_PACKET)
@@ -1322,7 +1358,7 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
 
        switch(optname) {
 #ifdef CONFIG_PACKET_MULTICAST
-       case PACKET_ADD_MEMBERSHIP:     
+       case PACKET_ADD_MEMBERSHIP:
        case PACKET_DROP_MEMBERSHIP:
        {
                struct packet_mreq_max mreq;
@@ -1367,6 +1403,18 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
                return 0;
        }
 #endif
+       case PACKET_AUXDATA:
+       {
+               int val;
+
+               if (optlen < sizeof(val))
+                       return -EINVAL;
+               if (copy_from_user(&val, optval, sizeof(val)))
+                       return -EFAULT;
+
+               po->auxdata = !!val;
+               return 0;
+       }
        default:
                return -ENOPROTOOPT;
        }
@@ -1376,8 +1424,11 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
                             char __user *optval, int __user *optlen)
 {
        int len;
+       int val;
        struct sock *sk = sock->sk;
        struct packet_sock *po = pkt_sk(sk);
+       void *data;
+       struct tpacket_stats st;
 
        if (level != SOL_PACKET)
                return -ENOPROTOOPT;
@@ -1387,12 +1438,9 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
 
        if (len < 0)
                return -EINVAL;
-               
+
        switch(optname) {
        case PACKET_STATISTICS:
-       {
-               struct tpacket_stats st;
-
                if (len > sizeof(struct tpacket_stats))
                        len = sizeof(struct tpacket_stats);
                spin_lock_bh(&sk->sk_receive_queue.lock);
@@ -1401,16 +1449,23 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
                spin_unlock_bh(&sk->sk_receive_queue.lock);
                st.tp_packets += st.tp_drops;
 
-               if (copy_to_user(optval, &st, len))
-                       return -EFAULT;
+               data = &st;
+               break;
+       case PACKET_AUXDATA:
+               if (len > sizeof(int))
+                       len = sizeof(int);
+               val = po->auxdata;
+
+               data = &val;
                break;
-       }
        default:
                return -ENOPROTOOPT;
        }
 
        if (put_user(len, optlen))
                return -EFAULT;
+       if (copy_to_user(optval, data, len))
+               return -EFAULT;
        return 0;
 }
 
@@ -1492,7 +1547,7 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd,
                }
                case SIOCGSTAMP:
                        return sock_get_timestamp(sk, (struct timeval __user *)arg);
-                       
+
 #ifdef CONFIG_INET
                case SIOCADDRT:
                case SIOCDELRT:
@@ -1553,7 +1608,7 @@ static void packet_mm_open(struct vm_area_struct *vma)
        struct file *file = vma->vm_file;
        struct socket * sock = file->private_data;
        struct sock *sk = sock->sk;
-       
+
        if (sk)
                atomic_inc(&pkt_sk(sk)->mapped);
 }
@@ -1563,7 +1618,7 @@ static void packet_mm_close(struct vm_area_struct *vma)
        struct file *file = vma->vm_file;
        struct socket * sock = file->private_data;
        struct sock *sk = sock->sk;
-       
+
        if (sk)
                atomic_dec(&pkt_sk(sk)->mapped);
 }
@@ -1624,9 +1679,10 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing
 {
        char **pg_vec = NULL;
        struct packet_sock *po = pkt_sk(sk);
-       int was_running, num, order = 0;
+       int was_running, order = 0;
+       __be16 num;
        int err = 0;
-       
+
        if (req->tp_block_nr) {
                int i, l;
 
@@ -1688,7 +1744,7 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing
                __sock_put(sk);
        }
        spin_unlock(&po->bind_lock);
-               
+
        synchronize_net();
 
        err = -EBUSY;
@@ -1805,7 +1861,7 @@ static const struct proto_ops packet_ops = {
        .connect =      sock_no_connect,
        .socketpair =   sock_no_socketpair,
        .accept =       sock_no_accept,
-       .getname =      packet_getname, 
+       .getname =      packet_getname,
        .poll =         packet_poll,
        .ioctl =        packet_ioctl,
        .listen =       sock_no_listen,
@@ -1850,17 +1906,17 @@ static void *packet_seq_start(struct seq_file *seq, loff_t *pos)
 static void *packet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
        ++*pos;
-       return  (v == SEQ_START_TOKEN) 
-               ? sk_head(&packet_sklist) 
+       return  (v == SEQ_START_TOKEN)
+               ? sk_head(&packet_sklist)
                : sk_next((struct sock*)v) ;
 }
 
 static void packet_seq_stop(struct seq_file *seq, void *v)
 {
-       read_unlock(&packet_sklist_lock);               
+       read_unlock(&packet_sklist_lock);
 }
 
-static int packet_seq_show(struct seq_file *seq, void *v) 
+static int packet_seq_show(struct seq_file *seq, void *v)
 {
        if (v == SEQ_START_TOKEN)
                seq_puts(seq, "sk       RefCnt Type Proto  Iface R Rmem   User   Inode\n");