Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

Advertisement
[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Advertisement

Kernel v2.6.25-rc7 /net/can/raw.c

Filename:/net/can/raw.c
Lines Added:757
Lines Deleted:0
Also changed in: (Previous) 2.6.25-rc6  2.6.25-rc5  2.6.25-rc4  2.6.25-rc3  2.6.25-rc2  2.6.25-rc1 
(Following) 2.6.25-rc8  2.6.25-rc9  2.6.25  2.6.25-git2  2.6.25-git3  2.6.25-git4 

Location
[  2.6.25-rc7
  [  net
    [  can
       o  raw.c

Patch

diff --git a/net/can/raw.c b/net/can/raw.c
new file mode 100644
index 0000000..94cd7f2
--- /dev/null
+++ b/net/can/raw.c
@@ -0,0 +1,757 @@
+/*
+ * raw.c - Raw sockets for protocol family CAN
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/uio.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <linux/can/raw.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+
+#define CAN_RAW_VERSION CAN_VERSION
+static __initdata const char banner[] =
+   KERN_INFO "can: raw protocol (rev " CAN_RAW_VERSION ")\n";
+
+MODULE_DESCRIPTION("PF_CAN raw protocol");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
+
+#define MASK_ALL 0
+
+/*
+ * A raw socket has a list of can_filters attached to it, each receiving
+ * the CAN frames matching that filter.  If the filter list is empty,
+ * no CAN frames will be received by the socket.  The default after
+ * opening the socket, is to have one filter which receives all frames.
+ * The filter list is allocated dynamically with the exception of the
+ * list containing only one item.  This common case is optimized by
+ * storing the single filter in dfilter, to avoid using dynamic memory.
+ */
+
+struct raw_sock {
+   struct sock sk;
+   int bound;
+   int ifindex;
+   struct notifier_block notifier;
+   int loopback;
+   int recv_own_msgs;
+   int count;                 /* number of active filters */
+   struct can_filter dfilter; /* default/single filter */
+   struct can_filter *filter; /* pointer to filter(s) */
+   can_err_mask_t err_mask;
+};
+
+static inline struct raw_sock *raw_sk(const struct sock *sk)
+{
+   return (struct raw_sock *)sk;
+}
+
+static void raw_rcv(struct sk_buff *skb, void *data)
+{
+   struct sock *sk = (struct sock *)data;
+   struct raw_sock *ro = raw_sk(sk);
+   struct sockaddr_can *addr;
+
+   if (!ro->recv_own_msgs) {
+      /* check the received tx sock reference */
+      if (skb->sk == sk) {
+         kfree_skb(skb);
+         return;
+      }
+   }
+
+   /*
+    *  Put the datagram to the queue so that raw_recvmsg() can
+    *  get it from there.  We need to pass the interface index to
+    *  raw_recvmsg().  We pass a whole struct sockaddr_can in skb->cb
+    *  containing the interface index.
+    */
+
+   BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can));
+   addr = (struct sockaddr_can *)skb->cb;
+   memset(addr, 0, sizeof(*addr));
+   addr->can_family  = AF_CAN;
+   addr->can_ifindex = skb->dev->ifindex;
+
+   if (sock_queue_rcv_skb(sk, skb) < 0)
+      kfree_skb(skb);
+}
+
+static int raw_enable_filters(struct net_device *dev, struct sock *sk,
+               struct can_filter *filter, int count)
+{
+   int err = 0;
+   int i;
+
+   for (i = 0; i < count; i++) {
+      err = can_rx_register(dev, filter[i].can_id,
+                  filter[i].can_mask,
+                  raw_rcv, sk, "raw");
+      if (err) {
+         /* clean up successfully registered filters */
+         while (--i >= 0)
+            can_rx_unregister(dev, filter[i].can_id,
+                    filter[i].can_mask,
+                    raw_rcv, sk);
+         break;
+      }
+   }
+
+   return err;
+}
+
+static int raw_enable_errfilter(struct net_device *dev, struct sock *sk,
+            can_err_mask_t err_mask)
+{
+   int err = 0;
+
+   if (err_mask)
+      err = can_rx_register(dev, 0, err_mask | CAN_ERR_FLAG,
+                  raw_rcv, sk, "raw");
+
+   return err;
+}
+
+static void raw_disable_filters(struct net_device *dev, struct sock *sk,
+               struct can_filter *filter, int count)
+{
+   int i;
+
+   for (i = 0; i < count; i++)
+      can_rx_unregister(dev, filter[i].can_id, filter[i].can_mask,
+              raw_rcv, sk);
+}
+
+static inline void raw_disable_errfilter(struct net_device *dev,
+                struct sock *sk,
+                can_err_mask_t err_mask)
+
+{
+   if (err_mask)
+      can_rx_unregister(dev, 0, err_mask | CAN_ERR_FLAG,
+              raw_rcv, sk);
+}
+
+static inline void raw_disable_allfilters(struct net_device *dev,
+                 struct sock *sk)
+{
+   struct raw_sock *ro = raw_sk(sk);
+
+   raw_disable_filters(dev, sk, ro->filter, ro->count);
+   raw_disable_errfilter(dev, sk, ro->err_mask);
+}
+
+static int raw_enable_allfilters(struct net_device *dev, struct sock *sk)
+{
+   struct raw_sock *ro = raw_sk(sk);
+   int err;
+
+   err = raw_enable_filters(dev, sk, ro->filter, ro->count);
+   if (!err) {
+      err = raw_enable_errfilter(dev, sk, ro->err_mask);
+      if (err)
+         raw_disable_filters(dev, sk, ro->filter, ro->count);
+   }
+
+   return err;
+}
+
+static int raw_notifier(struct notifier_block *nb,
+         unsigned long msg, void *data)
+{
+   struct net_device *dev = (struct net_device *)data;
+   struct raw_sock *ro = container_of(nb, struct raw_sock, notifier);
+   struct sock *sk = &ro->sk;
+
+   if (dev->nd_net != &init_net)
+      return NOTIFY_DONE;
+
+   if (dev->type != ARPHRD_CAN)
+      return NOTIFY_DONE;
+
+   if (ro->ifindex != dev->ifindex)
+      return NOTIFY_DONE;
+
+   switch (msg) {
+
+   case NETDEV_UNREGISTER:
+      lock_sock(sk);
+      /* remove current filters & unregister */
+      if (ro->bound)
+         raw_disable_allfilters(dev, sk);
+
+      if (ro->count > 1)
+         kfree(ro->filter);
+
+      ro->ifindex = 0;
+      ro->bound   = 0;
+      ro->count   = 0;
+      release_sock(sk);
+
+      sk->sk_err = ENODEV;
+      if (!sock_flag(sk, SOCK_DEAD))
+         sk->sk_error_report(sk);
+      break;
+
+   case NETDEV_DOWN:
+      sk->sk_err = ENETDOWN;
+      if (!sock_flag(sk, SOCK_DEAD))
+         sk->sk_error_report(sk);
+      break;
+   }
+
+   return NOTIFY_DONE;
+}
+
+static int raw_init(struct sock *sk)
+{
+   struct raw_sock *ro = raw_sk(sk);
+
+   ro->bound            = 0;
+   ro->ifindex          = 0;
+
+   /* set default filter to single entry dfilter */
+   ro->dfilter.can_id   = 0;
+   ro->dfilter.can_mask = MASK_ALL;
+   ro->filter           = &ro->dfilter;
+   ro->count            = 1;
+
+   /* set default loopback behaviour */
+   ro->loopback         = 1;
+   ro->recv_own_msgs    = 0;
+
+   /* set notifier */
+   ro->notifier.notifier_call = raw_notifier;
+
+   register_netdevice_notifier(&ro->notifier);
+
+   return 0;
+}
+
+static int raw_release(struct socket *sock)
+{
+   struct sock *sk = sock->sk;
+   struct raw_sock *ro = raw_sk(sk);
+
+   unregister_netdevice_notifier(&ro->notifier);
+
+   lock_sock(sk);
+
+   /* remove current filters & unregister */
+   if (ro->bound) {
+      if (ro->ifindex) {
+         struct net_device *dev;
+
+         dev = dev_get_by_index(&init_net, ro->ifindex);
+         if (dev) {
+            raw_disable_allfilters(dev, sk);
+            dev_put(dev);
+         }
+      } else
+         raw_disable_allfilters(NULL, sk);
+   }
+
+   if (ro->count > 1)
+      kfree(ro->filter);
+
+   ro->ifindex = 0;
+   ro->bound   = 0;
+   ro->count   = 0;
+
+   release_sock(sk);
+   sock_put(sk);
+
+   return 0;
+}
+
+static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+{
+   struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+   struct sock *sk = sock->sk;
+   struct raw_sock *ro = raw_sk(sk);
+   int ifindex;
+   int err = 0;
+   int notify_enetdown = 0;
+
+   if (len < sizeof(*addr))
+      return -EINVAL;
+
+   lock_sock(sk);
+
+   if (ro->bound && addr->can_ifindex == ro->ifindex)
+      goto out;
+
+   if (addr->can_ifindex) {
+      struct net_device *dev;
+
+      dev = dev_get_by_index(&init_net, addr->can_ifindex);
+      if (!dev) {
+         err = -ENODEV;
+         goto out;
+      }
+      if (dev->type != ARPHRD_CAN) {
+         dev_put(dev);
+         err = -ENODEV;
+         goto out;
+      }
+      if (!(dev->flags & IFF_UP))
+         notify_enetdown = 1;
+
+      ifindex = dev->ifindex;
+
+      /* filters set by default/setsockopt */
+      err = raw_enable_allfilters(dev, sk);
+      dev_put(dev);
+   } else {
+      ifindex = 0;
+
+      /* filters set by default/setsockopt */
+      err = raw_enable_allfilters(NULL, sk);
+   }
+
+   if (!err) {
+      if (ro->bound) {
+         /* unregister old filters */
+         if (ro->ifindex) {
+            struct net_device *dev;
+
+            dev = dev_get_by_index(&init_net, ro->ifindex);
+            if (dev) {
+               raw_disable_allfilters(dev, sk);
+               dev_put(dev);
+            }
+         } else
+            raw_disable_allfilters(NULL, sk);
+      }
+      ro->ifindex = ifindex;
+      ro->bound = 1;
+   }
+
+ out:
+   release_sock(sk);
+
+   if (notify_enetdown) {
+      sk->sk_err = ENETDOWN;
+      if (!sock_flag(sk, SOCK_DEAD))
+         sk->sk_error_report(sk);
+   }
+
+   return err;
+}
+
+static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
+             int *len, int peer)
+{
+   struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+   struct sock *sk = sock->sk;
+   struct raw_sock *ro = raw_sk(sk);
+
+   if (peer)
+      return -EOPNOTSUPP;
+
+   addr->can_family  = AF_CAN;
+   addr->can_ifindex = ro->ifindex;
+
+   *len = sizeof(*addr);
+
+   return 0;
+}
+
+static int raw_setsockopt(struct socket *sock, int level, int optname,
+           char __user *optval, int optlen)
+{
+   struct sock *sk = sock->sk;
+   struct raw_sock *ro = raw_sk(sk);
+   struct can_filter *filter = NULL;  /* dyn. alloc'ed filters */
+   struct can_filter sfilter;         /* single filter */
+   struct net_device *dev = NULL;
+   can_err_mask_t err_mask = 0;
+   int count = 0;
+   int err = 0;
+
+   if (level != SOL_CAN_RAW)
+      return -EINVAL;
+   if (optlen < 0)
+      return -EINVAL;
+
+   switch (optname) {
+
+   case CAN_RAW_FILTER:
+      if (optlen % sizeof(struct can_filter) != 0)
+         return -EINVAL;
+
+      count = optlen / sizeof(struct can_filter);
+
+      if (count > 1) {
+         /* filter does not fit into dfilter => alloc space */
+         filter = kmalloc(optlen, GFP_KERNEL);
+         if (!filter)
+            return -ENOMEM;
+
+         err = copy_from_user(filter, optval, optlen);
+         if (err) {
+            kfree(filter);
+            return err;
+         }
+      } else if (count == 1) {
+         err = copy_from_user(&sfilter, optval, optlen);
+         if (err)
+            return err;
+      }
+
+      lock_sock(sk);
+
+      if (ro->bound && ro->ifindex)
+         dev = dev_get_by_index(&init_net, ro->ifindex);
+
+      if (ro->bound) {
+         /* (try to) register the new filters */
+         if (count == 1)
+            err = raw_enable_filters(dev, sk, &sfilter, 1);
+         else
+            err = raw_enable_filters(dev, sk, filter,
+                      count);
+         if (err) {
+            if (count > 1)
+               kfree(filter);
+            goto out_fil;
+         }
+
+         /* remove old filter registrations */
+         raw_disable_filters(dev, sk, ro->filter, ro->count);
+      }
+
+      /* remove old filter space */
+      if (ro->count > 1)
+         kfree(ro->filter);
+
+      /* link new filters to the socket */
+      if (count == 1) {
+         /* copy filter data for single filter */
+         ro->dfilter = sfilter;
+         filter = &ro->dfilter;
+      }
+      ro->filter = filter;
+      ro->count  = count;
+
+ out_fil:
+      if (dev)
+         dev_put(dev);
+
+      release_sock(sk);
+
+      break;
+
+   case CAN_RAW_ERR_FILTER:
+      if (optlen != sizeof(err_mask))
+         return -EINVAL;
+
+      err = copy_from_user(&err_mask, optval, optlen);
+      if (err)
+         return err;
+
+      err_mask &= CAN_ERR_MASK;
+
+      lock_sock(sk);
+
+      if (ro->bound && ro->ifindex)
+         dev = dev_get_by_index(&init_net, ro->ifindex);
+
+      /* remove current error mask */
+      if (ro->bound) {
+         /* (try to) register the new err_mask */
+         err = raw_enable_errfilter(dev, sk, err_mask);
+
+         if (err)
+            goto out_err;
+
+         /* remove old err_mask registration */
+         raw_disable_errfilter(dev, sk, ro->err_mask);
+      }
+
+      /* link new err_mask to the socket */
+      ro->err_mask = err_mask;
+
+ out_err:
+      if (dev)
+         dev_put(dev);
+
+      release_sock(sk);
+
+      break;
+
+   case CAN_RAW_LOOPBACK:
+      if (optlen != sizeof(ro->loopback))
+         return -EINVAL;
+
+      err = copy_from_user(&ro->loopback, optval, optlen);
+
+      break;
+
+   case CAN_RAW_RECV_OWN_MSGS:
+      if (optlen != sizeof(ro->recv_own_msgs))
+         return -EINVAL;
+
+      err = copy_from_user(&ro->recv_own_msgs, optval, optlen);
+
+      break;
+
+   default:
+      return -ENOPROTOOPT;
+   }
+   return err;
+}
+
+static int raw_getsockopt(struct socket *sock, int level, int optname,
+           char __user *optval, int __user *optlen)
+{
+   struct sock *sk = sock->sk;
+   struct raw_sock *ro = raw_sk(sk);
+   int len;
+   void *val;
+   int err = 0;
+
+   if (level != SOL_CAN_RAW)
+      return -EINVAL;
+   if (get_user(len, optlen))
+      return -EFAULT;
+   if (len < 0)
+      return -EINVAL;
+
+   switch (optname) {
+
+   case CAN_RAW_FILTER:
+      lock_sock(sk);
+      if (ro->count > 0) {
+         int fsize = ro->count * sizeof(struct can_filter);
+         if (len > fsize)
+            len = fsize;
+         err = copy_to_user(optval, ro->filter, len);
+      } else
+         len = 0;
+      release_sock(sk);
+
+      if (!err)
+         err = put_user(len, optlen);
+      return err;
+
+   case CAN_RAW_ERR_FILTER:
+      if (len > sizeof(can_err_mask_t))
+         len = sizeof(can_err_mask_t);
+      val = &ro->err_mask;
+      break;
+
+   case CAN_RAW_LOOPBACK:
+      if (len > sizeof(int))
+         len = sizeof(int);
+      val = &ro->loopback;
+      break;
+
+   case CAN_RAW_RECV_OWN_MSGS:
+      if (len > sizeof(int))
+         len = sizeof(int);
+      val = &ro->recv_own_msgs;
+      break;
+
+   default:
+      return -ENOPROTOOPT;
+   }
+
+   if (put_user(len, optlen))
+      return -EFAULT;
+   if (copy_to_user(optval, val, len))
+      return -EFAULT;
+   return 0;
+}
+
+static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
+             struct msghdr *msg, size_t size)
+{
+   struct sock *sk = sock->sk;
+   struct raw_sock *ro = raw_sk(sk);
+   struct sk_buff *skb;
+   struct net_device *dev;
+   int ifindex;
+   int err;
+
+   if (msg->msg_name) {
+      struct sockaddr_can *addr =
+         (struct sockaddr_can *)msg->msg_name;
+
+      if (addr->can_family != AF_CAN)
+         return -EINVAL;
+
+      ifindex = addr->can_ifindex;
+   } else
+      ifindex = ro->ifindex;
+
+   dev = dev_get_by_index(&init_net, ifindex);
+   if (!dev)
+      return -ENXIO;
+
+   skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT,
+              &err);
+   if (!skb) {
+      dev_put(dev);
+      return err;
+   }
+
+   err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+   if (err < 0) {
+      kfree_skb(skb);
+      dev_put(dev);
+      return err;
+   }
+   skb->dev = dev;
+   skb->sk  = sk;
+
+   err = can_send(skb, ro->loopback);
+
+   dev_put(dev);
+
+   if (err)
+      return err;
+
+   return size;
+}
+
+static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
+             struct msghdr *msg, size_t size, int flags)
+{
+   struct sock *sk = sock->sk;
+   struct sk_buff *skb;
+   int err = 0;
+   int noblock;
+
+   noblock =  flags & MSG_DONTWAIT;
+   flags   &= ~MSG_DONTWAIT;
+
+   skb = skb_recv_datagram(sk, flags, noblock, &err);
+   if (!skb)
+      return err;
+
+   if (size < skb->len)
+      msg->msg_flags |= MSG_TRUNC;
+   else
+      size = skb->len;
+
+   err = memcpy_toiovec(msg->msg_iov, skb->data, size);
+   if (err < 0) {
+      skb_free_datagram(sk, skb);
+      return err;
+   }
+
+   sock_recv_timestamp(msg, sk, skb);
+
+   if (msg->msg_name) {
+      msg->msg_namelen = sizeof(struct sockaddr_can);
+      memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+   }
+
+   skb_free_datagram(sk, skb);
+
+   return size;
+}
+
+static struct proto_ops raw_ops __read_mostly = {
+   .family        = PF_CAN,
+   .release       = raw_release,
+   .bind          = raw_bind,
+   .connect       = sock_no_connect,
+   .socketpair    = sock_no_socketpair,
+   .accept        = sock_no_accept,
+   .getname       = raw_getname,
+   .poll          = datagram_poll,
+   .ioctl         = NULL,      /* use can_ioctl() from af_can.c */
+   .listen        = sock_no_listen,
+   .shutdown      = sock_no_shutdown,
+   .setsockopt    = raw_setsockopt,
+   .getsockopt    = raw_getsockopt,
+   .sendmsg       = raw_sendmsg,
+   .recvmsg       = raw_recvmsg,
+   .mmap          = sock_no_mmap,
+   .sendpage      = sock_no_sendpage,
+};
+
+static struct proto raw_proto __read_mostly = {
+   .name       = "CAN_RAW",
+   .owner      = THIS_MODULE,
+   .obj_size   = sizeof(struct raw_sock),
+   .init       = raw_init,
+};
+
+static struct can_proto raw_can_proto __read_mostly = {
+   .type       = SOCK_RAW,
+   .protocol   = CAN_RAW,
+   .capability = -1,
+   .ops        = &raw_ops,
+   .prot       = &raw_proto,
+};
+
+static __init int raw_module_init(void)
+{
+   int err;
+
+   printk(banner);
+
+   err = can_proto_register(&raw_can_proto);
+   if (err < 0)
+      printk(KERN_ERR "can: registration of raw protocol failed\n");
+
+   return err;
+}
+
+static __exit void raw_module_exit(void)
+{
+   can_proto_unregister(&raw_can_proto);
+}
+
+module_init(raw_module_init);
+module_exit(raw_module_exit);


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.