From 6e552919a470e5817163d9a1444368b69398f94e Mon Sep 17 00:00:00 2001
From: Masahide NAKAMURA <nakam@linux-ipv6.org>
Date: Sat, 24 Mar 2007 16:05:55 +0900
Subject: [PATCH] [XFRM] IPSEC: Skip flow with MIPv6 routing header type2 by IPsec policy.

While MIPv6 Mobile Node (MN) should receive route optimization (RO)
message from Correspondent Node (CN), RFC3776 describes to protect
mobility header (MH) between MN and Home Agent (HA) by IPsec tunnel.

The RO message is sent directly from CN to the MN using routing
header type 2 (RH2).
Here is example in receiving IPsec tunnel(a) and RO message(b) by MN.
The original message is from CN for both cases. "HoA" and "CoA" means
Home Address and Care-of Address respectively:

a-1. tunnel message on wire

[ IPv6 ][ESP][ IPv6 ][ ... ]
 s=HA         s=CN
 d=CoA        d=HoA

a-2. After decapsulated tunnel message on MN protocol stack

             [ IPv6 ][ ... ]
              s=CN
              d=HoA

b-1. RO message on wire

  [ IPv6 ][   RH2   ][ ... ]
   s=CN    addr=HoA
   d=CoA

b-2. After received RH2 of RO message on MNprotocol stack

             [ IPv6 ][ ... ]
              s=CN
              d=HoA

See a-2 with b-2 to realize the final flow.
For IPsec's point of view, it is ordinary correct these are
the same flow, however, it should be handled differently for MIPv6.

If operation policy is to protect only MH message by the tunnel and
MN and HA specify the tunnel policy selector with MH type granularity
(i.e. Home Test Init and Home Test), MN has no problem
to receive RO because MH message is defined whether it goes through
the tunnel or not for each type and Home Test Init and Home Test
never use RO.

OTOH some people prefer to protect payload packet between
MN and HA than using MH type specific selector even RFC3776
defines such tunnel with policy with unspecified protocol
selector is "MAY" level statement.

If MN and HA uses the tunnel protecting payload packet, the MN on
current kernel breaks the RFC as discard RO packet by the
tunnel policy incorrectly.

RFC3776 also says the requirement of IPsec policy with an example
"by defining the security policy database entiries specifically
for the tunnel interface" to achive it.

We cannot add the feature easiliy since our IPsec stack is a
XFRM system and does not always have tunnel interface (even
kernel has "ifindex" field at xfrm policy selector).

To support this MIPv6 requirement, I'd like to introduce another way
to define new flag value of xfrm policy to skip flow with RH2
if user specifies it. The flag is named "MULTILOCAL" since
receiving RH2 is that source routed frame hops inside the receiver.

MN stack will use the flag for inbound IPsec tunnel policy.

Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org>
---
 include/linux/xfrm.h   |    3 ++-
 include/net/flow.h     |    1 +
 include/net/xfrm.h     |    1 +
 net/ipv6/mip6.c        |    2 +-
 net/xfrm/xfrm_policy.c |   13 ++++++++++++-
 5 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index 15ca89e..931ab6e 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -315,7 +315,8 @@ struct xfrm_userpolicy_info {
 #define XFRM_POLICY_ALLOW	0
 #define XFRM_POLICY_BLOCK	1
 	__u8				flags;
-#define XFRM_POLICY_LOCALOK	1	/* Allow user to override global policy */
+#define XFRM_POLICY_LOCALOK		0x01	/* Allow user to override global policy */
+#define XFRM_POLICY_X_MULTILOCAL	0x02	/* Exclude inbound flow with multilocal state */
 	__u8				share;
 };
 
diff --git a/include/net/flow.h b/include/net/flow.h
index f3cc1f8..1dce23a 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -49,6 +49,7 @@ struct flowi {
 	__u8	proto;
 	__u8	flags;
 #define FLOWI_FLAG_MULTIPATHOLDROUTE 0x01
+#define FLOWI_FLAG_MULTILOCAL 0x02
 	union {
 		struct {
 			__be16	sport;
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 1f85641..29755ba 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -280,6 +280,7 @@ struct xfrm_type
 	__u8			proto;
 	__u8			flags;
 #define XFRM_TYPE_NON_FRAGMENT	1
+#define XFRM_TYPE_MULTILOCAL	2
 
 	int			(*init_state)(struct xfrm_state *x);
 	void			(*destructor)(struct xfrm_state *);
diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c
index 0afcabd..716c7bc 100644
--- a/net/ipv6/mip6.c
+++ b/net/ipv6/mip6.c
@@ -454,7 +454,7 @@ static struct xfrm_type mip6_rthdr_type =
 	.description	= "MIP6RT",
 	.owner		= THIS_MODULE,
 	.proto	     	= IPPROTO_ROUTING,
-	.flags		= XFRM_TYPE_NON_FRAGMENT,
+	.flags		= XFRM_TYPE_NON_FRAGMENT | XFRM_TYPE_MULTILOCAL,
 	.init_state	= mip6_rthdr_init_state,
 	.destructor	= mip6_rthdr_destroy,
 	.input		= mip6_rthdr_input,
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index ae0f686..2573617 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -941,7 +941,9 @@ static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl,
 	int match, ret = -ESRCH;
 
 	if (pol->family != family ||
-	    pol->type != type)
+	    pol->type != type ||
+	    ((pol->flags & XFRM_POLICY_X_MULTILOCAL) &&
+	     (fl->flags & FLOWI_FLAG_MULTILOCAL)))
 		return ret;
 
 	match = xfrm_selector_match(sel, fl, family);
@@ -1677,6 +1679,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 
 	/* First, check used SA against their selectors. */
 	if (skb->sp) {
+		int ml = 0;
 		int i;
 
 		for (i=skb->sp->len-1; i>=0; i--) {
@@ -1685,7 +1688,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 				XFRM_INC_STATS(XFRM_MIB_INSTATEINVALID);
 				return 0;
 			}
+
+			if (!ml && (x->type->flags & XFRM_TYPE_MULTILOCAL))
+				ml = 1;
+			else if (ml == 1 && x->props.mode == XFRM_MODE_TUNNEL)
+				ml = -1;
 		}
+
+		if (ml == 1)
+			fl.flags |= FLOWI_FLAG_MULTILOCAL;
 	}
 
 	pol = NULL;
-- 
1.5.0.3

