From: Neale Ranns Date: Wed, 25 Apr 2018 13:34:31 +0000 (-0700) Subject: IPv6 NS: use the mcast rewrite node to fill in the destination MAC X-Git-Tag: v18.07-rc1~402 X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commitdiff_plain;h=c7b8f203c0740ddd36bced39439aef63ea092d59 IPv6 NS: use the mcast rewrite node to fill in the destination MAC - move the IPv6 incomplete and glean node to ip6_neighbour.c (so it has access to ip6_neighbour_main_t) - use the RA info config on the interface to find the multicast adj to use Change-Id: I835e419072abe54fb09dafb0e7eb0a9e50eba1af Signed-off-by: Neale Ranns --- diff --git a/src/vnet/ip/ip6_forward.c b/src/vnet/ip/ip6_forward.c index f4e45a4702a..b53cdc47346 100644 --- a/src/vnet/ip/ip6_forward.c +++ b/src/vnet/ip/ip6_forward.c @@ -1400,269 +1400,6 @@ ip6_register_protocol (u32 protocol, u32 node_index) vlib_node_add_next (vm, ip6_local_node.index, node_index); } -typedef enum -{ - IP6_DISCOVER_NEIGHBOR_NEXT_DROP, - IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX, - IP6_DISCOVER_NEIGHBOR_N_NEXT, -} ip6_discover_neighbor_next_t; - -typedef enum -{ - IP6_DISCOVER_NEIGHBOR_ERROR_DROP, - IP6_DISCOVER_NEIGHBOR_ERROR_REQUEST_SENT, - IP6_DISCOVER_NEIGHBOR_ERROR_NO_SOURCE_ADDRESS, -} ip6_discover_neighbor_error_t; - -static uword -ip6_discover_neighbor_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, int is_glean) -{ - vnet_main_t *vnm = vnet_get_main (); - ip6_main_t *im = &ip6_main; - ip_lookup_main_t *lm = &im->lookup_main; - u32 *from, *to_next_drop; - uword n_left_from, n_left_to_next_drop; - static f64 time_last_seed_change = -1e100; - static u32 hash_seeds[3]; - static uword hash_bitmap[256 / BITS (uword)]; - f64 time_now; - int bogus_length; - - if (node->flags & VLIB_NODE_FLAG_TRACE) - ip6_forward_next_trace (vm, node, frame, VLIB_TX); - - time_now = vlib_time_now (vm); - if (time_now - time_last_seed_change > 1e-3) - { - uword i; - u32 *r = clib_random_buffer_get_data (&vm->random_buffer, - sizeof (hash_seeds)); - for (i = 0; i < ARRAY_LEN (hash_seeds); i++) - hash_seeds[i] = r[i]; - - /* Mark all hash keys as been not-seen before. */ - for (i = 0; i < ARRAY_LEN (hash_bitmap); i++) - hash_bitmap[i] = 0; - - time_last_seed_change = time_now; - } - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - - while (n_left_from > 0) - { - vlib_get_next_frame (vm, node, IP6_DISCOVER_NEIGHBOR_NEXT_DROP, - to_next_drop, n_left_to_next_drop); - - while (n_left_from > 0 && n_left_to_next_drop > 0) - { - vlib_buffer_t *p0; - ip6_header_t *ip0; - u32 pi0, adj_index0, a0, b0, c0, m0, sw_if_index0, drop0; - uword bm0; - ip_adjacency_t *adj0; - vnet_hw_interface_t *hw_if0; - u32 next0; - - pi0 = from[0]; - - p0 = vlib_get_buffer (vm, pi0); - - adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX]; - - ip0 = vlib_buffer_get_current (p0); - - adj0 = adj_get (adj_index0); - - if (!is_glean) - { - ip0->dst_address.as_u64[0] = - adj0->sub_type.nbr.next_hop.ip6.as_u64[0]; - ip0->dst_address.as_u64[1] = - adj0->sub_type.nbr.next_hop.ip6.as_u64[1]; - } - - a0 = hash_seeds[0]; - b0 = hash_seeds[1]; - c0 = hash_seeds[2]; - - sw_if_index0 = adj0->rewrite_header.sw_if_index; - vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0; - - a0 ^= sw_if_index0; - b0 ^= ip0->dst_address.as_u32[0]; - c0 ^= ip0->dst_address.as_u32[1]; - - hash_v3_mix32 (a0, b0, c0); - - b0 ^= ip0->dst_address.as_u32[2]; - c0 ^= ip0->dst_address.as_u32[3]; - - hash_v3_finalize32 (a0, b0, c0); - - c0 &= BITS (hash_bitmap) - 1; - c0 = c0 / BITS (uword); - m0 = (uword) 1 << (c0 % BITS (uword)); - - bm0 = hash_bitmap[c0]; - drop0 = (bm0 & m0) != 0; - - /* Mark it as seen. */ - hash_bitmap[c0] = bm0 | m0; - - from += 1; - n_left_from -= 1; - to_next_drop[0] = pi0; - to_next_drop += 1; - n_left_to_next_drop -= 1; - - hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0); - - /* If the interface is link-down, drop the pkt */ - if (!(hw_if0->flags & VNET_HW_INTERFACE_FLAG_LINK_UP)) - drop0 = 1; - - p0->error = - node->errors[drop0 ? IP6_DISCOVER_NEIGHBOR_ERROR_DROP - : IP6_DISCOVER_NEIGHBOR_ERROR_REQUEST_SENT]; - if (drop0) - continue; - - /* - * the adj has been updated to a rewrite but the node the DPO that got - * us here hasn't - yet. no big deal. we'll drop while we wait. - */ - if (IP_LOOKUP_NEXT_REWRITE == adj0->lookup_next_index) - continue; - - { - u32 bi0 = 0; - icmp6_neighbor_solicitation_header_t *h0; - vlib_buffer_t *b0; - - h0 = vlib_packet_template_get_packet - (vm, &im->discover_neighbor_packet_template, &bi0); - - /* - * Build ethernet header. - * Choose source address based on destination lookup - * adjacency. - */ - if (!ip6_src_address_for_packet (lm, - sw_if_index0, - &ip0->dst_address, - &h0->ip.src_address)) - { - /* There is no address on the interface */ - p0->error = - node->errors[IP6_DISCOVER_NEIGHBOR_ERROR_NO_SOURCE_ADDRESS]; - vlib_buffer_free (vm, &bi0, 1); - continue; - } - - /* - * Destination address is a solicited node multicast address. - * We need to fill in - * the low 24 bits with low 24 bits of target's address. - */ - h0->ip.dst_address.as_u8[13] = ip0->dst_address.as_u8[13]; - h0->ip.dst_address.as_u8[14] = ip0->dst_address.as_u8[14]; - h0->ip.dst_address.as_u8[15] = ip0->dst_address.as_u8[15]; - - h0->neighbor.target_address = ip0->dst_address; - - clib_memcpy (h0->link_layer_option.ethernet_address, - hw_if0->hw_address, vec_len (hw_if0->hw_address)); - - /* $$$$ appears we need this; why is the checksum non-zero? */ - h0->neighbor.icmp.checksum = 0; - h0->neighbor.icmp.checksum = - ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h0->ip, - &bogus_length); - - ASSERT (bogus_length == 0); - - vlib_buffer_copy_trace_flag (vm, p0, bi0); - b0 = vlib_get_buffer (vm, bi0); - vnet_buffer (b0)->sw_if_index[VLIB_TX] - = vnet_buffer (p0)->sw_if_index[VLIB_TX]; - - /* Add rewrite/encap string. */ - vnet_rewrite_one_header (adj0[0], h0, sizeof (ethernet_header_t)); - vlib_buffer_advance (b0, -adj0->rewrite_header.data_bytes); - - next0 = IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX; - - vlib_set_next_frame_buffer (vm, node, next0, bi0); - } - } - - vlib_put_next_frame (vm, node, IP6_DISCOVER_NEIGHBOR_NEXT_DROP, - n_left_to_next_drop); - } - - return frame->n_vectors; -} - -static uword -ip6_discover_neighbor (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return (ip6_discover_neighbor_inline (vm, node, frame, 0)); -} - -static uword -ip6_glean (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return (ip6_discover_neighbor_inline (vm, node, frame, 1)); -} - -static char *ip6_discover_neighbor_error_strings[] = { - [IP6_DISCOVER_NEIGHBOR_ERROR_DROP] = "address overflow drops", - [IP6_DISCOVER_NEIGHBOR_ERROR_REQUEST_SENT] = "neighbor solicitations sent", - [IP6_DISCOVER_NEIGHBOR_ERROR_NO_SOURCE_ADDRESS] - = "no source address for ND solicitation", -}; - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (ip6_discover_neighbor_node) = -{ - .function = ip6_discover_neighbor, - .name = "ip6-discover-neighbor", - .vector_size = sizeof (u32), - .format_trace = format_ip6_forward_next_trace, - .n_errors = ARRAY_LEN (ip6_discover_neighbor_error_strings), - .error_strings = ip6_discover_neighbor_error_strings, - .n_next_nodes = IP6_DISCOVER_NEIGHBOR_N_NEXT, - .next_nodes = - { - [IP6_DISCOVER_NEIGHBOR_NEXT_DROP] = "ip6-drop", - [IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX] = "interface-output", - }, -}; -/* *INDENT-ON* */ - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (ip6_glean_node) = -{ - .function = ip6_glean, - .name = "ip6-glean", - .vector_size = sizeof (u32), - .format_trace = format_ip6_forward_next_trace, - .n_errors = ARRAY_LEN (ip6_discover_neighbor_error_strings), - .error_strings = ip6_discover_neighbor_error_strings, - .n_next_nodes = IP6_DISCOVER_NEIGHBOR_N_NEXT, - .next_nodes = - { - [IP6_DISCOVER_NEIGHBOR_NEXT_DROP] = "ip6-drop", - [IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX] = "interface-output", - }, -}; -/* *INDENT-ON* */ - clib_error_t * ip6_probe_neighbor (vlib_main_t * vm, ip6_address_t * dst, u32 sw_if_index) { diff --git a/src/vnet/ip/ip6_neighbor.c b/src/vnet/ip/ip6_neighbor.c index 8e444024c44..34ddd7d2d58 100644 --- a/src/vnet/ip/ip6_neighbor.c +++ b/src/vnet/ip/ip6_neighbor.c @@ -3070,6 +3070,281 @@ VLIB_REGISTER_NODE (ip6_icmp_neighbor_advertisement_node,static) = }; /* *INDENT-ON* */ +typedef enum +{ + IP6_DISCOVER_NEIGHBOR_NEXT_DROP, + IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX, + IP6_DISCOVER_NEIGHBOR_N_NEXT, +} ip6_discover_neighbor_next_t; + +typedef enum +{ + IP6_DISCOVER_NEIGHBOR_ERROR_DROP, + IP6_DISCOVER_NEIGHBOR_ERROR_REQUEST_SENT, + IP6_DISCOVER_NEIGHBOR_ERROR_NO_SOURCE_ADDRESS, +} ip6_discover_neighbor_error_t; + +static uword +ip6_discover_neighbor_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, int is_glean) +{ + vnet_main_t *vnm = vnet_get_main (); + ip6_main_t *im = &ip6_main; + ip_lookup_main_t *lm = &im->lookup_main; + u32 *from, *to_next_drop; + uword n_left_from, n_left_to_next_drop; + static f64 time_last_seed_change = -1e100; + static u32 hash_seeds[3]; + static uword hash_bitmap[256 / BITS (uword)]; + f64 time_now; + int bogus_length; + ip6_neighbor_main_t *nm = &ip6_neighbor_main; + + if (node->flags & VLIB_NODE_FLAG_TRACE) + ip6_forward_next_trace (vm, node, frame, VLIB_TX); + + time_now = vlib_time_now (vm); + if (time_now - time_last_seed_change > 1e-3) + { + uword i; + u32 *r = clib_random_buffer_get_data (&vm->random_buffer, + sizeof (hash_seeds)); + for (i = 0; i < ARRAY_LEN (hash_seeds); i++) + hash_seeds[i] = r[i]; + + /* Mark all hash keys as been not-seen before. */ + for (i = 0; i < ARRAY_LEN (hash_bitmap); i++) + hash_bitmap[i] = 0; + + time_last_seed_change = time_now; + } + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, IP6_DISCOVER_NEIGHBOR_NEXT_DROP, + to_next_drop, n_left_to_next_drop); + + while (n_left_from > 0 && n_left_to_next_drop > 0) + { + vlib_buffer_t *p0; + ip6_header_t *ip0; + u32 pi0, adj_index0, a0, b0, c0, m0, sw_if_index0, drop0; + uword bm0; + ip_adjacency_t *adj0; + vnet_hw_interface_t *hw_if0; + ip6_radv_t *radv_info; + u32 next0; + + pi0 = from[0]; + + p0 = vlib_get_buffer (vm, pi0); + + adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX]; + + ip0 = vlib_buffer_get_current (p0); + + adj0 = adj_get (adj_index0); + + if (!is_glean) + { + ip0->dst_address.as_u64[0] = + adj0->sub_type.nbr.next_hop.ip6.as_u64[0]; + ip0->dst_address.as_u64[1] = + adj0->sub_type.nbr.next_hop.ip6.as_u64[1]; + } + + a0 = hash_seeds[0]; + b0 = hash_seeds[1]; + c0 = hash_seeds[2]; + + sw_if_index0 = adj0->rewrite_header.sw_if_index; + vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0; + + a0 ^= sw_if_index0; + b0 ^= ip0->dst_address.as_u32[0]; + c0 ^= ip0->dst_address.as_u32[1]; + + hash_v3_mix32 (a0, b0, c0); + + b0 ^= ip0->dst_address.as_u32[2]; + c0 ^= ip0->dst_address.as_u32[3]; + + hash_v3_finalize32 (a0, b0, c0); + + c0 &= BITS (hash_bitmap) - 1; + c0 = c0 / BITS (uword); + m0 = (uword) 1 << (c0 % BITS (uword)); + + bm0 = hash_bitmap[c0]; + drop0 = (bm0 & m0) != 0; + + /* Mark it as seen. */ + hash_bitmap[c0] = bm0 | m0; + + from += 1; + n_left_from -= 1; + to_next_drop[0] = pi0; + to_next_drop += 1; + n_left_to_next_drop -= 1; + + hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0); + + /* If the interface is link-down, drop the pkt */ + if (!(hw_if0->flags & VNET_HW_INTERFACE_FLAG_LINK_UP)) + drop0 = 1; + + if (vec_len (nm->if_radv_pool_index_by_sw_if_index) > sw_if_index0) + { + u32 ri = nm->if_radv_pool_index_by_sw_if_index[sw_if_index0]; + + if (ri != ~0) + radv_info = pool_elt_at_index (nm->if_radv_pool, ri); + else + drop0 = 1; + } + else + drop0 = 1; + + /* + * the adj has been updated to a rewrite but the node the DPO that got + * us here hasn't - yet. no big deal. we'll drop while we wait. + */ + if (IP_LOOKUP_NEXT_REWRITE == adj0->lookup_next_index) + drop0 = 1; + + p0->error = + node->errors[drop0 ? IP6_DISCOVER_NEIGHBOR_ERROR_DROP + : IP6_DISCOVER_NEIGHBOR_ERROR_REQUEST_SENT]; + + if (drop0) + continue; + + { + u32 bi0 = 0; + icmp6_neighbor_solicitation_header_t *h0; + vlib_buffer_t *b0; + + h0 = vlib_packet_template_get_packet + (vm, &im->discover_neighbor_packet_template, &bi0); + + /* + * Build ethernet header. + * Choose source address based on destination lookup + * adjacency. + */ + if (!ip6_src_address_for_packet (lm, + sw_if_index0, + &ip0->dst_address, + &h0->ip.src_address)) + { + /* There is no address on the interface */ + p0->error = + node->errors[IP6_DISCOVER_NEIGHBOR_ERROR_NO_SOURCE_ADDRESS]; + vlib_buffer_free (vm, &bi0, 1); + continue; + } + + /* + * Destination address is a solicited node multicast address. + * We need to fill in + * the low 24 bits with low 24 bits of target's address. + */ + h0->ip.dst_address.as_u8[13] = ip0->dst_address.as_u8[13]; + h0->ip.dst_address.as_u8[14] = ip0->dst_address.as_u8[14]; + h0->ip.dst_address.as_u8[15] = ip0->dst_address.as_u8[15]; + + h0->neighbor.target_address = ip0->dst_address; + + clib_memcpy (h0->link_layer_option.ethernet_address, + hw_if0->hw_address, vec_len (hw_if0->hw_address)); + + /* $$$$ appears we need this; why is the checksum non-zero? */ + h0->neighbor.icmp.checksum = 0; + h0->neighbor.icmp.checksum = + ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h0->ip, + &bogus_length); + + ASSERT (bogus_length == 0); + + vlib_buffer_copy_trace_flag (vm, p0, bi0); + b0 = vlib_get_buffer (vm, bi0); + vnet_buffer (b0)->sw_if_index[VLIB_TX] + = vnet_buffer (p0)->sw_if_index[VLIB_TX]; + + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = + radv_info->mcast_adj_index; + + b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; + next0 = IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX; + + vlib_set_next_frame_buffer (vm, node, next0, bi0); + } + } + + vlib_put_next_frame (vm, node, IP6_DISCOVER_NEIGHBOR_NEXT_DROP, + n_left_to_next_drop); + } + + return frame->n_vectors; +} + +static uword +ip6_discover_neighbor (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (ip6_discover_neighbor_inline (vm, node, frame, 0)); +} + +static uword +ip6_glean (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (ip6_discover_neighbor_inline (vm, node, frame, 1)); +} + +static char *ip6_discover_neighbor_error_strings[] = { + [IP6_DISCOVER_NEIGHBOR_ERROR_DROP] = "address overflow drops", + [IP6_DISCOVER_NEIGHBOR_ERROR_REQUEST_SENT] = "neighbor solicitations sent", + [IP6_DISCOVER_NEIGHBOR_ERROR_NO_SOURCE_ADDRESS] + = "no source address for ND solicitation", +}; + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip6_glean_node) = +{ + .function = ip6_glean, + .name = "ip6-glean", + .vector_size = sizeof (u32), + .format_trace = format_ip6_forward_next_trace, + .n_errors = ARRAY_LEN (ip6_discover_neighbor_error_strings), + .error_strings = ip6_discover_neighbor_error_strings, + .n_next_nodes = IP6_DISCOVER_NEIGHBOR_N_NEXT, + .next_nodes = + { + [IP6_DISCOVER_NEIGHBOR_NEXT_DROP] = "ip6-drop", + [IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX] = "ip6-rewrite-mcast", + }, +}; +VLIB_REGISTER_NODE (ip6_discover_neighbor_node) = +{ + .function = ip6_discover_neighbor, + .name = "ip6-discover-neighbor", + .vector_size = sizeof (u32), + .format_trace = format_ip6_forward_next_trace, + .n_errors = ARRAY_LEN (ip6_discover_neighbor_error_strings), + .error_strings = ip6_discover_neighbor_error_strings, + .n_next_nodes = IP6_DISCOVER_NEIGHBOR_N_NEXT, + .next_nodes = + { + [IP6_DISCOVER_NEIGHBOR_NEXT_DROP] = "ip6-drop", + [IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX] = "ip6-rewrite-mcast", + }, +}; +/* *INDENT-ON* */ + /* API support functions */ int ip6_neighbor_ra_config (vlib_main_t * vm, u32 sw_if_index, diff --git a/test/test_ip6.py b/test/test_ip6.py index c9efcbbc51c..884f793680d 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -84,7 +84,7 @@ class TestIPv6ND(VppTestCase): dst_ip = inet_ntop(AF_INET6, nsma) # NS is broadcast - self.assertEqual(rx[Ether].dst, "ff:ff:ff:ff:ff:ff") + self.assertEqual(rx[Ether].dst, in6_getnsmac(nsma)) # and from the router's MAC self.assertEqual(rx[Ether].src, intf.local_mac)