1128 lines · 25.9 KB · C
RawDownload
/* * This is a module which is used for queueing packets and communicating with * userspace via nfetlink. * * (C) 2005 by Harald Welte <laforge@netfilter.org> * * Based on the old ipv4-only ip_queue.c: * (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. * */#include <linux/module.h>#include <linux/skbuff.h>#include <linux/init.h>#include <linux/spinlock.h>#include <linux/notifier.h>#include <linux/netdevice.h>#include <linux/netfilter.h>#include <linux/proc_fs.h>#include <linux/netfilter_ipv4.h>#include <linux/netfilter_ipv6.h>#include <linux/netfilter/nfnetlink.h>#include <linux/netfilter/nfnetlink_queue.h>#include <linux/list.h>#include <net/sock.h> #include <asm/atomic.h> #ifdef CONFIG_BRIDGE_NETFILTER#include "../bridge/br_private.h"#endif #define NFQNL_QMAX_DEFAULT 1024 #if 0#define QDEBUG(x, args ...)	printk(KERN_DEBUG "%s(%d):%s():	" x, 	   \					__FILE__, __LINE__, __FUNCTION__,  \					## args)#else#define QDEBUG(x, ...)#endif struct nfqnl_queue_entry {	struct list_head list;	struct nf_info *info;	struct sk_buff *skb;	unsigned int id;}; struct nfqnl_instance {	struct hlist_node hlist;		/* global list of queues */	atomic_t use; 	int peer_pid;	unsigned int queue_maxlen;	unsigned int copy_range;	unsigned int queue_total;	unsigned int queue_dropped;	unsigned int queue_user_dropped; 	atomic_t id_sequence;			/* 'sequence' of pkt ids */ 	u_int16_t queue_num;			/* number of this queue */	u_int8_t copy_mode; 	spinlock_t lock; 	struct list_head queue_list;		/* packets in queue */}; typedef int (*nfqnl_cmpfn)(struct nfqnl_queue_entry *, unsigned long); static DEFINE_RWLOCK(instances_lock); #define INSTANCE_BUCKETS	16static struct hlist_head instance_table[INSTANCE_BUCKETS]; static inline u_int8_t instance_hashfn(u_int16_t queue_num){	return ((queue_num >> 8) | queue_num) % INSTANCE_BUCKETS;} static struct nfqnl_instance *__instance_lookup(u_int16_t queue_num){	struct hlist_head *head;	struct hlist_node *pos;	struct nfqnl_instance *inst; 	head = &instance_table[instance_hashfn(queue_num)];	hlist_for_each_entry(inst, pos, head, hlist) {		if (inst->queue_num == queue_num)			return inst;	}	return NULL;} static struct nfqnl_instance *instance_lookup_get(u_int16_t queue_num){	struct nfqnl_instance *inst; 	read_lock_bh(&instances_lock);	inst = __instance_lookup(queue_num);	if (inst)		atomic_inc(&inst->use);	read_unlock_bh(&instances_lock); 	return inst;} static voidinstance_put(struct nfqnl_instance *inst){	if (inst && atomic_dec_and_test(&inst->use)) {		QDEBUG("kfree(inst=%p)\n", inst);		kfree(inst);	}} static struct nfqnl_instance *instance_create(u_int16_t queue_num, int pid){	struct nfqnl_instance *inst; 	QDEBUG("entering for queue_num=%u, pid=%d\n", queue_num, pid); 	write_lock_bh(&instances_lock);		if (__instance_lookup(queue_num)) {		inst = NULL;		QDEBUG("aborting, instance already exists\n");		goto out_unlock;	} 	inst = kzalloc(sizeof(*inst), GFP_ATOMIC);	if (!inst)		goto out_unlock; 	inst->queue_num = queue_num;	inst->peer_pid = pid;	inst->queue_maxlen = NFQNL_QMAX_DEFAULT;	inst->copy_range = 0xfffff;	inst->copy_mode = NFQNL_COPY_NONE;	atomic_set(&inst->id_sequence, 0);	/* needs to be two, since we _put() after creation */	atomic_set(&inst->use, 2);	spin_lock_init(&inst->lock);	INIT_LIST_HEAD(&inst->queue_list); 	if (!try_module_get(THIS_MODULE))		goto out_free; 	hlist_add_head(&inst->hlist, 		       &instance_table[instance_hashfn(queue_num)]); 	write_unlock_bh(&instances_lock); 	QDEBUG("successfully created new instance\n"); 	return inst; out_free:	kfree(inst);out_unlock:	write_unlock_bh(&instances_lock);	return NULL;} static void nfqnl_flush(struct nfqnl_instance *queue, int verdict); static void_instance_destroy2(struct nfqnl_instance *inst, int lock){	/* first pull it out of the global list */	if (lock)		write_lock_bh(&instances_lock); 	QDEBUG("removing instance %p (queuenum=%u) from hash\n",		inst, inst->queue_num);	hlist_del(&inst->hlist); 	if (lock)		write_unlock_bh(&instances_lock); 	/* then flush all pending skbs from the queue */	nfqnl_flush(inst, NF_DROP); 	/* and finally put the refcount */	instance_put(inst); 	module_put(THIS_MODULE);} static inline void__instance_destroy(struct nfqnl_instance *inst){	_instance_destroy2(inst, 0);} static inline voidinstance_destroy(struct nfqnl_instance *inst){	_instance_destroy2(inst, 1);}   static voidissue_verdict(struct nfqnl_queue_entry *entry, int verdict){	QDEBUG("entering for entry %p, verdict %u\n", entry, verdict); 	/* TCP input path (and probably other bits) assume to be called	 * from softirq context, not from syscall, like 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__enqueue_entry(struct nfqnl_instance *queue,		      struct nfqnl_queue_entry *entry){       list_add(&entry->list, &queue->queue_list);       queue->queue_total++;} /* * Find and return a queued entry matched by cmpfn, or return the last * entry if cmpfn is NULL. */static inline struct nfqnl_queue_entry *__find_entry(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, 		   unsigned long data){	struct list_head *p; 	list_for_each_prev(p, &queue->queue_list) {		struct nfqnl_queue_entry *entry = (struct nfqnl_queue_entry *)p;				if (!cmpfn || cmpfn(entry, data))			return entry;	}	return NULL;} static inline void__dequeue_entry(struct nfqnl_instance *q, struct nfqnl_queue_entry *entry){	list_del(&entry->list);	q->queue_total--;} static inline struct nfqnl_queue_entry *__find_dequeue_entry(struct nfqnl_instance *queue,		     nfqnl_cmpfn cmpfn, unsigned long data){	struct nfqnl_queue_entry *entry; 	entry = __find_entry(queue, cmpfn, data);	if (entry == NULL)		return NULL; 	__dequeue_entry(queue, entry);	return entry;}  static inline void__nfqnl_flush(struct nfqnl_instance *queue, int verdict){	struct nfqnl_queue_entry *entry;		while ((entry = __find_dequeue_entry(queue, NULL, 0)))		issue_verdict(entry, verdict);} static inline int__nfqnl_set_mode(struct nfqnl_instance *queue,		 unsigned char mode, unsigned int range){	int status = 0;		switch (mode) {	case NFQNL_COPY_NONE:	case NFQNL_COPY_META:		queue->copy_mode = mode;		queue->copy_range = 0;		break;			case NFQNL_COPY_PACKET:		queue->copy_mode = mode;		/* we're using struct nfattr which has 16bit nfa_len */		if (range > 0xffff)			queue->copy_range = 0xffff;		else			queue->copy_range = range;		break;			default:		status = -EINVAL; 	}	return status;} static struct nfqnl_queue_entry *find_dequeue_entry(struct nfqnl_instance *queue,			 nfqnl_cmpfn cmpfn, unsigned long data){	struct nfqnl_queue_entry *entry;		spin_lock_bh(&queue->lock);	entry = __find_dequeue_entry(queue, cmpfn, data);	spin_unlock_bh(&queue->lock); 	return entry;} static voidnfqnl_flush(struct nfqnl_instance *queue, int verdict){	spin_lock_bh(&queue->lock);	__nfqnl_flush(queue, verdict);	spin_unlock_bh(&queue->lock);} static struct sk_buff *nfqnl_build_packet_message(struct nfqnl_instance *queue,			   struct nfqnl_queue_entry *entry, int *errp){	unsigned char *old_tail;	size_t size;	size_t data_len = 0;	struct sk_buff *skb;	struct nfqnl_msg_packet_hdr pmsg;	struct nlmsghdr *nlh;	struct nfgenmsg *nfmsg;	struct nf_info *entinf = entry->info;	struct sk_buff *entskb = entry->skb;	struct net_device *indev;	struct net_device *outdev;	unsigned int tmp_uint; 	QDEBUG("entered\n"); 	/* all macros expand to constant values at compile time */	size =    NLMSG_SPACE(sizeof(struct nfgenmsg)) +		+ NFA_SPACE(sizeof(struct nfqnl_msg_packet_hdr))		+ NFA_SPACE(sizeof(u_int32_t))	/* ifindex */		+ NFA_SPACE(sizeof(u_int32_t))	/* ifindex */#ifdef CONFIG_BRIDGE_NETFILTER		+ NFA_SPACE(sizeof(u_int32_t))	/* ifindex */		+ NFA_SPACE(sizeof(u_int32_t))	/* ifindex */#endif		+ NFA_SPACE(sizeof(u_int32_t))	/* mark */		+ NFA_SPACE(sizeof(struct nfqnl_msg_packet_hw))		+ NFA_SPACE(sizeof(struct nfqnl_msg_packet_timestamp)); 	outdev = entinf->outdev; 	spin_lock_bh(&queue->lock);		switch (queue->copy_mode) {	case NFQNL_COPY_META:	case NFQNL_COPY_NONE:		data_len = 0;		break;		case NFQNL_COPY_PACKET:		if (entskb->ip_summed == CHECKSUM_HW &&		    (*errp = skb_checksum_help(entskb,		                               outdev == NULL))) {			spin_unlock_bh(&queue->lock);			return NULL;		}		if (queue->copy_range == 0 		    || queue->copy_range > entskb->len)			data_len = entskb->len;		else			data_len = queue->copy_range;				size += NFA_SPACE(data_len);		break;		default:		*errp = -EINVAL;		spin_unlock_bh(&queue->lock);		return NULL;	} 	spin_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, 			NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET,			sizeof(struct nfgenmsg));	nfmsg = NLMSG_DATA(nlh);	nfmsg->nfgen_family = entinf->pf;	nfmsg->version = NFNETLINK_V0;	nfmsg->res_id = htons(queue->queue_num); 	pmsg.packet_id 		= htonl(entry->id);	pmsg.hw_protocol	= htons(entskb->protocol);	pmsg.hook		= entinf->hook; 	NFA_PUT(skb, NFQA_PACKET_HDR, sizeof(pmsg), &pmsg); 	indev = entinf->indev;	if (indev) {		tmp_uint = htonl(indev->ifindex);#ifndef CONFIG_BRIDGE_NETFILTER		NFA_PUT(skb, NFQA_IFINDEX_INDEV, sizeof(tmp_uint), &tmp_uint);#else		if (entinf->pf == PF_BRIDGE) {			/* Case 1: indev is physical input device, we need to			 * look for bridge group (when called from 			 * netfilter_bridge) */			NFA_PUT(skb, NFQA_IFINDEX_PHYSINDEV, sizeof(tmp_uint), 				&tmp_uint);			/* this is the bridge group "brX" */			tmp_uint = htonl(indev->br_port->br->dev->ifindex);			NFA_PUT(skb, NFQA_IFINDEX_INDEV, sizeof(tmp_uint),				&tmp_uint);		} else {			/* Case 2: indev is bridge group, we need to look for			 * physical device (when called from ipv4) */			NFA_PUT(skb, NFQA_IFINDEX_INDEV, sizeof(tmp_uint),				&tmp_uint);			if (entskb->nf_bridge			    && entskb->nf_bridge->physindev) {				tmp_uint = htonl(entskb->nf_bridge->physindev->ifindex);				NFA_PUT(skb, NFQA_IFINDEX_PHYSINDEV,					sizeof(tmp_uint), &tmp_uint);			}		}#endif	} 	if (outdev) {		tmp_uint = htonl(outdev->ifindex);#ifndef CONFIG_BRIDGE_NETFILTER		NFA_PUT(skb, NFQA_IFINDEX_OUTDEV, sizeof(tmp_uint), &tmp_uint);#else		if (entinf->pf == PF_BRIDGE) {			/* Case 1: outdev is physical output device, we need to			 * look for bridge group (when called from 			 * netfilter_bridge) */			NFA_PUT(skb, NFQA_IFINDEX_PHYSOUTDEV, sizeof(tmp_uint),				&tmp_uint);			/* this is the bridge group "brX" */			tmp_uint = htonl(outdev->br_port->br->dev->ifindex);			NFA_PUT(skb, NFQA_IFINDEX_OUTDEV, sizeof(tmp_uint),				&tmp_uint);		} else {			/* Case 2: outdev is bridge group, we need to look for			 * physical output device (when called from ipv4) */			NFA_PUT(skb, NFQA_IFINDEX_OUTDEV, sizeof(tmp_uint),				&tmp_uint);			if (entskb->nf_bridge			    && entskb->nf_bridge->physoutdev) {				tmp_uint = htonl(entskb->nf_bridge->physoutdev->ifindex);				NFA_PUT(skb, NFQA_IFINDEX_PHYSOUTDEV,					sizeof(tmp_uint), &tmp_uint);			}		}#endif	} 	if (entskb->nfmark) {		tmp_uint = htonl(entskb->nfmark);		NFA_PUT(skb, NFQA_MARK, sizeof(u_int32_t), &tmp_uint);	} 	if (indev && entskb->dev	    && entskb->dev->hard_header_parse) {		struct nfqnl_msg_packet_hw phw; 		phw.hw_addrlen =			entskb->dev->hard_header_parse(entskb,			                                   phw.hw_addr);		phw.hw_addrlen = htons(phw.hw_addrlen);		NFA_PUT(skb, NFQA_HWADDR, sizeof(phw), &phw);	} 	if (entskb->tstamp.off_sec) {		struct nfqnl_msg_packet_timestamp ts;

Lines 1–500 of 1128.