misc: remove GNU Indent directives
[vpp.git] / src / vnet / ip6-nd / rd_cp.c
1 /*
2  * Copyright (c) 2018 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/ip6-nd/ip6_ra.h>
17
18 #include <vnet/ip/ip6.h>
19 #include <vnet/ip/ip6_link.h>
20 #include <vnet/ethernet/ethernet.h>
21 #include <vnet/fib/fib_table.h>
22 #include <signal.h>
23 #include <math.h>
24
25 typedef struct
26 {
27   u32 sw_if_index;
28   u8 address_length;
29   ip6_address_t address;
30   f64 due_time;
31 } slaac_address_t;
32
33 typedef struct
34 {
35   u32 sw_if_index;
36   ip6_address_t router_address;
37   f64 due_time;
38 } default_route_t;
39
40 typedef struct
41 {
42   u8 enabled;
43   u8 install_default_routes;
44 } interface_config_t;
45
46 typedef struct
47 {
48   u8 enabled;
49   u8 events_on;
50
51   interface_config_t *config_by_sw_if_index;
52   slaac_address_t *slaac_address_pool;
53   default_route_t *default_route_pool;
54
55   /* binary API client */
56   u8 api_connected;
57   u32 my_client_index;
58
59   /* logging */
60   vlib_log_class_t log_class;
61
62   /* convenience */
63   vlib_main_t *vlib_main;
64   vnet_main_t *vnet_main;
65   u32 node_index;
66 } rd_cp_main_t;
67
68 rd_cp_main_t rd_cp_main;
69
70 enum
71 {
72   RD_CP_EVENT_INTERRUPT,
73 };
74
75 static void
76 router_solicitation_start_stop (u32 sw_if_index, u8 start)
77 {
78   rd_cp_main_t *rm = &rd_cp_main;
79   icmp6_send_router_solicitation_params_t params = { 0, };
80
81   if (start)
82     {
83       params.irt = 1;
84       params.mrt = 120;
85     }
86
87   icmp6_send_router_solicitation (rm->vlib_main, sw_if_index, !start,
88                                   &params);
89 }
90
91 static void interrupt_process (void);
92
93 static int
94 add_slaac_address (vlib_main_t * vm, u32 sw_if_index, u8 address_length,
95                    const ip6_address_t * address, f64 due_time)
96 {
97   rd_cp_main_t *rm = &rd_cp_main;
98   slaac_address_t *slaac_address;
99   clib_error_t *rv = 0;
100
101   pool_get_zero (rm->slaac_address_pool, slaac_address);
102
103   slaac_address->sw_if_index = sw_if_index;
104   slaac_address->address_length = address_length;
105   slaac_address->address = *address;
106   slaac_address->due_time = due_time;
107
108   rv =
109     ip6_add_del_interface_address (vm, sw_if_index, &slaac_address->address,
110                                    address_length, 0);
111
112   return rv != 0;
113 }
114
115 static void
116 add_default_route (vlib_main_t * vm, u32 sw_if_index,
117                    const ip6_address_t * next_hop_address, f64 due_time)
118 {
119   rd_cp_main_t *rm = &rd_cp_main;
120   default_route_t *default_route;
121
122   pool_get_zero (rm->default_route_pool, default_route);
123
124   default_route->sw_if_index = sw_if_index;
125   default_route->router_address = *next_hop_address;
126   default_route->due_time = due_time;
127
128   {
129     u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
130                                                          default_route->
131                                                          sw_if_index);
132     fib_prefix_t pfx = {
133       .fp_proto = FIB_PROTOCOL_IP6,
134     };
135     ip46_address_t nh = {
136       .ip6 = default_route->router_address,
137     };
138     fib_table_entry_update_one_path (fib_index, &pfx,
139                                      FIB_SOURCE_API,
140                                      FIB_ENTRY_FLAG_NONE,
141                                      DPO_PROTO_IP6,
142                                      &nh,
143                                      default_route->sw_if_index,
144                                      0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
145   }
146 }
147
148 static int
149 remove_slaac_address (vlib_main_t * vm, slaac_address_t * slaac_address)
150 {
151   rd_cp_main_t *rm = &rd_cp_main;
152   clib_error_t *rv = 0;
153
154   rv = ip6_add_del_interface_address (vm, slaac_address->sw_if_index,
155                                       &slaac_address->address,
156                                       slaac_address->address_length, 1);
157
158   pool_put (rm->slaac_address_pool, slaac_address);
159
160   return rv != 0;
161 }
162
163 static void
164 remove_default_route (vlib_main_t * vm, default_route_t * default_route)
165 {
166   rd_cp_main_t *rm = &rd_cp_main;
167
168   {
169     u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
170                                                          default_route->
171                                                          sw_if_index);
172     fib_prefix_t pfx = {
173       .fp_proto = FIB_PROTOCOL_IP6,
174     };
175     ip46_address_t nh = {
176       .ip6 = default_route->router_address,
177     };
178     fib_table_entry_path_remove (fib_index, &pfx,
179                                  FIB_SOURCE_API,
180                                  DPO_PROTO_IP6,
181                                  &nh,
182                                  default_route->sw_if_index,
183                                  0, 1, FIB_ROUTE_PATH_FLAG_NONE);
184   }
185
186   pool_put (rm->default_route_pool, default_route);
187 }
188
189 static u32
190 get_interface_mac_address (u32 sw_if_index, u8 mac[])
191 {
192   rd_cp_main_t *rm = &rd_cp_main;
193   vnet_sw_interface_t *si;
194   ethernet_interface_t *eth_if = 0;
195
196   if (!vnet_sw_interface_is_api_valid (rm->vnet_main, sw_if_index))
197     {
198       vlib_log_warn (rm->log_class, "Invalid sw_if_index");
199       return 1;
200     }
201
202   si = vnet_get_sup_sw_interface (rm->vnet_main, sw_if_index);
203   if (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
204     eth_if = ethernet_get_interface (&ethernet_main, si->hw_if_index);
205
206   if (!eth_if)
207     {
208       vlib_log_warn (rm->log_class, "Failed to get hardware interface");
209       return 1;
210     }
211
212   clib_memcpy_fast (mac, &eth_if->address, 6);
213
214   return 0;
215 }
216
217 static u8
218 ip6_prefixes_equal (ip6_address_t * prefix1, ip6_address_t * prefix2, u8 len)
219 {
220   if (len >= 64)
221     {
222       if (prefix1->as_u64[0] != prefix2->as_u64[0])
223         return 0;
224       if (len == 64)
225         return 1;
226       return prefix1->as_u64[1] >> (128 - len) ==
227         prefix2->as_u64[1] >> (128 - len);
228     }
229   return prefix1->as_u64[0] >> (64 - len) == prefix2->as_u64[0] >> (64 - len);
230 }
231
232 #define PREFIX_FLAG_A (1 << 6)
233 #define PREFIX_FLAG_L (1 << 7)
234
235 static void
236 ip6_ra_report_handler (const ip6_ra_report_t * r)
237 {
238   rd_cp_main_t *rm = &rd_cp_main;
239   vlib_main_t *vm = rm->vlib_main;
240   interface_config_t *if_config;
241   default_route_t *default_route;
242   slaac_address_t *slaac_address;
243   u32 sw_if_index;
244   u16 router_lifetime_in_sec;
245   u32 n_prefixes;
246   ra_report_prefix_info_t *prefix;
247   u8 mac[6];
248   f64 current_time;
249   u32 i;
250
251   current_time = vlib_time_now (vm);
252
253   sw_if_index = r->sw_if_index;
254
255   if (sw_if_index >= vec_len (rm->config_by_sw_if_index))
256     return;
257   if_config = &rm->config_by_sw_if_index[sw_if_index];
258
259   if (if_config->install_default_routes)
260     {
261       router_lifetime_in_sec = r->router_lifetime_in_sec;
262       u8 route_already_present = 0;
263       pool_foreach (default_route, rm->default_route_pool)
264        {
265         if (default_route->sw_if_index != sw_if_index)
266           ;
267         else if (0 != memcmp (&default_route->router_address,
268                               &r->router_address, 16))
269           ;
270         else
271           {
272             route_already_present = 1;
273             goto default_route_pool_foreach_out;
274           }
275       }
276     default_route_pool_foreach_out:
277
278       if (!route_already_present)
279         {
280           if (router_lifetime_in_sec != 0)
281             add_default_route (vm, sw_if_index, &r->router_address,
282                                current_time + router_lifetime_in_sec);
283         }
284       else
285         {
286           if (router_lifetime_in_sec != 0)
287             default_route->due_time = current_time + router_lifetime_in_sec;
288           else
289             remove_default_route (vm, default_route);
290         }
291     }
292
293   if (get_interface_mac_address (sw_if_index, mac) != 0)
294     {
295       vlib_log_warn (rm->log_class, "Error getting MAC address");
296       return;
297     }
298
299   if (!if_config->enabled)
300     return;
301
302   n_prefixes = vec_len (r->prefixes);
303   for (i = 0; i < n_prefixes; i++)
304     {
305       ip6_address_t *dst_address;
306       u8 prefix_length;
307       u32 valid_time;
308       u32 preferred_time;
309       f64 due_time;
310
311       prefix = &r->prefixes[i];
312
313       if (!(prefix->flags & PREFIX_FLAG_A))
314         continue;
315
316       dst_address = &prefix->prefix.fp_addr.ip6;
317       prefix_length = prefix->prefix.fp_len;
318
319       if (ip6_address_is_link_local_unicast (dst_address))
320         continue;
321
322       valid_time = prefix->valid_time;
323       preferred_time = prefix->preferred_time;
324
325       if (preferred_time > valid_time)
326         continue;
327
328       if (prefix_length != 64)
329         continue;
330
331       u8 address_already_present = 0;
332       pool_foreach (slaac_address, rm->slaac_address_pool)
333        {
334         if (slaac_address->sw_if_index != sw_if_index)
335           ;
336         else if (slaac_address->address_length != prefix_length)
337           ;
338         else if (!ip6_prefixes_equal (&slaac_address->address, dst_address,
339                                  prefix_length))
340           ;
341         else
342           {
343             address_already_present = 1;
344             goto slaac_address_pool_foreach_out;
345           }
346       }
347     slaac_address_pool_foreach_out:
348
349       if (address_already_present)
350         {
351           f64 remaining_life_time = slaac_address->due_time - current_time;
352           if (valid_time > 2 * 60 * 60 || valid_time > remaining_life_time)
353             slaac_address->due_time = current_time + valid_time;
354           else if (remaining_life_time > 2 * 60 * 60)
355             slaac_address->due_time = current_time + 2 * 60 * 60;
356           continue;
357         }
358
359       if (valid_time == 0)
360         continue;
361
362       due_time = current_time + valid_time;
363
364       ip6_address_t addr;
365       addr.as_u64[0] = dst_address->as_u64[0];
366       /* Invert the "u" bit */
367       addr.as_u8[8] = mac[0] ^ (1 << 1);
368       addr.as_u8[9] = mac[1];
369       addr.as_u8[10] = mac[2];
370       addr.as_u8[11] = 0xFF;
371       addr.as_u8[12] = 0xFE;
372       addr.as_u8[13] = mac[3];
373       addr.as_u8[14] = mac[4];
374       addr.as_u8[15] = mac[5];
375
376       add_slaac_address (vm, sw_if_index, prefix_length, &addr, due_time);
377     }
378
379   interrupt_process ();
380
381   return;
382 }
383
384 static uword
385 rd_cp_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
386 {
387   uword *event_data = 0;
388   rd_cp_main_t *rm = &rd_cp_main;
389   slaac_address_t *slaac_address;
390   default_route_t *default_route;
391   f64 sleep_time = 1e9;
392   f64 current_time;
393   f64 due_time;
394
395   while (1)
396     {
397       vlib_process_wait_for_event_or_clock (vm, sleep_time);
398       vlib_process_get_events (vm, &event_data);
399
400       vec_reset_length (event_data);
401
402       current_time = vlib_time_now (vm);
403       do
404         {
405           due_time = current_time + 1e9;
406           u32 index;
407           /*
408            * we do not use pool_foreach() to iterate over pool elements here
409            * as we are removing elements inside the loop body
410            */
411           pool_foreach_index (index, rm->slaac_address_pool)
412            {
413             slaac_address = pool_elt_at_index(rm->slaac_address_pool, index);
414             if (slaac_address->due_time > current_time)
415               {
416                 if (slaac_address->due_time < due_time)
417                   due_time = slaac_address->due_time;
418               }
419             else
420               {
421                 u32 sw_if_index = slaac_address->sw_if_index;
422                 remove_slaac_address (vm, slaac_address);
423                 /* make sure ip6 stays enabled */
424                 ip6_link_enable (sw_if_index, NULL);
425               }
426           }
427           pool_foreach_index (index, rm->default_route_pool)
428            {
429             default_route = pool_elt_at_index(rm->default_route_pool, index);
430             if (default_route->due_time > current_time)
431               {
432                 if (default_route->due_time < due_time)
433                   due_time = default_route->due_time;
434               }
435             else
436               remove_default_route (vm, default_route);
437           }
438           current_time = vlib_time_now (vm);
439         }
440       while (due_time < current_time);
441
442       sleep_time = due_time - current_time;
443     }
444
445   return 0;
446 }
447
448 VLIB_REGISTER_NODE (rd_cp_process_node) = {
449     .function = rd_cp_process,
450     .type = VLIB_NODE_TYPE_PROCESS,
451     .name = "rd-cp-process",
452 };
453
454 static void
455 interrupt_process (void)
456 {
457   rd_cp_main_t *rm = &rd_cp_main;
458   vlib_main_t *vm = rm->vlib_main;
459
460   vlib_process_signal_event (vm, rd_cp_process_node.index,
461                              RD_CP_EVENT_INTERRUPT, 0);
462 }
463
464 int
465 rd_cp_set_address_autoconfig (u32 sw_if_index,
466                               u8 enable, u8 install_default_routes)
467 {
468   rd_cp_main_t *rm = &rd_cp_main;
469   vlib_main_t *vm = rm->vlib_main;
470   vnet_main_t *vnm = rm->vnet_main;
471   interface_config_t *if_config;
472   interface_config_t empty_config = { 0, 0 };
473   slaac_address_t *slaac_address;
474   default_route_t *default_route;
475
476   if (!enable)
477     install_default_routes = 0;
478
479   if (!vnet_sw_interface_is_api_valid (vnm, sw_if_index))
480     {
481       vlib_log_warn (rm->log_class, "Invalid sw_if_index");
482       return 1;
483     }
484
485   if (!rm->enabled)
486     {
487       /* process kickoff */
488       interrupt_process ();
489       rm->enabled = 1;
490     }
491
492   vec_validate_init_empty (rm->config_by_sw_if_index, sw_if_index,
493                            empty_config);
494   if_config = &rm->config_by_sw_if_index[sw_if_index];
495
496   if (!if_config->enabled && enable)
497     ip6_link_enable (sw_if_index, NULL);
498
499   if ((!if_config->enabled && enable)
500       || (!if_config->install_default_routes && install_default_routes))
501     router_solicitation_start_stop (sw_if_index, 1);
502   else if (if_config->enabled && !enable)
503     router_solicitation_start_stop (sw_if_index, 0);
504
505   if (if_config->enabled && !enable)
506     {
507       pool_foreach (slaac_address, rm->slaac_address_pool)
508        {
509           remove_slaac_address (vm, slaac_address);
510       }
511     }
512   if (if_config->install_default_routes && !install_default_routes)
513     {
514       pool_foreach (default_route, rm->default_route_pool)
515        {
516           remove_default_route (vm, default_route);
517       }
518     }
519
520   if_config->enabled = enable;
521   if_config->install_default_routes = install_default_routes;
522
523   return 0;
524 }
525
526 static clib_error_t *
527 ip6_nd_address_autoconfig (vlib_main_t * vm,
528                            unformat_input_t * input, vlib_cli_command_t * cmd)
529 {
530   rd_cp_main_t *rm = &rd_cp_main;
531   vnet_main_t *vnm = rm->vnet_main;
532   clib_error_t *error = 0;
533   u32 sw_if_index = ~0;
534   u8 enable = 1;
535   u8 default_route = 0;
536
537   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
538     {
539       if (unformat
540           (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
541         ;
542       if (unformat (input, "default-route"))
543         default_route = 1;
544       if (unformat (input, "disable"))
545         enable = 0;
546       else
547         break;
548     }
549
550   if (sw_if_index != ~0)
551     {
552       if (rd_cp_set_address_autoconfig (sw_if_index, enable, default_route) !=
553           0)
554         error = clib_error_return (0, "Invalid sw_if_index");
555     }
556   else
557     error = clib_error_return (0, "Missing sw_if_index");
558
559   return error;
560 }
561
562 /*?
563  * This command is used to enable ND address autoconfiguration
564  * on particular interface including setting up default routes.
565  *
566  * @cliexpar
567  * @parblock
568  * Example of how to enable ND address autoconfiguration:
569  * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0}
570  * Example of how to enable ND address autoconfiguration
571  * with setting up default routes:
572  * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0 default-route}
573  * Example of how to disable ND address autoconfiguration:
574  * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0 disable}
575  * @endparblock
576 ?*/
577 VLIB_CLI_COMMAND (ip6_nd_address_autoconfig_command, static) = {
578   .path = "ip6 nd address autoconfig",
579   .short_help = "ip6 nd address autoconfig <interface> [default-route|disable]",
580   .function = ip6_nd_address_autoconfig,
581 };
582
583 static clib_error_t *
584 rd_cp_init (vlib_main_t * vm)
585 {
586   rd_cp_main_t *rm = &rd_cp_main;
587
588   rm->vlib_main = vm;
589   rm->vnet_main = vnet_get_main ();
590   rm->node_index = rd_cp_process_node.index;
591
592   rm->log_class = vlib_log_register_class ("rd_cp", 0);
593
594   ip6_ra_report_register (ip6_ra_report_handler);
595
596   return (NULL);
597 }
598
599 VLIB_INIT_FUNCTION (rd_cp_init);
600
601 /*
602  * fd.io coding-style-patch-verification: ON
603  *
604  * Local Variables:
605  * eval: (c-set-style "gnu")
606  * End:
607  */