aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Ahern <dsa@cumulusnetworks.com>2017-01-17 17:57:36 -0500
committerDavid S. Miller <davem@davemloft.net>2017-01-18 17:07:14 -0500
commit9ed59592e3e379b2e9557dc1d9e9ec8fcbb33f16 (patch)
treecdbe6db947985c50e17f87f58ada3a21c7a834b3
parent719ca8111402aa6157bd83a3c966d184db0d8956 (diff)
lwtunnel: fix autoload of lwt modules
Trying to add an mpls encap route when the MPLS modules are not loaded hangs. For example: CONFIG_MPLS=y CONFIG_NET_MPLS_GSO=m CONFIG_MPLS_ROUTING=m CONFIG_MPLS_IPTUNNEL=m $ ip route add 10.10.10.10/32 encap mpls 100 via inet 10.100.1.2 The ip command hangs: root 880 826 0 21:25 pts/0 00:00:00 ip route add 10.10.10.10/32 encap mpls 100 via inet 10.100.1.2 $ cat /proc/880/stack [<ffffffff81065a9b>] call_usermodehelper_exec+0xd6/0x134 [<ffffffff81065efc>] __request_module+0x27b/0x30a [<ffffffff814542f6>] lwtunnel_build_state+0xe4/0x178 [<ffffffff814aa1e4>] fib_create_info+0x47f/0xdd4 [<ffffffff814ae451>] fib_table_insert+0x90/0x41f [<ffffffff814a8010>] inet_rtm_newroute+0x4b/0x52 ... modprobe is trying to load rtnl-lwt-MPLS: root 881 5 0 21:25 ? 00:00:00 /sbin/modprobe -q -- rtnl-lwt-MPLS and it hangs after loading mpls_router: $ cat /proc/881/stack [<ffffffff81441537>] rtnl_lock+0x12/0x14 [<ffffffff8142ca2a>] register_netdevice_notifier+0x16/0x179 [<ffffffffa0033025>] mpls_init+0x25/0x1000 [mpls_router] [<ffffffff81000471>] do_one_initcall+0x8e/0x13f [<ffffffff81119961>] do_init_module+0x5a/0x1e5 [<ffffffff810bd070>] load_module+0x13bd/0x17d6 ... The problem is that lwtunnel_build_state is called with rtnl lock held preventing mpls_init from registering. Given the potential references held by the time lwtunnel_build_state it can not drop the rtnl lock to the load module. So, extract the module loading code from lwtunnel_build_state into a new function to validate the encap type. The new function is called while converting the user request into a fib_config which is well before any table, device or fib entries are examined. Fixes: 745041e2aaf1 ("lwtunnel: autoload of lwt modules") Signed-off-by: David Ahern <dsa@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/lwtunnel.h11
-rw-r--r--net/core/lwtunnel.c62
-rw-r--r--net/ipv4/fib_frontend.c8
-rw-r--r--net/ipv6/route.c12
4 files changed, 86 insertions, 7 deletions
diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h
index d4c1c75b8862..0b585f1fd340 100644
--- a/include/net/lwtunnel.h
+++ b/include/net/lwtunnel.h
@@ -105,6 +105,8 @@ int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *op,
105 unsigned int num); 105 unsigned int num);
106int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op, 106int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op,
107 unsigned int num); 107 unsigned int num);
108int lwtunnel_valid_encap_type(u16 encap_type);
109int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int len);
108int lwtunnel_build_state(struct net_device *dev, u16 encap_type, 110int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
109 struct nlattr *encap, 111 struct nlattr *encap,
110 unsigned int family, const void *cfg, 112 unsigned int family, const void *cfg,
@@ -168,6 +170,15 @@ static inline int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op,
168 return -EOPNOTSUPP; 170 return -EOPNOTSUPP;
169} 171}
170 172
173static inline int lwtunnel_valid_encap_type(u16 encap_type)
174{
175 return -EOPNOTSUPP;
176}
177static inline int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int len)
178{
179 return -EOPNOTSUPP;
180}
181
171static inline int lwtunnel_build_state(struct net_device *dev, u16 encap_type, 182static inline int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
172 struct nlattr *encap, 183 struct nlattr *encap,
173 unsigned int family, const void *cfg, 184 unsigned int family, const void *cfg,
diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c
index a5d4e866ce88..47b1dd65947b 100644
--- a/net/core/lwtunnel.c
+++ b/net/core/lwtunnel.c
@@ -26,6 +26,7 @@
26#include <net/lwtunnel.h> 26#include <net/lwtunnel.h>
27#include <net/rtnetlink.h> 27#include <net/rtnetlink.h>
28#include <net/ip6_fib.h> 28#include <net/ip6_fib.h>
29#include <net/nexthop.h>
29 30
30#ifdef CONFIG_MODULES 31#ifdef CONFIG_MODULES
31 32
@@ -114,25 +115,74 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
114 ret = -EOPNOTSUPP; 115 ret = -EOPNOTSUPP;
115 rcu_read_lock(); 116 rcu_read_lock();
116 ops = rcu_dereference(lwtun_encaps[encap_type]); 117 ops = rcu_dereference(lwtun_encaps[encap_type]);
118 if (likely(ops && ops->build_state))
119 ret = ops->build_state(dev, encap, family, cfg, lws);
120 rcu_read_unlock();
121
122 return ret;
123}
124EXPORT_SYMBOL(lwtunnel_build_state);
125
126int lwtunnel_valid_encap_type(u16 encap_type)
127{
128 const struct lwtunnel_encap_ops *ops;
129 int ret = -EINVAL;
130
131 if (encap_type == LWTUNNEL_ENCAP_NONE ||
132 encap_type > LWTUNNEL_ENCAP_MAX)
133 return ret;
134
135 rcu_read_lock();
136 ops = rcu_dereference(lwtun_encaps[encap_type]);
137 rcu_read_unlock();
117#ifdef CONFIG_MODULES 138#ifdef CONFIG_MODULES
118 if (!ops) { 139 if (!ops) {
119 const char *encap_type_str = lwtunnel_encap_str(encap_type); 140 const char *encap_type_str = lwtunnel_encap_str(encap_type);
120 141
121 if (encap_type_str) { 142 if (encap_type_str) {
122 rcu_read_unlock(); 143 __rtnl_unlock();
123 request_module("rtnl-lwt-%s", encap_type_str); 144 request_module("rtnl-lwt-%s", encap_type_str);
145 rtnl_lock();
146
124 rcu_read_lock(); 147 rcu_read_lock();
125 ops = rcu_dereference(lwtun_encaps[encap_type]); 148 ops = rcu_dereference(lwtun_encaps[encap_type]);
149 rcu_read_unlock();
126 } 150 }
127 } 151 }
128#endif 152#endif
129 if (likely(ops && ops->build_state)) 153 return ops ? 0 : -EOPNOTSUPP;
130 ret = ops->build_state(dev, encap, family, cfg, lws); 154}
131 rcu_read_unlock(); 155EXPORT_SYMBOL(lwtunnel_valid_encap_type);
132 156
133 return ret; 157int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining)
158{
159 struct rtnexthop *rtnh = (struct rtnexthop *)attr;
160 struct nlattr *nla_entype;
161 struct nlattr *attrs;
162 struct nlattr *nla;
163 u16 encap_type;
164 int attrlen;
165
166 while (rtnh_ok(rtnh, remaining)) {
167 attrlen = rtnh_attrlen(rtnh);
168 if (attrlen > 0) {
169 attrs = rtnh_attrs(rtnh);
170 nla = nla_find(attrs, attrlen, RTA_ENCAP);
171 nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
172
173 if (nla_entype) {
174 encap_type = nla_get_u16(nla_entype);
175
176 if (lwtunnel_valid_encap_type(encap_type) != 0)
177 return -EOPNOTSUPP;
178 }
179 }
180 rtnh = rtnh_next(rtnh, &remaining);
181 }
182
183 return 0;
134} 184}
135EXPORT_SYMBOL(lwtunnel_build_state); 185EXPORT_SYMBOL(lwtunnel_valid_encap_type_attr);
136 186
137void lwtstate_free(struct lwtunnel_state *lws) 187void lwtstate_free(struct lwtunnel_state *lws)
138{ 188{
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index eae0332b0e8c..7db2ad2e82d3 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -46,6 +46,7 @@
46#include <net/rtnetlink.h> 46#include <net/rtnetlink.h>
47#include <net/xfrm.h> 47#include <net/xfrm.h>
48#include <net/l3mdev.h> 48#include <net/l3mdev.h>
49#include <net/lwtunnel.h>
49#include <trace/events/fib.h> 50#include <trace/events/fib.h>
50 51
51#ifndef CONFIG_IP_MULTIPLE_TABLES 52#ifndef CONFIG_IP_MULTIPLE_TABLES
@@ -677,6 +678,10 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
677 cfg->fc_mx_len = nla_len(attr); 678 cfg->fc_mx_len = nla_len(attr);
678 break; 679 break;
679 case RTA_MULTIPATH: 680 case RTA_MULTIPATH:
681 err = lwtunnel_valid_encap_type_attr(nla_data(attr),
682 nla_len(attr));
683 if (err < 0)
684 goto errout;
680 cfg->fc_mp = nla_data(attr); 685 cfg->fc_mp = nla_data(attr);
681 cfg->fc_mp_len = nla_len(attr); 686 cfg->fc_mp_len = nla_len(attr);
682 break; 687 break;
@@ -691,6 +696,9 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
691 break; 696 break;
692 case RTA_ENCAP_TYPE: 697 case RTA_ENCAP_TYPE:
693 cfg->fc_encap_type = nla_get_u16(attr); 698 cfg->fc_encap_type = nla_get_u16(attr);
699 err = lwtunnel_valid_encap_type(cfg->fc_encap_type);
700 if (err < 0)
701 goto errout;
694 break; 702 break;
695 } 703 }
696 } 704 }
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 4f6b067c8753..7ea85370c11c 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2896,6 +2896,11 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2896 if (tb[RTA_MULTIPATH]) { 2896 if (tb[RTA_MULTIPATH]) {
2897 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 2897 cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
2898 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 2898 cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
2899
2900 err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
2901 cfg->fc_mp_len);
2902 if (err < 0)
2903 goto errout;
2899 } 2904 }
2900 2905
2901 if (tb[RTA_PREF]) { 2906 if (tb[RTA_PREF]) {
@@ -2909,9 +2914,14 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
2909 if (tb[RTA_ENCAP]) 2914 if (tb[RTA_ENCAP])
2910 cfg->fc_encap = tb[RTA_ENCAP]; 2915 cfg->fc_encap = tb[RTA_ENCAP];
2911 2916
2912 if (tb[RTA_ENCAP_TYPE]) 2917 if (tb[RTA_ENCAP_TYPE]) {
2913 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 2918 cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
2914 2919
2920 err = lwtunnel_valid_encap_type(cfg->fc_encap_type);
2921 if (err < 0)
2922 goto errout;
2923 }
2924
2915 if (tb[RTA_EXPIRES]) { 2925 if (tb[RTA_EXPIRES]) {
2916 unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ); 2926 unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
2917 2927