[IPSEC]: Sync series - core changes

This patch provides the core functionality needed for sync events
for ipsec. Derived work of Krisztian KOVACS <hidden@balabit.hu>

Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 6e8880e..b686548 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -261,6 +261,8 @@
 	NET_CORE_DEV_WEIGHT=17,
 	NET_CORE_SOMAXCONN=18,
 	NET_CORE_BUDGET=19,
+	NET_CORE_AEVENT_ETIME=20,
+	NET_CORE_AEVENT_RSEQTH=21,
 };
 
 /* /proc/sys/net/ethernet */
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index 82fbb75..b54a129 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -156,6 +156,10 @@
 	XFRM_MSG_FLUSHPOLICY,
 #define XFRM_MSG_FLUSHPOLICY XFRM_MSG_FLUSHPOLICY
 
+	XFRM_MSG_NEWAE,
+#define XFRM_MSG_NEWAE XFRM_MSG_NEWAE
+	XFRM_MSG_GETAE,
+#define XFRM_MSG_GETAE XFRM_MSG_GETAE
 	__XFRM_MSG_MAX
 };
 #define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
@@ -194,6 +198,21 @@
 	xfrm_address_t	encap_oa;
 };
 
+/* AEVENT flags  */
+enum xfrm_ae_ftype_t {
+	XFRM_AE_UNSPEC,
+	XFRM_AE_RTHR=1,	/* replay threshold*/
+	XFRM_AE_RVAL=2, /* replay value */
+	XFRM_AE_LVAL=4, /* lifetime value */
+	XFRM_AE_ETHR=8, /* expiry timer threshold */
+	XFRM_AE_CR=16, /* Event cause is replay update */
+	XFRM_AE_CE=32, /* Event cause is timer expiry */
+	XFRM_AE_CU=64, /* Event cause is policy update */
+	__XFRM_AE_MAX
+
+#define XFRM_AE_MAX (__XFRM_AE_MAX - 1)
+};
+
 /* Netlink message attributes.  */
 enum xfrm_attr_type_t {
 	XFRMA_UNSPEC,
@@ -205,6 +224,10 @@
 	XFRMA_SA,
 	XFRMA_POLICY,
 	XFRMA_SEC_CTX,		/* struct xfrm_sec_ctx */
+	XFRMA_LTIME_VAL,
+	XFRMA_REPLAY_VAL,
+	XFRMA_REPLAY_THRESH,
+	XFRMA_ETIMER_THRESH,
 	__XFRMA_MAX
 
 #define XFRMA_MAX (__XFRMA_MAX - 1)
@@ -235,6 +258,11 @@
 	__u8				proto;
 };
 
+struct xfrm_aevent_id {
+	__u32				flags;
+	struct xfrm_usersa_id		sa_id;
+};
+
 struct xfrm_userspi_info {
 	struct xfrm_usersa_info		info;
 	__u32				min;
@@ -306,6 +334,8 @@
 #define XFRMNLGRP_SA		XFRMNLGRP_SA
 	XFRMNLGRP_POLICY,
 #define XFRMNLGRP_POLICY	XFRMNLGRP_POLICY
+	XFRMNLGRP_AEVENTS,
+#define XFRMNLGRP_AEVENTS	XFRMNLGRP_AEVENTS
 	__XFRMNLGRP_MAX
 };
 #define XFRMNLGRP_MAX	(__XFRMNLGRP_MAX - 1)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 8d362c4..bc005e6 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -20,6 +20,10 @@
 
 #define XFRM_ALIGN8(len)	(((len) + 7) & ~7)
 
+extern struct sock *xfrm_nl;
+extern u32 sysctl_xfrm_aevent_etime;
+extern u32 sysctl_xfrm_aevent_rseqth;
+
 extern struct semaphore xfrm_cfg_sem;
 
 /* Organization of SPD aka "XFRM rules"
@@ -135,6 +139,16 @@
 	/* State for replay detection */
 	struct xfrm_replay_state replay;
 
+	/* Replay detection state at the time we sent the last notification */
+	struct xfrm_replay_state preplay;
+
+	/* Replay detection notification settings */
+	u32			replay_maxage;
+	u32			replay_maxdiff;
+
+	/* Replay detection notification timer */
+	struct timer_list	rtimer;
+
 	/* Statistics */
 	struct xfrm_stats	stats;
 
