ipsec: compress ipsec_sa_t so data used by dataplane code fits in cacheline
[vpp.git] / src / vnet / ipsec / esp.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 __ESP_H__
16 #define __ESP_H__
17
18 #include <vnet/ip/ip.h>
19 #include <vnet/crypto/crypto.h>
20 #include <vnet/ipsec/ipsec.h>
21
22 typedef struct
23 {
24   u32 spi;
25   u32 seq;
26   u8 data[0];
27 } esp_header_t;
28
29 typedef struct
30 {
31   u8 pad_length;
32   u8 next_header;
33 } esp_footer_t;
34
35 /* *INDENT-OFF* */
36 typedef CLIB_PACKED (struct {
37   ip4_header_t ip4;
38   esp_header_t esp;
39 }) ip4_and_esp_header_t;
40 /* *INDENT-ON* */
41
42 /* *INDENT-OFF* */
43 typedef CLIB_PACKED (struct {
44   ip4_header_t ip4;
45   udp_header_t udp;
46   esp_header_t esp;
47 }) ip4_and_udp_and_esp_header_t;
48 /* *INDENT-ON* */
49
50 /* *INDENT-OFF* */
51 typedef CLIB_PACKED (struct {
52   ip6_header_t ip6;
53   esp_header_t esp;
54 }) ip6_and_esp_header_t;
55 /* *INDENT-ON* */
56
57 #define ESP_WINDOW_SIZE         (64)
58 #define ESP_SEQ_MAX             (4294967295UL)
59 #define ESP_MAX_BLOCK_SIZE      (16)
60 #define ESP_MAX_ICV_SIZE        (16)
61
62 u8 *format_esp_header (u8 * s, va_list * args);
63
64 always_inline int
65 esp_replay_check (ipsec_sa_t * sa, u32 seq)
66 {
67   u32 diff;
68
69   if (PREDICT_TRUE (seq > sa->last_seq))
70     return 0;
71
72   diff = sa->last_seq - seq;
73
74   if (ESP_WINDOW_SIZE > diff)
75     return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
76   else
77     return 1;
78
79   return 0;
80 }
81
82 always_inline int
83 esp_replay_check_esn (ipsec_sa_t * sa, u32 seq)
84 {
85   u32 tl = sa->last_seq;
86   u32 th = sa->last_seq_hi;
87   u32 diff = tl - seq;
88
89   if (PREDICT_TRUE (tl >= (ESP_WINDOW_SIZE - 1)))
90     {
91       if (seq >= (tl - ESP_WINDOW_SIZE + 1))
92         {
93           sa->seq_hi = th;
94           if (seq <= tl)
95             return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
96           else
97             return 0;
98         }
99       else
100         {
101           sa->seq_hi = th + 1;
102           return 0;
103         }
104     }
105   else
106     {
107       if (seq >= (tl - ESP_WINDOW_SIZE + 1))
108         {
109           sa->seq_hi = th - 1;
110           return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
111         }
112       else
113         {
114           sa->seq_hi = th;
115           if (seq <= tl)
116             return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
117           else
118             return 0;
119         }
120     }
121
122   return 0;
123 }
124
125 /* TODO seq increment should be atomic to be accessed by multiple workers */
126 always_inline void
127 esp_replay_advance (ipsec_sa_t * sa, u32 seq)
128 {
129   u32 pos;
130
131   if (seq > sa->last_seq)
132     {
133       pos = seq - sa->last_seq;
134       if (pos < ESP_WINDOW_SIZE)
135         sa->replay_window = ((sa->replay_window) << pos) | 1;
136       else
137         sa->replay_window = 1;
138       sa->last_seq = seq;
139     }
140   else
141     {
142       pos = sa->last_seq - seq;
143       sa->replay_window |= (1ULL << pos);
144     }
145 }
146
147 always_inline void
148 esp_replay_advance_esn (ipsec_sa_t * sa, u32 seq)
149 {
150   int wrap = sa->seq_hi - sa->last_seq_hi;
151   u32 pos;
152
153   if (wrap == 0 && seq > sa->last_seq)
154     {
155       pos = seq - sa->last_seq;
156       if (pos < ESP_WINDOW_SIZE)
157         sa->replay_window = ((sa->replay_window) << pos) | 1;
158       else
159         sa->replay_window = 1;
160       sa->last_seq = seq;
161     }
162   else if (wrap > 0)
163     {
164       pos = ~seq + sa->last_seq + 1;
165       if (pos < ESP_WINDOW_SIZE)
166         sa->replay_window = ((sa->replay_window) << pos) | 1;
167       else
168         sa->replay_window = 1;
169       sa->last_seq = seq;
170       sa->last_seq_hi = sa->seq_hi;
171     }
172   else if (wrap < 0)
173     {
174       pos = ~seq + sa->last_seq + 1;
175       sa->replay_window |= (1ULL << pos);
176     }
177   else
178     {
179       pos = sa->last_seq - seq;
180       sa->replay_window |= (1ULL << pos);
181     }
182 }
183
184 always_inline int
185 esp_seq_advance (ipsec_sa_t * sa)
186 {
187   if (PREDICT_TRUE (ipsec_sa_is_set_USE_EXTENDED_SEQ_NUM (sa)))
188     {
189       if (PREDICT_FALSE (sa->seq == ESP_SEQ_MAX))
190         {
191           if (PREDICT_FALSE (ipsec_sa_is_set_USE_ANTI_REPLAY (sa) &&
192                              sa->seq_hi == ESP_SEQ_MAX))
193             return 1;
194           sa->seq_hi++;
195         }
196       sa->seq++;
197     }
198   else
199     {
200       if (PREDICT_FALSE (ipsec_sa_is_set_USE_ANTI_REPLAY (sa) &&
201                          sa->seq == ESP_SEQ_MAX))
202         return 1;
203       sa->seq++;
204     }
205
206   return 0;
207 }
208
209
210 always_inline unsigned int
211 hmac_calc (vlib_main_t * vm, ipsec_sa_t * sa, u8 * data, int data_len,
212            u8 * signature)
213 {
214   vnet_crypto_op_t _op, *op = &_op;
215
216   if (PREDICT_FALSE (sa->integ_op_type == 0))
217     return 0;
218
219   op->op = sa->integ_op_type;
220   op->key = sa->integ_key.data;
221   op->key_len = sa->integ_key.len;
222   op->src = data;
223   op->len = data_len;
224   op->dst = signature;
225   op->hmac_trunc_len = sa->integ_trunc_size;
226
227   if (ipsec_sa_is_set_USE_EXTENDED_SEQ_NUM (sa))
228     {
229       u32 seq_hi = clib_host_to_net_u32 (sa->seq_hi);
230
231       op->len += 4;
232       clib_memcpy (data + data_len, &seq_hi, 4);
233     }
234
235   vnet_crypto_process_ops (vm, op, 1);
236   return sa->integ_trunc_size;
237 }
238
239 #endif /* __ESP_H__ */
240
241 /*
242  * fd.io coding-style-patch-verification: ON
243  *
244  * Local Variables:
245  * eval: (c-set-style "gnu")
246  * End:
247  */