diff options
author | Juraj Sloboda <jsloboda@cisco.com> | 2018-01-15 10:39:21 +0100 |
---|---|---|
committer | Ole Trøan <otroan@employees.org> | 2018-03-16 10:37:18 +0000 |
commit | 4b9669dc116f6c9be5ef124e4aff7b201404b6ea (patch) | |
tree | 46c6cfe298640f36126693ab4196999adf959c32 /src/vnet/ip/ip6_neighbor.c | |
parent | e1b819efac7f8434f17d0482b631c2450e3dd5bb (diff) |
IPv6 ND Router discovery data plane (VPP-1095)
Add API call to send Router Solicitation messages.
Save info from incoming Router Advertisement messages and notify listeners.
Change-Id: Ie518b5492231e03291bd4c4280be4727bfecab46
Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
Diffstat (limited to 'src/vnet/ip/ip6_neighbor.c')
-rw-r--r-- | src/vnet/ip/ip6_neighbor.c | 346 |
1 files changed, 345 insertions, 1 deletions
diff --git a/src/vnet/ip/ip6_neighbor.c b/src/vnet/ip/ip6_neighbor.c index c011ec5e877..0df29c6bc02 100644 --- a/src/vnet/ip/ip6_neighbor.c +++ b/src/vnet/ip/ip6_neighbor.c @@ -154,6 +154,15 @@ typedef struct /* Link local address to use (defaults to underlying physical for logical interfaces */ ip6_address_t link_local_address; + + /* router solicitations sending state */ + u8 keep_sending_rs; /* when true then next fields are valid */ + icmp6_send_router_solicitation_params_t params; + f64 sleep_interval; + f64 due_time; + u32 n_left; + f64 start_time; + vlib_buffer_t *buffer; } ip6_radv_t; typedef struct @@ -198,6 +207,10 @@ typedef struct /* Wildcard nd report publisher */ uword wc_ip6_nd_publisher_node; uword wc_ip6_nd_publisher_et; + + /* Router advertisement report publisher */ + uword ip6_ra_publisher_node; + uword ip6_ra_publisher_et; } ip6_neighbor_main_t; /* ipv6 neighbor discovery - timer/event types */ @@ -220,6 +233,7 @@ static ip6_neighbor_main_t ip6_neighbor_main; static ip6_address_t ip6a_zero; /* ip6 address 0 */ static void wc_nd_signal_report (wc_nd_report_t * r); +static void ra_signal_report (ra_report_t * r); /** * @brief publish wildcard arp event @@ -263,6 +277,37 @@ wc_nd_set_publisher_node (uword node_index, uword event_type) nm->wc_ip6_nd_publisher_et = event_type; } +static int +ra_publish (ra_report_t * r) +{ + void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length); + vl_api_rpc_call_main_thread (ra_signal_report, (u8 *) r, sizeof *r); + return 0; +} + +static void +ra_signal_report (ra_report_t * r) +{ + vlib_main_t *vm = vlib_get_main (); + ip6_neighbor_main_t *nm = &ip6_neighbor_main; + uword ni = nm->ip6_ra_publisher_node; + uword et = nm->ip6_ra_publisher_et; + + if (ni == (uword) ~ 0) + return; + ra_report_t *q = vlib_process_signal_event_data (vm, ni, et, 1, sizeof *q); + + *q = *r; +} + +void +ra_set_publisher_node (uword node_index, uword event_type) +{ + ip6_neighbor_main_t *nm = &ip6_neighbor_main; + nm->ip6_ra_publisher_node = node_index; + nm->ip6_ra_publisher_et = event_type; +} + static u8 * format_ip6_neighbor_ip6_entry (u8 * s, va_list * va) { @@ -1877,6 +1922,22 @@ icmp6_router_advertisement (vlib_main_t * vm, if (error0 == ICMP6_ERROR_NONE) { + radv_info->keep_sending_rs = 0; + + ra_report_t r; + + r.sw_if_index = sw_if_index0; + memcpy (r.router_address, &ip0->src_address, 16); + r.current_hop_limit = h0->current_hop_limit; + r.flags = h0->flags; + r.router_lifetime_in_sec = + clib_net_to_host_u16 (h0->router_lifetime_in_sec); + r.neighbor_reachable_time_in_msec = + clib_net_to_host_u32 + (h0->neighbor_reachable_time_in_msec); + r.time_in_msec_between_retransmitted_neighbor_solicitations = clib_net_to_host_u32 (h0->time_in_msec_between_retransmitted_neighbor_solicitations); + r.prefixes = 0; + /* validate advertised information */ if ((h0->current_hop_limit && radv_info->curr_hop_limit) && (h0->current_hop_limit != @@ -1994,6 +2055,20 @@ icmp6_router_advertisement (vlib_main_t * vm, switch (option_type) { + case ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address: + { + icmp6_neighbor_discovery_ethernet_link_layer_address_option_t + * h = + (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t + *) (o0); + + if (opt_len < sizeof (*h)) + break; + + memcpy (r.slla, h->ethernet_address, 6); + } + break; + case ICMP6_NEIGHBOR_DISCOVERY_OPTION_mtu: { icmp6_neighbor_discovery_mtu_option_t *h = @@ -2003,6 +2078,8 @@ icmp6_router_advertisement (vlib_main_t * vm, if (opt_len < sizeof (*h)) break; + r.mtu = clib_net_to_host_u32 (h->mtu); + if ((h->mtu && radv_info->adv_link_mtu) && (h->mtu != clib_host_to_net_u32 @@ -2032,10 +2109,23 @@ icmp6_router_advertisement (vlib_main_t * vm, if (opt_len < sizeof (*h)) break; + vec_validate (r.prefixes, + vec_len (r.prefixes)); + ra_report_prefix_info_t *prefix = + vec_elt_at_index (r.prefixes, + vec_len (r.prefixes) - 1); + preferred = clib_net_to_host_u32 (h->preferred_time); valid = clib_net_to_host_u32 (h->valid_time); + prefix->preferred_time = preferred; + prefix->valid_time = valid; + prefix->flags = h->flags & 0xc0; + prefix->dst_address_length = + h->dst_address_length; + prefix->dst_address = h->dst_address; + /* look for matching prefix - if we our advertising it, it better be consistant */ /* *INDENT-OFF* */ pool_foreach (pr_info, radv_info->adv_prefixes_pool, @@ -2076,6 +2166,7 @@ icmp6_router_advertisement (vlib_main_t * vm, break; } } + ra_publish (&r); } } } @@ -2093,7 +2184,7 @@ icmp6_router_advertisement (vlib_main_t * vm, vlib_put_next_frame (vm, node, next_index, n_left_to_next); } - /* Account for router advertisements sent. */ + /* Account for router advertisements received. */ vlib_error_count (vm, error_node->node_index, ICMP6_ERROR_ROUTER_ADVERTISEMENTS_RX, n_advertisements_rcvd); @@ -2101,6 +2192,252 @@ icmp6_router_advertisement (vlib_main_t * vm, return frame->n_vectors; } +static inline f64 +random_f64_from_to (f64 from, f64 to) +{ + static u32 seed = 0; + static u8 seed_set = 0; + if (!seed_set) + { + seed = random_default_seed (); + seed_set = 1; + } + return random_f64 (&seed) * (to - from) + from; +} + +static inline u8 +get_mac_address (u32 sw_if_index, u8 * address) +{ + vnet_main_t *vnm = vnet_get_main (); + vnet_hw_interface_t *hw_if = vnet_get_sup_hw_interface (vnm, sw_if_index); + if (!hw_if->hw_address) + return 1; + clib_memcpy (address, hw_if->hw_address, 6); + return 0; +} + +static inline vlib_buffer_t * +create_buffer_for_rs (vlib_main_t * vm, ip6_radv_t * radv_info) +{ + u32 bi0; + vlib_buffer_t *p0; + vlib_buffer_free_list_t *fl; + icmp6_router_solicitation_header_t *rh; + u16 payload_length; + int bogus_length; + u32 sw_if_index; + + sw_if_index = radv_info->sw_if_index; + + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + { + clib_warning ("buffer allocation failure"); + return 0; + } + + p0 = vlib_get_buffer (vm, bi0); + fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); + vlib_buffer_init_for_free_list (p0, fl); + VLIB_BUFFER_TRACE_TRAJECTORY_INIT (p0); + p0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; + + vnet_buffer (p0)->sw_if_index[VLIB_RX] = sw_if_index; + vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index; + + vnet_buffer (p0)->ip.adj_index[VLIB_TX] = radv_info->mcast_adj_index; + + rh = vlib_buffer_get_current (p0); + p0->current_length = sizeof (*rh); + + rh->neighbor.icmp.type = ICMP6_router_solicitation; + rh->neighbor.icmp.code = 0; + rh->neighbor.icmp.checksum = 0; + rh->neighbor.reserved_must_be_zero = 0; + + rh->link_layer_option.header.type = + ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address; + if (0 != get_mac_address (sw_if_index, + rh->link_layer_option.ethernet_address)) + { + clib_warning ("interface with sw_if_index %u has no mac address", + sw_if_index); + vlib_buffer_free (vm, &bi0, 1); + return 0; + } + rh->link_layer_option.header.n_data_u64s = 1; + + payload_length = sizeof (rh->neighbor) + sizeof (u64); + + rh->ip.ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 (0x6 << 28); + rh->ip.payload_length = clib_host_to_net_u16 (payload_length); + rh->ip.protocol = IP_PROTOCOL_ICMP6; + rh->ip.hop_limit = 255; + rh->ip.src_address = radv_info->link_local_address; + /* set address ff02::2 */ + rh->ip.dst_address.as_u64[0] = clib_host_to_net_u64 (0xff02L << 48); + rh->ip.dst_address.as_u64[1] = clib_host_to_net_u64 (2); + + rh->neighbor.icmp.checksum = ip6_tcp_udp_icmp_compute_checksum (vm, p0, + &rh->ip, + &bogus_length); + + return p0; +} + +static inline void +stop_sending_rs (vlib_main_t * vm, ip6_radv_t * ra) +{ + u32 bi0; + + ra->keep_sending_rs = 0; + if (ra->buffer) + { + bi0 = vlib_get_buffer_index (vm, ra->buffer); + vlib_buffer_free (vm, &bi0, 1); + ra->buffer = 0; + } +} + +static inline bool +check_send_rs (vlib_main_t * vm, ip6_radv_t * radv_info, f64 current_time, + f64 * due_time) +{ + vlib_buffer_t *p0; + vlib_frame_t *f; + u32 *to_next; + u32 next_index; + vlib_buffer_t *c0; + u32 ci0; + + icmp6_send_router_solicitation_params_t *params; + + if (!radv_info->keep_sending_rs) + return false; + + params = &radv_info->params; + + if (radv_info->due_time > current_time) + { + *due_time = radv_info->due_time; + return true; + } + + p0 = radv_info->buffer; + + next_index = ip6_rewrite_mcast_node.index; + + c0 = vlib_buffer_copy (vm, p0); + ci0 = vlib_get_buffer_index (vm, c0); + + f = vlib_get_frame_to_node (vm, next_index); + to_next = vlib_frame_vector_args (f); + to_next[0] = ci0; + f->n_vectors = 1; + vlib_put_frame_to_node (vm, next_index, f); + + if (params->mrc != 0 && --radv_info->n_left == 0) + stop_sending_rs (vm, radv_info); + else + { + radv_info->sleep_interval = + (2 + random_f64_from_to (-0.1, 0.1)) * radv_info->sleep_interval; + if (radv_info->sleep_interval > params->mrt) + radv_info->sleep_interval = + (1 + random_f64_from_to (-0.1, 0.1)) * params->mrt; + + radv_info->due_time = current_time + radv_info->sleep_interval; + + if (params->mrd != 0 + && current_time > radv_info->start_time + params->mrd) + stop_sending_rs (vm, radv_info); + else + *due_time = radv_info->due_time; + } + + return radv_info->keep_sending_rs; +} + +static uword +send_rs_process (vlib_main_t * vm, vlib_node_runtime_t * rt, + vlib_frame_t * f0) +{ + ip6_neighbor_main_t *nm = &ip6_neighbor_main; + ip6_radv_t *radv_info; + uword *event_data = 0; + f64 sleep_time = 1e9; + f64 current_time; + f64 due_time; + f64 dt = 0; + + while (true) + { + vlib_process_wait_for_event_or_clock (vm, sleep_time); + vlib_process_get_events (vm, &event_data); + vec_reset_length (event_data); + + current_time = vlib_time_now (vm); + do + { + due_time = current_time + 1e9; + /* *INDENT-OFF* */ + pool_foreach (radv_info, nm->if_radv_pool, + ({ + if (check_send_rs (vm, radv_info, current_time, &dt) + && (dt < due_time)) + due_time = dt; + })); + /* *INDENT-ON* */ + current_time = vlib_time_now (vm); + } + while (due_time < current_time); + + sleep_time = due_time - current_time; + } + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (send_rs_process_node) = { + .function = send_rs_process, + .type = VLIB_NODE_TYPE_PROCESS, + .name = "send-rs-process", +}; +/* *INDENT-ON* */ + +void +icmp6_send_router_solicitation (vlib_main_t * vm, u32 sw_if_index, u8 stop, + icmp6_send_router_solicitation_params_t * + params) +{ + ip6_neighbor_main_t *nm = &ip6_neighbor_main; + u32 rai; + ip6_radv_t *ra = 0; + + ASSERT (~0 != sw_if_index); + + rai = nm->if_radv_pool_index_by_sw_if_index[sw_if_index]; + ra = pool_elt_at_index (nm->if_radv_pool, rai); + + if (stop) + stop_sending_rs (vm, ra); + else + { + ra->keep_sending_rs = 1; + ra->params = *params; + ra->n_left = params->mrc; + ra->start_time = vlib_time_now (vm); + ra->sleep_interval = (1 + random_f64_from_to (-0.1, 0.1)) * params->irt; + ra->due_time = 0; /* send first packet ASAP */ + ra->buffer = create_buffer_for_rs (vm, ra); + if (!ra->buffer) + ra->keep_sending_rs = 0; + else + vlib_process_signal_event (vm, send_rs_process_node.index, 1, 0); + } +} + /** * @brief Add a multicast Address to the advertised MLD set */ @@ -2226,6 +2563,9 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm, mhash_free (&a->address_to_prefix_index); mhash_free (&a->address_to_mldp_index); + if (a->keep_sending_rs) + a->keep_sending_rs = 0; + pool_put (nm->if_radv_pool, a); nm->if_radv_pool_index_by_sw_if_index[sw_if_index] = ~0; ri = ~0; @@ -2289,6 +2629,8 @@ ip6_neighbor_sw_interface_add_del (vnet_main_t * vnm, VNET_LINK_IP6, sw_if_index); + a->keep_sending_rs = 0; + /* add multicast groups we will always be reporting */ ip6_neighbor_add_mld_grp (a, IP6_MULTICAST_SCOPE_link_local, @@ -4030,6 +4372,8 @@ ip6_neighbor_init (vlib_main_t * vm) nm->wc_ip6_nd_publisher_node = (uword) ~ 0; + nm->ip6_ra_publisher_node = (uword) ~ 0; + #if 0 /* $$$$ Hack fix for today */ vec_validate_init_empty |