IPSEC tests fnd fix or Extended Sequence Numbers
[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
60 u8 *format_esp_header (u8 * s, va_list * args);
61
62 always_inline int
63 esp_replay_check (ipsec_sa_t * sa, u32 seq)
64 {
65   u32 diff;
66
67   if (PREDICT_TRUE (seq > sa->last_seq))
68     return 0;
69
70   diff = sa->last_seq - seq;
71
72   if (ESP_WINDOW_SIZE > diff)
73     return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
74   else
75     return 1;
76
77   return 0;
78 }
79
80 always_inline int
81 esp_replay_check_esn (ipsec_sa_t * sa, u32 seq)
82 {
83   u32 tl = sa->last_seq;
84   u32 th = sa->last_seq_hi;
85   u32 diff = tl - seq;
86
87   if (PREDICT_TRUE (tl >= (ESP_WINDOW_SIZE - 1)))
88     {
89       if (seq >= (tl - ESP_WINDOW_SIZE + 1))
90         {
91           sa->seq_hi = th;
92           if (seq <= tl)
93             return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
94           else
95             return 0;
96         }
97       else
98         {
99           sa->seq_hi = th + 1;
100           return 0;
101         }
102     }
103   else
104     {
105       if (seq >= (tl - ESP_WINDOW_SIZE + 1))
106         {
107           sa->seq_hi = th - 1;
108           return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
109         }
110       else
111         {
112           sa->seq_hi = th;
113           if (seq <= tl)
114             return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
115           else
116             return 0;
117         }
118     }
119
120   return 0;
121 }
122
123 /* TODO seq increment should be atomic to be accessed by multiple workers */
124 always_inline void
125 esp_replay_advance (ipsec_sa_t * sa, u32 seq)
126 {
127   u32 pos;
128
129   if (seq > sa->last_seq)
130     {
131       pos = seq - sa->last_seq;
132       if (pos < ESP_WINDOW_SIZE)
133         sa->replay_window = ((sa->replay_window) << pos) | 1;
134       else
135         sa->replay_window = 1;
136       sa->last_seq = seq;
137     }
138   else
139     {
140       pos = sa->last_seq - seq;
141       sa->replay_window |= (1ULL << pos);
142     }
143 }
144
145 always_inline void
146 esp_replay_advance_esn (ipsec_sa_t * sa, u32 seq)
147 {
148   int wrap = sa->seq_hi - sa->last_seq_hi;
149   u32 pos;
150
151   if (wrap == 0 && seq > sa->last_seq)
152     {
153       pos = seq - sa->last_seq;
154       if (pos < ESP_WINDOW_SIZE)
155         sa->replay_window = ((sa->replay_window) << pos) | 1;
156       else
157         sa->replay_window = 1;
158       sa->last_seq = seq;
159     }
160   else if (wrap > 0)
161     {
162       pos = ~seq + sa->last_seq + 1;
163       if (pos < ESP_WINDOW_SIZE)
164         sa->replay_window = ((sa->replay_window) << pos) | 1;
165       else
166         sa->replay_window = 1;
167       sa->last_seq = seq;
168       sa->last_seq_hi = sa->seq_hi;
169     }
170   else if (wrap < 0)
171     {
172       pos = ~seq + sa->last_seq + 1;
173       sa->replay_window |= (1ULL << pos);
174     }
175   else
176     {
177       pos = sa->last_seq - seq;
178       sa->replay_window |= (1ULL << pos);
179     }
180 }
181
182 always_inline int
183 esp_seq_advance (ipsec_sa_t * sa)
184 {
185   if (PREDICT_TRUE (sa->use_esn))
186     {
187       if (PREDICT_FALSE (sa->seq == ESP_SEQ_MAX))
188         {
189           if (PREDICT_FALSE
190               (sa->use_anti_replay && sa->seq_hi == ESP_SEQ_MAX))
191             return 1;
192           sa->seq_hi++;
193         }
194       sa->seq++;
195     }
196   else
197     {
198       if (PREDICT_FALSE (sa->use_anti_replay && sa->seq == ESP_SEQ_MAX))
199         return 1;
200       sa->seq++;
201     }
202
203   return 0;
204 }
205
206
207 always_inline unsigned int
208 hmac_calc (vlib_main_t * vm, ipsec_sa_t * sa, u8 * data, int data_len,
209            u8 * signature)
210 {
211   vnet_crypto_op_t _op, *op = &_op;
212
213   if (PREDICT_FALSE (sa->integ_op_type == 0))
214     return 0;
215
216   op->op = sa->integ_op_type;
217   op->key = sa->integ_key.data;
218   op->key_len = sa->integ_key.len;
219   op->src = data;
220   op->len = data_len;
221   op->dst = signature;
222   op->hmac_trunc_len = sa->integ_trunc_size;
223
224   if (sa->use_esn)
225     {
226       u32 seq_hi = clib_host_to_net_u32 (sa->seq_hi);
227
228       op->len += 4;
229       clib_memcpy (data + data_len, &seq_hi, 4);
230     }
231
232   vnet_crypto_process_ops (vm, op, 1);
233   return sa->integ_trunc_size;
234 }
235
236 #endif /* __ESP_H__ */
237
238 /*
239  * fd.io coding-style-patch-verification: ON
240  *
241  * Local Variables:
242  * eval: (c-set-style "gnu")
243  * End:
244  */