ipsec: change wildcard value for any protocol of spd policy
[vpp.git] / src / vnet / ipsec / ipsec_output.c
1 /*
2  * ipsec_output.c : IPSec output 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/ipsec_io.h>
24
25 #define foreach_ipsec_output_error                   \
26  _(RX_PKTS, "IPSec pkts received")                   \
27  _(POLICY_DISCARD, "IPSec policy discard")           \
28  _(POLICY_NO_MATCH, "IPSec policy (no match)")       \
29  _(POLICY_PROTECT, "IPSec policy protect")           \
30  _(POLICY_BYPASS, "IPSec policy bypass")             \
31  _(ENCAPS_FAILED, "IPSec encapsulation failed")
32
33 typedef enum
34 {
35 #define _(sym,str) IPSEC_OUTPUT_ERROR_##sym,
36   foreach_ipsec_output_error
37 #undef _
38     IPSEC_DECAP_N_ERROR,
39 } ipsec_output_error_t;
40
41 static char *ipsec_output_error_strings[] = {
42 #define _(sym,string) string,
43   foreach_ipsec_output_error
44 #undef _
45 };
46
47 typedef struct
48 {
49   u32 spd_id;
50   u32 policy_id;
51 } ipsec_output_trace_t;
52
53 /* packet trace format function */
54 static u8 *
55 format_ipsec_output_trace (u8 * s, va_list * args)
56 {
57   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
58   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
59   ipsec_output_trace_t *t = va_arg (*args, ipsec_output_trace_t *);
60
61   s = format (s, "spd %u policy %d", t->spd_id, t->policy_id);
62
63   return s;
64 }
65
66 always_inline void
67 ipsec4_out_spd_add_flow_cache_entry (ipsec_main_t *im, u8 pr, u32 la, u32 ra,
68                                      u16 lp, u16 rp, u32 pol_id)
69 {
70   u64 hash;
71   u8 overwrite = 0, stale_overwrite = 0;
72   ipsec4_spd_5tuple_t ip4_5tuple = { .ip4_addr = { (ip4_address_t) la,
73                                                    (ip4_address_t) ra },
74                                      .port = { lp, rp },
75                                      .proto = pr };
76
77   ip4_5tuple.kv_16_8.value = (((u64) pol_id) << 32) | ((u64) im->epoch_count);
78
79   hash = ipsec4_hash_16_8 (&ip4_5tuple.kv_16_8);
80   hash &= (im->ipsec4_out_spd_hash_num_buckets - 1);
81
82   ipsec_spinlock_lock (&im->ipsec4_out_spd_hash_tbl[hash].bucket_lock);
83   /* Check if we are overwriting an existing entry so we know
84   whether to increment the flow cache counter. Since flow
85   cache counter is reset on any policy add/remove, but
86   hash table values are not, we also need to check if the entry
87   we are overwriting is stale or not. If it's a stale entry
88   overwrite, we still want to increment flow cache counter */
89   overwrite = (im->ipsec4_out_spd_hash_tbl[hash].value != 0);
90   /* Check for stale entry by comparing with current epoch count */
91   if (PREDICT_FALSE (overwrite))
92     stale_overwrite =
93       (im->epoch_count !=
94        ((u32) (im->ipsec4_out_spd_hash_tbl[hash].value & 0xFFFFFFFF)));
95   clib_memcpy_fast (&im->ipsec4_out_spd_hash_tbl[hash], &ip4_5tuple.kv_16_8,
96                     sizeof (ip4_5tuple.kv_16_8));
97   ipsec_spinlock_unlock (&im->ipsec4_out_spd_hash_tbl[hash].bucket_lock);
98
99   /* Increment the counter to track active flow cache entries
100     when entering a fresh entry or overwriting a stale one */
101   if (!overwrite || stale_overwrite)
102     clib_atomic_fetch_add_relax (&im->ipsec4_out_spd_flow_cache_entries, 1);
103
104   return;
105 }
106
107 always_inline ipsec_policy_t *
108 ipsec4_out_spd_find_flow_cache_entry (ipsec_main_t *im, u8 pr, u32 la, u32 ra,
109                                       u16 lp, u16 rp)
110 {
111   ipsec_policy_t *p = NULL;
112   ipsec4_hash_kv_16_8_t kv_result;
113   u64 hash;
114
115   if (PREDICT_FALSE ((pr != IP_PROTOCOL_TCP) && (pr != IP_PROTOCOL_UDP) &&
116                      (pr != IP_PROTOCOL_SCTP)))
117     {
118       lp = 0;
119       rp = 0;
120     }
121   ipsec4_spd_5tuple_t ip4_5tuple = { .ip4_addr = { (ip4_address_t) la,
122                                                    (ip4_address_t) ra },
123                                      .port = { lp, rp },
124                                      .proto = pr };
125
126   hash = ipsec4_hash_16_8 (&ip4_5tuple.kv_16_8);
127   hash &= (im->ipsec4_out_spd_hash_num_buckets - 1);
128
129   ipsec_spinlock_lock (&im->ipsec4_out_spd_hash_tbl[hash].bucket_lock);
130   kv_result = im->ipsec4_out_spd_hash_tbl[hash];
131   ipsec_spinlock_unlock (&im->ipsec4_out_spd_hash_tbl[hash].bucket_lock);
132
133   if (ipsec4_hash_key_compare_16_8 ((u64 *) &ip4_5tuple.kv_16_8,
134                                     (u64 *) &kv_result))
135     {
136       if (im->epoch_count == ((u32) (kv_result.value & 0xFFFFFFFF)))
137         {
138           /* Get the policy based on the index */
139           p =
140             pool_elt_at_index (im->policies, ((u32) (kv_result.value >> 32)));
141         }
142     }
143
144   return p;
145 }
146
147 always_inline ipsec_policy_t *
148 ipsec_output_policy_match (ipsec_spd_t *spd, u8 pr, u32 la, u32 ra, u16 lp,
149                            u16 rp, u8 flow_cache_enabled)
150 {
151   ipsec_main_t *im = &ipsec_main;
152   ipsec_policy_t *p;
153   u32 *i;
154
155   if (!spd)
156     return 0;
157
158   vec_foreach (i, spd->policies[IPSEC_SPD_POLICY_IP4_OUTBOUND])
159   {
160     p = pool_elt_at_index (im->policies, *i);
161     if (PREDICT_FALSE ((p->protocol != IPSEC_POLICY_PROTOCOL_ANY) &&
162                        (p->protocol != pr)))
163       continue;
164
165     if (ra < clib_net_to_host_u32 (p->raddr.start.ip4.as_u32))
166       continue;
167
168     if (ra > clib_net_to_host_u32 (p->raddr.stop.ip4.as_u32))
169       continue;
170
171     if (la < clib_net_to_host_u32 (p->laddr.start.ip4.as_u32))
172       continue;
173
174     if (la > clib_net_to_host_u32 (p->laddr.stop.ip4.as_u32))
175       continue;
176
177     if (PREDICT_FALSE ((pr != IP_PROTOCOL_TCP) && (pr != IP_PROTOCOL_UDP) &&
178                        (pr != IP_PROTOCOL_SCTP)))
179       {
180         lp = 0;
181         rp = 0;
182         goto add_flow_cache;
183       }
184
185     if (lp < p->lport.start)
186       continue;
187
188     if (lp > p->lport.stop)
189       continue;
190
191     if (rp < p->rport.start)
192       continue;
193
194     if (rp > p->rport.stop)
195       continue;
196
197   add_flow_cache:
198     if (flow_cache_enabled)
199       {
200         /* Add an Entry in Flow cache */
201         ipsec4_out_spd_add_flow_cache_entry (
202           im, pr, clib_host_to_net_u32 (la), clib_host_to_net_u32 (ra),
203           clib_host_to_net_u16 (lp), clib_host_to_net_u16 (rp), *i);
204       }
205
206     return p;
207   }
208   return 0;
209 }
210
211 always_inline uword
212 ip6_addr_match_range (ip6_address_t * a, ip6_address_t * la,
213                       ip6_address_t * ua)
214 {
215   if ((memcmp (a->as_u64, la->as_u64, 2 * sizeof (u64)) >= 0) &&
216       (memcmp (a->as_u64, ua->as_u64, 2 * sizeof (u64)) <= 0))
217     return 1;
218   return 0;
219 }
220
221 always_inline ipsec_policy_t *
222 ipsec6_output_policy_match (ipsec_spd_t * spd,
223                             ip6_address_t * la,
224                             ip6_address_t * ra, u16 lp, u16 rp, u8 pr)
225 {
226   ipsec_main_t *im = &ipsec_main;
227   ipsec_policy_t *p;
228   u32 *i;
229
230   if (!spd)
231     return 0;
232
233   vec_foreach (i, spd->policies[IPSEC_SPD_POLICY_IP6_OUTBOUND])
234   {
235     p = pool_elt_at_index (im->policies, *i);
236     if (PREDICT_FALSE ((p->protocol != IPSEC_POLICY_PROTOCOL_ANY) &&
237                        (p->protocol != pr)))
238       continue;
239
240     if (!ip6_addr_match_range (ra, &p->raddr.start.ip6, &p->raddr.stop.ip6))
241       continue;
242
243     if (!ip6_addr_match_range (la, &p->laddr.start.ip6, &p->laddr.stop.ip6))
244       continue;
245
246     if (PREDICT_FALSE
247         ((pr != IP_PROTOCOL_TCP) && (pr != IP_PROTOCOL_UDP)
248          && (pr != IP_PROTOCOL_SCTP)))
249       return p;
250
251     if (lp < p->lport.start)
252       continue;
253
254     if (lp > p->lport.stop)
255       continue;
256
257     if (rp < p->rport.start)
258       continue;
259
260     if (rp > p->rport.stop)
261       continue;
262
263     return p;
264   }
265
266   return 0;
267 }
268
269 static inline uword
270 ipsec_output_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
271                      vlib_frame_t * from_frame, int is_ipv6)
272 {
273   ipsec_main_t *im = &ipsec_main;
274
275   u32 *from, *to_next = 0, thread_index;
276   u32 n_left_from, sw_if_index0, last_sw_if_index = (u32) ~ 0;
277   u32 next_node_index = (u32) ~ 0, last_next_node_index = (u32) ~ 0;
278   vlib_frame_t *f = 0;
279   u32 spd_index0 = ~0;
280   ipsec_spd_t *spd0 = 0;
281   int bogus;
282   u64 nc_protect = 0, nc_bypass = 0, nc_discard = 0, nc_nomatch = 0;
283   u8 flow_cache_enabled = im->output_flow_cache_flag;
284
285   from = vlib_frame_vector_args (from_frame);
286   n_left_from = from_frame->n_vectors;
287   thread_index = vm->thread_index;
288
289   while (n_left_from > 0)
290     {
291       u32 bi0, pi0, bi1;
292       vlib_buffer_t *b0, *b1;
293       ipsec_policy_t *p0 = NULL;
294       ip4_header_t *ip0;
295       ip6_header_t *ip6_0 = 0;
296       udp_header_t *udp0;
297       u32 iph_offset = 0;
298       tcp_header_t *tcp0;
299       u64 bytes0;
300
301       bi0 = from[0];
302       b0 = vlib_get_buffer (vm, bi0);
303       if (n_left_from > 1)
304         {
305           bi1 = from[1];
306           b1 = vlib_get_buffer (vm, bi1);
307           CLIB_PREFETCH (b1, CLIB_CACHE_LINE_BYTES * 2, STORE);
308           vlib_prefetch_buffer_data (b1, LOAD);
309         }
310       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
311       iph_offset = vnet_buffer (b0)->ip.save_rewrite_length;
312       ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0)
313                               + iph_offset);
314
315       /* lookup for SPD only if sw_if_index is changed */
316       if (PREDICT_FALSE (last_sw_if_index != sw_if_index0))
317         {
318           uword *p = hash_get (im->spd_index_by_sw_if_index, sw_if_index0);
319           ALWAYS_ASSERT (p);
320           spd_index0 = p[0];
321           spd0 = pool_elt_at_index (im->spds, spd_index0);
322           last_sw_if_index = sw_if_index0;
323         }
324
325       if (is_ipv6)
326         {
327           ip6_0 = (ip6_header_t *) ((u8 *) vlib_buffer_get_current (b0)
328                                     + iph_offset);
329
330           udp0 = ip6_next_header (ip6_0);
331 #if 0
332           clib_warning
333             ("packet received from %U port %u to %U port %u spd_id %u",
334              format_ip6_address, &ip6_0->src_address,
335              clib_net_to_host_u16 (udp0->src_port), format_ip6_address,
336              &ip6_0->dst_address, clib_net_to_host_u16 (udp0->dst_port),
337              spd0->id);
338 #endif
339
340           p0 = ipsec6_output_policy_match (spd0,
341                                            &ip6_0->src_address,
342                                            &ip6_0->dst_address,
343                                            clib_net_to_host_u16
344                                            (udp0->src_port),
345                                            clib_net_to_host_u16
346                                            (udp0->dst_port), ip6_0->protocol);
347         }
348       else
349         {
350           udp0 = (udp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0));
351
352 #if 0
353           clib_warning ("packet received from %U to %U port %u",
354                         format_ip4_address, ip0->src_address.as_u8,
355                         format_ip4_address, ip0->dst_address.as_u8,
356                         clib_net_to_host_u16 (udp0->dst_port));
357           clib_warning ("sw_if_index0 %u spd_index0 %u spd_id %u",
358                         sw_if_index0, spd_index0, spd0->id);
359 #endif
360
361           /*
362            * Check whether flow cache is enabled.
363            */
364           if (flow_cache_enabled)
365             {
366               p0 = ipsec4_out_spd_find_flow_cache_entry (
367                 im, ip0->protocol, ip0->src_address.as_u32,
368                 ip0->dst_address.as_u32, udp0->src_port, udp0->dst_port);
369             }
370
371           /* Fall back to linear search if flow cache lookup fails */
372           if (p0 == NULL)
373             {
374               p0 = ipsec_output_policy_match (
375                 spd0, ip0->protocol,
376                 clib_net_to_host_u32 (ip0->src_address.as_u32),
377                 clib_net_to_host_u32 (ip0->dst_address.as_u32),
378                 clib_net_to_host_u16 (udp0->src_port),
379                 clib_net_to_host_u16 (udp0->dst_port), flow_cache_enabled);
380             }
381         }
382       tcp0 = (void *) udp0;
383
384       if (PREDICT_TRUE (p0 != NULL))
385         {
386           pi0 = p0 - im->policies;
387
388           vlib_prefetch_combined_counter (&ipsec_spd_policy_counters,
389                                           thread_index, pi0);
390
391           if (is_ipv6)
392             {
393               bytes0 = clib_net_to_host_u16 (ip6_0->payload_length);
394               bytes0 += sizeof (ip6_header_t);
395             }
396           else
397             {
398               bytes0 = clib_net_to_host_u16 (ip0->length);
399             }
400
401           if (p0->policy == IPSEC_POLICY_ACTION_PROTECT)
402             {
403               ipsec_sa_t *sa = 0;
404               nc_protect++;
405               sa = ipsec_sa_get (p0->sa_index);
406               if (sa->protocol == IPSEC_PROTOCOL_ESP)
407                 if (is_ipv6)
408                   next_node_index = im->esp6_encrypt_node_index;
409                 else
410                   next_node_index = im->esp4_encrypt_node_index;
411               else if (is_ipv6)
412                 next_node_index = im->ah6_encrypt_node_index;
413               else
414                 next_node_index = im->ah4_encrypt_node_index;
415               vnet_buffer (b0)->ipsec.sad_index = p0->sa_index;
416
417               if (PREDICT_FALSE (b0->flags & VNET_BUFFER_F_OFFLOAD))
418                 {
419                   vnet_buffer_oflags_t oflags = vnet_buffer (b0)->oflags;
420
421                   /*
422                    * Clearing offload flags before checksum is computed
423                    * It guarantees the cache hit!
424                    */
425                   vnet_buffer_offload_flags_clear (b0, oflags);
426
427                   if (is_ipv6)
428                     {
429                       if (PREDICT_FALSE (oflags &
430                                          VNET_BUFFER_OFFLOAD_F_TCP_CKSUM))
431                         {
432                           tcp0->checksum = ip6_tcp_udp_icmp_compute_checksum (
433                             vm, b0, ip6_0, &bogus);
434                         }
435                       if (PREDICT_FALSE (oflags &
436                                          VNET_BUFFER_OFFLOAD_F_UDP_CKSUM))
437                         {
438                           udp0->checksum = ip6_tcp_udp_icmp_compute_checksum (
439                             vm, b0, ip6_0, &bogus);
440                         }
441                     }
442                   else
443                     {
444                       if (PREDICT_FALSE (oflags &
445                                          VNET_BUFFER_OFFLOAD_F_IP_CKSUM))
446                         {
447                           ip0->checksum = ip4_header_checksum (ip0);
448                         }
449                       if (PREDICT_FALSE (oflags &
450                                          VNET_BUFFER_OFFLOAD_F_TCP_CKSUM))
451                         {
452                           tcp0->checksum =
453                             ip4_tcp_udp_compute_checksum (vm, b0, ip0);
454                         }
455                       if (PREDICT_FALSE (oflags &
456                                          VNET_BUFFER_OFFLOAD_F_UDP_CKSUM))
457                         {
458                           udp0->checksum =
459                             ip4_tcp_udp_compute_checksum (vm, b0, ip0);
460                         }
461                     }
462                 }
463               vlib_buffer_advance (b0, iph_offset);
464             }
465           else if (p0->policy == IPSEC_POLICY_ACTION_BYPASS)
466             {
467               nc_bypass++;
468               next_node_index = get_next_output_feature_node_index (b0, node);
469             }
470           else
471             {
472               nc_discard++;
473               next_node_index = im->error_drop_node_index;
474             }
475           vlib_increment_combined_counter
476             (&ipsec_spd_policy_counters, thread_index, pi0, 1, bytes0);
477         }
478       else
479         {
480           pi0 = ~0;
481           nc_nomatch++;
482           next_node_index = im->error_drop_node_index;
483         }
484
485       from += 1;
486       n_left_from -= 1;
487
488       if (PREDICT_FALSE ((last_next_node_index != next_node_index) || f == 0))
489         {
490           /* if this is not 1st frame */
491           if (f)
492             vlib_put_frame_to_node (vm, last_next_node_index, f);
493
494           last_next_node_index = next_node_index;
495
496           f = vlib_get_frame_to_node (vm, next_node_index);
497
498           /* frame->frame_flags, copy it from node */
499           /* Copy trace flag from next_frame and from runtime. */
500           f->frame_flags |= node->flags & VLIB_NODE_FLAG_TRACE;
501
502           to_next = vlib_frame_vector_args (f);
503         }
504
505       to_next[0] = bi0;
506       to_next += 1;
507       f->n_vectors++;
508
509       if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
510           PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
511         {
512           ipsec_output_trace_t *tr =
513             vlib_add_trace (vm, node, b0, sizeof (*tr));
514           if (spd0)
515             tr->spd_id = spd0->id;
516           tr->policy_id = pi0;
517         }
518     }
519
520   vlib_put_frame_to_node (vm, next_node_index, f);
521   vlib_node_increment_counter (vm, node->node_index,
522                                IPSEC_OUTPUT_ERROR_POLICY_PROTECT, nc_protect);
523   vlib_node_increment_counter (vm, node->node_index,
524                                IPSEC_OUTPUT_ERROR_POLICY_BYPASS, nc_bypass);
525   vlib_node_increment_counter (vm, node->node_index,
526                                IPSEC_OUTPUT_ERROR_POLICY_DISCARD, nc_discard);
527   vlib_node_increment_counter (vm, node->node_index,
528                                IPSEC_OUTPUT_ERROR_POLICY_NO_MATCH,
529                                nc_nomatch);
530   return from_frame->n_vectors;
531 }
532
533 VLIB_NODE_FN (ipsec4_output_node) (vlib_main_t * vm,
534                                    vlib_node_runtime_t * node,
535                                    vlib_frame_t * frame)
536 {
537   return ipsec_output_inline (vm, node, frame, 0);
538 }
539
540 /* *INDENT-OFF* */
541 VLIB_REGISTER_NODE (ipsec4_output_node) = {
542   .name = "ipsec4-output-feature",
543   .vector_size = sizeof (u32),
544   .format_trace = format_ipsec_output_trace,
545   .type = VLIB_NODE_TYPE_INTERNAL,
546
547   .n_errors = ARRAY_LEN(ipsec_output_error_strings),
548   .error_strings = ipsec_output_error_strings,
549
550   .n_next_nodes = IPSEC_OUTPUT_N_NEXT,
551   .next_nodes = {
552 #define _(s,n) [IPSEC_OUTPUT_NEXT_##s] = n,
553     foreach_ipsec_output_next
554 #undef _
555   },
556 };
557 /* *INDENT-ON* */
558
559 VLIB_NODE_FN (ipsec6_output_node) (vlib_main_t * vm,
560                                    vlib_node_runtime_t * node,
561                                    vlib_frame_t * frame)
562 {
563   return ipsec_output_inline (vm, node, frame, 1);
564 }
565
566 VLIB_REGISTER_NODE (ipsec6_output_node) = {
567   .name = "ipsec6-output-feature",
568   .vector_size = sizeof (u32),
569   .format_trace = format_ipsec_output_trace,
570   .type = VLIB_NODE_TYPE_INTERNAL,
571
572   .n_errors = ARRAY_LEN(ipsec_output_error_strings),
573   .error_strings = ipsec_output_error_strings,
574
575   .n_next_nodes = IPSEC_OUTPUT_N_NEXT,
576   .next_nodes = {
577 #define _(s,n) [IPSEC_OUTPUT_NEXT_##s] = n,
578     foreach_ipsec_output_next
579 #undef _
580   },
581 };
582