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