xfrm: Introduce xfrm_input_afinfo to access the the callbacks properly
authorSteffen Klassert <steffen.klassert@secunet.com>
Fri, 14 Mar 2014 06:28:07 +0000 (07:28 +0100)
committerSteffen Klassert <steffen.klassert@secunet.com>
Fri, 14 Mar 2014 06:28:07 +0000 (07:28 +0100)
IPv6 can be build as a module, so we need mechanism to access
the address family dependent callback functions properly.
Therefore we introduce xfrm_input_afinfo, similar to that
what we have for the address family dependent part of
policies and states.

Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
include/net/xfrm.h
net/ipv4/xfrm4_policy.c
net/ipv4/xfrm4_protocol.c
net/xfrm/xfrm_input.c

index ce3d96f..af13599 100644 (file)
@@ -349,6 +349,16 @@ int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
 struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
 void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
 
+struct xfrm_input_afinfo {
+       unsigned int            family;
+       struct module           *owner;
+       int                     (*callback)(struct sk_buff *skb, u8 protocol,
+                                           int err);
+};
+
+int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo);
+int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo);
+
 void xfrm_state_delete_tunnel(struct xfrm_state *x);
 
 struct xfrm_type {
@@ -1392,6 +1402,7 @@ void xfrm4_init(void);
 int xfrm_state_init(struct net *net);
 void xfrm_state_fini(struct net *net);
 void xfrm4_state_init(void);
+void xfrm4_protocol_init(void);
 #ifdef CONFIG_XFRM
 int xfrm6_init(void);
 void xfrm6_fini(void);
@@ -1773,18 +1784,6 @@ static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m)
        return ret;
 }
 
-static inline int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family,
-                             u8 protocol, int err)
-{
-       switch(family) {
-#ifdef CONFIG_INET
-       case AF_INET:
-               return xfrm4_rcv_cb(skb, protocol, err);
-#endif
-       }
-       return 0;
-}
-
 static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x,
                                    unsigned int family)
 {
index e1a6393..6156f68 100644 (file)
@@ -325,6 +325,7 @@ void __init xfrm4_init(void)
 
        xfrm4_state_init();
        xfrm4_policy_init();
+       xfrm4_protocol_init();
 #ifdef CONFIG_SYSCTL
        register_pernet_subsys(&xfrm4_net_ops);
 #endif
index cdc09ef..7f7b243 100644 (file)
@@ -179,6 +179,12 @@ static const struct net_protocol ipcomp4_protocol = {
        .netns_ok       =       1,
 };
 
+static struct xfrm_input_afinfo xfrm4_input_afinfo = {
+       .family         =       AF_INET,
+       .owner          =       THIS_MODULE,
+       .callback       =       xfrm4_rcv_cb,
+};
+
 static inline const struct net_protocol *netproto(unsigned char protocol)
 {
        switch (protocol) {
@@ -199,7 +205,6 @@ int xfrm4_protocol_register(struct xfrm4_protocol *handler,
        struct xfrm4_protocol __rcu **pprev;
        struct xfrm4_protocol *t;
        bool add_netproto = false;
-
        int ret = -EEXIST;
        int priority = handler->priority;
 
@@ -273,3 +278,9 @@ int xfrm4_protocol_deregister(struct xfrm4_protocol *handler,
        return ret;
 }
 EXPORT_SYMBOL(xfrm4_protocol_deregister);
+
+void __init xfrm4_protocol_init(void)
+{
+       xfrm_input_register_afinfo(&xfrm4_input_afinfo);
+}
+EXPORT_SYMBOL(xfrm4_protocol_init);
index 4218164..85d1d47 100644 (file)
 
 static struct kmem_cache *secpath_cachep __read_mostly;
 
+static DEFINE_SPINLOCK(xfrm_input_afinfo_lock);
+static struct xfrm_input_afinfo __rcu *xfrm_input_afinfo[NPROTO];
+
+int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo)
+{
+       int err = 0;
+
+       if (unlikely(afinfo == NULL))
+               return -EINVAL;
+       if (unlikely(afinfo->family >= NPROTO))
+               return -EAFNOSUPPORT;
+       spin_lock_bh(&xfrm_input_afinfo_lock);
+       if (unlikely(xfrm_input_afinfo[afinfo->family] != NULL))
+               err = -ENOBUFS;
+       else
+               rcu_assign_pointer(xfrm_input_afinfo[afinfo->family], afinfo);
+       spin_unlock_bh(&xfrm_input_afinfo_lock);
+       return err;
+}
+EXPORT_SYMBOL(xfrm_input_register_afinfo);
+
+int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo)
+{
+       int err = 0;
+
+       if (unlikely(afinfo == NULL))
+               return -EINVAL;
+       if (unlikely(afinfo->family >= NPROTO))
+               return -EAFNOSUPPORT;
+       spin_lock_bh(&xfrm_input_afinfo_lock);
+       if (likely(xfrm_input_afinfo[afinfo->family] != NULL)) {
+               if (unlikely(xfrm_input_afinfo[afinfo->family] != afinfo))
+                       err = -EINVAL;
+               else
+                       RCU_INIT_POINTER(xfrm_input_afinfo[afinfo->family], NULL);
+       }
+       spin_unlock_bh(&xfrm_input_afinfo_lock);
+       synchronize_rcu();
+       return err;
+}
+EXPORT_SYMBOL(xfrm_input_unregister_afinfo);
+
+static struct xfrm_input_afinfo *xfrm_input_get_afinfo(unsigned int family)
+{
+       struct xfrm_input_afinfo *afinfo;
+
+       if (unlikely(family >= NPROTO))
+               return NULL;
+       rcu_read_lock();
+       afinfo = rcu_dereference(xfrm_input_afinfo[family]);
+       if (unlikely(!afinfo))
+               rcu_read_unlock();
+       return afinfo;
+}
+
+static void xfrm_input_put_afinfo(struct xfrm_input_afinfo *afinfo)
+{
+       rcu_read_unlock();
+}
+
+static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol,
+                      int err)
+{
+       int ret;
+       struct xfrm_input_afinfo *afinfo = xfrm_input_get_afinfo(family);
+
+       if (!afinfo)
+               return -EAFNOSUPPORT;
+
+       ret = afinfo->callback(skb, protocol, err);
+       xfrm_input_put_afinfo(afinfo);
+
+       return ret;
+}
+
 void __secpath_destroy(struct sec_path *sp)
 {
        int i;