Kernel crash with wake_up_interruptible in "simple" device driver....

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hey all,

I am trying to write a kernel module which will intercept all incoming IP
packets for a particular subnet and queue them up for reading on a new
device.  The purpose is to allow me create a system which can emulate
hundreds of users.....

I have managed to create the firewall module which appears to correctly
intercept the packets, and I have written a read-only device module which
sends data to the reader.  However, when I combined the two, I get a kernel
crash/hang.  I have narrowed it down to the line marked "<------ CRASH
HERE!!!!!!!!" It occurs when I have queued the skb and am attempting to wake
up any sleeping readers.

I would appreciate any assistance which can be provided.

Thanks!!

----------------------  Source Code (sorry about the length)
----------------------

#define MODULE
#define __KERNEL__

#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/skbuff.h>
#include <linux/tcp.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/fcntl.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/poll.h>
#include <linux/wait.h>

#include <linux/sem.h>

#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif


/*******************************************************************\
**                                                                 **
** Globar variables                                                **
**                                                                 **
\*******************************************************************/

static struct sk_buff_head dev_q;				// queue to
hold intercepted packets
wait_queue_head_t waitq;						//
queue for process waiting for data

/*******************************************************************\
**                                                                 **
** Firewall Module Portion                                         **
**                                                                 **
\*******************************************************************/

// structs for intercepting the socket buffers
static struct nf_hook_ops rx_hook_ops;	// receive netfilter options struct

// These masks define the desired network
static unsigned long netmask = 0x001410ac;	// 172.16.20.0 in network
order
static int netmask_bits = 24;


/*
 * This function adds an intercepted packet to the device queue.
 */
void Add2Queue(struct sk_buff *skb)
{
	printk("Queueing packet: %d bytes - data\n", skb->len);
	skb_queue_tail(&dev_q, skb);	   // add the skb to my device queue
	wake_up_interruptible(&waitq);   // wake any sleeping readers
<------ CRASH HERE!!!!!!!!
	printk("Packet Queued.\n");
}


/*
 * This function hooks into the receive processing for network packets.
 * If the packet comes in on the tunnel, and is destined for the IPMN
 * subnet, the packet is intercepted and queued for reading on the
 * new device.
 */
static unsigned int rx_hook(unsigned int hooknum, struct sk_buff **skb,
	const struct net_device *in, const struct net_device *out,
	int (*okfn)(struct sk_buff *))
{
	char buf1[32], buf2[32];
	struct sk_buff *sock_buff;				// socket
kernel buffer

	sock_buff = *skb;

	if (!sock_buff)
	{
		printk("sock_buff == NULL\n");
		return NF_ACCEPT;
	}

	if (!sock_buff->nh.iph)
	{
		printk("Not an ip packet.\n");
		return NF_ACCEPT;
	}

	// verify the packet arrived on the tunnel interface
	if (!ks_strcmp(in->name, "tunl0"))
	{
		// if the packet is destined for the ipmn subnet
		if (InSubnet(sock_buff->nh.iph->daddr, netmask,
netmask_bits))
		{
			printk("Intercept: examining packet from %s to %s,
proto %d\n",
				ks_inetaddr(sock_buff->nh.iph->saddr, buf1),
				ks_inetaddr(sock_buff->nh.iph->daddr, buf2),
				sock_buff->nh.iph->protocol);

				printk("    Intercepting packet.\n");
				printk("    len %d, proto %d, ttl %d\n",
ntohs(sock_buff->nh.iph->tot_len),
					sock_buff->nh.iph->protocol,
sock_buff->nh.iph->ttl);
				Add2Queue(sock_buff);
				return NF_STOLEN;
		}
	}

	return NF_ACCEPT;
}


/*
 * This function initializes the firewall module for packet interception
 */
int init_fw_module(void)
{
	char buf[32];
	printk("Intercept Module loading.\n");

	skb_queue_head_init(&dev_q);

	// register hooks
	rx_hook_ops.hook = rx_hook;
	rx_hook_ops.hooknum = NF_IP_PRE_ROUTING;
	rx_hook_ops.pf = PF_INET;
	rx_hook_ops.priority = NF_IP_PRI_FIRST;

	// register the hook
	nf_register_hook(&rx_hook_ops);

	printk("Intercept Module loaded.\n");
	return 0;
}


/*
 * this function cleans up the firewall module.
 */
void cleanup_fw_module(void)
{
	nf_unregister_hook(&rx_hook_ops);
	printk("Intercept Module unloaded.\n");
}


/*******************************************************************\
**                                                                 **
** Read Only Device portion                                        **
**                                                                 **
\*******************************************************************/

// prototypes for the new device operations
int rodev_open(struct inode *inode, struct file *filp);
int rodev_release(struct inode *inode, struct file *filp);
ssize_t rodev_read(struct file *filp, char *user_buf, size_t count, loff_t
*offp);
ssize_t rodev_write(struct file*filp, const char *user_buff, size_t count,
loff_t *offp);
unsigned int rodev_poll(struct file *filp, poll_table *ptable);


