Line data Source code
1 : /*
2 : * sixrd.c - 6RD specific functions (RFC5969)
3 : *
4 : * Copyright (c) 2018 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 : /**
19 : * This code supports the following sixrd modes:
20 : *
21 : * 32 EA bits (Complete IPv4 address is embedded):
22 : * ea_bits_len = 32
23 : * IPv4 suffix is embedded:
24 : * ea_bits_len = < 32
25 : * No embedded address bits (1:1 mode):
26 : * ea_bits_len = 0
27 : */
28 :
29 : #include "ipip.h"
30 : #include <vlibapi/api.h>
31 : #include <vlibmemory/api.h>
32 : #include <vnet/adj/adj.h>
33 : #include <vnet/adj/adj_delegate.h>
34 : #include <vnet/adj/adj_midchain.h>
35 : #include <vnet/dpo/lookup_dpo.h>
36 : #include <vnet/fib/fib_table.h>
37 : #include <vnet/fib/fib_entry_track.h>
38 : #include <vnet/fib/ip6_fib.h>
39 : #include <vnet/plugin/plugin.h>
40 :
41 : extern vlib_node_registration_t ip4_sixrd_node;
42 :
43 : /**
44 : * Adj delegate data
45 : */
46 : typedef struct sixrd_adj_delegate_t_
47 : {
48 : u32 adj_index;
49 : fib_node_t sixrd_node;
50 : fib_node_index_t sixrd_fib_entry_index;
51 : u32 sixrd_sibling;
52 : } sixrd_adj_delegate_t;
53 :
54 : /**
55 : * Pool of delegate structs
56 : */
57 : static sixrd_adj_delegate_t *sixrd_adj_delegate_pool;
58 :
59 : /**
60 : * Adj delegate registered type
61 : */
62 : static adj_delegate_type_t sixrd_adj_delegate_type;
63 :
64 : /**
65 : * FIB node registered type
66 : */
67 : static fib_node_type_t sixrd_fib_node_type;
68 :
69 : static inline sixrd_adj_delegate_t *
70 2 : sixrd_adj_from_base (adj_delegate_t * ad)
71 : {
72 2 : if (ad == NULL)
73 1 : return (NULL);
74 1 : return (pool_elt_at_index (sixrd_adj_delegate_pool, ad->ad_index));
75 : }
76 :
77 : static inline const sixrd_adj_delegate_t *
78 0 : sixrd_adj_from_const_base (const adj_delegate_t * ad)
79 : {
80 0 : if (ad == NULL)
81 : {
82 0 : return (NULL);
83 : }
84 0 : return (pool_elt_at_index (sixrd_adj_delegate_pool, ad->ad_index));
85 : }
86 :
87 : static void
88 30 : sixrd_fixup (vlib_main_t * vm,
89 : const ip_adjacency_t * adj, vlib_buffer_t * b0, const void *data)
90 : {
91 30 : ip4_header_t *ip4 = vlib_buffer_get_current (b0);
92 30 : ip6_header_t *ip6 = vlib_buffer_get_current (b0) + sizeof (ip4_header_t);
93 30 : const ipip_tunnel_t *t = data;
94 :
95 30 : ip4->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
96 30 : ip4->dst_address.as_u32 =
97 30 : sixrd_get_addr_net (t, ip6->dst_address.as_u64[0]);
98 30 : ip4->checksum = ip4_header_checksum (ip4);
99 30 : }
100 :
101 : static void
102 10 : ip6ip_fixup (vlib_main_t * vm,
103 : const ip_adjacency_t * adj, vlib_buffer_t * b0, const void *data)
104 : {
105 10 : const ipip_tunnel_t *t = data;
106 10 : ip4_header_t *ip4 = vlib_buffer_get_current (b0);
107 10 : ip4->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
108 10 : ip4->dst_address.as_u32 =
109 10 : sixrd_get_addr_net (t, adj->sub_type.nbr.next_hop.as_u64[0]);
110 10 : ip4->checksum = ip4_header_checksum (ip4);
111 10 : }
112 :
113 : static u8 *
114 12 : sixrd_build_rewrite (vnet_main_t * vnm, u32 sw_if_index,
115 : vnet_link_t link_type, const void *dst_address)
116 : {
117 12 : u8 *rewrite = NULL;
118 : ipip_tunnel_t *t;
119 :
120 12 : t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
121 12 : if (!t)
122 0 : return 0;
123 :
124 12 : vec_validate (rewrite, sizeof (ip4_header_t) - 1);
125 12 : ip4_header_t *ip4 = (ip4_header_t *) rewrite;
126 12 : ip4->ip_version_and_header_length = 0x45;
127 12 : ip4->ttl = 64;
128 12 : ip4->protocol = IP_PROTOCOL_IPV6;
129 : /* fixup ip4 header length and checksum after-the-fact */
130 12 : ip4->src_address.as_u32 = t->tunnel_src.ip4.as_u32;
131 12 : ip4->dst_address.as_u32 = 0;
132 12 : ip4->checksum = ip4_header_checksum (ip4);
133 :
134 12 : return rewrite;
135 : }
136 :
137 : static void
138 3 : ip6ip_tunnel_stack (adj_index_t ai, u32 fib_entry_index)
139 : {
140 3 : ip_adjacency_t *adj = adj_get (ai);
141 : ipip_tunnel_t *t;
142 3 : u32 sw_if_index = adj->rewrite_header.sw_if_index;
143 :
144 3 : t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
145 3 : if (!t)
146 0 : return;
147 :
148 : /*
149 : * find the adjacency that is contributed by the FIB entry
150 : * that this tunnel resolves via, and use it as the next adj
151 : * in the midchain
152 : */
153 3 : if (vnet_hw_interface_get_flags (vnet_get_main (), t->hw_if_index) &
154 : VNET_HW_INTERFACE_FLAG_LINK_UP)
155 : {
156 3 : adj_nbr_midchain_stack_on_fib_entry (ai,
157 : fib_entry_index,
158 : FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
159 : }
160 : else
161 : {
162 0 : adj_nbr_midchain_unstack (ai);
163 : }
164 : }
165 :
166 : static void
167 11 : sixrd_tunnel_stack (adj_index_t ai, u32 fib_index)
168 : {
169 11 : dpo_id_t dpo = DPO_INVALID;
170 11 : ip_adjacency_t *adj = adj_get (ai);
171 11 : u32 sw_if_index = adj->rewrite_header.sw_if_index;
172 :
173 11 : ipip_tunnel_t *t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
174 11 : if (!t)
175 0 : return;
176 :
177 11 : lookup_dpo_add_or_lock_w_fib_index (fib_index, DPO_PROTO_IP4,
178 : LOOKUP_UNICAST, LOOKUP_INPUT_DST_ADDR,
179 : LOOKUP_TABLE_FROM_CONFIG, &dpo);
180 11 : adj_nbr_midchain_stack (ai, &dpo);
181 11 : dpo_reset (&dpo);
182 : }
183 :
184 : static void
185 12 : sixrd_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
186 : {
187 12 : ip_adjacency_t *adj = adj_get (ai);
188 12 : ipip_tunnel_t *t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
189 :
190 : /* Not our tunnel */
191 12 : if (!t)
192 0 : return;
193 12 : if (IP_LOOKUP_NEXT_BCAST == adj->lookup_next_index)
194 : {
195 11 : adj_nbr_midchain_update_rewrite (ai, sixrd_fixup, t, ADJ_FLAG_NONE,
196 : sixrd_build_rewrite (vnm, sw_if_index,
197 11 : adj_get_link_type
198 : (ai), NULL));
199 11 : sixrd_tunnel_stack (ai, t->fib_index);
200 : }
201 : else
202 : {
203 : sixrd_adj_delegate_t *sixrd_ad;
204 : ip4_address_t da4;
205 :
206 1 : da4.as_u32 =
207 1 : sixrd_get_addr_net (t, adj->sub_type.nbr.next_hop.as_u64[0]);
208 :
209 1 : fib_prefix_t pfx = {
210 : .fp_proto = FIB_PROTOCOL_IP4,
211 : .fp_len = 32,
212 : .fp_addr = {
213 : .ip4 = da4,
214 : }
215 : ,
216 : };
217 :
218 1 : adj_nbr_midchain_update_rewrite (ai, ip6ip_fixup, t, ADJ_FLAG_NONE,
219 : sixrd_build_rewrite (vnm, sw_if_index,
220 1 : adj_get_link_type
221 : (ai), NULL));
222 :
223 1 : sixrd_ad =
224 1 : sixrd_adj_from_base (adj_delegate_get (adj, sixrd_adj_delegate_type));
225 1 : if (sixrd_ad == NULL)
226 : {
227 1 : pool_get (sixrd_adj_delegate_pool, sixrd_ad);
228 1 : fib_node_init (&sixrd_ad->sixrd_node, sixrd_fib_node_type);
229 1 : sixrd_ad->adj_index = ai;
230 2 : sixrd_ad->sixrd_fib_entry_index =
231 1 : fib_entry_track (t->fib_index, &pfx,
232 : sixrd_fib_node_type,
233 1 : sixrd_ad - sixrd_adj_delegate_pool,
234 1 : &sixrd_ad->sixrd_sibling);
235 :
236 1 : adj_delegate_add (adj, sixrd_adj_delegate_type,
237 1 : sixrd_ad - sixrd_adj_delegate_pool);
238 :
239 1 : ip6ip_tunnel_stack (ai, sixrd_ad->sixrd_fib_entry_index);
240 : }
241 : }
242 : }
243 :
244 : clib_error_t *
245 22 : sixrd_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
246 : {
247 : /* Always up */
248 22 : vnet_hw_interface_set_flags (vnm, hw_if_index,
249 : VNET_HW_INTERFACE_FLAG_LINK_UP);
250 22 : return /* no error */ 0;
251 : }
252 :
253 : /* *INDENT-OFF* */
254 8063 : VNET_HW_INTERFACE_CLASS(sixrd_hw_interface_class) = {
255 : .name = "ip6ip-6rd",
256 : .build_rewrite = sixrd_build_rewrite,
257 : .update_adjacency = sixrd_update_adj,
258 : };
259 :
260 12095 : VNET_DEVICE_CLASS(sixrd_device_class) = {
261 : .name = "ip6ip-6rd",
262 : .admin_up_down_function = sixrd_interface_admin_up_down,
263 : #ifdef SOON
264 : .clear counter = 0;
265 : #endif
266 : }
267 : ;
268 : /* *INDENT-ON* */
269 :
270 : int
271 11 : sixrd_add_tunnel (ip6_address_t * ip6_prefix, u8 ip6_prefix_len,
272 : ip4_address_t * ip4_prefix, u8 ip4_prefix_len,
273 : ip4_address_t * ip4_src, bool security_check,
274 : u32 ip4_fib_index, u32 ip6_fib_index, u32 * sw_if_index)
275 : {
276 11 : ipip_main_t *gm = &ipip_main;
277 : ipip_tunnel_t *t;
278 :
279 11 : if ((ip6_prefix_len + 32 - ip4_prefix_len) > 64)
280 0 : return VNET_API_ERROR_INVALID_VALUE;
281 :
282 : /* Tunnel already configured */
283 11 : ip46_address_t src = ip46_address_initializer, dst =
284 : ip46_address_initializer;
285 11 : ip_set (&src, ip4_src, true);
286 : ipip_tunnel_key_t key;
287 :
288 11 : ipip_mk_key_i (IPIP_TRANSPORT_IP4, IPIP_MODE_6RD, &src, &dst, ip4_fib_index,
289 : &key);
290 :
291 11 : t = ipip_tunnel_db_find (&key);
292 11 : if (t)
293 0 : return VNET_API_ERROR_IF_ALREADY_EXISTS;
294 :
295 : /* Get tunnel index */
296 11 : pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES);
297 11 : clib_memset (t, 0, sizeof (*t));
298 11 : u32 t_idx = t - gm->tunnels; /* tunnel index (or instance) */
299 :
300 : /* Init tunnel struct */
301 11 : t->mode = IPIP_MODE_6RD;
302 11 : t->sixrd.ip4_prefix.as_u32 = ip4_prefix->as_u32;
303 11 : t->sixrd.ip4_prefix_len = ip4_prefix_len;
304 11 : t->sixrd.ip6_prefix = *ip6_prefix;
305 11 : t->sixrd.ip6_prefix_len = ip6_prefix_len;
306 11 : t->sixrd.ip6_fib_index = ip6_fib_index;
307 11 : t->tunnel_src = src;
308 11 : t->sixrd.security_check = security_check;
309 11 : t->sixrd.shift =
310 11 : (ip4_prefix_len < 32) ? 64 - ip6_prefix_len - (32 - ip4_prefix_len) : 0;
311 :
312 : /* Create interface */
313 : u32 hw_if_index =
314 11 : vnet_register_interface (vnet_get_main (), sixrd_device_class.index,
315 : t_idx,
316 : sixrd_hw_interface_class.index, t_idx);
317 :
318 : /* Default the interface to up and enable IPv6 (payload) */
319 : vnet_hw_interface_t *hi =
320 11 : vnet_get_hw_interface (vnet_get_main (), hw_if_index);
321 11 : t->hw_if_index = hw_if_index;
322 11 : t->fib_index = ip4_fib_index;
323 11 : t->sw_if_index = hi->sw_if_index;
324 11 : t->dev_instance = t_idx;
325 11 : t->user_instance = t_idx;
326 :
327 11 : vnet_sw_interface_set_mtu (vnet_get_main (), t->sw_if_index, 1480);
328 11 : vnet_set_interface_l3_output_node (gm->vlib_main, hi->sw_if_index,
329 : (u8 *) "tunnel-output");
330 :
331 11 : ipip_tunnel_db_add (t, &key);
332 :
333 18 : vec_validate_init_empty (gm->tunnel_index_by_sw_if_index, hi->sw_if_index,
334 : ~0);
335 11 : gm->tunnel_index_by_sw_if_index[hi->sw_if_index] = t_idx;
336 :
337 11 : vnet_hw_interface_set_flags (vnet_get_main (), hw_if_index,
338 : VNET_HW_INTERFACE_FLAG_LINK_UP);
339 11 : vnet_sw_interface_set_flags (vnet_get_main (), hi->sw_if_index,
340 : VNET_SW_INTERFACE_FLAG_ADMIN_UP);
341 11 : ip6_sw_interface_enable_disable (t->sw_if_index, true);
342 :
343 : /* Create IPv6 route/adjacency */
344 : /* *INDENT-OFF* */
345 11 : fib_prefix_t pfx6 = {
346 : .fp_proto = FIB_PROTOCOL_IP6,
347 11 : .fp_len = t->sixrd.ip6_prefix_len,
348 : .fp_addr = {
349 11 : .ip6 = t->sixrd.ip6_prefix,
350 : },
351 : };
352 : /* *INDENT-ON* */
353 :
354 11 : fib_table_lock (ip6_fib_index, FIB_PROTOCOL_IP6, FIB_SOURCE_6RD);
355 11 : fib_table_entry_update_one_path (ip6_fib_index, &pfx6, FIB_SOURCE_6RD,
356 : FIB_ENTRY_FLAG_ATTACHED, DPO_PROTO_IP6,
357 11 : &ADJ_BCAST_ADDR, t->sw_if_index, ~0, 1,
358 : NULL, FIB_ROUTE_PATH_FLAG_NONE);
359 :
360 11 : *sw_if_index = t->sw_if_index;
361 :
362 11 : if (!gm->ip4_protocol_registered)
363 : {
364 : vlib_node_t *ipip4_input =
365 11 : vlib_get_node_by_name (gm->vlib_main, (u8 *) "ipip4-input");
366 11 : ASSERT (ipip4_input);
367 11 : ip4_register_protocol (IP_PROTOCOL_IPV6, ipip4_input->index);
368 : }
369 11 : return 0;
370 : }
371 :
372 : /*
373 : * sixrd_del_tunnel
374 : */
375 : int
376 11 : sixrd_del_tunnel (u32 sw_if_index)
377 : {
378 11 : ipip_main_t *gm = &ipip_main;
379 11 : ipip_tunnel_t *t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
380 : ipip_tunnel_key_t key;
381 :
382 11 : if (!t)
383 : {
384 0 : clib_warning ("SIXRD tunnel delete: tunnel does not exist: %d",
385 : sw_if_index);
386 0 : return -1;
387 : }
388 :
389 : /* *INDENT-OFF* */
390 11 : fib_prefix_t pfx6 = {
391 : .fp_proto = FIB_PROTOCOL_IP6,
392 11 : .fp_len = t->sixrd.ip6_prefix_len,
393 : .fp_addr = {
394 : .ip6 = t->sixrd.ip6_prefix,
395 : },
396 : };
397 : /* *INDENT-ON* */
398 :
399 11 : fib_table_entry_path_remove (t->sixrd.ip6_fib_index, &pfx6,
400 : FIB_SOURCE_6RD,
401 : DPO_PROTO_IP6,
402 : &ADJ_BCAST_ADDR, t->sw_if_index, ~0, 1,
403 : FIB_ROUTE_PATH_FLAG_NONE);
404 11 : fib_table_unlock (t->sixrd.ip6_fib_index, FIB_PROTOCOL_IP6, FIB_SOURCE_6RD);
405 :
406 11 : vnet_sw_interface_set_flags (vnet_get_main (), t->sw_if_index,
407 : 0 /* down */ );
408 11 : vnet_reset_interface_l3_output_node (gm->vlib_main, t->sw_if_index);
409 11 : ip6_sw_interface_enable_disable (t->sw_if_index, false);
410 11 : gm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0;
411 :
412 11 : vnet_delete_hw_interface (vnet_get_main (), t->hw_if_index);
413 11 : ipip_mk_key (t, &key);
414 11 : ipip_tunnel_db_remove (t, &key);
415 11 : pool_put (gm->tunnels, t);
416 :
417 11 : return 0;
418 : }
419 :
420 : static void
421 1 : sixrd_adj_delegate_adj_deleted (adj_delegate_t * aed)
422 : {
423 : sixrd_adj_delegate_t *sixrd_ad;
424 :
425 1 : sixrd_ad = sixrd_adj_from_base (aed);
426 1 : fib_entry_untrack (sixrd_ad->sixrd_fib_entry_index,
427 : sixrd_ad->sixrd_sibling);
428 1 : pool_put (sixrd_adj_delegate_pool, sixrd_ad);
429 1 : }
430 :
431 : static u8 *
432 0 : sixrd_adj_delegate_format (const adj_delegate_t * aed, u8 * s)
433 : {
434 : const sixrd_adj_delegate_t *sixrd_ad;
435 :
436 0 : sixrd_ad = sixrd_adj_from_const_base (aed);
437 0 : s = format (s, "SIXRD:[fib-entry:%d]", sixrd_ad->sixrd_fib_entry_index);
438 :
439 0 : return (s);
440 : }
441 :
442 : static void
443 0 : sixrd_fib_node_last_lock_gone (fib_node_t * node)
444 : {
445 : /* top of the dependency tree, locks not managed here. */
446 0 : }
447 :
448 : static sixrd_adj_delegate_t *
449 2 : sixrd_adj_delegate_from_fib_node (fib_node_t * node)
450 : {
451 2 : return ((sixrd_adj_delegate_t *) (((char *) node) -
452 : STRUCT_OFFSET_OF (sixrd_adj_delegate_t,
453 : sixrd_node)));
454 : }
455 :
456 : static fib_node_back_walk_rc_t
457 2 : sixrd_fib_node_back_walk_notify (fib_node_t * node,
458 : fib_node_back_walk_ctx_t * ctx)
459 : {
460 : sixrd_adj_delegate_t *sixrd_ad;
461 :
462 2 : sixrd_ad = sixrd_adj_delegate_from_fib_node (node);
463 2 : ip6ip_tunnel_stack (sixrd_ad->adj_index, sixrd_ad->sixrd_fib_entry_index);
464 :
465 2 : return (FIB_NODE_BACK_WALK_CONTINUE);
466 : }
467 :
468 : /**
469 : * Function definition to get a FIB node from its index
470 : */
471 : static fib_node_t *
472 2 : sixrd_fib_node_get (fib_node_index_t index)
473 : {
474 : sixrd_adj_delegate_t *sixrd_ad;
475 :
476 2 : sixrd_ad = pool_elt_at_index (sixrd_adj_delegate_pool, index);
477 :
478 2 : return (&sixrd_ad->sixrd_node);
479 : }
480 :
481 : /**
482 : * VFT registered with the adjacency delegate
483 : */
484 : const static adj_delegate_vft_t sixrd_adj_delegate_vft = {
485 : .adv_adj_deleted = sixrd_adj_delegate_adj_deleted,
486 : .adv_format = sixrd_adj_delegate_format,
487 : };
488 :
489 : /**
490 : * VFT registered with the FIB node for the adj delegate
491 : */
492 : const static fib_node_vft_t sixrd_fib_node_vft = {
493 : .fnv_get = sixrd_fib_node_get,
494 : .fnv_last_lock = sixrd_fib_node_last_lock_gone,
495 : .fnv_back_walk = sixrd_fib_node_back_walk_notify,
496 : };
497 :
498 : static clib_error_t *
499 575 : sixrd_init (vlib_main_t * vm)
500 : {
501 575 : clib_error_t *error = 0;
502 :
503 : /* Make sure the IPIP tunnel subsystem is initialised */
504 575 : error = vlib_call_init_function (vm, ipip_init);
505 :
506 575 : sixrd_adj_delegate_type =
507 575 : adj_delegate_register_new_type (&sixrd_adj_delegate_vft);
508 575 : sixrd_fib_node_type =
509 575 : fib_node_register_new_type ("sixrd", &sixrd_fib_node_vft);
510 :
511 575 : return error;
512 : }
513 :
514 62783 : VLIB_INIT_FUNCTION (sixrd_init);
515 :
516 : /*
517 : * fd.io coding-style-patch-verification: ON
518 : *
519 : * Local Variables:
520 : * eval: (c-set-style "gnu")
521 : * End:
522 : */
|