X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=vnet%2Fvnet%2Fip%2Fip6_forward.c;h=86ee8d5ba2bc59ba53630b5d53b322144fc0d192;hb=a18e413085b1986889b06a2ecc4c618c3f788c0c;hp=abd34208112b51b2dbe2be23ad0e9e3f3d1593a2;hpb=13056f93d95182790224c592b4ab78cf5082ebf7;p=vpp.git diff --git a/vnet/vnet/ip/ip6_forward.c b/vnet/vnet/ip/ip6_forward.c index abd34208112..86ee8d5ba2b 100644 --- a/vnet/vnet/ip/ip6_forward.c +++ b/vnet/vnet/ip/ip6_forward.c @@ -64,19 +64,24 @@ ip6_fib_lookup_with_table (ip6_main_t * im, u32 fib_index, ip6_address_t * dst) int i, len; int rv; BVT(clib_bihash_kv) kv, value; + u64 fib; len = vec_len (im->prefix_lengths_in_search_order); + kv.key[0] = dst->as_u64[0]; + kv.key[1] = dst->as_u64[1]; + fib = ((u64)((fib_index))<<32); + for (i = 0; i < len; i++) { int dst_address_length = im->prefix_lengths_in_search_order[i]; ip6_address_t * mask = &im->fib_masks[dst_address_length]; ASSERT(dst_address_length >= 0 && dst_address_length <= 128); - - kv.key[0] = dst->as_u64[0] & mask->as_u64[0]; - kv.key[1] = dst->as_u64[1] & mask->as_u64[1]; - kv.key[2] = ((u64)((fib_index))<<32) | dst_address_length; + //As lengths are decreasing, masks are increasingly specific. + kv.key[0] &= mask->as_u64[0]; + kv.key[1] &= mask->as_u64[1]; + kv.key[2] = fib | dst_address_length; rv = BV(clib_bihash_search_inline_2)(&im->ip6_lookup_table, &kv, &value); if (rv == 0) @@ -184,6 +189,14 @@ find_ip6_fib_by_table_index_or_id (ip6_main_t * im, u32 table_index_or_id, u32 f fib_index = table_index_or_id; if (! (flags & IP6_ROUTE_FLAG_FIB_INDEX)) { + if (table_index_or_id == ~0) { + table_index_or_id = 0; + while (hash_get (im->fib_index_by_table_id, table_index_or_id)) { + table_index_or_id++; + } + return create_fib_with_table_id (im, table_index_or_id); + } + p = hash_get (im->fib_index_by_table_id, table_index_or_id); if (! p) return create_fib_with_table_id (im, table_index_or_id); @@ -265,6 +278,16 @@ void ip6_add_del_route (ip6_main_t * im, ip6_add_del_route_args_t * a) BV(clib_bihash_add_del) (&im->ip6_lookup_table, &kv, 1 /* is_add */); } + /* Avoid spurious reference count increments */ + if (old_adj_index == adj_index + && adj_index != ~0 + && !(a->flags & IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY)) + { + ip_adjacency_t * adj = ip_get_adjacency (lm, adj_index); + if (adj->share_count > 0) + adj->share_count --; + } + /* Delete old adjacency index if present and changed. */ { if (! (a->flags & IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY) @@ -327,7 +350,7 @@ ip6_add_del_route_next_hop (ip6_main_t * im, ip6_adjacency_set_interface_route (vnm, adj, next_hop_sw_if_index, ~0); ip_call_add_del_adjacency_callbacks - (lm, next_hop_sw_if_index, /* is_del */ 0); + (lm, nh_adj_index, /* is_del */ 0); hash_set (im->interface_route_adj_index_by_sw_if_index, next_hop_sw_if_index, nh_adj_index); } @@ -340,14 +363,33 @@ ip6_add_del_route_next_hop (ip6_main_t * im, kv.key[2] = ((u64)((fib - im->fibs))<<32) | 128; if (BV(clib_bihash_search)(&im->ip6_lookup_table, &kv, &value) < 0) + { + ip_adjacency_t * adj; + nh_adj_index = ip6_fib_lookup_with_table (im, fib_index, next_hop); + adj = ip_get_adjacency (lm, nh_adj_index); + /* if ND interface adjacencty is present, we need to + install ND adjaceny for specific next hop */ + if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP && + adj->arp.next_hop.ip6.as_u64[0] == 0 && + adj->arp.next_hop.ip6.as_u64[1] == 0) { - vnm->api_errno = VNET_API_ERROR_UNKNOWN_DESTINATION; - error = clib_error_return (0, "next-hop %U/128 not in FIB", - format_ip6_address, next_hop); - goto done; + nh_adj_index = vnet_ip6_neighbor_glean_add(fib_index, next_hop); } - - nh_adj_index = value.value; + else + { + ip_adjacency_t add_adj; + memset (&add_adj, 0, sizeof(add_adj)); + add_adj.n_adj = 1; + add_adj.lookup_next_index = IP_LOOKUP_NEXT_INDIRECT; + add_adj.indirect.next_hop.ip6.as_u64[0] = next_hop->as_u64[0]; + add_adj.indirect.next_hop.ip6.as_u64[1] = next_hop->as_u64[1]; + add_adj.explicit_fib_index = explicit_fib_index; + ip_add_adjacency (lm, &add_adj, 1, &nh_adj_index); + } + } + else + nh_adj_index = value.value; + } } else @@ -411,6 +453,28 @@ ip6_add_del_route_next_hop (ip6_main_t * im, goto done; } + /* Destination is not known and default weight is set so add route + to existing non-multipath adjacency */ + if (dst_adj_index == ~0 && next_hop_weight == 1 && next_hop_sw_if_index == ~0) + { + /* create new adjacency */ + ip6_add_del_route_args_t a; + a.table_index_or_table_id = fib_index; + a.flags = ((is_del ? IP6_ROUTE_FLAG_DEL : IP6_ROUTE_FLAG_ADD) + | IP6_ROUTE_FLAG_FIB_INDEX + | IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY + | (flags & (IP6_ROUTE_FLAG_NO_REDISTRIBUTE + | IP6_ROUTE_FLAG_NOT_LAST_IN_GROUP))); + a.dst_address = dst_address[0]; + a.dst_address_length = dst_address_length; + a.adj_index = nh_adj_index; + a.add_adj = 0; + a.n_add_adj = 0; + + ip6_add_del_route (im, &a); + goto done; + } + old_mp_adj_index = dst_adj ? dst_adj->heap_handle : ~0; if (! ip_multipath_adjacency_add_del_next_hop @@ -467,7 +531,7 @@ ip6_get_route (ip6_main_t * im, BVT(clib_bihash_kv) kv, value; ASSERT (address_length < ARRAY_LEN (im->fib_masks)); - memcpy (&masked_address, address, sizeof (masked_address)); + clib_memcpy (&masked_address, address, sizeof (masked_address)); ip6_address_mask (&masked_address, &im->fib_masks[address_length]); kv.key[0] = masked_address.as_u64[0]; @@ -591,10 +655,17 @@ void ip6_delete_matching_routes (ip6_main_t * im, ip6_maybe_remap_adjacencies (im, table_index_or_table_id, flags); } -static uword -ip6_lookup (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +void +ip6_forward_next_trace (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + vlib_rx_or_tx_t which_adj_index); + +always_inline uword +ip6_lookup_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_indirect) { ip6_main_t * im = &ip6_main; ip_lookup_main_t * lm = &im->lookup_main; @@ -619,6 +690,7 @@ ip6_lookup (vlib_main_t * vm, ip_lookup_next_t next0, next1; ip6_header_t * ip0, * ip1; ip_adjacency_t * adj0, * adj1; + ip6_address_t * dst_addr0, * dst_addr1; u32 fib_index0, fib_index1; u32 flow_hash_config0, flow_hash_config1; @@ -644,6 +716,20 @@ ip6_lookup (vlib_main_t * vm, ip0 = vlib_buffer_get_current (p0); ip1 = vlib_buffer_get_current (p1); + if (is_indirect) + { + ip_adjacency_t * iadj0, * iadj1; + iadj0 = ip_get_adjacency (lm, vnet_buffer(p0)->ip.adj_index[VLIB_TX]); + iadj1 = ip_get_adjacency (lm, vnet_buffer(p1)->ip.adj_index[VLIB_TX]); + dst_addr0 = &iadj0->indirect.next_hop.ip6; + dst_addr1 = &iadj1->indirect.next_hop.ip6; + } + else + { + dst_addr0 = &ip0->dst_address; + dst_addr1 = &ip1->dst_address; + } + fib_index0 = vec_elt (im->fib_index_by_sw_if_index, vnet_buffer (p0)->sw_if_index[VLIB_RX]); fib_index1 = vec_elt (im->fib_index_by_sw_if_index, vnet_buffer (p1)->sw_if_index[VLIB_RX]); @@ -652,10 +738,8 @@ ip6_lookup (vlib_main_t * vm, fib_index1 = (vnet_buffer(p1)->sw_if_index[VLIB_TX] == (u32)~0) ? fib_index1 : vnet_buffer(p1)->sw_if_index[VLIB_TX]; - adj_index0 = ip6_fib_lookup_with_table (im, fib_index0, - &ip0->dst_address); - adj_index1 = ip6_fib_lookup_with_table (im, fib_index1, - &ip1->dst_address); + adj_index0 = ip6_fib_lookup_with_table (im, fib_index0, dst_addr0); + adj_index1 = ip6_fib_lookup_with_table (im, fib_index1, dst_addr1); adj0 = ip_get_adjacency (lm, adj_index0); adj1 = ip_get_adjacency (lm, adj_index1); @@ -663,24 +747,24 @@ ip6_lookup (vlib_main_t * vm, if (PREDICT_FALSE (adj0->explicit_fib_index != ~0)) { adj_index0 = ip6_fib_lookup_with_table - (im, adj0->explicit_fib_index, &ip0->dst_address); + (im, adj0->explicit_fib_index, dst_addr0); adj0 = ip_get_adjacency (lm, adj_index0); } if (PREDICT_FALSE (adj1->explicit_fib_index != ~0)) { adj_index1 = ip6_fib_lookup_with_table - (im, adj1->explicit_fib_index, &ip1->dst_address); + (im, adj1->explicit_fib_index, dst_addr1); adj1 = ip_get_adjacency (lm, adj_index1); } next0 = adj0->lookup_next_index; next1 = adj1->lookup_next_index; - /* Process hop-by-hop options if present */ - next0 = (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS) ? - IP_LOOKUP_NEXT_HOP_BY_HOP : next0; - next1 = (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS) ? - IP_LOOKUP_NEXT_HOP_BY_HOP : next1; + /* Only process the HBH Option Header if explicitly configured to do so */ + next0 = (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS) && im->hbh_enabled && + adj_index0 ? IP6_LOOKUP_NEXT_HOP_BY_HOP : adj0->lookup_next_index; + next1 = (ip1->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS) && im->hbh_enabled && + adj_index1 ? IP6_LOOKUP_NEXT_HOP_BY_HOP : adj1->lookup_next_index; vnet_buffer (p0)->ip.flow_hash = vnet_buffer(p1)->ip.flow_hash = 0; @@ -768,6 +852,7 @@ ip6_lookup (vlib_main_t * vm, u32 pi0, adj_index0; ip_lookup_next_t next0; ip_adjacency_t * adj0; + ip6_address_t * dst_addr0; u32 fib_index0, flow_hash_config0; pi0 = from[0]; @@ -777,6 +862,17 @@ ip6_lookup (vlib_main_t * vm, ip0 = vlib_buffer_get_current (p0); + if (is_indirect) + { + ip_adjacency_t * iadj0; + iadj0 = ip_get_adjacency (lm, vnet_buffer(p0)->ip.adj_index[VLIB_TX]); + dst_addr0 = &iadj0->indirect.next_hop.ip6; + } + else + { + dst_addr0 = &ip0->dst_address; + } + fib_index0 = vec_elt (im->fib_index_by_sw_if_index, vnet_buffer (p0)->sw_if_index[VLIB_RX]); fib_index0 = (vnet_buffer(p0)->sw_if_index[VLIB_TX] == (u32)~0) ? fib_index0 : vnet_buffer(p0)->sw_if_index[VLIB_TX]; @@ -784,21 +880,20 @@ ip6_lookup (vlib_main_t * vm, flow_hash_config0 = vec_elt_at_index (im->fibs,fib_index0)->flow_hash_config; - adj_index0 = ip6_fib_lookup_with_table (im, fib_index0, - &ip0->dst_address); + adj_index0 = ip6_fib_lookup_with_table (im, fib_index0, dst_addr0); adj0 = ip_get_adjacency (lm, adj_index0); if (PREDICT_FALSE (adj0->explicit_fib_index != ~0)) { - adj_index0 = ip6_fib_lookup_with_table - (im, adj0->explicit_fib_index, &ip0->dst_address); + adj_index0 = ip6_fib_lookup_with_table + (im, adj0->explicit_fib_index, dst_addr0); adj0 = ip_get_adjacency (lm, adj_index0); } - next0 = adj0->lookup_next_index; - next0 = (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS) ? - IP_LOOKUP_NEXT_HOP_BY_HOP : next0; + /* Only process the HBH Option Header if explicitly configured to do so */ + next0 = (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS) && im->hbh_enabled && + adj_index0 ? IP6_LOOKUP_NEXT_HOP_BY_HOP : adj0->lookup_next_index; vnet_buffer (p0)->ip.flow_hash = 0; @@ -841,6 +936,9 @@ ip6_lookup (vlib_main_t * vm, vlib_put_next_frame (vm, node, next, n_left_to_next); } + if (node->flags & VLIB_NODE_FLAG_TRACE) + ip6_forward_next_trace(vm, node, frame, VLIB_TX); + return frame->n_vectors; } @@ -859,6 +957,8 @@ void ip6_adjacency_set_interface_route (vnet_main_t * vnm, n = IP_LOOKUP_NEXT_ARP; node_index = ip6_discover_neighbor_node.index; adj->if_address_index = if_address_index; + adj->arp.next_hop.ip6.as_u64[0] = 0; + adj->arp.next_hop.ip6.as_u64[1] = 0; } else { @@ -922,7 +1022,7 @@ ip6_add_interface_routes (vnet_main_t * vnm, u32 sw_if_index, if (classify_table_index != (u32) ~0) { adj->lookup_next_index = IP_LOOKUP_NEXT_CLASSIFY; - adj->classify_table_index = classify_table_index; + adj->classify.table_index = classify_table_index; } else adj->lookup_next_index = IP_LOOKUP_NEXT_LOCAL; @@ -1100,6 +1200,75 @@ ip6_sw_interface_admin_up_down (vnet_main_t * vnm, VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip6_sw_interface_admin_up_down); +/* Built-in ip6 unicast rx feature path definition */ +VNET_IP6_UNICAST_FEATURE_INIT (ip6_inacl, static) = { + .node_name = "ip6-inacl", + .runs_before = {"ipsec-input-ip6", 0}, + .feature_index = &ip6_main.ip6_unicast_rx_feature_check_access, +}; + +VNET_IP6_UNICAST_FEATURE_INIT (ip6_ipsec, static) = { + .node_name = "ipsec-input-ip6", + .runs_before = {"l2tp-decap", 0}, + .feature_index = &ip6_main.ip6_unicast_rx_feature_ipsec, +}; + +VNET_IP6_UNICAST_FEATURE_INIT (ip6_l2tp, static) = { + .node_name = "l2tp-decap", + .runs_before = {"vpath-input-ip6", 0}, + .feature_index = &ip6_main.ip6_unicast_rx_feature_l2tp_decap, +}; + +VNET_IP6_UNICAST_FEATURE_INIT (ip6_vpath, static) = { + .node_name = "vpath-input-ip6", + .runs_before = {"ip6-lookup", 0}, + .feature_index = &ip6_main.ip6_unicast_rx_feature_vpath, +}; + +VNET_IP6_UNICAST_FEATURE_INIT (ip6_lookup, static) = { + .node_name = "ip6-lookup", + .runs_before = {0}, /* not before any other features */ + .feature_index = &ip6_main.ip6_unicast_rx_feature_lookup, +}; + +/* Built-in ip6 multicast rx feature path definition (none now) */ +VNET_IP6_MULTICAST_FEATURE_INIT (ip4_vpath_mc, static) = { + .node_name = "vpath-input-ip6", + .runs_before = {"ip6-lookup", 0}, + .feature_index = &ip6_main.ip6_multicast_rx_feature_vpath, +}; + +VNET_IP6_MULTICAST_FEATURE_INIT (ip6_lookup, static) = { + .node_name = "ip6-lookup", + .runs_before = {0}, /* not before any other features */ + .feature_index = &ip6_main.ip6_multicast_rx_feature_lookup, +}; + +static char * feature_start_nodes[] = + {"ip6-input"}; + +static clib_error_t * +ip6_feature_init (vlib_main_t * vm, ip6_main_t * im) +{ + ip_lookup_main_t * lm = &im->lookup_main; + clib_error_t * error; + vnet_cast_t cast; + + for (cast = 0; cast < VNET_N_CAST; cast++) + { + ip_config_main_t * cm = &lm->rx_config_mains[cast]; + vnet_config_main_t * vcm = &cm->config_main; + + if ((error = ip_feature_init_cast (vm, cm, vcm, + feature_start_nodes, + ARRAY_LEN(feature_start_nodes), + cast, + 0 /* is_ip4 */))) + return error; + } + return 0; +} + clib_error_t * ip6_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, @@ -1109,41 +1278,31 @@ ip6_sw_interface_add_del (vnet_main_t * vnm, ip6_main_t * im = &ip6_main; ip_lookup_main_t * lm = &im->lookup_main; u32 ci, cast; + u32 feature_index; for (cast = 0; cast < VNET_N_CAST; cast++) { ip_config_main_t * cm = &lm->rx_config_mains[cast]; vnet_config_main_t * vcm = &cm->config_main; - /* FIXME multicast. */ - if (! vcm->node_index_by_feature_index) - { - char * start_nodes[] = { "ip6-input", }; - char * feature_nodes[] = { - [IP6_RX_FEATURE_CHECK_ACCESS] = "ip6-inacl", - [IP6_RX_FEATURE_IPSEC] = "ipsec-input-ip6", - [IP6_RX_FEATURE_L2TPV3] = "l2tp-decap", - [IP6_RX_FEATURE_VPATH] = "vpath-input-ip6", - [IP6_RX_FEATURE_LOOKUP] = "ip6-lookup", - }; - vnet_config_init (vm, vcm, - start_nodes, ARRAY_LEN (start_nodes), - feature_nodes, ARRAY_LEN (feature_nodes)); - } - vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0); ci = cm->config_index_by_sw_if_index[sw_if_index]; + if (cast == VNET_UNICAST) + feature_index = im->ip6_unicast_rx_feature_lookup; + else + feature_index = im->ip6_multicast_rx_feature_lookup; + if (is_add) ci = vnet_config_add_feature (vm, vcm, ci, - IP6_RX_FEATURE_LOOKUP, + feature_index, /* config data */ 0, /* # bytes of config data */ 0); else ci = vnet_config_del_feature (vm, vcm, ci, - IP6_RX_FEATURE_LOOKUP, + feature_index, /* config data */ 0, /* # bytes of config data */ 0); @@ -1154,29 +1313,49 @@ ip6_sw_interface_add_del (vnet_main_t * vnm, VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip6_sw_interface_add_del); +static uword +ip6_lookup (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return ip6_lookup_inline (vm, node, frame, /* is_indirect */ 0); +} + +static u8 * format_ip6_lookup_trace (u8 * s, va_list * args); + VLIB_REGISTER_NODE (ip6_lookup_node) = { .function = ip6_lookup, .name = "ip6-lookup", .vector_size = sizeof (u32), - .n_next_nodes = IP_LOOKUP_N_NEXT, - .next_nodes = { - [IP_LOOKUP_NEXT_MISS] = "ip6-miss", - [IP_LOOKUP_NEXT_DROP] = "ip6-drop", - [IP_LOOKUP_NEXT_PUNT] = "ip6-punt", - [IP_LOOKUP_NEXT_LOCAL] = "ip6-local", - [IP_LOOKUP_NEXT_ARP] = "ip6-discover-neighbor", - [IP_LOOKUP_NEXT_REWRITE] = "ip6-rewrite", - [IP_LOOKUP_NEXT_CLASSIFY] = "ip6-classify", - [IP_LOOKUP_NEXT_MAP] = "ip6-map", - [IP_LOOKUP_NEXT_MAP_T] = "ip6-map-t", - [IP_LOOKUP_NEXT_SIXRD] = "ip6-sixrd", - [IP_LOOKUP_NEXT_HOP_BY_HOP] = "ip6-hop-by-hop", - [IP_LOOKUP_NEXT_ADD_HOP_BY_HOP] = "ip6-add-hop-by-hop", - [IP_LOOKUP_NEXT_POP_HOP_BY_HOP] = "ip6-pop-hop-by-hop", - }, + .format_trace = format_ip6_lookup_trace, + + .n_next_nodes = IP6_LOOKUP_N_NEXT, + .next_nodes = IP6_LOOKUP_NEXT_NODES, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip6_lookup_node, ip6_lookup) + +static uword +ip6_indirect (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return ip6_lookup_inline (vm, node, frame, /* is_indirect */ 1); +} + + +VLIB_REGISTER_NODE (ip6_indirect_node) = { + .function = ip6_indirect, + .name = "ip6-indirect", + .vector_size = sizeof (u32), + .sibling_of = "ip6-lookup", + .format_trace = format_ip6_lookup_trace, + .n_next_nodes = 0, }; +VLIB_NODE_FUNCTION_MULTIARCH (ip6_indirect_node, ip6_indirect) + typedef struct { /* Adjacency taken. */ u32 adj_index; @@ -1184,37 +1363,58 @@ typedef struct { u32 fib_index; /* Packet data, possibly *after* rewrite. */ - u8 packet_data[64 - 1*sizeof(u32)]; + u8 packet_data[128 - 1*sizeof(u32)]; } ip6_forward_next_trace_t; static u8 * format_ip6_forward_next_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + ip6_forward_next_trace_t * t = va_arg (*args, ip6_forward_next_trace_t *); + uword indent = format_get_indent (s); + + s = format(s, "%U%U", + format_white_space, indent, + format_ip6_header, t->packet_data); + return s; +} + +static u8 * format_ip6_lookup_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); ip6_forward_next_trace_t * t = va_arg (*args, ip6_forward_next_trace_t *); vnet_main_t * vnm = vnet_get_main(); ip6_main_t * im = &ip6_main; - ip_adjacency_t * adj; uword indent = format_get_indent (s); - adj = ip_get_adjacency (&im->lookup_main, t->adj_index); s = format (s, "fib %d adj-idx %d : %U flow hash: 0x%08x", - t->fib_index, t->adj_index, format_ip_adjacency, - vnm, &im->lookup_main, t->adj_index, t->flow_hash); - switch (adj->lookup_next_index) - { - case IP_LOOKUP_NEXT_REWRITE: - s = format (s, "\n%U%U", - format_white_space, indent, - format_ip_adjacency_packet_data, - vnm, &im->lookup_main, t->adj_index, - t->packet_data, sizeof (t->packet_data)); - break; + t->fib_index, t->adj_index, format_ip_adjacency, + vnm, &im->lookup_main, t->adj_index, t->flow_hash); + s = format(s, "\n%U%U", + format_white_space, indent, + format_ip6_header, t->packet_data); + return s; +} - default: - break; - } +static u8 * format_ip6_rewrite_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + ip6_forward_next_trace_t * t = va_arg (*args, ip6_forward_next_trace_t *); + vnet_main_t * vnm = vnet_get_main(); + ip6_main_t * im = &ip6_main; + uword indent = format_get_indent (s); + + s = format (s, "tx_sw_if_index %d adj-idx %d : %U flow hash: 0x%08x", + t->fib_index, t->adj_index, format_ip_adjacency, + vnm, &im->lookup_main, t->adj_index, t->flow_hash); + s = format (s, "\n%U%U", + format_white_space, indent, + format_ip_adjacency_packet_data, + vnm, &im->lookup_main, t->adj_index, + t->packet_data, sizeof (t->packet_data)); return s; } @@ -1230,7 +1430,7 @@ ip6_forward_next_trace (vlib_main_t * vm, n_left = frame->n_vectors; from = vlib_frame_vector_args (frame); - + while (n_left >= 4) { u32 bi0, bi1; @@ -1252,9 +1452,12 @@ ip6_forward_next_trace (vlib_main_t * vm, t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0])); t0->adj_index = vnet_buffer (b0)->ip.adj_index[which_adj_index]; t0->flow_hash = vnet_buffer (b0)->ip.flow_hash; - t0->fib_index = vec_elt (im->fib_index_by_sw_if_index, - vnet_buffer(b0)->sw_if_index[VLIB_RX]); - memcpy (t0->packet_data, + t0->fib_index = (vnet_buffer(b0)->sw_if_index[VLIB_TX] != (u32)~0) ? + vnet_buffer(b0)->sw_if_index[VLIB_TX] : + vec_elt (im->fib_index_by_sw_if_index, + vnet_buffer(b0)->sw_if_index[VLIB_RX]); + + clib_memcpy (t0->packet_data, vlib_buffer_get_current (b0), sizeof (t0->packet_data)); } @@ -1263,9 +1466,12 @@ ip6_forward_next_trace (vlib_main_t * vm, t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0])); t1->adj_index = vnet_buffer (b1)->ip.adj_index[which_adj_index]; t1->flow_hash = vnet_buffer (b1)->ip.flow_hash; - t1->fib_index = vec_elt (im->fib_index_by_sw_if_index, - vnet_buffer(b1)->sw_if_index[VLIB_RX]); - memcpy (t1->packet_data, + t1->fib_index = (vnet_buffer(b1)->sw_if_index[VLIB_TX] != (u32)~0) ? + vnet_buffer(b1)->sw_if_index[VLIB_TX] : + vec_elt (im->fib_index_by_sw_if_index, + vnet_buffer(b1)->sw_if_index[VLIB_RX]); + + clib_memcpy (t1->packet_data, vlib_buffer_get_current (b1), sizeof (t1->packet_data)); } @@ -1288,9 +1494,12 @@ ip6_forward_next_trace (vlib_main_t * vm, t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0])); t0->adj_index = vnet_buffer (b0)->ip.adj_index[which_adj_index]; t0->flow_hash = vnet_buffer (b0)->ip.flow_hash; - t0->fib_index = vec_elt (im->fib_index_by_sw_if_index, - vnet_buffer(b0)->sw_if_index[VLIB_RX]); - memcpy (t0->packet_data, + t0->fib_index = (vnet_buffer(b0)->sw_if_index[VLIB_TX] != (u32)~0) ? + vnet_buffer(b0)->sw_if_index[VLIB_TX] : + vec_elt (im->fib_index_by_sw_if_index, + vnet_buffer(b0)->sw_if_index[VLIB_RX]); + + clib_memcpy (t0->packet_data, vlib_buffer_get_current (b0), sizeof (t0->packet_data)); } @@ -1353,6 +1562,8 @@ VLIB_REGISTER_NODE (ip6_drop_node,static) = { }, }; +VLIB_NODE_FUNCTION_MULTIARCH (ip6_drop_node, ip6_drop) + VLIB_REGISTER_NODE (ip6_punt_node,static) = { .function = ip6_punt, .name = "ip6-punt", @@ -1366,6 +1577,8 @@ VLIB_REGISTER_NODE (ip6_punt_node,static) = { }, }; +VLIB_NODE_FUNCTION_MULTIARCH (ip6_punt_node, ip6_punt) + VLIB_REGISTER_NODE (ip6_miss_node,static) = { .function = ip6_miss, .name = "ip6-miss", @@ -1379,6 +1592,8 @@ VLIB_REGISTER_NODE (ip6_miss_node,static) = { }, }; +VLIB_NODE_FUNCTION_MULTIARCH (ip6_miss_node, ip6_miss) + VLIB_REGISTER_NODE (ip6_multicast_node,static) = { .function = ip6_drop, .name = "ip6-multicast", @@ -1435,9 +1650,9 @@ u16 ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0, ip6 n_bytes_left = n_this_buffer = payload_length_host_byte_order; #if DPDK > 0 - if (p0) + if (p0 && n_this_buffer + headers_size > p0->current_length) { - struct rte_mbuf *mb = ((struct rte_mbuf *)p0)-1; + struct rte_mbuf *mb = rte_mbuf_from_vlib_buffer(p0); u8 nb_segs = mb->nb_segs; n_this_buffer = (p0->current_length > headers_size ? @@ -1621,7 +1836,6 @@ ip6_local (vlib_main_t * vm, error1 = len_diff1 < 0 ? IP6_ERROR_UDP_LENGTH : error1; ASSERT (IP6_ERROR_UDP_CHECKSUM + IP_BUILTIN_PROTOCOL_UDP == IP6_ERROR_UDP_CHECKSUM); - ASSERT (IP6_ERROR_UDP_CHECKSUM + IP_BUILTIN_PROTOCOL_TCP == IP6_ERROR_TCP_CHECKSUM); ASSERT (IP6_ERROR_UDP_CHECKSUM + IP_BUILTIN_PROTOCOL_ICMP == IP6_ERROR_ICMP_CHECKSUM); error0 = (! good_l4_checksum0 ? IP6_ERROR_UDP_CHECKSUM + type0 @@ -1713,7 +1927,6 @@ ip6_local (vlib_main_t * vm, error0 = len_diff0 < 0 ? IP6_ERROR_UDP_LENGTH : error0; ASSERT (IP6_ERROR_UDP_CHECKSUM + IP_BUILTIN_PROTOCOL_UDP == IP6_ERROR_UDP_CHECKSUM); - ASSERT (IP6_ERROR_UDP_CHECKSUM + IP_BUILTIN_PROTOCOL_TCP == IP6_ERROR_TCP_CHECKSUM); ASSERT (IP6_ERROR_UDP_CHECKSUM + IP_BUILTIN_PROTOCOL_ICMP == IP6_ERROR_ICMP_CHECKSUM); error0 = (! good_l4_checksum0 ? IP6_ERROR_UDP_CHECKSUM + type0 @@ -1754,12 +1967,13 @@ VLIB_REGISTER_NODE (ip6_local_node,static) = { .next_nodes = { [IP_LOCAL_NEXT_DROP] = "error-drop", [IP_LOCAL_NEXT_PUNT] = "error-punt", - // [IP_LOCAL_NEXT_TCP_LOOKUP] = "ip6-tcp-lookup", [IP_LOCAL_NEXT_UDP_LOOKUP] = "ip6-udp-lookup", [IP_LOCAL_NEXT_ICMP] = "ip6-icmp-input", }, }; +VLIB_NODE_FUNCTION_MULTIARCH (ip6_local_node, ip6_local) + void ip6_register_protocol (u32 protocol, u32 node_index) { vlib_main_t * vm = vlib_get_main(); @@ -1772,6 +1986,7 @@ void ip6_register_protocol (u32 protocol, u32 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; @@ -1843,6 +2058,12 @@ ip6_discover_neighbor (vlib_main_t * vm, adj0 = ip_get_adjacency (lm, adj_index0); + if (adj0->arp.next_hop.ip6.as_u64[0] || + adj0->arp.next_hop.ip6.as_u64[1]) { + ip0->dst_address.as_u64[0] = adj0->arp.next_hop.ip6.as_u64[0]; + ip0->dst_address.as_u64[1] = adj0->arp.next_hop.ip6.as_u64[1]; + } + a0 = hash_seeds[0]; b0 = hash_seeds[1]; c0 = hash_seeds[2]; @@ -1916,7 +2137,7 @@ ip6_discover_neighbor (vlib_main_t * vm, h0->neighbor.target_address = ip0->dst_address; - memcpy (h0->link_layer_option.ethernet_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? */ @@ -1937,11 +2158,7 @@ ip6_discover_neighbor (vlib_main_t * vm, sizeof (ethernet_header_t)); vlib_buffer_advance (b0, -adj0->rewrite_header.data_bytes); - /* $$$$ hack in case next0 == 0 */ - b0->error = node->errors[IP6_DISCOVER_NEIGHBOR_ERROR_DROP]; - next0 = - vec_elt (im->discover_neighbor_next_index_by_hw_if_index, - hw_if0->hw_if_index); + next0 = IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX; vlib_set_next_frame_buffer (vm, node, next0, bi0); } @@ -1973,31 +2190,10 @@ VLIB_REGISTER_NODE (ip6_discover_neighbor_node) = { .n_next_nodes = IP6_DISCOVER_NEIGHBOR_N_NEXT, .next_nodes = { [IP6_DISCOVER_NEIGHBOR_NEXT_DROP] = "error-drop", + [IP6_DISCOVER_NEIGHBOR_NEXT_REPLY_TX] = "interface-output", }, }; -clib_error_t * -ip6_discover_neighbor_hw_interface_link_up_down (vnet_main_t * vnm, - u32 hw_if_index, - u32 flags) -{ - vlib_main_t * vm = vnm->vlib_main; - ip6_main_t * im = &ip6_main; - vnet_hw_interface_t * hw_if; - - hw_if = vnet_get_hw_interface (vnm, hw_if_index); - - vec_validate_init_empty - (im->discover_neighbor_next_index_by_hw_if_index, hw_if_index, 0); - im->discover_neighbor_next_index_by_hw_if_index[hw_if_index] - = vlib_node_add_next (vm, ip6_discover_neighbor_node.index, - hw_if->output_node_index); - return 0; -} - -VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION -(ip6_discover_neighbor_hw_interface_link_up_down); - clib_error_t * ip6_probe_neighbor (vlib_main_t * vm, ip6_address_t * dst, u32 sw_if_index) { @@ -2046,7 +2242,7 @@ ip6_probe_neighbor (vlib_main_t * vm, ip6_address_t * dst, u32 sw_if_index) h->ip.src_address = src[0]; h->neighbor.target_address = dst[0]; - memcpy (h->link_layer_option.ethernet_address, hi->hw_address, vec_len (hi->hw_address)); + clib_memcpy (h->link_layer_option.ethernet_address, hi->hw_address, vec_len (hi->hw_address)); h->neighbor.icmp.checksum = ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h->ip, &bogus_length); @@ -2336,7 +2532,7 @@ VLIB_REGISTER_NODE (ip6_rewrite_node) = { .name = "ip6-rewrite", .vector_size = sizeof (u32), - .format_trace = format_ip6_forward_next_trace, + .format_trace = format_ip6_rewrite_trace, .n_next_nodes = 1, .next_nodes = { @@ -2344,21 +2540,309 @@ VLIB_REGISTER_NODE (ip6_rewrite_node) = { }, }; -VLIB_REGISTER_NODE (ip6_rewrite_local_node,static) = { +VLIB_NODE_FUNCTION_MULTIARCH (ip6_rewrite_node, ip6_rewrite_transit) + +VLIB_REGISTER_NODE (ip6_rewrite_local_node) = { .function = ip6_rewrite_local, .name = "ip6-rewrite-local", .vector_size = sizeof (u32), .sibling_of = "ip6-rewrite", - .format_trace = format_ip6_forward_next_trace, + .format_trace = format_ip6_rewrite_trace, - .n_next_nodes = 1, - .next_nodes = { - [IP6_REWRITE_NEXT_DROP] = "error-drop", - }, + .n_next_nodes = 0, }; +VLIB_NODE_FUNCTION_MULTIARCH (ip6_rewrite_local_node, ip6_rewrite_local) + +/* + * Hop-by-Hop handling + */ + +ip6_hop_by_hop_main_t ip6_hop_by_hop_main; + +#define foreach_ip6_hop_by_hop_error \ +_(PROCESSED, "pkts with ip6 hop-by-hop options") \ +_(FORMAT, "incorrectly formatted hop-by-hop options") \ +_(UNKNOWN_OPTION, "unknown ip6 hop-by-hop options") + +typedef enum { +#define _(sym,str) IP6_HOP_BY_HOP_ERROR_##sym, + foreach_ip6_hop_by_hop_error +#undef _ + IP6_HOP_BY_HOP_N_ERROR, +} ip6_hop_by_hop_error_t; + +/* + * Primary h-b-h handler trace support + * We work pretty hard on the problem for obvious reasons + */ +typedef struct { + u32 next_index; + u32 trace_len; + u8 option_data[256]; +} ip6_hop_by_hop_trace_t; + +vlib_node_registration_t ip6_hop_by_hop_node; + +static char * ip6_hop_by_hop_error_strings[] = { +#define _(sym,string) string, + foreach_ip6_hop_by_hop_error +#undef _ +}; + +static u8 * +format_ip6_hop_by_hop_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + ip6_hop_by_hop_trace_t * t = va_arg (*args, ip6_hop_by_hop_trace_t *); + ip6_hop_by_hop_header_t *hbh0; + ip6_hop_by_hop_option_t *opt0, *limit0; + ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main; + + u8 type0; + + hbh0 = (ip6_hop_by_hop_header_t *)t->option_data; + + s = format (s, "IP6_HOP_BY_HOP: next index %d len %d traced %d", + t->next_index, (hbh0->length+1)<<3, t->trace_len); + + opt0 = (ip6_hop_by_hop_option_t *) (hbh0+1); + limit0 = (ip6_hop_by_hop_option_t *) ((u8 *)hbh0) + t->trace_len; + + while (opt0 < limit0) { + type0 = opt0->type; + switch (type0) { + case 0: /* Pad, just stop */ + opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1; + break; + + default: + if (hm->trace[type0]) { + s = (*hm->trace[type0])(s, opt0); + } else { + s = format (s, "\n unrecognized option %d length %d", type0, opt0->length); + } + opt0 = (ip6_hop_by_hop_option_t *) (((u8 *)opt0) + opt0->length + sizeof (ip6_hop_by_hop_option_t)); + break; + } + } + return s; +} + +/* + * Process the Hop-by-Hop Options header + */ +static uword +ip6_hop_by_hop (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + vlib_node_runtime_t *error_node = vlib_node_get_runtime(vm, ip6_hop_by_hop_node.index); + ip6_hop_by_hop_main_t *hm = &ip6_hop_by_hop_main; + u32 n_left_from, *from, *to_next; + ip_lookup_next_t next_index; + ip6_main_t * im = &ip6_main; + ip_lookup_main_t *lm = &im->lookup_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) { + u32 bi0; + vlib_buffer_t * b0; + u32 next0; + ip6_header_t * ip0; + ip6_hop_by_hop_header_t *hbh0; + ip6_hop_by_hop_option_t *opt0, *limit0; + u8 type0; + u8 error0 = 0; + + /* Speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + u32 adj_index0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX]; + ip_adjacency_t *adj0 = ip_get_adjacency(lm, adj_index0); + /* Default use the next_index from the adjacency. A HBH option rarely redirects to a different node */ + next0 = adj0->lookup_next_index; + + ip0 = vlib_buffer_get_current (b0); + hbh0 = (ip6_hop_by_hop_header_t *)(ip0+1); + opt0 = (ip6_hop_by_hop_option_t *)(hbh0+1); + limit0 = (ip6_hop_by_hop_option_t *)((u8 *)hbh0 + ((hbh0->length + 1) << 3)); + + /* + * Basic validity checks + */ + if ((hbh0->length + 1) << 3 > clib_net_to_host_u16(ip0->payload_length)) { + error0 = IP6_HOP_BY_HOP_ERROR_FORMAT; + next0 = IP_LOOKUP_NEXT_DROP; + goto out0; + } + + /* Scan the set of h-b-h options, process ones that we understand */ + while (opt0 < limit0) { + type0 = opt0->type; + switch (type0) { + case 0: /* Pad1 */ + opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1; + continue; + case 1: /* PadN */ + break; + default: + if (hm->options[type0]) { + if ((*hm->options[type0])(b0, ip0, opt0) < 0) { + error0 = IP6_HOP_BY_HOP_ERROR_FORMAT; + goto out0; + } + } else { + /* Unrecognized mandatory option, check the two high order bits */ + switch (opt0->type & HBH_OPTION_TYPE_HIGH_ORDER_BITS) { + case HBH_OPTION_TYPE_SKIP_UNKNOWN: + break; + case HBH_OPTION_TYPE_DISCARD_UNKNOWN: + next0 = IP_LOOKUP_NEXT_DROP; + break; + case HBH_OPTION_TYPE_DISCARD_UNKNOWN_ICMP: + next0 = IP_LOOKUP_NEXT_ICMP_ERROR; + icmp6_error_set_vnet_buffer(b0, ICMP6_parameter_problem, + ICMP6_parameter_problem_unrecognized_option, (u8 *)opt0 - (u8 *)ip0); + break; + case HBH_OPTION_TYPE_DISCARD_UNKNOWN_ICMP_NOT_MCAST: + if (!ip6_address_is_multicast(&ip0->dst_address)) { + next0 = IP_LOOKUP_NEXT_ICMP_ERROR; + icmp6_error_set_vnet_buffer(b0, ICMP6_parameter_problem, + ICMP6_parameter_problem_unrecognized_option, (u8 *)opt0 - (u8 *)ip0); + } else { + next0 = IP_LOOKUP_NEXT_DROP; + } + break; + } + error0 = IP6_HOP_BY_HOP_ERROR_UNKNOWN_OPTION; + goto out0; + } + } + opt0 = (ip6_hop_by_hop_option_t *) (((u8 *)opt0) + opt0->length + sizeof (ip6_hop_by_hop_option_t)); + } + + out0: + /* Has the classifier flagged this buffer for special treatment? */ + if ((error0 == 0) && (vnet_buffer(b0)->l2_classify.opaque_index == OI_DECAP)) + next0 = IP6_LOOKUP_NEXT_POP_HOP_BY_HOP; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) { + ip6_hop_by_hop_trace_t *t = vlib_add_trace(vm, node, b0, sizeof (*t)); + u32 trace_len = (hbh0->length + 1) << 3; + t->next_index = next0; + /* Capture the h-b-h option verbatim */ + trace_len = trace_len < ARRAY_LEN(t->option_data) ? trace_len : ARRAY_LEN(t->option_data); + t->trace_len = trace_len; + clib_memcpy(t->option_data, hbh0, trace_len); + } + + b0->error = error_node->errors[error0]; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (ip6_hop_by_hop_node) = { + .function = ip6_hop_by_hop, + .name = "ip6-hop-by-hop", + .sibling_of = "ip6-lookup", + .vector_size = sizeof (u32), + .format_trace = format_ip6_hop_by_hop_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(ip6_hop_by_hop_error_strings), + .error_strings = ip6_hop_by_hop_error_strings, + .n_next_nodes = 0, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip6_hop_by_hop_node, ip6_hop_by_hop) + +static clib_error_t * +ip6_hop_by_hop_init (vlib_main_t * vm) +{ + ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main; + memset(hm->options, 0, sizeof(hm->options)); + memset(hm->trace, 0, sizeof(hm->trace)); + + return (0); +} + +VLIB_INIT_FUNCTION (ip6_hop_by_hop_init); + +int +ip6_hbh_register_option (u8 option, + int options(vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt), + u8 *trace(u8 *s, ip6_hop_by_hop_option_t *opt)) +{ + ip6_main_t * im = &ip6_main; + ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main; + + ASSERT (option < ARRAY_LEN (hm->options)); + + /* Already registered */ + if (hm->options[option]) + return (-1); + + hm->options[option] = options; + hm->trace[option] = trace; + + /* Set global variable */ + im->hbh_enabled = 1; + + return (0); +} + +int +ip6_hbh_unregister_option (u8 option) +{ + ip6_main_t * im = &ip6_main; + ip6_hop_by_hop_main_t * hm = &ip6_hop_by_hop_main; + + ASSERT (option < ARRAY_LEN (hm->options)); + + /* Not registered */ + if (!hm->options[option]) + return (-1); + + hm->options[option] = NULL; + hm->trace[option] = NULL; + + /* Disable global knob if this was the last option configured */ + int i; + bool found = false; + for (i = 0; i < 256; i++) { + if (hm->options[option]) { + found = true; + break; + } + } + if (!found) + im->hbh_enabled = 0; + + return (0); +} + /* Global IP6 main. */ ip6_main_t ip6_main; @@ -2405,6 +2889,9 @@ ip6_lookup_init (vlib_main_t * vm) pn->unformat_edit = unformat_pg_ip6_header; } + /* Unless explicitly configured, don't process HBH options */ + im->hbh_enabled = 0; + { icmp6_neighbor_solicitation_header_t p; @@ -2429,6 +2916,8 @@ ip6_lookup_init (vlib_main_t * vm) "ip6 neighbor discovery"); } + ip6_feature_init (vm, im); + return 0; }