LCOV - code coverage report
Current view: top level - lib/ipsec - ipsec_sqn.h (source / functions) Hit Total Coverage
Test: Code coverage Lines: 0 73 0.0 %
Date: 2025-01-02 22:41:34 Functions: 0 4 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 54 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2018 Intel Corporation
       3                 :            :  */
       4                 :            : 
       5                 :            : #ifndef _IPSEC_SQN_H_
       6                 :            : #define _IPSEC_SQN_H_
       7                 :            : 
       8                 :            : #define WINDOW_BUCKET_BITS              6 /* uint64_t */
       9                 :            : #define WINDOW_BUCKET_SIZE              (1 << WINDOW_BUCKET_BITS)
      10                 :            : #define WINDOW_BIT_LOC_MASK             (WINDOW_BUCKET_SIZE - 1)
      11                 :            : 
      12                 :            : /* minimum number of bucket, power of 2*/
      13                 :            : #define WINDOW_BUCKET_MIN               2
      14                 :            : #define WINDOW_BUCKET_MAX               (INT16_MAX + 1)
      15                 :            : 
      16                 :            : #define IS_ESN(sa)      ((sa)->sqn_mask == UINT64_MAX)
      17                 :            : 
      18                 :            : #define SQN_ATOMIC(sa)  ((sa)->type & RTE_IPSEC_SATP_SQN_ATOM)
      19                 :            : 
      20                 :            : /*
      21                 :            :  * gets SQN.hi32 bits, SQN supposed to be in network byte order.
      22                 :            :  */
      23                 :            : static inline rte_be32_t
      24                 :            : sqn_hi32(rte_be64_t sqn)
      25                 :            : {
      26                 :            : #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
      27                 :            :         return (sqn >> 32);
      28                 :            : #else
      29                 :          0 :         return sqn;
      30                 :            : #endif
      31                 :            : }
      32                 :            : 
      33                 :            : /*
      34                 :            :  * gets SQN.low32 bits, SQN supposed to be in network byte order.
      35                 :            :  */
      36                 :            : static inline rte_be32_t
      37                 :            : sqn_low32(rte_be64_t sqn)
      38                 :            : {
      39                 :            : #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
      40                 :            :         return sqn;
      41                 :            : #else
      42   [ #  #  #  # ]:          0 :         return (sqn >> 32);
      43                 :            : #endif
      44                 :            : }
      45                 :            : 
      46                 :            : /*
      47                 :            :  * gets SQN.low16 bits, SQN supposed to be in network byte order.
      48                 :            :  */
      49                 :            : static inline rte_be16_t
      50                 :            : sqn_low16(rte_be64_t sqn)
      51                 :            : {
      52                 :            : #if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
      53                 :            :         return sqn;
      54                 :            : #else
      55                 :          0 :         return (sqn >> 48);
      56                 :            : #endif
      57                 :            : }
      58                 :            : 
      59                 :            : /*
      60                 :            :  * According to RFC4303 A2.1, determine the high-order bit of sequence number.
      61                 :            :  * use 32bit arithmetic inside, return uint64_t.
      62                 :            :  */
      63                 :            : static inline uint64_t
      64                 :            : reconstruct_esn(uint64_t t, uint32_t sqn, uint32_t w)
      65                 :            : {
      66                 :            :         uint32_t th, tl, bl;
      67                 :            : 
      68                 :          0 :         tl = t;
      69                 :          0 :         th = t >> 32;
      70                 :          0 :         bl = tl - w + 1;
      71                 :            : 
      72                 :            :         /* case A: window is within one sequence number subspace */
      73         [ #  # ]:          0 :         if (tl >= (w - 1))
      74                 :          0 :                 th += (sqn < bl);
      75                 :            :         /* case B: window spans two sequence number subspaces */
      76   [ #  #  #  # ]:          0 :         else if (th != 0)
      77                 :          0 :                 th -= (sqn >= bl);
      78                 :            : 
      79                 :            :         /* return constructed sequence with proper high-order bits */
      80                 :          0 :         return (uint64_t)th << 32 | sqn;
      81                 :            : }
      82                 :            : 
      83                 :            : /**
      84                 :            :  * Perform the replay checking.
      85                 :            :  *
      86                 :            :  * struct rte_ipsec_sa contains the window and window related parameters,
      87                 :            :  * such as the window size, bitmask, and the last acknowledged sequence number.
      88                 :            :  *
      89                 :            :  * Based on RFC 6479.
      90                 :            :  * Blocks are 64 bits unsigned integers
      91                 :            :  */
      92                 :            : static inline int32_t
      93                 :            : esn_inb_check_sqn(const struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
      94                 :            :         uint64_t sqn)
      95                 :            : {
      96                 :            :         uint32_t bit, bucket;
      97                 :            : 
      98                 :            :         /* replay not enabled */
      99         [ #  # ]:          0 :         if (sa->replay.win_sz == 0)
     100                 :            :                 return 0;
     101                 :            : 
     102                 :            :         /* seq is larger than lastseq */
     103         [ #  # ]:          0 :         if (sqn > rsn->sqn)
     104                 :            :                 return 0;
     105                 :            : 
     106                 :            :         /* seq is outside window */
     107   [ #  #  #  # ]:          0 :         if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
     108                 :            :                 return -EINVAL;
     109                 :            : 
     110                 :            :         /* seq is inside the window */
     111                 :          0 :         bit = sqn & WINDOW_BIT_LOC_MASK;
     112                 :          0 :         bucket = (sqn >> WINDOW_BUCKET_BITS) & sa->replay.bucket_index_mask;
     113                 :            : 
     114                 :            :         /* already seen packet */
     115         [ #  # ]:          0 :         if (rsn->window[bucket] & ((uint64_t)1 << bit))
     116                 :          0 :                 return -EINVAL;
     117                 :            : 
     118                 :            :         return 0;
     119                 :            : }
     120                 :            : 
     121                 :            : /**
     122                 :            :  * For outbound SA perform the sequence number update.
     123                 :            :  */
     124                 :            : static inline uint64_t
     125                 :          0 : esn_outb_update_sqn(struct rte_ipsec_sa *sa, uint32_t *num)
     126                 :            : {
     127                 :            :         uint64_t n, s, sqn;
     128                 :            : 
     129                 :          0 :         n = *num;
     130         [ #  # ]:          0 :         if (SQN_ATOMIC(sa))
     131                 :          0 :                 sqn = rte_atomic_fetch_add_explicit(&sa->sqn.outb, n, rte_memory_order_relaxed) + n;
     132                 :            :         else {
     133                 :          0 :                 sqn = sa->sqn.outb + n;
     134                 :          0 :                 sa->sqn.outb = sqn;
     135                 :            :         }
     136                 :            : 
     137                 :            :         /* overflow */
     138         [ #  # ]:          0 :         if (sqn > sa->sqn_mask) {
     139                 :          0 :                 s = sqn - sa->sqn_mask;
     140         [ #  # ]:          0 :                 *num = (s < n) ?  n - s : 0;
     141                 :            :         }
     142                 :            : 
     143                 :          0 :         return sqn - n;
     144                 :            : }
     145                 :            : 
     146                 :            : /**
     147                 :            :  * For inbound SA perform the sequence number and replay window update.
     148                 :            :  */
     149                 :            : static inline int32_t
     150                 :          0 : esn_inb_update_sqn(struct replay_sqn *rsn, const struct rte_ipsec_sa *sa,
     151                 :            :         uint64_t sqn)
     152                 :            : {
     153                 :            :         uint32_t bit, bucket, last_bucket, new_bucket, diff, i;
     154                 :            : 
     155                 :            :         /* handle ESN */
     156         [ #  # ]:          0 :         if (IS_ESN(sa))
     157         [ #  # ]:          0 :                 sqn = reconstruct_esn(rsn->sqn, sqn, sa->replay.win_sz);
     158                 :            : 
     159                 :            :         /* seq is outside window*/
     160   [ #  #  #  # ]:          0 :         if (sqn == 0 || sqn + sa->replay.win_sz < rsn->sqn)
     161                 :            :                 return -EINVAL;
     162                 :            : 
     163                 :            :         /* update the bit */
     164                 :          0 :         bucket = (sqn >> WINDOW_BUCKET_BITS);
     165                 :            : 
     166                 :            :         /* check if the seq is within the range */
     167         [ #  # ]:          0 :         if (sqn > rsn->sqn) {
     168                 :          0 :                 last_bucket = rsn->sqn >> WINDOW_BUCKET_BITS;
     169                 :          0 :                 diff = bucket - last_bucket;
     170                 :            :                 /* seq is way after the range of WINDOW_SIZE */
     171                 :          0 :                 if (diff > sa->replay.nb_bucket)
     172                 :            :                         diff = sa->replay.nb_bucket;
     173                 :            : 
     174         [ #  # ]:          0 :                 for (i = 0; i != diff; i++) {
     175                 :          0 :                         new_bucket = (i + last_bucket + 1) &
     176                 :          0 :                                 sa->replay.bucket_index_mask;
     177                 :          0 :                         rsn->window[new_bucket] = 0;
     178                 :            :                 }
     179                 :          0 :                 rsn->sqn = sqn;
     180                 :            :         }
     181                 :            : 
     182                 :          0 :         bucket &= sa->replay.bucket_index_mask;
     183                 :          0 :         bit = (uint64_t)1 << (sqn & WINDOW_BIT_LOC_MASK);
     184                 :            : 
     185                 :            :         /* already seen packet */
     186         [ #  # ]:          0 :         if (rsn->window[bucket] & bit)
     187                 :            :                 return -EINVAL;
     188                 :            : 
     189                 :          0 :         rsn->window[bucket] |= bit;
     190                 :          0 :         return 0;
     191                 :            : }
     192                 :            : 
     193                 :            : /**
     194                 :            :  * To achieve ability to do multiple readers single writer for
     195                 :            :  * SA replay window information and sequence number (RSN)
     196                 :            :  * basic RCU schema is used:
     197                 :            :  * SA have 2 copies of RSN (one for readers, another for writers).
     198                 :            :  * Each RSN contains a rwlock that has to be grabbed (for read/write)
     199                 :            :  * to avoid races between readers and writer.
     200                 :            :  * Writer is responsible to make a copy or reader RSN, update it
     201                 :            :  * and mark newly updated RSN as readers one.
     202                 :            :  * That approach is intended to minimize contention and cache sharing
     203                 :            :  * between writer and readers.
     204                 :            :  */
     205                 :            : 
     206                 :            : /**
     207                 :            :  * Copy replay window and SQN.
     208                 :            :  */
     209                 :            : static inline void
     210                 :            : rsn_copy(const struct rte_ipsec_sa *sa, uint32_t dst, uint32_t src)
     211                 :            : {
     212                 :            :         uint32_t i, n;
     213                 :            :         struct replay_sqn *d;
     214                 :            :         const struct replay_sqn *s;
     215                 :            : 
     216                 :          0 :         d = sa->sqn.inb.rsn[dst];
     217                 :          0 :         s = sa->sqn.inb.rsn[src];
     218                 :            : 
     219                 :          0 :         n = sa->replay.nb_bucket;
     220                 :            : 
     221                 :          0 :         d->sqn = s->sqn;
     222         [ #  # ]:          0 :         for (i = 0; i != n; i++)
     223                 :          0 :                 d->window[i] = s->window[i];
     224                 :            : }
     225                 :            : 
     226                 :            : /**
     227                 :            :  * Get RSN for read-only access.
     228                 :            :  */
     229                 :            : static inline struct replay_sqn *
     230                 :          0 : rsn_acquire(struct rte_ipsec_sa *sa)
     231                 :            : {
     232                 :            :         uint32_t n;
     233                 :            :         struct replay_sqn *rsn;
     234                 :            : 
     235                 :          0 :         n = sa->sqn.inb.rdidx;
     236                 :          0 :         rsn = sa->sqn.inb.rsn[n];
     237                 :            : 
     238         [ #  # ]:          0 :         if (!SQN_ATOMIC(sa))
     239                 :            :                 return rsn;
     240                 :            : 
     241                 :            :         /* check there are no writers */
     242         [ #  # ]:          0 :         while (rte_rwlock_read_trylock(&rsn->rwl) < 0) {
     243                 :            :                 rte_pause();
     244                 :          0 :                 n = sa->sqn.inb.rdidx;
     245                 :          0 :                 rsn = sa->sqn.inb.rsn[n];
     246                 :          0 :                 rte_compiler_barrier();
     247                 :            :         }
     248                 :            : 
     249                 :            :         return rsn;
     250                 :            : }
     251                 :            : 
     252                 :            : /**
     253                 :            :  * Release read-only access for RSN.
     254                 :            :  */
     255                 :            : static inline void
     256                 :            : rsn_release(struct rte_ipsec_sa *sa, struct replay_sqn *rsn)
     257                 :            : {
     258   [ #  #  #  # ]:          0 :         if (SQN_ATOMIC(sa))
     259                 :            :                 rte_rwlock_read_unlock(&rsn->rwl);
     260                 :            : }
     261                 :            : 
     262                 :            : /**
     263                 :            :  * Start RSN update.
     264                 :            :  */
     265                 :            : static inline struct replay_sqn *
     266                 :          0 : rsn_update_start(struct rte_ipsec_sa *sa)
     267                 :            : {
     268                 :            :         uint32_t k, n;
     269                 :            :         struct replay_sqn *rsn;
     270                 :            : 
     271                 :          0 :         n = sa->sqn.inb.wridx;
     272                 :            : 
     273                 :            :         /* no active writers */
     274                 :            :         RTE_ASSERT(n == sa->sqn.inb.rdidx);
     275                 :            : 
     276         [ #  # ]:          0 :         if (!SQN_ATOMIC(sa))
     277                 :          0 :                 return sa->sqn.inb.rsn[n];
     278                 :            : 
     279                 :          0 :         k = REPLAY_SQN_NEXT(n);
     280                 :          0 :         sa->sqn.inb.wridx = k;
     281                 :            : 
     282                 :          0 :         rsn = sa->sqn.inb.rsn[k];
     283                 :          0 :         rte_rwlock_write_lock(&rsn->rwl);
     284                 :            :         rsn_copy(sa, k, n);
     285                 :            : 
     286                 :            :         return rsn;
     287                 :            : }
     288                 :            : 
     289                 :            : /**
     290                 :            :  * Finish RSN update.
     291                 :            :  */
     292                 :            : static inline void
     293                 :            : rsn_update_finish(struct rte_ipsec_sa *sa, struct replay_sqn *rsn)
     294                 :            : {
     295                 :            :         uint32_t n;
     296                 :            : 
     297         [ #  # ]:          0 :         if (!SQN_ATOMIC(sa))
     298                 :            :                 return;
     299                 :            : 
     300                 :          0 :         n = sa->sqn.inb.wridx;
     301                 :            :         RTE_ASSERT(n != sa->sqn.inb.rdidx);
     302                 :            :         RTE_ASSERT(rsn == sa->sqn.inb.rsn[n]);
     303                 :            : 
     304                 :            :         rte_rwlock_write_unlock(&rsn->rwl);
     305                 :          0 :         sa->sqn.inb.rdidx = n;
     306                 :            : }
     307                 :            : 
     308                 :            : 
     309                 :            : #endif /* _IPSEC_SQN_H_ */

Generated by: LCOV version 1.14