[XFRM] SPD info TLV aggregation
[pandora-kernel.git] / net / xfrm / xfrm_policy.c
index 7629260..95271e8 100644 (file)
@@ -579,8 +579,22 @@ static inline int xfrm_byidx_should_resize(int total)
        return 0;
 }
 
-static DEFINE_MUTEX(hash_resize_mutex);
+void xfrm_spd_getinfo(struct xfrmk_spdinfo *si)
+{
+       read_lock_bh(&xfrm_policy_lock);
+       si->incnt = xfrm_policy_count[XFRM_POLICY_IN];
+       si->outcnt = xfrm_policy_count[XFRM_POLICY_OUT];
+       si->fwdcnt = xfrm_policy_count[XFRM_POLICY_FWD];
+       si->inscnt = xfrm_policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
+       si->outscnt = xfrm_policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
+       si->fwdscnt = xfrm_policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
+       si->spdhcnt = xfrm_idx_hmask;
+       si->spdhmcnt = xfrm_policy_hashmax;
+       read_unlock_bh(&xfrm_policy_lock);
+}
+EXPORT_SYMBOL(xfrm_spd_getinfo);
 
+static DEFINE_MUTEX(hash_resize_mutex);
 static void xfrm_hash_resize(struct work_struct *__unused)
 {
        int dir, total;
@@ -1330,6 +1344,40 @@ xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
        return err;
 }
 
+static int inline
+xfrm_dst_alloc_copy(void **target, void *src, int size)
+{
+       if (!*target) {
+               *target = kmalloc(size, GFP_ATOMIC);
+               if (!*target)
+                       return -ENOMEM;
+       }
+       memcpy(*target, src, size);
+       return 0;
+}
+
+static int inline
+xfrm_dst_update_parent(struct dst_entry *dst, struct xfrm_selector *sel)
+{
+#ifdef CONFIG_XFRM_SUB_POLICY
+       struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
+       return xfrm_dst_alloc_copy((void **)&(xdst->partner),
+                                  sel, sizeof(*sel));
+#else
+       return 0;
+#endif
+}
+
+static int inline
+xfrm_dst_update_origin(struct dst_entry *dst, struct flowi *fl)
+{
+#ifdef CONFIG_XFRM_SUB_POLICY
+       struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
+       return xfrm_dst_alloc_copy((void **)&(xdst->origin), fl, sizeof(*fl));
+#else
+       return 0;
+#endif
+}
 
 static int stale_bundle(struct dst_entry *dst);
 
@@ -1518,6 +1566,18 @@ restart:
                        err = -EHOSTUNREACH;
                        goto error;
                }
+
+               if (npols > 1)
+                       err = xfrm_dst_update_parent(dst, &pols[1]->selector);
+               else
+                       err = xfrm_dst_update_origin(dst, fl);
+               if (unlikely(err)) {
+                       write_unlock_bh(&policy->lock);
+                       if (dst)
+                               dst_free(dst);
+                       goto error;
+               }
+
                dst->next = policy->bundles;
                policy->bundles = dst;
                dst_hold(dst);
@@ -1933,6 +1993,15 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
        if (!dst_check(dst->path, ((struct xfrm_dst *)dst)->path_cookie) ||
            (dst->dev && !netif_running(dst->dev)))
                return 0;
+#ifdef CONFIG_XFRM_SUB_POLICY
+       if (fl) {
+               if (first->origin && !flow_cache_uli_match(first->origin, fl))
+                       return 0;
+               if (first->partner &&
+                   !xfrm_selector_match(first->partner, fl, family))
+                       return 0;
+       }
+#endif
 
        last = NULL;