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/