diff options
author | Tom Herbert <therbert@google.com> | 2014-11-12 14:54:09 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-11-12 15:01:35 -0500 |
commit | a8c5f90fb59a2d3bff0bd29adbb3e39fe0dd52f8 (patch) | |
tree | 49171c0ba2df50c16595901743dad7c0d32a7c33 | |
parent | 4243cdc2c1e5a1375cc8397e8f9b06530f099c60 (diff) |
ip_tunnel: Ops registration for secondary encap (fou, gue)
Instead of calling fou and gue functions directly from ip_tunnel
use ops for these that were previously registered. This patch adds the
logic to add and remove encapsulation operations for ip_tunnel,
and modified fou (and gue) to register with ip_tunnels.
This patch also addresses a circular dependency between ip_tunnel
and fou that was causing link errors when CONFIG_NET_IP_TUNNEL=y
and CONFIG_NET_FOU=m. References to fou an gue have been removed from
ip_tunnel.c
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/fou.h | 25 | ||||
-rw-r--r-- | include/net/ip_tunnels.h | 16 | ||||
-rw-r--r-- | net/ipv4/fou.c | 85 | ||||
-rw-r--r-- | net/ipv4/ip_tunnel.c | 78 |
4 files changed, 157 insertions, 47 deletions
diff --git a/include/net/fou.h b/include/net/fou.h index 25b26ffcf1df..19b8a0c62a98 100644 --- a/include/net/fou.h +++ b/include/net/fou.h | |||
@@ -8,31 +8,12 @@ | |||
8 | #include <net/ip_tunnels.h> | 8 | #include <net/ip_tunnels.h> |
9 | #include <net/udp.h> | 9 | #include <net/udp.h> |
10 | 10 | ||
11 | size_t fou_encap_hlen(struct ip_tunnel_encap *e); | ||
12 | static size_t gue_encap_hlen(struct ip_tunnel_encap *e); | ||
13 | |||
11 | int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | 14 | int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, |
12 | u8 *protocol, struct flowi4 *fl4); | 15 | u8 *protocol, struct flowi4 *fl4); |
13 | int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | 16 | int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, |
14 | u8 *protocol, struct flowi4 *fl4); | 17 | u8 *protocol, struct flowi4 *fl4); |
15 | 18 | ||
16 | static size_t fou_encap_hlen(struct ip_tunnel_encap *e) | ||
17 | { | ||
18 | return sizeof(struct udphdr); | ||
19 | } | ||
20 | |||
21 | static size_t gue_encap_hlen(struct ip_tunnel_encap *e) | ||
22 | { | ||
23 | size_t len; | ||
24 | bool need_priv = false; | ||
25 | |||
26 | len = sizeof(struct udphdr) + sizeof(struct guehdr); | ||
27 | |||
28 | if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) { | ||
29 | len += GUE_PLEN_REMCSUM; | ||
30 | need_priv = true; | ||
31 | } | ||
32 | |||
33 | len += need_priv ? GUE_LEN_PRIV : 0; | ||
34 | |||
35 | return len; | ||
36 | } | ||
37 | |||
38 | #endif | 19 | #endif |
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 5bc6edeb7143..25a59eb388a6 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h | |||
@@ -117,6 +117,22 @@ struct ip_tunnel_net { | |||
117 | struct hlist_head tunnels[IP_TNL_HASH_SIZE]; | 117 | struct hlist_head tunnels[IP_TNL_HASH_SIZE]; |
118 | }; | 118 | }; |
119 | 119 | ||
120 | struct ip_tunnel_encap_ops { | ||
121 | size_t (*encap_hlen)(struct ip_tunnel_encap *e); | ||
122 | int (*build_header)(struct sk_buff *skb, struct ip_tunnel_encap *e, | ||
123 | u8 *protocol, struct flowi4 *fl4); | ||
124 | }; | ||
125 | |||
126 | #define MAX_IPTUN_ENCAP_OPS 8 | ||
127 | |||
128 | extern const struct ip_tunnel_encap_ops __rcu * | ||
129 | iptun_encaps[MAX_IPTUN_ENCAP_OPS]; | ||
130 | |||
131 | int ip_tunnel_encap_add_ops(const struct ip_tunnel_encap_ops *op, | ||
132 | unsigned int num); | ||
133 | int ip_tunnel_encap_del_ops(const struct ip_tunnel_encap_ops *op, | ||
134 | unsigned int num); | ||
135 | |||
120 | #ifdef CONFIG_INET | 136 | #ifdef CONFIG_INET |
121 | 137 | ||
122 | int ip_tunnel_init(struct net_device *dev); | 138 | int ip_tunnel_init(struct net_device *dev); |
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index 740ae099a0d9..fe0907774ce8 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c | |||
@@ -668,6 +668,30 @@ static const struct genl_ops fou_nl_ops[] = { | |||
668 | }, | 668 | }, |
669 | }; | 669 | }; |
670 | 670 | ||
671 | size_t fou_encap_hlen(struct ip_tunnel_encap *e) | ||
672 | { | ||
673 | return sizeof(struct udphdr); | ||
674 | } | ||
675 | EXPORT_SYMBOL(fou_encap_hlen); | ||
676 | |||
677 | size_t gue_encap_hlen(struct ip_tunnel_encap *e) | ||
678 | { | ||
679 | size_t len; | ||
680 | bool need_priv = false; | ||
681 | |||
682 | len = sizeof(struct udphdr) + sizeof(struct guehdr); | ||
683 | |||
684 | if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) { | ||
685 | len += GUE_PLEN_REMCSUM; | ||
686 | need_priv = true; | ||
687 | } | ||
688 | |||
689 | len += need_priv ? GUE_LEN_PRIV : 0; | ||
690 | |||
691 | return len; | ||
692 | } | ||
693 | EXPORT_SYMBOL(gue_encap_hlen); | ||
694 | |||
671 | static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e, | 695 | static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e, |
672 | struct flowi4 *fl4, u8 *protocol, __be16 sport) | 696 | struct flowi4 *fl4, u8 *protocol, __be16 sport) |
673 | { | 697 | { |
@@ -787,6 +811,57 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | |||
787 | } | 811 | } |
788 | EXPORT_SYMBOL(gue_build_header); | 812 | EXPORT_SYMBOL(gue_build_header); |
789 | 813 | ||
814 | #ifdef CONFIG_NET_FOU_IP_TUNNELS | ||
815 | |||
816 | static const struct ip_tunnel_encap_ops __read_mostly fou_iptun_ops = { | ||
817 | .encap_hlen = fou_encap_hlen, | ||
818 | .build_header = fou_build_header, | ||
819 | }; | ||
820 | |||
821 | static const struct ip_tunnel_encap_ops __read_mostly gue_iptun_ops = { | ||
822 | .encap_hlen = gue_encap_hlen, | ||
823 | .build_header = gue_build_header, | ||
824 | }; | ||
825 | |||
826 | static int ip_tunnel_encap_add_fou_ops(void) | ||
827 | { | ||
828 | int ret; | ||
829 | |||
830 | ret = ip_tunnel_encap_add_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); | ||
831 | if (ret < 0) { | ||
832 | pr_err("can't add fou ops\n"); | ||
833 | return ret; | ||
834 | } | ||
835 | |||
836 | ret = ip_tunnel_encap_add_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE); | ||
837 | if (ret < 0) { | ||
838 | pr_err("can't add gue ops\n"); | ||
839 | ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); | ||
840 | return ret; | ||
841 | } | ||
842 | |||
843 | return 0; | ||
844 | } | ||
845 | |||
846 | static void ip_tunnel_encap_del_fou_ops(void) | ||
847 | { | ||
848 | ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); | ||
849 | ip_tunnel_encap_del_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE); | ||
850 | } | ||
851 | |||
852 | #else | ||
853 | |||
854 | static int ip_tunnel_encap_add_fou_ops(void) | ||
855 | { | ||
856 | return 0; | ||
857 | } | ||
858 | |||
859 | static int ip_tunnel_encap_del_fou_ops(void) | ||
860 | { | ||
861 | } | ||
862 | |||
863 | #endif | ||
864 | |||
790 | static int __init fou_init(void) | 865 | static int __init fou_init(void) |
791 | { | 866 | { |
792 | int ret; | 867 | int ret; |
@@ -794,6 +869,14 @@ static int __init fou_init(void) | |||
794 | ret = genl_register_family_with_ops(&fou_nl_family, | 869 | ret = genl_register_family_with_ops(&fou_nl_family, |
795 | fou_nl_ops); | 870 | fou_nl_ops); |
796 | 871 | ||
872 | if (ret < 0) | ||
873 | goto exit; | ||
874 | |||
875 | ret = ip_tunnel_encap_add_fou_ops(); | ||
876 | if (ret < 0) | ||
877 | genl_unregister_family(&fou_nl_family); | ||
878 | |||
879 | exit: | ||
797 | return ret; | 880 | return ret; |
798 | } | 881 | } |
799 | 882 | ||
@@ -801,6 +884,8 @@ static void __exit fou_fini(void) | |||
801 | { | 884 | { |
802 | struct fou *fou, *next; | 885 | struct fou *fou, *next; |
803 | 886 | ||
887 | ip_tunnel_encap_del_fou_ops(); | ||
888 | |||
804 | genl_unregister_family(&fou_nl_family); | 889 | genl_unregister_family(&fou_nl_family); |
805 | 890 | ||
806 | /* Close all the FOU sockets */ | 891 | /* Close all the FOU sockets */ |
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index c3587e1c8b82..63e745aadab6 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c | |||
@@ -57,10 +57,6 @@ | |||
57 | #include <net/rtnetlink.h> | 57 | #include <net/rtnetlink.h> |
58 | #include <net/udp.h> | 58 | #include <net/udp.h> |
59 | 59 | ||
60 | #if IS_ENABLED(CONFIG_NET_FOU) | ||
61 | #include <net/fou.h> | ||
62 | #endif | ||
63 | |||
64 | #if IS_ENABLED(CONFIG_IPV6) | 60 | #if IS_ENABLED(CONFIG_IPV6) |
65 | #include <net/ipv6.h> | 61 | #include <net/ipv6.h> |
66 | #include <net/ip6_fib.h> | 62 | #include <net/ip6_fib.h> |
@@ -494,19 +490,50 @@ EXPORT_SYMBOL_GPL(ip_tunnel_rcv); | |||
494 | 490 | ||
495 | static int ip_encap_hlen(struct ip_tunnel_encap *e) | 491 | static int ip_encap_hlen(struct ip_tunnel_encap *e) |
496 | { | 492 | { |
497 | switch (e->type) { | 493 | const struct ip_tunnel_encap_ops *ops; |
498 | case TUNNEL_ENCAP_NONE: | 494 | int hlen = -EINVAL; |
495 | |||
496 | if (e->type == TUNNEL_ENCAP_NONE) | ||
499 | return 0; | 497 | return 0; |
500 | #if IS_ENABLED(CONFIG_NET_FOU) | 498 | |
501 | case TUNNEL_ENCAP_FOU: | 499 | if (e->type >= MAX_IPTUN_ENCAP_OPS) |
502 | return fou_encap_hlen(e); | ||
503 | case TUNNEL_ENCAP_GUE: | ||
504 | return gue_encap_hlen(e); | ||
505 | #endif | ||
506 | default: | ||
507 | return -EINVAL; | 500 | return -EINVAL; |
508 | } | 501 | |
502 | rcu_read_lock(); | ||
503 | ops = rcu_dereference(iptun_encaps[e->type]); | ||
504 | if (likely(ops && ops->encap_hlen)) | ||
505 | hlen = ops->encap_hlen(e); | ||
506 | rcu_read_unlock(); | ||
507 | |||
508 | return hlen; | ||
509 | } | ||
510 | |||
511 | const struct ip_tunnel_encap_ops __rcu * | ||
512 | iptun_encaps[MAX_IPTUN_ENCAP_OPS] __read_mostly; | ||
513 | |||
514 | int ip_tunnel_encap_add_ops(const struct ip_tunnel_encap_ops *ops, | ||
515 | unsigned int num) | ||
516 | { | ||
517 | return !cmpxchg((const struct ip_tunnel_encap_ops **) | ||
518 | &iptun_encaps[num], | ||
519 | NULL, ops) ? 0 : -1; | ||
509 | } | 520 | } |
521 | EXPORT_SYMBOL(ip_tunnel_encap_add_ops); | ||
522 | |||
523 | int ip_tunnel_encap_del_ops(const struct ip_tunnel_encap_ops *ops, | ||
524 | unsigned int num) | ||
525 | { | ||
526 | int ret; | ||
527 | |||
528 | ret = (cmpxchg((const struct ip_tunnel_encap_ops **) | ||
529 | &iptun_encaps[num], | ||
530 | ops, NULL) == ops) ? 0 : -1; | ||
531 | |||
532 | synchronize_net(); | ||
533 | |||
534 | return ret; | ||
535 | } | ||
536 | EXPORT_SYMBOL(ip_tunnel_encap_del_ops); | ||
510 | 537 | ||
511 | int ip_tunnel_encap_setup(struct ip_tunnel *t, | 538 | int ip_tunnel_encap_setup(struct ip_tunnel *t, |
512 | struct ip_tunnel_encap *ipencap) | 539 | struct ip_tunnel_encap *ipencap) |
@@ -534,18 +561,19 @@ EXPORT_SYMBOL_GPL(ip_tunnel_encap_setup); | |||
534 | int ip_tunnel_encap(struct sk_buff *skb, struct ip_tunnel *t, | 561 | int ip_tunnel_encap(struct sk_buff *skb, struct ip_tunnel *t, |
535 | u8 *protocol, struct flowi4 *fl4) | 562 | u8 *protocol, struct flowi4 *fl4) |
536 | { | 563 | { |
537 | switch (t->encap.type) { | 564 | const struct ip_tunnel_encap_ops *ops; |
538 | case TUNNEL_ENCAP_NONE: | 565 | int ret = -EINVAL; |
566 | |||
567 | if (t->encap.type == TUNNEL_ENCAP_NONE) | ||
539 | return 0; | 568 | return 0; |
540 | #if IS_ENABLED(CONFIG_NET_FOU) | 569 | |
541 | case TUNNEL_ENCAP_FOU: | 570 | rcu_read_lock(); |
542 | return fou_build_header(skb, &t->encap, protocol, fl4); | 571 | ops = rcu_dereference(iptun_encaps[t->encap.type]); |
543 | case TUNNEL_ENCAP_GUE: | 572 | if (likely(ops && ops->build_header)) |
544 | return gue_build_header(skb, &t->encap, protocol, fl4); | 573 | ret = ops->build_header(skb, &t->encap, protocol, fl4); |
545 | #endif | 574 | rcu_read_unlock(); |
546 | default: | 575 | |
547 | return -EINVAL; | 576 | return ret; |
548 | } | ||
549 | } | 577 | } |
550 | EXPORT_SYMBOL(ip_tunnel_encap); | 578 | EXPORT_SYMBOL(ip_tunnel_encap); |
551 | 579 | ||