e991671aa633334c9da5569b328aeb34a9d94981
[vpp.git] / src / plugins / ipsecmb / ah_decrypt.c
1 /*
2  * ah_decrypt.c : ipsecmb 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
26 #include <ipsecmb/ipsecmb.h>
27
28 #define foreach_ah_decrypt_next \
29   _ (DROP, "error-drop")        \
30   _ (IP4_INPUT, "ip4-input")    \
31   _ (IP6_INPUT, "ip6-input")    \
32   _ (IPSEC_GRE_INPUT, "ipsec-gre-input")
33
34 #define _(v, s) AH_DECRYPT_NEXT_##v,
35 typedef enum
36 {
37   foreach_ah_decrypt_next
38 #undef _
39     AH_DECRYPT_N_NEXT,
40 } ah_decrypt_next_t;
41
42 #define foreach_ah_decrypt_error                \
43   _ (RX_PKTS, "AH pkts received")               \
44   _ (DECRYPTION_FAILED, "AH decryption failed") \
45   _ (INTEG_ERROR, "Integrity check failed")     \
46   _ (REPLAY, "SA replayed packet")              \
47   _ (NOT_IP, "Not IP packet (dropped)")
48
49 typedef enum
50 {
51 #define _(sym, str) AH_DECRYPT_ERROR_##sym,
52   foreach_ah_decrypt_error
53 #undef _
54     AH_DECRYPT_N_ERROR,
55 } ah_decrypt_error_t;
56
57 static char *ah_decrypt_error_strings[] = {
58 #define _(sym, string) string,
59   foreach_ah_decrypt_error
60 #undef _
61 };
62
63 typedef struct
64 {
65   ipsec_integ_alg_t integ_alg;
66 } ah_decrypt_trace_t;
67
68 /* packet trace format function */
69 static u8 *
70 format_ah_decrypt_trace (u8 * s, va_list * args)
71 {
72   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
73   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
74   ah_decrypt_trace_t *t = va_arg (*args, ah_decrypt_trace_t *);
75
76   s = format (s, "ah: integrity %U", format_ipsec_integ_alg, t->integ_alg);
77   return s;
78 }
79
80 typedef struct
81 {
82   u8 tos;
83   u8 ttl;
84   u32 ip_version_traffic_class_and_flow_label;
85   u8 hop_limit;
86 } ip_mutable_data_t;
87
88 #ifdef CLIB_MARCH_VARIANT
89 always_inline void
90 remove_ah (vlib_main_t * vm, vlib_node_runtime_t * node, u32 * bi0,
91            u32 * next0, ipsec_sa_t * sa0, u32 ip_hdr_size, u32 icv_size,
92            u8 icv_padding_len, ah_header_t * ah0, int is_ip6)
93 {
94   vlib_buffer_t *b0 = vlib_get_buffer (vm, *bi0);
95
96   if (sa0->is_tunnel)
97     {                           /* tunnel mode */
98       vlib_buffer_advance (b0, ip_hdr_size + sizeof (ah_header_t) + icv_size +
99                            icv_padding_len);
100       if (ah0->nexthdr == IP_PROTOCOL_IP_IN_IP)
101         *next0 = AH_DECRYPT_NEXT_IP4_INPUT;
102       else if (ah0->nexthdr == IP_PROTOCOL_IPV6)
103         *next0 = AH_DECRYPT_NEXT_IP6_INPUT;
104       else
105         {
106           clib_warning ("next header: 0x%x", ah0->nexthdr);
107           vlib_node_increment_counter (vm, node->node_index,
108                                        AH_DECRYPT_ERROR_DECRYPTION_FAILED, 1);
109           *next0 = AH_DECRYPT_NEXT_DROP;
110           return;
111         }
112     }
113   else
114     {                           /* transport mode */
115       const size_t ip_hdr_offset =
116         sizeof (ah_header_t) + icv_size + icv_padding_len;
117       if (is_ip6)
118         {                       /* ipv6 */
119           ip6_header_t *ih6 =
120             (ip6_header_t *) ((u8 *) vlib_buffer_get_current (b0) +
121                               ip_hdr_offset);
122           u8 nexthdr = ah0->nexthdr;
123           memmove (ih6, vlib_buffer_get_current (b0), sizeof (ip6_header_t));
124           vlib_buffer_advance (b0, ip_hdr_offset);
125
126           *next0 = AH_DECRYPT_NEXT_IP6_INPUT;
127           ih6->protocol = nexthdr;
128           ih6->payload_length =
129             clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) -
130                                   sizeof (ip6_header_t));
131         }
132       else
133         {                       /* ipv4 */
134           ip4_header_t *ih4 =
135             (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) +
136                               ip_hdr_offset);
137           u8 nexthdr = ah0->nexthdr;
138           memmove (ih4, vlib_buffer_get_current (b0), sizeof (ip4_header_t));
139           vlib_buffer_advance (b0, ip_hdr_offset);
140
141           *next0 = AH_DECRYPT_NEXT_IP4_INPUT;
142           ih4->ip_version_and_header_length = 0x45;
143           ih4->fragment_id = 0;
144           ih4->flags_and_fragment_offset = 0;
145           ih4->protocol = nexthdr;
146           ih4->length =
147             clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
148           ih4->checksum = ip4_header_checksum (ih4);
149         }
150     }
151
152   /* for IPSec-GRE tunnel next node is ipsec-gre-input */
153   if (PREDICT_FALSE
154       ((vnet_buffer (b0)->ipsec.flags & IPSEC_FLAG_IPSEC_GRE_TUNNEL)))
155     {
156       *next0 = AH_DECRYPT_NEXT_IPSEC_GRE_INPUT;
157     }
158 }
159
160 always_inline void
161 ah_finish_decrypt (vlib_main_t * vm, vlib_node_runtime_t * node,
162                    JOB_AES_HMAC * job, u32 * bi0, u32 * next0, int is_ip6)
163 {
164   ipsec_main_t *im = &ipsec_main;
165   *bi0 = (uintptr_t) job->user_data;
166   vlib_buffer_t *b0 = vlib_get_buffer (vm, *bi0);
167   ipsec_sa_t *sa0 =
168     pool_elt_at_index (im->sad, vnet_buffer (b0)->ipsec.sad_index);
169   ipsec_proto_main_t *em = &ipsec_proto_main;
170   u32 icv_size = em->ipsec_proto_main_integ_algs[sa0->integ_alg].trunc_size;
171   u32 ip_hdr_size = 0;
172   ip4_header_t *ih4 = vlib_buffer_get_current (b0);
173   size_t seq_size = 0;
174   if (PREDICT_TRUE (sa0->use_esn))
175     {
176       seq_size = sizeof (u32);
177     }
178   ip_mutable_data_t *md =
179     (ip_mutable_data_t *) ((u8 *) vlib_buffer_get_current (b0) +
180                            b0->current_length + seq_size + icv_size);
181   if (is_ip6)
182     {
183       ip_hdr_size = sizeof (ip6_header_t);
184       ip6_header_t *ih6 = vlib_buffer_get_current (b0);
185       ih6->ip_version_traffic_class_and_flow_label =
186         md->ip_version_traffic_class_and_flow_label;
187       ih6->hop_limit = md->hop_limit;
188     }
189   else
190     {
191       ip_hdr_size = ip4_header_bytes (ih4);
192       ih4->ttl = md->ttl;
193       ih4->tos = md->tos;
194     }
195
196   u8 icv_padding_len = ah_calc_icv_padding_len (icv_size, is_ip6);
197   ah_header_t *ah0 =
198     (ah_header_t *) ((u8 *) vlib_buffer_get_current (b0) + ip_hdr_size);
199   void *digest = ah0 + 1;
200   void *sig = vlib_buffer_get_current (b0) + b0->current_length + seq_size;
201
202   if (PREDICT_FALSE (memcmp (digest, sig, icv_size)))
203     {
204       vlib_node_increment_counter (vm, node->node_index,
205                                    AH_DECRYPT_ERROR_INTEG_ERROR, 1);
206       *next0 = AH_DECRYPT_NEXT_DROP;
207       return;
208     }
209
210   if (PREDICT_TRUE (sa0->use_anti_replay))
211     {
212       if (PREDICT_TRUE (sa0->use_esn))
213         esp_replay_advance_esn (sa0, clib_host_to_net_u32 (ah0->seq_no));
214       else
215         esp_replay_advance (sa0, clib_host_to_net_u32 (ah0->seq_no));
216     }
217   remove_ah (vm, node, bi0, next0, sa0, ip_hdr_size, icv_size,
218              icv_padding_len, ah0, is_ip6);
219   vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
220 }
221
222 always_inline uword
223 ah_decrypt_ipsecmb_inline (vlib_main_t * vm,
224                            vlib_node_runtime_t * node,
225                            vlib_frame_t * from_frame, int is_ip6)
226 {
227   u32 n_left_from, *from, next_index, *to_next;
228   ipsec_main_t *im = &ipsec_main;
229   ipsecmb_main_t *imbm = &ipsecmb_main;
230   ipsec_proto_main_t *em = &ipsec_proto_main;
231   from = vlib_frame_vector_args (from_frame);
232   n_left_from = from_frame->n_vectors;
233   int icv_size = 0;
234   u32 thread_index = vlib_get_thread_index ();
235   MB_MGR *mgr = imbm->mb_mgr[thread_index];
236   u32 packets_in_flight = 0;
237
238   next_index = node->cached_next_index;
239
240   while (n_left_from > 0 || packets_in_flight > 0)
241     {
242       u32 n_left_to_next;
243
244       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
245
246       while (n_left_from > 0 && n_left_to_next > 0)
247         {
248           u32 bi0;
249           u32 next0;
250           vlib_buffer_t *b0;
251           ah_header_t *ah0;
252           ipsec_sa_t *sa0;
253           ipsecmb_sa_t *samb0;
254           u32 sa_index0 = ~0;
255           u32 seq;
256           ip4_header_t *ih4 = 0;
257           ip6_header_t *ih6 = 0;
258           u8 ip_hdr_size = 0;
259
260           bi0 = from[0];
261           from += 1;
262           n_left_from -= 1;
263
264           next0 = AH_DECRYPT_NEXT_DROP;
265
266           b0 = vlib_get_buffer (vm, bi0);
267           ih4 = vlib_buffer_get_current (b0);
268           ih6 = vlib_buffer_get_current (b0);
269
270           sa_index0 = vnet_buffer (b0)->ipsec.sad_index;
271           sa0 = pool_elt_at_index (im->sad, sa_index0);
272           samb0 = pool_elt_at_index (imbm->sad, sa_index0);
273
274           if (is_ip6)
275             {
276               ip6_ext_header_t *prev = NULL;
277               ip6_ext_header_find_t (ih6, prev, ah0, IP_PROTOCOL_IPSEC_AH);
278               ip_hdr_size = sizeof (ip6_header_t);
279               ASSERT ((u8 *) ah0 - (u8 *) ih6 == ip_hdr_size);
280             }
281           else
282             {
283               ip_hdr_size = ip4_header_bytes (ih4);
284               ah0 = (ah_header_t *) (ih4 + 1);
285             }
286
287           seq = clib_host_to_net_u32 (ah0->seq_no);
288           /* anti-replay check */
289           // TODO UT remaining
290           if (sa0->use_anti_replay)
291             {
292               int rv = 0;
293
294               if (PREDICT_TRUE (sa0->use_esn))
295                 rv = esp_replay_check_esn (sa0, seq);
296               else
297                 rv = esp_replay_check (sa0, seq);
298
299               if (PREDICT_FALSE (rv))
300                 {
301                   clib_warning ("anti-replay SPI %u seq %u", sa0->spi, seq);
302                   vlib_node_increment_counter (vm, node->node_index,
303                                                AH_DECRYPT_ERROR_REPLAY, 1);
304                   goto trace;
305                 }
306             }
307
308           sa0->total_data_size += b0->current_length;
309           icv_size =
310             em->ipsec_proto_main_integ_algs[sa0->integ_alg].trunc_size;
311           if (PREDICT_TRUE (sa0->integ_alg != IPSEC_INTEG_ALG_NONE))
312             {
313               u8 *icv = (u8 *) vlib_buffer_get_current (b0) + ip_hdr_size +
314                 sizeof (ah_header_t);
315               size_t seq_size = 0;
316               if (PREDICT_TRUE (sa0->use_esn))
317                 {
318                   *(u32 *) (vlib_buffer_get_current (b0) +
319                             b0->current_length) = sa0->seq_hi;
320                   seq_size = sizeof (u32);
321                 }
322               clib_memcpy (vlib_buffer_get_current (b0) + b0->current_length +
323                            seq_size, icv, icv_size);
324               memset (icv, 0, icv_size);
325
326               ip_mutable_data_t *md =
327                 (ip_mutable_data_t *) ((u8 *) vlib_buffer_get_current (b0) +
328                                        b0->current_length + seq_size +
329                                        icv_size);
330               if (is_ip6)
331                 {
332                   md->ip_version_traffic_class_and_flow_label =
333                     ih6->ip_version_traffic_class_and_flow_label;
334                   md->hop_limit = ih6->hop_limit;
335                   ih6->ip_version_traffic_class_and_flow_label = 0x60;
336                   ih6->hop_limit = 0;
337                 }
338               else
339                 {
340                   md->tos = ih4->tos;
341                   md->ttl = ih4->ttl;
342                   ih4->tos = 0;
343                   ih4->ttl = 0;
344                   ih4->checksum = 0;
345                   ih4->flags_and_fragment_offset = 0;
346                 }
347
348               JOB_AES_HMAC *job = IPSECMB_FUNC (get_next_job) (mgr);
349               job->src = vlib_buffer_get_current (b0);
350               job->hash_start_src_offset_in_bytes = 0;
351               job->cipher_mode = NULL_CIPHER;
352               job->hash_alg = imbm->integ_algs[sa0->integ_alg].hash_alg;
353               job->auth_tag_output_len_in_bytes =
354                 imbm->integ_algs[sa0->integ_alg].hash_output_length;
355               job->auth_tag_output = icv;
356               job->msg_len_to_hash_in_bytes = b0->current_length + seq_size;
357               job->cipher_direction = DECRYPT;
358               job->chain_order = HASH_CIPHER;
359               job->u.HMAC._hashed_auth_key_xor_ipad = samb0->ipad_hash;
360               job->u.HMAC._hashed_auth_key_xor_opad = samb0->opad_hash;
361
362               job->user_data = (void *) (uintptr_t) bi0;
363               job->user_data2 = (void *) (uintptr_t) next0;
364               vnet_buffer (b0)->ipsec.sad_index = sa_index0;
365               job = IPSECMB_FUNC (submit_job) (mgr);
366               ++packets_in_flight;
367
368               if (!job)
369                 {
370                   continue;
371                 }
372               --packets_in_flight;
373               ASSERT (STS_COMPLETED == job->status);
374               ah_finish_decrypt (vm, node, job, &bi0, &next0, is_ip6);
375             }
376           else
377             {
378               remove_ah (vm, node, &bi0, &next0, sa0, ip_hdr_size, icv_size,
379                          0, ah0, is_ip6);
380             }
381         trace:
382           to_next[0] = bi0;
383           to_next += 1;
384           n_left_to_next -= 1;
385           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
386             {
387               b0->flags |= VLIB_BUFFER_IS_TRACED;
388               ah_decrypt_trace_t *tr =
389                 vlib_add_trace (vm, node, b0, sizeof (*tr));
390               tr->integ_alg = sa0->integ_alg;
391             }
392           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
393                                            n_left_to_next, bi0, next0);
394         }
395
396       if (0 == n_left_from)
397         {
398           JOB_AES_HMAC *job = NULL;
399           while (n_left_to_next > 0 && (job = IPSECMB_FUNC (flush_job) (mgr)))
400             {
401               --packets_in_flight;
402               ASSERT (STS_COMPLETED == job->status);
403               u32 bi0, next0;
404               ah_finish_decrypt (vm, node, job, &bi0, &next0, is_ip6);
405               to_next[0] = bi0;
406               to_next += 1;
407               n_left_to_next -= 1;
408               vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
409               if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
410                 {
411                   ipsec_sa_t *sa0 = pool_elt_at_index (im->sad,
412                                                        vnet_buffer
413                                                        (b0)->ipsec.sad_index);
414                   b0->flags |= VLIB_BUFFER_IS_TRACED;
415                   ah_decrypt_trace_t *tr =
416                     vlib_add_trace (vm, node, b0, sizeof (*tr));
417                   tr->integ_alg = sa0->integ_alg;
418                 }
419               vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
420                                                n_left_to_next, bi0, next0);
421             }
422         }
423
424       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
425     }
426   vlib_node_increment_counter (vm, node->node_index, AH_DECRYPT_ERROR_RX_PKTS,
427                                from_frame->n_vectors);
428
429   return from_frame->n_vectors;
430 }
431
432 VLIB_NODE_FN (ah4_decrypt_ipsecmb_node) (vlib_main_t * vm,
433                                          vlib_node_runtime_t * node,
434                                          vlib_frame_t * from_frame)
435 {
436   return ah_decrypt_ipsecmb_inline (vm, node, from_frame, 0 /*is_ip6 */ );
437 }
438
439 VLIB_NODE_FN (ah6_decrypt_ipsecmb_node) (vlib_main_t * vm,
440                                          vlib_node_runtime_t * node,
441                                          vlib_frame_t * from_frame)
442 {
443   return ah_decrypt_ipsecmb_inline (vm, node, from_frame, 1 /*is_ip6 */ );
444 }
445 #endif
446
447 /* *INDENT-OFF* */
448 VLIB_REGISTER_NODE (ah4_decrypt_ipsecmb_node) = {
449     .name = "ah4-decrypt-ipsecmb",
450     .vector_size = sizeof (u32),
451     .format_trace = format_ah_decrypt_trace,
452     .type = VLIB_NODE_TYPE_INTERNAL,
453
454     .n_errors = ARRAY_LEN (ah_decrypt_error_strings),
455     .error_strings = ah_decrypt_error_strings,
456
457     .n_next_nodes = AH_DECRYPT_N_NEXT,
458     .next_nodes =
459         {
460 #define _(s, n) [AH_DECRYPT_NEXT_##s] = n,
461             foreach_ah_decrypt_next
462 #undef _
463         },
464 };
465 /* *INDENT-ON* */
466
467 /* *INDENT-OFF* */
468 VLIB_REGISTER_NODE (ah6_decrypt_ipsecmb_node) = {
469     .name = "ah6-decrypt-ipsecmb",
470     .vector_size = sizeof (u32),
471     .format_trace = format_ah_decrypt_trace,
472     .type = VLIB_NODE_TYPE_INTERNAL,
473
474     .n_errors = ARRAY_LEN (ah_decrypt_error_strings),
475     .error_strings = ah_decrypt_error_strings,
476
477     .n_next_nodes = AH_DECRYPT_N_NEXT,
478     .next_nodes =
479         {
480 #define _(s, n) [AH_DECRYPT_NEXT_##s] = n,
481             foreach_ah_decrypt_next
482 #undef _
483         },
484 };
485 /* *INDENT-ON* */
486
487 /*
488  * fd.io coding-style-patch-verification: ON
489  *
490  * Local Variables:
491  * eval: (c-set-style "gnu")
492  * End:
493  */