SIXRD: fix post introduction of the directed subnet broadcast
[vpp.git] / src / vnet / ipip / sixrd.c
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/ip6_fib.h>
38 #include <vnet/plugin/plugin.h>
39
40 extern vlib_node_registration_t ip4_sixrd_node;
41
42 /**
43  * Adj delegate data
44  */
45 typedef 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  */
56 static sixrd_adj_delegate_t *sixrd_adj_delegate_pool;
57
58 /**
59  * Adj delegate registered type
60  */
61 static adj_delegate_type_t sixrd_adj_delegate_type;
62
63 /**
64  * FIB node registered type
65  */
66 static fib_node_type_t sixrd_fib_node_type;
67
68 static inline sixrd_adj_delegate_t *
69 sixrd_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
76 static inline const sixrd_adj_delegate_t *
77 sixrd_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
86 static void
87 sixrd_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
100 static void
101 ip6ip_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
112 static u8 *
113 sixrd_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
136 static void
137 ip6ip_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
165 static void
166 sixrd_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
182
183 static void
184 sixrd_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
189   /* Not our tunnel */
190   if (!t)
191     return;
192   if (IP_LOOKUP_NEXT_BCAST == adj->lookup_next_index)
193     {
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
245 clib_error_t *
246 sixrd_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* */
255 VNET_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
261 VNET_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
271 int
272 sixrd_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
331   vnet_sw_interface_set_mtu (vnet_get_main (), t->sw_if_index, 1480);
332
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,
357                                    &ADJ_BCAST_ADDR, hi->sw_if_index, ~0, 1,
358                                    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  */
375 int
376 sixrd_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
409 static void
410 sixrd_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
422 static u8 *
423 sixrd_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
433 static void
434 sixrd_fib_node_last_lock_gone (fib_node_t * node)
435 {
436   /* top of the dependency tree, locks not managed here. */
437 }
438
439 static sixrd_adj_delegate_t *
440 sixrd_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
447 static fib_node_back_walk_rc_t
448 sixrd_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  */
462 static fib_node_t *
463 sixrd_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  */
475 const 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  */
483 const 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
489 static clib_error_t *
490 sixrd_init (vlib_main_t * vm)
491 {
492   clib_error_t *error = 0;
493
494   /* Make sure the IPIP tunnel subsystem is initialised */
495   error = vlib_call_init_function (vm, ipip_init);
496
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
504 VLIB_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  */