From f3c773ffddb4a6c0604ad43c41379538543215e7 Mon Sep 17 00:00:00 2001
From: Masahide NAKAMURA <nakam@linux-ipv6.org>
Date: Sun, 25 Mar 2007 04:25:05 +0900
Subject: [PATCH] [XFRM]: Restrict upper layer information by bundle.

On MIPv6 usage, XFRM sub policy is enabled.
When main (IPsec) and sub (MIPv6) policy selectors have the same
address set but different upper layer information (i.e. protocol
number and its ports or type/code), multiple bundle should be created.
However, currently we have issue to use the same bundle created for
the first time with all flows covered by the case.

It is useful for the bundle to have the upper layer information
to be restructured correctly if it does not match with the flow.

1. Bundle was created by two policies
Selector from another policy is added to xfrm_dst.
If the flow does not match the selector, it goes to slow path to
restructure new bundle by single policy.

2. Bundle was created by one policy
Flow cache is added to xfrm_dst as originated one. If the flow does
not match the cache, it goes to slow path to try searching another
policy.

Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org>
---
 include/net/flow.h     |    6 ++++++
 include/net/xfrm.h     |    6 ++++++
 net/xfrm/xfrm_policy.c |   23 +++++++++++++++++++++++
 3 files changed, 35 insertions(+), 0 deletions(-)

diff --git a/include/net/flow.h b/include/net/flow.h
index ce4b10d..f3cc1f8 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -97,4 +97,10 @@ extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
 extern void flow_cache_flush(void);
 extern atomic_t flow_cache_genid;
 
+static inline int flow_cache_uli_match(struct flowi *fl1, struct flowi *fl2)
+{
+	return (fl1->proto == fl2->proto &&
+		!memcmp(&fl1->uli_u, &fl2->uli_u, sizeof(fl1->uli_u)));
+}
+
 #endif
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 5a00aa8..4a345c6 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -584,6 +584,12 @@ struct xfrm_dst
 		struct rt6_info		rt6;
 	} u;
 	struct dst_entry *route;
+#ifdef CONFIG_XFRM_SUB_POLICY
+	u8 flags;
+#define XFRM_DST_MULTIPOLICY	1
+	struct flowi origin;
+	struct xfrm_selector partner;
+#endif
 	u32 genid;
 	u32 route_mtu_cached;
 	u32 child_mtu_cached;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 785c3e3..481b822 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1349,6 +1349,9 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
 	int pi;
 	struct xfrm_state *xfrm[XFRM_MAX_DEPTH];
 	struct dst_entry *dst, *dst_orig = *dst_p;
+#ifdef CONFIG_XFRM_SUB_POLICY
+	struct xfrm_dst *xdst;
+#endif
 	int nx = 0;
 	int err;
 	u32 genid;
@@ -1518,6 +1521,15 @@ restart:
 			err = -EHOSTUNREACH;
 			goto error;
 		}
+#ifdef CONFIG_XFRM_SUB_POLICY
+		xdst = (struct xfrm_dst *)dst;
+		if (npols > 1) {
+			memcpy(&xdst->partner, &pols[1]->selector,
+			       sizeof(xdst->partner));
+			xdst->flags |= XFRM_DST_MULTIPOLICY;
+		} else
+			memcpy(&xdst->origin, fl, sizeof(xdst->origin));
+#endif
 		dst->next = policy->bundles;
 		policy->bundles = dst;
 		dst_hold(dst);
@@ -1933,6 +1945,17 @@ 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->flags & XFRM_DST_MULTIPOLICY) {
+			if (!xfrm_selector_match(&first->partner, fl, family))
+				return 0;
+		} else {
+			if (!flow_cache_uli_match(&first->origin, fl))
+				return 0;
+		}
+	}
+#endif
 
 	last = NULL;
 
-- 
1.5.0.3

