From: Dmitry Eremin-Solenikov <dmitry.ereminsoleni...@linaro.org>

Signed-off-by: Dmitry Eremin-Solenikov <dmitry.ereminsoleni...@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: e3108af2f0b58c2ceca422b418439bba5de04b11
 ** Merge commit sha: 0b3c6fecf5adbd74ef6dd0c08d541051854b91a4
 **/
 .../linux-generic/include/odp_ipsec_internal.h     | 20 ++++++++
 platform/linux-generic/odp_ipsec_sad.c             | 60 ++++++++++++++++++++++
 2 files changed, 80 insertions(+)

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 fe8dfd0e4..e010cfaa3 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);
        }
@@ -525,3 +529,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;
+}

Reply via email to