From 4b9669dc116f6c9be5ef124e4aff7b201404b6ea Mon Sep 17 00:00:00 2001 From: Juraj Sloboda Date: Mon, 15 Jan 2018 10:39:21 +0100 Subject: [PATCH] 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 --- src/vlibapi/api_helper_macros.h | 3 +- src/vnet/ip/ip.api | 88 +++++++++- src/vnet/ip/ip6_neighbor.c | 346 +++++++++++++++++++++++++++++++++++++++- src/vnet/ip/ip6_neighbor.h | 38 +++++ src/vnet/ip/ip_api.c | 146 ++++++++++++++++- test/test_ip6.py | 116 +++++++++++++- test/util.py | 5 + test/vpp_papi_provider.py | 14 ++ 8 files changed, 747 insertions(+), 9 deletions(-) diff --git a/src/vlibapi/api_helper_macros.h b/src/vlibapi/api_helper_macros.h index 23340425978..de3c09bdb89 100644 --- a/src/vlibapi/api_helper_macros.h +++ b/src/vlibapi/api_helper_macros.h @@ -218,7 +218,8 @@ _(from_netconf_client) \ _(oam_events) \ _(bfd_events) \ _(wc_ip6_nd_events) \ -_(wc_ip4_arp_events) +_(wc_ip4_arp_events) \ +_(ip6_ra_events) typedef struct { diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api index 282f531e4fb..d6c5f6bcd02 100644 --- a/src/vnet/ip/ip.api +++ b/src/vnet/ip/ip.api @@ -19,7 +19,7 @@ called through a shared memory interface. */ -option version = "1.1.0"; +option version = "1.2.0"; import "vnet/fib/fib_types.api"; /** \brief Add / del table request @@ -304,6 +304,30 @@ define ip6nd_proxy_dump u32 context; }; +/** \brief Start / stop sending router solicitation + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param irt - initial retransmission time + @param mrt - maximum retransmission time + @param mrc - maximum retransmission count + @param mrd - maximum retransmission duration + @param sw_if_index - software interface index of interface + for sending router solicitation + @param stop - if non-zero then stop sending router solicitation, + otherwise start sending router solicitation +*/ +autoreply define ip6nd_send_router_solicitation +{ + u32 client_index; + u32 context; + u32 irt; + u32 mrt; + u32 mrc; + u32 mrd; + u32 sw_if_index; + u8 stop; +}; + /** \brief IPv6 interface enable / disable request @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -699,6 +723,68 @@ define ip6_nd_event u8 mac_ip; }; +service { + rpc want_ip6_ra_events returns want_ip6_ra_events_reply + events ip6_ra_event; +}; + +/** \brief Register for ip6 router advertisement events + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param enable_disable - 1 => register for events, 0 => cancel registration + @param pid - sender's pid +*/ +autoreply define want_ip6_ra_events +{ + u32 client_index; + u32 context; + u8 enable_disable; + u32 pid; +}; + +/** \brief Struct representing RA prefix info + @param dst_address - RA prefix info destination address + @param dst_address_length - RA prefix info destination address length + @param flags - RA prefix info flags + @param valid_time - RA prefix info valid time + @param preferred_time - RA prefix info preferred time +*/ +typeonly define ip6_ra_prefix_info +{ + u8 dst_address[16]; + u8 dst_address_length; + u8 flags; + u32 valid_time; + u32 preferred_time; +}; + +/** \brief Tell client about a router advertisement event + @param client_index - opaque cookie to identify the sender + @param pid - client pid registered to receive notification + @param current_hop_limit - RA current hop limit + @param flags - RA flags + @param router_lifetime_in_sec - RA lifetime in seconds + @param neighbor_reachable_time_in_msec - RA neighbor reachable time in msec + @param time_in_msec_between_retransmitted_neighbor_solicitations - + time in msec between retransmitted neighbor solicitations + @param n_prefixes - + @param prefixes - +*/ +define ip6_ra_event +{ + u32 client_index; + u32 pid; + u32 sw_if_index; + u8 router_address[16]; + u8 current_hop_limit; + u8 flags; + u16 router_lifetime_in_sec; + u32 neighbor_reachable_time_in_msec; + u32 time_in_msec_between_retransmitted_neighbor_solicitations; + u32 n_prefixes; + vl_api_ip6_ra_prefix_info_t prefixes[n_prefixes]; +}; + service { rpc want_ip6_nd_events returns want_ip6_nd_events_reply events ip6_nd_event; 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 diff --git a/src/vnet/ip/ip6_neighbor.h b/src/vnet/ip/ip6_neighbor.h index ed80381b35c..e46a6b14221 100644 --- a/src/vnet/ip/ip6_neighbor.h +++ b/src/vnet/ip/ip6_neighbor.h @@ -98,6 +98,44 @@ typedef struct void wc_nd_set_publisher_node (uword node_index, uword event_type); +typedef struct +{ + u32 irt; + u32 mrt; + u32 mrc; + u32 mrd; +} icmp6_send_router_solicitation_params_t; + +void icmp6_send_router_solicitation (vlib_main_t * vm, u32 sw_if_index, + u8 stop, + icmp6_send_router_solicitation_params_t * + params); + +typedef struct +{ + ip6_address_t dst_address; + u8 dst_address_length; + u8 flags; + u32 valid_time; + u32 preferred_time; +} ra_report_prefix_info_t; + +typedef struct +{ + u32 sw_if_index; + u8 router_address[16]; + u8 current_hop_limit; + u8 flags; + u16 router_lifetime_in_sec; + u32 neighbor_reachable_time_in_msec; + u32 time_in_msec_between_retransmitted_neighbor_solicitations; + u8 slla[6]; + u32 mtu; + ra_report_prefix_info_t *prefixes; +} ra_report_t; + +void ra_set_publisher_node (uword node_index, uword event_type); + #endif /* included_ip6_neighbor_h */ /* diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c index 9cd625799b6..0a055118732 100644 --- a/src/vnet/ip/ip_api.c +++ b/src/vnet/ip/ip_api.c @@ -78,6 +78,7 @@ _(IP_NEIGHBOR_ADD_DEL, ip_neighbor_add_del) \ _(SET_ARP_NEIGHBOR_LIMIT, set_arp_neighbor_limit) \ _(WANT_IP4_ARP_EVENTS, want_ip4_arp_events) \ _(WANT_IP6_ND_EVENTS, want_ip6_nd_events) \ +_(WANT_IP6_RA_EVENTS, want_ip6_ra_events) \ _(PROXY_ARP_ADD_DEL, proxy_arp_add_del) \ _(PROXY_ARP_INTFC_ENABLE_DISABLE, proxy_arp_intfc_enable_disable) \ _(RESET_FIB, reset_fib) \ @@ -90,6 +91,7 @@ _(SW_INTERFACE_IP6ND_RA_CONFIG, sw_interface_ip6nd_ra_config) \ _(SW_INTERFACE_IP6ND_RA_PREFIX, sw_interface_ip6nd_ra_prefix) \ _(IP6ND_PROXY_ADD_DEL, ip6nd_proxy_add_del) \ _(IP6ND_PROXY_DUMP, ip6nd_proxy_dump) \ +_(IP6ND_SEND_ROUTER_SOLICITATION, ip6nd_send_router_solicitation) \ _(SW_INTERFACE_IP6_ENABLE_DISABLE, sw_interface_ip6_enable_disable ) \ _(SW_INTERFACE_IP6_SET_LINK_LOCAL_ADDRESS, \ sw_interface_ip6_set_link_local_address) \ @@ -1650,6 +1652,32 @@ vl_api_ip6nd_proxy_add_del_t_handler (vl_api_ip6nd_proxy_add_del_t * mp) REPLY_MACRO (VL_API_IP6ND_PROXY_ADD_DEL_REPLY); } +static void + vl_api_ip6nd_send_router_solicitation_t_handler + (vl_api_ip6nd_send_router_solicitation_t * mp) +{ + vl_api_ip6nd_send_router_solicitation_reply_t *rmp; + icmp6_send_router_solicitation_params_t params; + vlib_main_t *vm = vlib_get_main (); + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + BAD_SW_IF_INDEX_LABEL; + REPLY_MACRO (VL_API_IP6ND_SEND_ROUTER_SOLICITATION_REPLY); + + if (rv != 0) + return; + + params.irt = ntohl (mp->irt); + params.mrt = ntohl (mp->mrt); + params.mrc = ntohl (mp->mrc); + params.mrd = ntohl (mp->mrd); + + icmp6_send_router_solicitation (vm, ntohl (mp->sw_if_index), mp->stop, + ¶ms); +} + static void vl_api_sw_interface_ip6_enable_disable_t_handler (vl_api_sw_interface_ip6_enable_disable_t * mp) @@ -2181,7 +2209,7 @@ nd_change_delete_callback (u32 pool_index, u8 * notused) static vlib_node_registration_t wc_arp_process_node; enum -{ WC_ARP_REPORT, WC_ND_REPORT }; +{ WC_ARP_REPORT, WC_ND_REPORT, RA_REPORT, REPORT_MAX }; static uword wc_arp_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) @@ -2280,6 +2308,74 @@ wc_arp_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) /* *INDENT-ON* */ } } + else if (event_type == RA_REPORT) + { + ra_report_t *ra_events = event_data; + for (i = 0; i < vec_len (ra_events); i++) + { + vpe_client_registration_t *reg; + /* *INDENT-OFF* */ + pool_foreach(reg, vpe_api_main.ip6_ra_events_registrations, + ({ + vl_api_registration_t *vl_reg; + vl_reg = + vl_api_client_index_to_registration (reg->client_index); + if (vl_reg && vl_api_can_send_msg (vl_reg)) + { + u32 event_size = + sizeof (vl_api_ip6_ra_event_t) + + vec_len (ra_events[i].prefixes) * + sizeof (vl_api_ip6_ra_prefix_info_t); + vl_api_ip6_ra_event_t *event = + vl_msg_api_alloc (event_size); + memset (event, 0, event_size); + event->_vl_msg_id = htons (VL_API_IP6_RA_EVENT); + event->client_index = reg->client_index; + event->pid = reg->client_pid; + + event->sw_if_index = clib_host_to_net_u32 (ra_events[i].sw_if_index); + + memcpy (event->router_address, ra_events[i].router_address, 16); + + event->current_hop_limit = ra_events[i].current_hop_limit; + event->flags = ra_events[i].flags; + event->router_lifetime_in_sec = + clib_host_to_net_u16 (ra_events + [i].router_lifetime_in_sec); + event->neighbor_reachable_time_in_msec = + clib_host_to_net_u32 (ra_events + [i].neighbor_reachable_time_in_msec); + event->time_in_msec_between_retransmitted_neighbor_solicitations + = + clib_host_to_net_u32 (ra_events + [i].time_in_msec_between_retransmitted_neighbor_solicitations); + + event->n_prefixes = + clib_host_to_net_u32 (vec_len (ra_events[i].prefixes)); + vl_api_ip6_ra_prefix_info_t *prefix = + (typeof (prefix)) event->prefixes; + u32 j; + for (j = 0; j < vec_len (ra_events[i].prefixes); j++) + { + ra_report_prefix_info_t *info = + &ra_events[i].prefixes[j]; + memcpy (prefix->dst_address, info->dst_address.as_u8, + 16); + prefix->dst_address_length = info->dst_address_length; + prefix->flags = info->flags; + prefix->valid_time = + clib_host_to_net_u32 (info->valid_time); + prefix->preferred_time = + clib_host_to_net_u32 (info->preferred_time); + prefix++; + } + + vl_api_send_msg (vl_reg, (u8 *) event); + } + })); + /* *INDENT-ON* */ + } + } vlib_process_put_event_data (vm, event_data); } @@ -2345,7 +2441,7 @@ vl_api_want_ip4_arp_events_t_handler (vl_api_want_ip4_arp_events_t * mp) hash_unset (am->wc_ip4_arp_events_registration_hash, mp->client_index); if (pool_elts (am->wc_ip4_arp_events_registrations) == 0) - wc_arp_set_publisher_node (~0, WC_ARP_REPORT); + wc_arp_set_publisher_node (~0, REPORT_MAX); goto reply; } } @@ -2430,7 +2526,7 @@ vl_api_want_ip6_nd_events_t_handler (vl_api_want_ip6_nd_events_t * mp) hash_unset (am->wc_ip6_nd_events_registration_hash, mp->client_index); if (pool_elts (am->wc_ip6_nd_events_registrations) == 0) - wc_nd_set_publisher_node (~0, 2); + wc_nd_set_publisher_node (~0, REPORT_MAX); goto reply; } } @@ -2484,6 +2580,50 @@ reply: REPLY_MACRO (VL_API_WANT_IP6_ND_EVENTS_REPLY); } +static void +vl_api_want_ip6_ra_events_t_handler (vl_api_want_ip6_ra_events_t * mp) +{ + vpe_api_main_t *am = &vpe_api_main; + vl_api_want_ip6_ra_events_reply_t *rmp; + int rv = 0; + + uword *p = hash_get (am->ip6_ra_events_registration_hash, mp->client_index); + vpe_client_registration_t *rp; + if (p) + { + if (mp->enable_disable) + { + clib_warning ("pid %d: already enabled...", ntohl (mp->pid)); + rv = VNET_API_ERROR_INVALID_REGISTRATION; + goto reply; + } + else + { + rp = pool_elt_at_index (am->ip6_ra_events_registrations, p[0]); + pool_put (am->ip6_ra_events_registrations, rp); + hash_unset (am->ip6_ra_events_registration_hash, mp->client_index); + if (pool_elts (am->ip6_ra_events_registrations) == 0) + ra_set_publisher_node (~0, REPORT_MAX); + goto reply; + } + } + if (mp->enable_disable == 0) + { + clib_warning ("pid %d: already disabled...", ntohl (mp->pid)); + rv = VNET_API_ERROR_INVALID_REGISTRATION; + goto reply; + } + pool_get (am->ip6_ra_events_registrations, rp); + rp->client_index = mp->client_index; + rp->client_pid = ntohl (mp->pid); + hash_set (am->ip6_ra_events_registration_hash, rp->client_index, + rp - am->ip6_ra_events_registrations); + ra_set_publisher_node (wc_arp_process_node.index, RA_REPORT); + +reply: + REPLY_MACRO (VL_API_WANT_IP6_RA_EVENTS_REPLY); +} + static void vl_api_proxy_arp_add_del_t_handler (vl_api_proxy_arp_add_del_t * mp) { diff --git a/test/test_ip6.py b/test/test_ip6.py index 68b077c1f8f..70ebc36dcc0 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -1,9 +1,10 @@ #!/usr/bin/env python import unittest -from socket import AF_INET6 +import socket from framework import VppTestCase, VppTestRunner +from util import ppp, ip6_normalize from vpp_sub_interface import VppSubInterface, VppDot1QSubint from vpp_pg_interface import is_ipv6_misc from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \ @@ -18,14 +19,15 @@ from scapy.layers.inet6 import IPv6, UDP, TCP, ICMPv6ND_NS, ICMPv6ND_RS, \ ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \ ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types, \ ICMPv6TimeExceeded - -from util import ppp from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ptop, in6_islladdr, \ in6_mactoifaceid, in6_ismaddr from scapy.utils import inet_pton, inet_ntop from scapy.contrib.mpls import MPLS +AF_INET6 = socket.AF_INET6 + + def mk_ll_addr(mac): euid = in6_mactoifaceid(mac) addr = "fe80::" + euid @@ -895,6 +897,114 @@ class TestIPv6(TestIPv6ND): self.pg0.ip6_ra_config(no=1, suppress=1, send_unicast=0) +class TestIPv6RD(TestIPv6ND): + """ IPv6 Router Discovery Test Case """ + + @classmethod + def setUpClass(cls): + super(TestIPv6RD, cls).setUpClass() + + def setUp(self): + super(TestIPv6RD, self).setUp() + + # create 2 pg interfaces + self.create_pg_interfaces(range(2)) + + self.interfaces = list(self.pg_interfaces) + + # setup all interfaces + for i in self.interfaces: + i.admin_up() + i.config_ip6() + + def tearDown(self): + super(TestIPv6RD, self).tearDown() + + def test_rd_send_router_solicitation(self): + """ Verify router solicitation packets """ + + count = 2 + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.vapi.ip6nd_send_router_solicitation(self.pg1.sw_if_index, + mrc=count) + rx_list = self.pg1.get_capture(count, timeout=3) + self.assertEqual(len(rx_list), count) + for packet in rx_list: + self.assertTrue(packet.haslayer(IPv6)) + self.assertTrue(packet[IPv6].haslayer(ICMPv6ND_RS)) + dst = ip6_normalize(packet[IPv6].dst) + dst2 = ip6_normalize("ff02::2") + self.assert_equal(dst, dst2) + src = ip6_normalize(packet[IPv6].src) + src2 = ip6_normalize(self.pg1.local_ip6_ll) + self.assert_equal(src, src2) + self.assertTrue(packet[ICMPv6ND_RS].haslayer(ICMPv6NDOptSrcLLAddr)) + self.assert_equal(packet[ICMPv6NDOptSrcLLAddr].lladdr, + self.pg1.local_mac) + + def verify_prefix_info(self, reported_prefix, prefix_option): + prefix = socket.inet_pton(socket.AF_INET6, + prefix_option.getfieldval("prefix")) + self.assert_equal(reported_prefix.dst_address, prefix) + self.assert_equal(reported_prefix.dst_address_length, + prefix_option.getfieldval("prefixlen")) + L = prefix_option.getfieldval("L") + A = prefix_option.getfieldval("A") + option_flags = (L << 7) | (A << 6) + self.assert_equal(reported_prefix.flags, option_flags) + self.assert_equal(reported_prefix.valid_time, + prefix_option.getfieldval("validlifetime")) + self.assert_equal(reported_prefix.preferred_time, + prefix_option.getfieldval("preferredlifetime")) + + def test_rd_receive_router_advertisement(self): + """ Verify events triggered by received RA packets """ + + self.vapi.want_ip6_ra_events() + + prefix_info_1 = ICMPv6NDOptPrefixInfo( + prefix="1::2", + prefixlen=50, + validlifetime=200, + preferredlifetime=500, + L=1, + A=1, + ) + + prefix_info_2 = ICMPv6NDOptPrefixInfo( + prefix="7::4", + prefixlen=20, + validlifetime=70, + preferredlifetime=1000, + L=1, + A=0, + ) + + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IPv6(dst=self.pg1.local_ip6_ll, + src=mk_ll_addr(self.pg1.remote_mac)) / + ICMPv6ND_RA() / + prefix_info_1 / + prefix_info_2) + self.pg1.add_stream([p]) + self.pg_start() + + ev = self.vapi.wait_for_event(10, "ip6_ra_event") + + self.assert_equal(ev.current_hop_limit, 0) + self.assert_equal(ev.flags, 8) + self.assert_equal(ev.router_lifetime_in_sec, 1800) + self.assert_equal(ev.neighbor_reachable_time_in_msec, 0) + self.assert_equal( + ev.time_in_msec_between_retransmitted_neighbor_solicitations, 0) + + self.assert_equal(ev.n_prefixes, 2) + + self.verify_prefix_info(ev.prefixes[0], prefix_info_1) + self.verify_prefix_info(ev.prefixes[1], prefix_info_2) + + class IPv6NDProxyTest(TestIPv6ND): """ IPv6 ND ProxyTest Case """ diff --git a/test/util.py b/test/util.py index 512bf9e3b7e..1044aa83a89 100644 --- a/test/util.py +++ b/test/util.py @@ -65,6 +65,11 @@ def mk_ll_addr(mac): return addr +def ip6_normalize(ip6): + return socket.inet_ntop(socket.AF_INET6, + socket.inet_pton(socket.AF_INET6, ip6)) + + class NumericConstant(object): __metaclass__ = ABCMeta diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 22c6a0d4597..8fc0bfdae85 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -466,6 +466,20 @@ class VppPapiProvider(object): 'address': address, 'pid': os.getpid(), }) + def want_ip6_ra_events(self, enable_disable=1): + return self.api(self.papi.want_ip6_ra_events, + {'enable_disable': enable_disable, + 'pid': os.getpid(), }) + + def ip6nd_send_router_solicitation(self, sw_if_index, irt=1, mrt=120, + mrc=0, mrd=0): + return self.api(self.papi.ip6nd_send_router_solicitation, + {'irt': irt, + 'mrt': mrt, + 'mrc': mrc, + 'mrd': mrd, + 'sw_if_index': sw_if_index}) + def want_macs_learn_events(self, enable_disable=1, scan_delay=0, max_macs_in_event=0, learn_limit=0): return self.api(self.papi.want_l2_macs_events, -- 2.16.6