fc0bf85a51721c6290971933ea58f3ed8d7a2f04
[vpp.git] / src / vnet / ipsec / ipsec_itf.c
1 /*
2  * ipsec_itf.c: IPSec dedicated interface type
3  *
4  * Copyright (c) 2020 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/ip/ip.h>
19 #include <vnet/ipsec/ipsec_itf.h>
20 #include <vnet/ipsec/ipsec_tun.h>
21 #include <vnet/ipsec/ipsec.h>
22 #include <vnet/adj/adj_midchain.h>
23 #include <vnet/ethernet/mac_address.h>
24
25 /* bitmap of Allocated IPSEC_ITF instances */
26 static uword *ipsec_itf_instances;
27
28 /* pool of interfaces */
29 static ipsec_itf_t *ipsec_itf_pool;
30
31 static u32 *ipsec_itf_index_by_sw_if_index;
32
33 ipsec_itf_t *
34 ipsec_itf_get (index_t ii)
35 {
36   return (pool_elt_at_index (ipsec_itf_pool, ii));
37 }
38
39 u32
40 ipsec_itf_count (void)
41 {
42   return (pool_elts (ipsec_itf_pool));
43 }
44
45 static ipsec_itf_t *
46 ipsec_itf_find_by_sw_if_index (u32 sw_if_index)
47 {
48   if (vec_len (ipsec_itf_index_by_sw_if_index) <= sw_if_index)
49     return NULL;
50   u32 ti = ipsec_itf_index_by_sw_if_index[sw_if_index];
51   if (ti == ~0)
52     return NULL;
53   return pool_elt_at_index (ipsec_itf_pool, ti);
54 }
55
56 static u8 *
57 format_ipsec_itf_name (u8 * s, va_list * args)
58 {
59   u32 dev_instance = va_arg (*args, u32);
60   return format (s, "ipsec%d", dev_instance);
61 }
62
63 void
64 ipsec_itf_adj_unstack (adj_index_t ai)
65 {
66   adj_midchain_delegate_unstack (ai);
67 }
68
69 void
70 ipsec_itf_adj_stack (adj_index_t ai, u32 sai)
71 {
72   const vnet_hw_interface_t *hw;
73
74   hw = vnet_get_sup_hw_interface (vnet_get_main (), adj_get_sw_if_index (ai));
75
76   if (hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
77     {
78       const ipsec_sa_t *sa;
79       fib_prefix_t dst;
80
81       sa = ipsec_sa_get (sai);
82       ip_address_to_fib_prefix (&sa->tunnel.t_dst, &dst);
83       adj_midchain_delegate_stack (ai, sa->tunnel.t_fib_index, &dst);
84     }
85   else
86     adj_midchain_delegate_unstack (ai);
87 }
88
89 static adj_walk_rc_t
90 ipsec_itf_adj_stack_cb (adj_index_t ai, void *arg)
91 {
92   ipsec_tun_protect_t *itp = arg;
93
94   ipsec_itf_adj_stack (ai, itp->itp_out_sa);
95
96   return (ADJ_WALK_RC_CONTINUE);
97 }
98
99 static void
100 ipsec_itf_restack (index_t itpi, const ipsec_itf_t * itf)
101 {
102   ipsec_tun_protect_t *itp;
103   fib_protocol_t proto;
104
105   itp = ipsec_tun_protect_get (itpi);
106
107   /*
108    * walk all the adjacencies on the interface and restack them
109    */
110   FOR_EACH_FIB_IP_PROTOCOL (proto)
111   {
112     adj_nbr_walk (itf->ii_sw_if_index, proto, ipsec_itf_adj_stack_cb, itp);
113   }
114 }
115
116 static walk_rc_t
117 ipsec_tun_protect_walk_state_change (index_t itpi, void *arg)
118 {
119   const ipsec_itf_t *itf = arg;
120
121   ipsec_itf_restack (itpi, itf);
122
123   return (WALK_CONTINUE);
124 }
125
126 static clib_error_t *
127 ipsec_itf_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
128 {
129   vnet_hw_interface_t *hi;
130   ipsec_itf_t *itf;
131   u32 hw_flags;
132
133   hi = vnet_get_hw_interface (vnm, hw_if_index);
134   hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
135               VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
136   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
137
138   itf = ipsec_itf_find_by_sw_if_index (hi->sw_if_index);
139
140   if (itf)
141     ipsec_tun_protect_walk_itf (itf->ii_sw_if_index,
142                                 ipsec_tun_protect_walk_state_change, itf);
143
144   return (NULL);
145 }
146
147 static int
148 ipsec_itf_tunnel_desc (u32 sw_if_index,
149                        ip46_address_t * src, ip46_address_t * dst, u8 * is_l2)
150 {
151   ip46_address_reset (src);
152   ip46_address_reset (dst);
153   *is_l2 = 0;
154
155   return (0);
156 }
157
158 static u8 *
159 ipsec_itf_build_rewrite (void)
160 {
161   /*
162    * passing the adj code a NULL rewrite means 'i don't have one cos
163    * t'other end is unresolved'. That's not the case here. For the ipsec
164    * tunnel there are just no bytes of encap to apply in the adj.
165    * So return a zero length rewrite. Encap will be added by a tunnel mode SA.
166    */
167   u8 *rewrite = NULL;
168
169   vec_validate (rewrite, 0);
170   vec_reset_length (rewrite);
171
172   return (rewrite);
173 }
174
175 static u8 *
176 ipsec_itf_build_rewrite_i (vnet_main_t * vnm,
177                            u32 sw_if_index,
178                            vnet_link_t link_type, const void *dst_address)
179 {
180   return (ipsec_itf_build_rewrite ());
181 }
182
183 void
184 ipsec_itf_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
185 {
186   adj_nbr_midchain_update_rewrite
187     (ai, NULL, NULL, ADJ_FLAG_MIDCHAIN_IP_STACK, ipsec_itf_build_rewrite ());
188 }
189
190 /* *INDENT-OFF* */
191 VNET_DEVICE_CLASS (ipsec_itf_device_class) = {
192   .name = "IPSEC Tunnel",
193   .format_device_name = format_ipsec_itf_name,
194   .admin_up_down_function = ipsec_itf_admin_up_down,
195   .ip_tun_desc = ipsec_itf_tunnel_desc,
196 };
197
198 VNET_HW_INTERFACE_CLASS(ipsec_hw_interface_class) = {
199   .name = "IPSec",
200   .build_rewrite = ipsec_itf_build_rewrite_i,
201   .update_adjacency = ipsec_itf_update_adj,
202   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
203 };
204 VNET_HW_INTERFACE_CLASS(ipsec_p2mp_hw_interface_class) = {
205   .name = "IPSec",
206   .build_rewrite = ipsec_itf_build_rewrite_i,
207   .update_adjacency = ipsec_itf_update_adj,
208   .flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA,
209 };
210 /* *INDENT-ON* */
211
212 /*
213  * Maintain a bitmap of allocated ipsec_itf instance numbers.
214  */
215 #define IPSEC_ITF_MAX_INSTANCE          (16 * 1024)
216
217 static u32
218 ipsec_itf_instance_alloc (u32 want)
219 {
220   /*
221    * Check for dynamically allocated instance number.
222    */
223   if (~0 == want)
224     {
225       u32 bit;
226
227       bit = clib_bitmap_first_clear (ipsec_itf_instances);
228       if (bit >= IPSEC_ITF_MAX_INSTANCE)
229         {
230           return ~0;
231         }
232       ipsec_itf_instances = clib_bitmap_set (ipsec_itf_instances, bit, 1);
233       return bit;
234     }
235
236   /*
237    * In range?
238    */
239   if (want >= IPSEC_ITF_MAX_INSTANCE)
240     {
241       return ~0;
242     }
243
244   /*
245    * Already in use?
246    */
247   if (clib_bitmap_get (ipsec_itf_instances, want))
248     {
249       return ~0;
250     }
251
252   /*
253    * Grant allocation request.
254    */
255   ipsec_itf_instances = clib_bitmap_set (ipsec_itf_instances, want, 1);
256
257   return want;
258 }
259
260 static int
261 ipsec_itf_instance_free (u32 instance)
262 {
263   if (instance >= IPSEC_ITF_MAX_INSTANCE)
264     {
265       return -1;
266     }
267
268   if (clib_bitmap_get (ipsec_itf_instances, instance) == 0)
269     {
270       return -1;
271     }
272
273   ipsec_itf_instances = clib_bitmap_set (ipsec_itf_instances, instance, 0);
274   return 0;
275 }
276
277 int
278 ipsec_itf_create (u32 user_instance, tunnel_mode_t mode, u32 * sw_if_indexp)
279 {
280   vnet_main_t *vnm = vnet_get_main ();
281   u32 instance, hw_if_index;
282   vnet_hw_interface_t *hi;
283   ipsec_itf_t *ipsec_itf;
284
285   ASSERT (sw_if_indexp);
286
287   *sw_if_indexp = (u32) ~ 0;
288
289   /*
290    * Allocate a ipsec_itf instance.  Either select on dynamically
291    * or try to use the desired user_instance number.
292    */
293   instance = ipsec_itf_instance_alloc (user_instance);
294   if (instance == ~0)
295     return VNET_API_ERROR_INVALID_REGISTRATION;
296
297   pool_get (ipsec_itf_pool, ipsec_itf);
298
299   /* tunnel index (or instance) */
300   u32 t_idx = ipsec_itf - ipsec_itf_pool;
301
302   ipsec_itf->ii_mode = mode;
303   ipsec_itf->ii_user_instance = instance;
304
305   hw_if_index = vnet_register_interface (vnm,
306                                          ipsec_itf_device_class.index,
307                                          ipsec_itf->ii_user_instance,
308                                          (mode == TUNNEL_MODE_P2P ?
309                                           ipsec_hw_interface_class.index :
310                                           ipsec_p2mp_hw_interface_class.index),
311                                          t_idx);
312
313   hi = vnet_get_hw_interface (vnm, hw_if_index);
314   vnet_sw_interface_set_mtu (vnm, hi->sw_if_index, 9000);
315
316   vec_validate_init_empty (ipsec_itf_index_by_sw_if_index, hi->sw_if_index,
317                            INDEX_INVALID);
318   ipsec_itf_index_by_sw_if_index[hi->sw_if_index] = t_idx;
319
320   ipsec_itf->ii_sw_if_index = *sw_if_indexp = hi->sw_if_index;
321
322   return 0;
323 }
324
325 int
326 ipsec_itf_delete (u32 sw_if_index)
327 {
328   vnet_main_t *vnm = vnet_get_main ();
329
330   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
331     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
332
333   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
334   if (hw == 0 || hw->dev_class_index != ipsec_itf_device_class.index)
335     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
336
337   ipsec_itf_t *ipsec_itf;
338   ipsec_itf = ipsec_itf_find_by_sw_if_index (sw_if_index);
339   if (NULL == ipsec_itf)
340     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
341
342   if (ipsec_itf_instance_free (hw->dev_instance) < 0)
343     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
344
345   vnet_reset_interface_l3_output_node (vnm->vlib_main, sw_if_index);
346
347   vnet_delete_hw_interface (vnm, hw->hw_if_index);
348   pool_put (ipsec_itf_pool, ipsec_itf);
349
350   return 0;
351 }
352
353 void
354 ipsec_itf_walk (ipsec_itf_walk_cb_t cb, void *ctx)
355 {
356   ipsec_itf_t *itf;
357
358   pool_foreach (itf, ipsec_itf_pool)
359     {
360       if (WALK_CONTINUE != cb (itf, ctx))
361         break;
362     }
363 }
364
365 static clib_error_t *
366 ipsec_itf_create_cli (vlib_main_t * vm,
367                       unformat_input_t * input, vlib_cli_command_t * cmd)
368 {
369   unformat_input_t _line_input, *line_input = &_line_input;
370   u32 instance, sw_if_index;
371   clib_error_t *error;
372   mac_address_t mac;
373   int rv;
374
375   error = NULL;
376   instance = sw_if_index = ~0;
377   mac_address_set_zero (&mac);
378
379   if (unformat_user (input, unformat_line_input, line_input))
380     {
381       while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
382         {
383           if (unformat (line_input, "instance %d", &instance))
384             ;
385           else
386             {
387               error = clib_error_return (0, "unknown input: %U",
388                                          format_unformat_error, line_input);
389               break;
390             }
391         }
392
393       unformat_free (line_input);
394
395       if (error)
396         return error;
397     }
398
399   rv = ipsec_itf_create (instance, TUNNEL_MODE_P2P, &sw_if_index);
400
401   if (rv)
402     return clib_error_return (0, "iPSec interface create failed");
403
404   vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (),
405                    sw_if_index);
406   return 0;
407 }
408
409 /*?
410  * Create a IPSec interface.
411  *
412  * @cliexpar
413  * The following two command syntaxes are equivalent:
414  * @cliexcmd{ipsec itf create [instance <instance>]}
415  * Example of how to create a ipsec interface:
416  * @cliexcmd{ipsec itf create}
417 ?*/
418 /* *INDENT-OFF* */
419 VLIB_CLI_COMMAND (ipsec_itf_create_command, static) = {
420   .path = "ipsec itf create",
421   .short_help = "ipsec itf create [instance <instance>]",
422   .function = ipsec_itf_create_cli,
423 };
424 /* *INDENT-ON* */
425
426 static clib_error_t *
427 ipsec_itf_delete_cli (vlib_main_t * vm,
428                       unformat_input_t * input, vlib_cli_command_t * cmd)
429 {
430   vnet_main_t *vnm;
431   u32 sw_if_index;
432   int rv;
433
434   vnm = vnet_get_main ();
435   sw_if_index = ~0;
436
437   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
438     {
439       if (unformat
440           (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
441         ;
442       else
443         break;
444     }
445
446   if (~0 != sw_if_index)
447     {
448       rv = ipsec_itf_delete (sw_if_index);
449
450       if (rv)
451         return clib_error_return (0, "ipsec interface delete failed");
452     }
453   else
454     return clib_error_return (0, "no such interface: %U",
455                               format_unformat_error, input);
456
457   return 0;
458 }
459
460 /*?
461  * Delete a IPSEC_ITF interface.
462  *
463  * @cliexpar
464  * The following two command syntaxes are equivalent:
465  * @cliexcmd{ipsec itf delete <interface>}
466  * Example of how to create a ipsec_itf interface:
467  * @cliexcmd{ipsec itf delete ipsec0}
468 ?*/
469 /* *INDENT-OFF* */
470 VLIB_CLI_COMMAND (ipsec_itf_delete_command, static) = {
471   .path = "ipsec itf delete",
472   .short_help = "ipsec itf delete <interface>",
473   .function = ipsec_itf_delete_cli,
474 };
475 /* *INDENT-ON* */
476
477 static clib_error_t *
478 ipsec_interface_show (vlib_main_t * vm,
479                       unformat_input_t * input, vlib_cli_command_t * cmd)
480 {
481   index_t ii;
482
483   /* *INDENT-OFF* */
484   pool_foreach_index (ii, ipsec_itf_pool)
485    {
486     vlib_cli_output (vm, "%U", format_ipsec_itf, ii);
487   }
488   /* *INDENT-ON* */
489
490   return NULL;
491 }
492
493 /**
494  * show IPSEC tunnel protection hash tables
495  */
496 /* *INDENT-OFF* */
497 VLIB_CLI_COMMAND (ipsec_interface_show_node, static) =
498 {
499   .path = "show ipsec interface",
500   .function = ipsec_interface_show,
501   .short_help =  "show ipsec interface",
502 };
503 /* *INDENT-ON* */
504
505 /*
506  * fd.io coding-style-patch-verification: ON
507  *
508  * Local Variables:
509  * eval: (c-set-style "gnu")
510  * End:
511  */