Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-next-2.6
[linux-2.6.git] / net / netfilter / nfnetlink_queue.c
index fdd2faf..49132bd 100644 (file)
@@ -58,7 +58,7 @@ struct nfqnl_instance {
  */
        spinlock_t      lock;
        unsigned int    queue_total;
-       atomic_t        id_sequence;            /* 'sequence' of pkt ids */
+       unsigned int    id_sequence;            /* 'sequence' of pkt ids */
        struct list_head queue_list;            /* packets in queue */
 };
 
@@ -171,6 +171,13 @@ __enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry)
        queue->queue_total++;
 }
 
+static void
+__dequeue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry)
+{
+       list_del(&entry->list);
+       queue->queue_total--;
+}
+
 static struct nf_queue_entry *
 find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id)
 {
@@ -185,10 +192,8 @@ find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id)
                }
        }
 
-       if (entry) {
-               list_del(&entry->list);
-               queue->queue_total--;
-       }
+       if (entry)
+               __dequeue_entry(queue, entry);
 
        spin_unlock_bh(&queue->lock);
 
@@ -213,13 +218,15 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data)
 
 static struct sk_buff *
 nfqnl_build_packet_message(struct nfqnl_instance *queue,
-                          struct nf_queue_entry *entry)
+                          struct nf_queue_entry *entry,
+                          __be32 **packet_id_ptr)
 {
        sk_buff_data_t old_tail;
        size_t size;
        size_t data_len = 0;
        struct sk_buff *skb;
-       struct nfqnl_msg_packet_hdr pmsg;
+       struct nlattr *nla;
+       struct nfqnl_msg_packet_hdr *pmsg;
        struct nlmsghdr *nlh;
        struct nfgenmsg *nfmsg;
        struct sk_buff *entskb = entry->skb;
@@ -272,12 +279,11 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
        nfmsg->version = NFNETLINK_V0;
        nfmsg->res_id = htons(queue->queue_num);
 
-       entry->id = atomic_inc_return(&queue->id_sequence);
-       pmsg.packet_id          = htonl(entry->id);
-       pmsg.hw_protocol        = entskb->protocol;
-       pmsg.hook               = entry->hook;
-
-       NLA_PUT(skb, NFQA_PACKET_HDR, sizeof(pmsg), &pmsg);
+       nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg));
+       pmsg = nla_data(nla);
+       pmsg->hw_protocol       = entskb->protocol;
+       pmsg->hook              = entry->hook;
+       *packet_id_ptr          = &pmsg->packet_id;
 
        indev = entry->indev;
        if (indev) {
@@ -389,6 +395,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
        struct sk_buff *nskb;
        struct nfqnl_instance *queue;
        int err = -ENOBUFS;
+       __be32 *packet_id_ptr;
 
        /* rcu_read_lock()ed by nf_hook_slow() */
        queue = instance_lookup(queuenum);
@@ -402,7 +409,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
                goto err_out;
        }
 
-       nskb = nfqnl_build_packet_message(queue, entry);
+       nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr);
        if (nskb == NULL) {
                err = -ENOMEM;
                goto err_out;
@@ -421,6 +428,8 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
                                 queue->queue_total);
                goto err_out_free_nskb;
        }
+       entry->id = ++queue->id_sequence;
+       *packet_id_ptr = htonl(entry->id);
 
        /* nfnetlink_unicast will either free the nskb or add it to a socket */
        err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT);
@@ -608,6 +617,92 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = {
        [NFQA_PAYLOAD]          = { .type = NLA_UNSPEC },
 };
 
