From 469611ff288f100ce35a2706295edfe5159c6f90 Mon Sep 17 00:00:00 2001
From: Masahide NAKAMURA <nakam@linux-ipv6.org>
Date: Sat, 23 Sep 2006 16:47:56 +0900
Subject: [PATCH] [XFRM]: Inbound trigger support.

Inbound trigger is an acquire generated by incomming packet
which matches user template.
Mobile node (MN), introduced by Mobile IPv6, would realize non route
optimized (RO) packet (i.e. packet through tunnel between Home agent)
in order to judge whether to start RO with the sender or not.
Currently kernel has only outbound acquire for IPsec key-exchange
initiator to realize outgoing packet. MN uses it for outbound, too.
This patch is for inbound case.

To-be-Signed-off-by: Ville Nuorvala <vnuorval@tcs.hut.fi>
Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org>
---
 include/net/xfrm.h     |    2 +
 net/xfrm/xfrm_policy.c |    7 ++-
 net/xfrm/xfrm_state.c  |  117 +++++++++++++++++++++++++++++++++---------------
 3 files changed, 88 insertions(+), 38 deletions(-)

diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 4a345c6..8d05ace 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -913,6 +913,8 @@ extern void xfrm6_state_fini(void);
 
 extern int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), void *);
 extern struct xfrm_state *xfrm_state_alloc(void);
+extern int xfrm_state_trigger(struct flowi *fl, struct xfrm_policy *pol,
+			      struct xfrm_tmpl *tmpl, unsigned short family);
 extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, 
 					  struct flowi *fl, struct xfrm_tmpl *tmpl,
 					  struct xfrm_policy *pol, int *err,
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 481b822..1897753 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1595,7 +1595,8 @@ xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x,
  * Otherwise "-2 - errored_index" is returned.
  */
 static inline int
-xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
+xfrm_policy_ok(struct flowi *fl, struct xfrm_policy *pol,
+	       struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
 	       unsigned short family)
 {
 	int idx = start;
@@ -1603,6 +1604,8 @@ xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
 	if (tmpl->optional) {
 		if (tmpl->mode == XFRM_MODE_TRANSPORT)
 			return start;
+		else if (tmpl->mode == XFRM_MODE_IN_TRIGGER)
+			xfrm_state_trigger(fl, pol, tmpl, family);
 	} else
 		start = -1;
 	for (; idx < sp->len; idx++) {
@@ -1746,7 +1749,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
 		 * are implied between each two transformations.
 		 */
 		for (i = xfrm_nr-1, k = 0; i >= 0; i--) {
-			k = xfrm_policy_ok(tpp[i], sp, k, family);
+			k = xfrm_policy_ok(&fl, pol, tpp[i], sp, k, family);
 			if (k < 0) {
 				if (k < -1)
 					/* "-2 - errored_index" returned */
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 5c5f6dc..90f7af5 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -522,6 +522,85 @@ static void xfrm_hash_grow_check(int have_hash_collision)
 		schedule_work(&xfrm_hash_work);
 }
 
+static inline int
+__xfrm_state_query(struct flowi *fl, struct xfrm_policy *pol,
+		   struct xfrm_tmpl *tmpl, xfrm_address_t *daddr,
+		   xfrm_address_t *saddr, unsigned short family,
+		   struct xfrm_state **statep)
+{
+	struct xfrm_state *x;
+	unsigned int h;
+	int error = 0;
+
+	x = xfrm_state_alloc();
+	if (x == NULL) {
+		error = -ENOMEM;
+		goto out;
+	}
+	/* Initialize temporary selector matching only
+	 * to current session. */
+	xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
+
+	error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
+	if (error) {
+		x->km.state = XFRM_STATE_DEAD;
+		xfrm_state_put(x);
+		x = NULL;
+		goto out;
+	}
+
+	error = km_query(x, tmpl, pol);
+	if (error) {
+		x->km.state = XFRM_STATE_DEAD;
+		xfrm_state_put(x);
+		x = NULL;
+		error = -ESRCH;
+		goto out;
+	}
+
+	x->km.state = XFRM_STATE_ACQ;
+	h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
+	hlist_add_head(&x->bydst, xfrm_state_bydst+h);
+	h = xfrm_src_hash(daddr, saddr, family);
+	hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
+	if (x->id.spi) {
+		h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
+		hlist_add_head(&x->byspi, xfrm_state_byspi+h);
+	}
+	x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
+	x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
+	add_timer(&x->timer);
+	xfrm_state_num++;
+	xfrm_hash_grow_check(x->bydst.next != NULL);
+
+ out:
+	if (statep)
+		*statep = x;
+	return error;
+
+}
+
+int xfrm_state_trigger(struct flowi *fl, struct xfrm_policy *pol,
+		       struct xfrm_tmpl *tmpl, unsigned short family)
+{
+	xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
+	xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);
+	struct xfrm_state *x0;
+	int err;
+
+	x0 = __xfrm_state_lookup_byaddr(daddr, saddr, tmpl->id.proto, family);
+	if (x0 != NULL) {
+		xfrm_state_put(x0);
+		err = -EEXIST;
+		goto out;
+	}
+
+	err = __xfrm_state_query(fl, pol, tmpl, daddr, saddr, family, NULL);
+
+ out:
+	return err;
+}
+
 struct xfrm_state *
 xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
 		struct flowi *fl, struct xfrm_tmpl *tmpl,
@@ -586,43 +665,9 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
 			error = -EEXIST;
 			goto out;
 		}
-		x = xfrm_state_alloc();
-		if (x == NULL) {
-			error = -ENOMEM;
-			goto out;
-		}
-		/* Initialize temporary selector matching only
-		 * to current session. */
-		xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family);
-
-		error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid);
-		if (error) {
-			x->km.state = XFRM_STATE_DEAD;
-			xfrm_state_put(x);
-			x = NULL;
-			goto out;
-		}
 
-		if (km_query(x, tmpl, pol) == 0) {
-			x->km.state = XFRM_STATE_ACQ;
-			hlist_add_head(&x->bydst, xfrm_state_bydst+h);
-			h = xfrm_src_hash(daddr, saddr, family);
-			hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
-			if (x->id.spi) {
-				h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
-				hlist_add_head(&x->byspi, xfrm_state_byspi+h);
-			}
-			x->lft.hard_add_expires_seconds = XFRM_ACQ_EXPIRES;
-			x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ;
-			add_timer(&x->timer);
-			xfrm_state_num++;
-			xfrm_hash_grow_check(x->bydst.next != NULL);
-		} else {
-			x->km.state = XFRM_STATE_DEAD;
-			xfrm_state_put(x);
-			x = NULL;
-			error = -ESRCH;
-		}
+		error = __xfrm_state_query(fl, pol, tmpl, daddr, saddr, family,
+					   &x);
 	}
 out:
 	if (x)
-- 
1.5.0.3

