diff mbox

[net-next,1/3] lwtunnel: autoload of lwt modules

Message ID 1455550923-23673-2-git-send-email-rshearma@brocade.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Robert Shearman Feb. 15, 2016, 3:42 p.m. UTC
The lwt implementations using net devices can autoload using the
existing mechanism using IFLA_INFO_KIND. However, there's no mechanism
that lwt modules not using net devices can use.

Therefore, add the ability to autoload modules registering lwt
operations for lwt implementations not using a net device so that
users don't have to manually load the modules.

Signed-off-by: Robert Shearman <rshearma@brocade.com>
---
 include/net/lwtunnel.h |  4 +++-
 net/core/lwtunnel.c    | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)

Comments

Jiri Benc Feb. 15, 2016, 4:02 p.m. UTC | #1
On Mon, 15 Feb 2016 15:42:01 +0000, Robert Shearman wrote:
> +static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
> +{
> +	switch (encap_type) {
> +	case LWTUNNEL_ENCAP_MPLS:
> +		return "LWTUNNEL_ENCAP_MPLS";
> +	case LWTUNNEL_ENCAP_IP:
> +		return "LWTUNNEL_ENCAP_IP";
> +	case LWTUNNEL_ENCAP_ILA:
> +		return "LWTUNNEL_ENCAP_ILA";
> +	case LWTUNNEL_ENCAP_IP6:
> +		return "LWTUNNEL_ENCAP_IP6";
> +	case LWTUNNEL_ENCAP_NONE:
> +	case __LWTUNNEL_ENCAP_MAX:
> +		/* should not have got here */
> +		break;
> +	}
> +	WARN_ON(1);
> +	return "LWTUNNEL_ENCAP_NONE";
> +}
> +
> +#endif /* CONFIG_MODULES */
> +
>  struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
>  {
>  	struct lwtunnel_state *lws;
> @@ -85,6 +109,14 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
>  	ret = -EOPNOTSUPP;
>  	rcu_read_lock();
>  	ops = rcu_dereference(lwtun_encaps[encap_type]);
> +#ifdef CONFIG_MODULES
> +	if (!ops) {
> +		rcu_read_unlock();
> +		request_module("rtnl-lwt-%s", lwtunnel_encap_str(encap_type));

Why the repeating of "lwt"/"LWTUNNEL" and the unnecessary "ENCAP"?
Wouldn't be lwtunnel_encap_str returning just "MPLS" or "ILA" enough?
I don't have any strong preference here, it just looks weird to me.
Maybe there's a reason.

Also, this doesn't affect IP lwtunnels, i.e. LWTUNNEL_ENCAP_IP and
LWTUNNEL_ENCAP_IP6. Should we just return NULL from lwtunnel_encap_str
in such cases (plus unknown encap_type) and WARN on the NULL here?

 Jiri
Robert Shearman Feb. 15, 2016, 4:22 p.m. UTC | #2
On 15/02/16 16:02, Jiri Benc wrote:
> On Mon, 15 Feb 2016 15:42:01 +0000, Robert Shearman wrote:
>> +static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
>> +{
>> +	switch (encap_type) {
>> +	case LWTUNNEL_ENCAP_MPLS:
>> +		return "LWTUNNEL_ENCAP_MPLS";
>> +	case LWTUNNEL_ENCAP_IP:
>> +		return "LWTUNNEL_ENCAP_IP";
>> +	case LWTUNNEL_ENCAP_ILA:
>> +		return "LWTUNNEL_ENCAP_ILA";
>> +	case LWTUNNEL_ENCAP_IP6:
>> +		return "LWTUNNEL_ENCAP_IP6";
>> +	case LWTUNNEL_ENCAP_NONE:
>> +	case __LWTUNNEL_ENCAP_MAX:
>> +		/* should not have got here */
>> +		break;
>> +	}
>> +	WARN_ON(1);
>> +	return "LWTUNNEL_ENCAP_NONE";
>> +}
>> +
>> +#endif /* CONFIG_MODULES */
>> +
>>   struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
>>   {
>>   	struct lwtunnel_state *lws;
>> @@ -85,6 +109,14 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
>>   	ret = -EOPNOTSUPP;
>>   	rcu_read_lock();
>>   	ops = rcu_dereference(lwtun_encaps[encap_type]);
>> +#ifdef CONFIG_MODULES
>> +	if (!ops) {
>> +		rcu_read_unlock();
>> +		request_module("rtnl-lwt-%s", lwtunnel_encap_str(encap_type));
>
> Why the repeating of "lwt"/"LWTUNNEL" and the unnecessary "ENCAP"?
> Wouldn't be lwtunnel_encap_str returning just "MPLS" or "ILA" enough?
> I don't have any strong preference here, it just looks weird to me.
> Maybe there's a reason.

Yeah, it's the C preprocessor. MODULE_ALIAS_RTNL_LWT includes the string 
for the encap type in the module alias, and since the LWT encap types 
are defined as enums this is symbolic name. I can't see any way of 
getting the preprocessor to convert 
MODULE_ALIAS_RTNL_LWT(LWTUNNEL_ENCAP_MPLS) into "rtnl-lwt-MPLS", but I'm 
open to suggestions.

I could just drop the "lwt-" bit of the alias string given that it's 
included in the name of the enum values.

> Also, this doesn't affect IP lwtunnels, i.e. LWTUNNEL_ENCAP_IP and
> LWTUNNEL_ENCAP_IP6. Should we just return NULL from lwtunnel_encap_str
> in such cases (plus unknown encap_type) and WARN on the NULL here?

True, but I figured that it was cleaner for the lwtunnel infra to not 
assume whether how those modules are implemented. If you disagree, then 
I can change to doing as you suggest.

Thanks,
Rob
Jiri Benc Feb. 15, 2016, 4:32 p.m. UTC | #3
On Mon, 15 Feb 2016 16:22:08 +0000, Robert Shearman wrote:
> Yeah, it's the C preprocessor. MODULE_ALIAS_RTNL_LWT includes the string 
> for the encap type in the module alias, and since the LWT encap types 
> are defined as enums this is symbolic name. I can't see any way of 
> getting the preprocessor to convert 
> MODULE_ALIAS_RTNL_LWT(LWTUNNEL_ENCAP_MPLS) into "rtnl-lwt-MPLS", but I'm 
> open to suggestions.

MODULE_ALIAS_RTNL_LWT(MPLS)?

But whatever, as I said, no strong preference.

> True, but I figured that it was cleaner for the lwtunnel infra to not 
> assume whether how those modules are implemented. If you disagree, then 
> I can change to doing as you suggest.

It's not completely transparent to the infrastructure anyway, the
tunnel type needs to be added to lwtunnel_encap_str for new tunnels.
The way I suggested, it's only added for those tunnels having the
module alias set.

Just trying to get rid of the unnecessary strings in
lwtunnel_encap_str. There's no need to bloat kernel with them if
they're never used.

 Jiri
Robert Shearman Feb. 15, 2016, 6:08 p.m. UTC | #4
On 15/02/16 16:32, Jiri Benc wrote:
> On Mon, 15 Feb 2016 16:22:08 +0000, Robert Shearman wrote:
>> Yeah, it's the C preprocessor. MODULE_ALIAS_RTNL_LWT includes the string
>> for the encap type in the module alias, and since the LWT encap types
>> are defined as enums this is symbolic name. I can't see any way of
>> getting the preprocessor to convert
>> MODULE_ALIAS_RTNL_LWT(LWTUNNEL_ENCAP_MPLS) into "rtnl-lwt-MPLS", but I'm
>> open to suggestions.
>
> MODULE_ALIAS_RTNL_LWT(MPLS)?
>
> But whatever, as I said, no strong preference.

I was so hung up on the making the string match the name of the enum 
that I'd discounted that, but you're right that doing that would reduce 
duplication in the alias string.

>> True, but I figured that it was cleaner for the lwtunnel infra to not
>> assume whether how those modules are implemented. If you disagree, then
>> I can change to doing as you suggest.
>
> It's not completely transparent to the infrastructure anyway, the
> tunnel type needs to be added to lwtunnel_encap_str for new tunnels.
> The way I suggested, it's only added for those tunnels having the
> module alias set.
>
> Just trying to get rid of the unnecessary strings in
> lwtunnel_encap_str. There's no need to bloat kernel with them if
> they're never used.

Ok, will resubmit without the unnecessary strings in that function as 
well as with your suggestion above.

Thanks for the review,
Rob
Eric W. Biederman Feb. 15, 2016, 9:33 p.m. UTC | #5
Robert Shearman <rshearma@brocade.com> writes:

> The lwt implementations using net devices can autoload using the
> existing mechanism using IFLA_INFO_KIND. However, there's no mechanism
> that lwt modules not using net devices can use.
>
> Therefore, add the ability to autoload modules registering lwt
> operations for lwt implementations not using a net device so that
> users don't have to manually load the modules.
>
> Signed-off-by: Robert Shearman <rshearma@brocade.com>
> ---
>  include/net/lwtunnel.h |  4 +++-
>  net/core/lwtunnel.c    | 32 ++++++++++++++++++++++++++++++++
>  2 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h
> index 66350ce3e955..e9f116e29c22 100644
> --- a/include/net/lwtunnel.h
> +++ b/include/net/lwtunnel.h
> @@ -170,6 +170,8 @@ static inline int lwtunnel_input(struct sk_buff *skb)
>  	return -EOPNOTSUPP;
>  }
>  
> -#endif
> +#endif /* CONFIG_LWTUNNEL */
> +
> +#define MODULE_ALIAS_RTNL_LWT(encap_type) MODULE_ALIAS("rtnl-lwt-" __stringify(encap_type))
>  
>  #endif /* __NET_LWTUNNEL_H */
> diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c
> index 299cfc24d888..8ef5e5cec03e 100644
> --- a/net/core/lwtunnel.c
> +++ b/net/core/lwtunnel.c
> @@ -27,6 +27,30 @@
>  #include <net/rtnetlink.h>
>  #include <net/ip6_fib.h>
>  
> +#ifdef CONFIG_MODULES
> +
> +static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
> +{
> +	switch (encap_type) {
> +	case LWTUNNEL_ENCAP_MPLS:
> +		return "LWTUNNEL_ENCAP_MPLS";
> +	case LWTUNNEL_ENCAP_IP:
> +		return "LWTUNNEL_ENCAP_IP";
> +	case LWTUNNEL_ENCAP_ILA:
> +		return "LWTUNNEL_ENCAP_ILA";
> +	case LWTUNNEL_ENCAP_IP6:
> +		return "LWTUNNEL_ENCAP_IP6";
> +	case LWTUNNEL_ENCAP_NONE:
> +	case __LWTUNNEL_ENCAP_MAX:
> +		/* should not have got here */
> +		break;
> +	}
> +	WARN_ON(1);
> +	return "LWTUNNEL_ENCAP_NONE";
> +}
> +
> +#endif /* CONFIG_MODULES */
> +
>  struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
>  {
>  	struct lwtunnel_state *lws;
> @@ -85,6 +109,14 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
>  	ret = -EOPNOTSUPP;
>  	rcu_read_lock();
>  	ops = rcu_dereference(lwtun_encaps[encap_type]);
> +#ifdef CONFIG_MODULES
> +	if (!ops) {
> +		rcu_read_unlock();
> +		request_module("rtnl-lwt-%s", lwtunnel_encap_str(encap_type));
> +		rcu_read_lock();
> +		ops = rcu_dereference(lwtun_encaps[encap_type]);
> +	}
> +#endif
>  	if (likely(ops && ops->build_state))
>  		ret = ops->build_state(dev, encap, family, cfg, lws);
>  	rcu_read_unlock();

My memory is fuzzy on how this is done elsewhere but this looks like it
needs a capability check to ensure that non-root user's can't trigger
this.

It tends to be problematic if a non-root user can trigger an autoload of
a known-buggy module.  With a combination of user namespaces and network
namespaces unprivileged users can cause just about every corner of the
network stack to be exercised.

Eric
Robert Shearman Feb. 16, 2016, 2:14 p.m. UTC | #6
On 15/02/16 21:33, Eric W. Biederman wrote:
> Robert Shearman <rshearma@brocade.com> writes:
>> @@ -85,6 +109,14 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
>>   	ret = -EOPNOTSUPP;
>>   	rcu_read_lock();
>>   	ops = rcu_dereference(lwtun_encaps[encap_type]);
>> +#ifdef CONFIG_MODULES
>> +	if (!ops) {
>> +		rcu_read_unlock();
>> +		request_module("rtnl-lwt-%s", lwtunnel_encap_str(encap_type));
>> +		rcu_read_lock();
>> +		ops = rcu_dereference(lwtun_encaps[encap_type]);
>> +	}
>> +#endif
>>   	if (likely(ops && ops->build_state))
>>   		ret = ops->build_state(dev, encap, family, cfg, lws);
>>   	rcu_read_unlock();
>
> My memory is fuzzy on how this is done elsewhere but this looks like it
> needs a capability check to ensure that non-root user's can't trigger
> this.
>
> It tends to be problematic if a non-root user can trigger an autoload of
> a known-buggy module.  With a combination of user namespaces and network
> namespaces unprivileged users can cause just about every corner of the
> network stack to be exercised.

The same protections apply to this as to the IFLA_INFO_KIND module 
autoloading, namely by rtnetlink_rcv_msg ensuring that no messages other 
than gets can be done by an unprivileged user:

	type = nlh->nlmsg_type;
...
	type -= RTM_BASE;
...
	kind = type&3;

	if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN))
		return -EPERM;

The lwtunnel_build_state function is only called by the processing of 
non-get message types.

Is this sufficient or are you looking for something in addition?

Thanks,
Rob
diff mbox

Patch

diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h
index 66350ce3e955..e9f116e29c22 100644
--- a/include/net/lwtunnel.h
+++ b/include/net/lwtunnel.h
@@ -170,6 +170,8 @@  static inline int lwtunnel_input(struct sk_buff *skb)
 	return -EOPNOTSUPP;
 }
 
-#endif
+#endif /* CONFIG_LWTUNNEL */
+
+#define MODULE_ALIAS_RTNL_LWT(encap_type) MODULE_ALIAS("rtnl-lwt-" __stringify(encap_type))
 
 #endif /* __NET_LWTUNNEL_H */
diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c
index 299cfc24d888..8ef5e5cec03e 100644
--- a/net/core/lwtunnel.c
+++ b/net/core/lwtunnel.c
@@ -27,6 +27,30 @@ 
 #include <net/rtnetlink.h>
 #include <net/ip6_fib.h>
 
+#ifdef CONFIG_MODULES
+
+static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
+{
+	switch (encap_type) {
+	case LWTUNNEL_ENCAP_MPLS:
+		return "LWTUNNEL_ENCAP_MPLS";
+	case LWTUNNEL_ENCAP_IP:
+		return "LWTUNNEL_ENCAP_IP";
+	case LWTUNNEL_ENCAP_ILA:
+		return "LWTUNNEL_ENCAP_ILA";
+	case LWTUNNEL_ENCAP_IP6:
+		return "LWTUNNEL_ENCAP_IP6";
+	case LWTUNNEL_ENCAP_NONE:
+	case __LWTUNNEL_ENCAP_MAX:
+		/* should not have got here */
+		break;
+	}
+	WARN_ON(1);
+	return "LWTUNNEL_ENCAP_NONE";
+}
+
+#endif /* CONFIG_MODULES */
+
 struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
 {
 	struct lwtunnel_state *lws;
@@ -85,6 +109,14 @@  int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
 	ret = -EOPNOTSUPP;
 	rcu_read_lock();
 	ops = rcu_dereference(lwtun_encaps[encap_type]);
+#ifdef CONFIG_MODULES
+	if (!ops) {
+		rcu_read_unlock();
+		request_module("rtnl-lwt-%s", lwtunnel_encap_str(encap_type));
+		rcu_read_lock();
+		ops = rcu_dereference(lwtun_encaps[encap_type]);
+	}
+#endif
 	if (likely(ops && ops->build_state))
 		ret = ops->build_state(dev, encap, family, cfg, lws);
 	rcu_read_unlock();