X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Fipsec%2Fipsec_sa.h;h=94f1554112f26e1eb5e80871e9db223273a3c3c9;hb=1e3aa5e213c23588981ee17d1413a0441a40527a;hp=43d699be928233efe6e34964e56c1d582c2c751a;hpb=999c8ee6d6f1c07ba7877fb3f9aa66a90774aacc;p=vpp.git diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h index 43d699be928..94f1554112f 100644 --- a/src/vnet/ipsec/ipsec_sa.h +++ b/src/vnet/ipsec/ipsec_sa.h @@ -17,6 +17,9 @@ #include #include +#include + +#define IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE (64) #define foreach_ipsec_crypto_alg \ _ (0, NONE, "none") \ @@ -63,55 +66,283 @@ typedef enum IPSEC_PROTOCOL_ESP = 1 } ipsec_protocol_t; +#define IPSEC_N_PROTOCOLS (IPSEC_PROTOCOL_ESP+1) + +#define IPSEC_KEY_MAX_LEN 128 +typedef struct ipsec_key_t_ +{ + u8 len; + u8 data[IPSEC_KEY_MAX_LEN]; +} ipsec_key_t; + +/* + * Enable extended sequence numbers + * Enable Anti-replay + * IPsec tunnel mode if non-zero, else transport mode + * IPsec tunnel mode is IPv6 if non-zero, + * else IPv4 tunnel only valid if is_tunnel is non-zero + * enable UDP encapsulation for NAT traversal + */ +#define foreach_ipsec_sa_flags \ + _ (0, NONE, "none") \ + _ (1, USE_ESN, "esn") \ + _ (2, USE_ANTI_REPLAY, "anti-replay") \ + _ (4, IS_TUNNEL, "tunnel") \ + _ (8, IS_TUNNEL_V6, "tunnel-v6") \ + _ (16, UDP_ENCAP, "udp-encap") \ + +typedef enum ipsec_sad_flags_t_ +{ +#define _(v, f, s) IPSEC_SA_FLAG_##f = v, + foreach_ipsec_sa_flags +#undef _ +} __clib_packed ipsec_sa_flags_t; + +STATIC_ASSERT (sizeof (ipsec_sa_flags_t) == 1, "IPSEC SA flags > 1 byte"); + typedef struct { - u32 id; + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + + /* flags */ + ipsec_sa_flags_t flags; + + u8 crypto_iv_size; + u8 crypto_block_size; + u8 integ_trunc_size; u32 spi; + u32 seq; + u32 seq_hi; + u32 last_seq; + u32 last_seq_hi; + u64 replay_window; + + vnet_crypto_op_type_t crypto_enc_op_type; + vnet_crypto_op_type_t crypto_dec_op_type; + vnet_crypto_op_type_t integ_op_type; + + dpo_id_t dpo[IPSEC_N_PROTOCOLS]; + + /* data accessed by dataplane code should be above this comment */ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline1); + + union + { + ip4_header_t ip4_hdr; + ip6_header_t ip6_hdr; + }; + udp_header_t udp_hdr; + + + fib_node_t node; + u32 id; + u32 stat_index; ipsec_protocol_t protocol; ipsec_crypto_alg_t crypto_alg; - u8 crypto_key_len; - u8 crypto_key[128]; + ipsec_key_t crypto_key; ipsec_integ_alg_t integ_alg; - u8 integ_key_len; - u8 integ_key[128]; + ipsec_key_t integ_key; - u8 use_esn; - u8 use_anti_replay; - - u8 is_tunnel; - u8 is_tunnel_ip6; - u8 udp_encap; ip46_address_t tunnel_src_addr; ip46_address_t tunnel_dst_addr; + fib_node_index_t fib_entry_index; + u32 sibling; + u32 tx_fib_index; u32 salt; /* runtime */ - u32 seq; - u32 seq_hi; - u32 last_seq; - u32 last_seq_hi; - u64 replay_window; - - /* lifetime data */ - u64 total_data_size; } ipsec_sa_t; -extern int ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, - int is_add); +STATIC_ASSERT_OFFSET_OF (ipsec_sa_t, cacheline1, CLIB_CACHE_LINE_BYTES); + +#define _(a,v,s) \ + always_inline int \ + ipsec_sa_is_set_##v (const ipsec_sa_t *sa) { \ + return (sa->flags & IPSEC_SA_FLAG_##v); \ + } +foreach_ipsec_sa_flags +#undef _ +#define _(a,v,s) \ + always_inline int \ + ipsec_sa_set_##v (ipsec_sa_t *sa) { \ + return (sa->flags |= IPSEC_SA_FLAG_##v); \ + } + foreach_ipsec_sa_flags +#undef _ +/** + * @brief + * SA packet & bytes counters + */ +extern vlib_combined_counter_main_t ipsec_sa_counters; + +extern void ipsec_mk_key (ipsec_key_t * key, const u8 * data, u8 len); + +extern int ipsec_sa_add (u32 id, + u32 spi, + ipsec_protocol_t proto, + ipsec_crypto_alg_t crypto_alg, + const ipsec_key_t * ck, + ipsec_integ_alg_t integ_alg, + const ipsec_key_t * ik, + ipsec_sa_flags_t flags, + u32 tx_table_id, + const ip46_address_t * tunnel_src_addr, + const ip46_address_t * tunnel_dst_addr, + u32 * sa_index); +extern u32 ipsec_sa_del (u32 id); +extern void ipsec_sa_stack (ipsec_sa_t * sa); +extern void ipsec_sa_set_crypto_alg (ipsec_sa_t * sa, + ipsec_crypto_alg_t crypto_alg); +extern void ipsec_sa_set_integ_alg (ipsec_sa_t * sa, + ipsec_integ_alg_t integ_alg); + extern u8 ipsec_is_sa_used (u32 sa_index); -extern int ipsec_set_sa_key (vlib_main_t * vm, ipsec_sa_t * sa_update); +extern int ipsec_set_sa_key (u32 id, + const ipsec_key_t * ck, const ipsec_key_t * ik); extern u32 ipsec_get_sa_index_by_sa_id (u32 sa_id); +typedef walk_rc_t (*ipsec_sa_walk_cb_t) (ipsec_sa_t * sa, void *ctx); +extern void ipsec_sa_walk (ipsec_sa_walk_cb_t cd, void *ctx); + extern u8 *format_ipsec_crypto_alg (u8 * s, va_list * args); extern u8 *format_ipsec_integ_alg (u8 * s, va_list * args); +extern u8 *format_ipsec_sa (u8 * s, va_list * args); +extern u8 *format_ipsec_key (u8 * s, va_list * args); extern uword unformat_ipsec_crypto_alg (unformat_input_t * input, va_list * args); extern uword unformat_ipsec_integ_alg (unformat_input_t * input, va_list * args); +extern uword unformat_ipsec_key (unformat_input_t * input, va_list * args); + +always_inline int +ipsec_sa_anti_replay_check (ipsec_sa_t * sa, u32 * seqp) +{ + u32 seq, diff, tl, th; + if ((sa->flags & IPSEC_SA_FLAG_USE_ANTI_REPLAY) == 0) + return 0; + + seq = clib_net_to_host_u32 (*seqp); + + if ((sa->flags & IPSEC_SA_FLAG_USE_ESN) == 0) + { + + if (PREDICT_TRUE (seq > sa->last_seq)) + return 0; + + diff = sa->last_seq - seq; + + if (IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE > diff) + return (sa->replay_window & (1ULL << diff)) ? 1 : 0; + else + return 1; + + return 0; + } + + tl = sa->last_seq; + th = sa->last_seq_hi; + diff = tl - seq; + + if (PREDICT_TRUE (tl >= (IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE - 1))) + { + if (seq >= (tl - IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE + 1)) + { + sa->seq_hi = th; + if (seq <= tl) + return (sa->replay_window & (1ULL << diff)) ? 1 : 0; + else + return 0; + } + else + { + sa->seq_hi = th + 1; + return 0; + } + } + else + { + if (seq >= (tl - IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE + 1)) + { + sa->seq_hi = th - 1; + return (sa->replay_window & (1ULL << diff)) ? 1 : 0; + } + else + { + sa->seq_hi = th; + if (seq <= tl) + return (sa->replay_window & (1ULL << diff)) ? 1 : 0; + else + return 0; + } + } + + return 0; +} + +always_inline void +ipsec_sa_anti_replay_advance (ipsec_sa_t * sa, u32 * seqp) +{ + u32 pos, seq; + if (PREDICT_TRUE (sa->flags & IPSEC_SA_FLAG_USE_ANTI_REPLAY) == 0) + return; + + seq = clib_host_to_net_u32 (*seqp); + if (PREDICT_TRUE (sa->flags & IPSEC_SA_FLAG_USE_ESN)) + { + int wrap = sa->seq_hi - sa->last_seq_hi; + + if (wrap == 0 && seq > sa->last_seq) + { + pos = seq - sa->last_seq; + if (pos < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE) + sa->replay_window = ((sa->replay_window) << pos) | 1; + else + sa->replay_window = 1; + sa->last_seq = seq; + } + else if (wrap > 0) + { + pos = ~seq + sa->last_seq + 1; + if (pos < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE) + sa->replay_window = ((sa->replay_window) << pos) | 1; + else + sa->replay_window = 1; + sa->last_seq = seq; + sa->last_seq_hi = sa->seq_hi; + } + else if (wrap < 0) + { + pos = ~seq + sa->last_seq + 1; + sa->replay_window |= (1ULL << pos); + } + else + { + pos = sa->last_seq - seq; + sa->replay_window |= (1ULL << pos); + } + } + else + { + if (seq > sa->last_seq) + { + pos = seq - sa->last_seq; + if (pos < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE) + sa->replay_window = ((sa->replay_window) << pos) | 1; + else + sa->replay_window = 1; + sa->last_seq = seq; + } + else + { + pos = sa->last_seq - seq; + sa->replay_window |= (1ULL << pos); + } + } +} #endif /* __IPSEC_SPD_SA_H__ */