aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2012-05-08 13:45:28 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2012-05-08 14:25:42 -0400
commitd16cf20e2f2f13411eece7f7fb72c17d141c4a84 (patch)
tree8154b3db8cdbb4b8d9f35d4c407cfe961253f0b4 /net/ipv6
parent6714cf5465d2803a21c6a46c1ea747795a8889fa (diff)
netfilter: remove ip_queue support
This patch removes ip_queue support which was marked as obsolete years ago. The nfnetlink_queue modules provides more advanced user-space packet queueing mechanism. This patch also removes capability code included in SELinux that refers to ip_queue. Otherwise, we break compilation. Several warning has been sent regarding this to the mailing list in the past month without anyone rising the hand to stop this with some strong argument. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/netfilter/Kconfig22
-rw-r--r--net/ipv6/netfilter/Makefile1
-rw-r--r--net/ipv6/netfilter/ip6_queue.c641
3 files changed, 0 insertions, 664 deletions
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index d33cddd16fbb..10135342799e 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -25,28 +25,6 @@ config NF_CONNTRACK_IPV6
25 25
26 To compile it as a module, choose M here. If unsure, say N. 26 To compile it as a module, choose M here. If unsure, say N.
27 27
28config IP6_NF_QUEUE
29 tristate "IP6 Userspace queueing via NETLINK (OBSOLETE)"
30 depends on INET && IPV6 && NETFILTER
31 depends on NETFILTER_ADVANCED
32 ---help---
33
34 This option adds a queue handler to the kernel for IPv6
35 packets which enables users to receive the filtered packets
36 with QUEUE target using libipq.
37
38 This option enables the old IPv6-only "ip6_queue" implementation
39 which has been obsoleted by the new "nfnetlink_queue" code (see
40 CONFIG_NETFILTER_NETLINK_QUEUE).
41
42 (C) Fernando Anton 2001
43 IPv64 Project - Work based in IPv64 draft by Arturo Azcorra.
44 Universidad Carlos III de Madrid
45 Universidad Politecnica de Alcala de Henares
46 email: <fanton@it.uc3m.es>.
47
48 To compile it as a module, choose M here. If unsure, say N.
49
50config IP6_NF_IPTABLES 28config IP6_NF_IPTABLES
51 tristate "IP6 tables support (required for filtering)" 29 tristate "IP6 tables support (required for filtering)"
52 depends on INET && IPV6 30 depends on INET && IPV6
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
index d4dfd0a21097..534d3f216f7b 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -6,7 +6,6 @@
6obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o 6obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o
7obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o 7obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o
8obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o 8obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o
9obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o
10obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o 9obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o
11obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o 10obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o
12 11
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
deleted file mode 100644
index 3ca9303b3a19..000000000000
--- a/net/ipv6/netfilter/ip6_queue.c
+++ /dev/null
@@ -1,641 +0,0 @@
1/*
2 * This is a module which is used for queueing IPv6 packets and
3 * communicating with userspace via netlink.
4 *
5 * (C) 2001 Fernando Anton, this code is GPL.
6 * IPv64 Project - Work based in IPv64 draft by Arturo Azcorra.
7 * Universidad Carlos III de Madrid - Leganes (Madrid) - Spain
8 * Universidad Politecnica de Alcala de Henares - Alcala de H. (Madrid) - Spain
9 * email: fanton@it.uc3m.es
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15#include <linux/module.h>
16#include <linux/skbuff.h>
17#include <linux/init.h>
18#include <linux/ipv6.h>
19#include <linux/notifier.h>
20#include <linux/netdevice.h>
21#include <linux/netfilter.h>
22#include <linux/netlink.h>
23#include <linux/spinlock.h>
24#include <linux/sysctl.h>
25#include <linux/proc_fs.h>
26#include <linux/seq_file.h>
27#include <linux/mutex.h>
28#include <linux/slab.h>
29#include <net/net_namespace.h>
30#include <net/sock.h>
31#include <net/ipv6.h>
32#include <net/ip6_route.h>
33#include <net/netfilter/nf_queue.h>
34#include <linux/netfilter_ipv4/ip_queue.h>
35#include <linux/netfilter_ipv4/ip_tables.h>
36#include <linux/netfilter_ipv6/ip6_tables.h>
37
38#define IPQ_QMAX_DEFAULT 1024
39#define IPQ_PROC_FS_NAME "ip6_queue"
40#define NET_IPQ_QMAX_NAME "ip6_queue_maxlen"
41
42typedef int (*ipq_cmpfn)(struct nf_queue_entry *, unsigned long);
43
44static unsigned char copy_mode __read_mostly = IPQ_COPY_NONE;
45static unsigned int queue_maxlen __read_mostly = IPQ_QMAX_DEFAULT;
46static DEFINE_SPINLOCK(queue_lock);
47static int peer_pid __read_mostly;
48static unsigned int copy_range __read_mostly;
49static unsigned int queue_total;
50static unsigned int queue_dropped = 0;
51static unsigned int queue_user_dropped = 0;
52static struct sock *ipqnl __read_mostly;
53static LIST_HEAD(queue_list);
54static DEFINE_MUTEX(ipqnl_mutex);
55
56static inline void
57__ipq_enqueue_entry(struct nf_queue_entry *entry)
58{
59 list_add_tail(&entry->list, &queue_list);
60 queue_total++;
61}
62
63static inline int
64__ipq_set_mode(unsigned char mode, unsigned int range)
65{
66 int status = 0;
67
68 switch(mode) {
69 case IPQ_COPY_NONE:
70 case IPQ_COPY_META:
71 copy_mode = mode;
72 copy_range = 0;
73 break;
74
75 case IPQ_COPY_PACKET:
76 if (range > 0xFFFF)
77 range = 0xFFFF;
78 copy_range = range;
79 copy_mode = mode;
80 break;
81
82 default:
83 status = -EINVAL;
84
85 }
86 return status;
87}
88
89static void __ipq_flush(ipq_cmpfn cmpfn, unsigned long data);
90
91static inline void
92__ipq_reset(void)
93{
94 peer_pid = 0;
95 net_disable_timestamp();
96 __ipq_set_mode(IPQ_COPY_NONE, 0);
97 __ipq_flush(NULL, 0);
98}
99
100static struct nf_queue_entry *
101ipq_find_dequeue_entry(unsigned long id)
102{
103 struct nf_queue_entry *entry = NULL, *i;
104
105 spin_lock_bh(&queue_lock);
106
107 list_for_each_entry(i, &queue_list, list) {
108 if ((unsigned long)i == id) {
109 entry = i;
110 break;
111 }
112 }
113
114 if (entry) {
115 list_del(&entry->list);
116 queue_total--;
117 }
118
119 spin_unlock_bh(&queue_lock);
120 return entry;
121}
122
123static void
124__ipq_flush(ipq_cmpfn cmpfn, unsigned long data)
125{
126 struct nf_queue_entry *entry, *next;
127
128 list_for_each_entry_safe(entry, next, &queue_list, list) {
129 if (!cmpfn || cmpfn(entry, data)) {
130 list_del(&entry->list);
131 queue_total--;
132 nf_reinject(entry, NF_DROP);
133 }
134 }
135}
136
137static void
138ipq_flush(ipq_cmpfn cmpfn, unsigned long data)
139{
140 spin_lock_bh(&queue_lock);
141 __ipq_flush(cmpfn, data);
142 spin_unlock_bh(&queue_lock);
143}
144
145static struct sk_buff *
146ipq_build_packet_message(struct nf_queue_entry *entry, int *errp)
147{
148 sk_buff_data_t old_tail;
149 size_t size = 0;
150 size_t data_len = 0;
151 struct sk_buff *skb;
152 struct ipq_packet_msg *pmsg;
153 struct nlmsghdr *nlh;
154 struct timeval tv;
155
156 switch (ACCESS_ONCE(copy_mode)) {
157 case IPQ_COPY_META:
158 case IPQ_COPY_NONE:
159 size = NLMSG_SPACE(sizeof(*pmsg));
160 break;
161
162 case IPQ_COPY_PACKET:
163 if (entry->skb->ip_summed == CHECKSUM_PARTIAL &&
164 (*errp = skb_checksum_help(entry->skb)))
165 return NULL;
166
167 data_len = ACCESS_ONCE(copy_range);
168 if (data_len == 0 || data_len > entry->skb->len)
169 data_len = entry->skb->len;
170
171 size = NLMSG_SPACE(sizeof(*pmsg) + data_len);
172 break;
173
174 default:
175 *errp = -EINVAL;
176 return NULL;
177 }
178
179 skb = alloc_skb(size, GFP_ATOMIC);
180 if (!skb)
181 goto nlmsg_failure;
182
183 old_tail = skb->tail;
184 nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh));
185 pmsg = NLMSG_DATA(nlh);
186 memset(pmsg, 0, sizeof(*pmsg));
187
188 pmsg->packet_id = (unsigned long )entry;
189 pmsg->data_len = data_len;
190 tv = ktime_to_timeval(entry->skb->tstamp);
191 pmsg->timestamp_sec = tv.tv_sec;
192 pmsg->timestamp_usec = tv.tv_usec;
193 pmsg->mark = entry->skb->mark;
194 pmsg->hook = entry->hook;
195 pmsg->hw_protocol = entry->skb->protocol;
196
197 if (entry->indev)
198 strcpy(pmsg->indev_name, entry->indev->name);
199 else
200 pmsg->indev_name[0] = '\0';
201
202 if (entry->outdev)
203 strcpy(pmsg->outdev_name, entry->outdev->name);
204 else
205 pmsg->outdev_name[0] = '\0';
206
207 if (entry->indev && entry->skb->dev &&
208 entry->skb->mac_header != entry->skb->network_header) {
209 pmsg->hw_type = entry->skb->dev->type;
210 pmsg->hw_addrlen = dev_parse_header(entry->skb, pmsg->hw_addr);
211 }
212
213 if (data_len)
214 if (skb_copy_bits(entry->skb, 0, pmsg->payload, data_len))
215 BUG();
216
217 nlh->nlmsg_len = skb->tail - old_tail;
218 return skb;
219
220nlmsg_failure:
221 kfree_skb(skb);
222 *errp = -EINVAL;
223 printk(KERN_ERR "ip6_queue: error creating packet message\n");
224 return NULL;
225}
226
227static int
228ipq_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
229{
230 int status = -EINVAL;
231 struct sk_buff *nskb;
232
233 if (copy_mode == IPQ_COPY_NONE)
234 return -EAGAIN;
235
236 nskb = ipq_build_packet_message(entry, &status);
237 if (nskb == NULL)
238 return status;
239
240 spin_lock_bh(&queue_lock);
241
242 if (!peer_pid)
243 goto err_out_free_nskb;
244
245 if (queue_total >= queue_maxlen) {
246 queue_dropped++;
247 status = -ENOSPC;
248 if (net_ratelimit())
249 printk (KERN_WARNING "ip6_queue: fill at %d entries, "
250 "dropping packet(s). Dropped: %d\n", queue_total,
251 queue_dropped);
252 goto err_out_free_nskb;
253 }
254
255 /* netlink_unicast will either free the nskb or attach it to a socket */
256 status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT);
257 if (status < 0) {
258 queue_user_dropped++;
259 goto err_out_unlock;
260 }
261
262 __ipq_enqueue_entry(entry);
263
264 spin_unlock_bh(&queue_lock);
265 return status;
266
267err_out_free_nskb:
268 kfree_skb(nskb);
269
270err_out_unlock:
271 spin_unlock_bh(&queue_lock);
272 return status;
273}
274
275static int
276ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
277{
278 int diff;
279 struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload;
280 struct sk_buff *nskb;
281
282 if (v->data_len < sizeof(*user_iph))
283 return 0;
284 diff = v->data_len - e->skb->len;
285 if (diff < 0) {
286 if (pskb_trim(e->skb, v->data_len))
287 return -ENOMEM;
288 } else if (diff > 0) {
289 if (v->data_len > 0xFFFF)
290 return -EINVAL;
291 if (diff > skb_tailroom(e->skb)) {
292 nskb = skb_copy_expand(e->skb, skb_headroom(e->skb),
293 diff, GFP_ATOMIC);
294 if (!nskb) {
295 printk(KERN_WARNING "ip6_queue: OOM "
296 "in mangle, dropping packet\n");
297 return -ENOMEM;
298 }
299 kfree_skb(e->skb);
300 e->skb = nskb;
301 }
302 skb_put(e->skb, diff);
303 }
304 if (!skb_make_writable(e->skb, v->data_len))
305 return -ENOMEM;
306 skb_copy_to_linear_data(e->skb, v->payload, v->data_len);
307 e->skb->ip_summed = CHECKSUM_NONE;
308
309 return 0;
310}
311
312static int
313ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len)
314{
315 struct nf_queue_entry *entry;
316
317 if (vmsg->value > NF_MAX_VERDICT || vmsg->value == NF_STOLEN)
318 return -EINVAL;
319
320 entry = ipq_find_dequeue_entry(vmsg->id);
321 if (entry == NULL)
322 return -ENOENT;
323 else {
324 int verdict = vmsg->value;
325
326 if (vmsg->data_len && vmsg->data_len == len)
327 if (ipq_mangle_ipv6(vmsg, entry) < 0)
328 verdict = NF_DROP;
329
330 nf_reinject(entry, verdict);
331 return 0;
332 }
333}
334
335static int
336ipq_set_mode(unsigned char mode, unsigned int range)
337{
338 int status;
339
340 spin_lock_bh(&queue_lock);
341 status = __ipq_set_mode(mode, range);
342 spin_unlock_bh(&queue_lock);
343 return status;
344}
345
346static int
347ipq_receive_peer(struct ipq_peer_msg *pmsg,
348 unsigned char type, unsigned int len)
349{
350 int status = 0;
351
352 if (len < sizeof(*pmsg))
353 return -EINVAL;
354
355 switch (type) {
356 case IPQM_MODE:
357 status = ipq_set_mode(pmsg->msg.mode.value,
358 pmsg->msg.mode.range);
359 break;
360
361 case IPQM_VERDICT:
362 status = ipq_set_verdict(&pmsg->msg.verdict,
363 len - sizeof(*pmsg));
364 break;
365 default:
366 status = -EINVAL;
367 }
368 return status;
369}
370
371static int
372dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)
373{
374 if (entry->indev)
375 if (entry->indev->ifindex == ifindex)
376 return 1;
377
378 if (entry->outdev)
379 if (entry->outdev->ifindex == ifindex)
380 return 1;
381#ifdef CONFIG_BRIDGE_NETFILTER
382 if (entry->skb->nf_bridge) {
383 if (entry->skb->nf_bridge->physindev &&
384 entry->skb->nf_bridge->physindev->ifindex == ifindex)
385 return 1;
386 if (entry->skb->nf_bridge->physoutdev &&
387 entry->skb->nf_bridge->physoutdev->ifindex == ifindex)
388 return 1;
389 }
390#endif
391 return 0;
392}
393
394static void
395ipq_dev_drop(int ifindex)
396{
397 ipq_flush(dev_cmp, ifindex);
398}
399
400#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)
401
402static inline void
403__ipq_rcv_skb(struct sk_buff *skb)
404{
405 int status, type, pid, flags;
406 unsigned int nlmsglen, skblen;
407 struct nlmsghdr *nlh;
408 bool enable_timestamp = false;
409
410 skblen = skb->len;
411 if (skblen < sizeof(*nlh))
412 return;
413
414 nlh = nlmsg_hdr(skb);
415 nlmsglen = nlh->nlmsg_len;
416 if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen)
417 return;
418
419 pid = nlh->nlmsg_pid;
420 flags = nlh->nlmsg_flags;
421
422 if(pid <= 0 || !(flags & NLM_F_REQUEST) || flags & NLM_F_MULTI)
423 RCV_SKB_FAIL(-EINVAL);
424
425 if (flags & MSG_TRUNC)
426 RCV_SKB_FAIL(-ECOMM);
427
428 type = nlh->nlmsg_type;
429 if (type < NLMSG_NOOP || type >= IPQM_MAX)
430 RCV_SKB_FAIL(-EINVAL);
431
432 if (type <= IPQM_BASE)
433 return;
434
435 if (!capable(CAP_NET_ADMIN))
436 RCV_SKB_FAIL(-EPERM);
437
438 spin_lock_bh(&queue_lock);
439
440 if (peer_pid) {
441 if (peer_pid != pid) {
442 spin_unlock_bh(&queue_lock);
443 RCV_SKB_FAIL(-EBUSY);
444 }
445 } else {
446 enable_timestamp = true;
447 peer_pid = pid;
448 }
449
450 spin_unlock_bh(&queue_lock);
451 if (enable_timestamp)
452 net_enable_timestamp();
453
454 status = ipq_receive_peer(NLMSG_DATA(nlh), type,
455 nlmsglen - NLMSG_LENGTH(0));
456 if (status < 0)
457 RCV_SKB_FAIL(status);
458
459 if (flags & NLM_F_ACK)
460 netlink_ack(skb, nlh, 0);
461}
462
463static void
464ipq_rcv_skb(struct sk_buff *skb)
465{
466 mutex_lock(&ipqnl_mutex);
467 __ipq_rcv_skb(skb);
468 mutex_unlock(&ipqnl_mutex);
469}
470
471static int
472ipq_rcv_dev_event(struct notifier_block *this,
473 unsigned long event, void *ptr)
474{
475 struct net_device *dev = ptr;
476
477 if (!net_eq(dev_net(dev), &init_net))
478 return NOTIFY_DONE;
479
480 /* Drop any packets associated with the downed device */
481 if (event == NETDEV_DOWN)
482 ipq_dev_drop(dev->ifindex);
483 return NOTIFY_DONE;
484}
485
486static struct notifier_block ipq_dev_notifier = {
487 .notifier_call = ipq_rcv_dev_event,
488};
489
490static int
491ipq_rcv_nl_event(struct notifier_block *this,
492 unsigned long event, void *ptr)
493{
494 struct netlink_notify *n = ptr;
495
496 if (event == NETLINK_URELEASE && n->protocol == NETLINK_IP6_FW) {
497 spin_lock_bh(&queue_lock);
498 if ((net_eq(n->net, &init_net)) && (n->pid == peer_pid))
499 __ipq_reset();
500 spin_unlock_bh(&queue_lock);
501 }
502 return NOTIFY_DONE;
503}
504
505static struct notifier_block ipq_nl_notifier = {
506 .notifier_call = ipq_rcv_nl_event,
507};
508
509#ifdef CONFIG_SYSCTL
510static struct ctl_table_header *ipq_sysctl_header;
511
512static ctl_table ipq_table[] = {
513 {
514 .procname = NET_IPQ_QMAX_NAME,
515 .data = &queue_maxlen,
516 .maxlen = sizeof(queue_maxlen),
517 .mode = 0644,
518 .proc_handler = proc_dointvec
519 },
520 { }
521};
522#endif
523
524#ifdef CONFIG_PROC_FS
525static int ip6_queue_show(struct seq_file *m, void *v)
526{
527 spin_lock_bh(&queue_lock);
528
529 seq_printf(m,
530 "Peer PID : %d\n"
531 "Copy mode : %hu\n"
532 "Copy range : %u\n"
533 "Queue length : %u\n"
534 "Queue max. length : %u\n"
535 "Queue dropped : %u\n"
536 "Netfilter dropped : %u\n",
537 peer_pid,
538 copy_mode,
539 copy_range,
540 queue_total,
541 queue_maxlen,
542 queue_dropped,
543 queue_user_dropped);
544
545 spin_unlock_bh(&queue_lock);
546 return 0;
547}
548
549static int ip6_queue_open(struct inode *inode, struct file *file)
550{
551 return single_open(file, ip6_queue_show, NULL);
552}
553
554static const struct file_operations ip6_queue_proc_fops = {
555 .open = ip6_queue_open,
556 .read = seq_read,
557 .llseek = seq_lseek,
558 .release = single_release,
559 .owner = THIS_MODULE,
560};
561#endif
562
563static const struct nf_queue_handler nfqh = {
564 .name = "ip6_queue",
565 .outfn = &ipq_enqueue_packet,
566};
567
568static int __init ip6_queue_init(void)
569{
570 int status = -ENOMEM;
571 struct proc_dir_entry *proc __maybe_unused;
572
573 netlink_register_notifier(&ipq_nl_notifier);
574 ipqnl = netlink_kernel_create(&init_net, NETLINK_IP6_FW, 0,
575 ipq_rcv_skb, NULL, THIS_MODULE);
576 if (ipqnl == NULL) {
577 printk(KERN_ERR "ip6_queue: failed to create netlink socket\n");
578 goto cleanup_netlink_notifier;
579 }
580
581#ifdef CONFIG_PROC_FS
582 proc = proc_create(IPQ_PROC_FS_NAME, 0, init_net.proc_net,
583 &ip6_queue_proc_fops);
584 if (!proc) {
585 printk(KERN_ERR "ip6_queue: failed to create proc entry\n");
586 goto cleanup_ipqnl;
587 }
588#endif
589 register_netdevice_notifier(&ipq_dev_notifier);
590#ifdef CONFIG_SYSCTL
591 ipq_sysctl_header = register_net_sysctl(&init_net, "net/ipv6", ipq_table);
592#endif
593 status = nf_register_queue_handler(NFPROTO_IPV6, &nfqh);
594 if (status < 0) {
595 printk(KERN_ERR "ip6_queue: failed to register queue handler\n");
596 goto cleanup_sysctl;
597 }
598 return status;
599
600cleanup_sysctl:
601#ifdef CONFIG_SYSCTL
602 unregister_net_sysctl_table(ipq_sysctl_header);
603#endif
604 unregister_netdevice_notifier(&ipq_dev_notifier);
605 proc_net_remove(&init_net, IPQ_PROC_FS_NAME);
606
607cleanup_ipqnl: __maybe_unused
608 netlink_kernel_release(ipqnl);
609 mutex_lock(&ipqnl_mutex);
610 mutex_unlock(&ipqnl_mutex);
611
612cleanup_netlink_notifier:
613 netlink_unregister_notifier(&ipq_nl_notifier);
614 return status;
615}
616
617static void __exit ip6_queue_fini(void)
618{
619 nf_unregister_queue_handlers(&nfqh);
620
621 ipq_flush(NULL, 0);
622
623#ifdef CONFIG_SYSCTL
624 unregister_net_sysctl_table(ipq_sysctl_header);
625#endif
626 unregister_netdevice_notifier(&ipq_dev_notifier);
627 proc_net_remove(&init_net, IPQ_PROC_FS_NAME);
628
629 netlink_kernel_release(ipqnl);
630 mutex_lock(&ipqnl_mutex);
631 mutex_unlock(&ipqnl_mutex);
632
633 netlink_unregister_notifier(&ipq_nl_notifier);
634}
635
636MODULE_DESCRIPTION("IPv6 packet queue handler");
637MODULE_LICENSE("GPL");
638MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_IP6_FW);
639
640module_init(ip6_queue_init);
641module_exit(ip6_queue_fini);