Add extern to *_main global variable declarations in header files.
[vpp.git] / src / vnet / ipsec / ipsec.c
1 /*
2  * decap.c : IPSec tunnel support
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 #include <vnet/interface.h>
22
23 #include <vnet/ipsec/ipsec.h>
24 #include <vnet/ipsec/ikev2.h>
25 #include <vnet/ipsec/esp.h>
26
27 ipsec_main_t ipsec_main;
28
29 u32
30 ipsec_get_sa_index_by_sa_id (u32 sa_id)
31 {
32   ipsec_main_t *im = &ipsec_main;
33   uword *p = hash_get (im->sa_index_by_sa_id, sa_id);
34   if (!p)
35     return ~0;
36
37   return p[0];
38 }
39
40 int
41 ipsec_set_interface_spd (vlib_main_t * vm, u32 sw_if_index, u32 spd_id,
42                          int is_add)
43 {
44   ipsec_main_t *im = &ipsec_main;
45   ip4_ipsec_config_t config;
46
47   u32 spd_index;
48   uword *p;
49
50   p = hash_get (im->spd_index_by_spd_id, spd_id);
51   if (!p)
52     return VNET_API_ERROR_SYSCALL_ERROR_1;      /* no such spd-id */
53
54   spd_index = p[0];
55
56   p = hash_get (im->spd_index_by_sw_if_index, sw_if_index);
57   if (p && is_add)
58     return VNET_API_ERROR_SYSCALL_ERROR_1;      /* spd already assigned */
59
60   if (is_add)
61     {
62       hash_set (im->spd_index_by_sw_if_index, sw_if_index, spd_index);
63     }
64   else
65     {
66       hash_unset (im->spd_index_by_sw_if_index, sw_if_index);
67     }
68
69   clib_warning ("sw_if_index %u spd_id %u spd_index %u",
70                 sw_if_index, spd_id, spd_index);
71
72   /* enable IPsec on TX */
73   vnet_feature_enable_disable ("ip4-output", "ipsec-output-ip4", sw_if_index,
74                                is_add, 0, 0);
75   vnet_feature_enable_disable ("ip6-output", "ipsec-output-ip6", sw_if_index,
76                                is_add, 0, 0);
77
78   /* enable IPsec on RX */
79   vnet_feature_enable_disable ("ip4-unicast", "ipsec-input-ip4", sw_if_index,
80                                is_add, &config, sizeof (config));
81   vnet_feature_enable_disable ("ip6-unicast", "ipsec-input-ip6", sw_if_index,
82                                is_add, &config, sizeof (config));
83
84   return 0;
85 }
86
87 int
88 ipsec_add_del_spd (vlib_main_t * vm, u32 spd_id, int is_add)
89 {
90   ipsec_main_t *im = &ipsec_main;
91   ipsec_spd_t *spd = 0;
92   uword *p;
93   u32 spd_index, k, v;
94
95   p = hash_get (im->spd_index_by_spd_id, spd_id);
96   if (p && is_add)
97     return VNET_API_ERROR_INVALID_VALUE;
98   if (!p && !is_add)
99     return VNET_API_ERROR_INVALID_VALUE;
100
101   if (!is_add)                  /* delete */
102     {
103       spd_index = p[0];
104       spd = pool_elt_at_index (im->spds, spd_index);
105       if (!spd)
106         return VNET_API_ERROR_INVALID_VALUE;
107       /* *INDENT-OFF* */
108       hash_foreach (k, v, im->spd_index_by_sw_if_index, ({
109         if (v == spd_index)
110           ipsec_set_interface_spd(vm, k, spd_id, 0);
111       }));
112       /* *INDENT-ON* */
113       hash_unset (im->spd_index_by_spd_id, spd_id);
114       pool_free (spd->policies);
115       vec_free (spd->ipv4_outbound_policies);
116       vec_free (spd->ipv6_outbound_policies);
117       vec_free (spd->ipv4_inbound_protect_policy_indices);
118       vec_free (spd->ipv4_inbound_policy_discard_and_bypass_indices);
119       pool_put (im->spds, spd);
120     }
121   else                          /* create new SPD */
122     {
123       pool_get (im->spds, spd);
124       memset (spd, 0, sizeof (*spd));
125       spd_index = spd - im->spds;
126       spd->id = spd_id;
127       hash_set (im->spd_index_by_spd_id, spd_id, spd_index);
128     }
129   return 0;
130 }
131
132 static int
133 ipsec_spd_entry_sort (void *a1, void *a2)
134 {
135   ipsec_main_t *im = &ipsec_main;
136   u32 *id1 = a1;
137   u32 *id2 = a2;
138   ipsec_spd_t *spd;
139   ipsec_policy_t *p1, *p2;
140
141   /* *INDENT-OFF* */
142   pool_foreach (spd, im->spds, ({
143     p1 = pool_elt_at_index(spd->policies, *id1);
144     p2 = pool_elt_at_index(spd->policies, *id2);
145     if (p1 && p2)
146       return p2->priority - p1->priority;
147   }));
148   /* *INDENT-ON* */
149
150   return 0;
151 }
152
153 int
154 ipsec_add_del_policy (vlib_main_t * vm, ipsec_policy_t * policy, int is_add)
155 {
156   ipsec_main_t *im = &ipsec_main;
157   ipsec_spd_t *spd = 0;
158   ipsec_policy_t *vp;
159   uword *p;
160   u32 spd_index;
161
162   clib_warning ("policy-id %u priority %d is_outbound %u", policy->id,
163                 policy->priority, policy->is_outbound);
164
165   if (policy->policy == IPSEC_POLICY_ACTION_PROTECT)
166     {
167       p = hash_get (im->sa_index_by_sa_id, policy->sa_id);
168       if (!p)
169         return VNET_API_ERROR_SYSCALL_ERROR_1;
170       policy->sa_index = p[0];
171     }
172
173   p = hash_get (im->spd_index_by_spd_id, policy->id);
174
175   if (!p)
176     return VNET_API_ERROR_SYSCALL_ERROR_1;
177
178   spd_index = p[0];
179   spd = pool_elt_at_index (im->spds, spd_index);
180   if (!spd)
181     return VNET_API_ERROR_SYSCALL_ERROR_1;
182
183   if (is_add)
184     {
185       u32 policy_index;
186
187       pool_get (spd->policies, vp);
188       clib_memcpy (vp, policy, sizeof (*vp));
189       policy_index = vp - spd->policies;
190
191       if (policy->is_outbound)
192         {
193           if (policy->is_ipv6)
194             {
195               vec_add1 (spd->ipv6_outbound_policies, policy_index);
196               clib_memcpy (vp, policy, sizeof (ipsec_policy_t));
197               vec_sort_with_function (spd->ipv6_outbound_policies,
198                                       ipsec_spd_entry_sort);
199             }
200           else
201             {
202               vec_add1 (spd->ipv4_outbound_policies, policy_index);
203               clib_memcpy (vp, policy, sizeof (ipsec_policy_t));
204               vec_sort_with_function (spd->ipv4_outbound_policies,
205                                       ipsec_spd_entry_sort);
206             }
207         }
208       else
209         {
210           if (policy->is_ipv6)
211             {
212               if (policy->policy == IPSEC_POLICY_ACTION_PROTECT)
213                 {
214                   vec_add1 (spd->ipv6_inbound_protect_policy_indices,
215                             policy_index);
216                   clib_memcpy (vp, policy, sizeof (ipsec_policy_t));
217                   vec_sort_with_function
218                     (spd->ipv6_inbound_protect_policy_indices,
219                      ipsec_spd_entry_sort);
220                 }
221               else
222                 {
223                   vec_add1
224                     (spd->ipv6_inbound_policy_discard_and_bypass_indices,
225                      policy_index);
226                   clib_memcpy (vp, policy, sizeof (ipsec_policy_t));
227                   vec_sort_with_function
228                     (spd->ipv6_inbound_policy_discard_and_bypass_indices,
229                      ipsec_spd_entry_sort);
230                 }
231             }
232           else
233             {
234               if (policy->policy == IPSEC_POLICY_ACTION_PROTECT)
235                 {
236                   vec_add1 (spd->ipv4_inbound_protect_policy_indices,
237                             policy_index);
238                   clib_memcpy (vp, policy, sizeof (ipsec_policy_t));
239                   vec_sort_with_function
240                     (spd->ipv4_inbound_protect_policy_indices,
241                      ipsec_spd_entry_sort);
242                 }
243               else
244                 {
245                   vec_add1
246                     (spd->ipv4_inbound_policy_discard_and_bypass_indices,
247                      policy_index);
248                   clib_memcpy (vp, policy, sizeof (ipsec_policy_t));
249                   vec_sort_with_function
250                     (spd->ipv4_inbound_policy_discard_and_bypass_indices,
251                      ipsec_spd_entry_sort);
252                 }
253             }
254         }
255
256     }
257   else
258     {
259       u32 i, j;
260       /* *INDENT-OFF* */
261       pool_foreach_index(i, spd->policies, ({
262         vp = pool_elt_at_index(spd->policies, i);
263         if (vp->priority != policy->priority)
264           continue;
265         if (vp->is_outbound != policy->is_outbound)
266           continue;
267         if (vp->policy != policy->policy)
268           continue;
269         if (vp->sa_id != policy->sa_id)
270           continue;
271         if (vp->protocol != policy->protocol)
272           continue;
273         if (vp->lport.start != policy->lport.start)
274           continue;
275         if (vp->lport.stop != policy->lport.stop)
276           continue;
277         if (vp->rport.start != policy->rport.start)
278           continue;
279         if (vp->rport.stop != policy->rport.stop)
280           continue;
281         if (vp->is_ipv6 != policy->is_ipv6)
282           continue;
283         if (policy->is_ipv6)
284           {
285             if (vp->laddr.start.ip6.as_u64[0] != policy->laddr.start.ip6.as_u64[0])
286               continue;
287             if (vp->laddr.start.ip6.as_u64[1] != policy->laddr.start.ip6.as_u64[1])
288               continue;
289             if (vp->laddr.stop.ip6.as_u64[0] != policy->laddr.stop.ip6.as_u64[0])
290               continue;
291             if (vp->laddr.stop.ip6.as_u64[1] != policy->laddr.stop.ip6.as_u64[1])
292               continue;
293             if (vp->raddr.start.ip6.as_u64[0] != policy->raddr.start.ip6.as_u64[0])
294               continue;
295             if (vp->raddr.start.ip6.as_u64[1] != policy->raddr.start.ip6.as_u64[1])
296               continue;
297             if (vp->raddr.stop.ip6.as_u64[0] != policy->raddr.stop.ip6.as_u64[0])
298               continue;
299            if (vp->laddr.stop.ip6.as_u64[1] != policy->laddr.stop.ip6.as_u64[1])
300               continue;
301            if (policy->is_outbound)
302              {
303                vec_foreach_index(j, spd->ipv6_outbound_policies) {
304                  if (vec_elt(spd->ipv6_outbound_policies, j) == i) {
305                    vec_del1 (spd->ipv6_outbound_policies, j);
306                    break;
307                  }
308                }
309              }
310            else
311              {
312                if (policy->policy == IPSEC_POLICY_ACTION_PROTECT)
313                  {
314                    vec_foreach_index(j, spd->ipv6_inbound_protect_policy_indices) {
315                      if (vec_elt(spd->ipv6_inbound_protect_policy_indices, j) == i) {
316                        vec_del1 (spd->ipv6_inbound_protect_policy_indices, j);
317                        break;
318                      }
319                    }
320                  }
321                else
322                  {
323                    vec_foreach_index(j, spd->ipv6_inbound_policy_discard_and_bypass_indices) {
324                      if (vec_elt(spd->ipv6_inbound_policy_discard_and_bypass_indices, j) == i) {
325                        vec_del1 (spd->ipv6_inbound_policy_discard_and_bypass_indices, j);
326                        break;
327                      }
328                    }
329                  }
330              }
331           }
332         else
333           {
334             if (vp->laddr.start.ip4.as_u32 != policy->laddr.start.ip4.as_u32)
335               continue;
336             if (vp->laddr.stop.ip4.as_u32 != policy->laddr.stop.ip4.as_u32)
337               continue;
338             if (vp->raddr.start.ip4.as_u32 != policy->raddr.start.ip4.as_u32)
339               continue;
340             if (vp->raddr.stop.ip4.as_u32 != policy->raddr.stop.ip4.as_u32)
341               continue;
342             if (policy->is_outbound)
343               {
344                 vec_foreach_index(j, spd->ipv4_outbound_policies) {
345                   if (vec_elt(spd->ipv4_outbound_policies, j) == i) {
346                     vec_del1 (spd->ipv4_outbound_policies, j);
347                     break;
348                   }
349                 }
350               }
351             else
352               {
353                 if (policy->policy == IPSEC_POLICY_ACTION_PROTECT)
354                   {
355                     vec_foreach_index(j, spd->ipv4_inbound_protect_policy_indices) {
356                       if (vec_elt(spd->ipv4_inbound_protect_policy_indices, j) == i) {
357                         vec_del1 (spd->ipv4_inbound_protect_policy_indices, j);
358                         break;
359                       }
360                     }
361                   }
362                 else
363                   {
364                     vec_foreach_index(j, spd->ipv4_inbound_policy_discard_and_bypass_indices) {
365                       if (vec_elt(spd->ipv4_inbound_policy_discard_and_bypass_indices, j) == i) {
366                         vec_del1 (spd->ipv4_inbound_policy_discard_and_bypass_indices, j);
367                         break;
368                     }
369                   }
370               }
371           }
372           pool_put (spd->policies, vp);
373           break;
374         }
375       }));
376       /* *INDENT-ON* */
377     }
378
379   return 0;
380 }
381
382 static u8
383 ipsec_is_sa_used (u32 sa_index)
384 {
385   ipsec_main_t *im = &ipsec_main;
386   ipsec_spd_t *spd;
387   ipsec_policy_t *p;
388   ipsec_tunnel_if_t *t;
389
390   /* *INDENT-OFF* */
391   pool_foreach(spd, im->spds, ({
392     pool_foreach(p, spd->policies, ({
393       if (p->policy == IPSEC_POLICY_ACTION_PROTECT)
394         {
395           if (p->sa_index == sa_index)
396             return 1;
397         }
398     }));
399   }));
400
401   pool_foreach(t, im->tunnel_interfaces, ({
402     if (t->input_sa_index == sa_index)
403       return 1;
404     if (t->output_sa_index == sa_index)
405       return 1;
406   }));
407   /* *INDENT-ON* */
408
409   return 0;
410 }
411
412 int
413 ipsec_add_del_sa (vlib_main_t * vm, ipsec_sa_t * new_sa, int is_add)
414 {
415   ipsec_main_t *im = &ipsec_main;
416   ipsec_sa_t *sa = 0;
417   uword *p;
418   u32 sa_index;
419   clib_error_t *err;
420
421   clib_warning ("id %u spi %u", new_sa->id, new_sa->spi);
422
423   p = hash_get (im->sa_index_by_sa_id, new_sa->id);
424   if (p && is_add)
425     return VNET_API_ERROR_SYSCALL_ERROR_1;      /* already exists */
426   if (!p && !is_add)
427     return VNET_API_ERROR_SYSCALL_ERROR_1;
428
429   if (!is_add)                  /* delete */
430     {
431       sa_index = p[0];
432       sa = pool_elt_at_index (im->sad, sa_index);
433       if (ipsec_is_sa_used (sa_index))
434         {
435           clib_warning ("sa_id %u used in policy", sa->id);
436           return VNET_API_ERROR_SYSCALL_ERROR_1;        /* sa used in policy */
437         }
438       hash_unset (im->sa_index_by_sa_id, sa->id);
439       if (im->cb.add_del_sa_sess_cb)
440         {
441           err = im->cb.add_del_sa_sess_cb (sa_index, 0);
442           if (err)
443             return VNET_API_ERROR_SYSCALL_ERROR_1;
444         }
445       pool_put (im->sad, sa);
446     }
447   else                          /* create new SA */
448     {
449       pool_get (im->sad, sa);
450       clib_memcpy (sa, new_sa, sizeof (*sa));
451       sa_index = sa - im->sad;
452       hash_set (im->sa_index_by_sa_id, sa->id, sa_index);
453       if (im->cb.add_del_sa_sess_cb)
454         {
455           err = im->cb.add_del_sa_sess_cb (sa_index, 1);
456           if (err)
457             return VNET_API_ERROR_SYSCALL_ERROR_1;
458         }
459     }
460   return 0;
461 }
462
463 int
464 ipsec_set_sa_key (vlib_main_t * vm, ipsec_sa_t * sa_update)
465 {
466   ipsec_main_t *im = &ipsec_main;
467   uword *p;
468   u32 sa_index;
469   ipsec_sa_t *sa = 0;
470   clib_error_t *err;
471
472   p = hash_get (im->sa_index_by_sa_id, sa_update->id);
473   if (!p)
474     return VNET_API_ERROR_SYSCALL_ERROR_1;      /* no such sa-id */
475
476   sa_index = p[0];
477   sa = pool_elt_at_index (im->sad, sa_index);
478
479   /* new crypto key */
480   if (0 < sa_update->crypto_key_len)
481     {
482       clib_memcpy (sa->crypto_key, sa_update->crypto_key,
483                    sa_update->crypto_key_len);
484       sa->crypto_key_len = sa_update->crypto_key_len;
485     }
486
487   /* new integ key */
488   if (0 < sa_update->integ_key_len)
489     {
490       clib_memcpy (sa->integ_key, sa_update->integ_key,
491                    sa_update->integ_key_len);
492       sa->integ_key_len = sa_update->integ_key_len;
493     }
494
495   if (0 < sa_update->crypto_key_len || 0 < sa_update->integ_key_len)
496     {
497       if (im->cb.add_del_sa_sess_cb)
498         {
499           err = im->cb.add_del_sa_sess_cb (sa_index, 0);
500           if (err)
501             return VNET_API_ERROR_SYSCALL_ERROR_1;
502         }
503     }
504
505   return 0;
506 }
507
508 static void
509 ipsec_rand_seed (void)
510 {
511   struct
512   {
513     time_t time;
514     pid_t pid;
515     void *p;
516   } seed_data;
517
518   seed_data.time = time (NULL);
519   seed_data.pid = getpid ();
520   seed_data.p = (void *) &seed_data;
521
522   RAND_seed ((const void *) &seed_data, sizeof (seed_data));
523 }
524
525 static clib_error_t *
526 ipsec_check_support (ipsec_sa_t * sa)
527 {
528   if (sa->crypto_alg == IPSEC_CRYPTO_ALG_AES_GCM_128)
529     return clib_error_return (0, "unsupported aes-gcm-128 crypto-alg");
530   if (sa->integ_alg == IPSEC_INTEG_ALG_NONE)
531     return clib_error_return (0, "unsupported none integ-alg");
532
533   return 0;
534 }
535
536 static clib_error_t *
537 ipsec_init (vlib_main_t * vm)
538 {
539   clib_error_t *error;
540   ipsec_main_t *im = &ipsec_main;
541   vlib_thread_main_t *tm = vlib_get_thread_main ();
542   vlib_node_t *node;
543
544   ipsec_rand_seed ();
545
546   memset (im, 0, sizeof (im[0]));
547
548   im->vnet_main = vnet_get_main ();
549   im->vlib_main = vm;
550
551   im->spd_index_by_spd_id = hash_create (0, sizeof (uword));
552   im->sa_index_by_sa_id = hash_create (0, sizeof (uword));
553   im->spd_index_by_sw_if_index = hash_create (0, sizeof (uword));
554
555   vec_validate_aligned (im->empty_buffers, tm->n_vlib_mains - 1,
556                         CLIB_CACHE_LINE_BYTES);
557
558   node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
559   ASSERT (node);
560   im->error_drop_node_index = node->index;
561
562   node = vlib_get_node_by_name (vm, (u8 *) "esp-encrypt");
563   ASSERT (node);
564   im->esp_encrypt_node_index = node->index;
565
566   node = vlib_get_node_by_name (vm, (u8 *) "esp-decrypt");
567   ASSERT (node);
568   im->esp_decrypt_node_index = node->index;
569
570   im->esp_encrypt_next_index = IPSEC_OUTPUT_NEXT_ESP_ENCRYPT;
571   im->esp_decrypt_next_index = IPSEC_INPUT_NEXT_ESP_DECRYPT;
572
573   im->cb.check_support_cb = ipsec_check_support;
574
575   if ((error = vlib_call_init_function (vm, ipsec_cli_init)))
576     return error;
577
578   if ((error = vlib_call_init_function (vm, ipsec_tunnel_if_init)))
579     return error;
580
581   esp_init ();
582
583   if ((error = ikev2_init (vm)))
584     return error;
585
586   return 0;
587 }
588
589 VLIB_INIT_FUNCTION (ipsec_init);
590
591 /*
592  * fd.io coding-style-patch-verification: ON
593  *
594  * Local Variables:
595  * eval: (c-set-style "gnu")
596  * End:
597  */