@@ -169,6 +183,7 @@
 		u32 hard;
 		u32 proto;
 		u32 byid;
+		u32 aevent;
 	} data;
 
 	u32	seq;
@@ -305,7 +320,21 @@
 	struct xfrm_tmpl       	xfrm_vec[XFRM_MAX_DEPTH];
 };
 
-#define XFRM_KM_TIMEOUT		30
+#define XFRM_KM_TIMEOUT                30
+/* which seqno */
+#define XFRM_REPLAY_SEQ		1
+#define XFRM_REPLAY_OSEQ	2
+#define XFRM_REPLAY_SEQ_MASK	3
+/* what happened */
+#define XFRM_REPLAY_UPDATE	XFRM_AE_CR
+#define XFRM_REPLAY_TIMEOUT	XFRM_AE_CE
+
+/* default aevent timeout in units of 100ms */
+#define XFRM_AE_ETIME			10
+/* Async Event timer multiplier */
+#define XFRM_AE_ETH_M			10
+/* default seq threshold size */
+#define XFRM_AE_SEQT_SIZE		2
 
 struct xfrm_mgr
 {
@@ -865,6 +894,7 @@
 extern void xfrm_state_flush(u8 proto);
 extern int xfrm_replay_check(struct xfrm_state *x, u32 seq);
 extern void xfrm_replay_advance(struct xfrm_state *x, u32 seq);
+extern void xfrm_replay_notify(struct xfrm_state *x, int event);
 extern int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb);
 extern int xfrm_state_mtu(struct xfrm_state *x, int mtu);
 extern int xfrm_init_state(struct xfrm_state *x);
@@ -965,4 +995,16 @@
 	return index & 7;
 }
 
+static inline int xfrm_aevent_is_on(void)
+{
+	return netlink_has_listeners(xfrm_nl,XFRMNLGRP_AEVENTS);
+}
+
+static inline void xfrm_aevent_doreplay(struct xfrm_state *x)
+{
+	if (xfrm_aevent_is_on())
+		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
+}
+
+
 #endif	/* _NET_XFRM_H */
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index 2f278c8..7104536 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -26,6 +26,11 @@
 extern char sysctl_divert_version[];
 #endif /* CONFIG_NET_DIVERT */
 
