diff options
author | Hongjun Ni <hongjun.ni@intel.com> | 2016-05-25 01:16:19 +0800 |
---|---|---|
committer | Florin Coras <florin.coras@gmail.com> | 2016-05-25 10:42:43 +0000 |
commit | df921cc65a25f6fb71b1169db6ff004b4e45430e (patch) | |
tree | f45fb2fe3641c59db8f89acba0bf51cba95ad8b2 | |
parent | c4cb44c05d121da2e0f0ccd39d5e1bf470731a85 (diff) |
Add Vxlan-Gpe over IPv6
PatchSet4: consolidate code as per comments.
PatchSet3: simplify the code using ip_udp_encap_one/two
PatchSet2: consolidate comments and indent style
Change-Id: Ia8b43f854a46d77e838e198566200ad28fd72472
Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
-rw-r--r-- | vnet/vnet/ip/udp.h | 1 | ||||
-rw-r--r-- | vnet/vnet/vxlan-gpe/decap.c | 824 | ||||
-rw-r--r-- | vnet/vnet/vxlan-gpe/encap.c | 559 | ||||
-rw-r--r-- | vnet/vnet/vxlan-gpe/vxlan_gpe.c | 238 | ||||
-rw-r--r-- | vnet/vnet/vxlan-gpe/vxlan_gpe.h | 45 | ||||
-rw-r--r-- | vpp-api-test/vat/api_format.c | 43 | ||||
-rw-r--r-- | vpp/api/api.c | 16 | ||||
-rw-r--r-- | vpp/api/custom_dump.c | 5 | ||||
-rw-r--r-- | vpp/api/vpe.api | 5 |
9 files changed, 1021 insertions, 715 deletions
diff --git a/vnet/vnet/ip/udp.h b/vnet/vnet/ip/udp.h index a6991487507..1cf525c6093 100644 --- a/vnet/vnet/ip/udp.h +++ b/vnet/vnet/ip/udp.h @@ -51,6 +51,7 @@ _ (547, dhcpv6_to_server) \ _ (546, dhcpv6_to_client) \ _ (4341, lisp_gpe6) \ _ (4342, lisp_cp6) \ +_ (4790, vxlan6_gpe) \ _ (6633, vpath6_3) typedef enum { diff --git a/vnet/vnet/vxlan-gpe/decap.c b/vnet/vnet/vxlan-gpe/decap.c index 870871e7de6..34bcccd9333 100644 --- a/vnet/vnet/vxlan-gpe/decap.c +++ b/vnet/vnet/vxlan-gpe/decap.c @@ -19,6 +19,8 @@ #include <vnet/pg/pg.h> #include <vnet/vxlan-gpe/vxlan_gpe.h> +vlib_node_registration_t vxlan_gpe_input_node; + typedef struct { u32 next_index; u32 tunnel_index; @@ -32,15 +34,15 @@ static u8 * format_vxlan_gpe_rx_trace (u8 * s, va_list * args) vxlan_gpe_rx_trace_t * t = va_arg (*args, vxlan_gpe_rx_trace_t *); if (t->tunnel_index != ~0) - { - s = format (s, "VXLAN-GPE: tunnel %d next %d error %d", t->tunnel_index, - t->next_index, t->error); - } + { + s = format (s, "VXLAN-GPE: tunnel %d next %d error %d", t->tunnel_index, + t->next_index, t->error); + } else - { - s = format (s, "VXLAN-GPE: no tunnel next %d error %d\n", t->next_index, - t->error); - } + { + s = format (s, "VXLAN-GPE: no tunnel next %d error %d\n", t->next_index, + t->error); + } return s; } @@ -54,22 +56,24 @@ static u8 * format_vxlan_gpe_with_length (u8 * s, va_list * args) return s; } -static uword +always_inline uword vxlan_gpe_input (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) + vlib_frame_t * from_frame, + u8 is_ip4) { - u32 n_left_from, next_index, * from, * to_next; + u32 n_left_from, next_index, *from, *to_next; vxlan_gpe_main_t * ngm = &vxlan_gpe_main; vnet_main_t * vnm = ngm->vnet_main; vnet_interface_main_t * im = &vnm->interface_main; u32 last_tunnel_index = ~0; - vxlan_gpe_tunnel_key_t last_key; + vxlan4_gpe_tunnel_key_t last_key4; + vxlan6_gpe_tunnel_key_t last_key6; u32 pkts_decapsulated = 0; - u32 cpu_index = os_get_cpu_number(); + u32 cpu_index = os_get_cpu_number (); u32 stats_sw_if_index, stats_n_packets, stats_n_bytes; - memset (&last_key, 0xff, sizeof (last_key)); + memset (&last_key4, 0xff, sizeof(last_key4)); from = vlib_frame_vector_args (from_frame); n_left_from = from_frame->n_vectors; @@ -79,339 +83,458 @@ vxlan_gpe_input (vlib_main_t * vm, stats_n_packets = stats_n_bytes = 0; 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 >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - vlib_buffer_t * b0, * b1; - u32 next0, next1; - ip4_vxlan_gpe_header_t * iuvn0, * iuvn1; - uword * p0, * p1; - u32 tunnel_index0, tunnel_index1; - vxlan_gpe_tunnel_t * t0, * t1; - vxlan_gpe_tunnel_key_t key0, key1; - u32 error0, error1; - u32 sw_if_index0, sw_if_index1, len0, len1; - - /* Prefetch next iteration. */ - { - vlib_buffer_t * p2, * p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); - CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); - } - - bi0 = from[0]; - bi1 = from[1]; - to_next[0] = bi0; - to_next[1] = bi1; - from += 2; - to_next += 2; - n_left_to_next -= 2; - n_left_from -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - /* udp leaves current_data pointing at the vxlan header */ - vlib_buffer_advance - (b0, -(word)(sizeof(udp_header_t)+sizeof(ip4_header_t))); - vlib_buffer_advance - (b1, -(word)(sizeof(udp_header_t)+sizeof(ip4_header_t))); - - iuvn0 = vlib_buffer_get_current (b0); - iuvn1 = vlib_buffer_get_current (b1); - - /* pop (ip, udp, vxlan) */ - vlib_buffer_advance (b0, sizeof (*iuvn0)); - vlib_buffer_advance (b1, sizeof (*iuvn1)); - - tunnel_index0 = ~0; - tunnel_index1 = ~0; - error0 = 0; - error1 = 0; - - next0 = (iuvn0->vxlan.protocol < node->n_next_nodes) ? iuvn0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP; - next1 = (iuvn1->vxlan.protocol < node->n_next_nodes) ? iuvn1->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP; - - - - - key0.local = iuvn0->ip4.dst_address.as_u32; - key1.local = iuvn1->ip4.dst_address.as_u32; - - key0.remote = iuvn0->ip4.src_address.as_u32; - key1.remote = iuvn1->ip4.src_address.as_u32; - - key0.vni = iuvn0->vxlan.vni_res; - key1.vni = iuvn1->vxlan.vni_res; - - key0.pad = 0; - key1.pad = 0; - - /* Processing for key0 */ - if (PREDICT_FALSE ((key0.as_u64[0] != last_key.as_u64[0]) - || (key0.as_u64[1] != last_key.as_u64[1]))) - { - p0 = hash_get_mem (ngm->vxlan_gpe_tunnel_by_key, &key0); - - if (p0 == 0) - { - error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL; - goto trace0; - } + { + u32 n_left_to_next; - last_key.as_u64[0] = key0.as_u64[0]; - last_key.as_u64[1] = key0.as_u64[1]; - tunnel_index0 = last_tunnel_index = p0[0]; - } - else - tunnel_index0 = last_tunnel_index; + vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next); - t0 = pool_elt_at_index (ngm->tunnels, tunnel_index0); + while (n_left_from >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, *b1; + u32 next0, next1; + ip4_vxlan_gpe_header_t * iuvn4_0, *iuvn4_1; + ip6_vxlan_gpe_header_t * iuvn6_0, *iuvn6_1; + uword * p0, *p1; + u32 tunnel_index0, tunnel_index1; + vxlan_gpe_tunnel_t * t0, *t1; + vxlan4_gpe_tunnel_key_t key4_0, key4_1; + vxlan6_gpe_tunnel_key_t key6_0, key6_1; + u32 error0, error1; + u32 sw_if_index0, sw_if_index1, len0, len1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header(p2, LOAD); + vlib_prefetch_buffer_header(p3, LOAD); + + CLIB_PREFETCH(p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH(p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + } + + bi0 = from[0]; + bi1 = from[1]; + to_next[0] = bi0; + to_next[1] = bi1; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + if (is_ip4) + { + /* udp leaves current_data pointing at the vxlan-gpe header */ + vlib_buffer_advance (b0, -(word) (sizeof(udp_header_t) + sizeof(ip4_header_t))); + vlib_buffer_advance (b1, -(word) (sizeof(udp_header_t) + sizeof(ip4_header_t))); + + iuvn4_0 = vlib_buffer_get_current (b0); + iuvn4_1 = vlib_buffer_get_current (b1); + + /* pop (ip, udp, vxlan) */ + vlib_buffer_advance (b0, sizeof(*iuvn4_0)); + vlib_buffer_advance (b1, sizeof(*iuvn4_1)); + } + else + { + /* udp leaves current_data pointing at the vxlan-gpe header */ + vlib_buffer_advance (b0, -(word) (sizeof(udp_header_t) + sizeof(ip6_header_t))); + vlib_buffer_advance (b1, -(word) (sizeof(udp_header_t) + sizeof(ip6_header_t))); + + iuvn6_0 = vlib_buffer_get_current (b0); + iuvn6_1 = vlib_buffer_get_current (b1); + + /* pop (ip, udp, vxlan) */ + vlib_buffer_advance (b0, sizeof(*iuvn6_0)); + vlib_buffer_advance (b1, sizeof(*iuvn6_1)); + } + + tunnel_index0 = ~0; + tunnel_index1 = ~0; + error0 = 0; + error1 = 0; + + if (is_ip4) + { + next0 = (iuvn4_0->vxlan.protocol < node->n_next_nodes) ? + iuvn4_0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP; + next1 = (iuvn4_1->vxlan.protocol < node->n_next_nodes) ? + iuvn4_1->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP; + + key4_0.local = iuvn4_0->ip4.dst_address.as_u32; + key4_1.local = iuvn4_1->ip4.dst_address.as_u32; + + key4_0.remote = iuvn4_0->ip4.src_address.as_u32; + key4_1.remote = iuvn4_1->ip4.src_address.as_u32; + + key4_0.vni = iuvn4_0->vxlan.vni_res; + key4_1.vni = iuvn4_1->vxlan.vni_res; + + key4_0.pad = 0; + key4_1.pad = 0; + + /* Processing for key4_0 */ + if (PREDICT_FALSE((key4_0.as_u64[0] != last_key4.as_u64[0]) + || (key4_0.as_u64[1] != last_key4.as_u64[1]))) + { + p0 = hash_get_mem(ngm->vxlan4_gpe_tunnel_by_key, &key4_0); + + if (p0 == 0) + { + error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL; + goto trace0; + } - next0 = t0->protocol; - - sw_if_index0 = t0->sw_if_index; - len0 = vlib_buffer_length_in_chain(vm, b0); - - /* Required to make the l2 tag push / pop code work on l2 subifs */ - vnet_update_l2_len (b0); - - /* - * ip[46] lookup in the configured FIB - */ - vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index; + last_key4.as_u64[0] = key4_0.as_u64[0]; + last_key4.as_u64[1] = key4_0.as_u64[1]; + tunnel_index0 = last_tunnel_index = p0[0]; + } + else + tunnel_index0 = last_tunnel_index; + } + else /* is_ip6 */ + { + next0 = (iuvn6_0->vxlan.protocol < node->n_next_nodes) ? + iuvn6_0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP; + next1 = (iuvn6_1->vxlan.protocol < node->n_next_nodes) ? + iuvn6_1->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP; + + key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0]; + key6_0.local.as_u64[1] = iuvn6_0->ip6.dst_address.as_u64[1]; + key6_1.local.as_u64[0] = iuvn6_1->ip6.dst_address.as_u64[0]; + key6_1.local.as_u64[1] = iuvn6_1->ip6.dst_address.as_u64[1]; + + key6_0.remote.as_u64[0] = iuvn6_0->ip6.src_address.as_u64[0]; + key6_0.remote.as_u64[1] = iuvn6_0->ip6.src_address.as_u64[1]; + key6_1.remote.as_u64[0] = iuvn6_1->ip6.src_address.as_u64[0]; + key6_1.remote.as_u64[1] = iuvn6_1->ip6.src_address.as_u64[1]; + + key6_0.vni = iuvn6_0->vxlan.vni_res; + key6_1.vni = iuvn6_1->vxlan.vni_res; + + /* Processing for key6_0 */ + if (PREDICT_FALSE(memcmp (&key6_0, &last_key6, sizeof(last_key6)) != 0)) + { + p0 = hash_get_mem(ngm->vxlan6_gpe_tunnel_by_key, &key6_0); + + if (p0 == 0) + { + error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL; + goto trace0; + } - pkts_decapsulated++; - stats_n_packets += 1; - stats_n_bytes += len0; + memcpy (&last_key6, &key6_0, sizeof(key6_0)); + tunnel_index0 = last_tunnel_index = p0[0]; + } + else + tunnel_index0 = last_tunnel_index; + } + + t0 = pool_elt_at_index(ngm->tunnels, tunnel_index0); + + next0 = t0->protocol; + + sw_if_index0 = t0->sw_if_index; + len0 = vlib_buffer_length_in_chain (vm, b0); + + /* Required to make the l2 tag push / pop code work on l2 subifs */ + vnet_update_l2_len (b0); + + /* + * ip[46] lookup in the configured FIB + */ + vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index; + + pkts_decapsulated++; + stats_n_packets += 1; + stats_n_bytes += len0; + + if (PREDICT_FALSE(sw_if_index0 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len0; + if (stats_n_packets) + vlib_increment_combined_counter ( + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len0; + stats_sw_if_index = sw_if_index0; + } + + trace0: b0->error = error0 ? node->errors[error0] : 0; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof(*tr)); + tr->next_index = next0; + tr->error = error0; + tr->tunnel_index = tunnel_index0; + } + + /* Process packet 1 */ + if (is_ip4) + { + /* Processing for key4_1 */ + if (PREDICT_FALSE( + (key4_1.as_u64[0] != last_key4.as_u64[0]) + || (key4_1.as_u64[1] != last_key4.as_u64[1]))) + { + p1 = hash_get_mem(ngm->vxlan4_gpe_tunnel_by_key, &key4_1); + + if (p1 == 0) + { + error1 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL; + goto trace1; + } - if (PREDICT_FALSE(sw_if_index0 != stats_sw_if_index)) + last_key4.as_u64[0] = key4_1.as_u64[0]; + last_key4.as_u64[1] = key4_1.as_u64[1]; + tunnel_index1 = last_tunnel_index = p1[0]; + } + else + tunnel_index1 = last_tunnel_index; + } + else /* is_ip6 */ + { + /* Processing for key6_1 */ + if (PREDICT_FALSE(memcmp (&key6_1, &last_key6, sizeof(last_key6)) != 0)) + { + p1 = hash_get_mem(ngm->vxlan6_gpe_tunnel_by_key, &key6_1); + + if (p1 == 0) { - stats_n_packets -= 1; - stats_n_bytes -= len0; - if (stats_n_packets) - vlib_increment_combined_counter( - im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, - cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); - stats_n_packets = 1; - stats_n_bytes = len0; - stats_sw_if_index = sw_if_index0; + error1 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL; + goto trace1; } - trace0: - b0->error = error0 ? node->errors[error0] : 0; - - if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) - { - vxlan_gpe_rx_trace_t *tr - = vlib_add_trace (vm, node, b0, sizeof (*tr)); - tr->next_index = next0; - tr->error = error0; - tr->tunnel_index = tunnel_index0; - } - - - /* Processing for key1 */ - if (PREDICT_FALSE ((key1.as_u64[0] != last_key.as_u64[0]) - || (key1.as_u64[1] != last_key.as_u64[1]))) - { - p1 = hash_get_mem (ngm->vxlan_gpe_tunnel_by_key, &key1); - - if (p1 == 0) - { - error1 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL; - goto trace1; - } - - last_key.as_u64[0] = key1.as_u64[0]; - last_key.as_u64[1] = key1.as_u64[1]; - tunnel_index1 = last_tunnel_index = p1[0]; - } - else - tunnel_index1 = last_tunnel_index; - - t1 = pool_elt_at_index (ngm->tunnels, tunnel_index1); - - next1 = t1->protocol; - sw_if_index1 = t1->sw_if_index; - len1 = vlib_buffer_length_in_chain(vm, b1); - - /* Required to make the l2 tag push / pop code work on l2 subifs */ - vnet_update_l2_len (b1); - - /* - * ip[46] lookup in the configured FIB - */ - vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index; - - - pkts_decapsulated++; - stats_n_packets += 1; - stats_n_bytes += len1; - - /* Batch stats increment on the same vxlan tunnel so counter - is not incremented per packet */ - if (PREDICT_FALSE(sw_if_index1 != stats_sw_if_index)) + memcpy (&last_key6, &key6_1, sizeof(key6_1)); + tunnel_index1 = last_tunnel_index = p1[0]; + } + else + tunnel_index1 = last_tunnel_index; + } + + t1 = pool_elt_at_index(ngm->tunnels, tunnel_index1); + + next1 = t1->protocol; + sw_if_index1 = t1->sw_if_index; + len1 = vlib_buffer_length_in_chain (vm, b1); + + /* Required to make the l2 tag push / pop code work on l2 subifs */ + vnet_update_l2_len (b1); + + /* + * ip[46] lookup in the configured FIB + */ + vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index; + + pkts_decapsulated++; + stats_n_packets += 1; + stats_n_bytes += len1; + + /* Batch stats increment on the same vxlan tunnel so counter + is not incremented per packet */ + if (PREDICT_FALSE(sw_if_index1 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len1; + if (stats_n_packets) + vlib_increment_combined_counter ( + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len1; + stats_sw_if_index = sw_if_index1; + } + vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index; + + trace1: b1->error = error1 ? node->errors[error1] : 0; + + if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b1, sizeof(*tr)); + tr->next_index = next1; + tr->error = error1; + tr->tunnel_index = tunnel_index1; + } + + vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, + n_left_to_next, bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + u32 next0; + ip4_vxlan_gpe_header_t * iuvn4_0; + ip6_vxlan_gpe_header_t * iuvn6_0; + uword * p0; + u32 tunnel_index0; + vxlan_gpe_tunnel_t * t0; + vxlan4_gpe_tunnel_key_t key4_0; + vxlan6_gpe_tunnel_key_t key6_0; + u32 error0; + u32 sw_if_index0, len0; + + 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); + + if (is_ip4) + { + /* udp leaves current_data pointing at the vxlan-gpe header */ + vlib_buffer_advance ( + b0, -(word) (sizeof(udp_header_t) + sizeof(ip4_header_t))); + + iuvn4_0 = vlib_buffer_get_current (b0); + + /* pop (ip, udp, vxlan) */ + vlib_buffer_advance (b0, sizeof(*iuvn4_0)); + } + else + { + /* udp leaves current_data pointing at the vxlan-gpe header */ + vlib_buffer_advance ( + b0, -(word) (sizeof(udp_header_t) + sizeof(ip6_header_t))); + + iuvn6_0 = vlib_buffer_get_current (b0); + + /* pop (ip, udp, vxlan) */ + vlib_buffer_advance (b0, sizeof(*iuvn6_0)); + } + + tunnel_index0 = ~0; + error0 = 0; + + if (is_ip4) + { + next0 = + (iuvn4_0->vxlan.protocol < node->n_next_nodes) ? + iuvn4_0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP; + + key4_0.local = iuvn4_0->ip4.dst_address.as_u32; + key4_0.remote = iuvn4_0->ip4.src_address.as_u32; + key4_0.vni = iuvn4_0->vxlan.vni_res; + key4_0.pad = 0; + + /* Processing for key4_0 */ + if (PREDICT_FALSE( + (key4_0.as_u64[0] != last_key4.as_u64[0]) + || (key4_0.as_u64[1] != last_key4.as_u64[1]))) + { + p0 = hash_get_mem(ngm->vxlan4_gpe_tunnel_by_key, &key4_0); + + if (p0 == 0) { - stats_n_packets -= 1; - stats_n_bytes -= len1; - if (stats_n_packets) - vlib_increment_combined_counter( - im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, - cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); - stats_n_packets = 1; - stats_n_bytes = len1; - stats_sw_if_index = sw_if_index1; + error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL; + goto trace00; } - vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index; - - trace1: - b1->error = error1 ? node->errors[error1] : 0; - - if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) - { - vxlan_gpe_rx_trace_t *tr - = vlib_add_trace (vm, node, b1, sizeof (*tr)); - tr->next_index = next1; - tr->error = error1; - tr->tunnel_index = tunnel_index1; - } - - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t * b0; - u32 next0; - ip4_vxlan_gpe_header_t * iuvn0; - uword * p0; - u32 tunnel_index0; - vxlan_gpe_tunnel_t * t0; - vxlan_gpe_tunnel_key_t key0; - u32 error0; - u32 sw_if_index0, len0; - - 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); - - /* udp leaves current_data pointing at the vxlan header */ - vlib_buffer_advance - (b0, -(word)(sizeof(udp_header_t)+sizeof(ip4_header_t))); - - iuvn0 = vlib_buffer_get_current (b0); - - /* pop (ip, udp, vxlan) */ - vlib_buffer_advance (b0, sizeof (*iuvn0)); - - tunnel_index0 = ~0; - error0 = 0; - next0 = (iuvn0->vxlan.protocol < node->n_next_nodes) ? iuvn0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP; - - key0.local = iuvn0->ip4.dst_address.as_u32; - key0.remote = iuvn0->ip4.src_address.as_u32; - key0.vni = iuvn0->vxlan.vni_res; - key0.pad = 0; - - if (PREDICT_FALSE ((key0.as_u64[0] != last_key.as_u64[0]) - || (key0.as_u64[1] != last_key.as_u64[1]))) - { - p0 = hash_get_mem (ngm->vxlan_gpe_tunnel_by_key, &key0); - - if (p0 == 0) - { - error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL; - goto trace00; - } - - last_key.as_u64[0] = key0.as_u64[0]; - last_key.as_u64[1] = key0.as_u64[1]; - tunnel_index0 = last_tunnel_index = p0[0]; - } - else - tunnel_index0 = last_tunnel_index; - - t0 = pool_elt_at_index (ngm->tunnels, tunnel_index0); - - next0 = t0->protocol; - - sw_if_index0 = t0->sw_if_index; - len0 = vlib_buffer_length_in_chain(vm, b0); - - /* Required to make the l2 tag push / pop code work on l2 subifs */ - vnet_update_l2_len (b0); - - /* - * ip[46] lookup in the configured FIB - */ - vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index; - - pkts_decapsulated ++; - stats_n_packets += 1; - stats_n_bytes += len0; - - /* Batch stats increment on the same vxlan-gpe tunnel so counter - is not incremented per packet */ - if (PREDICT_FALSE(sw_if_index0 != stats_sw_if_index)) + + last_key4.as_u64[0] = key4_0.as_u64[0]; + last_key4.as_u64[1] = key4_0.as_u64[1]; + tunnel_index0 = last_tunnel_index = p0[0]; + } + else + tunnel_index0 = last_tunnel_index; + } + else /* is_ip6 */ + { + next0 = (iuvn6_0->vxlan.protocol < node->n_next_nodes) ? + iuvn6_0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP; + + key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0]; + key6_0.local.as_u64[1] = iuvn6_0->ip6.dst_address.as_u64[1]; + key6_0.remote.as_u64[0] = iuvn6_0->ip6.src_address.as_u64[0]; + key6_0.remote.as_u64[1] = iuvn6_0->ip6.src_address.as_u64[1]; + key6_0.vni = iuvn6_0->vxlan.vni_res; + + /* Processing for key6_0 */ + if (PREDICT_FALSE(memcmp (&key6_0, &last_key6, sizeof(last_key6)) != 0)) + { + p0 = hash_get_mem(ngm->vxlan6_gpe_tunnel_by_key, &key6_0); + + if (p0 == 0) { - stats_n_packets -= 1; - stats_n_bytes -= len0; - if (stats_n_packets) - vlib_increment_combined_counter( - im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, - cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); - stats_n_packets = 1; - stats_n_bytes = len0; - stats_sw_if_index = sw_if_index0; + error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL; + goto trace00; } - trace00: - b0->error = error0 ? node->errors[error0] : 0; - - if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) - { - vxlan_gpe_rx_trace_t *tr - = vlib_add_trace (vm, node, b0, sizeof (*tr)); - tr->next_index = next0; - tr->error = error0; - tr->tunnel_index = tunnel_index0; - } - 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); + memcpy (&last_key6, &key6_0, sizeof(key6_0)); + tunnel_index0 = last_tunnel_index = p0[0]; + } + else + tunnel_index0 = last_tunnel_index; + } + + t0 = pool_elt_at_index(ngm->tunnels, tunnel_index0); + + next0 = t0->protocol; + + sw_if_index0 = t0->sw_if_index; + len0 = vlib_buffer_length_in_chain (vm, b0); + + /* Required to make the l2 tag push / pop code work on l2 subifs */ + vnet_update_l2_len (b0); + + /* + * ip[46] lookup in the configured FIB + */ + vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index; + + pkts_decapsulated++; + stats_n_packets += 1; + stats_n_bytes += len0; + + /* Batch stats increment on the same vxlan-gpe tunnel so counter + is not incremented per packet */ + if (PREDICT_FALSE(sw_if_index0 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len0; + if (stats_n_packets) + vlib_increment_combined_counter ( + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len0; + stats_sw_if_index = sw_if_index0; + } + + trace00: b0->error = error0 ? node->errors[error0] : 0; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof(*tr)); + tr->next_index = next0; + tr->error = error0; + tr->tunnel_index = tunnel_index0; + } + 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); + } vlib_node_increment_counter (vm, vxlan_gpe_input_node.index, - VXLAN_GPE_ERROR_DECAPSULATED, - pkts_decapsulated); + VXLAN_GPE_ERROR_DECAPSULATED, pkts_decapsulated); /* Increment any remaining batch stats */ if (stats_n_packets) { - vlib_increment_combined_counter( + vlib_increment_combined_counter ( im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); node->runtime_data[0] = stats_sw_if_index; @@ -419,6 +542,20 @@ vxlan_gpe_input (vlib_main_t * vm, return from_frame->n_vectors; } +static uword +vxlan4_gpe_input (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return vxlan_gpe_input (vm, node, from_frame, /* is_ip4 */1); +} + +static uword +vxlan6_gpe_input (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return vxlan_gpe_input (vm, node, from_frame, /* is_ip4 */0); +} + static char * vxlan_gpe_error_strings[] = { #define vxlan_gpe_error(n,s) s, #include <vnet/vxlan-gpe/vxlan_gpe_error.def> @@ -426,9 +563,9 @@ static char * vxlan_gpe_error_strings[] = { #undef _ }; -VLIB_REGISTER_NODE (vxlan_gpe_input_node) = { - .function = vxlan_gpe_input, - .name = "vxlan-gpe-input", +VLIB_REGISTER_NODE (vxlan4_gpe_input_node) = { + .function = vxlan4_gpe_input, + .name = "vxlan4-gpe-input", /* Takes a vector of packets. */ .vector_size = sizeof (u32), .type = VLIB_NODE_TYPE_INTERNAL, @@ -447,4 +584,27 @@ VLIB_REGISTER_NODE (vxlan_gpe_input_node) = { // $$$$ .unformat_buffer = unformat_vxlan_gpe_header, }; +VLIB_NODE_FUNCTION_MULTIARCH (vxlan4_gpe_input_node, vxlan4_gpe_input) + +VLIB_REGISTER_NODE (vxlan6_gpe_input_node) = { + .function = vxlan6_gpe_input, + .name = "vxlan6-gpe-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(vxlan_gpe_error_strings), + .error_strings = vxlan_gpe_error_strings, + + .n_next_nodes = VXLAN_GPE_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [VXLAN_GPE_INPUT_NEXT_##s] = n, + foreach_vxlan_gpe_input_next +#undef _ + }, + + .format_buffer = format_vxlan_gpe_with_length, + .format_trace = format_vxlan_gpe_rx_trace, + // $$$$ .unformat_buffer = unformat_vxlan_gpe_header, +}; +VLIB_NODE_FUNCTION_MULTIARCH (vxlan6_gpe_input_node, vxlan6_gpe_input) diff --git a/vnet/vnet/vxlan-gpe/encap.c b/vnet/vnet/vxlan-gpe/encap.c index 3ffe2a62db2..ba0eca2a0a1 100644 --- a/vnet/vnet/vxlan-gpe/encap.c +++ b/vnet/vnet/vxlan-gpe/encap.c @@ -38,6 +38,7 @@ typedef enum { typedef enum { VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP, + VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP, VXLAN_GPE_ENCAP_NEXT_DROP, VXLAN_GPE_ENCAP_N_NEXT } vxlan_gpe_encap_next_t; @@ -58,21 +59,59 @@ u8 * format_vxlan_gpe_encap_trace (u8 * s, va_list * args) return s; } -#define foreach_fixed_header_offset \ -_(0) _(1) _(2) _(3) _(4) _(5) _(6) +always_inline void +vxlan_gpe_encap_one_inline (vxlan_gpe_main_t * ngm, vlib_buffer_t * b0, + vxlan_gpe_tunnel_t * t0, u32 * next0, u8 is_v4) +{ + ASSERT(sizeof(ip4_vxlan_gpe_header_t) == 36); + ASSERT(sizeof(ip6_vxlan_gpe_header_t) == 56); + + if (is_v4) + { + ip_udp_encap_one (ngm->vlib_main, b0, t0->rewrite, 36, 1); + next0[0] = VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP; + + } + else + { + ip_udp_encap_one (ngm->vlib_main, b0, t0->rewrite, 56, 0); + next0[0] = VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP; + } +} + +always_inline void +vxlan_gpe_encap_two_inline (vxlan_gpe_main_t * ngm, vlib_buffer_t * b0, vlib_buffer_t * b1, + vxlan_gpe_tunnel_t * t0, vxlan_gpe_tunnel_t * t1, u32 * next0, + u32 * next1, u8 is_v4) +{ + ASSERT(sizeof(ip4_vxlan_gpe_header_t) == 36); + ASSERT(sizeof(ip6_vxlan_gpe_header_t) == 56); + + if (is_v4) + { + ip_udp_encap_one (ngm->vlib_main, b0, t0->rewrite, 36, 1); + ip_udp_encap_one (ngm->vlib_main, b1, t1->rewrite, 36, 1); + next0[0] = next1[0] = VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP; + } + else + { + ip_udp_encap_one (ngm->vlib_main, b0, t0->rewrite, 56, 0); + ip_udp_encap_one (ngm->vlib_main, b1, t1->rewrite, 56, 0); + next0[0] = next1[0] = VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP; + } +} static uword vxlan_gpe_encap (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { - u32 n_left_from, next_index, * from, * to_next; + u32 n_left_from, next_index, *from, *to_next; vxlan_gpe_main_t * ngm = &vxlan_gpe_main; vnet_main_t * vnm = ngm->vnet_main; vnet_interface_main_t * im = &vnm->interface_main; u32 pkts_encapsulated = 0; - u16 old_l0 = 0, old_l1 = 0; - u32 cpu_index = os_get_cpu_number(); + u32 cpu_index = os_get_cpu_number (); u32 stats_sw_if_index, stats_n_packets, stats_n_bytes; from = vlib_frame_vector_args (from_frame); @@ -83,324 +122,207 @@ vxlan_gpe_encap (vlib_main_t * vm, stats_n_packets = stats_n_bytes = 0; 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 >= 4 && n_left_to_next >= 2) { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, - to_next, n_left_to_next); - - while (n_left_from >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - vlib_buffer_t * b0, * b1; - u32 next0 = VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP; - u32 next1 = VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP; - u32 sw_if_index0, sw_if_index1, len0, len1; - vnet_hw_interface_t * hi0, * hi1; - ip4_header_t * ip0, * ip1; - udp_header_t * udp0, * udp1; - u64 * copy_src0, * copy_dst0; - u64 * copy_src1, * copy_dst1; - u32 * copy_src_last0, * copy_dst_last0; - u32 * copy_src_last1, * copy_dst_last1; - vxlan_gpe_tunnel_t * t0, * t1; - u16 new_l0, new_l1; - ip_csum_t sum0, sum1; - - /* Prefetch next iteration. */ - { - vlib_buffer_t * p2, * p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); - CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); - } - - bi0 = from[0]; - bi1 = from[1]; - to_next[0] = bi0; - to_next[1] = bi1; - from += 2; - to_next += 2; - n_left_to_next -= 2; - n_left_from -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - /* 1-wide cache? */ - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX]; - sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_TX]; - hi0 = vnet_get_sup_hw_interface - (vnm, vnet_buffer(b0)->sw_if_index[VLIB_TX]); - hi1 = vnet_get_sup_hw_interface - (vnm, vnet_buffer(b1)->sw_if_index[VLIB_TX]); - - t0 = pool_elt_at_index (ngm->tunnels, hi0->dev_instance); - t1 = pool_elt_at_index (ngm->tunnels, hi1->dev_instance); - - ASSERT(vec_len(t0->rewrite) >= 24); - ASSERT(vec_len(t1->rewrite) >= 24); - - /* Apply the rewrite string. $$$$ vnet_rewrite? */ - vlib_buffer_advance (b0, -(word)_vec_len(t0->rewrite)); - vlib_buffer_advance (b1, -(word)_vec_len(t1->rewrite)); - - ip0 = vlib_buffer_get_current(b0); - ip1 = vlib_buffer_get_current(b1); - /* Copy the fixed header */ - copy_dst0 = (u64 *) ip0; - copy_src0 = (u64 *) t0->rewrite; - copy_dst1 = (u64 *) ip1; - copy_src1 = (u64 *) t1->rewrite; - - ASSERT (sizeof (ip4_vxlan_gpe_header_t) == 36); - - /* Copy first 36 octets 8-bytes at a time */ -#define _(offs) copy_dst0[offs] = copy_src0[offs]; - foreach_fixed_header_offset; -#undef _ -#define _(offs) copy_dst1[offs] = copy_src1[offs]; - foreach_fixed_header_offset; -#undef _ + u32 bi0, bi1; + vlib_buffer_t * b0, *b1; + u32 next0, next1; + u32 sw_if_index0, sw_if_index1, len0, len1; + vnet_hw_interface_t * hi0, *hi1; + vxlan_gpe_tunnel_t * t0, *t1; + u8 is_ip4_0, is_ip4_1; + + next0 = next1 = VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header(p2, LOAD); + vlib_prefetch_buffer_header(p3, LOAD); + + CLIB_PREFETCH(p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH(p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + } + + bi0 = from[0]; + bi1 = from[1]; + to_next[0] = bi0; + to_next[1] = bi1; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + /* 1-wide cache? */ + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX]; + sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_TX]; + hi0 = vnet_get_sup_hw_interface (vnm, vnet_buffer(b0)->sw_if_index[VLIB_TX]); + hi1 = vnet_get_sup_hw_interface (vnm, vnet_buffer(b1)->sw_if_index[VLIB_TX]); + + t0 = pool_elt_at_index(ngm->tunnels, hi0->dev_instance); + t1 = pool_elt_at_index(ngm->tunnels, hi1->dev_instance); + + is_ip4_0 = (t0->flags & VXLAN_GPE_TUNNEL_IS_IPV4); + is_ip4_1 = (t1->flags & VXLAN_GPE_TUNNEL_IS_IPV4); + + if (PREDICT_TRUE(is_ip4_0 == is_ip4_1)) + { + vxlan_gpe_encap_two_inline (ngm, b0, b1, t0, t1, &next0, &next1,is_ip4_0); + } + else + { + vxlan_gpe_encap_one_inline (ngm, b0, t0, &next0, is_ip4_0); + vxlan_gpe_encap_one_inline (ngm, b1, t1, &next1, is_ip4_1); + } + + /* Reset to look up tunnel partner in the configured FIB */ + vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index; + vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->encap_fib_index; + vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0; + vnet_buffer(b1)->sw_if_index[VLIB_RX] = sw_if_index1; + pkts_encapsulated += 2; + + len0 = vlib_buffer_length_in_chain (vm, b0); + len1 = vlib_buffer_length_in_chain (vm, b0); + stats_n_packets += 2; + stats_n_bytes += len0 + len1; + + /* Batch stats increment on the same vxlan tunnel so counter is not + incremented per packet. Note stats are still incremented for deleted + and admin-down tunnel where packets are dropped. It is not worthwhile + to check for this rare case and affect normal path performance. */ + if (PREDICT_FALSE((sw_if_index0 != stats_sw_if_index) + || (sw_if_index1 != stats_sw_if_index))) + { + stats_n_packets -= 2; + stats_n_bytes -= len0 + len1; + if (sw_if_index0 == sw_if_index1) + { + if (stats_n_packets) + vlib_increment_combined_counter ( + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); + stats_sw_if_index = sw_if_index0; + stats_n_packets = 2; + stats_n_bytes = len0 + len1; + } + else + { + vlib_increment_combined_counter ( + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + cpu_index, sw_if_index0, 1, len0); + vlib_increment_combined_counter ( + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + cpu_index, sw_if_index1, 1, len1); + } + } + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gpe_encap_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof(*tr)); + tr->tunnel_index = t0 - ngm->tunnels; + } + + if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gpe_encap_trace_t *tr = vlib_add_trace (vm, node, b1, + sizeof(*tr)); + tr->tunnel_index = t1 - ngm->tunnels; + } + + vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, + n_left_to_next, bi0, bi1, next0, next1); + } - /* Last 4 octets. Hopefully gcc will be our friend */ - copy_dst_last0 = (u32 *)(©_dst0[7]); - copy_src_last0 = (u32 *)(©_src0[7]); - copy_dst_last1 = (u32 *)(©_dst1[7]); - copy_src_last1 = (u32 *)(©_src1[7]); - - copy_dst_last0[0] = copy_src_last0[0]; - copy_dst_last1[0] = copy_src_last1[0]; - - /* If there are TLVs to copy, do so */ - if (PREDICT_FALSE (_vec_len(t0->rewrite) > 64)) - clib_memcpy (©_dst0[3], t0->rewrite + 64 , - _vec_len (t0->rewrite)-64); - - if (PREDICT_FALSE (_vec_len(t1->rewrite) > 64)) - clib_memcpy (©_dst0[3], t1->rewrite + 64 , - _vec_len (t1->rewrite)-64); - - /* fix the <bleep>ing outer-IP checksum */ - sum0 = ip0->checksum; - /* old_l0 always 0, see the rewrite setup */ - new_l0 = - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); - - sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, - length /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - ip0->length = new_l0; - - sum1 = ip1->checksum; - /* old_l1 always 0, see the rewrite setup */ - new_l1 = - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)); - - sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t, - length /* changed member */); - ip1->checksum = ip_csum_fold (sum1); - ip1->length = new_l1; - - /* Fix UDP length */ - udp0 = (udp_header_t *)(ip0+1); - new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) - - sizeof (*ip0)); - udp1 = (udp_header_t *)(ip1+1); - new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1) - - sizeof (*ip1)); - - udp0->length = new_l0; - udp1->length = new_l1; - - /* Reset to look up tunnel partner in the configured FIB */ - vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index; - vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->encap_fib_index; - vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0; - vnet_buffer(b1)->sw_if_index[VLIB_RX] = sw_if_index1; - pkts_encapsulated += 2; - - len0 = vlib_buffer_length_in_chain(vm, b0); - len1 = vlib_buffer_length_in_chain(vm, b0); - stats_n_packets += 2; - stats_n_bytes += len0 + len1; - - /* Batch stats increment on the same vxlan tunnel so counter is not - incremented per packet. Note stats are still incremented for deleted - and admin-down tunnel where packets are dropped. It is not worthwhile - to check for this rare case and affect normal path performance. */ - if (PREDICT_FALSE( - (sw_if_index0 != stats_sw_if_index) - || (sw_if_index1 != stats_sw_if_index))) { - stats_n_packets -= 2; - stats_n_bytes -= len0 + len1; - if (sw_if_index0 == sw_if_index1) { - if (stats_n_packets) - vlib_increment_combined_counter( - im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, - cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); - stats_sw_if_index = sw_if_index0; - stats_n_packets = 2; - stats_n_bytes = len0 + len1; - } else { - vlib_increment_combined_counter( - im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, - cpu_index, sw_if_index0, 1, len0); - vlib_increment_combined_counter( - im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, - cpu_index, sw_if_index1, 1, len1); - } - } - - if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) - { - vxlan_gpe_encap_trace_t *tr = - vlib_add_trace (vm, node, b0, sizeof (*tr)); - tr->tunnel_index = t0 - ngm->tunnels; - } - - if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) - { - vxlan_gpe_encap_trace_t *tr = - vlib_add_trace (vm, node, b1, sizeof (*tr)); - tr->tunnel_index = t1 - ngm->tunnels; - } - - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t * b0; - u32 next0 = VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP; - u32 sw_if_index0, len0; - vnet_hw_interface_t * hi0; - ip4_header_t * ip0; - udp_header_t * udp0; - u64 * copy_src0, * copy_dst0; - u32 * copy_src_last0, * copy_dst_last0; - vxlan_gpe_tunnel_t * t0; - u16 new_l0; - ip_csum_t sum0; - - 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); - - /* 1-wide cache? */ - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX]; - hi0 = vnet_get_sup_hw_interface - (vnm, vnet_buffer(b0)->sw_if_index[VLIB_TX]); - - t0 = pool_elt_at_index (ngm->tunnels, hi0->dev_instance); - - ASSERT(vec_len(t0->rewrite) >= 24); - - /* Apply the rewrite string. $$$$ vnet_rewrite? */ - vlib_buffer_advance (b0, -(word)_vec_len(t0->rewrite)); - - ip0 = vlib_buffer_get_current(b0); - /* Copy the fixed header */ - copy_dst0 = (u64 *) ip0; - copy_src0 = (u64 *) t0->rewrite; - - ASSERT (sizeof (ip4_vxlan_gpe_header_t) == 36); - - /* Copy first 36 octets 8-bytes at a time */ -#define _(offs) copy_dst0[offs] = copy_src0[offs]; - foreach_fixed_header_offset; -#undef _ - /* Last 4 octets. Hopefully gcc will be our friend */ - copy_dst_last0 = (u32 *)(©_dst0[7]); - copy_src_last0 = (u32 *)(©_src0[7]); - - copy_dst_last0[0] = copy_src_last0[0]; - - /* If there are TLVs to copy, do so */ - if (PREDICT_FALSE (_vec_len(t0->rewrite) > 64)) - clib_memcpy (©_dst0[3], t0->rewrite + 64 , - _vec_len (t0->rewrite)-64); - - /* fix the <bleep>ing outer-IP checksum */ - sum0 = ip0->checksum; - /* old_l0 always 0, see the rewrite setup */ - new_l0 = - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); - - sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, - length /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - ip0->length = new_l0; - - /* Fix UDP length */ - udp0 = (udp_header_t *)(ip0+1); - new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) - - sizeof (*ip0)); - - udp0->length = new_l0; - - /* Reset to look up tunnel partner in the configured FIB */ - vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index; - vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0; - pkts_encapsulated ++; - - len0 = vlib_buffer_length_in_chain(vm, b0); - stats_n_packets += 1; - stats_n_bytes += len0; - - /* Batch stats increment on the same vxlan tunnel so counter is not - * incremented per packet. Note stats are still incremented for deleted - * and admin-down tunnel where packets are dropped. It is not worthwhile - * to check for this rare case and affect normal path performance. */ - if (PREDICT_FALSE(sw_if_index0 != stats_sw_if_index)) - { - stats_n_packets -= 1; - stats_n_bytes -= len0; - if (stats_n_packets) - vlib_increment_combined_counter( - im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, - cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); - stats_n_packets = 1; - stats_n_bytes = len0; - stats_sw_if_index = sw_if_index0; - } - if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) - { - vxlan_gpe_encap_trace_t *tr = - vlib_add_trace (vm, node, b0, sizeof (*tr)); - tr->tunnel_index = t0 - ngm->tunnels; - } - 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); + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + u32 next0 = VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP; + u32 sw_if_index0, len0; + vnet_hw_interface_t * hi0; + vxlan_gpe_tunnel_t * t0; + u8 is_ip4_0; + + 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); + + /* 1-wide cache? */ + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX]; + hi0 = vnet_get_sup_hw_interface (vnm, vnet_buffer(b0)->sw_if_index[VLIB_TX]); + + t0 = pool_elt_at_index(ngm->tunnels, hi0->dev_instance); + + is_ip4_0 = (t0->flags & VXLAN_GPE_TUNNEL_IS_IPV4); + + vxlan_gpe_encap_one_inline (ngm, b0, t0, &next0, is_ip4_0); + + /* Reset to look up tunnel partner in the configured FIB */ + vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index; + vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0; + pkts_encapsulated++; + + len0 = vlib_buffer_length_in_chain (vm, b0); + stats_n_packets += 1; + stats_n_bytes += len0; + + /* Batch stats increment on the same vxlan tunnel so counter is not + * incremented per packet. Note stats are still incremented for deleted + * and admin-down tunnel where packets are dropped. It is not worthwhile + * to check for this rare case and affect normal path performance. */ + if (PREDICT_FALSE(sw_if_index0 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len0; + if (stats_n_packets) + vlib_increment_combined_counter ( + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len0; + stats_sw_if_index = sw_if_index0; + } + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gpe_encap_trace_t *tr = vlib_add_trace (vm, node, b0, + sizeof(*tr)); + tr->tunnel_index = t0 - ngm->tunnels; + } + 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); + } vlib_node_increment_counter (vm, node->node_index, VXLAN_GPE_ENCAP_ERROR_ENCAPSULATED, pkts_encapsulated); /* Increment any remaining batch stats */ - if (stats_n_packets) { - vlib_increment_combined_counter( - im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, cpu_index, - stats_sw_if_index, stats_n_packets, stats_n_bytes); - node->runtime_data[0] = stats_sw_if_index; - } + if (stats_n_packets) + { + vlib_increment_combined_counter ( + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, cpu_index, + stats_sw_if_index, stats_n_packets, stats_n_bytes); + node->runtime_data[0] = stats_sw_if_index; + } return from_frame->n_vectors; } @@ -418,8 +340,9 @@ VLIB_REGISTER_NODE (vxlan_gpe_encap_node) = { .n_next_nodes = VXLAN_GPE_ENCAP_N_NEXT, .next_nodes = { - [VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP] = "ip4-lookup", - [VXLAN_GPE_ENCAP_NEXT_DROP] = "error-drop", + [VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP] = "ip4-lookup", + [VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP] = "ip6-lookup", + [VXLAN_GPE_ENCAP_NEXT_DROP] = "error-drop", }, }; diff --git a/vnet/vnet/vxlan-gpe/vxlan_gpe.c b/vnet/vnet/vxlan-gpe/vxlan_gpe.c index 13eda440ed1..bf1623af372 100644 --- a/vnet/vnet/vxlan-gpe/vxlan_gpe.c +++ b/vnet/vnet/vxlan-gpe/vxlan_gpe.c @@ -13,6 +13,7 @@ * limitations under the License. */ #include <vnet/vxlan-gpe/vxlan_gpe.h> +#include <vnet/ip/format.h> vxlan_gpe_main_t vxlan_gpe_main; @@ -23,8 +24,10 @@ u8 * format_vxlan_gpe_tunnel (u8 * s, va_list * args) s = format (s, "[%d] local: %U remote: %U ", t - gm->tunnels, - format_ip4_address, &t->local, - format_ip4_address, &t->remote); + format_ip46_address, &t->local, + format_ip46_address, &t->remote); + + s = format (s, " vxlan VNI %d ", t->vni); switch (t->protocol) { @@ -48,8 +51,6 @@ u8 * format_vxlan_gpe_tunnel (u8 * s, va_list * args) t->encap_fib_index, t->decap_fib_index); - s = format (s, " vxlan VNI %d ", t->vni); - return s; } @@ -121,24 +122,26 @@ VNET_HW_INTERFACE_CLASS (vxlan_gpe_hw_class) = { #define foreach_gpe_copy_field \ -_(local.as_u32) \ -_(remote.as_u32) \ _(vni) \ _(protocol) \ _(encap_fib_index) \ -_(decap_fib_index) - -#define foreach_copy_field \ -_(src.as_u32) \ -_(dst.as_u32) \ -_(vni) \ -_(encap_fib_index) \ _(decap_fib_index) \ _(decap_next_index) +#define foreach_copy_ipv4 { \ + _(local.ip4.as_u32) \ + _(remote.ip4.as_u32) \ +} +#define foreach_copy_ipv6 { \ + _(local.ip6.as_u64[0]) \ + _(local.ip6.as_u64[1]) \ + _(remote.ip6.as_u64[0]) \ + _(remote.ip6.as_u64[1]) \ +} -static int vxlan_gpe_rewrite (vxlan_gpe_tunnel_t * t) + +static int vxlan4_gpe_rewrite (vxlan_gpe_tunnel_t * t) { u8 *rw = 0; ip4_header_t * ip0; @@ -158,8 +161,8 @@ static int vxlan_gpe_rewrite (vxlan_gpe_tunnel_t * t) ip0->protocol = IP_PROTOCOL_UDP; /* we fix up the ip4 header length and checksum after-the-fact */ - ip0->src_address.as_u32 = t->local.as_u32; - ip0->dst_address.as_u32 = t->remote.as_u32; + ip0->src_address.as_u32 = t->local.ip4.as_u32; + ip0->dst_address.as_u32 = t->remote.ip4.as_u32; ip0->checksum = ip4_header_checksum (ip0); /* UDP header, randomize src port on something, maybe? */ @@ -176,6 +179,44 @@ static int vxlan_gpe_rewrite (vxlan_gpe_tunnel_t * t) return (0); } +static int vxlan6_gpe_rewrite (vxlan_gpe_tunnel_t * t) +{ + u8 *rw = 0; + ip6_header_t * ip0; + ip6_vxlan_gpe_header_t * h0; + int len; + + len = sizeof (*h0); + + vec_validate_aligned (rw, len-1, CLIB_CACHE_LINE_BYTES); + + h0 = (ip6_vxlan_gpe_header_t *) rw; + + /* Fixed portion of the (outer) ip4 header */ + ip0 = &h0->ip6; + ip0->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32(6 << 28); + ip0->hop_limit = 255; + ip0->protocol = IP_PROTOCOL_UDP; + + ip0->src_address.as_u64[0] = t->local.ip6.as_u64[0]; + ip0->src_address.as_u64[1] = t->local.ip6.as_u64[1]; + ip0->dst_address.as_u64[0] = t->remote.ip6.as_u64[0]; + ip0->dst_address.as_u64[1] = t->remote.ip6.as_u64[1]; + + /* UDP header, randomize src port on something, maybe? */ + h0->udp.src_port = clib_host_to_net_u16 (4790); + h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gpe); + + /* VXLAN header. Are we having fun yet? */ + h0->vxlan.flags = VXLAN_GPE_FLAGS_I | VXLAN_GPE_FLAGS_P; + h0->vxlan.ver_res = VXLAN_GPE_VERSION; + h0->vxlan.protocol = VXLAN_GPE_PROTOCOL_IP4; + h0->vxlan.vni_res = clib_host_to_net_u32 (t->vni<<8); + + t->rewrite = rw; + return (0); +} + int vnet_vxlan_gpe_add_del_tunnel (vnet_vxlan_gpe_add_del_tunnel_args_t *a, u32 * sw_if_indexp) { @@ -187,15 +228,29 @@ int vnet_vxlan_gpe_add_del_tunnel u32 hw_if_index = ~0; u32 sw_if_index = ~0; int rv; - vxlan_gpe_tunnel_key_t key, *key_copy; + vxlan4_gpe_tunnel_key_t key4, *key4_copy; + vxlan6_gpe_tunnel_key_t key6, *key6_copy; hash_pair_t *hp; - key.local = a->local.as_u32; - key.remote = a->remote.as_u32; - key.vni = clib_host_to_net_u32 (a->vni << 8); - key.pad = 0; - - p = hash_get_mem (gm->vxlan_gpe_tunnel_by_key, &key); + if (!a->is_ip6) + { + key4.local = a->local.ip4.as_u32; + key4.remote = a->remote.ip4.as_u32; + key4.vni = clib_host_to_net_u32 (a->vni << 8); + key4.pad = 0; + + p = hash_get_mem(gm->vxlan4_gpe_tunnel_by_key, &key4); + } + else + { + key6.local.as_u64[0] = a->local.ip6.as_u64[0]; + key6.local.as_u64[1] = a->local.ip6.as_u64[1]; + key6.remote.as_u64[0] = a->remote.ip6.as_u64[0]; + key6.remote.as_u64[1] = a->remote.ip6.as_u64[1]; + key6.vni = clib_host_to_net_u32 (a->vni << 8); + + p = hash_get(gm->vxlan6_gpe_tunnel_by_key, &key6); + } if (a->is_add) { @@ -212,21 +267,43 @@ int vnet_vxlan_gpe_add_del_tunnel /* copy from arg structure */ #define _(x) t->x = a->x; foreach_gpe_copy_field; + if (!a->is_ip6) foreach_copy_ipv4 + else foreach_copy_ipv6 #undef _ - rv = vxlan_gpe_rewrite (t); + if (a->is_ip6) { + /* copy the key */ + t->key6 = key6; + } + + if (!a->is_ip6) t->flags |= VXLAN_GPE_TUNNEL_IS_IPV4; + + if (!a->is_ip6) { + rv = vxlan4_gpe_rewrite (t); + } else { + rv = vxlan6_gpe_rewrite (t); + } if (rv) - { + { pool_put (gm->tunnels, t); return rv; - } - - key_copy = clib_mem_alloc (sizeof (*key_copy)); - clib_memcpy (key_copy, &key, sizeof (*key_copy)); + } - hash_set_mem (gm->vxlan_gpe_tunnel_by_key, key_copy, - t - gm->tunnels); + if (!a->is_ip6) + { + key4_copy = clib_mem_alloc (sizeof (*key4_copy)); + clib_memcpy (key4_copy, &key4, sizeof (*key4_copy)); + hash_set_mem (gm->vxlan4_gpe_tunnel_by_key, key4_copy, + t - gm->tunnels); + } + else + { + key6_copy = clib_mem_alloc (sizeof (*key6_copy)); + clib_memcpy (key6_copy, &key4, sizeof (*key6_copy)); + hash_set_mem (gm->vxlan4_gpe_tunnel_by_key, key6_copy, + t - gm->tunnels); + } if (vec_len (gm->free_vxlan_gpe_tunnel_hw_if_indices) > 0) { @@ -264,10 +341,20 @@ int vnet_vxlan_gpe_add_del_tunnel vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */); vec_add1 (gm->free_vxlan_gpe_tunnel_hw_if_indices, t->hw_if_index); - hp = hash_get_pair (gm->vxlan_gpe_tunnel_by_key, &key); - key_copy = (void *)(hp->key); - hash_unset_mem (gm->vxlan_gpe_tunnel_by_key, &key); - clib_mem_free (key_copy); + if (!a->is_ip6) + { + hp = hash_get_pair (gm->vxlan4_gpe_tunnel_by_key, &key4); + key4_copy = (void *)(hp->key); + hash_unset_mem (gm->vxlan4_gpe_tunnel_by_key, &key4); + clib_mem_free (key4_copy); + } + else + { + hp = hash_get_pair (gm->vxlan6_gpe_tunnel_by_key, &key6); + key6_copy = (void *)(hp->key); + hash_unset_mem (gm->vxlan4_gpe_tunnel_by_key, &key6); + clib_mem_free (key6_copy); + } vec_free (t->rewrite); pool_put (gm->tunnels, t); @@ -279,7 +366,7 @@ int vnet_vxlan_gpe_add_del_tunnel return 0; } -static u32 fib_index_from_fib_id (u32 fib_id) +static u32 fib4_index_from_fib_id (u32 fib_id) { ip4_main_t * im = &ip4_main; uword * p; @@ -291,6 +378,18 @@ static u32 fib_index_from_fib_id (u32 fib_id) return p[0]; } +static u32 fib6_index_from_fib_id (u32 fib_id) +{ + ip6_main_t * im = &ip6_main; + uword * p; + + p = hash_get (im->fib_index_by_table_id, fib_id); + if (!p) + return ~0; + + return p[0]; +} + static uword unformat_gpe_decap_next (unformat_input_t * input, va_list * args) { u32 * result = va_arg (*args, u32 *); @@ -318,9 +417,11 @@ vxlan_gpe_add_del_tunnel_command_fn (vlib_main_t * vm, { unformat_input_t _line_input, * line_input = &_line_input; u8 is_add = 1; - ip4_address_t local, remote; + ip46_address_t local, remote; u8 local_set = 0; u8 remote_set = 0; + u8 ipv4_set = 0; + u8 ipv6_set = 0; u32 encap_fib_index = 0; u32 decap_fib_index = 0; u8 protocol = VXLAN_GPE_PROTOCOL_IP4; @@ -340,20 +441,47 @@ vxlan_gpe_add_del_tunnel_command_fn (vlib_main_t * vm, if (unformat (line_input, "del")) is_add = 0; else if (unformat (line_input, "local %U", - unformat_ip4_address, &local)) + unformat_ip4_address, &local.ip4)) + { local_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "remote %U", + unformat_ip4_address, &remote.ip4)) + { + remote_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "local %U", + unformat_ip6_address, &local.ip6)) + { + local_set = 1; + ipv6_set = 1; + } else if (unformat (line_input, "remote %U", - unformat_ip4_address, &remote)) + unformat_ip6_address, &remote.ip6)) + { remote_set = 1; + ipv6_set = 1; + } else if (unformat (line_input, "encap-vrf-id %d", &tmp)) { - encap_fib_index = fib_index_from_fib_id (tmp); + if (ipv6_set) + encap_fib_index = fib6_index_from_fib_id (tmp); + else + encap_fib_index = fib4_index_from_fib_id (tmp); + if (encap_fib_index == ~0) return clib_error_return (0, "nonexistent encap fib id %d", tmp); } else if (unformat (line_input, "decap-vrf-id %d", &tmp)) { - decap_fib_index = fib_index_from_fib_id (tmp); + + if (ipv6_set) + decap_fib_index = fib6_index_from_fib_id (tmp); + else + decap_fib_index = fib4_index_from_fib_id (tmp); + if (decap_fib_index == ~0) return clib_error_return (0, "nonexistent decap fib id %d", tmp); } @@ -383,15 +511,25 @@ vxlan_gpe_add_del_tunnel_command_fn (vlib_main_t * vm, if (remote_set == 0) return clib_error_return (0, "tunnel remote address not specified"); + if (ipv4_set && ipv6_set) + return clib_error_return (0, "both IPv4 and IPv6 addresses specified"); + + if ((ipv4_set && memcmp(&local.ip4, &remote.ip4, sizeof(local.ip4)) == 0) || + (ipv6_set && memcmp(&local.ip6, &remote.ip6, sizeof(local.ip6)) == 0)) + return clib_error_return (0, "src and dst addresses are identical"); + if (vni_set == 0) return clib_error_return (0, "vni not specified"); memset (a, 0, sizeof (*a)); a->is_add = is_add; + a->is_ip6 = ipv6_set; #define _(x) a->x = x; foreach_gpe_copy_field; + if (ipv4_set) foreach_copy_ipv4 + else foreach_copy_ipv6 #undef _ rv = vnet_vxlan_gpe_add_del_tunnel (a, &sw_if_index); @@ -421,7 +559,7 @@ vxlan_gpe_add_del_tunnel_command_fn (vlib_main_t * vm, VLIB_CLI_COMMAND (create_vxlan_gpe_tunnel_command, static) = { .path = "create vxlan-gpe tunnel", .short_help = - "create vxlan-gpe tunnel local <ip4-addr> remote <ip4-addr>" + "create vxlan-gpe tunnel local <local-addr> remote <remote-addr>" " vni <nn> [next-ip4][next-ip6][next-ethernet][next-nsh]" " [encap-vrf-id <nn>] [decap-vrf-id <nn>]" " [del]\n", @@ -458,12 +596,18 @@ clib_error_t *vxlan_gpe_init (vlib_main_t *vm) gm->vnet_main = vnet_get_main(); gm->vlib_main = vm; - - gm->vxlan_gpe_tunnel_by_key - = hash_create_mem (0, sizeof(vxlan_gpe_tunnel_key_t), sizeof (uword)); - udp_register_dst_port (vm, UDP_DST_PORT_vxlan_gpe, - vxlan_gpe_input_node.index, 1 /* is_ip4 */); + gm->vxlan4_gpe_tunnel_by_key + = hash_create_mem (0, sizeof(vxlan4_gpe_tunnel_key_t), sizeof (uword)); + + gm->vxlan6_gpe_tunnel_by_key + = hash_create_mem (0, sizeof(vxlan6_gpe_tunnel_key_t), sizeof (uword)); + + + udp_register_dst_port (vm, UDP_DST_PORT_vxlan_gpe, + vxlan4_gpe_input_node.index, 1 /* is_ip4 */); + udp_register_dst_port (vm, UDP_DST_PORT_vxlan6_gpe, + vxlan6_gpe_input_node.index, 0 /* is_ip4 */); return 0; } diff --git a/vnet/vnet/vxlan-gpe/vxlan_gpe.h b/vnet/vnet/vxlan-gpe/vxlan_gpe.h index a51e0b31e3e..730abe70253 100644 --- a/vnet/vnet/vxlan-gpe/vxlan_gpe.h +++ b/vnet/vnet/vxlan-gpe/vxlan_gpe.h @@ -23,6 +23,7 @@ #include <vnet/ethernet/ethernet.h> #include <vnet/vxlan-gpe/vxlan_gpe_packet.h> #include <vnet/ip/ip4_packet.h> +#include <vnet/ip/ip6_packet.h> #include <vnet/ip/udp.h> @@ -32,6 +33,12 @@ typedef CLIB_PACKED (struct { vxlan_gpe_header_t vxlan; /* 8 bytes */ }) ip4_vxlan_gpe_header_t; +typedef CLIB_PACKED (struct { + ip6_header_t ip6; /* 40 bytes */ + udp_header_t udp; /* 8 bytes */ + vxlan_gpe_header_t vxlan; /* 8 bytes */ +}) ip6_vxlan_gpe_header_t; + typedef CLIB_PACKED(struct { /* * Key fields: local remote, vni @@ -46,7 +53,17 @@ typedef CLIB_PACKED(struct { }; u64 as_u64[2]; }; -}) vxlan_gpe_tunnel_key_t; +}) vxlan4_gpe_tunnel_key_t; + +typedef CLIB_PACKED(struct { + /* + * Key fields: local remote, vni + * all fields in NET byte order + */ + ip6_address_t local; + ip6_address_t remote; + u32 vni; /* shifted 8 bits */ +}) vxlan6_gpe_tunnel_key_t; typedef struct { /* Rewrite string. $$$$ embed vnet_rewrite header */ @@ -56,8 +73,8 @@ typedef struct { u8 protocol; /* tunnel src and dst addresses */ - ip4_address_t local; - ip4_address_t remote; + ip46_address_t local; + ip46_address_t remote; /* FIB indices */ u32 encap_fib_index; /* tunnel partner lookup here */ @@ -66,12 +83,25 @@ typedef struct { /* vxlan VNI in HOST byte order, shifted left 8 bits */ u32 vni; + /*decap next index*/ + u32 decap_next_index; + /* vnet intfc hw/sw_if_index */ u32 hw_if_index; u32 sw_if_index; + union { /* storage for the hash key */ + vxlan4_gpe_tunnel_key_t key4; + vxlan6_gpe_tunnel_key_t key6; + }; + + /* flags */ + u32 flags; } vxlan_gpe_tunnel_t; +/* Flags for vxlan_gpe_tunnel_t.flags */ +#define VXLAN_GPE_TUNNEL_IS_IPV4 1 + #define foreach_vxlan_gpe_input_next \ _(DROP, "error-drop") \ _(IP4_INPUT, "ip4-input") \ @@ -97,7 +127,8 @@ typedef struct { vxlan_gpe_tunnel_t *tunnels; /* lookup tunnel by key */ - uword * vxlan_gpe_tunnel_by_key; + uword * vxlan4_gpe_tunnel_by_key; + uword * vxlan6_gpe_tunnel_by_key; /* Free vlib hw_if_indices */ u32 * free_vxlan_gpe_tunnel_hw_if_indices; @@ -113,13 +144,15 @@ typedef struct { vxlan_gpe_main_t vxlan_gpe_main; extern vlib_node_registration_t vxlan_gpe_encap_node; -extern vlib_node_registration_t vxlan_gpe_input_node; +extern vlib_node_registration_t vxlan4_gpe_input_node; +extern vlib_node_registration_t vxlan6_gpe_input_node; u8 * format_vxlan_gpe_encap_trace (u8 * s, va_list * args); typedef struct { u8 is_add; - ip4_address_t local, remote; + u8 is_ip6; + ip46_address_t local, remote; u8 protocol; u32 encap_fib_index; u32 decap_fib_index; diff --git a/vpp-api-test/vat/api_format.c b/vpp-api-test/vat/api_format.c index 3fc56d700e6..59da077e053 100644 --- a/vpp-api-test/vat/api_format.c +++ b/vpp-api-test/vat/api_format.c @@ -8062,8 +8062,10 @@ static int api_vxlan_gpe_add_del_tunnel (vat_main_t * vam) unformat_input_t * line_input = vam->input; vl_api_vxlan_gpe_add_del_tunnel_t *mp; f64 timeout; - ip4_address_t local, remote; + ip4_address_t local4, remote4; + ip6_address_t local6, remote6; u8 is_add = 1; + u8 ipv4_set = 0, ipv6_set = 0; u8 local_set = 0; u8 remote_set = 0; u32 encap_vrf_id = 0; @@ -8076,11 +8078,29 @@ static int api_vxlan_gpe_add_del_tunnel (vat_main_t * vam) if (unformat (line_input, "del")) is_add = 0; else if (unformat (line_input, "local %U", - unformat_ip4_address, &local)) + unformat_ip4_address, &local4)) + { + local_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "remote %U", + unformat_ip4_address, &remote4)) + { + remote_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "local %U", + unformat_ip6_address, &local6)) + { local_set = 1; + ipv6_set = 1; + } else if (unformat (line_input, "remote %U", - unformat_ip4_address, &remote)) + unformat_ip6_address, &remote6)) + { remote_set = 1; + ipv6_set = 1; + } else if (unformat (line_input, "encap-vrf-id %d", &encap_vrf_id)) ; else if (unformat (line_input, "decap-vrf-id %d", &decap_vrf_id)) @@ -8109,6 +8129,10 @@ static int api_vxlan_gpe_add_del_tunnel (vat_main_t * vam) errmsg ("tunnel remote address not specified\n"); return -99; } + if (ipv4_set && ipv6_set) { + errmsg ("both IPv4 and IPv6 addresses specified"); + return -99; + } if (vni_set == 0) { errmsg ("vni not specified\n"); @@ -8117,14 +8141,21 @@ static int api_vxlan_gpe_add_del_tunnel (vat_main_t * vam) M(VXLAN_GPE_ADD_DEL_TUNNEL, vxlan_gpe_add_del_tunnel); - mp->local = local.as_u32; - mp->remote = remote.as_u32; + + if (ipv6_set) { + clib_memcpy(&mp->local, &local6, sizeof(local6)); + clib_memcpy(&mp->remote, &remote6, sizeof(remote6)); + } else { + clib_memcpy(&mp->local, &local4, sizeof(local4)); + clib_memcpy(&mp->remote, &remote4, sizeof(remote4)); + } + mp->encap_vrf_id = ntohl(encap_vrf_id); mp->decap_vrf_id = ntohl(decap_vrf_id); mp->protocol = ntohl(protocol); mp->vni = ntohl(vni); mp->is_add = is_add; - + mp->is_ipv6 = ipv6_set; S; W; /* NOTREACHED */ diff --git a/vpp/api/api.c b/vpp/api/api.c index 3bf2ed47088..8b801e5a5c0 100644 --- a/vpp/api/api.c +++ b/vpp/api/api.c @@ -4568,12 +4568,24 @@ vl_api_vxlan_gpe_add_del_tunnel_t_handler decap_fib_index = ntohl(mp->decap_vrf_id); } + /* Check src & dst are different */ + if ((a->is_ip6 && memcmp(mp->local, mp->remote, 16) == 0) || + (!a->is_ip6 && memcmp(mp->local, mp->remote, 4) == 0)) { + rv = VNET_API_ERROR_SAME_SRC_DST; + goto out; + } memset (a, 0, sizeof (*a)); a->is_add = mp->is_add; + a->is_ip6 = mp->is_ipv6; /* ip addresses sent in network byte order */ - a->local.as_u32 = ntohl(mp->local); - a->remote.as_u32 = ntohl(mp->remote); + if (a->is_ip6) { + clib_memcpy(&(a->local.ip6), mp->local, 16); + clib_memcpy(&(a->remote.ip6), mp->remote, 16); + } else { + clib_memcpy(&(a->local.ip4), mp->local, 4); + clib_memcpy(&(a->remote.ip4), mp->remote, 4); + } a->encap_fib_index = encap_fib_index; a->decap_fib_index = decap_fib_index; a->protocol = protocol; diff --git a/vpp/api/custom_dump.c b/vpp/api/custom_dump.c index 47c9f662517..ba4cf538d2f 100644 --- a/vpp/api/custom_dump.c +++ b/vpp/api/custom_dump.c @@ -1580,8 +1580,9 @@ static void *vl_api_vxlan_gpe_add_del_tunnel_t_print s = format (0, "SCRIPT: vxlan_gpe_add_del_tunnel "); - s = format (s, "local %U remote %U ", format_ip4_address, &mp->local, - format_ip4_address, &mp->remote); + s = format (s, "local %U ", format_ip46_address, &mp->local, mp->is_ipv6); + + s = format (s, "remote %U ", format_ip46_address, &mp->remote, mp->is_ipv6); s = format (s, "protocol %d ", ntohl(mp->protocol)); diff --git a/vpp/api/vpe.api b/vpp/api/vpe.api index 2a827d12188..9c90c338e73 100644 --- a/vpp/api/vpe.api +++ b/vpp/api/vpe.api @@ -2113,8 +2113,9 @@ manual_java define l2_fib_table_dump { define vxlan_gpe_add_del_tunnel { u32 client_index; u32 context; - u32 local; - u32 remote; + u8 is_ipv6; + u8 local[16]; + u8 remote[16]; u32 encap_vrf_id; u32 decap_vrf_id; u8 protocol; |