int rodev_major = 0;		// read-only device major device number, set
for dynamic
struct file_operations rodev_fops = 		// device methods
{
	open: rodev_open,
	release: rodev_release,
	read: rodev_read,
	poll: rodev_poll
};

devfs_handle_t rodev_handle;
struct semaphore sem;

/*
 * this function is a kernel safe string length function.
 */
ssize_t ks_strlen(char *str)
{
	int len = 0 ;

	if (str)
	{
		while (*str++)
		{
			len++;
		}
	}
	return len;
}


/*
 * This function is called when the device is opened.
 */
int rodev_open(struct inode *inode, struct file *filp)
{
	int major = MAJOR(inode->i_rdev);
	int minor = MINOR(inode->i_rdev);

	MOD_INC_USE_COUNT;
	printk("<1>rodev: device (%d, %d) opened.\n", major, minor);
	return 0;
}


/*
 * This function is called when the device is closed for the final time.
 */
int rodev_release(struct inode *inode, struct file *filp)
{
	int major = MAJOR(inode->i_rdev);
	int minor = MINOR(inode->i_rdev);

	MOD_DEC_USE_COUNT;
	printk("<1>rodev: device (%d, %d) released.\n", major, minor);
	return 0;
}


/*
 * this function is called to read data from the device
 */
ssize_t rodev_read(struct file *filp, char *user_buf, size_t count, loff_t
*offp)
{
	int ret = 0;
	int len = count;
	struct sk_buff *skb;

	// check to see if data is available in the queue.  Put the process
to sleep
	// if not, unless the process specified non-blocking operation.
	while (skb_queue_len(&dev_q) == 0)
	{
		if (filp->f_flags & O_NONBLOCK)
		{
			return -EAGAIN;
		}

		if (wait_event_interruptible(waitq, (skb_queue_len(&dev_q)
!= 0)))
		{
			return -ERESTARTSYS;
		}
	}

	// data is available
	skb = skb_dequeue(&dev_q);
	if (skb)
	{
		if (skb->len < len)
		{
			len = skb->len;
		}
		if (copy_to_user(user_buf, skb->data, len))
		{
			printk("<1>RODEV ERROR: read copy_to_user
failed.\n");
			return -EFAULT;
		}
		kfree_skb(skb);
	}
	else
	{
		return -EFAULT;
	}

	printk("<1>rodev: read returned %d bytes\n", len);
	return 0;
}


/*
 * this function is used to support the poll() and select() operations.
 */
unsigned int rodev_poll(struct file *filp, poll_table *ptable)
{
	printk("<1>rodev: poll called.\n");
	poll_wait(filp, &waitq, ptable);
	if (skb_queue_len(&dev_q) > 0)			// if there is data
in the queue
	{
		return (POLLIN | POLLRDNORM);		// return normal
read okay
	}
	else
	{
		return 0;
// otherwise return block
	}
}


/*
 * This function sets up the read-only device
 */
int init_rodev(void)
{
	printk("<1>RODEV loading.\n");

	SET_MODULE_OWNER(&rodev_fops);
// set the owner in the file operations struct
	rodev_major = register_chrdev(0, "rodev", &rodev_fops);		//
register the new device
	if (rodev_major < 0)
// registration failed
	{
		printk("<1>RODEV ERROR: failed to register device.\n");
		return rodev_major;
	}
	printk("<1>rodev: major device id: %d\n", rodev_major);

	init_waitqueue_head(&waitq);
	printk("sizeof waitq %d, sizeof wait_queue_head_t %d\n",
sizeof(waitq), sizeof(wait_queue_head_t));

	printk("<1>RODEV loaded successfully.\n");
	return 0;
}


void cleanup_rodev(void)
{
	printk("<1>RODEV unloading.\n");

	// free the memory still held in any intercepted packets
	while (skb_queue_len(&dev_q) > 0)
	{
		struct sk_buff *skb = skb_dequeue(&dev_q);
		if (skb)
		{
			printk("<1>rodev: freeing skb at %p\n", skb);
			kfree_skb(skb);
		}
	}

	if (unregister_chrdev(rodev_major, "rodev") < 0)		//
unregister device
	{
		printk("<1>RODEV ERROR: Failed to unregister device.\n");
		return;
	}

	printk("<1>RODEV unloaded successfully.\n");
}


/*******************************************************************\
**                                                                 **
** Module Initialization                                           **
**                                                                 **
\*******************************************************************/

int init_module(void)
{
	int ret = 0;

	if ((ret = init_fw_module()) != 0)
	{
		return ret;
	}

	if ((ret = init_rodev()) != 0)
	{
		cleanup_fw_module();
		return ret;
	}

	return 0;
}


void cleanup_module(void)
{
	cleanup_fw_module();
	cleanup_rodev();
}

Russell Markus
Sr. Software Engineer
IPMobileNet, Inc.
rmarkus@xxxxxxxx


--
Kernelnewbies: Help each other learn about the Linux kernel.
Archive:       http://mail.nl.linux.org/kernelnewbies/
FAQ:           http://kernelnewbies.org/faq/


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux