diff mbox series

[API-NEXT,v16,7/16] linux-gen: ipsec: add replay window support to SAD

Message ID 1510581630-13993-8-git-send-email-odpbot@yandex.ru
State New
Headers show
Series [API-NEXT,v16,1/16] linux-gen: ipsec: use counter instead of random IV for GCM | expand

Commit Message

Github ODP bot Nov. 13, 2017, 2 p.m. UTC
From: Dmitry Eremin-Solenikov <dmitry.ereminsolenikov@linaro.org>


Signed-off-by: Dmitry Eremin-Solenikov <dmitry.ereminsolenikov@linaro.org>

---
/** Email created from pull request 243 (lumag:ipsec-packet-impl-3)
 ** https://github.com/Linaro/odp/pull/243
 ** Patch: https://github.com/Linaro/odp/pull/243.patch
 ** Base sha: a908a4dead95321e84d6a8a23de060051dcd8969
 ** Merge commit sha: 845914564f2d99792452ac22a524279f44496a1d
 **/
 .../linux-generic/include/odp_ipsec_internal.h     | 20 ++++++++
 platform/linux-generic/odp_ipsec_sad.c             | 60 ++++++++++++++++++++++
 2 files changed, 80 insertions(+)
diff mbox series

Patch

diff --git a/platform/linux-generic/include/odp_ipsec_internal.h b/platform/linux-generic/include/odp_ipsec_internal.h
index 68ab195c7..0a7f96256 100644
--- a/platform/linux-generic/include/odp_ipsec_internal.h
+++ b/platform/linux-generic/include/odp_ipsec_internal.h
@@ -81,6 +81,9 @@  int _odp_ipsec_status_send(odp_queue_t queue,
 
 #define IPSEC_MAX_SALT_LEN	4    /**< Maximum salt length in bytes */
 
+/* 32 is minimum required by the standard. We do not support more */
+#define IPSEC_ANTIREPLAY_WS	32
+
 /**
  * Maximum number of available SAs
  */
@@ -127,6 +130,9 @@  struct ipsec_sa_s {
 
 			/* Only for outbound */
 			unsigned	use_counter_iv : 1;
+
+			/* Only for inbound */
+			unsigned	antireplay : 1;
 		};
 	};
 
@@ -134,6 +140,7 @@  struct ipsec_sa_s {
 		struct {
 			odp_ipsec_lookup_mode_t lookup_mode;
 			odp_u32be_t	lookup_dst_ip;
+			odp_atomic_u64_t antireplay;
 		} in;
 
 		struct {
@@ -200,6 +207,19 @@  int _odp_ipsec_sa_stats_precheck(ipsec_sa_t *ipsec_sa,
 int _odp_ipsec_sa_stats_update(ipsec_sa_t *ipsec_sa, uint32_t len,
 			       odp_ipsec_op_status_t *status);
 
+/* Run pre-check on sequence number of the packet.
+ *
+ * @retval <0 if the packet falls out of window
+ */
+int _odp_ipsec_sa_replay_precheck(ipsec_sa_t *ipsec_sa, uint32_t seq,
+				  odp_ipsec_op_status_t *status);
+
+/* Run check on sequence number of the packet and update window if necessary.
+ *
+ * @retval <0 if the packet falls out of window
+ */
+int _odp_ipsec_sa_replay_update(ipsec_sa_t *ipsec_sa, uint32_t seq,
+				odp_ipsec_op_status_t *status);
 /**
  * Try inline IPsec processing of provided packet.
  *
diff --git a/platform/linux-generic/odp_ipsec_sad.c b/platform/linux-generic/odp_ipsec_sad.c
index e42bf94ef..c30119249 100644
--- a/platform/linux-generic/odp_ipsec_sad.c
+++ b/platform/linux-generic/odp_ipsec_sad.c
@@ -215,6 +215,10 @@  odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param)
 			       param->inbound.lookup_param.dst_addr,
 			       sizeof(ipsec_sa->in.lookup_dst_ip));
 
+		if (param->inbound.antireplay_ws > IPSEC_ANTIREPLAY_WS)
+			return ODP_IPSEC_SA_INVALID;
+		ipsec_sa->antireplay = (param->inbound.antireplay_ws != 0);
+		odp_atomic_init_u64(&ipsec_sa->in.antireplay, 0);
 	} else {
 		odp_atomic_store_u32(&ipsec_sa->out.seq, 1);
 	}
@@ -528,3 +532,59 @@  int _odp_ipsec_sa_stats_update(ipsec_sa_t *ipsec_sa, uint32_t len,
 
 	return rc;
 }
+
+int _odp_ipsec_sa_replay_precheck(ipsec_sa_t *ipsec_sa, uint32_t seq,
+				  odp_ipsec_op_status_t *status)
+{
+	/* Try to be as quick as possible, we will discard packets later */
+	if (ipsec_sa->antireplay &&
+	    seq + IPSEC_ANTIREPLAY_WS <=
+	    (odp_atomic_load_u64(&ipsec_sa->in.antireplay) & 0xffffffff)) {
+		status->error.antireplay = 1;
+		return -1;
+	}
+
+	return 0;
+}
+
+int _odp_ipsec_sa_replay_update(ipsec_sa_t *ipsec_sa, uint32_t seq,
+				odp_ipsec_op_status_t *status)
+{
+	int cas = 0;
+	uint64_t state, new_state;
+
+	if (!ipsec_sa->antireplay)
+		return 0;
+
+	state = odp_atomic_load_u64(&ipsec_sa->in.antireplay);
+
+	while (0 == cas) {
+		uint32_t max_seq = state & 0xffffffff;
+		uint32_t mask = state >> 32;
+
+		if (seq + IPSEC_ANTIREPLAY_WS <= max_seq) {
+			status->error.antireplay = 1;
+			return -1;
+		}
+
+		if (seq > max_seq) {
+			mask <<= seq - max_seq;
+			mask |= 1;
+			max_seq = seq;
+		} else {
+			if (mask & (1U << (max_seq - seq))) {
+				status->error.antireplay = 1;
+				return -1;
+			}
+
+			mask |= (1U << (max_seq - seq));
+		}
+
+		new_state = (((uint64_t)mask) << 32) | max_seq;
+
+		cas = odp_atomic_cas_acq_rel_u64(&ipsec_sa->in.antireplay,
+						 &state, new_state);
+	}
+
+	return 0;
+}