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