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