api: multiple connections per process
[vpp.git] / src / vnet / ip / 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/vnet.h>
17 #include <vlibmemory/api.h>
18 #include <vnet/vnet_msg_enum.h>
19 #include <vnet/ip/ip6.h>
20 #include <vnet/ethernet/ethernet.h>
21 #include <vnet/ip/ip6_neighbor.h>
22 #include <vnet/fib/fib_table.h>
23 #include <signal.h>
24 #include <math.h>
25
26 #define vl_typedefs             /* define message structures */
27 #include <vnet/vnet_all_api_h.h>
28 #undef vl_typedefs
29
30 #define vl_endianfun            /* define message structures */
31 #include <vnet/vnet_all_api_h.h>
32 #undef vl_endianfun
33
34 #include <vlibapi/api_helper_macros.h>
35
36 #define foreach_rd_cp_msg                                                     \
37 _(IP6_ND_ADDRESS_AUTOCONFIG, ip6_nd_address_autoconfig)
38
39 typedef struct
40 {
41   u32 sw_if_index;
42   u8 address_length;
43   ip6_address_t address;
44   f64 due_time;
45 } slaac_address_t;
46
47 typedef struct
48 {
49   u32 sw_if_index;
50   ip6_address_t router_address;
51   f64 due_time;
52 } default_route_t;
53
54 typedef struct
55 {
56   u8 enabled;
57   u8 install_default_routes;
58 } interface_config_t;
59
60 typedef struct
61 {
62   u8 enabled;
63   u8 events_on;
64
65   interface_config_t *config_by_sw_if_index;
66   slaac_address_t *slaac_address_pool;
67   default_route_t *default_route_pool;
68
69   /* binary API client */
70   u8 api_connected;
71   svm_queue_t *vl_input_queue;
72   u32 my_client_index;
73
74   /* logging */
75   vlib_log_class_t log_class;
76
77   /* convenience */
78   vlib_main_t *vlib_main;
79   vnet_main_t *vnet_main;
80   api_main_t *api_main;
81   u32 node_index;
82 } rd_cp_main_t;
83
84 rd_cp_main_t rd_cp_main;
85
86 enum
87 {
88   RD_CP_EVENT_INTERRUPT,
89 };
90
91 #define vl_api_ip6_nd_address_autoconfig_t_print vl_noop_handler
92
93 static void
94 router_solicitation_start_stop (u32 sw_if_index, u8 start)
95 {
96   rd_cp_main_t *rm = &rd_cp_main;
97   icmp6_send_router_solicitation_params_t params = { 0, };
98
99   if (start)
100     {
101       params.irt = 1;
102       params.mrt = 120;
103     }
104
105   icmp6_send_router_solicitation (rm->vlib_main, sw_if_index, !start,
106                                   &params);
107 }
108
109 static void interrupt_process (void);
110
111 static int
112 add_slaac_address (vlib_main_t * vm, u32 sw_if_index, u8 address_length,
113                    ip6_address_t * address, f64 due_time)
114 {
115   rd_cp_main_t *rm = &rd_cp_main;
116   slaac_address_t *slaac_address;
117   clib_error_t *rv = 0;
118
119   pool_get (rm->slaac_address_pool, slaac_address);
120
121   slaac_address->sw_if_index = sw_if_index;
122   slaac_address->address_length = address_length;
123   slaac_address->address = *address;
124   slaac_address->due_time = due_time;
125
126   rv =
127     ip6_add_del_interface_address (vm, sw_if_index, &slaac_address->address,
128                                    address_length, 0);
129
130   return rv != 0;
131 }
132
133 static void
134 add_default_route (vlib_main_t * vm, u32 sw_if_index,
135                    ip6_address_t * next_hop_address, f64 due_time)
136 {
137   rd_cp_main_t *rm = &rd_cp_main;
138   default_route_t *default_route;
139
140   pool_get (rm->default_route_pool, default_route);
141
142   default_route->sw_if_index = sw_if_index;
143   default_route->router_address = *next_hop_address;
144   default_route->due_time = due_time;
145
146   {
147     u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
148                                                          default_route->
149                                                          sw_if_index);
150     fib_prefix_t pfx = {
151       .fp_proto = FIB_PROTOCOL_IP6,
152     };
153     ip46_address_t nh = {
154       .ip6 = default_route->router_address,
155     };
156     fib_table_entry_update_one_path (fib_index, &pfx,
157                                      FIB_SOURCE_API,
158                                      FIB_ENTRY_FLAG_NONE,
159                                      DPO_PROTO_IP6,
160                                      &nh,
161                                      default_route->sw_if_index,
162                                      0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
163   }
164 }
165
166 static int
167 remove_slaac_address (vlib_main_t * vm, slaac_address_t * slaac_address)
168 {
169   rd_cp_main_t *rm = &rd_cp_main;
170   clib_error_t *rv = 0;
171
172   rv = ip6_add_del_interface_address (vm, slaac_address->sw_if_index,
173                                       &slaac_address->address,
174                                       slaac_address->address_length, 1);
175
176   pool_put (rm->slaac_address_pool, slaac_address);
177
178   return rv != 0;
179 }
180
181 static void
182 remove_default_route (vlib_main_t * vm, default_route_t * default_route)
183 {
184   rd_cp_main_t *rm = &rd_cp_main;
185
186   {
187     u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
188                                                          default_route->
189                                                          sw_if_index);
190     fib_prefix_t pfx = {
191       .fp_proto = FIB_PROTOCOL_IP6,
192     };
193     ip46_address_t nh = {
194       .ip6 = default_route->router_address,
195     };
196     fib_table_entry_path_remove (fib_index, &pfx,
197                                  FIB_SOURCE_API,
198                                  DPO_PROTO_IP6,
199                                  &nh,
200                                  default_route->sw_if_index,
201                                  0, 1, FIB_ROUTE_PATH_FLAG_NONE);
202   }
203
204   pool_put (rm->default_route_pool, default_route);
205 }
206
207 static u32
208 get_interface_mac_address (u32 sw_if_index, u8 mac[])
209 {
210   rd_cp_main_t *rm = &rd_cp_main;
211   vnet_sw_interface_t *si;
212   ethernet_interface_t *eth_if = 0;
213
214   if (!vnet_sw_interface_is_api_valid (rm->vnet_main, sw_if_index))
215     {
216       vlib_log_warn (rm->log_class, "Invalid sw_if_index");
217       return 1;
218     }
219
220   si = vnet_get_sup_sw_interface (rm->vnet_main, sw_if_index);
221   if (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
222     eth_if = ethernet_get_interface (&ethernet_main, si->hw_if_index);
223
224   if (!eth_if)
225     {
226       vlib_log_warn (rm->log_class, "Failed to get hardware interface");
227       return 1;
228     }
229
230   clib_memcpy_fast (mac, eth_if->address, 6);
231
232   return 0;
233 }
234
235 static u32
236 ip6_enable (u32 sw_if_index)
237 {
238   rd_cp_main_t *rm = &rd_cp_main;
239   clib_error_t *rv;
240
241   rv = enable_ip6_interface (rm->vlib_main, sw_if_index);
242
243   return rv != 0;
244 }
245
246 static u8
247 ip6_prefixes_equal (ip6_address_t * prefix1, ip6_address_t * prefix2, u8 len)
248 {
249   if (len >= 64)
250     {
251       if (prefix1->as_u64[0] != prefix2->as_u64[0])
252         return 0;
253       if (len == 64)
254         return 1;
255       return prefix1->as_u64[1] >> (128 - len) ==
256         prefix2->as_u64[1] >> (128 - len);
257     }
258   return prefix1->as_u64[0] >> (64 - len) == prefix2->as_u64[0] >> (64 - len);
259 }
260
261 #define PREFIX_FLAG_A (1 << 6)
262 #define PREFIX_FLAG_L (1 << 7)
263
264 static clib_error_t *
265 ip6_ra_report_handler (void *data)
266 {
267   rd_cp_main_t *rm = &rd_cp_main;
268   vlib_main_t *vm = rm->vlib_main;
269   clib_error_t *error = 0;
270   ra_report_t *r = data;
271   interface_config_t *if_config;
272   default_route_t *default_route;
273   slaac_address_t *slaac_address;
274   u32 sw_if_index;
275   u16 router_lifetime_in_sec;
276   u32 n_prefixes;
277   ra_report_prefix_info_t *prefix;
278   u8 mac[6];
279   f64 current_time;
280   u32 i;
281
282   current_time = vlib_time_now (vm);
283
284   sw_if_index = r->sw_if_index;
285
286   if (sw_if_index >= vec_len (rm->config_by_sw_if_index))
287     return 0;
288   if_config = &rm->config_by_sw_if_index[sw_if_index];
289
290   if (if_config->install_default_routes)
291     {
292       router_lifetime_in_sec = r->router_lifetime_in_sec;
293       u8 route_already_present = 0;
294       /* *INDENT-OFF* */
295       pool_foreach (default_route, rm->default_route_pool,
296       ({
297         if (default_route->sw_if_index != sw_if_index)
298           ;
299         else if (0 != memcmp (&default_route->router_address,
300                               &r->router_address, 16))
301           ;
302         else
303           {
304             route_already_present = 1;
305             goto default_route_pool_foreach_out;
306           }
307       }));
308       /* *INDENT-ON* */
309     default_route_pool_foreach_out:
310
311       if (!route_already_present)
312         {
313           if (router_lifetime_in_sec != 0)
314             add_default_route (vm, sw_if_index, &r->router_address,
315                                current_time + router_lifetime_in_sec);
316         }
317       else
318         {
319           if (router_lifetime_in_sec != 0)
320             default_route->due_time = current_time + router_lifetime_in_sec;
321           else
322             remove_default_route (vm, default_route);
323         }
324     }
325
326   if (get_interface_mac_address (sw_if_index, mac) != 0)
327     {
328       vlib_log_warn (rm->log_class, "Error getting MAC address");
329       return clib_error_return (0, "Error getting MAC address");
330     }
331
332   if (!if_config->enabled)
333     return 0;
334
335   n_prefixes = vec_len (r->prefixes);
336   for (i = 0; i < n_prefixes; i++)
337     {
338       ip6_address_t *dst_address;
339       u8 prefix_length;
340       u32 valid_time;
341       u32 preferred_time;
342       f64 due_time;
343
344       prefix = &r->prefixes[i];
345
346       if (!(prefix->flags & PREFIX_FLAG_A))
347         continue;
348
349       dst_address = &prefix->prefix.fp_addr.ip6;
350       prefix_length = prefix->prefix.fp_len;
351
352       if (ip6_address_is_link_local_unicast (dst_address))
353         continue;
354
355       valid_time = prefix->valid_time;
356       preferred_time = prefix->preferred_time;
357
358       if (preferred_time > valid_time)
359         continue;
360
361       if (prefix_length != 64)
362         continue;
363
364       u8 address_already_present = 0;
365       /* *INDENT-OFF* */
366       pool_foreach (slaac_address, rm->slaac_address_pool,
367       ({
368         if (slaac_address->sw_if_index != sw_if_index)
369           ;
370         else if (slaac_address->address_length != prefix_length)
371           ;
372         else if (!ip6_prefixes_equal (&slaac_address->address, dst_address,
373                                  prefix_length))
374           ;
375         else
376           {
377             address_already_present = 1;
378             goto slaac_address_pool_foreach_out;
379           }
380       }));
381       /* *INDENT-ON* */
382     slaac_address_pool_foreach_out:
383
384       if (address_already_present)
385         {
386           f64 remaining_life_time = slaac_address->due_time - current_time;
387           if (valid_time > 2 * 60 * 60 || valid_time > remaining_life_time)
388             slaac_address->due_time = current_time + valid_time;
389           else if (remaining_life_time > 2 * 60 * 60)
390             slaac_address->due_time = current_time + 2 * 60 * 60;
391           continue;
392         }
393
394       if (valid_time == 0)
395         continue;
396
397       due_time = current_time + valid_time;
398
399       ip6_address_t addr;
400       addr.as_u64[0] = dst_address->as_u64[0];
401       /* Invert the "u" bit */
402       addr.as_u8[8] = mac[0] ^ (1 << 1);
403       addr.as_u8[9] = mac[1];
404       addr.as_u8[10] = mac[2];
405       addr.as_u8[11] = 0xFF;
406       addr.as_u8[12] = 0xFE;
407       addr.as_u8[13] = mac[3];
408       addr.as_u8[14] = mac[4];
409       addr.as_u8[15] = mac[5];
410
411       add_slaac_address (vm, sw_if_index, prefix_length, &addr, due_time);
412     }
413
414   interrupt_process ();
415
416   return error;
417 }
418
419 VNET_IP6_NEIGHBOR_RA_FUNCTION (ip6_ra_report_handler);
420
421 static uword
422 rd_cp_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
423 {
424   uword *event_data = 0;
425   rd_cp_main_t *rm = &rd_cp_main;
426   slaac_address_t *slaac_address;
427   default_route_t *default_route;
428   f64 sleep_time = 1e9;
429   f64 current_time;
430   f64 due_time;
431
432   while (1)
433     {
434       vlib_process_wait_for_event_or_clock (vm, sleep_time);
435       vlib_process_get_events (vm, &event_data);
436
437       vec_reset_length (event_data);
438
439       current_time = vlib_time_now (vm);
440       do
441         {
442           due_time = current_time + 1e9;
443           u32 index;
444           /*
445            * we do not use pool_foreach() to iterate over pool elements here
446            * as we are removing elements inside the loop body
447            */
448           /* *INDENT-OFF* */
449           pool_foreach_index (index, rm->slaac_address_pool,
450           ({
451             slaac_address = pool_elt_at_index(rm->slaac_address_pool, index);
452             if (slaac_address->due_time > current_time)
453               {
454                 if (slaac_address->due_time < due_time)
455                   due_time = slaac_address->due_time;
456               }
457             else
458               {
459                 u32 sw_if_index = slaac_address->sw_if_index;
460                 remove_slaac_address (vm, slaac_address);
461                 /* make sure ip6 stays enabled */
462                 ip6_enable (sw_if_index);
463               }
464           }));
465           pool_foreach_index (index, rm->default_route_pool,
466           ({
467             default_route = pool_elt_at_index(rm->default_route_pool, index);
468             if (default_route->due_time > current_time)
469               {
470                 if (default_route->due_time < due_time)
471                   due_time = default_route->due_time;
472               }
473             else
474               remove_default_route (vm, default_route);
475           }));
476           /* *INDENT-ON* */
477           current_time = vlib_time_now (vm);
478         }
479       while (due_time < current_time);
480
481       sleep_time = due_time - current_time;
482     }
483
484   return 0;
485 }
486
487 /* *INDENT-OFF* */
488 VLIB_REGISTER_NODE (rd_cp_process_node) = {
489     .function = rd_cp_process,
490     .type = VLIB_NODE_TYPE_PROCESS,
491     .name = "rd-cp-process",
492 };
493 /* *INDENT-ON* */
494
495 static void
496 interrupt_process (void)
497 {
498   rd_cp_main_t *rm = &rd_cp_main;
499   vlib_main_t *vm = rm->vlib_main;
500
501   vlib_process_signal_event (vm, rd_cp_process_node.index,
502                              RD_CP_EVENT_INTERRUPT, 0);
503 }
504
505 static int
506 set_address_autoconfig (u32 sw_if_index, u8 enable, u8 install_default_routes)
507 {
508   rd_cp_main_t *rm = &rd_cp_main;
509   vlib_main_t *vm = rm->vlib_main;
510   vnet_main_t *vnm = rm->vnet_main;
511   interface_config_t *if_config;
512   interface_config_t empty_config = { 0, 0 };
513   slaac_address_t *slaac_address;
514   default_route_t *default_route;
515
516   if (!enable)
517     install_default_routes = 0;
518
519   if (!vnet_sw_interface_is_api_valid (vnm, sw_if_index))
520     {
521       vlib_log_warn (rm->log_class, "Invalid sw_if_index");
522       return 1;
523     }
524
525   if (!rm->enabled)
526     {
527       /* process kickoff */
528       interrupt_process ();
529       rm->enabled = 1;
530     }
531
532   vec_validate_init_empty (rm->config_by_sw_if_index, sw_if_index,
533                            empty_config);
534   if_config = &rm->config_by_sw_if_index[sw_if_index];
535
536   if (!if_config->enabled && enable)
537     ip6_enable (sw_if_index);
538
539   if ((!if_config->enabled && enable)
540       || (!if_config->install_default_routes && install_default_routes))
541     router_solicitation_start_stop (sw_if_index, 1);
542   else if (if_config->enabled && !enable)
543     router_solicitation_start_stop (sw_if_index, 0);
544
545   if (if_config->enabled && !enable)
546     {
547       /* *INDENT-OFF* */
548       pool_foreach (slaac_address, rm->slaac_address_pool,
549       ({
550           remove_slaac_address (vm, slaac_address);
551       }));
552       /* *INDENT-ON* */
553     }
554   if (if_config->install_default_routes && !install_default_routes)
555     {
556       /* *INDENT-OFF* */
557       pool_foreach (default_route, rm->default_route_pool,
558       ({
559           remove_default_route (vm, default_route);
560       }));
561       /* *INDENT-ON* */
562     }
563
564   if_config->enabled = enable;
565   if_config->install_default_routes = install_default_routes;
566
567   return 0;
568 }
569
570 static clib_error_t *
571 ip6_nd_address_autoconfig (vlib_main_t * vm,
572                            unformat_input_t * input, vlib_cli_command_t * cmd)
573 {
574   rd_cp_main_t *rm = &rd_cp_main;
575   vnet_main_t *vnm = rm->vnet_main;
576   clib_error_t *error = 0;
577   u32 sw_if_index = ~0;
578   u8 enable = 1;
579   u8 default_route = 0;
580
581   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
582     {
583       if (unformat
584           (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
585         ;
586       if (unformat (input, "default-route"))
587         default_route = 1;
588       if (unformat (input, "disable"))
589         enable = 0;
590       else
591         break;
592     }
593
594   if (sw_if_index != ~0)
595     {
596       if (set_address_autoconfig (sw_if_index, enable, default_route) != 0)
597         error = clib_error_return (0, "Invalid sw_if_index");
598     }
599   else
600     error = clib_error_return (0, "Missing sw_if_index");
601
602   return error;
603 }
604
605 /*?
606  * This command is used to enable ND address autoconfiguration
607  * on particular interface including setting up default routes.
608  *
609  * @cliexpar
610  * @parblock
611  * Example of how to enable ND address autoconfiguration:
612  * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0}
613  * Example of how to enable ND address autoconfiguration
614  * with setting up default routes:
615  * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0 default-route}
616  * Example of how to disable ND address autoconfiguration:
617  * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0 disable}
618  * @endparblock
619 ?*/
620 /* *INDENT-OFF* */
621 VLIB_CLI_COMMAND (ip6_nd_address_autoconfig_command, static) = {
622   .path = "ip6 nd address autoconfig",
623   .short_help = "ip6 nd address autoconfig <interface> [default-route|disable]",
624   .function = ip6_nd_address_autoconfig,
625 };
626 /* *INDENT-ON* */
627
628 static void
629 vl_api_ip6_nd_address_autoconfig_t_handler (vl_api_ip6_nd_address_autoconfig_t
630                                             * mp)
631 {
632   vl_api_ip6_nd_address_autoconfig_reply_t *rmp;
633   u32 sw_if_index;
634   int rv = 0;
635
636   VALIDATE_SW_IF_INDEX (mp);
637
638   sw_if_index = ntohl (mp->sw_if_index);
639
640   rv =
641     set_address_autoconfig (sw_if_index, mp->enable,
642                             mp->install_default_routes);
643
644   BAD_SW_IF_INDEX_LABEL;
645
646   REPLY_MACRO (VL_API_IP6_ND_ADDRESS_AUTOCONFIG_REPLY);
647 }
648
649 #define vl_msg_name_crc_list
650 #include <vnet/ip/rd_cp.api.h>
651 #undef vl_msg_name_crc_list
652
653 static void
654 setup_message_id_table (api_main_t * am)
655 {
656 #define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
657   foreach_vl_msg_name_crc_rd_cp;
658 #undef _
659 }
660
661 static clib_error_t *
662 rd_cp_init (vlib_main_t * vm)
663 {
664   rd_cp_main_t *rm = &rd_cp_main;
665   api_main_t *am = vlibapi_get_main ();
666
667   rm->vlib_main = vm;
668   rm->vnet_main = vnet_get_main ();
669   rm->api_main = am;
670   rm->node_index = rd_cp_process_node.index;
671
672   rm->log_class = vlib_log_register_class ("rd_cp", 0);
673
674 #define _(N,n)                                                  \
675     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
676                            vl_api_##n##_t_handler,              \
677                            vl_noop_handler,                     \
678                            vl_api_##n##_t_endian,               \
679                            vl_api_##n##_t_print,                \
680                            sizeof(vl_api_##n##_t), 0/* do NOT trace! */);
681   foreach_rd_cp_msg;
682 #undef _
683
684   /*
685    * Set up the (msg_name, crc, message-id) table
686    */
687   setup_message_id_table (am);
688
689   return 0;
690 }
691
692 VLIB_INIT_FUNCTION (rd_cp_init);
693
694 /*
695  * fd.io coding-style-patch-verification: ON
696  *
697  * Local Variables:
698  * eval: (c-set-style "gnu")
699  * End:
700  */