]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/xfrm/xfrm_state.c
xfrm: Add an IPsec hardware offloading API
[mirror_ubuntu-bionic-kernel.git] / net / xfrm / xfrm_state.c
index 5a597dbbe564f09ad6b77e1f529127a0410c7fd4..fc3c5aa387543a63bf18955e60309207bf06e961 100644 (file)
@@ -251,6 +251,75 @@ static void xfrm_put_type(const struct xfrm_type *type)
        module_put(type->owner);
 }
 
+static DEFINE_SPINLOCK(xfrm_type_offload_lock);
+int xfrm_register_type_offload(const struct xfrm_type_offload *type,
+                              unsigned short family)
+{
+       struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
+       const struct xfrm_type_offload **typemap;
+       int err = 0;
+
+       if (unlikely(afinfo == NULL))
+               return -EAFNOSUPPORT;
+       typemap = afinfo->type_offload_map;
+       spin_lock_bh(&xfrm_type_offload_lock);
+
+       if (likely(typemap[type->proto] == NULL))
+               typemap[type->proto] = type;
+       else
+               err = -EEXIST;
+       spin_unlock_bh(&xfrm_type_offload_lock);
+       rcu_read_unlock();
+       return err;
+}
+EXPORT_SYMBOL(xfrm_register_type_offload);
+
+int xfrm_unregister_type_offload(const struct xfrm_type_offload *type,
+                                unsigned short family)
+{
+       struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
+       const struct xfrm_type_offload **typemap;
+       int err = 0;
+
+       if (unlikely(afinfo == NULL))
+               return -EAFNOSUPPORT;
+       typemap = afinfo->type_offload_map;
+       spin_lock_bh(&xfrm_type_offload_lock);
+
+       if (unlikely(typemap[type->proto] != type))
+               err = -ENOENT;
+       else
+               typemap[type->proto] = NULL;
+       spin_unlock_bh(&xfrm_type_offload_lock);
+       rcu_read_unlock();
+       return err;
+}
+EXPORT_SYMBOL(xfrm_unregister_type_offload);
+
+static const struct xfrm_type_offload *xfrm_get_type_offload(u8 proto, unsigned short family)
+{
+       struct xfrm_state_afinfo *afinfo;
+       const struct xfrm_type_offload **typemap;
+       const struct xfrm_type_offload *type;
+
+       afinfo = xfrm_state_get_afinfo(family);
+       if (unlikely(afinfo == NULL))
+               return NULL;
+       typemap = afinfo->type_offload_map;
+
+       type = typemap[proto];
+       if ((type && !try_module_get(type->owner)))
+               type = NULL;
+
+       rcu_read_unlock();
+       return type;
+}
+
+static void xfrm_put_type_offload(const struct xfrm_type_offload *type)
+{
+       module_put(type->owner);
+}
+
 static DEFINE_SPINLOCK(xfrm_mode_lock);
 int xfrm_register_mode(struct xfrm_mode *mode, int family)
 {
@@ -365,10 +434,13 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
                xfrm_put_mode(x->inner_mode_iaf);
        if (x->outer_mode)
                xfrm_put_mode(x->outer_mode);
+       if (x->type_offload)
+               xfrm_put_type_offload(x->type_offload);
        if (x->type) {
                x->type->destructor(x);
                xfrm_put_type(x->type);
        }
+       xfrm_dev_state_free(x);
        security_xfrm_state_free(x);
        kfree(x);
 }
@@ -538,6 +610,8 @@ int __xfrm_state_delete(struct xfrm_state *x)
                net->xfrm.state_num--;
                spin_unlock(&net->xfrm.xfrm_state_lock);
 
+               xfrm_dev_state_delete(x);
+
                /* All xfrm_state objects are created by xfrm_state_alloc.
                 * The xfrm_state_alloc call gives a reference, and that
                 * is what we are dropping here.
@@ -582,12 +656,41 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid)
 
        return err;
 }
+
+static inline int
+xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool task_valid)
+{
+       int i, err = 0;
+
+       for (i = 0; i <= net->xfrm.state_hmask; i++) {
+               struct xfrm_state *x;
+               struct xfrm_state_offload *xso;
+
+               hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {
+                       xso = &x->xso;
+
+                       if (xso->dev == dev &&
+                          (err = security_xfrm_state_delete(x)) != 0) {
+                               xfrm_audit_state_delete(x, 0, task_valid);
+                               return err;
+                       }
+               }
+       }
+
+       return err;
+}
 #else
 static inline int
 xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid)
 {
        return 0;
 }
+
+static inline int
+xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool task_valid)
+{
+       return 0;
+}
 #endif
 
 int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
@@ -630,6 +733,48 @@ out:
 }
 EXPORT_SYMBOL(xfrm_state_flush);
 
+int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid)
+{
+       int i, err = 0, cnt = 0;
+
+       spin_lock_bh(&net->xfrm.xfrm_state_lock);
+       err = xfrm_dev_state_flush_secctx_check(net, dev, task_valid);
+       if (err)
+               goto out;
+
+       err = -ESRCH;
+       for (i = 0; i <= net->xfrm.state_hmask; i++) {
+               struct xfrm_state *x;
+               struct xfrm_state_offload *xso;
+restart:
+               hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {
+                       xso = &x->xso;
+
+                       if (!xfrm_state_kern(x) && xso->dev == dev) {
+                               xfrm_state_hold(x);
+                               spin_unlock_bh(&net->xfrm.xfrm_state_lock);
+
+                               err = xfrm_state_delete(x);
+                               xfrm_audit_state_delete(x, err ? 0 : 1,
+                                                       task_valid);
+                               xfrm_state_put(x);
+                               if (!err)
+                                       cnt++;
+
+                               spin_lock_bh(&net->xfrm.xfrm_state_lock);
+                               goto restart;
+                       }
+               }
+       }
+       if (cnt)
+               err = 0;
+
+out:
+       spin_unlock_bh(&net->xfrm.xfrm_state_lock);
+       return err;
+}
+EXPORT_SYMBOL(xfrm_dev_state_flush);
+
 void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si)
 {
        spin_lock_bh(&net->xfrm.xfrm_state_lock);
@@ -2077,6 +2222,8 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay)
        if (x->type == NULL)
                goto error;
 
+       x->type_offload = xfrm_get_type_offload(x->id.proto, family);
+
        err = x->type->init_state(x);
        if (err)
                goto error;