+static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = {
+       [NFQA_VERDICT_HDR]      = { .len = sizeof(struct nfqnl_msg_verdict_hdr) },
+       [NFQA_MARK]             = { .type = NLA_U32 },
+};
+
+static struct nfqnl_instance *verdict_instance_lookup(u16 queue_num, int nlpid)
+{
+       struct nfqnl_instance *queue;
+
+       queue = instance_lookup(queue_num);
+       if (!queue)
+               return ERR_PTR(-ENODEV);
+
+       if (queue->peer_pid != nlpid)
+               return ERR_PTR(-EPERM);
+
+       return queue;
+}
+
+static struct nfqnl_msg_verdict_hdr*
+verdicthdr_get(const struct nlattr * const nfqa[])
+{
+       struct nfqnl_msg_verdict_hdr *vhdr;
+       unsigned int verdict;
+
+       if (!nfqa[NFQA_VERDICT_HDR])
+               return NULL;
+
+       vhdr = nla_data(nfqa[NFQA_VERDICT_HDR]);
+       verdict = ntohl(vhdr->verdict);
+       if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT)
+               return NULL;
+       return vhdr;
+}
+
+static int nfq_id_after(unsigned int id, unsigned int max)
+{
+       return (int)(id - max) > 0;
+}
+
+static int
+nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb,
+                  const struct nlmsghdr *nlh,
+                  const struct nlattr * const nfqa[])
+{
+       struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
+       struct nf_queue_entry *entry, *tmp;
+       unsigned int verdict, maxid;
+       struct nfqnl_msg_verdict_hdr *vhdr;
+       struct nfqnl_instance *queue;
+       LIST_HEAD(batch_list);
+       u16 queue_num = ntohs(nfmsg->res_id);
+
+       queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid);
+       if (IS_ERR(queue))
+               return PTR_ERR(queue);
+
+       vhdr = verdicthdr_get(nfqa);
+       if (!vhdr)
+               return -EINVAL;
+
+       verdict = ntohl(vhdr->verdict);
+       maxid = ntohl(vhdr->id);
+
+       spin_lock_bh(&queue->lock);
+
+       list_for_each_entry_safe(entry, tmp, &queue->queue_list, list) {
+               if (nfq_id_after(entry->id, maxid))
+                       break;
+               __dequeue_entry(queue, entry);
+               list_add_tail(&entry->list, &batch_list);
+       }
+
+       spin_unlock_bh(&queue->lock);
+
+       if (list_empty(&batch_list))
+               return -ENOENT;
+
+       list_for_each_entry_safe(entry, tmp, &batch_list, list) {
+               if (nfqa[NFQA_MARK])
+                       entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK]));
+               nf_reinject(entry, verdict);
+       }
+       return 0;
+}
+
 static int
 nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
                   const struct nlmsghdr *nlh,
@@ -620,39 +715,23 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
        struct nfqnl_instance *queue;
        unsigned int verdict;
        struct nf_queue_entry *entry;
-       int err;
 
-       rcu_read_lock();
        queue = instance_lookup(queue_num);
-       if (!queue) {
-               err = -ENODEV;
-               goto err_out_unlock;
-       }
+       if (!queue)
 
-       if (queue->peer_pid != NETLINK_CB(skb).pid) {
-               err = -EPERM;
-               goto err_out_unlock;
-       }
+       queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid);
+       if (IS_ERR(queue))
+               return PTR_ERR(queue);
 
-       if (!nfqa[NFQA_VERDICT_HDR]) {
-               err = -EINVAL;
-               goto err_out_unlock;
-       }
+       vhdr = verdicthdr_get(nfqa);
+       if (!vhdr)
+               return -EINVAL;
 
-       vhdr = nla_data(nfqa[NFQA_VERDICT_HDR]);
        verdict = ntohl(vhdr->verdict);
 
-       if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT) {
-               err = -EINVAL;
-               goto err_out_unlock;
-       }
-
        entry = find_dequeue_entry(queue, ntohl(vhdr->id));
-       if (entry == NULL) {
-               err = -ENOENT;
-               goto err_out_unlock;
-       }
-       rcu_read_unlock();
+       if (entry == NULL)
+               return -ENOENT;
 
        if (nfqa[NFQA_PAYLOAD]) {
                if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]),
@@ -665,10 +744,6 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
 
        nf_reinject(entry, verdict);
        return 0;
-
-err_out_unlock:
-       rcu_read_unlock();
-       return err;
 }
 
 static int
@@ -781,14 +856,17 @@ err_out_unlock:
 }
 
 static const struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = {
-       [NFQNL_MSG_PACKET]      = { .call = nfqnl_recv_unsupp,
+       [NFQNL_MSG_PACKET]      = { .call_rcu = nfqnl_recv_unsupp,
                                    .attr_count = NFQA_MAX, },
-       [NFQNL_MSG_VERDICT]     = { .call = nfqnl_recv_verdict,
+       [NFQNL_MSG_VERDICT]     = { .call_rcu = nfqnl_recv_verdict,
                                    .attr_count = NFQA_MAX,
                                    .policy = nfqa_verdict_policy },
        [NFQNL_MSG_CONFIG]      = { .call = nfqnl_recv_config,
                                    .attr_count = NFQA_CFG_MAX,
                                    .policy = nfqa_cfg_policy },
+       [NFQNL_MSG_VERDICT_BATCH]={ .call_rcu = nfqnl_recv_verdict_batch,
+                                   .attr_count = NFQA_MAX,
+                                   .policy = nfqa_verdict_batch_policy },
 };
 
 static const struct nfnetlink_subsystem nfqnl_subsys = {
@@ -870,7 +948,7 @@ static int seq_show(struct seq_file *s, void *v)
                          inst->peer_pid, inst->queue_total,
                          inst->copy_mode, inst->copy_range,
                          inst->queue_dropped, inst->queue_user_dropped,
-                         atomic_read(&inst->id_sequence), 1);
+                         inst->id_sequence, 1);
 }
 
 static const struct seq_operations nfqnl_seq_ops = {