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