063b74b7c5cdb73bd139e6c8c6b596e362d48c9e
[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 (sa->use_esn))
188     {
189       if (PREDICT_FALSE (sa->seq == ESP_SEQ_MAX))
190         {
191           if (PREDICT_FALSE
192               (sa->use_anti_replay && 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 (sa->use_anti_replay && sa->seq == ESP_SEQ_MAX))
201         return 1;
202       sa->seq++;
203     }
204
205   return 0;
206 }
207
208
209 always_inline unsigned int
210 hmac_calc (vlib_main_t * vm, ipsec_sa_t * sa, u8 * data, int data_len,
211            u8 * signature)
212 {
213   vnet_crypto_op_t _op, *op = &_op;
214
215   if (PREDICT_FALSE (sa->integ_op_type == 0))
216     return 0;
217
218   op->op = sa->integ_op_type;
219   op->key = sa->integ_key.data;
220   op->key_len = sa->integ_key.len;
221   op->src = data;
222   op->len = data_len;
223   op->dst = signature;
224   op->hmac_trunc_len = sa->integ_trunc_size;
225
226   if (sa->use_esn)
227     {
228       u32 seq_hi = clib_host_to_net_u32 (sa->seq_hi);
229
230       op->len += 4;
231       clib_memcpy (data + data_len, &seq_hi, 4);
232     }
233
234   vnet_crypto_process_ops (vm, op, 1);
235   return sa->integ_trunc_size;
236 }
237
238 #endif /* __ESP_H__ */
239
240 /*
241  * fd.io coding-style-patch-verification: ON
242  *
243  * Local Variables:
244  * eval: (c-set-style "gnu")
245  * End:
246  */