]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/core/lwtunnel.c
Merge branches 'pm-core', 'pm-qos', 'pm-domains' and 'pm-opp'
[linux.git] / net / core / lwtunnel.c
index a5d4e866ce88b4d055798d9ea55fc905b351fb3d..c23465005f2f4ced93d7bcb2754fb267c2cf00d0 100644 (file)
@@ -26,6 +26,7 @@
 #include <net/lwtunnel.h>
 #include <net/rtnetlink.h>
 #include <net/ip6_fib.h>
+#include <net/nexthop.h>
 
 #ifdef CONFIG_MODULES
 
@@ -114,25 +115,77 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
        ret = -EOPNOTSUPP;
        rcu_read_lock();
        ops = rcu_dereference(lwtun_encaps[encap_type]);
+       if (likely(ops && ops->build_state && try_module_get(ops->owner))) {
+               ret = ops->build_state(dev, encap, family, cfg, lws);
+               if (ret)
+                       module_put(ops->owner);
+       }
+       rcu_read_unlock();
+
+       return ret;
+}
+EXPORT_SYMBOL(lwtunnel_build_state);
+
+int lwtunnel_valid_encap_type(u16 encap_type)
+{
+       const struct lwtunnel_encap_ops *ops;
+       int ret = -EINVAL;
+
+       if (encap_type == LWTUNNEL_ENCAP_NONE ||
+           encap_type > LWTUNNEL_ENCAP_MAX)
+               return ret;
+
+       rcu_read_lock();
+       ops = rcu_dereference(lwtun_encaps[encap_type]);
+       rcu_read_unlock();
 #ifdef CONFIG_MODULES
        if (!ops) {
                const char *encap_type_str = lwtunnel_encap_str(encap_type);
 
                if (encap_type_str) {
-                       rcu_read_unlock();
+                       __rtnl_unlock();
                        request_module("rtnl-lwt-%s", encap_type_str);
+                       rtnl_lock();
+
                        rcu_read_lock();
                        ops = rcu_dereference(lwtun_encaps[encap_type]);
+                       rcu_read_unlock();
                }
        }
 #endif
-       if (likely(ops && ops->build_state))
-               ret = ops->build_state(dev, encap, family, cfg, lws);
-       rcu_read_unlock();
+       return ops ? 0 : -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(lwtunnel_valid_encap_type);
 
-       return ret;
+int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining)
+{
+       struct rtnexthop *rtnh = (struct rtnexthop *)attr;
+       struct nlattr *nla_entype;
+       struct nlattr *attrs;
+       struct nlattr *nla;
+       u16 encap_type;
+       int attrlen;
+
+       while (rtnh_ok(rtnh, remaining)) {
+               attrlen = rtnh_attrlen(rtnh);
+               if (attrlen > 0) {
+                       attrs = rtnh_attrs(rtnh);
+                       nla = nla_find(attrs, attrlen, RTA_ENCAP);
+                       nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
+
+                       if (nla_entype) {
+                               encap_type = nla_get_u16(nla_entype);
+
+                               if (lwtunnel_valid_encap_type(encap_type) != 0)
+                                       return -EOPNOTSUPP;
+                       }
+               }
+               rtnh = rtnh_next(rtnh, &remaining);
+       }
+
+       return 0;
 }
-EXPORT_SYMBOL(lwtunnel_build_state);
+EXPORT_SYMBOL(lwtunnel_valid_encap_type_attr);
 
 void lwtstate_free(struct lwtunnel_state *lws)
 {
@@ -144,6 +197,7 @@ void lwtstate_free(struct lwtunnel_state *lws)
        } else {
                kfree(lws);
        }
+       module_put(ops->owner);
 }
 EXPORT_SYMBOL(lwtstate_free);