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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
26 #define vl_typedefs /* define message structures */
27 #include <vnet/vnet_all_api_h.h>
30 #define vl_endianfun /* define message structures */
31 #include <vnet/vnet_all_api_h.h>
34 #include <vlibapi/api_helper_macros.h>
36 #define foreach_rd_cp_msg \
37 _(IP6_ND_ADDRESS_AUTOCONFIG, ip6_nd_address_autoconfig)
43 ip6_address_t address;
50 ip6_address_t router_address;
57 u8 install_default_routes;
65 interface_config_t *config_by_sw_if_index;
66 slaac_address_t *slaac_address_pool;
67 default_route_t *default_route_pool;
69 /* binary API client */
71 svm_queue_t *vl_input_queue;
75 vlib_log_class_t log_class;
78 vlib_main_t *vlib_main;
79 vnet_main_t *vnet_main;
84 rd_cp_main_t rd_cp_main;
88 RD_CP_EVENT_INTERRUPT,
91 #define vl_api_ip6_nd_address_autoconfig_t_print vl_noop_handler
94 router_solicitation_start_stop (u32 sw_if_index, u8 start)
96 rd_cp_main_t *rm = &rd_cp_main;
97 icmp6_send_router_solicitation_params_t params = { 0, };
105 icmp6_send_router_solicitation (rm->vlib_main, sw_if_index, !start,
109 static void interrupt_process (void);
112 add_slaac_address (vlib_main_t * vm, u32 sw_if_index, u8 address_length,
113 ip6_address_t * address, f64 due_time)
115 rd_cp_main_t *rm = &rd_cp_main;
116 slaac_address_t *slaac_address;
117 clib_error_t *rv = 0;
119 pool_get (rm->slaac_address_pool, slaac_address);
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;
127 ip6_add_del_interface_address (vm, sw_if_index, &slaac_address->address,
134 add_default_route (vlib_main_t * vm, u32 sw_if_index,
135 ip6_address_t * next_hop_address, f64 due_time)
137 rd_cp_main_t *rm = &rd_cp_main;
138 default_route_t *default_route;
140 pool_get (rm->default_route_pool, default_route);
142 default_route->sw_if_index = sw_if_index;
143 default_route->router_address = *next_hop_address;
144 default_route->due_time = due_time;
147 u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
151 .fp_proto = FIB_PROTOCOL_IP6,
153 ip46_address_t nh = {
154 .ip6 = default_route->router_address,
156 fib_table_entry_update_one_path (fib_index, &pfx,
161 default_route->sw_if_index,
162 0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
167 remove_slaac_address (vlib_main_t * vm, slaac_address_t * slaac_address)
169 rd_cp_main_t *rm = &rd_cp_main;
170 clib_error_t *rv = 0;
172 rv = ip6_add_del_interface_address (vm, slaac_address->sw_if_index,
173 &slaac_address->address,
174 slaac_address->address_length, 1);
176 pool_put (rm->slaac_address_pool, slaac_address);
182 remove_default_route (vlib_main_t * vm, default_route_t * default_route)
184 rd_cp_main_t *rm = &rd_cp_main;
187 u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
191 .fp_proto = FIB_PROTOCOL_IP6,
193 ip46_address_t nh = {
194 .ip6 = default_route->router_address,
196 fib_table_entry_path_remove (fib_index, &pfx,
200 default_route->sw_if_index,
201 0, 1, FIB_ROUTE_PATH_FLAG_NONE);
204 pool_put (rm->default_route_pool, default_route);
208 get_interface_mac_address (u32 sw_if_index, u8 mac[])
210 rd_cp_main_t *rm = &rd_cp_main;
211 vnet_sw_interface_t *si;
212 ethernet_interface_t *eth_if = 0;
214 if (!vnet_sw_interface_is_api_valid (rm->vnet_main, sw_if_index))
216 vlib_log_warn (rm->log_class, "Invalid sw_if_index");
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 (ðernet_main, si->hw_if_index);
226 vlib_log_warn (rm->log_class, "Failed to get hardware interface");
230 clib_memcpy_fast (mac, eth_if->address, 6);
236 ip6_enable (u32 sw_if_index)
238 rd_cp_main_t *rm = &rd_cp_main;
241 rv = enable_ip6_interface (rm->vlib_main, sw_if_index);
247 ip6_prefixes_equal (ip6_address_t * prefix1, ip6_address_t * prefix2, u8 len)
251 if (prefix1->as_u64[0] != prefix2->as_u64[0])
255 return prefix1->as_u64[1] >> (128 - len) ==
256 prefix2->as_u64[1] >> (128 - len);
258 return prefix1->as_u64[0] >> (64 - len) == prefix2->as_u64[0] >> (64 - len);
261 #define PREFIX_FLAG_A (1 << 6)
262 #define PREFIX_FLAG_L (1 << 7)
264 static clib_error_t *
265 ip6_ra_report_handler (void *data)
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;
275 u16 router_lifetime_in_sec;
277 ra_report_prefix_info_t *prefix;
282 current_time = vlib_time_now (vm);
284 sw_if_index = r->sw_if_index;
286 if (sw_if_index >= vec_len (rm->config_by_sw_if_index))
288 if_config = &rm->config_by_sw_if_index[sw_if_index];
290 if (if_config->install_default_routes)
292 router_lifetime_in_sec = r->router_lifetime_in_sec;
293 u8 route_already_present = 0;
295 pool_foreach (default_route, rm->default_route_pool,
297 if (default_route->sw_if_index != sw_if_index)
299 else if (0 != memcmp (&default_route->router_address,
300 &r->router_address, 16))
304 route_already_present = 1;
305 goto default_route_pool_foreach_out;
309 default_route_pool_foreach_out:
311 if (!route_already_present)
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);
319 if (router_lifetime_in_sec != 0)
320 default_route->due_time = current_time + router_lifetime_in_sec;
322 remove_default_route (vm, default_route);
326 if (get_interface_mac_address (sw_if_index, mac) != 0)
328 vlib_log_warn (rm->log_class, "Error getting MAC address");
329 return clib_error_return (0, "Error getting MAC address");
332 if (!if_config->enabled)
335 n_prefixes = vec_len (r->prefixes);
336 for (i = 0; i < n_prefixes; i++)
338 ip6_address_t *dst_address;
344 prefix = &r->prefixes[i];
346 if (!(prefix->flags & PREFIX_FLAG_A))
349 dst_address = &prefix->prefix.fp_addr.ip6;
350 prefix_length = prefix->prefix.fp_len;
352 if (ip6_address_is_link_local_unicast (dst_address))
355 valid_time = prefix->valid_time;
356 preferred_time = prefix->preferred_time;
358 if (preferred_time > valid_time)
361 if (prefix_length != 64)
364 u8 address_already_present = 0;
366 pool_foreach (slaac_address, rm->slaac_address_pool,
368 if (slaac_address->sw_if_index != sw_if_index)
370 else if (slaac_address->address_length != prefix_length)
372 else if (!ip6_prefixes_equal (&slaac_address->address, dst_address,
377 address_already_present = 1;
378 goto slaac_address_pool_foreach_out;
382 slaac_address_pool_foreach_out:
384 if (address_already_present)
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;
397 due_time = current_time + valid_time;
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];
411 add_slaac_address (vm, sw_if_index, prefix_length, &addr, due_time);
414 interrupt_process ();
419 VNET_IP6_NEIGHBOR_RA_FUNCTION (ip6_ra_report_handler);
422 rd_cp_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
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;
434 vlib_process_wait_for_event_or_clock (vm, sleep_time);
435 vlib_process_get_events (vm, &event_data);
437 vec_reset_length (event_data);
439 current_time = vlib_time_now (vm);
442 due_time = current_time + 1e9;
444 pool_foreach (slaac_address, rm->slaac_address_pool,
446 if (slaac_address->due_time > current_time)
448 if (slaac_address->due_time < due_time)
449 due_time = slaac_address->due_time;
453 remove_slaac_address (vm, slaac_address);
454 /* make sure ip6 stays enabled */
455 ip6_enable (slaac_address->sw_if_index);
458 pool_foreach (default_route, rm->default_route_pool,
460 if (default_route->due_time > current_time)
462 if (default_route->due_time < due_time)
463 due_time = default_route->due_time;
466 remove_default_route (vm, default_route);
469 current_time = vlib_time_now (vm);
471 while (due_time < current_time);
473 sleep_time = due_time - current_time;
480 VLIB_REGISTER_NODE (rd_cp_process_node) = {
481 .function = rd_cp_process,
482 .type = VLIB_NODE_TYPE_PROCESS,
483 .name = "rd-cp-process",
488 interrupt_process (void)
490 rd_cp_main_t *rm = &rd_cp_main;
491 vlib_main_t *vm = rm->vlib_main;
493 vlib_process_signal_event (vm, rd_cp_process_node.index,
494 RD_CP_EVENT_INTERRUPT, 0);
498 set_address_autoconfig (u32 sw_if_index, u8 enable, u8 install_default_routes)
500 rd_cp_main_t *rm = &rd_cp_main;
501 vlib_main_t *vm = rm->vlib_main;
502 vnet_main_t *vnm = rm->vnet_main;
503 interface_config_t *if_config;
504 interface_config_t empty_config = { 0, 0 };
505 slaac_address_t *slaac_address;
506 default_route_t *default_route;
509 install_default_routes = 0;
511 if (!vnet_sw_interface_is_api_valid (vnm, sw_if_index))
513 vlib_log_warn (rm->log_class, "Invalid sw_if_index");
519 /* process kickoff */
520 interrupt_process ();
524 vec_validate_init_empty (rm->config_by_sw_if_index, sw_if_index,
526 if_config = &rm->config_by_sw_if_index[sw_if_index];
528 if (!if_config->enabled && enable)
529 ip6_enable (sw_if_index);
531 if ((!if_config->enabled && enable)
532 || (!if_config->install_default_routes && install_default_routes))
533 router_solicitation_start_stop (sw_if_index, 1);
534 else if (if_config->enabled && !enable)
535 router_solicitation_start_stop (sw_if_index, 0);
537 if (if_config->enabled && !enable)
540 pool_foreach (slaac_address, rm->slaac_address_pool,
542 remove_slaac_address (vm, slaac_address);
546 if (if_config->install_default_routes && !install_default_routes)
549 pool_foreach (default_route, rm->default_route_pool,
551 remove_default_route (vm, default_route);
556 if_config->enabled = enable;
557 if_config->install_default_routes = install_default_routes;
562 static clib_error_t *
563 ip6_nd_address_autoconfig (vlib_main_t * vm,
564 unformat_input_t * input, vlib_cli_command_t * cmd)
566 rd_cp_main_t *rm = &rd_cp_main;
567 vnet_main_t *vnm = rm->vnet_main;
568 clib_error_t *error = 0;
569 u32 sw_if_index = ~0;
571 u8 default_route = 0;
573 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
576 (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
578 if (unformat (input, "default-route"))
580 if (unformat (input, "disable"))
586 if (sw_if_index != ~0)
588 if (set_address_autoconfig (sw_if_index, enable, default_route) != 0)
589 error = clib_error_return (0, "Invalid sw_if_index");
592 error = clib_error_return (0, "Missing sw_if_index");
598 * This command is used to enable ND address autoconfiguration
599 * on particular interface including setting up default routes.
603 * Example of how to enable ND address autoconfiguration:
604 * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0}
605 * Example of how to enable ND address autoconfiguration
606 * with setting up default routes:
607 * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0 default-route}
608 * Example of how to disable ND address autoconfiguration:
609 * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0 disable}
613 VLIB_CLI_COMMAND (ip6_nd_address_autoconfig_command, static) = {
614 .path = "ip6 nd address autoconfig",
615 .short_help = "ip6 nd address autoconfig <interface> [default-route|disable]",
616 .function = ip6_nd_address_autoconfig,
621 vl_api_ip6_nd_address_autoconfig_t_handler (vl_api_ip6_nd_address_autoconfig_t
624 vl_api_ip6_nd_address_autoconfig_reply_t *rmp;
628 VALIDATE_SW_IF_INDEX (mp);
630 sw_if_index = ntohl (mp->sw_if_index);
633 set_address_autoconfig (sw_if_index, mp->enable,
634 mp->install_default_routes);
636 BAD_SW_IF_INDEX_LABEL;
638 REPLY_MACRO (VL_API_SW_INTERFACE_SET_TABLE_REPLY);
641 #define vl_msg_name_crc_list
642 #include <vnet/ip/rd_cp.api.h>
643 #undef vl_msg_name_crc_list
646 setup_message_id_table (api_main_t * am)
648 #define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
649 foreach_vl_msg_name_crc_rd_cp;
653 static clib_error_t *
654 rd_cp_init (vlib_main_t * vm)
656 rd_cp_main_t *rm = &rd_cp_main;
657 api_main_t *am = &api_main;
660 rm->vnet_main = vnet_get_main ();
662 rm->node_index = rd_cp_process_node.index;
664 rm->log_class = vlib_log_register_class ("rd_cp", 0);
667 vl_msg_api_set_handlers(VL_API_##N, #n, \
668 vl_api_##n##_t_handler, \
670 vl_api_##n##_t_endian, \
671 vl_api_##n##_t_print, \
672 sizeof(vl_api_##n##_t), 0/* do NOT trace! */);
677 * Set up the (msg_name, crc, message-id) table
679 setup_message_id_table (am);
684 VLIB_INIT_FUNCTION (rd_cp_init);
687 * fd.io coding-style-patch-verification: ON
690 * eval: (c-set-style "gnu")