]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/xfrm/xfrm_state.c
xfrm: Add an IPsec hardware offloading API
[mirror_ubuntu-artful-kernel.git] / net / xfrm / xfrm_state.c
index 47fefe97d1e308691399aa9157abfe32149ec25e..fc3c5aa387543a63bf18955e60309207bf06e961 100644 (file)
@@ -440,6 +440,7 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
                x->type->destructor(x);
                xfrm_put_type(x->type);
        }
+       xfrm_dev_state_free(x);
        security_xfrm_state_free(x);
        kfree(x);
 }
@@ -609,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.
@@ -653,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)
@@ -701,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);