⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ip_queue.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * This is a module which is used for queueing IPv4 packets and * communicating with userspace via netlink. * * (C) 2000-2002 James Morris <jmorris@intercode.com.au> * (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 2000-03-27: Simplified code (thanks to Andi Kleen for clues). * 2000-05-20: Fixed notifier problems (following Miguel Freitas' report). * 2000-06-19: Fixed so nfmark is copied to metadata (reported by Sebastian  *             Zander). * 2000-08-01: Added Nick Williams' MAC support. * 2002-06-25: Code cleanup. * 2005-01-10: Added /proc counter for dropped packets; fixed so *             packets aren't delivered to user space if they're going  *             to be dropped.  * 2005-05-26: local_bh_{disable,enable} around nf_reinject (Harald Welte) * */#include <linux/module.h>#include <linux/skbuff.h>#include <linux/init.h>#include <linux/ip.h>#include <linux/notifier.h>#include <linux/netdevice.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv4/ip_queue.h>#include <linux/netfilter_ipv4/ip_tables.h>#include <linux/netlink.h>#include <linux/spinlock.h>#include <linux/sysctl.h>#include <linux/proc_fs.h>#include <linux/security.h>#include <net/sock.h>#include <net/route.h>#define IPQ_QMAX_DEFAULT 1024#define IPQ_PROC_FS_NAME "ip_queue"#define NET_IPQ_QMAX 2088#define NET_IPQ_QMAX_NAME "ip_queue_maxlen"struct ipq_queue_entry {	struct list_head list;	struct nf_info *info;	struct sk_buff *skb;};typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);static unsigned char copy_mode = IPQ_COPY_NONE;static unsigned int queue_maxlen = IPQ_QMAX_DEFAULT;static DEFINE_RWLOCK(queue_lock);static int peer_pid;static unsigned int copy_range;static unsigned int queue_total;static unsigned int queue_dropped = 0;static unsigned int queue_user_dropped = 0;static struct sock *ipqnl;static LIST_HEAD(queue_list);static DECLARE_MUTEX(ipqnl_sem);static voidipq_issue_verdict(struct ipq_queue_entry *entry, int verdict){	/* TCP input path (and probably other bits) assume to be called	 * from softirq context, not from syscall, like ipq_issue_verdict is	 * called.  TCP input path deadlocks with locks taken from timer	 * softirq, e.g.  We therefore emulate this by local_bh_disable() */	local_bh_disable();	nf_reinject(entry->skb, entry->info, verdict);	local_bh_enable();	kfree(entry);}static inline void__ipq_enqueue_entry(struct ipq_queue_entry *entry){       list_add(&entry->list, &queue_list);       queue_total++;}/* * Find and return a queued entry matched by cmpfn, or return the last * entry if cmpfn is NULL. */static inline struct ipq_queue_entry *__ipq_find_entry(ipq_cmpfn cmpfn, unsigned long data){	struct list_head *p;	list_for_each_prev(p, &queue_list) {		struct ipq_queue_entry *entry = (struct ipq_queue_entry *)p;				if (!cmpfn || cmpfn(entry, data))			return entry;	}	return NULL;}static inline void__ipq_dequeue_entry(struct ipq_queue_entry *entry){	list_del(&entry->list);	queue_total--;}static inline struct ipq_queue_entry *__ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data){	struct ipq_queue_entry *entry;	entry = __ipq_find_entry(cmpfn, data);	if (entry == NULL)		return NULL;	__ipq_dequeue_entry(entry);	return entry;}static inline void__ipq_flush(int verdict){	struct ipq_queue_entry *entry;		while ((entry = __ipq_find_dequeue_entry(NULL, 0)))		ipq_issue_verdict(entry, verdict);}static inline int__ipq_set_mode(unsigned char mode, unsigned int range){	int status = 0;		switch(mode) {	case IPQ_COPY_NONE:	case IPQ_COPY_META:		copy_mode = mode;		copy_range = 0;		break;			case IPQ_COPY_PACKET:		copy_mode = mode;		copy_range = range;		if (copy_range > 0xFFFF)			copy_range = 0xFFFF;		break;			default:		status = -EINVAL;	}	return status;}static inline void__ipq_reset(void){	peer_pid = 0;	net_disable_timestamp();	__ipq_set_mode(IPQ_COPY_NONE, 0);	__ipq_flush(NF_DROP);}static struct ipq_queue_entry *ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data){	struct ipq_queue_entry *entry;		write_lock_bh(&queue_lock);	entry = __ipq_find_dequeue_entry(cmpfn, data);	write_unlock_bh(&queue_lock);	return entry;}static voidipq_flush(int verdict){	write_lock_bh(&queue_lock);	__ipq_flush(verdict);	write_unlock_bh(&queue_lock);}static struct sk_buff *ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp){	unsigned char *old_tail;	size_t size = 0;	size_t data_len = 0;	struct sk_buff *skb;	struct ipq_packet_msg *pmsg;	struct nlmsghdr *nlh;	read_lock_bh(&queue_lock);		switch (copy_mode) {	case IPQ_COPY_META:	case IPQ_COPY_NONE:		size = NLMSG_SPACE(sizeof(*pmsg));		data_len = 0;		break;		case IPQ_COPY_PACKET:		if (entry->skb->ip_summed == CHECKSUM_HW &&		    (*errp = skb_checksum_help(entry->skb,		                               entry->info->outdev == NULL))) {			read_unlock_bh(&queue_lock);			return NULL;		}		if (copy_range == 0 || copy_range > entry->skb->len)			data_len = entry->skb->len;		else			data_len = copy_range;				size = NLMSG_SPACE(sizeof(*pmsg) + data_len);		break;		default:		*errp = -EINVAL;		read_unlock_bh(&queue_lock);		return NULL;	}	read_unlock_bh(&queue_lock);	skb = alloc_skb(size, GFP_ATOMIC);	if (!skb)		goto nlmsg_failure;			old_tail= skb->tail;	nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh));	pmsg = NLMSG_DATA(nlh);	memset(pmsg, 0, sizeof(*pmsg));	pmsg->packet_id       = (unsigned long )entry;	pmsg->data_len        = data_len;	pmsg->timestamp_sec   = entry->skb->tstamp.off_sec;	pmsg->timestamp_usec  = entry->skb->tstamp.off_usec;	pmsg->mark            = entry->skb->nfmark;	pmsg->hook            = entry->info->hook;	pmsg->hw_protocol     = entry->skb->protocol;		if (entry->info->indev)		strcpy(pmsg->indev_name, entry->info->indev->name);	else		pmsg->indev_name[0] = '\0';		if (entry->info->outdev)		strcpy(pmsg->outdev_name, entry->info->outdev->name);	else		pmsg->outdev_name[0] = '\0';		if (entry->info->indev && entry->skb->dev) {		pmsg->hw_type = entry->skb->dev->type;		if (entry->skb->dev->hard_header_parse)			pmsg->hw_addrlen =				entry->skb->dev->hard_header_parse(entry->skb,				                                   pmsg->hw_addr);	}		if (data_len)		if (skb_copy_bits(entry->skb, 0, pmsg->payload, data_len))			BUG();			nlh->nlmsg_len = skb->tail - old_tail;	return skb;nlmsg_failure:	if (skb)		kfree_skb(skb);	*errp = -EINVAL;	printk(KERN_ERR "ip_queue: error creating packet message\n");	return NULL;}static intipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info,		   unsigned int queuenum, void *data){	int status = -EINVAL;	struct sk_buff *nskb;	struct ipq_queue_entry *entry;	if (copy_mode == IPQ_COPY_NONE)		return -EAGAIN;	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);	if (entry == NULL) {		printk(KERN_ERR "ip_queue: OOM in ipq_enqueue_packet()\n");		return -ENOMEM;	}	entry->info = info;	entry->skb = skb;	nskb = ipq_build_packet_message(entry, &status);	if (nskb == NULL)		goto err_out_free;			write_lock_bh(&queue_lock);		if (!peer_pid)		goto err_out_free_nskb; 	if (queue_total >= queue_maxlen) {                queue_dropped++;		status = -ENOSPC;		if (net_ratelimit())		          printk (KERN_WARNING "ip_queue: full at %d entries, "				  "dropping packets(s). Dropped: %d\n", queue_total,				  queue_dropped);		goto err_out_free_nskb;	} 	/* netlink_unicast will either free the nskb or attach it to a socket */ 	status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT);	if (status < 0) {	        queue_user_dropped++;		goto err_out_unlock;	}	__ipq_enqueue_entry(entry);	write_unlock_bh(&queue_lock);	return status;err_out_free_nskb:	kfree_skb(nskb); 	err_out_unlock:	write_unlock_bh(&queue_lock);err_out_free:	kfree(entry);	return status;}static intipq_mangle_ipv4(ipq_verdict_msg_t *v, struct ipq_queue_entry *e){	int diff;	struct iphdr *user_iph = (struct iphdr *)v->payload;	if (v->data_len < sizeof(*user_iph))		return 0;	diff = v->data_len - e->skb->len;	if (diff < 0)		skb_trim(e->skb, v->data_len);	else if (diff > 0) {		if (v->data_len > 0xFFFF)			return -EINVAL;		if (diff > skb_tailroom(e->skb)) {			struct sk_buff *newskb;						newskb = skb_copy_expand(e->skb,			                         skb_headroom(e->skb),			                         diff,			                         GFP_ATOMIC);			if (newskb == NULL) {				printk(KERN_WARNING "ip_queue: OOM "				      "in mangle, dropping packet\n");				return -ENOMEM;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -