api: replace print functions wth format
[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       /* *INDENT-OFF* */
264       pool_foreach (default_route, rm->default_route_pool)
265        {
266         if (default_route->sw_if_index != sw_if_index)
267           ;
268         else if (0 != memcmp (&default_route->router_address,
269                               &r->router_address, 16))
270           ;
271         else
272           {
273             route_already_present = 1;
274             goto default_route_pool_foreach_out;
275           }
276       }
277       /* *INDENT-ON* */
278     default_route_pool_foreach_out:
279
280       if (!route_already_present)
281         {
282           if (router_lifetime_in_sec != 0)
283             add_default_route (vm, sw_if_index, &r->router_address,
284                                current_time + router_lifetime_in_sec);
285         }
286       else
287         {
288           if (router_lifetime_in_sec != 0)
289             default_route->due_time = current_time + router_lifetime_in_sec;
290           else
291             remove_default_route (vm, default_route);
292         }
293     }
294
295   if (get_interface_mac_address (sw_if_index, mac) != 0)
296     {
297       vlib_log_warn (rm->log_class, "Error getting MAC address");
298       return;
299     }
300
301   if (!if_config->enabled)
302     return;
303
304   n_prefixes = vec_len (r->prefixes);
305   for (i = 0; i < n_prefixes; i++)
306     {
307       ip6_address_t *dst_address;
308       u8 prefix_length;
309       u32 valid_time;
310       u32 preferred_time;
311       f64 due_time;
312
313       prefix = &r->prefixes[i];
314
315       if (!(prefix->flags & PREFIX_FLAG_A))
316         continue;
317
318       dst_address = &prefix->prefix.fp_addr.ip6;
319       prefix_length = prefix->prefix.fp_len;
320
321       if (ip6_address_is_link_local_unicast (dst_address))
322         continue;
323
324       valid_time = prefix->valid_time;
325       preferred_time = prefix->preferred_time;
326
327       if (preferred_time > valid_time)
328         continue;
329
330       if (prefix_length != 64)
331         continue;
332
333       u8 address_already_present = 0;
334       /* *INDENT-OFF* */
335       pool_foreach (slaac_address, rm->slaac_address_pool)
336        {
337         if (slaac_address->sw_if_index != sw_if_index)
338           ;
339         else if (slaac_address->address_length != prefix_length)
340           ;
341         else if (!ip6_prefixes_equal (&slaac_address->address, dst_address,
342                                  prefix_length))
343           ;
344         else
345           {
346             address_already_present = 1;
347             goto slaac_address_pool_foreach_out;
348           }
349       }
350       /* *INDENT-ON* */
351     slaac_address_pool_foreach_out:
352
353       if (address_already_present)
354         {
355           f64 remaining_life_time = slaac_address->due_time - current_time;
356           if (valid_time > 2 * 60 * 60 || valid_time > remaining_life_time)
357             slaac_address->due_time = current_time + valid_time;
358           else if (remaining_life_time > 2 * 60 * 60)
359             slaac_address->due_time = current_time + 2 * 60 * 60;
360           continue;
361         }
362
363       if (valid_time == 0)
364         continue;
365
366       due_time = current_time + valid_time;
367
368       ip6_address_t addr;
369       addr.as_u64[0] = dst_address->as_u64[0];
370       /* Invert the "u" bit */
371       addr.as_u8[8] = mac[0] ^ (1 << 1);
372       addr.as_u8[9] = mac[1];
373       addr.as_u8[10] = mac[2];
374       addr.as_u8[11] = 0xFF;
375       addr.as_u8[12] = 0xFE;
376       addr.as_u8[13] = mac[3];
377       addr.as_u8[14] = mac[4];
378       addr.as_u8[15] = mac[5];
379
380       add_slaac_address (vm, sw_if_index, prefix_length, &addr, due_time);
381     }
382
383   interrupt_process ();
384
385   return;
386 }
387
388 static uword
389 rd_cp_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
390 {
391   uword *event_data = 0;
392   rd_cp_main_t *rm = &rd_cp_main;
393   slaac_address_t *slaac_address;
394   default_route_t *default_route;
395   f64 sleep_time = 1e9;
396   f64 current_time;
397   f64 due_time;
398
399   while (1)
400     {
401       vlib_process_wait_for_event_or_clock (vm, sleep_time);
402       vlib_process_get_events (vm, &event_data);
403
404       vec_reset_length (event_data);
405
406       current_time = vlib_time_now (vm);
407       do
408         {
409           due_time = current_time + 1e9;
410           u32 index;
411           /*
412            * we do not use pool_foreach() to iterate over pool elements here
413            * as we are removing elements inside the loop body
414            */
415           /* *INDENT-OFF* */
416           pool_foreach_index (index, rm->slaac_address_pool)
417            {
418             slaac_address = pool_elt_at_index(rm->slaac_address_pool, index);
419             if (slaac_address->due_time > current_time)
420               {
421                 if (slaac_address->due_time < due_time)
422                   due_time = slaac_address->due_time;
423               }
424             else
425               {
426                 u32 sw_if_index = slaac_address->sw_if_index;
427                 remove_slaac_address (vm, slaac_address);
428                 /* make sure ip6 stays enabled */
429                 ip6_link_enable (sw_if_index, NULL);
430               }
431           }
432           pool_foreach_index (index, rm->default_route_pool)
433            {
434             default_route = pool_elt_at_index(rm->default_route_pool, index);
435             if (default_route->due_time > current_time)
436               {
437                 if (default_route->due_time < due_time)
438                   due_time = default_route->due_time;
439               }
440             else
441               remove_default_route (vm, default_route);
442           }
443           /* *INDENT-ON* */
444           current_time = vlib_time_now (vm);
445         }
446       while (due_time < current_time);
447
448       sleep_time = due_time - current_time;
449     }
450
451   return 0;
452 }
453
454 /* *INDENT-OFF* */
455 VLIB_REGISTER_NODE (rd_cp_process_node) = {
456     .function = rd_cp_process,
457     .type = VLIB_NODE_TYPE_PROCESS,
458     .name = "rd-cp-process",
459 };
460 /* *INDENT-ON* */
461
462 static void
463 interrupt_process (void)
464 {
465   rd_cp_main_t *rm = &rd_cp_main;
466   vlib_main_t *vm = rm->vlib_main;
467
468   vlib_process_signal_event (vm, rd_cp_process_node.index,
469                              RD_CP_EVENT_INTERRUPT, 0);
470 }
471
472 int
473 rd_cp_set_address_autoconfig (u32 sw_if_index,
474                               u8 enable, u8 install_default_routes)
475 {
476   rd_cp_main_t *rm = &rd_cp_main;
477   vlib_main_t *vm = rm->vlib_main;
478   vnet_main_t *vnm = rm->vnet_main;
479   interface_config_t *if_config;
480   interface_config_t empty_config = { 0, 0 };
481   slaac_address_t *slaac_address;
482   default_route_t *default_route;
483
484   if (!enable)
485     install_default_routes = 0;
486
487   if (!vnet_sw_interface_is_api_valid (vnm, sw_if_index))
488     {
489       vlib_log_warn (rm->log_class, "Invalid sw_if_index");
490       return 1;
491     }
492
493   if (!rm->enabled)
494     {
495       /* process kickoff */
496       interrupt_process ();
497       rm->enabled = 1;
498     }
499
500   vec_validate_init_empty (rm->config_by_sw_if_index, sw_if_index,
501                            empty_config);
502   if_config = &rm->config_by_sw_if_index[sw_if_index];
503
504   if (!if_config->enabled && enable)
505     ip6_link_enable (sw_if_index, NULL);
506
507   if ((!if_config->enabled && enable)
508       || (!if_config->install_default_routes && install_default_routes))
509     router_solicitation_start_stop (sw_if_index, 1);
510   else if (if_config->enabled && !enable)
511     router_solicitation_start_stop (sw_if_index, 0);
512
513   if (if_config->enabled && !enable)
514     {
515       /* *INDENT-OFF* */
516       pool_foreach (slaac_address, rm->slaac_address_pool)
517        {
518           remove_slaac_address (vm, slaac_address);
519       }
520       /* *INDENT-ON* */
521     }
522   if (if_config->install_default_routes && !install_default_routes)
523     {
524       /* *INDENT-OFF* */
525       pool_foreach (default_route, rm->default_route_pool)
526        {
527           remove_default_route (vm, default_route);
528       }
529       /* *INDENT-ON* */
530     }
531
532   if_config->enabled = enable;
533   if_config->install_default_routes = install_default_routes;
534
535   return 0;
536 }
537
538 static clib_error_t *
539 ip6_nd_address_autoconfig (vlib_main_t * vm,
540                            unformat_input_t * input, vlib_cli_command_t * cmd)
541 {
542   rd_cp_main_t *rm = &rd_cp_main;
543   vnet_main_t *vnm = rm->vnet_main;
544   clib_error_t *error = 0;
545   u32 sw_if_index = ~0;
546   u8 enable = 1;
547   u8 default_route = 0;
548
549   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
550     {
551       if (unformat
552           (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
553         ;
554       if (unformat (input, "default-route"))
555         default_route = 1;
556       if (unformat (input, "disable"))
557         enable = 0;
558       else
559         break;
560     }
561
562   if (sw_if_index != ~0)
563     {
564       if (rd_cp_set_address_autoconfig (sw_if_index, enable, default_route) !=
565           0)
566         error = clib_error_return (0, "Invalid sw_if_index");
567     }
568   else
569     error = clib_error_return (0, "Missing sw_if_index");
570
571   return error;
572 }
573
574 /*?
575  * This command is used to enable ND address autoconfiguration
576  * on particular interface including setting up default routes.
577  *
578  * @cliexpar
579  * @parblock
580  * Example of how to enable ND address autoconfiguration:
581  * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0}
582  * Example of how to enable ND address autoconfiguration
583  * with setting up default routes:
584  * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0 default-route}
585  * Example of how to disable ND address autoconfiguration:
586  * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0 disable}
587  * @endparblock
588 ?*/
589 /* *INDENT-OFF* */
590 VLIB_CLI_COMMAND (ip6_nd_address_autoconfig_command, static) = {
591   .path = "ip6 nd address autoconfig",
592   .short_help = "ip6 nd address autoconfig <interface> [default-route|disable]",
593   .function = ip6_nd_address_autoconfig,
594 };
595 /* *INDENT-ON* */
596
597 static clib_error_t *
598 rd_cp_init (vlib_main_t * vm)
599 {
600   rd_cp_main_t *rm = &rd_cp_main;
601
602   rm->vlib_main = vm;
603   rm->vnet_main = vnet_get_main ();
604   rm->node_index = rd_cp_process_node.index;
605
606   rm->log_class = vlib_log_register_class ("rd_cp", 0);
607
608   ip6_ra_report_register (ip6_ra_report_handler);
609
610   return (NULL);
611 }
612
613 VLIB_INIT_FUNCTION (rd_cp_init);
614
615 /*
616  * fd.io coding-style-patch-verification: ON
617  *
618  * Local Variables:
619  * eval: (c-set-style "gnu")
620  * End:
621  */