X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=net%2Fipv6%2Fnetfilter%2Fip6_queue.c;h=8d366f7f2a9a5b3818bfdfddeddff079e7470880;hb=8eeee8b152ae6bbe181518efaf62ba8e9c613693;hp=968a14be0d0578eb17f48e2ea03018845cbd40f8;hpb=c7bdb545d23026b18be53289fd866d1ac07f5f8c;p=safe%2Fjmp%2Flinux-2.6 diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c index 968a14b..8d366f7 100644 --- a/net/ipv6/netfilter/ip6_queue.c +++ b/net/ipv6/netfilter/ip6_queue.c @@ -11,18 +11,6 @@ * 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. - * - * 2001-11-06: First try. Working with ip_queue.c for IPv4 and trying - * to adapt it to IPv6 - * HEAVILY based in ipqueue.c by James Morris. It's just - * a little modified version of it, so he's nearly the - * real coder of this. - * Few changes needed, mainly the hard_routing code and - * the netlink socket protocol (we're NETLINK_IP6_FW). - * 2002-06-25: Code cleanup. [JM: ported cleanup over from ip_queue.c] - * 2005-02-04: Added /proc counter for dropped packets; fixed so - * packets aren't delivered to user space if they're going - * to be dropped. */ #include #include @@ -35,10 +23,13 @@ #include #include #include +#include #include +#include #include #include #include +#include #include #include #include @@ -48,109 +39,46 @@ #define NET_IPQ_QMAX 2088 #define NET_IPQ_QMAX_NAME "ip6_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); +typedef int (*ipq_cmpfn)(struct nf_queue_entry *, unsigned long); -static unsigned char copy_mode = IPQ_COPY_NONE; -static unsigned int queue_maxlen = IPQ_QMAX_DEFAULT; +static unsigned char copy_mode __read_mostly = IPQ_COPY_NONE; +static unsigned int queue_maxlen __read_mostly = IPQ_QMAX_DEFAULT; static DEFINE_RWLOCK(queue_lock); -static int peer_pid; -static unsigned int copy_range; +static int peer_pid __read_mostly; +static unsigned int copy_range __read_mostly; static unsigned int queue_total; static unsigned int queue_dropped = 0; static unsigned int queue_user_dropped = 0; -static struct sock *ipqnl; +static struct sock *ipqnl __read_mostly; static LIST_HEAD(queue_list); static DEFINE_MUTEX(ipqnl_mutex); -static void -ipq_issue_verdict(struct ipq_queue_entry *entry, int verdict) -{ - 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) +__ipq_enqueue_entry(struct nf_queue_entry *entry) { - list_add(&entry->list, &queue_list); + list_add_tail(&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; @@ -158,57 +86,86 @@ __ipq_set_mode(unsigned char mode, unsigned int range) return status; } +static void __ipq_flush(ipq_cmpfn cmpfn, unsigned long data); + static inline void __ipq_reset(void) { peer_pid = 0; net_disable_timestamp(); __ipq_set_mode(IPQ_COPY_NONE, 0); - __ipq_flush(NF_DROP); + __ipq_flush(NULL, 0); } -static struct ipq_queue_entry * -ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data) +static struct nf_queue_entry * +ipq_find_dequeue_entry(unsigned long id) { - struct ipq_queue_entry *entry; - + struct nf_queue_entry *entry = NULL, *i; + write_lock_bh(&queue_lock); - entry = __ipq_find_dequeue_entry(cmpfn, data); + + list_for_each_entry(i, &queue_list, list) { + if ((unsigned long)i == id) { + entry = i; + break; + } + } + + if (entry) { + list_del(&entry->list); + queue_total--; + } + write_unlock_bh(&queue_lock); return entry; } static void -ipq_flush(int verdict) +__ipq_flush(ipq_cmpfn cmpfn, unsigned long data) +{ + struct nf_queue_entry *entry, *next; + + list_for_each_entry_safe(entry, next, &queue_list, list) { + if (!cmpfn || cmpfn(entry, data)) { + list_del(&entry->list); + queue_total--; + nf_reinject(entry, NF_DROP); + } + } +} + +static void +ipq_flush(ipq_cmpfn cmpfn, unsigned long data) { write_lock_bh(&queue_lock); - __ipq_flush(verdict); + __ipq_flush(cmpfn, data); write_unlock_bh(&queue_lock); } static struct sk_buff * -ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp) +ipq_build_packet_message(struct nf_queue_entry *entry, int *errp) { - unsigned char *old_tail; + sk_buff_data_t old_tail; size_t size = 0; size_t data_len = 0; struct sk_buff *skb; struct ipq_packet_msg *pmsg; struct nlmsghdr *nlh; + struct timeval tv; 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))) { + if ((entry->skb->ip_summed == CHECKSUM_PARTIAL || + entry->skb->ip_summed == CHECKSUM_COMPLETE) && + (*errp = skb_checksum_help(entry->skb))) { read_unlock_bh(&queue_lock); return NULL; } @@ -216,10 +173,10 @@ ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp) 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); @@ -231,42 +188,40 @@ ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp) skb = alloc_skb(size, GFP_ATOMIC); if (!skb) goto nlmsg_failure; - - old_tail= skb->tail; + + 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; + tv = ktime_to_timeval(entry->skb->tstamp); + pmsg->timestamp_sec = tv.tv_sec; + pmsg->timestamp_usec = tv.tv_usec; + pmsg->mark = entry->skb->mark; + pmsg->hook = entry->hook; pmsg->hw_protocol = entry->skb->protocol; - - if (entry->info->indev) - strcpy(pmsg->indev_name, entry->info->indev->name); + + if (entry->indev) + strcpy(pmsg->indev_name, entry->indev->name); else pmsg->indev_name[0] = '\0'; - - if (entry->info->outdev) - strcpy(pmsg->outdev_name, entry->info->outdev->name); + + if (entry->outdev) + strcpy(pmsg->outdev_name, entry->outdev->name); else pmsg->outdev_name[0] = '\0'; - - if (entry->info->indev && entry->skb->dev) { + + if (entry->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); + pmsg->hw_addrlen = dev_parse_header(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; @@ -279,133 +234,110 @@ nlmsg_failure: } static int -ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info, - unsigned int queuenum, void *data) +ipq_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) { 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 "ip6_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; - + return status; + write_lock_bh(&queue_lock); - + if (!peer_pid) - goto err_out_free_nskb; + goto err_out_free_nskb; if (queue_total >= queue_maxlen) { - queue_dropped++; + queue_dropped++; status = -ENOSPC; if (net_ratelimit()) - printk (KERN_WARNING "ip6_queue: fill at %d entries, " + printk (KERN_WARNING "ip6_queue: fill at %d entries, " "dropping packet(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 */ + /* 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++; + 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); - + kfree_skb(nskb); + err_out_unlock: write_unlock_bh(&queue_lock); - -err_out_free: - kfree(entry); return status; } static int -ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct ipq_queue_entry *e) +ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e) { int diff; struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload; + struct sk_buff *nskb; 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 (diff < 0) { + if (pskb_trim(e->skb, v->data_len)) + return -ENOMEM; + } 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) { + nskb = skb_copy_expand(e->skb, 0, + diff - skb_tailroom(e->skb), + GFP_ATOMIC); + if (!nskb) { printk(KERN_WARNING "ip6_queue: OOM " "in mangle, dropping packet\n"); return -ENOMEM; } - if (e->skb->sk) - skb_set_owner_w(newskb, e->skb->sk); kfree_skb(e->skb); - e->skb = newskb; + e->skb = nskb; } skb_put(e->skb, diff); } - if (!skb_make_writable(&e->skb, v->data_len)) + if (!skb_make_writable(e->skb, v->data_len)) return -ENOMEM; - memcpy(e->skb->data, v->payload, v->data_len); + skb_copy_to_linear_data(e->skb, v->payload, v->data_len); e->skb->ip_summed = CHECKSUM_NONE; return 0; } -static inline int -id_cmp(struct ipq_queue_entry *e, unsigned long id) -{ - return (id == (unsigned long )e); -} - static int ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len) { - struct ipq_queue_entry *entry; + struct nf_queue_entry *entry; if (vmsg->value > NF_MAX_VERDICT) return -EINVAL; - entry = ipq_find_dequeue_entry(id_cmp, vmsg->id); + entry = ipq_find_dequeue_entry(vmsg->id); if (entry == NULL) return -ENOENT; else { int verdict = vmsg->value; - + if (vmsg->data_len && vmsg->data_len == len) if (ipq_mangle_ipv6(vmsg, entry) < 0) verdict = NF_DROP; - - ipq_issue_verdict(entry, verdict); + + nf_reinject(entry, verdict); return 0; } } @@ -423,7 +355,7 @@ ipq_set_mode(unsigned char mode, unsigned int range) static int ipq_receive_peer(struct ipq_peer_msg *pmsg, - unsigned char type, unsigned int len) + unsigned char type, unsigned int len) { int status = 0; @@ -433,15 +365,15 @@ ipq_receive_peer(struct ipq_peer_msg *pmsg, switch (type) { case IPQM_MODE: status = ipq_set_mode(pmsg->msg.mode.value, - pmsg->msg.mode.range); + pmsg->msg.mode.range); break; - + case IPQM_VERDICT: if (pmsg->msg.verdict.value > NF_MAX_VERDICT) status = -EINVAL; else status = ipq_set_verdict(&pmsg->msg.verdict, - len - sizeof(*pmsg)); + len - sizeof(*pmsg)); break; default: status = -EINVAL; @@ -450,32 +382,38 @@ ipq_receive_peer(struct ipq_peer_msg *pmsg, } static int -dev_cmp(struct ipq_queue_entry *entry, unsigned long ifindex) +dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) { - if (entry->info->indev) - if (entry->info->indev->ifindex == ifindex) - return 1; - - if (entry->info->outdev) - if (entry->info->outdev->ifindex == ifindex) + if (entry->indev) + if (entry->indev->ifindex == ifindex) return 1; + if (entry->outdev) + if (entry->outdev->ifindex == ifindex) + return 1; +#ifdef CONFIG_BRIDGE_NETFILTER + if (entry->skb->nf_bridge) { + if (entry->skb->nf_bridge->physindev && + entry->skb->nf_bridge->physindev->ifindex == ifindex) + return 1; + if (entry->skb->nf_bridge->physoutdev && + entry->skb->nf_bridge->physoutdev->ifindex == ifindex) + return 1; + } +#endif return 0; } static void ipq_dev_drop(int ifindex) { - struct ipq_queue_entry *entry; - - while ((entry = ipq_find_dequeue_entry(dev_cmp, ifindex)) != NULL) - ipq_issue_verdict(entry, NF_DROP); + ipq_flush(dev_cmp, ifindex); } #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) static inline void -ipq_rcv_skb(struct sk_buff *skb) +__ipq_rcv_skb(struct sk_buff *skb) { int status, type, pid, flags, nlmsglen, skblen; struct nlmsghdr *nlh; @@ -484,32 +422,32 @@ ipq_rcv_skb(struct sk_buff *skb) if (skblen < sizeof(*nlh)) return; - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); nlmsglen = nlh->nlmsg_len; if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) return; pid = nlh->nlmsg_pid; flags = nlh->nlmsg_flags; - + if(pid <= 0 || !(flags & NLM_F_REQUEST) || flags & NLM_F_MULTI) RCV_SKB_FAIL(-EINVAL); - + if (flags & MSG_TRUNC) RCV_SKB_FAIL(-ECOMM); - + type = nlh->nlmsg_type; if (type < NLMSG_NOOP || type >= IPQM_MAX) RCV_SKB_FAIL(-EINVAL); - + if (type <= IPQM_BASE) return; - + if (security_netlink_recv(skb, CAP_NET_ADMIN)) - RCV_SKB_FAIL(-EPERM); + RCV_SKB_FAIL(-EPERM); write_lock_bh(&queue_lock); - + if (peer_pid) { if (peer_pid != pid) { write_unlock_bh(&queue_lock); @@ -519,42 +457,36 @@ ipq_rcv_skb(struct sk_buff *skb) net_enable_timestamp(); peer_pid = pid; } - + write_unlock_bh(&queue_lock); - + status = ipq_receive_peer(NLMSG_DATA(nlh), type, - nlmsglen - NLMSG_LENGTH(0)); + nlmsglen - NLMSG_LENGTH(0)); if (status < 0) RCV_SKB_FAIL(status); - + if (flags & NLM_F_ACK) netlink_ack(skb, nlh, 0); - return; + return; } static void -ipq_rcv_sk(struct sock *sk, int len) +ipq_rcv_skb(struct sk_buff *skb) { - struct sk_buff *skb; - unsigned int qlen; - mutex_lock(&ipqnl_mutex); - - for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) { - skb = skb_dequeue(&sk->sk_receive_queue); - ipq_rcv_skb(skb); - kfree_skb(skb); - } - + __ipq_rcv_skb(skb); mutex_unlock(&ipqnl_mutex); } static int ipq_rcv_dev_event(struct notifier_block *this, - unsigned long event, void *ptr) + unsigned long event, void *ptr) { struct net_device *dev = ptr; + if (dev->nd_net != &init_net) + return NOTIFY_DONE; + /* Drop any packets associated with the downed device */ if (event == NETDEV_DOWN) ipq_dev_drop(dev->ifindex); @@ -567,14 +499,14 @@ static struct notifier_block ipq_dev_notifier = { static int ipq_rcv_nl_event(struct notifier_block *this, - unsigned long event, void *ptr) + unsigned long event, void *ptr) { struct netlink_notify *n = ptr; if (event == NETLINK_URELEASE && n->protocol == NETLINK_IP6_FW && n->pid) { write_lock_bh(&queue_lock); - if (n->pid == peer_pid) + if ((n->net == &init_net) && (n->pid == peer_pid)) __ipq_reset(); write_unlock_bh(&queue_lock); } @@ -585,6 +517,7 @@ static struct notifier_block ipq_nl_notifier = { .notifier_call = ipq_rcv_nl_event, }; +#ifdef CONFIG_SYSCTL static struct ctl_table_header *ipq_sysctl_header; static ctl_table ipq_table[] = { @@ -596,64 +529,50 @@ static ctl_table ipq_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { .ctl_name = 0 } -}; - -static ctl_table ipq_dir_table[] = { - { - .ctl_name = NET_IPV6, - .procname = "ipv6", - .mode = 0555, - .child = ipq_table - }, { .ctl_name = 0 } }; +#endif -static ctl_table ipq_root_table[] = { - { - .ctl_name = CTL_NET, - .procname = "net", - .mode = 0555, - .child = ipq_dir_table - }, - { .ctl_name = 0 } -}; - -static int -ipq_get_info(char *buffer, char **start, off_t offset, int length) +#ifdef CONFIG_PROC_FS +static int ip6_queue_show(struct seq_file *m, void *v) { - int len; - read_lock_bh(&queue_lock); - - len = sprintf(buffer, - "Peer PID : %d\n" - "Copy mode : %hu\n" - "Copy range : %u\n" - "Queue length : %u\n" - "Queue max. length : %u\n" + + seq_printf(m, + "Peer PID : %d\n" + "Copy mode : %hu\n" + "Copy range : %u\n" + "Queue length : %u\n" + "Queue max. length : %u\n" "Queue dropped : %u\n" "Netfilter dropped : %u\n", - peer_pid, - copy_mode, - copy_range, - queue_total, - queue_maxlen, + peer_pid, + copy_mode, + copy_range, + queue_total, + queue_maxlen, queue_dropped, queue_user_dropped); read_unlock_bh(&queue_lock); - - *start = buffer + offset; - len -= offset; - if (len > length) - len = length; - else if (len < 0) - len = 0; - return len; + return 0; } -static struct nf_queue_handler nfqh = { +static int ip6_queue_open(struct inode *inode, struct file *file) +{ + return single_open(file, ip6_queue_show, NULL); +} + +static const struct file_operations ip6_queue_proc_fops = { + .open = ip6_queue_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; +#endif + +static const struct nf_queue_handler nfqh = { .name = "ip6_queue", .outfn = &ipq_enqueue_packet, }; @@ -661,27 +580,28 @@ static struct nf_queue_handler nfqh = { static int __init ip6_queue_init(void) { int status = -ENOMEM; - struct proc_dir_entry *proc; - + struct proc_dir_entry *proc __maybe_unused; + netlink_register_notifier(&ipq_nl_notifier); - ipqnl = netlink_kernel_create(NETLINK_IP6_FW, 0, ipq_rcv_sk, - THIS_MODULE); + ipqnl = netlink_kernel_create(&init_net, NETLINK_IP6_FW, 0, + ipq_rcv_skb, NULL, THIS_MODULE); if (ipqnl == NULL) { printk(KERN_ERR "ip6_queue: failed to create netlink socket\n"); goto cleanup_netlink_notifier; } - proc = proc_net_create(IPQ_PROC_FS_NAME, 0, ipq_get_info); - if (proc) - proc->owner = THIS_MODULE; - else { +#ifdef CONFIG_PROC_FS + proc = proc_create(IPQ_PROC_FS_NAME, 0, init_net.proc_net, + &ip6_queue_proc_fops); + if (!proc) { printk(KERN_ERR "ip6_queue: failed to create proc entry\n"); goto cleanup_ipqnl; } - +#endif register_netdevice_notifier(&ipq_dev_notifier); - ipq_sysctl_header = register_sysctl_table(ipq_root_table, 0); - +#ifdef CONFIG_SYSCTL + ipq_sysctl_header = register_sysctl_paths(net_ipv6_ctl_path, ipq_table); +#endif status = nf_register_queue_handler(PF_INET6, &nfqh); if (status < 0) { printk(KERN_ERR "ip6_queue: failed to register queue handler\n"); @@ -690,15 +610,17 @@ static int __init ip6_queue_init(void) return status; cleanup_sysctl: +#ifdef CONFIG_SYSCTL unregister_sysctl_table(ipq_sysctl_header); +#endif unregister_netdevice_notifier(&ipq_dev_notifier); - proc_net_remove(IPQ_PROC_FS_NAME); - -cleanup_ipqnl: - sock_release(ipqnl->sk_socket); + proc_net_remove(&init_net, IPQ_PROC_FS_NAME); + +cleanup_ipqnl: __maybe_unused + netlink_kernel_release(ipqnl); mutex_lock(&ipqnl_mutex); mutex_unlock(&ipqnl_mutex); - + cleanup_netlink_notifier: netlink_unregister_notifier(&ipq_nl_notifier); return status; @@ -708,13 +630,15 @@ static void __exit ip6_queue_fini(void) { nf_unregister_queue_handlers(&nfqh); synchronize_net(); - ipq_flush(NF_DROP); + ipq_flush(NULL, 0); +#ifdef CONFIG_SYSCTL unregister_sysctl_table(ipq_sysctl_header); +#endif unregister_netdevice_notifier(&ipq_dev_notifier); - proc_net_remove(IPQ_PROC_FS_NAME); + proc_net_remove(&init_net, IPQ_PROC_FS_NAME); - sock_release(ipqnl->sk_socket); + netlink_kernel_release(ipqnl); mutex_lock(&ipqnl_mutex); mutex_unlock(&ipqnl_mutex);