diff options
author | Cyrill Gorcunov <gorcunov@gmail.com> | 2009-01-21 18:55:15 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-01-21 18:55:15 -0500 |
commit | 4e9fb8016a351b5b9da7fea32bcfdbc9d836e421 (patch) | |
tree | d8cce017f0327cef58f9caa9fea92f9ebd6e263f /drivers/net/pppol2tp.c | |
parent | a6bcf1c1d38e0672db35e0d9f2504ac04ddf3ed5 (diff) |
net: pppol2tp - introduce net-namespace functionality
- Each tunnel and appropriate lock are inside own namespace now.
- pppox code allows to create per-namespace sockets for
both PX_PROTO_OE and PX_PROTO_OL2TP protocols. Actually since
now pppox_create support net-namespaces new PPPo... protocols
(if they ever will be) should support net-namespace too otherwise
explicit check for &init_net would be needed.
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: James Chapman <jchapman@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/pppol2tp.c')
-rw-r--r-- | drivers/net/pppol2tp.c | 160 |
1 files changed, 117 insertions, 43 deletions
diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 635dd5fbe62d..f3f9cb1f77c0 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c | |||
@@ -90,7 +90,9 @@ | |||
90 | #include <linux/hash.h> | 90 | #include <linux/hash.h> |
91 | #include <linux/sort.h> | 91 | #include <linux/sort.h> |
92 | #include <linux/proc_fs.h> | 92 | #include <linux/proc_fs.h> |
93 | #include <linux/nsproxy.h> | ||
93 | #include <net/net_namespace.h> | 94 | #include <net/net_namespace.h> |
95 | #include <net/netns/generic.h> | ||
94 | #include <net/dst.h> | 96 | #include <net/dst.h> |
95 | #include <net/ip.h> | 97 | #include <net/ip.h> |
96 | #include <net/udp.h> | 98 | #include <net/udp.h> |
@@ -204,6 +206,7 @@ struct pppol2tp_tunnel | |||
204 | struct sock *sock; /* Parent socket */ | 206 | struct sock *sock; /* Parent socket */ |
205 | struct list_head list; /* Keep a list of all open | 207 | struct list_head list; /* Keep a list of all open |
206 | * prepared sockets */ | 208 | * prepared sockets */ |
209 | struct net *pppol2tp_net; /* the net we belong to */ | ||
207 | 210 | ||
208 | atomic_t ref_count; | 211 | atomic_t ref_count; |
209 | }; | 212 | }; |
@@ -227,8 +230,20 @@ static atomic_t pppol2tp_tunnel_count; | |||
227 | static atomic_t pppol2tp_session_count; | 230 | static atomic_t pppol2tp_session_count; |
228 | static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL }; | 231 | static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL }; |
229 | static struct proto_ops pppol2tp_ops; | 232 | static struct proto_ops pppol2tp_ops; |
230 | static LIST_HEAD(pppol2tp_tunnel_list); | 233 | |
231 | static DEFINE_RWLOCK(pppol2tp_tunnel_list_lock); | 234 | /* per-net private data for this module */ |
235 | static unsigned int pppol2tp_net_id; | ||
236 | struct pppol2tp_net { | ||
237 | struct list_head pppol2tp_tunnel_list; | ||
238 | rwlock_t pppol2tp_tunnel_list_lock; | ||
239 | }; | ||
240 | |||
241 | static inline struct pppol2tp_net *pppol2tp_pernet(struct net *net) | ||
242 | { | ||
243 | BUG_ON(!net); | ||
244 | |||
245 | return net_generic(net, pppol2tp_net_id); | ||
246 | } | ||
232 | 247 | ||
233 | /* Helpers to obtain tunnel/session contexts from sockets. | 248 | /* Helpers to obtain tunnel/session contexts from sockets. |
234 | */ | 249 | */ |
@@ -321,18 +336,19 @@ pppol2tp_session_find(struct pppol2tp_tunnel *tunnel, u16 session_id) | |||
321 | 336 | ||
322 | /* Lookup a tunnel by id | 337 | /* Lookup a tunnel by id |
323 | */ | 338 | */ |
324 | static struct pppol2tp_tunnel *pppol2tp_tunnel_find(u16 tunnel_id) | 339 | static struct pppol2tp_tunnel *pppol2tp_tunnel_find(struct net *net, u16 tunnel_id) |
325 | { | 340 | { |
326 | struct pppol2tp_tunnel *tunnel = NULL; | 341 | struct pppol2tp_tunnel *tunnel; |
342 | struct pppol2tp_net *pn = pppol2tp_pernet(net); | ||
327 | 343 | ||
328 | read_lock_bh(&pppol2tp_tunnel_list_lock); | 344 | read_lock_bh(&pn->pppol2tp_tunnel_list_lock); |
329 | list_for_each_entry(tunnel, &pppol2tp_tunnel_list, list) { | 345 | list_for_each_entry(tunnel, &pn->pppol2tp_tunnel_list, list) { |
330 | if (tunnel->stats.tunnel_id == tunnel_id) { | 346 | if (tunnel->stats.tunnel_id == tunnel_id) { |
331 | read_unlock_bh(&pppol2tp_tunnel_list_lock); | 347 | read_unlock_bh(&pn->pppol2tp_tunnel_list_lock); |
332 | return tunnel; | 348 | return tunnel; |
333 | } | 349 | } |
334 | } | 350 | } |
335 | read_unlock_bh(&pppol2tp_tunnel_list_lock); | 351 | read_unlock_bh(&pn->pppol2tp_tunnel_list_lock); |
336 | 352 | ||
337 | return NULL; | 353 | return NULL; |
338 | } | 354 | } |
@@ -1287,10 +1303,12 @@ again: | |||
1287 | */ | 1303 | */ |
1288 | static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel) | 1304 | static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel) |
1289 | { | 1305 | { |
1306 | struct pppol2tp_net *pn = pppol2tp_pernet(tunnel->pppol2tp_net); | ||
1307 | |||
1290 | /* Remove from socket list */ | 1308 | /* Remove from socket list */ |
1291 | write_lock_bh(&pppol2tp_tunnel_list_lock); | 1309 | write_lock_bh(&pn->pppol2tp_tunnel_list_lock); |
1292 | list_del_init(&tunnel->list); | 1310 | list_del_init(&tunnel->list); |
1293 | write_unlock_bh(&pppol2tp_tunnel_list_lock); | 1311 | write_unlock_bh(&pn->pppol2tp_tunnel_list_lock); |
1294 | 1312 | ||
1295 | atomic_dec(&pppol2tp_tunnel_count); | 1313 | atomic_dec(&pppol2tp_tunnel_count); |
1296 | kfree(tunnel); | 1314 | kfree(tunnel); |
@@ -1444,13 +1462,14 @@ error: | |||
1444 | /* Internal function to prepare a tunnel (UDP) socket to have PPPoX | 1462 | /* Internal function to prepare a tunnel (UDP) socket to have PPPoX |
1445 | * sockets attached to it. | 1463 | * sockets attached to it. |
1446 | */ | 1464 | */ |
1447 | static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id, | 1465 | static struct sock *pppol2tp_prepare_tunnel_socket(struct net *net, |
1448 | int *error) | 1466 | int fd, u16 tunnel_id, int *error) |
1449 | { | 1467 | { |
1450 | int err; | 1468 | int err; |
1451 | struct socket *sock = NULL; | 1469 | struct socket *sock = NULL; |
1452 | struct sock *sk; | 1470 | struct sock *sk; |
1453 | struct pppol2tp_tunnel *tunnel; | 1471 | struct pppol2tp_tunnel *tunnel; |
1472 | struct pppol2tp_net *pn; | ||
1454 | struct sock *ret = NULL; | 1473 | struct sock *ret = NULL; |
1455 | 1474 | ||
1456 | /* Get the tunnel UDP socket from the fd, which was opened by | 1475 | /* Get the tunnel UDP socket from the fd, which was opened by |
@@ -1524,11 +1543,15 @@ static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id, | |||
1524 | /* Misc init */ | 1543 | /* Misc init */ |
1525 | rwlock_init(&tunnel->hlist_lock); | 1544 | rwlock_init(&tunnel->hlist_lock); |
1526 | 1545 | ||
1546 | /* The net we belong to */ | ||
1547 | tunnel->pppol2tp_net = net; | ||
1548 | pn = pppol2tp_pernet(net); | ||
1549 | |||
1527 | /* Add tunnel to our list */ | 1550 | /* Add tunnel to our list */ |
1528 | INIT_LIST_HEAD(&tunnel->list); | 1551 | INIT_LIST_HEAD(&tunnel->list); |
1529 | write_lock_bh(&pppol2tp_tunnel_list_lock); | 1552 | write_lock_bh(&pn->pppol2tp_tunnel_list_lock); |
1530 | list_add(&tunnel->list, &pppol2tp_tunnel_list); | 1553 | list_add(&tunnel->list, &pn->pppol2tp_tunnel_list); |
1531 | write_unlock_bh(&pppol2tp_tunnel_list_lock); | 1554 | write_unlock_bh(&pn->pppol2tp_tunnel_list_lock); |
1532 | atomic_inc(&pppol2tp_tunnel_count); | 1555 | atomic_inc(&pppol2tp_tunnel_count); |
1533 | 1556 | ||
1534 | /* Bump the reference count. The tunnel context is deleted | 1557 | /* Bump the reference count. The tunnel context is deleted |
@@ -1629,7 +1652,8 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, | |||
1629 | * tunnel id. | 1652 | * tunnel id. |
1630 | */ | 1653 | */ |
1631 | if ((sp->pppol2tp.s_session == 0) && (sp->pppol2tp.d_session == 0)) { | 1654 | if ((sp->pppol2tp.s_session == 0) && (sp->pppol2tp.d_session == 0)) { |
1632 | tunnel_sock = pppol2tp_prepare_tunnel_socket(sp->pppol2tp.fd, | 1655 | tunnel_sock = pppol2tp_prepare_tunnel_socket(sock_net(sk), |
1656 | sp->pppol2tp.fd, | ||
1633 | sp->pppol2tp.s_tunnel, | 1657 | sp->pppol2tp.s_tunnel, |
1634 | &error); | 1658 | &error); |
1635 | if (tunnel_sock == NULL) | 1659 | if (tunnel_sock == NULL) |
@@ -1637,7 +1661,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, | |||
1637 | 1661 | ||
1638 | tunnel = tunnel_sock->sk_user_data; | 1662 | tunnel = tunnel_sock->sk_user_data; |
1639 | } else { | 1663 | } else { |
1640 | tunnel = pppol2tp_tunnel_find(sp->pppol2tp.s_tunnel); | 1664 | tunnel = pppol2tp_tunnel_find(sock_net(sk), sp->pppol2tp.s_tunnel); |
1641 | 1665 | ||
1642 | /* Error if we can't find the tunnel */ | 1666 | /* Error if we can't find the tunnel */ |
1643 | error = -ENOENT; | 1667 | error = -ENOENT; |
@@ -2347,8 +2371,9 @@ end: | |||
2347 | #include <linux/seq_file.h> | 2371 | #include <linux/seq_file.h> |
2348 | 2372 | ||
2349 | struct pppol2tp_seq_data { | 2373 | struct pppol2tp_seq_data { |
2350 | struct pppol2tp_tunnel *tunnel; /* current tunnel */ | 2374 | struct net *seq_net; /* net of inode */ |
2351 | struct pppol2tp_session *session; /* NULL means get first session in tunnel */ | 2375 | struct pppol2tp_tunnel *tunnel; /* current tunnel */ |
2376 | struct pppol2tp_session *session; /* NULL means get first session in tunnel */ | ||
2352 | }; | 2377 | }; |
2353 | 2378 | ||
2354 | static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, struct pppol2tp_session *curr) | 2379 | static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, struct pppol2tp_session *curr) |
@@ -2384,17 +2409,18 @@ out: | |||
2384 | return session; | 2409 | return session; |
2385 | } | 2410 | } |
2386 | 2411 | ||
2387 | static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_tunnel *curr) | 2412 | static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_net *pn, |
2413 | struct pppol2tp_tunnel *curr) | ||
2388 | { | 2414 | { |
2389 | struct pppol2tp_tunnel *tunnel = NULL; | 2415 | struct pppol2tp_tunnel *tunnel = NULL; |
2390 | 2416 | ||
2391 | read_lock_bh(&pppol2tp_tunnel_list_lock); | 2417 | read_lock_bh(&pn->pppol2tp_tunnel_list_lock); |
2392 | if (list_is_last(&curr->list, &pppol2tp_tunnel_list)) { | 2418 | if (list_is_last(&curr->list, &pn->pppol2tp_tunnel_list)) { |
2393 | goto out; | 2419 | goto out; |
2394 | } | 2420 | } |
2395 | tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list); | 2421 | tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list); |
2396 | out: | 2422 | out: |
2397 | read_unlock_bh(&pppol2tp_tunnel_list_lock); | 2423 | read_unlock_bh(&pn->pppol2tp_tunnel_list_lock); |
2398 | 2424 | ||
2399 | return tunnel; | 2425 | return tunnel; |
2400 | } | 2426 | } |
@@ -2402,6 +2428,7 @@ out: | |||
2402 | static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs) | 2428 | static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs) |
2403 | { | 2429 | { |
2404 | struct pppol2tp_seq_data *pd = SEQ_START_TOKEN; | 2430 | struct pppol2tp_seq_data *pd = SEQ_START_TOKEN; |
2431 | struct pppol2tp_net *pn; | ||
2405 | loff_t pos = *offs; | 2432 | loff_t pos = *offs; |
2406 | 2433 | ||
2407 | if (!pos) | 2434 | if (!pos) |
@@ -2409,14 +2436,15 @@ static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs) | |||
2409 | 2436 | ||
2410 | BUG_ON(m->private == NULL); | 2437 | BUG_ON(m->private == NULL); |
2411 | pd = m->private; | 2438 | pd = m->private; |
2439 | pn = pppol2tp_pernet(pd->seq_net); | ||
2412 | 2440 | ||
2413 | if (pd->tunnel == NULL) { | 2441 | if (pd->tunnel == NULL) { |
2414 | if (!list_empty(&pppol2tp_tunnel_list)) | 2442 | if (!list_empty(&pn->pppol2tp_tunnel_list)) |
2415 | pd->tunnel = list_entry(pppol2tp_tunnel_list.next, struct pppol2tp_tunnel, list); | 2443 | pd->tunnel = list_entry(pn->pppol2tp_tunnel_list.next, struct pppol2tp_tunnel, list); |
2416 | } else { | 2444 | } else { |
2417 | pd->session = next_session(pd->tunnel, pd->session); | 2445 | pd->session = next_session(pd->tunnel, pd->session); |
2418 | if (pd->session == NULL) { | 2446 | if (pd->session == NULL) { |
2419 | pd->tunnel = next_tunnel(pd->tunnel); | 2447 | pd->tunnel = next_tunnel(pn, pd->tunnel); |
2420 | } | 2448 | } |
2421 | } | 2449 | } |
2422 | 2450 | ||
@@ -2532,6 +2560,7 @@ static int pppol2tp_proc_open(struct inode *inode, struct file *file) | |||
2532 | { | 2560 | { |
2533 | struct seq_file *m; | 2561 | struct seq_file *m; |
2534 | struct pppol2tp_seq_data *pd; | 2562 | struct pppol2tp_seq_data *pd; |
2563 | struct net *net; | ||
2535 | int ret = 0; | 2564 | int ret = 0; |
2536 | 2565 | ||
2537 | ret = seq_open(file, &pppol2tp_seq_ops); | 2566 | ret = seq_open(file, &pppol2tp_seq_ops); |
@@ -2542,12 +2571,15 @@ static int pppol2tp_proc_open(struct inode *inode, struct file *file) | |||
2542 | 2571 | ||
2543 | /* Allocate and fill our proc_data for access later */ | 2572 | /* Allocate and fill our proc_data for access later */ |
2544 | ret = -ENOMEM; | 2573 | ret = -ENOMEM; |
2545 | m->private = kzalloc(sizeof(struct pppol2tp_seq_data), GFP_KERNEL); | 2574 | m->private = kzalloc(sizeof(*pd), GFP_KERNEL); |
2546 | if (m->private == NULL) | 2575 | if (m->private == NULL) |
2547 | goto out; | 2576 | goto out; |
2548 | 2577 | ||
2549 | pd = m->private; | 2578 | pd = m->private; |
2550 | ret = 0; | 2579 | net = maybe_get_net(PDE_NET(PDE(inode))); |
2580 | BUG_ON(!net); | ||
2581 | pd->seq_net = net; | ||
2582 | return 0; | ||
2551 | 2583 | ||
2552 | out: | 2584 | out: |
2553 | return ret; | 2585 | return ret; |
@@ -2558,6 +2590,9 @@ out: | |||
2558 | static int pppol2tp_proc_release(struct inode *inode, struct file *file) | 2590 | static int pppol2tp_proc_release(struct inode *inode, struct file *file) |
2559 | { | 2591 | { |
2560 | struct seq_file *m = (struct seq_file *)file->private_data; | 2592 | struct seq_file *m = (struct seq_file *)file->private_data; |
2593 | struct pppol2tp_seq_data *pd = m->private; | ||
2594 | |||
2595 | put_net(pd->seq_net); | ||
2561 | 2596 | ||
2562 | kfree(m->private); | 2597 | kfree(m->private); |
2563 | m->private = NULL; | 2598 | m->private = NULL; |
@@ -2573,8 +2608,6 @@ static const struct file_operations pppol2tp_proc_fops = { | |||
2573 | .release = pppol2tp_proc_release, | 2608 | .release = pppol2tp_proc_release, |
2574 | }; | 2609 | }; |
2575 | 2610 | ||
2576 | static struct proc_dir_entry *pppol2tp_proc; | ||
2577 | |||
2578 | #endif /* CONFIG_PROC_FS */ | 2611 | #endif /* CONFIG_PROC_FS */ |
2579 | 2612 | ||
2580 | /***************************************************************************** | 2613 | /***************************************************************************** |
@@ -2606,6 +2639,57 @@ static struct pppox_proto pppol2tp_proto = { | |||
2606 | .ioctl = pppol2tp_ioctl | 2639 | .ioctl = pppol2tp_ioctl |
2607 | }; | 2640 | }; |
2608 | 2641 | ||
2642 | static __net_init int pppol2tp_init_net(struct net *net) | ||
2643 | { | ||
2644 | struct pppol2tp_net *pn; | ||
2645 | struct proc_dir_entry *pde; | ||
2646 | int err; | ||
2647 | |||
2648 | pn = kzalloc(sizeof(*pn), GFP_KERNEL); | ||
2649 | if (!pn) | ||
2650 | return -ENOMEM; | ||
2651 | |||
2652 | INIT_LIST_HEAD(&pn->pppol2tp_tunnel_list); | ||
2653 | rwlock_init(&pn->pppol2tp_tunnel_list_lock); | ||
2654 | |||
2655 | err = net_assign_generic(net, pppol2tp_net_id, pn); | ||
2656 | if (err) | ||
2657 | goto out; | ||
2658 | |||
2659 | pde = proc_net_fops_create(net, "pppol2tp", S_IRUGO, &pppol2tp_proc_fops); | ||
2660 | #ifdef CONFIG_PROC_FS | ||
2661 | if (!pde) { | ||
2662 | err = -ENOMEM; | ||
2663 | goto out; | ||
2664 | } | ||
2665 | #endif | ||
2666 | |||
2667 | return 0; | ||
2668 | |||
2669 | out: | ||
2670 | kfree(pn); | ||
2671 | return err; | ||
2672 | } | ||
2673 | |||
2674 | static __net_exit void pppol2tp_exit_net(struct net *net) | ||
2675 | { | ||
2676 | struct pppoe_net *pn; | ||
2677 | |||
2678 | proc_net_remove(net, "pppol2tp"); | ||
2679 | pn = net_generic(net, pppol2tp_net_id); | ||
2680 | /* | ||
2681 | * if someone has cached our net then | ||
2682 | * further net_generic call will return NULL | ||
2683 | */ | ||
2684 | net_assign_generic(net, pppol2tp_net_id, NULL); | ||
2685 | kfree(pn); | ||
2686 | } | ||
2687 | |||
2688 | static __net_initdata struct pernet_operations pppol2tp_net_ops = { | ||
2689 | .init = pppol2tp_init_net, | ||
2690 | .exit = pppol2tp_exit_net, | ||
2691 | }; | ||
2692 | |||
2609 | static int __init pppol2tp_init(void) | 2693 | static int __init pppol2tp_init(void) |
2610 | { | 2694 | { |
2611 | int err; | 2695 | int err; |
@@ -2617,23 +2701,17 @@ static int __init pppol2tp_init(void) | |||
2617 | if (err) | 2701 | if (err) |
2618 | goto out_unregister_pppol2tp_proto; | 2702 | goto out_unregister_pppol2tp_proto; |
2619 | 2703 | ||
2620 | #ifdef CONFIG_PROC_FS | 2704 | err = register_pernet_gen_device(&pppol2tp_net_id, &pppol2tp_net_ops); |
2621 | pppol2tp_proc = proc_net_fops_create(&init_net, "pppol2tp", 0, | 2705 | if (err) |
2622 | &pppol2tp_proc_fops); | ||
2623 | if (!pppol2tp_proc) { | ||
2624 | err = -ENOMEM; | ||
2625 | goto out_unregister_pppox_proto; | 2706 | goto out_unregister_pppox_proto; |
2626 | } | 2707 | |
2627 | #endif /* CONFIG_PROC_FS */ | ||
2628 | printk(KERN_INFO "PPPoL2TP kernel driver, %s\n", | 2708 | printk(KERN_INFO "PPPoL2TP kernel driver, %s\n", |
2629 | PPPOL2TP_DRV_VERSION); | 2709 | PPPOL2TP_DRV_VERSION); |
2630 | 2710 | ||
2631 | out: | 2711 | out: |
2632 | return err; | 2712 | return err; |
2633 | #ifdef CONFIG_PROC_FS | ||
2634 | out_unregister_pppox_proto: | 2713 | out_unregister_pppox_proto: |
2635 | unregister_pppox_proto(PX_PROTO_OL2TP); | 2714 | unregister_pppox_proto(PX_PROTO_OL2TP); |
2636 | #endif | ||
2637 | out_unregister_pppol2tp_proto: | 2715 | out_unregister_pppol2tp_proto: |
2638 | proto_unregister(&pppol2tp_sk_proto); | 2716 | proto_unregister(&pppol2tp_sk_proto); |
2639 | goto out; | 2717 | goto out; |
@@ -2642,10 +2720,6 @@ out_unregister_pppol2tp_proto: | |||
2642 | static void __exit pppol2tp_exit(void) | 2720 | static void __exit pppol2tp_exit(void) |
2643 | { | 2721 | { |
2644 | unregister_pppox_proto(PX_PROTO_OL2TP); | 2722 | unregister_pppox_proto(PX_PROTO_OL2TP); |
2645 | |||
2646 | #ifdef CONFIG_PROC_FS | ||
2647 | remove_proc_entry("pppol2tp", init_net.proc_net); | ||
2648 | #endif | ||
2649 | proto_unregister(&pppol2tp_sk_proto); | 2723 | proto_unregister(&pppol2tp_sk_proto); |
2650 | } | 2724 | } |
2651 | 2725 | ||