Loading include/linux/netfilter/nfnetlink_queue.h +1 −0 Original line number Original line Diff line number Diff line Loading @@ -81,5 +81,6 @@ enum nfqnl_attr_config { NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ __NFQA_CFG_MAX __NFQA_CFG_MAX }; }; #define NFQA_CFG_MAX (__NFQA_CFG_MAX-1) #endif /* _NFNETLINK_QUEUE_H */ #endif /* _NFNETLINK_QUEUE_H */ net/netfilter/nfnetlink_queue.c +220 −28 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/notifier.h> #include <linux/notifier.h> #include <linux/netdevice.h> #include <linux/netdevice.h> #include <linux/netfilter.h> #include <linux/netfilter.h> #include <linux/proc_fs.h> #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink.h> Loading Loading @@ -48,6 +49,7 @@ struct nfqnl_queue_entry { struct nfqnl_instance { struct nfqnl_instance { struct hlist_node hlist; /* global list of queues */ struct hlist_node hlist; /* global list of queues */ atomic_t use; int peer_pid; int peer_pid; unsigned int queue_maxlen; unsigned int queue_maxlen; Loading Loading @@ -105,17 +107,28 @@ __instance_lookup(u_int16_t queue_num) } } static struct nfqnl_instance * static struct nfqnl_instance * instance_lookup(u_int16_t queue_num) instance_lookup_get(u_int16_t queue_num) { { struct nfqnl_instance *inst; struct nfqnl_instance *inst; read_lock_bh(&instances_lock); read_lock_bh(&instances_lock); inst = __instance_lookup(queue_num); inst = __instance_lookup(queue_num); if (inst) atomic_inc(&inst->use); read_unlock_bh(&instances_lock); read_unlock_bh(&instances_lock); return inst; return inst; } } static void instance_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 * static struct nfqnl_instance * instance_create(u_int16_t queue_num, int pid) instance_create(u_int16_t queue_num, int pid) { { Loading @@ -141,6 +154,8 @@ instance_create(u_int16_t queue_num, int pid) inst->copy_range = 0xfffff; inst->copy_range = 0xfffff; inst->copy_mode = NFQNL_COPY_NONE; inst->copy_mode = NFQNL_COPY_NONE; atomic_set(&inst->id_sequence, 0); atomic_set(&inst->id_sequence, 0); /* needs to be two, since we _put() after creation */ atomic_set(&inst->use, 2); inst->lock = SPIN_LOCK_UNLOCKED; inst->lock = SPIN_LOCK_UNLOCKED; INIT_LIST_HEAD(&inst->queue_list); INIT_LIST_HEAD(&inst->queue_list); Loading Loading @@ -182,8 +197,8 @@ _instance_destroy2(struct nfqnl_instance *inst, int lock) /* then flush all pending skbs from the queue */ /* then flush all pending skbs from the queue */ nfqnl_flush(inst, NF_DROP); nfqnl_flush(inst, NF_DROP); /* and finally free the data structure */ /* and finally put the refcount */ kfree(inst); instance_put(inst); module_put(THIS_MODULE); module_put(THIS_MODULE); } } Loading Loading @@ -471,7 +486,7 @@ nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, QDEBUG("entered\n"); QDEBUG("entered\n"); queue = instance_lookup(queuenum); queue = instance_lookup_get(queuenum); if (!queue) { if (!queue) { QDEBUG("no queue instance matching\n"); QDEBUG("no queue instance matching\n"); return -EINVAL; return -EINVAL; Loading @@ -479,7 +494,8 @@ nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, if (queue->copy_mode == NFQNL_COPY_NONE) { if (queue->copy_mode == NFQNL_COPY_NONE) { QDEBUG("mode COPY_NONE, aborting\n"); QDEBUG("mode COPY_NONE, aborting\n"); return -EAGAIN; status = -EAGAIN; goto err_out_put; } } entry = kmalloc(sizeof(*entry), GFP_ATOMIC); entry = kmalloc(sizeof(*entry), GFP_ATOMIC); Loading @@ -487,7 +503,8 @@ nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, if (net_ratelimit()) if (net_ratelimit()) printk(KERN_ERR printk(KERN_ERR "nf_queue: OOM in nfqnl_enqueue_packet()\n"); "nf_queue: OOM in nfqnl_enqueue_packet()\n"); return -ENOMEM; status = -ENOMEM; goto err_out_put; } } entry->info = info; entry->info = info; Loading Loading @@ -523,6 +540,7 @@ nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, __enqueue_entry(queue, entry); __enqueue_entry(queue, entry); spin_unlock_bh(&queue->lock); spin_unlock_bh(&queue->lock); instance_put(queue); return status; return status; err_out_free_nskb: err_out_free_nskb: Loading @@ -533,6 +551,8 @@ nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, err_out_free: err_out_free: kfree(entry); kfree(entry); err_out_put: instance_put(queue); return status; return status; } } Loading Loading @@ -685,6 +705,12 @@ static struct notifier_block nfqnl_rtnl_notifier = { .notifier_call = nfqnl_rcv_nl_event, .notifier_call = nfqnl_rcv_nl_event, }; }; static const int nfqa_verdict_min[NFQA_MAX] = { [NFQA_VERDICT_HDR-1] = sizeof(struct nfqnl_msg_verdict_hdr), [NFQA_MARK-1] = sizeof(u_int32_t), [NFQA_PAYLOAD-1] = 0, }; static int static int nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) Loading @@ -696,26 +722,40 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, struct nfqnl_instance *queue; struct nfqnl_instance *queue; unsigned int verdict; unsigned int verdict; struct nfqnl_queue_entry *entry; struct nfqnl_queue_entry *entry; int err; if (nfattr_bad_size(nfqa, NFQA_MAX, nfqa_verdict_min)) { QDEBUG("bad attribute size\n"); return -EINVAL; } queue = instance_lookup(queue_num); queue = instance_lookup_get(queue_num); if (!queue) if (!queue) return -ENODEV; return -ENODEV; if (queue->peer_pid != NETLINK_CB(skb).pid) if (queue->peer_pid != NETLINK_CB(skb).pid) { return -EPERM; err = -EPERM; goto err_out_put; } if (!nfqa[NFQA_VERDICT_HDR-1]) if (!nfqa[NFQA_VERDICT_HDR-1]) { return -EINVAL; err = -EINVAL; goto err_out_put; } vhdr = NFA_DATA(nfqa[NFQA_VERDICT_HDR-1]); vhdr = NFA_DATA(nfqa[NFQA_VERDICT_HDR-1]); verdict = ntohl(vhdr->verdict); verdict = ntohl(vhdr->verdict); if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT) if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT) { return -EINVAL; err = -EINVAL; goto err_out_put; } entry = find_dequeue_entry(queue, id_cmp, ntohl(vhdr->id)); entry = find_dequeue_entry(queue, id_cmp, ntohl(vhdr->id)); if (entry == NULL) if (entry == NULL) { return -ENOENT; err = -ENOENT; goto err_out_put; } if (nfqa[NFQA_PAYLOAD-1]) { if (nfqa[NFQA_PAYLOAD-1]) { if (nfqnl_mangle(NFA_DATA(nfqa[NFQA_PAYLOAD-1]), if (nfqnl_mangle(NFA_DATA(nfqa[NFQA_PAYLOAD-1]), Loading @@ -727,7 +767,12 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, skb->nfmark = ntohl(*(u_int32_t *)NFA_DATA(nfqa[NFQA_MARK-1])); skb->nfmark = ntohl(*(u_int32_t *)NFA_DATA(nfqa[NFQA_MARK-1])); issue_verdict(entry, verdict); issue_verdict(entry, verdict); instance_put(queue); return 0; return 0; err_out_put: instance_put(queue); return err; } } static int static int Loading @@ -737,6 +782,11 @@ nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, return -ENOTSUPP; return -ENOTSUPP; } } static const int nfqa_cfg_min[NFQA_CFG_MAX] = { [NFQA_CFG_CMD-1] = sizeof(struct nfqnl_msg_config_cmd), [NFQA_CFG_PARAMS-1] = sizeof(struct nfqnl_msg_config_params), }; static int static int nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) Loading @@ -744,10 +794,16 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); u_int16_t queue_num = ntohs(nfmsg->res_id); u_int16_t queue_num = ntohs(nfmsg->res_id); struct nfqnl_instance *queue; struct nfqnl_instance *queue; int ret = 0; QDEBUG("entering for msg %u\n", NFNL_MSG_TYPE(nlh->nlmsg_type)); QDEBUG("entering for msg %u\n", NFNL_MSG_TYPE(nlh->nlmsg_type)); queue = instance_lookup(queue_num); if (nfattr_bad_size(nfqa, NFQA_CFG_MAX, nfqa_cfg_min)) { QDEBUG("bad attribute size\n"); return -EINVAL; } queue = instance_lookup_get(queue_num); if (nfqa[NFQA_CFG_CMD-1]) { if (nfqa[NFQA_CFG_CMD-1]) { struct nfqnl_msg_config_cmd *cmd; struct nfqnl_msg_config_cmd *cmd; cmd = NFA_DATA(nfqa[NFQA_CFG_CMD-1]); cmd = NFA_DATA(nfqa[NFQA_CFG_CMD-1]); Loading @@ -766,15 +822,17 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, if (!queue) if (!queue) return -ENODEV; return -ENODEV; if (queue->peer_pid != NETLINK_CB(skb).pid) if (queue->peer_pid != NETLINK_CB(skb).pid) { return -EPERM; ret = -EPERM; goto out_put; } instance_destroy(queue); instance_destroy(queue); break; break; case NFQNL_CFG_CMD_PF_BIND: case NFQNL_CFG_CMD_PF_BIND: QDEBUG("registering queue handler for pf=%u\n", QDEBUG("registering queue handler for pf=%u\n", ntohs(cmd->pf)); ntohs(cmd->pf)); return nf_register_queue_handler(ntohs(cmd->pf), ret = nf_register_queue_handler(ntohs(cmd->pf), nfqnl_enqueue_packet, nfqnl_enqueue_packet, NULL); NULL); Loading @@ -784,20 +842,23 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, ntohs(cmd->pf)); ntohs(cmd->pf)); /* This is a bug and a feature. We can unregister /* This is a bug and a feature. We can unregister * other handlers(!) */ * other handlers(!) */ return nf_unregister_queue_handler(ntohs(cmd->pf)); ret = nf_unregister_queue_handler(ntohs(cmd->pf)); break; break; default: default: return -EINVAL; ret = -EINVAL; break; } } } else { } else { if (!queue) { if (!queue) { QDEBUG("no config command, and no instance ENOENT\n"); QDEBUG("no config command, and no instance ENOENT\n"); return -ENOENT; ret = -ENOENT; goto out_put; } } if (queue->peer_pid != NETLINK_CB(skb).pid) { if (queue->peer_pid != NETLINK_CB(skb).pid) { QDEBUG("no config command, and wrong pid\n"); QDEBUG("no config command, and wrong pid\n"); return -EPERM; ret = -EPERM; goto out_put; } } } } Loading @@ -809,7 +870,9 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, ntohl(params->copy_range)); ntohl(params->copy_range)); } } return 0; out_put: instance_put(queue); return ret; } } static struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { static struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { Loading @@ -829,14 +892,132 @@ static struct nfnetlink_subsystem nfqnl_subsys = { .cb = nfqnl_cb, .cb = nfqnl_cb, }; }; #ifdef CONFIG_PROC_FS struct iter_state { unsigned int bucket; }; static struct hlist_node *get_first(struct seq_file *seq) { struct iter_state *st = seq->private; if (!st) return NULL; for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { if (!hlist_empty(&instance_table[st->bucket])) return instance_table[st->bucket].first; } return NULL; } static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h) { struct iter_state *st = seq->private; h = h->next; while (!h) { if (++st->bucket >= INSTANCE_BUCKETS) return NULL; h = instance_table[st->bucket].first; } return h; } static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos) { struct hlist_node *head; head = get_first(seq); if (head) while (pos && (head = get_next(seq, head))) pos--; return pos ? NULL : head; } static void *seq_start(struct seq_file *seq, loff_t *pos) { read_lock_bh(&instances_lock); return get_idx(seq, *pos); } static void *seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos)++; return get_next(s, v); } static void seq_stop(struct seq_file *s, void *v) { read_unlock_bh(&instances_lock); } static int seq_show(struct seq_file *s, void *v) { const struct nfqnl_instance *inst = v; return seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n", inst->queue_num, inst->peer_pid, inst->queue_total, inst->copy_mode, inst->copy_range, inst->queue_dropped, inst->queue_user_dropped, atomic_read(&inst->id_sequence), atomic_read(&inst->use)); } static struct seq_operations nfqnl_seq_ops = { .start = seq_start, .next = seq_next, .stop = seq_stop, .show = seq_show, }; static int nfqnl_open(struct inode *inode, struct file *file) { struct seq_file *seq; struct iter_state *is; int ret; is = kmalloc(sizeof(*is), GFP_KERNEL); if (!is) return -ENOMEM; memset(is, 0, sizeof(*is)); ret = seq_open(file, &nfqnl_seq_ops); if (ret < 0) goto out_free; seq = file->private_data; seq->private = is; return ret; out_free: kfree(is); return ret; } static struct file_operations nfqnl_file_ops = { .owner = THIS_MODULE, .open = nfqnl_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private, }; #endif /* PROC_FS */ static int static int init_or_cleanup(int init) init_or_cleanup(int init) { { int status = -ENOMEM; int i, status = -ENOMEM; #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_nfqueue; #endif if (!init) if (!init) goto cleanup; goto cleanup; for (i = 0; i < INSTANCE_BUCKETS; i++) INIT_HLIST_HEAD(&instance_table[i]); netlink_register_notifier(&nfqnl_rtnl_notifier); netlink_register_notifier(&nfqnl_rtnl_notifier); status = nfnetlink_subsys_register(&nfqnl_subsys); status = nfnetlink_subsys_register(&nfqnl_subsys); if (status < 0) { if (status < 0) { Loading @@ -844,14 +1025,25 @@ init_or_cleanup(int init) goto cleanup_netlink_notifier; goto cleanup_netlink_notifier; } } #ifdef CONFIG_PROC_FS proc_nfqueue = create_proc_entry("nfnetlink_queue", 0440, proc_net_netfilter); if (!proc_nfqueue) goto cleanup_subsys; proc_nfqueue->proc_fops = &nfqnl_file_ops; #endif register_netdevice_notifier(&nfqnl_dev_notifier); register_netdevice_notifier(&nfqnl_dev_notifier); return status; return status; cleanup: cleanup: nf_unregister_queue_handlers(nfqnl_enqueue_packet); nf_unregister_queue_handlers(nfqnl_enqueue_packet); unregister_netdevice_notifier(&nfqnl_dev_notifier); unregister_netdevice_notifier(&nfqnl_dev_notifier); #ifdef CONFIG_PROC_FS cleanup_subsys: #endif nfnetlink_subsys_unregister(&nfqnl_subsys); nfnetlink_subsys_unregister(&nfqnl_subsys); cleanup_netlink_notifier: cleanup_netlink_notifier: netlink_unregister_notifier(&nfqnl_rtnl_notifier); netlink_unregister_notifier(&nfqnl_rtnl_notifier); return status; return status; Loading Loading
include/linux/netfilter/nfnetlink_queue.h +1 −0 Original line number Original line Diff line number Diff line Loading @@ -81,5 +81,6 @@ enum nfqnl_attr_config { NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ __NFQA_CFG_MAX __NFQA_CFG_MAX }; }; #define NFQA_CFG_MAX (__NFQA_CFG_MAX-1) #endif /* _NFNETLINK_QUEUE_H */ #endif /* _NFNETLINK_QUEUE_H */
net/netfilter/nfnetlink_queue.c +220 −28 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/notifier.h> #include <linux/notifier.h> #include <linux/netdevice.h> #include <linux/netdevice.h> #include <linux/netfilter.h> #include <linux/netfilter.h> #include <linux/proc_fs.h> #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink.h> Loading Loading @@ -48,6 +49,7 @@ struct nfqnl_queue_entry { struct nfqnl_instance { struct nfqnl_instance { struct hlist_node hlist; /* global list of queues */ struct hlist_node hlist; /* global list of queues */ atomic_t use; int peer_pid; int peer_pid; unsigned int queue_maxlen; unsigned int queue_maxlen; Loading Loading @@ -105,17 +107,28 @@ __instance_lookup(u_int16_t queue_num) } } static struct nfqnl_instance * static struct nfqnl_instance * instance_lookup(u_int16_t queue_num) instance_lookup_get(u_int16_t queue_num) { { struct nfqnl_instance *inst; struct nfqnl_instance *inst; read_lock_bh(&instances_lock); read_lock_bh(&instances_lock); inst = __instance_lookup(queue_num); inst = __instance_lookup(queue_num); if (inst) atomic_inc(&inst->use); read_unlock_bh(&instances_lock); read_unlock_bh(&instances_lock); return inst; return inst; } } static void instance_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 * static struct nfqnl_instance * instance_create(u_int16_t queue_num, int pid) instance_create(u_int16_t queue_num, int pid) { { Loading @@ -141,6 +154,8 @@ instance_create(u_int16_t queue_num, int pid) inst->copy_range = 0xfffff; inst->copy_range = 0xfffff; inst->copy_mode = NFQNL_COPY_NONE; inst->copy_mode = NFQNL_COPY_NONE; atomic_set(&inst->id_sequence, 0); atomic_set(&inst->id_sequence, 0); /* needs to be two, since we _put() after creation */ atomic_set(&inst->use, 2); inst->lock = SPIN_LOCK_UNLOCKED; inst->lock = SPIN_LOCK_UNLOCKED; INIT_LIST_HEAD(&inst->queue_list); INIT_LIST_HEAD(&inst->queue_list); Loading Loading @@ -182,8 +197,8 @@ _instance_destroy2(struct nfqnl_instance *inst, int lock) /* then flush all pending skbs from the queue */ /* then flush all pending skbs from the queue */ nfqnl_flush(inst, NF_DROP); nfqnl_flush(inst, NF_DROP); /* and finally free the data structure */ /* and finally put the refcount */ kfree(inst); instance_put(inst); module_put(THIS_MODULE); module_put(THIS_MODULE); } } Loading Loading @@ -471,7 +486,7 @@ nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, QDEBUG("entered\n"); QDEBUG("entered\n"); queue = instance_lookup(queuenum); queue = instance_lookup_get(queuenum); if (!queue) { if (!queue) { QDEBUG("no queue instance matching\n"); QDEBUG("no queue instance matching\n"); return -EINVAL; return -EINVAL; Loading @@ -479,7 +494,8 @@ nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, if (queue->copy_mode == NFQNL_COPY_NONE) { if (queue->copy_mode == NFQNL_COPY_NONE) { QDEBUG("mode COPY_NONE, aborting\n"); QDEBUG("mode COPY_NONE, aborting\n"); return -EAGAIN; status = -EAGAIN; goto err_out_put; } } entry = kmalloc(sizeof(*entry), GFP_ATOMIC); entry = kmalloc(sizeof(*entry), GFP_ATOMIC); Loading @@ -487,7 +503,8 @@ nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, if (net_ratelimit()) if (net_ratelimit()) printk(KERN_ERR printk(KERN_ERR "nf_queue: OOM in nfqnl_enqueue_packet()\n"); "nf_queue: OOM in nfqnl_enqueue_packet()\n"); return -ENOMEM; status = -ENOMEM; goto err_out_put; } } entry->info = info; entry->info = info; Loading Loading @@ -523,6 +540,7 @@ nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, __enqueue_entry(queue, entry); __enqueue_entry(queue, entry); spin_unlock_bh(&queue->lock); spin_unlock_bh(&queue->lock); instance_put(queue); return status; return status; err_out_free_nskb: err_out_free_nskb: Loading @@ -533,6 +551,8 @@ nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, err_out_free: err_out_free: kfree(entry); kfree(entry); err_out_put: instance_put(queue); return status; return status; } } Loading Loading @@ -685,6 +705,12 @@ static struct notifier_block nfqnl_rtnl_notifier = { .notifier_call = nfqnl_rcv_nl_event, .notifier_call = nfqnl_rcv_nl_event, }; }; static const int nfqa_verdict_min[NFQA_MAX] = { [NFQA_VERDICT_HDR-1] = sizeof(struct nfqnl_msg_verdict_hdr), [NFQA_MARK-1] = sizeof(u_int32_t), [NFQA_PAYLOAD-1] = 0, }; static int static int nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) Loading @@ -696,26 +722,40 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, struct nfqnl_instance *queue; struct nfqnl_instance *queue; unsigned int verdict; unsigned int verdict; struct nfqnl_queue_entry *entry; struct nfqnl_queue_entry *entry; int err; if (nfattr_bad_size(nfqa, NFQA_MAX, nfqa_verdict_min)) { QDEBUG("bad attribute size\n"); return -EINVAL; } queue = instance_lookup(queue_num); queue = instance_lookup_get(queue_num); if (!queue) if (!queue) return -ENODEV; return -ENODEV; if (queue->peer_pid != NETLINK_CB(skb).pid) if (queue->peer_pid != NETLINK_CB(skb).pid) { return -EPERM; err = -EPERM; goto err_out_put; } if (!nfqa[NFQA_VERDICT_HDR-1]) if (!nfqa[NFQA_VERDICT_HDR-1]) { return -EINVAL; err = -EINVAL; goto err_out_put; } vhdr = NFA_DATA(nfqa[NFQA_VERDICT_HDR-1]); vhdr = NFA_DATA(nfqa[NFQA_VERDICT_HDR-1]); verdict = ntohl(vhdr->verdict); verdict = ntohl(vhdr->verdict); if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT) if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT) { return -EINVAL; err = -EINVAL; goto err_out_put; } entry = find_dequeue_entry(queue, id_cmp, ntohl(vhdr->id)); entry = find_dequeue_entry(queue, id_cmp, ntohl(vhdr->id)); if (entry == NULL) if (entry == NULL) { return -ENOENT; err = -ENOENT; goto err_out_put; } if (nfqa[NFQA_PAYLOAD-1]) { if (nfqa[NFQA_PAYLOAD-1]) { if (nfqnl_mangle(NFA_DATA(nfqa[NFQA_PAYLOAD-1]), if (nfqnl_mangle(NFA_DATA(nfqa[NFQA_PAYLOAD-1]), Loading @@ -727,7 +767,12 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, skb->nfmark = ntohl(*(u_int32_t *)NFA_DATA(nfqa[NFQA_MARK-1])); skb->nfmark = ntohl(*(u_int32_t *)NFA_DATA(nfqa[NFQA_MARK-1])); issue_verdict(entry, verdict); issue_verdict(entry, verdict); instance_put(queue); return 0; return 0; err_out_put: instance_put(queue); return err; } } static int static int Loading @@ -737,6 +782,11 @@ nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, return -ENOTSUPP; return -ENOTSUPP; } } static const int nfqa_cfg_min[NFQA_CFG_MAX] = { [NFQA_CFG_CMD-1] = sizeof(struct nfqnl_msg_config_cmd), [NFQA_CFG_PARAMS-1] = sizeof(struct nfqnl_msg_config_params), }; static int static int nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) Loading @@ -744,10 +794,16 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); u_int16_t queue_num = ntohs(nfmsg->res_id); u_int16_t queue_num = ntohs(nfmsg->res_id); struct nfqnl_instance *queue; struct nfqnl_instance *queue; int ret = 0; QDEBUG("entering for msg %u\n", NFNL_MSG_TYPE(nlh->nlmsg_type)); QDEBUG("entering for msg %u\n", NFNL_MSG_TYPE(nlh->nlmsg_type)); queue = instance_lookup(queue_num); if (nfattr_bad_size(nfqa, NFQA_CFG_MAX, nfqa_cfg_min)) { QDEBUG("bad attribute size\n"); return -EINVAL; } queue = instance_lookup_get(queue_num); if (nfqa[NFQA_CFG_CMD-1]) { if (nfqa[NFQA_CFG_CMD-1]) { struct nfqnl_msg_config_cmd *cmd; struct nfqnl_msg_config_cmd *cmd; cmd = NFA_DATA(nfqa[NFQA_CFG_CMD-1]); cmd = NFA_DATA(nfqa[NFQA_CFG_CMD-1]); Loading @@ -766,15 +822,17 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, if (!queue) if (!queue) return -ENODEV; return -ENODEV; if (queue->peer_pid != NETLINK_CB(skb).pid) if (queue->peer_pid != NETLINK_CB(skb).pid) { return -EPERM; ret = -EPERM; goto out_put; } instance_destroy(queue); instance_destroy(queue); break; break; case NFQNL_CFG_CMD_PF_BIND: case NFQNL_CFG_CMD_PF_BIND: QDEBUG("registering queue handler for pf=%u\n", QDEBUG("registering queue handler for pf=%u\n", ntohs(cmd->pf)); ntohs(cmd->pf)); return nf_register_queue_handler(ntohs(cmd->pf), ret = nf_register_queue_handler(ntohs(cmd->pf), nfqnl_enqueue_packet, nfqnl_enqueue_packet, NULL); NULL); Loading @@ -784,20 +842,23 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, ntohs(cmd->pf)); ntohs(cmd->pf)); /* This is a bug and a feature. We can unregister /* This is a bug and a feature. We can unregister * other handlers(!) */ * other handlers(!) */ return nf_unregister_queue_handler(ntohs(cmd->pf)); ret = nf_unregister_queue_handler(ntohs(cmd->pf)); break; break; default: default: return -EINVAL; ret = -EINVAL; break; } } } else { } else { if (!queue) { if (!queue) { QDEBUG("no config command, and no instance ENOENT\n"); QDEBUG("no config command, and no instance ENOENT\n"); return -ENOENT; ret = -ENOENT; goto out_put; } } if (queue->peer_pid != NETLINK_CB(skb).pid) { if (queue->peer_pid != NETLINK_CB(skb).pid) { QDEBUG("no config command, and wrong pid\n"); QDEBUG("no config command, and wrong pid\n"); return -EPERM; ret = -EPERM; goto out_put; } } } } Loading @@ -809,7 +870,9 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, ntohl(params->copy_range)); ntohl(params->copy_range)); } } return 0; out_put: instance_put(queue); return ret; } } static struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { static struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { Loading @@ -829,14 +892,132 @@ static struct nfnetlink_subsystem nfqnl_subsys = { .cb = nfqnl_cb, .cb = nfqnl_cb, }; }; #ifdef CONFIG_PROC_FS struct iter_state { unsigned int bucket; }; static struct hlist_node *get_first(struct seq_file *seq) { struct iter_state *st = seq->private; if (!st) return NULL; for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { if (!hlist_empty(&instance_table[st->bucket])) return instance_table[st->bucket].first; } return NULL; } static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h) { struct iter_state *st = seq->private; h = h->next; while (!h) { if (++st->bucket >= INSTANCE_BUCKETS) return NULL; h = instance_table[st->bucket].first; } return h; } static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos) { struct hlist_node *head; head = get_first(seq); if (head) while (pos && (head = get_next(seq, head))) pos--; return pos ? NULL : head; } static void *seq_start(struct seq_file *seq, loff_t *pos) { read_lock_bh(&instances_lock); return get_idx(seq, *pos); } static void *seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos)++; return get_next(s, v); } static void seq_stop(struct seq_file *s, void *v) { read_unlock_bh(&instances_lock); } static int seq_show(struct seq_file *s, void *v) { const struct nfqnl_instance *inst = v; return seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n", inst->queue_num, inst->peer_pid, inst->queue_total, inst->copy_mode, inst->copy_range, inst->queue_dropped, inst->queue_user_dropped, atomic_read(&inst->id_sequence), atomic_read(&inst->use)); } static struct seq_operations nfqnl_seq_ops = { .start = seq_start, .next = seq_next, .stop = seq_stop, .show = seq_show, }; static int nfqnl_open(struct inode *inode, struct file *file) { struct seq_file *seq; struct iter_state *is; int ret; is = kmalloc(sizeof(*is), GFP_KERNEL); if (!is) return -ENOMEM; memset(is, 0, sizeof(*is)); ret = seq_open(file, &nfqnl_seq_ops); if (ret < 0) goto out_free; seq = file->private_data; seq->private = is; return ret; out_free: kfree(is); return ret; } static struct file_operations nfqnl_file_ops = { .owner = THIS_MODULE, .open = nfqnl_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private, }; #endif /* PROC_FS */ static int static int init_or_cleanup(int init) init_or_cleanup(int init) { { int status = -ENOMEM; int i, status = -ENOMEM; #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_nfqueue; #endif if (!init) if (!init) goto cleanup; goto cleanup; for (i = 0; i < INSTANCE_BUCKETS; i++) INIT_HLIST_HEAD(&instance_table[i]); netlink_register_notifier(&nfqnl_rtnl_notifier); netlink_register_notifier(&nfqnl_rtnl_notifier); status = nfnetlink_subsys_register(&nfqnl_subsys); status = nfnetlink_subsys_register(&nfqnl_subsys); if (status < 0) { if (status < 0) { Loading @@ -844,14 +1025,25 @@ init_or_cleanup(int init) goto cleanup_netlink_notifier; goto cleanup_netlink_notifier; } } #ifdef CONFIG_PROC_FS proc_nfqueue = create_proc_entry("nfnetlink_queue", 0440, proc_net_netfilter); if (!proc_nfqueue) goto cleanup_subsys; proc_nfqueue->proc_fops = &nfqnl_file_ops; #endif register_netdevice_notifier(&nfqnl_dev_notifier); register_netdevice_notifier(&nfqnl_dev_notifier); return status; return status; cleanup: cleanup: nf_unregister_queue_handlers(nfqnl_enqueue_packet); nf_unregister_queue_handlers(nfqnl_enqueue_packet); unregister_netdevice_notifier(&nfqnl_dev_notifier); unregister_netdevice_notifier(&nfqnl_dev_notifier); #ifdef CONFIG_PROC_FS cleanup_subsys: #endif nfnetlink_subsys_unregister(&nfqnl_subsys); nfnetlink_subsys_unregister(&nfqnl_subsys); cleanup_netlink_notifier: cleanup_netlink_notifier: netlink_unregister_notifier(&nfqnl_rtnl_notifier); netlink_unregister_notifier(&nfqnl_rtnl_notifier); return status; return status; Loading