summaryrefslogtreecommitdiffstats
path: root/drivers/staging/batman-adv/icmp_socket.c
diff options
context:
space:
mode:
authorSven Eckelmann <sven.eckelmann@gmx.de>2010-06-22 01:25:42 +0200
committerGreg Kroah-Hartman <gregkh@suse.de>2010-06-22 14:05:03 -0700
commitc41214328a7635dc35aa81d89ea579c8a2eb2769 (patch)
treef93b668db3fd0270e06eff9a84a136fa34736f3f /drivers/staging/batman-adv/icmp_socket.c
parent1bd2c2159a32313ae1f872b6225601aed397524c (diff)
downloadop-kernel-dev-c41214328a7635dc35aa81d89ea579c8a2eb2769.zip
op-kernel-dev-c41214328a7635dc35aa81d89ea579c8a2eb2769.tar.gz
Staging: batman-adv: Move device for icmp injection to debugfs
batctl uses /dev/batman-adv to send special batman-adv icmp packets to other nodes in the mesh. To get it working with multiple batX devices we must ensure that every mesh device can have their own socket which is used to inject those packets in exactly one mesh. The current implementation still doesn't allow to use complete separated meshes as we rely on structures which are not part of the private data of a batman device. Signed-off-by: Sven Eckelmann <sven.eckelmann@gmx.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/batman-adv/icmp_socket.c')
-rw-r--r--drivers/staging/batman-adv/icmp_socket.c328
1 files changed, 328 insertions, 0 deletions
diff --git a/drivers/staging/batman-adv/icmp_socket.c b/drivers/staging/batman-adv/icmp_socket.c
new file mode 100644
index 0000000..d4411cb
--- /dev/null
+++ b/drivers/staging/batman-adv/icmp_socket.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include "main.h"
+#include "icmp_socket.h"
+#include "send.h"
+#include "types.h"
+#include "hash.h"
+#include "hard-interface.h"
+
+
+static struct socket_client *socket_client_hash[256];
+
+static void bat_socket_add_packet(struct socket_client *socket_client,
+ struct icmp_packet *icmp_packet);
+
+void bat_socket_init(void)
+{
+ memset(socket_client_hash, 0, sizeof(socket_client_hash));
+}
+
+static int bat_socket_open(struct inode *inode, struct file *file)
+{
+ unsigned int i;
+ struct socket_client *socket_client;
+
+ socket_client = kmalloc(sizeof(struct socket_client), GFP_KERNEL);
+
+ if (!socket_client)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(socket_client_hash); i++) {
+ if (!socket_client_hash[i]) {
+ socket_client_hash[i] = socket_client;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(socket_client_hash)) {
+ printk(KERN_ERR "batman-adv:"
+ "Error - can't add another packet client: "
+ "maximum number of clients reached\n");
+ kfree(socket_client);
+ return -EXFULL;
+ }
+
+ INIT_LIST_HEAD(&socket_client->queue_list);
+ socket_client->queue_len = 0;
+ socket_client->index = i;
+ spin_lock_init(&socket_client->lock);
+ init_waitqueue_head(&socket_client->queue_wait);
+
+ file->private_data = socket_client;
+
+ inc_module_count();
+ return 0;
+}
+
+static int bat_socket_release(struct inode *inode, struct file *file)
+{
+ struct socket_client *socket_client =
+ (struct socket_client *)file->private_data;
+ struct socket_packet *socket_packet;
+ struct list_head *list_pos, *list_pos_tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&socket_client->lock, flags);
+
+ /* for all packets in the queue ... */
+ list_for_each_safe(list_pos, list_pos_tmp, &socket_client->queue_list) {
+ socket_packet = list_entry(list_pos,
+ struct socket_packet, list);
+
+ list_del(list_pos);
+ kfree(socket_packet);
+ }
+
+ socket_client_hash[socket_client->index] = NULL;
+ spin_unlock_irqrestore(&socket_client->lock, flags);
+
+ kfree(socket_client);
+ dec_module_count();
+
+ return 0;
+}
+
+static ssize_t bat_socket_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct socket_client *socket_client =
+ (struct socket_client *)file->private_data;
+ struct socket_packet *socket_packet;
+ int error;
+ unsigned long flags;
+
+ if ((file->f_flags & O_NONBLOCK) && (socket_client->queue_len == 0))
+ return -EAGAIN;
+
+ if ((!buf) || (count < sizeof(struct icmp_packet)))
+ return -EINVAL;
+
+ if (!access_ok(VERIFY_WRITE, buf, count))
+ return -EFAULT;
+
+ error = wait_event_interruptible(socket_client->queue_wait,
+ socket_client->queue_len);
+
+ if (error)
+ return error;
+
+ spin_lock_irqsave(&socket_client->lock, flags);
+
+ socket_packet = list_first_entry(&socket_client->queue_list,
+ struct socket_packet, list);
+ list_del(&socket_packet->list);
+ socket_client->queue_len--;
+
+ spin_unlock_irqrestore(&socket_client->lock, flags);
+
+ error = __copy_to_user(buf, &socket_packet->icmp_packet,
+ sizeof(struct icmp_packet));
+
+ kfree(socket_packet);
+
+ if (error)
+ return -EFAULT;
+
+ return sizeof(struct icmp_packet);
+}
+
+static ssize_t bat_socket_write(struct file *file, const char __user *buff,
+ size_t len, loff_t *off)
+{
+ struct socket_client *socket_client =
+ (struct socket_client *)file->private_data;
+ struct icmp_packet icmp_packet;
+ struct orig_node *orig_node;
+ struct batman_if *batman_if;
+ uint8_t dstaddr[ETH_ALEN];
+ unsigned long flags;
+
+ if (len < sizeof(struct icmp_packet)) {
+ bat_dbg(DBG_BATMAN, "batman-adv:"
+ "Error - can't send packet from char device: "
+ "invalid packet size\n");
+ return -EINVAL;
+ }
+
+ if (!access_ok(VERIFY_READ, buff, sizeof(struct icmp_packet)))
+ return -EFAULT;
+
+ if (__copy_from_user(&icmp_packet, buff, sizeof(icmp_packet)))
+ return -EFAULT;
+
+ if (icmp_packet.packet_type != BAT_ICMP) {
+ bat_dbg(DBG_BATMAN, "batman-adv:"
+ "Error - can't send packet from char device: "
+ "got bogus packet type (expected: BAT_ICMP)\n");
+ return -EINVAL;
+ }
+
+ if (icmp_packet.msg_type != ECHO_REQUEST) {
+ bat_dbg(DBG_BATMAN, "batman-adv:"
+ "Error - can't send packet from char device: "
+ "got bogus message type (expected: ECHO_REQUEST)\n");
+ return -EINVAL;
+ }
+
+ icmp_packet.uid = socket_client->index;
+
+ if (icmp_packet.version != COMPAT_VERSION) {
+ icmp_packet.msg_type = PARAMETER_PROBLEM;
+ icmp_packet.ttl = COMPAT_VERSION;
+ bat_socket_add_packet(socket_client, &icmp_packet);
+ goto out;
+ }
+
+ if (atomic_read(&module_state) != MODULE_ACTIVE)
+ goto dst_unreach;
+
+ spin_lock_irqsave(&orig_hash_lock, flags);
+ orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet.dst));
+
+ if (!orig_node)
+ goto unlock;
+
+ if (!orig_node->router)
+ goto unlock;
+
+ batman_if = orig_node->router->if_incoming;
+ memcpy(dstaddr, orig_node->router->addr, ETH_ALEN);
+
+ spin_unlock_irqrestore(&orig_hash_lock, flags);
+
+ if (!batman_if)
+ goto dst_unreach;
+
+ if (batman_if->if_status != IF_ACTIVE)
+ goto dst_unreach;
+
+ memcpy(icmp_packet.orig,
+ batman_if->net_dev->dev_addr,
+ ETH_ALEN);
+
+ send_raw_packet((unsigned char *)&icmp_packet,
+ sizeof(struct icmp_packet),
+ batman_if, dstaddr);
+
+ goto out;
+
+unlock:
+ spin_unlock_irqrestore(&orig_hash_lock, flags);
+dst_unreach:
+ icmp_packet.msg_type = DESTINATION_UNREACHABLE;
+ bat_socket_add_packet(socket_client, &icmp_packet);
+out:
+ return len;
+}
+
+static unsigned int bat_socket_poll(struct file *file, poll_table *wait)
+{
+ struct socket_client *socket_client =
+ (struct socket_client *)file->private_data;
+
+ poll_wait(file, &socket_client->queue_wait, wait);
+
+ if (socket_client->queue_len > 0)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .open = bat_socket_open,
+ .release = bat_socket_release,
+ .read = bat_socket_read,
+ .write = bat_socket_write,
+ .poll = bat_socket_poll,
+};
+
+int bat_socket_setup(struct bat_priv *bat_priv)
+{
+ struct dentry *d;
+
+ if (!bat_priv->debug_dir)
+ goto err;
+
+ d = debugfs_create_file(ICMP_SOCKET, S_IFREG | S_IWUSR | S_IRUSR,
+ bat_priv->debug_dir, NULL, &fops);
+ if (d)
+ goto err;
+
+ return 0;
+
+err:
+ return 1;
+}
+
+static void bat_socket_add_packet(struct socket_client *socket_client,
+ struct icmp_packet *icmp_packet)
+{
+ struct socket_packet *socket_packet;
+ unsigned long flags;
+
+ socket_packet = kmalloc(sizeof(struct socket_packet), GFP_ATOMIC);
+
+ if (!socket_packet)
+ return;
+
+ INIT_LIST_HEAD(&socket_packet->list);
+ memcpy(&socket_packet->icmp_packet, icmp_packet,
+ sizeof(struct icmp_packet));
+
+ spin_lock_irqsave(&socket_client->lock, flags);
+
+ /* while waiting for the lock the socket_client could have been
+ * deleted */
+ if (!socket_client_hash[icmp_packet->uid]) {
+ spin_unlock_irqrestore(&socket_client->lock, flags);
+ kfree(socket_packet);
+ return;
+ }
+
+ list_add_tail(&socket_packet->list, &socket_client->queue_list);
+ socket_client->queue_len++;
+
+ if (socket_client->queue_len > 100) {
+ socket_packet = list_first_entry(&socket_client->queue_list,
+ struct socket_packet, list);
+
+ list_del(&socket_packet->list);
+ kfree(socket_packet);
+ socket_client->queue_len--;
+ }
+
+ spin_unlock_irqrestore(&socket_client->lock, flags);
+
+ wake_up(&socket_client->queue_wait);
+}
+
+void bat_socket_receive_packet(struct icmp_packet *icmp_packet)
+{
+ struct socket_client *hash = socket_client_hash[icmp_packet->uid];
+
+ if (hash)
+ bat_socket_add_packet(hash, icmp_packet);
+}
OpenPOWER on IntegriCloud