lwtunnel: autoload of lwt modules
authorRobert Shearman <rshearma@brocade.com>
Fri, 19 Feb 2016 09:43:16 +0000 (09:43 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 22 Feb 2016 03:00:28 +0000 (22:00 -0500)
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.

Only users with the CAP_NET_ADMIN capability can cause modules to be
loaded, which is ensured by rtnetlink_rcv_msg rejecting non-RTM_GETxxx
messages for users without this capability, and by
lwtunnel_build_state not being called in response to RTM_GETxxx
messages.

Signed-off-by: Robert Shearman <rshearma@brocade.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/lwtunnel.h
net/core/lwtunnel.c

index 66350ce..e9f116e 100644 (file)
@@ -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 */
index 299cfc2..669ecc9 100644 (file)
 #include <net/rtnetlink.h>
 #include <net/ip6_fib.h>
 
+#ifdef CONFIG_MODULES
+
+static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
+{
+       /* Only lwt encaps implemented without using an interface for
+        * the encap need to return a string here.
+        */
+       switch (encap_type) {
+       case LWTUNNEL_ENCAP_MPLS:
+               return "MPLS";
+       case LWTUNNEL_ENCAP_ILA:
+               return "ILA";
+       case LWTUNNEL_ENCAP_IP6:
+       case LWTUNNEL_ENCAP_IP:
+       case LWTUNNEL_ENCAP_NONE:
+       case __LWTUNNEL_ENCAP_MAX:
+               /* should not have got here */
+               WARN_ON(1);
+               break;
+       }
+       return NULL;
+}
+
+#endif /* CONFIG_MODULES */
+
 struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
 {
        struct lwtunnel_state *lws;
@@ -85,6 +110,18 @@ 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) {
+               const char *encap_type_str = lwtunnel_encap_str(encap_type);
+
+               if (encap_type_str) {
+                       rcu_read_unlock();
+                       request_module("rtnl-lwt-%s", encap_type_str);
+                       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();