Skip to content

Commit 9ed5959

Browse files
David Aherndavem330
authored andcommitted
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: 745041e ("lwtunnel: autoload of lwt modules") Signed-off-by: David Ahern <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 719ca81 commit 9ed5959

File tree

4 files changed

+86
-7
lines changed

4 files changed

+86
-7
lines changed

include/net/lwtunnel.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *op,
105105
unsigned int num);
106106
int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op,
107107
unsigned int num);
108+
int lwtunnel_valid_encap_type(u16 encap_type);
109+
int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int len);
108110
int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
109111
struct nlattr *encap,
110112
unsigned int family, const void *cfg,
@@ -168,6 +170,15 @@ static inline int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op,
168170
return -EOPNOTSUPP;
169171
}
170172

173+
static inline int lwtunnel_valid_encap_type(u16 encap_type)
174+
{
175+
return -EOPNOTSUPP;
176+
}
177+
static inline int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int len)
178+
{
179+
return -EOPNOTSUPP;
180+
}
181+
171182
static inline int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
172183
struct nlattr *encap,
173184
unsigned int family, const void *cfg,

net/core/lwtunnel.c

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <net/lwtunnel.h>
2727
#include <net/rtnetlink.h>
2828
#include <net/ip6_fib.h>
29+
#include <net/nexthop.h>
2930

3031
#ifdef CONFIG_MODULES
3132

@@ -114,25 +115,74 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
114115
ret = -EOPNOTSUPP;
115116
rcu_read_lock();
116117
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+
}
124+
EXPORT_SYMBOL(lwtunnel_build_state);
125+
126+
int 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();
117138
#ifdef CONFIG_MODULES
118139
if (!ops) {
119140
const char *encap_type_str = lwtunnel_encap_str(encap_type);
120141

121142
if (encap_type_str) {
122-
rcu_read_unlock();
143+
__rtnl_unlock();
123144
request_module("rtnl-lwt-%s", encap_type_str);
145+
rtnl_lock();
146+
124147
rcu_read_lock();
125148
ops = rcu_dereference(lwtun_encaps[encap_type]);
149+
rcu_read_unlock();
126150
}
127151
}
128152
#endif
129-
if (likely(ops && ops->build_state))
130-
ret = ops->build_state(dev, encap, family, cfg, lws);
131-
rcu_read_unlock();
153+
return ops ? 0 : -EOPNOTSUPP;
154+
}
155+
EXPORT_SYMBOL(lwtunnel_valid_encap_type);
132156

133-
return ret;
157+
int 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;
134184
}
135-
EXPORT_SYMBOL(lwtunnel_build_state);
185+
EXPORT_SYMBOL(lwtunnel_valid_encap_type_attr);
136186

137187
void lwtstate_free(struct lwtunnel_state *lws)
138188
{

net/ipv4/fib_frontend.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <net/rtnetlink.h>
4747
#include <net/xfrm.h>
4848
#include <net/l3mdev.h>
49+
#include <net/lwtunnel.h>
4950
#include <trace/events/fib.h>
5051

5152
#ifndef CONFIG_IP_MULTIPLE_TABLES
@@ -677,6 +678,10 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
677678
cfg->fc_mx_len = nla_len(attr);
678679
break;
679680
case RTA_MULTIPATH:
681+
err = lwtunnel_valid_encap_type_attr(nla_data(attr),
682+
nla_len(attr));
683+
if (err < 0)
684+
goto errout;
680685
cfg->fc_mp = nla_data(attr);
681686
cfg->fc_mp_len = nla_len(attr);
682687
break;
@@ -691,6 +696,9 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
691696
break;
692697
case RTA_ENCAP_TYPE:
693698
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;
694702
break;
695703
}
696704
}

net/ipv6/route.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2896,6 +2896,11 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
28962896
if (tb[RTA_MULTIPATH]) {
28972897
cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
28982898
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;
28992904
}
29002905

29012906
if (tb[RTA_PREF]) {
@@ -2909,9 +2914,14 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
29092914
if (tb[RTA_ENCAP])
29102915
cfg->fc_encap = tb[RTA_ENCAP];
29112916

2912-
if (tb[RTA_ENCAP_TYPE])
2917+
if (tb[RTA_ENCAP_TYPE]) {
29132918
cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
29142919

2920+
err = lwtunnel_valid_encap_type(cfg->fc_encap_type);
2921+
if (err < 0)
2922+
goto errout;
2923+
}
2924+
29152925
if (tb[RTA_EXPIRES]) {
29162926
unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
29172927

0 commit comments

Comments
 (0)