+#ifdef CONFIG_XFRM
+extern u32 sysctl_xfrm_aevent_etime;
+extern u32 sysctl_xfrm_aevent_rseqth;
+#endif
+
 ctl_table core_table[] = {
 #ifdef CONFIG_NET
 	{
@@ -111,6 +116,24 @@
 		.proc_handler	= &proc_dostring
 	},
 #endif /* CONFIG_NET_DIVERT */
+#ifdef CONFIG_XFRM
+	{
+		.ctl_name	= NET_CORE_AEVENT_ETIME,
+		.procname	= "xfrm_aevent_etime",
+		.data		= &sysctl_xfrm_aevent_etime,
+		.maxlen		= sizeof(u32),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
+	{
+		.ctl_name	= NET_CORE_AEVENT_RSEQTH,
+		.procname	= "xfrm_aevent_rseqth",
+		.data		= &sysctl_xfrm_aevent_rseqth,
+		.maxlen		= sizeof(u32),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
+#endif /* CONFIG_XFRM */
 #endif /* CONFIG_NET */
 	{
 		.ctl_name	= NET_CORE_SOMAXCONN,
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index c656cba..8eaee49 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -20,6 +20,8 @@
 #include <linux/module.h>
 #include <asm/uaccess.h>
 
+u32 sysctl_xfrm_aevent_etime = XFRM_AE_ETIME;
+u32 sysctl_xfrm_aevent_rseqth = XFRM_AE_SEQT_SIZE;
 /* Each xfrm_state may be linked to two tables:
 
    1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
@@ -62,6 +64,8 @@
 {
 	if (del_timer(&x->timer))
 		BUG();
+	if (del_timer(&x->rtimer))
+		BUG();
 	kfree(x->aalg);
 	kfree(x->ealg);
 	kfree(x->calg);
@@ -190,11 +194,16 @@
 		init_timer(&x->timer);
 		x->timer.function = xfrm_timer_handler;
 		x->timer.data	  = (unsigned long)x;
+		init_timer(&x->rtimer);
+		x->rtimer.function = xfrm_replay_timer_handler;
+		x->rtimer.data     = (unsigned long)x;
 		x->curlft.add_time = (unsigned long)xtime.tv_sec;
 		x->lft.soft_byte_limit = XFRM_INF;
 		x->lft.soft_packet_limit = XFRM_INF;
 		x->lft.hard_byte_limit = XFRM_INF;
 		x->lft.hard_packet_limit = XFRM_INF;
+		x->replay_maxage = 0;
+		x->replay_maxdiff = 0;
 		spin_lock_init(&x->lock);
 	}
 	return x;
@@ -228,6 +237,8 @@
 		spin_unlock(&xfrm_state_lock);
 		if (del_timer(&x->timer))
 			__xfrm_state_put(x);
+		if (del_timer(&x->rtimer))
+			__xfrm_state_put(x);
 
 		/* The number two in this test is the reference
 		 * mentioned in the comment below plus the reference
@@ -426,6 +437,10 @@
 	if (!mod_timer(&x->timer, jiffies + HZ))
 		xfrm_state_hold(x);
 
+	if (x->replay_maxage &&
+	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
+		xfrm_state_hold(x);
+
 	wake_up(&km_waitq);
 }
 
@@ -762,6 +777,62 @@
 }
 EXPORT_SYMBOL(xfrm_state_walk);
 
+
+void xfrm_replay_notify(struct xfrm_state *x, int event)
+{
+	struct km_event c;
+	/* we send notify messages in case
+	 *  1. we updated on of the sequence numbers, and the seqno difference
+	 *     is at least x->replay_maxdiff, in this case we also update the
+	 *     timeout of our timer function
+	 *  2. if x->replay_maxage has elapsed since last update,
+	 *     and there were changes
+	 *
+	 *  The state structure must be locked!
+	 */
+
+	switch (event) {
+	case XFRM_REPLAY_UPDATE:
+		if (x->replay_maxdiff &&
+		    (x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
+		    (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))
+			return;
+
+		break;
+
+	case XFRM_REPLAY_TIMEOUT:
+		if ((x->replay.seq == x->preplay.seq) &&
+		    (x->replay.bitmap == x->preplay.bitmap) &&
+		    (x->replay.oseq == x->preplay.oseq))
+			return;
+
+		break;
+	}
+
+	memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
+	c.event = XFRM_MSG_NEWAE;
+	c.data.aevent = event;
+	km_state_notify(x, &c);
+
+resched:
+	if (x->replay_maxage &&
+	    !mod_timer(&x->rtimer, jiffies + x->replay_maxage))
+		xfrm_state_hold(x);
+
+}
+
+static void xfrm_replay_timer_handler(unsigned long data)
+{
+	struct xfrm_state *x = (struct xfrm_state*)data;
+
+	spin_lock(&x->lock);
+
+	if (xfrm_aevent_is_on() && x->km.state == XFRM_STATE_VALID)
+		xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
+
+	spin_unlock(&x->lock);
+}
+
 int xfrm_replay_check(struct xfrm_state *x, u32 seq)
 {
 	u32 diff;
@@ -805,6 +876,9 @@
 		diff = x->replay.seq - seq;
 		x->replay.bitmap |= (1U << diff);
 	}
+
+	if (xfrm_aevent_is_on())
+		xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
 }
 EXPORT_SYMBOL(xfrm_replay_advance);
 
@@ -835,7 +909,7 @@
 EXPORT_SYMBOL(km_policy_notify);
 EXPORT_SYMBOL(km_state_notify);
 
-static void km_state_expired(struct xfrm_state *x, int hard)
+void km_state_expired(struct xfrm_state *x, int hard)
 {
 	struct km_event c;
 
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 7de1755..6f643e5 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -28,7 +28,7 @@
 #include <net/netlink.h>
 #include <asm/uaccess.h>
 
-static struct sock *xfrm_nl;
+struct sock *xfrm_nl;
 
 static int verify_one_alg(struct rtattr **xfrma, enum xfrm_attr_type_t type)
 {
@@ -1618,3 +1618,5 @@
 module_exit(xfrm_user_exit);
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_XFRM);
+EXPORT_SYMBOL(xfrm_nl);
+