diff options
author | WANG Cong <xiyou.wangcong@gmail.com> | 2015-04-10 15:00:29 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-04-12 21:25:13 -0400 |
commit | 02d793c5bbebf2c750da03df4c950fc4e8e8a5a7 (patch) | |
tree | 453d992bc7880d54d7a4433323de0691f08f1b78 /net/ipv4/fou.c | |
parent | 4cbcdf2b6c8065cb9f2e0eda8c12d33b1b617043 (diff) |
fou: add network namespace support
Also convert the spinlock to a mutex.
Cc: Tom Herbert <tom@herbertland.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/fou.c')
-rw-r--r-- | net/ipv4/fou.c | 106 |
1 files changed, 67 insertions, 39 deletions
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index d61f6f995733..c244b1a65787 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c | |||
@@ -16,9 +16,6 @@ | |||
16 | #include <uapi/linux/fou.h> | 16 | #include <uapi/linux/fou.h> |
17 | #include <uapi/linux/genetlink.h> | 17 | #include <uapi/linux/genetlink.h> |
18 | 18 | ||
19 | static DEFINE_SPINLOCK(fou_lock); | ||
20 | static LIST_HEAD(fou_list); | ||
21 | |||
22 | struct fou { | 19 | struct fou { |
23 | struct socket *sock; | 20 | struct socket *sock; |
24 | u8 protocol; | 21 | u8 protocol; |
@@ -37,6 +34,13 @@ struct fou_cfg { | |||
37 | struct udp_port_cfg udp_config; | 34 | struct udp_port_cfg udp_config; |
38 | }; | 35 | }; |
39 | 36 | ||
37 | static unsigned int fou_net_id; | ||
38 | |||
39 | struct fou_net { | ||
40 | struct list_head fou_list; | ||
41 | struct mutex fou_lock; | ||
42 | }; | ||
43 | |||
40 | static inline struct fou *fou_from_sock(struct sock *sk) | 44 | static inline struct fou *fou_from_sock(struct sock *sk) |
41 | { | 45 | { |
42 | return sk->sk_user_data; | 46 | return sk->sk_user_data; |
@@ -387,20 +391,21 @@ out_unlock: | |||
387 | return err; | 391 | return err; |
388 | } | 392 | } |
389 | 393 | ||
390 | static int fou_add_to_port_list(struct fou *fou) | 394 | static int fou_add_to_port_list(struct net *net, struct fou *fou) |
391 | { | 395 | { |
396 | struct fou_net *fn = net_generic(net, fou_net_id); | ||
392 | struct fou *fout; | 397 | struct fou *fout; |
393 | 398 | ||
394 | spin_lock(&fou_lock); | 399 | mutex_lock(&fn->fou_lock); |
395 | list_for_each_entry(fout, &fou_list, list) { | 400 | list_for_each_entry(fout, &fn->fou_list, list) { |
396 | if (fou->port == fout->port) { | 401 | if (fou->port == fout->port) { |
397 | spin_unlock(&fou_lock); | 402 | mutex_unlock(&fn->fou_lock); |
398 | return -EALREADY; | 403 | return -EALREADY; |
399 | } | 404 | } |
400 | } | 405 | } |
401 | 406 | ||
402 | list_add(&fou->list, &fou_list); | 407 | list_add(&fou->list, &fn->fou_list); |
403 | spin_unlock(&fou_lock); | 408 | mutex_unlock(&fn->fou_lock); |
404 | 409 | ||
405 | return 0; | 410 | return 0; |
406 | } | 411 | } |
@@ -412,13 +417,8 @@ static void fou_release(struct fou *fou) | |||
412 | 417 | ||
413 | if (sk->sk_family == AF_INET) | 418 | if (sk->sk_family == AF_INET) |
414 | udp_del_offload(&fou->udp_offloads); | 419 | udp_del_offload(&fou->udp_offloads); |
415 | |||
416 | list_del(&fou->list); | 420 | list_del(&fou->list); |
417 | 421 | udp_tunnel_sock_release(sock); | |
418 | /* Remove hooks into tunnel socket */ | ||
419 | sk->sk_user_data = NULL; | ||
420 | |||
421 | sock_release(sock); | ||
422 | 422 | ||
423 | kfree(fou); | 423 | kfree(fou); |
424 | } | 424 | } |
@@ -448,10 +448,10 @@ static int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg) | |||
448 | static int fou_create(struct net *net, struct fou_cfg *cfg, | 448 | static int fou_create(struct net *net, struct fou_cfg *cfg, |
449 | struct socket **sockp) | 449 | struct socket **sockp) |
450 | { | 450 | { |
451 | struct fou *fou = NULL; | ||
452 | int err; | ||
453 | struct socket *sock = NULL; | 451 | struct socket *sock = NULL; |
452 | struct fou *fou = NULL; | ||
454 | struct sock *sk; | 453 | struct sock *sk; |
454 | int err; | ||
455 | 455 | ||
456 | /* Open UDP socket */ | 456 | /* Open UDP socket */ |
457 | err = udp_sock_create(net, &cfg->udp_config, &sock); | 457 | err = udp_sock_create(net, &cfg->udp_config, &sock); |
@@ -503,7 +503,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, | |||
503 | goto error; | 503 | goto error; |
504 | } | 504 | } |
505 | 505 | ||
506 | err = fou_add_to_port_list(fou); | 506 | err = fou_add_to_port_list(net, fou); |
507 | if (err) | 507 | if (err) |
508 | goto error; | 508 | goto error; |
509 | 509 | ||
@@ -515,26 +515,27 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, | |||
515 | error: | 515 | error: |
516 | kfree(fou); | 516 | kfree(fou); |
517 | if (sock) | 517 | if (sock) |
518 | sock_release(sock); | 518 | udp_tunnel_sock_release(sock); |
519 | 519 | ||
520 | return err; | 520 | return err; |
521 | } | 521 | } |
522 | 522 | ||
523 | static int fou_destroy(struct net *net, struct fou_cfg *cfg) | 523 | static int fou_destroy(struct net *net, struct fou_cfg *cfg) |
524 | { | 524 | { |
525 | struct fou *fou; | 525 | struct fou_net *fn = net_generic(net, fou_net_id); |
526 | __be16 port = cfg->udp_config.local_udp_port; | 526 | __be16 port = cfg->udp_config.local_udp_port; |
527 | int err = -EINVAL; | 527 | int err = -EINVAL; |
528 | struct fou *fou; | ||
528 | 529 | ||
529 | spin_lock(&fou_lock); | 530 | mutex_lock(&fn->fou_lock); |
530 | list_for_each_entry(fou, &fou_list, list) { | 531 | list_for_each_entry(fou, &fn->fou_list, list) { |
531 | if (fou->port == port) { | 532 | if (fou->port == port) { |
532 | fou_release(fou); | 533 | fou_release(fou); |
533 | err = 0; | 534 | err = 0; |
534 | break; | 535 | break; |
535 | } | 536 | } |
536 | } | 537 | } |
537 | spin_unlock(&fou_lock); | 538 | mutex_unlock(&fn->fou_lock); |
538 | 539 | ||
539 | return err; | 540 | return err; |
540 | } | 541 | } |
@@ -592,6 +593,7 @@ static int parse_nl_config(struct genl_info *info, | |||
592 | 593 | ||
593 | static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) | 594 | static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) |
594 | { | 595 | { |
596 | struct net *net = genl_info_net(info); | ||
595 | struct fou_cfg cfg; | 597 | struct fou_cfg cfg; |
596 | int err; | 598 | int err; |
597 | 599 | ||
@@ -599,11 +601,12 @@ static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) | |||
599 | if (err) | 601 | if (err) |
600 | return err; | 602 | return err; |
601 | 603 | ||
602 | return fou_create(&init_net, &cfg, NULL); | 604 | return fou_create(net, &cfg, NULL); |
603 | } | 605 | } |
604 | 606 | ||
605 | static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info) | 607 | static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info) |
606 | { | 608 | { |
609 | struct net *net = genl_info_net(info); | ||
607 | struct fou_cfg cfg; | 610 | struct fou_cfg cfg; |
608 | int err; | 611 | int err; |
609 | 612 | ||
@@ -611,7 +614,7 @@ static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info) | |||
611 | if (err) | 614 | if (err) |
612 | return err; | 615 | return err; |
613 | 616 | ||
614 | return fou_destroy(&init_net, &cfg); | 617 | return fou_destroy(net, &cfg); |
615 | } | 618 | } |
616 | 619 | ||
617 | static const struct genl_ops fou_nl_ops[] = { | 620 | static const struct genl_ops fou_nl_ops[] = { |
@@ -823,38 +826,63 @@ static void ip_tunnel_encap_del_fou_ops(void) | |||
823 | 826 | ||
824 | #endif | 827 | #endif |
825 | 828 | ||
829 | static __net_init int fou_init_net(struct net *net) | ||
830 | { | ||
831 | struct fou_net *fn = net_generic(net, fou_net_id); | ||
832 | |||
833 | INIT_LIST_HEAD(&fn->fou_list); | ||
834 | mutex_init(&fn->fou_lock); | ||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | static __net_exit void fou_exit_net(struct net *net) | ||
839 | { | ||
840 | struct fou_net *fn = net_generic(net, fou_net_id); | ||
841 | struct fou *fou, *next; | ||
842 | |||
843 | /* Close all the FOU sockets */ | ||
844 | mutex_lock(&fn->fou_lock); | ||
845 | list_for_each_entry_safe(fou, next, &fn->fou_list, list) | ||
846 | fou_release(fou); | ||
847 | mutex_unlock(&fn->fou_lock); | ||
848 | } | ||
849 | |||
850 | static struct pernet_operations fou_net_ops = { | ||
851 | .init = fou_init_net, | ||
852 | .exit = fou_exit_net, | ||
853 | .id = &fou_net_id, | ||
854 | .size = sizeof(struct fou_net), | ||
855 | }; | ||
856 | |||
826 | static int __init fou_init(void) | 857 | static int __init fou_init(void) |
827 | { | 858 | { |
828 | int ret; | 859 | int ret; |
829 | 860 | ||
861 | ret = register_pernet_device(&fou_net_ops); | ||
862 | if (ret) | ||
863 | goto exit; | ||
864 | |||
830 | ret = genl_register_family_with_ops(&fou_nl_family, | 865 | ret = genl_register_family_with_ops(&fou_nl_family, |
831 | fou_nl_ops); | 866 | fou_nl_ops); |
832 | |||
833 | if (ret < 0) | 867 | if (ret < 0) |
834 | goto exit; | 868 | goto unregister; |
835 | 869 | ||
836 | ret = ip_tunnel_encap_add_fou_ops(); | 870 | ret = ip_tunnel_encap_add_fou_ops(); |
837 | if (ret < 0) | 871 | if (ret == 0) |
838 | genl_unregister_family(&fou_nl_family); | 872 | return 0; |
839 | 873 | ||
874 | genl_unregister_family(&fou_nl_family); | ||
875 | unregister: | ||
876 | unregister_pernet_device(&fou_net_ops); | ||
840 | exit: | 877 | exit: |
841 | return ret; | 878 | return ret; |
842 | } | 879 | } |
843 | 880 | ||
844 | static void __exit fou_fini(void) | 881 | static void __exit fou_fini(void) |
845 | { | 882 | { |
846 | struct fou *fou, *next; | ||
847 | |||
848 | ip_tunnel_encap_del_fou_ops(); | 883 | ip_tunnel_encap_del_fou_ops(); |
849 | |||
850 | genl_unregister_family(&fou_nl_family); | 884 | genl_unregister_family(&fou_nl_family); |
851 | 885 | unregister_pernet_device(&fou_net_ops); | |
852 | /* Close all the FOU sockets */ | ||
853 | |||
854 | spin_lock(&fou_lock); | ||
855 | list_for_each_entry_safe(fou, next, &fou_list, list) | ||
856 | fou_release(fou); | ||
857 | spin_unlock(&fou_lock); | ||
858 | } | 886 | } |
859 | 887 | ||
860 | module_init(fou_init); | 888 | module_init(fou_init); |