Line data Source code
1 : /*
2 : * ah_decrypt.c : IPSec AH decrypt node
3 : *
4 : * Copyright (c) 2015 Cisco and/or its affiliates.
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at:
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #include <vnet/vnet.h>
19 : #include <vnet/api_errno.h>
20 : #include <vnet/ip/ip.h>
21 :
22 : #include <vnet/ipsec/ipsec.h>
23 : #include <vnet/ipsec/esp.h>
24 : #include <vnet/ipsec/ah.h>
25 : #include <vnet/ipsec/ipsec_io.h>
26 :
27 : #define foreach_ah_decrypt_next \
28 : _(DROP, "error-drop") \
29 : _(IP4_INPUT, "ip4-input") \
30 : _(IP6_INPUT, "ip6-input") \
31 : _(HANDOFF, "handoff")
32 :
33 : #define _(v, s) AH_DECRYPT_NEXT_##v,
34 : typedef enum
35 : {
36 : foreach_ah_decrypt_next
37 : #undef _
38 : AH_DECRYPT_N_NEXT,
39 : } ah_decrypt_next_t;
40 :
41 : typedef struct
42 : {
43 : ipsec_integ_alg_t integ_alg;
44 : u32 seq_num;
45 : } ah_decrypt_trace_t;
46 :
47 : /* packet trace format function */
48 : static u8 *
49 3208 : format_ah_decrypt_trace (u8 * s, va_list * args)
50 : {
51 3208 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
52 3208 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
53 3208 : ah_decrypt_trace_t *t = va_arg (*args, ah_decrypt_trace_t *);
54 :
55 3208 : s = format (s, "ah: integrity %U seq-num %d",
56 3208 : format_ipsec_integ_alg, t->integ_alg, t->seq_num);
57 3208 : return s;
58 : }
59 :
60 : typedef struct
61 : {
62 : union
63 : {
64 : struct
65 : {
66 : u8 hop_limit;
67 : u8 nexthdr;
68 : u32 ip_version_traffic_class_and_flow_label;
69 : };
70 :
71 : struct
72 : {
73 : u8 ttl;
74 : u8 tos;
75 : };
76 : };
77 : u32 sa_index;
78 : u32 seq;
79 : u32 seq_hi;
80 : u8 icv_padding_len;
81 : u8 icv_size;
82 : u8 ip_hdr_size;
83 : i16 current_data;
84 : u8 nexthdr_cached;
85 : } ah_decrypt_packet_data_t;
86 :
87 : static_always_inline void
88 156 : ah_process_ops (vlib_main_t * vm, vlib_node_runtime_t * node,
89 : vnet_crypto_op_t * ops, vlib_buffer_t * b[], u16 * nexts)
90 : {
91 156 : u32 n_fail, n_ops = vec_len (ops);
92 156 : vnet_crypto_op_t *op = ops;
93 :
94 156 : if (n_ops == 0)
95 7 : return;
96 :
97 149 : n_fail = n_ops - vnet_crypto_process_ops (vm, op, n_ops);
98 :
99 166 : while (n_fail)
100 : {
101 17 : ASSERT (op - ops < n_ops);
102 :
103 17 : if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED)
104 : {
105 17 : u32 bi = op->user_data;
106 17 : ah_decrypt_set_next_index (
107 17 : b[bi], node, vm->thread_index, AH_DECRYPT_ERROR_INTEG_ERROR, bi,
108 17 : nexts, AH_DECRYPT_NEXT_DROP, vnet_buffer (b[bi])->ipsec.sad_index);
109 17 : n_fail--;
110 : }
111 17 : op++;
112 : }
113 : }
114 :
115 : always_inline uword
116 156 : ah_decrypt_inline (vlib_main_t * vm,
117 : vlib_node_runtime_t * node, vlib_frame_t * from_frame,
118 : int is_ip6)
119 : {
120 : u32 n_left, *from;
121 156 : u32 thread_index = vm->thread_index;
122 156 : u16 buffer_data_size = vlib_buffer_get_default_data_size (vm);
123 156 : ah_decrypt_packet_data_t pkt_data[VLIB_FRAME_SIZE], *pd = pkt_data;
124 156 : vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
125 156 : u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
126 156 : ipsec_main_t *im = &ipsec_main;
127 156 : ipsec_per_thread_data_t *ptd = vec_elt_at_index (im->ptd, thread_index);
128 156 : from = vlib_frame_vector_args (from_frame);
129 156 : n_left = from_frame->n_vectors;
130 156 : ipsec_sa_t *sa0 = 0;
131 156 : u32 current_sa_index = ~0, current_sa_bytes = 0, current_sa_pkts = 0;
132 :
133 156 : clib_memset (pkt_data, 0, VLIB_FRAME_SIZE * sizeof (pkt_data[0]));
134 156 : vlib_get_buffers (vm, from, b, n_left);
135 156 : clib_memset_u16 (nexts, -1, n_left);
136 156 : vec_reset_length (ptd->integ_ops);
137 :
138 4136 : while (n_left > 0)
139 : {
140 : ah_header_t *ah0;
141 : ip4_header_t *ih4;
142 : ip6_header_t *ih6;
143 :
144 3980 : if (vnet_buffer (b[0])->ipsec.sad_index != current_sa_index)
145 : {
146 156 : if (current_sa_index != ~0)
147 0 : vlib_increment_combined_counter (&ipsec_sa_counters, thread_index,
148 : current_sa_index, current_sa_pkts,
149 : current_sa_bytes);
150 156 : current_sa_index = vnet_buffer (b[0])->ipsec.sad_index;
151 156 : sa0 = ipsec_sa_get (current_sa_index);
152 :
153 156 : current_sa_bytes = current_sa_pkts = 0;
154 156 : vlib_prefetch_combined_counter (&ipsec_sa_counters,
155 : thread_index, current_sa_index);
156 : }
157 :
158 3980 : if (PREDICT_FALSE ((u16) ~0 == sa0->thread_index))
159 : {
160 : /* this is the first packet to use this SA, claim the SA
161 : * for this thread. this could happen simultaneously on
162 : * another thread */
163 2 : clib_atomic_cmp_and_swap (&sa0->thread_index, ~0,
164 : ipsec_sa_assign_thread (thread_index));
165 : }
166 :
167 3980 : if (PREDICT_TRUE (thread_index != sa0->thread_index))
168 : {
169 60 : vnet_buffer (b[0])->ipsec.thread_index = sa0->thread_index;
170 60 : next[0] = AH_DECRYPT_NEXT_HANDOFF;
171 60 : goto next;
172 : }
173 :
174 3920 : pd->sa_index = current_sa_index;
175 :
176 3920 : ih4 = vlib_buffer_get_current (b[0]);
177 3920 : ih6 = vlib_buffer_get_current (b[0]);
178 3920 : pd->current_data = b[0]->current_data;
179 :
180 3920 : if (is_ip6)
181 : {
182 1911 : ip6_ext_header_t *prev = NULL;
183 : ah0 =
184 1911 : ip6_ext_header_find (vm, b[0], ih6, IP_PROTOCOL_IPSEC_AH, &prev);
185 1911 : pd->ip_hdr_size = sizeof (ip6_header_t);
186 1911 : ASSERT ((u8 *) ah0 - (u8 *) ih6 == pd->ip_hdr_size);
187 : }
188 : else
189 : {
190 2009 : if (ip4_is_fragment (ih4))
191 : {
192 0 : ah_decrypt_set_next_index (
193 : b[0], node, vm->thread_index, AH_DECRYPT_ERROR_DROP_FRAGMENTS,
194 : 0, next, AH_DECRYPT_NEXT_DROP, current_sa_index);
195 0 : goto next;
196 : }
197 2009 : pd->ip_hdr_size = ip4_header_bytes (ih4);
198 2009 : ah0 = (ah_header_t *) ((u8 *) ih4 + pd->ip_hdr_size);
199 : }
200 :
201 3920 : pd->seq = clib_host_to_net_u32 (ah0->seq_no);
202 :
203 : /* anti-replay check */
204 3920 : if (ipsec_sa_anti_replay_and_sn_advance (sa0, pd->seq, ~0, false,
205 : &pd->seq_hi))
206 : {
207 53 : ah_decrypt_set_next_index (b[0], node, vm->thread_index,
208 : AH_DECRYPT_ERROR_REPLAY, 0, next,
209 : AH_DECRYPT_NEXT_DROP, current_sa_index);
210 53 : goto next;
211 : }
212 :
213 3867 : current_sa_bytes += b[0]->current_length;
214 3867 : current_sa_pkts += 1;
215 :
216 3867 : pd->icv_size = sa0->integ_icv_size;
217 3867 : pd->nexthdr_cached = ah0->nexthdr;
218 3867 : if (PREDICT_TRUE (sa0->integ_alg != IPSEC_INTEG_ALG_NONE))
219 : {
220 3867 : if (PREDICT_FALSE (ipsec_sa_is_set_USE_ESN (sa0) &&
221 : pd->current_data + b[0]->current_length
222 : + sizeof (u32) > buffer_data_size))
223 : {
224 0 : ah_decrypt_set_next_index (
225 : b[0], node, vm->thread_index, AH_DECRYPT_ERROR_NO_TAIL_SPACE,
226 : 0, next, AH_DECRYPT_NEXT_DROP, current_sa_index);
227 0 : goto next;
228 : }
229 :
230 : vnet_crypto_op_t *op;
231 3867 : vec_add2_aligned (ptd->integ_ops, op, 1, CLIB_CACHE_LINE_BYTES);
232 3867 : vnet_crypto_op_init (op, sa0->integ_op_id);
233 :
234 3867 : op->src = (u8 *) ih4;
235 3867 : op->len = b[0]->current_length;
236 3867 : op->digest = (u8 *) ih4 - pd->icv_size;
237 3867 : op->flags = VNET_CRYPTO_OP_FLAG_HMAC_CHECK;
238 3867 : op->digest_len = pd->icv_size;
239 3867 : op->key_index = sa0->integ_key_index;
240 3867 : op->user_data = b - bufs;
241 3867 : if (ipsec_sa_is_set_USE_ESN (sa0))
242 : {
243 816 : u32 seq_hi = clib_host_to_net_u32 (pd->seq_hi);
244 :
245 816 : op->len += sizeof (seq_hi);
246 816 : clib_memcpy (op->src + b[0]->current_length, &seq_hi,
247 : sizeof (seq_hi));
248 : }
249 3867 : clib_memcpy (op->digest, ah0->auth_data, pd->icv_size);
250 3867 : clib_memset (ah0->auth_data, 0, pd->icv_size);
251 :
252 3867 : if (is_ip6)
253 : {
254 1911 : pd->ip_version_traffic_class_and_flow_label =
255 1911 : ih6->ip_version_traffic_class_and_flow_label;
256 1911 : pd->hop_limit = ih6->hop_limit;
257 1911 : ih6->ip_version_traffic_class_and_flow_label = 0x60;
258 1911 : ih6->hop_limit = 0;
259 1911 : pd->nexthdr = ah0->nexthdr;
260 1911 : pd->icv_padding_len =
261 1911 : ah_calc_icv_padding_len (pd->icv_size, 1 /* is_ipv6 */ );
262 : }
263 : else
264 : {
265 1956 : pd->tos = ih4->tos;
266 1956 : pd->ttl = ih4->ttl;
267 1956 : ih4->tos = 0;
268 1956 : ih4->ttl = 0;
269 1956 : ih4->checksum = 0;
270 1956 : pd->icv_padding_len =
271 1956 : ah_calc_icv_padding_len (pd->icv_size, 0 /* is_ipv6 */ );
272 : }
273 : }
274 :
275 0 : next:
276 3980 : n_left -= 1;
277 3980 : pd += 1;
278 3980 : next += 1;
279 3980 : b += 1;
280 : }
281 :
282 156 : n_left = from_frame->n_vectors;
283 156 : next = nexts;
284 156 : pd = pkt_data;
285 156 : b = bufs;
286 :
287 156 : vlib_node_increment_counter (vm, node->node_index, AH_DECRYPT_ERROR_RX_PKTS,
288 : n_left);
289 156 : vlib_increment_combined_counter (&ipsec_sa_counters, thread_index,
290 : current_sa_index, current_sa_pkts,
291 : current_sa_bytes);
292 :
293 156 : ah_process_ops (vm, node, ptd->integ_ops, bufs, nexts);
294 :
295 4136 : while (n_left > 0)
296 : {
297 : ip4_header_t *oh4;
298 : ip6_header_t *oh6;
299 3980 : u64 n_lost = 0;
300 :
301 3980 : if (next[0] < AH_DECRYPT_N_NEXT)
302 130 : goto trace;
303 :
304 3850 : sa0 = ipsec_sa_get (pd->sa_index);
305 :
306 3850 : if (PREDICT_TRUE (sa0->integ_alg != IPSEC_INTEG_ALG_NONE))
307 : {
308 : /* redo the anti-reply check. see esp_decrypt for details */
309 3850 : if (ipsec_sa_anti_replay_and_sn_advance (sa0, pd->seq, pd->seq_hi,
310 : true, NULL))
311 : {
312 7 : ah_decrypt_set_next_index (b[0], node, vm->thread_index,
313 : AH_DECRYPT_ERROR_REPLAY, 0, next,
314 : AH_DECRYPT_NEXT_DROP, pd->sa_index);
315 7 : goto trace;
316 : }
317 3843 : n_lost = ipsec_sa_anti_replay_advance (sa0, thread_index, pd->seq,
318 : pd->seq_hi);
319 3843 : vlib_prefetch_simple_counter (
320 : &ipsec_sa_err_counters[IPSEC_SA_ERROR_LOST], thread_index,
321 : pd->sa_index);
322 : }
323 :
324 3843 : u16 ah_hdr_len = sizeof (ah_header_t) + pd->icv_size
325 3843 : + pd->icv_padding_len;
326 3843 : vlib_buffer_advance (b[0], pd->ip_hdr_size + ah_hdr_len);
327 3843 : b[0]->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
328 3843 : b[0]->flags &= ~(VNET_BUFFER_F_L4_CHECKSUM_COMPUTED |
329 : VNET_BUFFER_F_L4_CHECKSUM_CORRECT);
330 :
331 3843 : if (PREDICT_TRUE (ipsec_sa_is_set_IS_TUNNEL (sa0)))
332 : { /* tunnel mode */
333 2104 : if (PREDICT_TRUE (pd->nexthdr_cached == IP_PROTOCOL_IP_IN_IP))
334 859 : next[0] = AH_DECRYPT_NEXT_IP4_INPUT;
335 1245 : else if (pd->nexthdr_cached == IP_PROTOCOL_IPV6)
336 1245 : next[0] = AH_DECRYPT_NEXT_IP6_INPUT;
337 : else
338 : {
339 0 : ah_decrypt_set_next_index (b[0], node, vm->thread_index,
340 : AH_DECRYPT_ERROR_DECRYPTION_FAILED, 0,
341 : next, AH_DECRYPT_NEXT_DROP,
342 : pd->sa_index);
343 0 : goto trace;
344 : }
345 : }
346 : else
347 : { /* transport mode */
348 1739 : if (is_ip6)
349 : {
350 666 : vlib_buffer_advance (b[0], -sizeof (ip6_header_t));
351 666 : oh6 = vlib_buffer_get_current (b[0]);
352 666 : if (ah_hdr_len >= sizeof (ip6_header_t))
353 204 : clib_memcpy (oh6, b[0]->data + pd->current_data,
354 : sizeof (ip6_header_t));
355 : else
356 462 : memmove (oh6, b[0]->data + pd->current_data,
357 : sizeof (ip6_header_t));
358 :
359 666 : next[0] = AH_DECRYPT_NEXT_IP6_INPUT;
360 666 : oh6->protocol = pd->nexthdr;
361 666 : oh6->hop_limit = pd->hop_limit;
362 666 : oh6->ip_version_traffic_class_and_flow_label =
363 666 : pd->ip_version_traffic_class_and_flow_label;
364 666 : oh6->payload_length =
365 666 : clib_host_to_net_u16 (vlib_buffer_length_in_chain
366 666 : (vm, b[0]) - sizeof (ip6_header_t));
367 : }
368 : else
369 : {
370 1073 : vlib_buffer_advance (b[0], -sizeof (ip4_header_t));
371 1073 : oh4 = vlib_buffer_get_current (b[0]);
372 1073 : if (ah_hdr_len >= sizeof (ip4_header_t))
373 1073 : clib_memcpy (oh4, b[0]->data + pd->current_data,
374 : sizeof (ip4_header_t));
375 : else
376 0 : memmove (oh4, b[0]->data + pd->current_data,
377 : sizeof (ip4_header_t));
378 :
379 1073 : next[0] = AH_DECRYPT_NEXT_IP4_INPUT;
380 1073 : oh4->ip_version_and_header_length = 0x45;
381 1073 : oh4->fragment_id = 0;
382 1073 : oh4->flags_and_fragment_offset = 0;
383 1073 : oh4->protocol = pd->nexthdr_cached;
384 1073 : oh4->length =
385 1073 : clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b[0]));
386 1073 : oh4->ttl = pd->ttl;
387 1073 : oh4->tos = pd->tos;
388 1073 : oh4->checksum = ip4_header_checksum (oh4);
389 : }
390 : }
391 :
392 3843 : if (PREDICT_FALSE (n_lost))
393 137 : vlib_increment_simple_counter (
394 : &ipsec_sa_err_counters[IPSEC_SA_ERROR_LOST], thread_index,
395 : pd->sa_index, n_lost);
396 :
397 3843 : vnet_buffer (b[0])->sw_if_index[VLIB_TX] = (u32) ~ 0;
398 3980 : trace:
399 3980 : if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
400 : {
401 3980 : sa0 = ipsec_sa_get (vnet_buffer (b[0])->ipsec.sad_index);
402 : ah_decrypt_trace_t *tr =
403 3980 : vlib_add_trace (vm, node, b[0], sizeof (*tr));
404 3980 : tr->integ_alg = sa0->integ_alg;
405 3980 : tr->seq_num = pd->seq;
406 : }
407 :
408 3980 : n_left -= 1;
409 3980 : pd += 1;
410 3980 : next += 1;
411 3980 : b += 1;
412 : }
413 :
414 156 : n_left = from_frame->n_vectors;
415 156 : vlib_buffer_enqueue_to_next (vm, node, from, nexts, n_left);
416 :
417 156 : return n_left;
418 : }
419 :
420 2387 : VLIB_NODE_FN (ah4_decrypt_node) (vlib_main_t * vm,
421 : vlib_node_runtime_t * node,
422 : vlib_frame_t * from_frame)
423 : {
424 87 : return ah_decrypt_inline (vm, node, from_frame, 0 /* is_ip6 */ );
425 : }
426 :
427 : /* *INDENT-OFF* */
428 183788 : VLIB_REGISTER_NODE (ah4_decrypt_node) = {
429 : .name = "ah4-decrypt",
430 : .vector_size = sizeof (u32),
431 : .format_trace = format_ah_decrypt_trace,
432 : .type = VLIB_NODE_TYPE_INTERNAL,
433 :
434 : .n_errors = AH_DECRYPT_N_ERROR,
435 : .error_counters = ah_decrypt_error_counters,
436 :
437 : .n_next_nodes = AH_DECRYPT_N_NEXT,
438 : .next_nodes = {
439 : [AH_DECRYPT_NEXT_DROP] = "ip4-drop",
440 : [AH_DECRYPT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
441 : [AH_DECRYPT_NEXT_IP6_INPUT] = "ip6-input",
442 : [AH_DECRYPT_NEXT_HANDOFF] = "ah4-decrypt-handoff",
443 : },
444 : };
445 : /* *INDENT-ON* */
446 :
447 2369 : VLIB_NODE_FN (ah6_decrypt_node) (vlib_main_t * vm,
448 : vlib_node_runtime_t * node,
449 : vlib_frame_t * from_frame)
450 : {
451 69 : return ah_decrypt_inline (vm, node, from_frame, 1 /* is_ip6 */ );
452 : }
453 :
454 : /* *INDENT-OFF* */
455 183788 : VLIB_REGISTER_NODE (ah6_decrypt_node) = {
456 : .name = "ah6-decrypt",
457 : .vector_size = sizeof (u32),
458 : .format_trace = format_ah_decrypt_trace,
459 : .type = VLIB_NODE_TYPE_INTERNAL,
460 :
461 : .n_errors = AH_DECRYPT_N_ERROR,
462 : .error_counters = ah_decrypt_error_counters,
463 :
464 : .n_next_nodes = AH_DECRYPT_N_NEXT,
465 : .next_nodes = {
466 : [AH_DECRYPT_NEXT_DROP] = "ip6-drop",
467 : [AH_DECRYPT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
468 : [AH_DECRYPT_NEXT_IP6_INPUT] = "ip6-input",
469 : [AH_DECRYPT_NEXT_HANDOFF] = "ah6-decrypt-handoff",
470 : },
471 : };
472 : /* *INDENT-ON* */
473 :
474 : #ifndef CLIB_MARCH_VARIANT
475 :
476 : static clib_error_t *
477 575 : ah_decrypt_init (vlib_main_t *vm)
478 : {
479 575 : ipsec_main_t *im = &ipsec_main;
480 :
481 575 : im->ah4_dec_fq_index =
482 575 : vlib_frame_queue_main_init (ah4_decrypt_node.index, 0);
483 575 : im->ah6_dec_fq_index =
484 575 : vlib_frame_queue_main_init (ah6_decrypt_node.index, 0);
485 :
486 575 : return 0;
487 : }
488 :
489 55871 : VLIB_INIT_FUNCTION (ah_decrypt_init);
490 :
491 : #endif
492 :
493 : /*
494 : * fd.io coding-style-patch-verification: ON
495 : *
496 : * Local Variables:
497 : * eval: (c-set-style "gnu")
498 : * End:
499 : */
|