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