ipsec: add per-SA error counters
[vpp.git] / src / vnet / ipsec / ipsec_sa.h
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifndef __IPSEC_SPD_SA_H__
16 #define __IPSEC_SPD_SA_H__
17
18 #include <vlib/vlib.h>
19 #include <vnet/crypto/crypto.h>
20 #include <vnet/ip/ip.h>
21 #include <vnet/fib/fib_node.h>
22 #include <vnet/tunnel/tunnel.h>
23
24 #define foreach_ipsec_crypto_alg                                              \
25   _ (0, NONE, "none")                                                         \
26   _ (1, AES_CBC_128, "aes-cbc-128")                                           \
27   _ (2, AES_CBC_192, "aes-cbc-192")                                           \
28   _ (3, AES_CBC_256, "aes-cbc-256")                                           \
29   _ (4, AES_CTR_128, "aes-ctr-128")                                           \
30   _ (5, AES_CTR_192, "aes-ctr-192")                                           \
31   _ (6, AES_CTR_256, "aes-ctr-256")                                           \
32   _ (7, AES_GCM_128, "aes-gcm-128")                                           \
33   _ (8, AES_GCM_192, "aes-gcm-192")                                           \
34   _ (9, AES_GCM_256, "aes-gcm-256")                                           \
35   _ (10, DES_CBC, "des-cbc")                                                  \
36   _ (11, 3DES_CBC, "3des-cbc")                                                \
37   _ (12, CHACHA20_POLY1305, "chacha20-poly1305")
38
39 typedef enum
40 {
41 #define _(v, f, s) IPSEC_CRYPTO_ALG_##f = v,
42   foreach_ipsec_crypto_alg
43 #undef _
44     IPSEC_CRYPTO_N_ALG,
45 } __clib_packed ipsec_crypto_alg_t;
46
47 #define IPSEC_CRYPTO_ALG_IS_GCM(_alg)                     \
48   (((_alg == IPSEC_CRYPTO_ALG_AES_GCM_128) ||             \
49     (_alg == IPSEC_CRYPTO_ALG_AES_GCM_192) ||             \
50     (_alg == IPSEC_CRYPTO_ALG_AES_GCM_256)))
51
52 #define IPSEC_CRYPTO_ALG_IS_CTR(_alg)                                         \
53   (((_alg == IPSEC_CRYPTO_ALG_AES_CTR_128) ||                                 \
54     (_alg == IPSEC_CRYPTO_ALG_AES_CTR_192) ||                                 \
55     (_alg == IPSEC_CRYPTO_ALG_AES_CTR_256)))
56
57 #define IPSEC_CRYPTO_ALG_CTR_AEAD_OTHERS(_alg)                                \
58   (_alg == IPSEC_CRYPTO_ALG_CHACHA20_POLY1305)
59
60 #define foreach_ipsec_integ_alg                                            \
61   _ (0, NONE, "none")                                                      \
62   _ (1, MD5_96, "md5-96")           /* RFC2403 */                          \
63   _ (2, SHA1_96, "sha1-96")         /* RFC2404 */                          \
64   _ (3, SHA_256_96, "sha-256-96")   /* draft-ietf-ipsec-ciph-sha-256-00 */ \
65   _ (4, SHA_256_128, "sha-256-128") /* RFC4868 */                          \
66   _ (5, SHA_384_192, "sha-384-192") /* RFC4868 */                          \
67   _ (6, SHA_512_256, "sha-512-256")     /* RFC4868 */
68
69 typedef enum
70 {
71 #define _(v, f, s) IPSEC_INTEG_ALG_##f = v,
72   foreach_ipsec_integ_alg
73 #undef _
74     IPSEC_INTEG_N_ALG,
75 } __clib_packed ipsec_integ_alg_t;
76
77 typedef enum
78 {
79   IPSEC_PROTOCOL_AH = 0,
80   IPSEC_PROTOCOL_ESP = 1
81 } __clib_packed ipsec_protocol_t;
82
83 #define IPSEC_KEY_MAX_LEN 128
84 typedef struct ipsec_key_t_
85 {
86   u8 len;
87   u8 data[IPSEC_KEY_MAX_LEN];
88 } ipsec_key_t;
89
90 /*
91  * Enable extended sequence numbers
92  * Enable Anti-replay
93  * IPsec tunnel mode if non-zero, else transport mode
94  * IPsec tunnel mode is IPv6 if non-zero,
95  * else IPv4 tunnel only valid if is_tunnel is non-zero
96  * enable UDP encapsulation for NAT traversal
97  */
98 #define foreach_ipsec_sa_flags                                                \
99   _ (0, NONE, "none")                                                         \
100   _ (1, USE_ESN, "esn")                                                       \
101   _ (2, USE_ANTI_REPLAY, "anti-replay")                                       \
102   _ (4, IS_TUNNEL, "tunnel")                                                  \
103   _ (8, IS_TUNNEL_V6, "tunnel-v6")                                            \
104   _ (16, UDP_ENCAP, "udp-encap")                                              \
105   _ (32, IS_PROTECT, "Protect")                                               \
106   _ (64, IS_INBOUND, "inbound")                                               \
107   _ (128, IS_AEAD, "aead")                                                    \
108   _ (256, IS_CTR, "ctr")                                                      \
109   _ (512, IS_ASYNC, "async")                                                  \
110   _ (1024, NO_ALGO_NO_DROP, "no-algo-no-drop")
111
112 typedef enum ipsec_sad_flags_t_
113 {
114 #define _(v, f, s) IPSEC_SA_FLAG_##f = v,
115   foreach_ipsec_sa_flags
116 #undef _
117 } __clib_packed ipsec_sa_flags_t;
118
119 STATIC_ASSERT (sizeof (ipsec_sa_flags_t) == 2, "IPSEC SA flags != 2 byte");
120
121 #define foreach_ipsec_sa_err                                                  \
122   _ (0, LOST, lost, "packets lost")                                           \
123   _ (1, HANDOFF, handoff, "hand-off")                                         \
124   _ (2, INTEG_ERROR, integ_error, "Integrity check failed")                   \
125   _ (3, DECRYPTION_FAILED, decryption_failed, "Decryption failed")            \
126   _ (4, CRYPTO_ENGINE_ERROR, crypto_engine_error,                             \
127      "crypto engine error (dropped)")                                         \
128   _ (5, REPLAY, replay, "SA replayed packet")                                 \
129   _ (6, RUNT, runt, "undersized packet")                                      \
130   _ (7, NO_BUFFERS, no_buffers, "no buffers (dropped)")                       \
131   _ (8, OVERSIZED_HEADER, oversized_header,                                   \
132      "buffer with oversized header (dropped)")                                \
133   _ (9, NO_TAIL_SPACE, no_tail_space,                                         \
134      "no enough buffer tail space (dropped)")                                 \
135   _ (10, TUN_NO_PROTO, tun_no_proto, "no tunnel protocol")                    \
136   _ (11, UNSUP_PAYLOAD, unsup_payload, "unsupported payload")                 \
137   _ (12, SEQ_CYCLED, seq_cycled, "sequence number cycled (dropped)")          \
138   _ (13, CRYPTO_QUEUE_FULL, crypto_queue_full, "crypto queue full (dropped)") \
139   _ (14, NO_ENCRYPTION, no_encryption, "no Encrypting SA (dropped)")          \
140   _ (15, DROP_FRAGMENTS, drop_fragments, "IP fragments drop")
141
142 typedef enum
143 {
144 #define _(v, f, s, d) IPSEC_SA_ERROR_##f = v,
145   foreach_ipsec_sa_err
146 #undef _
147     IPSEC_SA_N_ERRORS,
148 } __clib_packed ipsec_sa_err_t;
149
150 typedef struct
151 {
152   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
153
154   /* flags */
155   ipsec_sa_flags_t flags;
156
157   u8 crypto_iv_size;
158   u8 esp_block_align;
159   u8 integ_icv_size;
160
161   u8 __pad1[3];
162
163   u32 thread_index;
164
165   u32 spi;
166   u32 seq;
167   u32 seq_hi;
168   u64 replay_window;
169   u64 iv_counter;
170   dpo_id_t dpo;
171
172   vnet_crypto_key_index_t crypto_key_index;
173   vnet_crypto_key_index_t integ_key_index;
174
175   /* Union data shared by sync and async ops, updated when mode is
176    * changed. */
177   union
178   {
179     struct
180     {
181       vnet_crypto_op_id_t crypto_enc_op_id:16;
182       vnet_crypto_op_id_t crypto_dec_op_id:16;
183       vnet_crypto_op_id_t integ_op_id:16;
184     };
185
186     struct
187     {
188       vnet_crypto_async_op_id_t crypto_async_enc_op_id:16;
189       vnet_crypto_async_op_id_t crypto_async_dec_op_id:16;
190       vnet_crypto_key_index_t linked_key_index;
191     };
192
193     u64 crypto_op_data;
194   };
195
196   CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);
197
198   union
199   {
200     ip4_header_t ip4_hdr;
201     ip6_header_t ip6_hdr;
202   };
203   udp_header_t udp_hdr;
204
205   /* Salt used in CTR modes (incl. GCM) - stored in network byte order */
206   u32 salt;
207
208   ipsec_protocol_t protocol;
209   tunnel_encap_decap_flags_t tunnel_flags;
210   u8 __pad[2];
211
212   /* data accessed by dataplane code should be above this comment */
213     CLIB_CACHE_LINE_ALIGN_MARK (cacheline2);
214
215   /* Elements with u64 size multiples */
216   union
217   {
218     struct
219     {
220       vnet_crypto_op_id_t crypto_enc_op_id:16;
221       vnet_crypto_op_id_t crypto_dec_op_id:16;
222       vnet_crypto_op_id_t integ_op_id:16;
223     };
224     u64 data;
225   } sync_op_data;
226
227   union
228   {
229     struct
230     {
231       vnet_crypto_async_op_id_t crypto_async_enc_op_id:16;
232       vnet_crypto_async_op_id_t crypto_async_dec_op_id:16;
233       vnet_crypto_key_index_t linked_key_index;
234     };
235     u64 data;
236   } async_op_data;
237
238   tunnel_t tunnel;
239
240   fib_node_t node;
241
242   /* elements with u32 size */
243   u32 id;
244   u32 stat_index;
245   vnet_crypto_alg_t integ_calg;
246   vnet_crypto_alg_t crypto_calg;
247
248   /* else u8 packed */
249   ipsec_crypto_alg_t crypto_alg;
250   ipsec_integ_alg_t integ_alg;
251
252   ipsec_key_t integ_key;
253   ipsec_key_t crypto_key;
254 } ipsec_sa_t;
255
256 STATIC_ASSERT_OFFSET_OF (ipsec_sa_t, cacheline1, CLIB_CACHE_LINE_BYTES);
257 STATIC_ASSERT_OFFSET_OF (ipsec_sa_t, cacheline2, 2 * CLIB_CACHE_LINE_BYTES);
258
259 /**
260  * Pool of IPSec SAs
261  */
262 extern ipsec_sa_t *ipsec_sa_pool;
263
264 /*
265  * Ensure that the IPsec data does not overlap with the IP data in
266  * the buffer meta data
267  */
268 STATIC_ASSERT (STRUCT_OFFSET_OF (vnet_buffer_opaque_t, ipsec.sad_index) ==
269                  STRUCT_OFFSET_OF (vnet_buffer_opaque_t, ip.save_protocol),
270                "IPSec data is overlapping with IP data");
271
272 #define _(a,v,s)                                                        \
273   always_inline int                                                     \
274   ipsec_sa_is_set_##v (const ipsec_sa_t *sa) {                          \
275     return (sa->flags & IPSEC_SA_FLAG_##v);                             \
276   }
277 foreach_ipsec_sa_flags
278 #undef _
279 #define _(a,v,s)                                                        \
280   always_inline int                                                     \
281   ipsec_sa_set_##v (ipsec_sa_t *sa) {                                   \
282     return (sa->flags |= IPSEC_SA_FLAG_##v);                            \
283   }
284   foreach_ipsec_sa_flags
285 #undef _
286 #define _(a,v,s)                                                        \
287   always_inline int                                                     \
288   ipsec_sa_unset_##v (ipsec_sa_t *sa) {                                 \
289     return (sa->flags &= ~IPSEC_SA_FLAG_##v);                           \
290   }
291   foreach_ipsec_sa_flags
292 #undef _
293 /**
294  * @brief
295  * SA packet & bytes counters
296  */
297 extern vlib_combined_counter_main_t ipsec_sa_counters;
298 extern vlib_simple_counter_main_t ipsec_sa_err_counters[IPSEC_SA_N_ERRORS];
299
300 extern void ipsec_mk_key (ipsec_key_t * key, const u8 * data, u8 len);
301
302 extern int ipsec_sa_update (u32 id, u16 src_port, u16 dst_port,
303                             const tunnel_t *tun, bool is_tun);
304 extern int
305 ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
306                        ipsec_crypto_alg_t crypto_alg, const ipsec_key_t *ck,
307                        ipsec_integ_alg_t integ_alg, const ipsec_key_t *ik,
308                        ipsec_sa_flags_t flags, u32 salt, u16 src_port,
309                        u16 dst_port, const tunnel_t *tun, u32 *sa_out_index);
310 extern index_t ipsec_sa_find_and_lock (u32 id);
311 extern int ipsec_sa_unlock_id (u32 id);
312 extern void ipsec_sa_unlock (index_t sai);
313 extern void ipsec_sa_lock (index_t sai);
314 extern void ipsec_sa_clear (index_t sai);
315 extern void ipsec_sa_set_crypto_alg (ipsec_sa_t * sa,
316                                      ipsec_crypto_alg_t crypto_alg);
317 extern void ipsec_sa_set_integ_alg (ipsec_sa_t * sa,
318                                     ipsec_integ_alg_t integ_alg);
319
320 typedef walk_rc_t (*ipsec_sa_walk_cb_t) (ipsec_sa_t * sa, void *ctx);
321 extern void ipsec_sa_walk (ipsec_sa_walk_cb_t cd, void *ctx);
322
323 extern u8 *format_ipsec_replay_window (u8 *s, va_list *args);
324 extern u8 *format_ipsec_crypto_alg (u8 * s, va_list * args);
325 extern u8 *format_ipsec_integ_alg (u8 * s, va_list * args);
326 extern u8 *format_ipsec_sa (u8 * s, va_list * args);
327 extern u8 *format_ipsec_key (u8 * s, va_list * args);
328 extern uword unformat_ipsec_crypto_alg (unformat_input_t * input,
329                                         va_list * args);
330 extern uword unformat_ipsec_integ_alg (unformat_input_t * input,
331                                        va_list * args);
332 extern uword unformat_ipsec_key (unformat_input_t * input, va_list * args);
333
334 #define IPSEC_UDP_PORT_NONE ((u16)~0)
335
336 /*
337  * Anti Replay definitions
338  */
339
340 #define IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE (64)
341 #define IPSEC_SA_ANTI_REPLAY_WINDOW_MAX_INDEX (IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE-1)
342
343 /*
344  * sequence number less than the lower bound are outside of the window
345  * From RFC4303 Appendix A:
346  *  Bl = Tl - W + 1
347  */
348 #define IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND(_tl) (_tl - IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE + 1)
349
350 always_inline int
351 ipsec_sa_anti_replay_check (const ipsec_sa_t *sa, u32 seq)
352 {
353   if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa) &&
354       sa->replay_window & (1ULL << (sa->seq - seq)))
355     return 1;
356   else
357     return 0;
358 }
359
360 /*
361  * Anti replay check.
362  *  inputs need to be in host byte order.
363  *
364  * The function runs in two contexts. pre and post decrypt.
365  * Pre-decrypt it:
366  *  1 - determines if a packet is a replay - a simple check in the window
367  *  2 - returns the hi-seq number that should be used to decrypt.
368  * post-decrypt:
369  *  Checks whether the packet is a replay or falls out of window
370  *
371  * This funcion should be called even without anti-replay enabled to ensure
372  * the high sequence number is set.
373  */
374 always_inline int
375 ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
376                                      u32 hi_seq_used, bool post_decrypt,
377                                      u32 *hi_seq_req)
378 {
379   ASSERT ((post_decrypt == false) == (hi_seq_req != 0));
380
381   if (!ipsec_sa_is_set_USE_ESN (sa))
382     {
383       if (hi_seq_req)
384         /* no ESN, therefore the hi-seq is always 0 */
385         *hi_seq_req = 0;
386
387       if (!ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
388         return 0;
389
390       if (PREDICT_TRUE (seq > sa->seq))
391         return 0;
392
393       u32 diff = sa->seq - seq;
394
395       if (IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE > diff)
396         return ((sa->replay_window & (1ULL << diff)) ? 1 : 0);
397       else
398         return 1;
399
400       return 0;
401     }
402
403   if (!ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
404     {
405       /* there's no AR configured for this SA, but in order
406        * to know whether a packet has wrapped the hi ESN we need
407        * to know whether it is out of window. if we use the default
408        * lower bound then we are effectively forcing AR because
409        * out of window packets will get the increased hi seq number
410        * and will thus fail to decrypt. IOW we need a window to know
411        * if the SN has wrapped, but we don't want a window to check for
412        * anti replay. to resolve the contradiction we use a huge window.
413        * if the packet is not within 2^30 of the current SN, we'll consider
414        * it a wrap.
415        */
416       if (hi_seq_req)
417         {
418           if (seq >= sa->seq)
419             /* The packet's sequence number is larger that the SA's.
420              * that can't be a warp - unless we lost more than
421              * 2^32 packets ... how could we know? */
422             *hi_seq_req = sa->seq_hi;
423           else
424             {
425               /* The packet's SN is less than the SAs, so either the SN has
426                * wrapped or the SN is just old. */
427               if (sa->seq - seq > (1 << 30))
428                 /* It's really really really old => it wrapped */
429                 *hi_seq_req = sa->seq_hi + 1;
430               else
431                 *hi_seq_req = sa->seq_hi;
432             }
433         }
434       /*
435        * else
436        *   this is post-decrpyt and since it decrypted we accept it
437        */
438       return 0;
439     }
440   if (PREDICT_TRUE (sa->seq >= (IPSEC_SA_ANTI_REPLAY_WINDOW_MAX_INDEX)))
441     {
442       /*
443        * the last sequence number VPP recieved is more than one
444        * window size greater than zero.
445        * Case A from RFC4303 Appendix A.
446        */
447       if (seq < IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND (sa->seq))
448         {
449           /*
450            * the received sequence number is lower than the lower bound
451            * of the window, this could mean either a replay packet or that
452            * the high sequence number has wrapped. if it decrypts corrently
453            * then it's the latter.
454            */
455           if (post_decrypt)
456             {
457               if (hi_seq_used == sa->seq_hi)
458                 /* the high sequence number used to succesfully decrypt this
459                  * packet is the same as the last-sequnence number of the SA.
460                  * that means this packet did not cause a wrap.
461                  * this packet is thus out of window and should be dropped */
462                 return 1;
463               else
464                 /* The packet decrypted with a different high sequence number
465                  * to the SA, that means it is the wrap packet and should be
466                  * accepted */
467                 return 0;
468             }
469           else
470             {
471               /* pre-decrypt it might be the might that casues a wrap, we
472                * need to decrpyt to find out */
473               if (hi_seq_req)
474                 *hi_seq_req = sa->seq_hi + 1;
475               return 0;
476             }
477         }
478       else
479         {
480           /*
481            * the recieved sequence number greater than the low
482            * end of the window.
483            */
484           if (hi_seq_req)
485             *hi_seq_req = sa->seq_hi;
486           if (seq <= sa->seq)
487             /*
488              * The recieved seq number is within bounds of the window
489              * check if it's a duplicate
490              */
491             return (ipsec_sa_anti_replay_check (sa, seq));
492           else
493             /*
494              * The received sequence number is greater than the window
495              * upper bound. this packet will move the window along, assuming
496              * it decrypts correctly.
497              */
498             return 0;
499         }
500     }
501   else
502     {
503       /*
504        * the last sequence number VPP recieved is within one window
505        * size of zero, i.e. 0 < TL < WINDOW_SIZE, the lower bound is thus a
506        * large sequence number.
507        * Note that the check below uses unsiged integer arthimetic, so the
508        * RHS will be a larger number.
509        * Case B from RFC4303 Appendix A.
510        */
511       if (seq < IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND (sa->seq))
512         {
513           /*
514            * the sequence number is less than the lower bound.
515            */
516           if (seq <= sa->seq)
517             {
518               /*
519                * the packet is within the window upper bound.
520                * check for duplicates.
521                */
522               if (hi_seq_req)
523                 *hi_seq_req = sa->seq_hi;
524               return (ipsec_sa_anti_replay_check (sa, seq));
525             }
526           else
527             {
528               /*
529                * the packet is less the window lower bound or greater than
530                * the higher bound, depending on how you look at it...
531                * We're assuming, given that the last sequence number received,
532                * TL < WINDOW_SIZE, that a largeer seq num is more likely to be
533                * a packet that moves the window forward, than a packet that has
534                * wrapped the high sequence again. If it were the latter then
535                * we've lost close to 2^32 packets.
536                */
537               if (hi_seq_req)
538                 *hi_seq_req = sa->seq_hi;
539               return 0;
540             }
541         }
542       else
543         {
544           /*
545            * the packet seq number is between the lower bound (a large nubmer)
546            * and MAX_SEQ_NUM. This is in the window since the window upper bound
547            * tl > 0.
548            * However, since TL is the other side of 0 to the received
549            * packet, the SA has moved on to a higher sequence number.
550            */
551           if (hi_seq_req)
552             *hi_seq_req = sa->seq_hi - 1;
553           return (ipsec_sa_anti_replay_check (sa, seq));
554         }
555     }
556
557   /* unhandled case */
558   ASSERT (0);
559   return 0;
560 }
561
562 always_inline u32
563 ipsec_sa_anti_replay_window_shift (ipsec_sa_t *sa, u32 inc)
564 {
565   u32 n_lost = 0;
566
567   if (inc < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
568     {
569       if (sa->seq > IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
570         {
571           /*
572            * count how many holes there are in the portion
573            * of the window that we will right shift of the end
574            * as a result of this increments
575            */
576           u64 mask = (((u64) 1 << inc) - 1) << (BITS (u64) - inc);
577           u64 old = sa->replay_window & mask;
578           /* the number of packets we saw in this section of the window */
579           u64 seen = count_set_bits (old);
580
581           /*
582            * the number we missed is the size of the window section
583            * minus the number we saw.
584            */
585           n_lost = inc - seen;
586         }
587       sa->replay_window = ((sa->replay_window) << inc) | 1;
588     }
589   else
590     {
591       /* holes in the replay window are lost packets */
592       n_lost = BITS (u64) - count_set_bits (sa->replay_window);
593
594       /* any sequence numbers that now fall outside the window
595        * are forever lost */
596       n_lost += inc - IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE;
597
598       sa->replay_window = 1;
599     }
600
601   return (n_lost);
602 }
603
604 /*
605  * Anti replay window advance
606  *  inputs need to be in host byte order.
607  * This function both advances the anti-replay window and the sequence number
608  * We always need to move on the SN but the window updates are only needed
609  * if AR is on.
610  * However, updating the window is trivial, so we do it anyway to save
611  * the branch cost.
612  */
613 always_inline u64
614 ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 thread_index, u32 seq,
615                               u32 hi_seq)
616 {
617   u64 n_lost = 0;
618   u32 pos;
619
620   if (ipsec_sa_is_set_USE_ESN (sa))
621     {
622       int wrap = hi_seq - sa->seq_hi;
623
624       if (wrap == 0 && seq > sa->seq)
625         {
626           pos = seq - sa->seq;
627           n_lost = ipsec_sa_anti_replay_window_shift (sa, pos);
628           sa->seq = seq;
629         }
630       else if (wrap > 0)
631         {
632           pos = ~seq + sa->seq + 1;
633           n_lost = ipsec_sa_anti_replay_window_shift (sa, pos);
634           sa->seq = seq;
635           sa->seq_hi = hi_seq;
636         }
637       else if (wrap < 0)
638         {
639           pos = ~seq + sa->seq + 1;
640           sa->replay_window |= (1ULL << pos);
641         }
642       else
643         {
644           pos = sa->seq - seq;
645           sa->replay_window |= (1ULL << pos);
646         }
647     }
648   else
649     {
650       if (seq > sa->seq)
651         {
652           pos = seq - sa->seq;
653           n_lost = ipsec_sa_anti_replay_window_shift (sa, pos);
654           sa->seq = seq;
655         }
656       else
657         {
658           pos = sa->seq - seq;
659           sa->replay_window |= (1ULL << pos);
660         }
661     }
662
663   return n_lost;
664 }
665
666
667 /*
668  * Makes choice for thread_id should be assigned.
669  *  if input ~0, gets random worker_id based on unix_time_now_nsec
670 */
671 always_inline u32
672 ipsec_sa_assign_thread (u32 thread_id)
673 {
674   return ((thread_id) ? thread_id
675           : (unix_time_now_nsec () % vlib_num_workers ()) + 1);
676 }
677
678 always_inline ipsec_sa_t *
679 ipsec_sa_get (u32 sa_index)
680 {
681   return (pool_elt_at_index (ipsec_sa_pool, sa_index));
682 }
683
684 #endif /* __IPSEC_SPD_SA_H__ */
685
686 /*
687  * fd.io coding-style-patch-verification: ON
688  *
689  * Local Variables:
690  * eval: (c-set-style "gnu")
691  * End:
692  */