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