diff options
author | Hongjun Ni <hongjun.ni@intel.com> | 2017-05-27 20:23:09 +0800 |
---|---|---|
committer | Neale Ranns <nranns@cisco.com> | 2017-06-06 07:26:00 +0000 |
commit | 8a0a0ae60b8dd9da7cf53c895e85dc6daf67143d (patch) | |
tree | 9323fb2d785635c88ce7d51b99284ae5aa7effe3 /src/vnet/vxlan-gpe | |
parent | e68de8c333609fa45ad29a97a460f656c272a3dc (diff) |
Rework vxlan-gpe to support FIB 2.0 and bypass mode
Change-Id: I0324f945bdb4dd3b19151be6f3ce24a47a000104
Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
Diffstat (limited to 'src/vnet/vxlan-gpe')
-rw-r--r-- | src/vnet/vxlan-gpe/decap.c | 479 | ||||
-rw-r--r-- | src/vnet/vxlan-gpe/vxlan_gpe.api | 19 | ||||
-rw-r--r-- | src/vnet/vxlan-gpe/vxlan_gpe.c | 688 | ||||
-rw-r--r-- | src/vnet/vxlan-gpe/vxlan_gpe.h | 38 | ||||
-rw-r--r-- | src/vnet/vxlan-gpe/vxlan_gpe_api.c | 20 |
5 files changed, 1161 insertions, 83 deletions
diff --git a/src/vnet/vxlan-gpe/decap.c b/src/vnet/vxlan-gpe/decap.c index d4fe423126e..075b0f51c99 100644 --- a/src/vnet/vxlan-gpe/decap.c +++ b/src/vnet/vxlan-gpe/decap.c @@ -108,8 +108,8 @@ vxlan_gpe_input (vlib_main_t * vm, u8 is_ip4) { u32 n_left_from, next_index, *from, *to_next; - vxlan_gpe_main_t * ngm = &vxlan_gpe_main; - vnet_main_t * vnm = ngm->vnet_main; + vxlan_gpe_main_t * nngm = &vxlan_gpe_main; + vnet_main_t * vnm = nngm->vnet_main; vnet_interface_main_t * im = &vnm->interface_main; u32 last_tunnel_index = ~0; vxlan4_gpe_tunnel_key_t last_key4; @@ -213,11 +213,11 @@ vxlan_gpe_input (vlib_main_t * vm, { next0 = (iuvn4_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX)? - ngm->decap_next_node_list[iuvn4_0->vxlan.protocol]: \ + nngm->decap_next_node_list[iuvn4_0->vxlan.protocol]: \ VXLAN_GPE_INPUT_NEXT_DROP; next1 = (iuvn4_1->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX)? - ngm->decap_next_node_list[iuvn4_1->vxlan.protocol]: \ + nngm->decap_next_node_list[iuvn4_1->vxlan.protocol]: \ VXLAN_GPE_INPUT_NEXT_DROP; key4_0.local = iuvn4_0->ip4.dst_address.as_u32; @@ -260,7 +260,7 @@ vxlan_gpe_input (vlib_main_t * vm, 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); + p0 = hash_get_mem(nngm->vxlan4_gpe_tunnel_by_key, &key4_0); if (p0 == 0) { @@ -279,11 +279,11 @@ vxlan_gpe_input (vlib_main_t * vm, { next0 = (iuvn6_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX)? - ngm->decap_next_node_list[iuvn6_0->vxlan.protocol]: \ + nngm->decap_next_node_list[iuvn6_0->vxlan.protocol]: \ VXLAN_GPE_INPUT_NEXT_DROP; next1 = (iuvn6_1->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX)? - ngm->decap_next_node_list[iuvn6_1->vxlan.protocol]: \ + nngm->decap_next_node_list[iuvn6_1->vxlan.protocol]: \ VXLAN_GPE_INPUT_NEXT_DROP; key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0]; @@ -302,7 +302,7 @@ vxlan_gpe_input (vlib_main_t * vm, /* 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); + p0 = hash_get_mem(nngm->vxlan6_gpe_tunnel_by_key, &key6_0); if (p0 == 0) { @@ -317,7 +317,7 @@ vxlan_gpe_input (vlib_main_t * vm, tunnel_index0 = last_tunnel_index; } - t0 = pool_elt_at_index(ngm->tunnels, tunnel_index0); + t0 = pool_elt_at_index(nngm->tunnels, tunnel_index0); sw_if_index0 = t0->sw_if_index; @@ -366,7 +366,7 @@ vxlan_gpe_input (vlib_main_t * vm, (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); + p1 = hash_get_mem(nngm->vxlan4_gpe_tunnel_by_key, &key4_1); if (p1 == 0) { @@ -386,7 +386,7 @@ vxlan_gpe_input (vlib_main_t * vm, /* 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); + p1 = hash_get_mem(nngm->vxlan6_gpe_tunnel_by_key, &key6_1); if (p1 == 0) { @@ -401,7 +401,7 @@ vxlan_gpe_input (vlib_main_t * vm, tunnel_index1 = last_tunnel_index; } - t1 = pool_elt_at_index(ngm->tunnels, tunnel_index1); + t1 = pool_elt_at_index(nngm->tunnels, tunnel_index1); sw_if_index1 = t1->sw_if_index; len1 = vlib_buffer_length_in_chain (vm, b1); @@ -502,7 +502,7 @@ vxlan_gpe_input (vlib_main_t * vm, { next0 = (iuvn4_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX)? - ngm->decap_next_node_list[iuvn4_0->vxlan.protocol]: \ + nngm->decap_next_node_list[iuvn4_0->vxlan.protocol]: \ VXLAN_GPE_INPUT_NEXT_DROP; key4_0.local = iuvn4_0->ip4.dst_address.as_u32; @@ -515,7 +515,7 @@ vxlan_gpe_input (vlib_main_t * vm, (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); + p0 = hash_get_mem(nngm->vxlan4_gpe_tunnel_by_key, &key4_0); if (p0 == 0) { @@ -534,7 +534,7 @@ vxlan_gpe_input (vlib_main_t * vm, { next0 = (iuvn6_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX)? - ngm->decap_next_node_list[iuvn6_0->vxlan.protocol]: \ + nngm->decap_next_node_list[iuvn6_0->vxlan.protocol]: \ VXLAN_GPE_INPUT_NEXT_DROP; key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0]; @@ -546,7 +546,7 @@ vxlan_gpe_input (vlib_main_t * vm, /* 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); + p0 = hash_get_mem(nngm->vxlan6_gpe_tunnel_by_key, &key6_0); if (p0 == 0) { @@ -561,7 +561,7 @@ vxlan_gpe_input (vlib_main_t * vm, tunnel_index0 = last_tunnel_index; } - t0 = pool_elt_at_index(ngm->tunnels, tunnel_index0); + t0 = pool_elt_at_index(nngm->tunnels, tunnel_index0); sw_if_index0 = t0->sw_if_index; @@ -731,3 +731,448 @@ VLIB_REGISTER_NODE (vxlan6_gpe_input_node) = { }; VLIB_NODE_FUNCTION_MULTIARCH (vxlan6_gpe_input_node, vxlan6_gpe_input); +typedef enum { + IP_VXLAN_BYPASS_NEXT_DROP, + IP_VXLAN_BYPASS_NEXT_VXLAN, + IP_VXLAN_BYPASS_N_NEXT, +} ip_vxan_bypass_next_t; + +always_inline uword +ip_vxlan_gpe_bypass_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + u32 is_ip4) +{ + vxlan_gpe_main_t * ngm = &vxlan_gpe_main; + u32 * from, * to_next, n_left_from, n_left_to_next, next_index; + vlib_node_runtime_t * error_node = vlib_node_get_runtime (vm, ip4_input_node.index); + ip4_address_t addr4; /* last IPv4 address matching a local VTEP address */ + ip6_address_t addr6; /* last IPv6 address matching a local VTEP address */ + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + if (node->flags & VLIB_NODE_FLAG_TRACE) + ip4_forward_next_trace (vm, node, frame, VLIB_TX); + + if (is_ip4) addr4.data_u32 = ~0; + else ip6_address_set_zero (&addr6); + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + vlib_buffer_t * b0, * b1; + ip4_header_t * ip40, * ip41; + ip6_header_t * ip60, * ip61; + udp_header_t * udp0, * udp1; + u32 bi0, ip_len0, udp_len0, flags0, next0; + u32 bi1, ip_len1, udp_len1, flags1, next1; + i32 len_diff0, len_diff1; + u8 error0, good_udp0, proto0; + u8 error1, good_udp1, proto1; + + /* 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 = to_next[0] = from[0]; + bi1 = to_next[1] = from[1]; + from += 2; + n_left_from -= 2; + to_next += 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + if (is_ip4) + { + ip40 = vlib_buffer_get_current (b0); + ip41 = vlib_buffer_get_current (b1); + } + else + { + ip60 = vlib_buffer_get_current (b0); + ip61 = vlib_buffer_get_current (b1); + } + + /* Setup packet for next IP feature */ + vnet_feature_next(vnet_buffer(b0)->sw_if_index[VLIB_RX], &next0, b0); + vnet_feature_next(vnet_buffer(b1)->sw_if_index[VLIB_RX], &next1, b1); + + if (is_ip4) + { + proto0 = ip40->protocol; + proto1 = ip41->protocol; + } + else + { + proto0 = ip60->protocol; + proto1 = ip61->protocol; + } + + /* Process packet 0 */ + if (proto0 != IP_PROTOCOL_UDP) + goto exit0; /* not UDP packet */ + + if (is_ip4) + udp0 = ip4_next_header (ip40); + else + udp0 = ip6_next_header (ip60); + + if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE)) + goto exit0; /* not VXLAN packet */ + + /* Validate DIP against VTEPs*/ + if (is_ip4) + { + if (addr4.as_u32 != ip40->dst_address.as_u32) + { + if (!hash_get (ngm->vtep4, ip40->dst_address.as_u32)) + goto exit0; /* no local VTEP for VXLAN packet */ + addr4 = ip40->dst_address; + } + } + else + { + if (!ip6_address_is_equal (&addr6, &ip60->dst_address)) + { + if (!hash_get_mem (ngm->vtep6, &ip60->dst_address)) + goto exit0; /* no local VTEP for VXLAN packet */ + addr6 = ip60->dst_address; + } + } + + flags0 = b0->flags; + good_udp0 = (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + + /* Don't verify UDP checksum for packets with explicit zero checksum. */ + good_udp0 |= udp0->checksum == 0; + + /* Verify UDP length */ + if (is_ip4) + ip_len0 = clib_net_to_host_u16 (ip40->length); + else + ip_len0 = clib_net_to_host_u16 (ip60->payload_length); + udp_len0 = clib_net_to_host_u16 (udp0->length); + len_diff0 = ip_len0 - udp_len0; + + /* Verify UDP checksum */ + if (PREDICT_FALSE (!good_udp0)) + { + if ((flags0 & IP_BUFFER_L4_CHECKSUM_COMPUTED) == 0) + { + if (is_ip4) + flags0 = ip4_tcp_udp_validate_checksum (vm, b0); + else + flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0); + good_udp0 = + (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + } + } + + if (is_ip4) + { + error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH; + } + else + { + error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH; + } + + next0 = error0 ? + IP_VXLAN_BYPASS_NEXT_DROP : IP_VXLAN_BYPASS_NEXT_VXLAN; + b0->error = error0 ? error_node->errors[error0] : 0; + + /* vxlan_gpe-input node expect current at VXLAN header */ + if (is_ip4) + vlib_buffer_advance (b0, sizeof(ip4_header_t)+sizeof(udp_header_t)); + else + vlib_buffer_advance (b0, sizeof(ip6_header_t)+sizeof(udp_header_t)); + + exit0: + /* Process packet 1 */ + if (proto1 != IP_PROTOCOL_UDP) + goto exit1; /* not UDP packet */ + + if (is_ip4) + udp1 = ip4_next_header (ip41); + else + udp1 = ip6_next_header (ip61); + + if (udp1->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE)) + goto exit1; /* not VXLAN packet */ + + /* Validate DIP against VTEPs*/ + if (is_ip4) + { + if (addr4.as_u32 != ip41->dst_address.as_u32) + { + if (!hash_get (ngm->vtep4, ip41->dst_address.as_u32)) + goto exit1; /* no local VTEP for VXLAN packet */ + addr4 = ip41->dst_address; + } + } + else + { + if (!ip6_address_is_equal (&addr6, &ip61->dst_address)) + { + if (!hash_get_mem (ngm->vtep6, &ip61->dst_address)) + goto exit1; /* no local VTEP for VXLAN packet */ + addr6 = ip61->dst_address; + } + } + + flags1 = b1->flags; + good_udp1 = (flags1 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + + /* Don't verify UDP checksum for packets with explicit zero checksum. */ + good_udp1 |= udp1->checksum == 0; + + /* Verify UDP length */ + if (is_ip4) + ip_len1 = clib_net_to_host_u16 (ip41->length); + else + ip_len1 = clib_net_to_host_u16 (ip61->payload_length); + udp_len1 = clib_net_to_host_u16 (udp1->length); + len_diff1 = ip_len1 - udp_len1; + + /* Verify UDP checksum */ + if (PREDICT_FALSE (!good_udp1)) + { + if ((flags1 & IP_BUFFER_L4_CHECKSUM_COMPUTED) == 0) + { + if (is_ip4) + flags1 = ip4_tcp_udp_validate_checksum (vm, b1); + else + flags1 = ip6_tcp_udp_icmp_validate_checksum (vm, b1); + good_udp1 = + (flags1 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + } + } + + if (is_ip4) + { + error1 = good_udp1 ? 0 : IP4_ERROR_UDP_CHECKSUM; + error1 = (len_diff1 >= 0) ? error1 : IP4_ERROR_UDP_LENGTH; + } + else + { + error1 = good_udp1 ? 0 : IP6_ERROR_UDP_CHECKSUM; + error1 = (len_diff1 >= 0) ? error1 : IP6_ERROR_UDP_LENGTH; + } + + next1 = error1 ? + IP_VXLAN_BYPASS_NEXT_DROP : IP_VXLAN_BYPASS_NEXT_VXLAN; + b1->error = error1 ? error_node->errors[error1] : 0; + + /* vxlan_gpe-input node expect current at VXLAN header */ + if (is_ip4) + vlib_buffer_advance (b1, sizeof(ip4_header_t)+sizeof(udp_header_t)); + else + vlib_buffer_advance (b1, sizeof(ip6_header_t)+sizeof(udp_header_t)); + + exit1: + 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) + { + vlib_buffer_t * b0; + ip4_header_t * ip40; + ip6_header_t * ip60; + udp_header_t * udp0; + u32 bi0, ip_len0, udp_len0, flags0, next0; + i32 len_diff0; + u8 error0, good_udp0, proto0; + + bi0 = to_next[0] = from[0]; + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + if (is_ip4) + ip40 = vlib_buffer_get_current (b0); + else + ip60 = vlib_buffer_get_current (b0); + + /* Setup packet for next IP feature */ + vnet_feature_next(vnet_buffer(b0)->sw_if_index[VLIB_RX], &next0, b0); + + if (is_ip4) + proto0 = ip40->protocol; + else + proto0 = ip60->protocol; + + if (proto0 != IP_PROTOCOL_UDP) + goto exit; /* not UDP packet */ + + if (is_ip4) + udp0 = ip4_next_header (ip40); + else + udp0 = ip6_next_header (ip60); + + if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE)) + goto exit; /* not VXLAN packet */ + + /* Validate DIP against VTEPs*/ + if (is_ip4) + { + if (addr4.as_u32 != ip40->dst_address.as_u32) + { + if (!hash_get (ngm->vtep4, ip40->dst_address.as_u32)) + goto exit; /* no local VTEP for VXLAN packet */ + addr4 = ip40->dst_address; + } + } + else + { + if (!ip6_address_is_equal (&addr6, &ip60->dst_address)) + { + if (!hash_get_mem (ngm->vtep6, &ip60->dst_address)) + goto exit; /* no local VTEP for VXLAN packet */ + addr6 = ip60->dst_address; + } + } + + flags0 = b0->flags; + good_udp0 = (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + + /* Don't verify UDP checksum for packets with explicit zero checksum. */ + good_udp0 |= udp0->checksum == 0; + + /* Verify UDP length */ + if (is_ip4) + ip_len0 = clib_net_to_host_u16 (ip40->length); + else + ip_len0 = clib_net_to_host_u16 (ip60->payload_length); + udp_len0 = clib_net_to_host_u16 (udp0->length); + len_diff0 = ip_len0 - udp_len0; + + /* Verify UDP checksum */ + if (PREDICT_FALSE (!good_udp0)) + { + if ((flags0 & IP_BUFFER_L4_CHECKSUM_COMPUTED) == 0) + { + if (is_ip4) + flags0 = ip4_tcp_udp_validate_checksum (vm, b0); + else + flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0); + good_udp0 = + (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + } + } + + if (is_ip4) + { + error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH; + } + else + { + error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH; + } + + next0 = error0 ? + IP_VXLAN_BYPASS_NEXT_DROP : IP_VXLAN_BYPASS_NEXT_VXLAN; + b0->error = error0 ? error_node->errors[error0] : 0; + + /* vxlan_gpe-input node expect current at VXLAN header */ + if (is_ip4) + vlib_buffer_advance (b0, sizeof(ip4_header_t)+sizeof(udp_header_t)); + else + vlib_buffer_advance (b0, sizeof(ip6_header_t)+sizeof(udp_header_t)); + + exit: + 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; +} + +static uword +ip4_vxlan_gpe_bypass (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return ip_vxlan_gpe_bypass_inline (vm, node, frame, /* is_ip4 */ 1); +} + +VLIB_REGISTER_NODE (ip4_vxlan_gpe_bypass_node) = { + .function = ip4_vxlan_gpe_bypass, + .name = "ip4-vxlan-gpe-bypass", + .vector_size = sizeof (u32), + + .n_next_nodes = IP_VXLAN_BYPASS_N_NEXT, + .next_nodes = { + [IP_VXLAN_BYPASS_NEXT_DROP] = "error-drop", + [IP_VXLAN_BYPASS_NEXT_VXLAN] = "vxlan4-gpe-input", + }, + + .format_buffer = format_ip4_header, + .format_trace = format_ip4_forward_next_trace, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip4_vxlan_gpe_bypass_node,ip4_vxlan_gpe_bypass) + +/* Dummy init function to get us linked in. */ +clib_error_t * ip4_vxlan_gpe_bypass_init (vlib_main_t * vm) +{ return 0; } + +VLIB_INIT_FUNCTION (ip4_vxlan_gpe_bypass_init); + +static uword +ip6_vxlan_gpe_bypass (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return ip_vxlan_gpe_bypass_inline (vm, node, frame, /* is_ip4 */ 0); +} + +VLIB_REGISTER_NODE (ip6_vxlan_gpe_bypass_node) = { + .function = ip6_vxlan_gpe_bypass, + .name = "ip6-vxlan-gpe-bypass", + .vector_size = sizeof (u32), + + .n_next_nodes = IP_VXLAN_BYPASS_N_NEXT, + .next_nodes = { + [IP_VXLAN_BYPASS_NEXT_DROP] = "error-drop", + [IP_VXLAN_BYPASS_NEXT_VXLAN] = "vxlan6-gpe-input", + }, + + .format_buffer = format_ip6_header, + .format_trace = format_ip6_forward_next_trace, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip6_vxlan_gpe_bypass_node,ip6_vxlan_gpe_bypass) + +/* Dummy init function to get us linked in. */ +clib_error_t * ip6_vxlan_gpe_bypass_init (vlib_main_t * vm) +{ return 0; } + +VLIB_INIT_FUNCTION (ip6_vxlan_gpe_bypass_init); diff --git a/src/vnet/vxlan-gpe/vxlan_gpe.api b/src/vnet/vxlan-gpe/vxlan_gpe.api index 6c6973f8384..41b10316451 100644 --- a/src/vnet/vxlan-gpe/vxlan_gpe.api +++ b/src/vnet/vxlan-gpe/vxlan_gpe.api @@ -20,6 +20,7 @@ define vxlan_gpe_add_del_tunnel u8 is_ipv6; u8 local[16]; u8 remote[16]; + u32 mcast_sw_if_index; u32 encap_vrf_id; u32 decap_vrf_id; u8 protocol; @@ -49,11 +50,29 @@ define vxlan_gpe_tunnel_details u8 remote[16]; u32 vni; u8 protocol; + u32 mcast_sw_if_index; u32 encap_vrf_id; u32 decap_vrf_id; + u8 is_ipv6; }; +/** \brief Interface set vxlan-gpe-bypass request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_if_index - interface used to reach neighbor + @param is_ipv6 - if non-zero, enable ipv6-vxlan-bypass, else ipv4-vxlan-bypass + @param enable - if non-zero enable, else disable +*/ +autoreply define sw_interface_set_vxlan_gpe_bypass +{ + u32 client_index; + u32 context; + u32 sw_if_index; + u8 is_ipv6; + u8 enable; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/vnet/vxlan-gpe/vxlan_gpe.c b/src/vnet/vxlan-gpe/vxlan_gpe.c index 1e674085880..3a92c88fbb6 100644 --- a/src/vnet/vxlan-gpe/vxlan_gpe.c +++ b/src/vnet/vxlan-gpe/vxlan_gpe.c @@ -20,6 +20,27 @@ #include <vnet/vxlan-gpe/vxlan_gpe.h> #include <vnet/fib/fib.h> #include <vnet/ip/format.h> +#include <vnet/fib/fib_entry.h> +#include <vnet/fib/fib_table.h> +#include <vnet/mfib/mfib_table.h> +#include <vnet/adj/adj_mcast.h> +#include <vnet/interface.h> +#include <vlib/vlib.h> + +/** + * @file + * @brief VXLAN-GPE. + * + * VXLAN-GPE provides the features needed to allow L2 bridge domains (BDs) + * to span multiple servers. This is done by building an L2 overlay on + * top of an L3 network underlay using VXLAN-GPE tunnels. + * + * This makes it possible for servers to be co-located in the same data + * center or be separated geographically as long as they are reachable + * through the underlay L3 network. + * + * You can refer to this kind of L2 overlay bridge domain as a VXLAN-GPE segment. + */ vxlan_gpe_main_t vxlan_gpe_main; @@ -35,10 +56,10 @@ vxlan_gpe_main_t vxlan_gpe_main; u8 * format_vxlan_gpe_tunnel (u8 * s, va_list * args) { vxlan_gpe_tunnel_t * t = va_arg (*args, vxlan_gpe_tunnel_t *); - vxlan_gpe_main_t * gm = &vxlan_gpe_main; + vxlan_gpe_main_t * ngm = &vxlan_gpe_main; s = format (s, "[%d] local: %U remote: %U ", - t - gm->tunnels, + t - ngm->tunnels, format_ip46_address, &t->local, IP46_TYPE_ANY, format_ip46_address, &t->remote, IP46_TYPE_ANY); @@ -105,10 +126,9 @@ static uword dummy_interface_tx (vlib_main_t * vm, static clib_error_t * vxlan_gpe_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) { - if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) - vnet_hw_interface_set_flags (vnm, hw_if_index, VNET_HW_INTERFACE_FLAG_LINK_UP); - else - vnet_hw_interface_set_flags (vnm, hw_if_index, 0); + u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? + VNET_HW_INTERFACE_FLAG_LINK_UP : 0; + vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags); return 0; } @@ -142,13 +162,84 @@ VNET_HW_INTERFACE_CLASS (vxlan_gpe_hw_class) = { .name = "VXLAN_GPE", .format_header = format_vxlan_gpe_header_with_length, .build_rewrite = default_build_rewrite, - .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P, }; +static void +vxlan_gpe_tunnel_restack_dpo(vxlan_gpe_tunnel_t * t) +{ + dpo_id_t dpo = DPO_INVALID; + u32 encap_index = vxlan_gpe_encap_node.index; + fib_forward_chain_type_t forw_type = ip46_address_is_ip4(&t->remote) ? + FIB_FORW_CHAIN_TYPE_UNICAST_IP4 : FIB_FORW_CHAIN_TYPE_UNICAST_IP6; + + fib_entry_contribute_forwarding (t->fib_entry_index, forw_type, &dpo); + dpo_stack_from_node (encap_index, &t->next_dpo, &dpo); + dpo_reset(&dpo); +} + +static vxlan_gpe_tunnel_t * +vxlan_gpe_tunnel_from_fib_node (fib_node_t *node) +{ +#if (CLIB_DEBUG > 0) + ASSERT(FIB_NODE_TYPE_VXLAN_GPE_TUNNEL == node->fn_type); +#endif + return ((vxlan_gpe_tunnel_t*) (((char*)node) - + STRUCT_OFFSET_OF(vxlan_gpe_tunnel_t, node))); +} + +/** + * Function definition to backwalk a FIB node - + * Here we will restack the new dpo of VXLAN_GPE DIP to encap node. + */ +static fib_node_back_walk_rc_t +vxlan_gpe_tunnel_back_walk (fib_node_t *node, + fib_node_back_walk_ctx_t *ctx) +{ + vxlan_gpe_tunnel_restack_dpo(vxlan_gpe_tunnel_from_fib_node(node)); + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/** + * Function definition to get a FIB node from its index + */ +static fib_node_t* +vxlan_gpe_tunnel_fib_node_get (fib_node_index_t index) +{ + vxlan_gpe_tunnel_t * t; + vxlan_gpe_main_t * ngm = &vxlan_gpe_main; + + t = pool_elt_at_index(ngm->tunnels, index); + + return (&t->node); +} + +/** + * Function definition to inform the FIB node that its last lock has gone. + */ +static void +vxlan_gpe_tunnel_last_lock_gone (fib_node_t *node) +{ + /* + * The VXLAN_GPE tunnel is a root of the graph. As such + * it never has children and thus is never locked. + */ + ASSERT(0); +} + +/* + * Virtual function table registered by VXLAN_GPE tunnels + * for participation in the FIB object graph. + */ +const static fib_node_vft_t vxlan_gpe_vft = { + .fnv_get = vxlan_gpe_tunnel_fib_node_get, + .fnv_last_lock = vxlan_gpe_tunnel_last_lock_gone, + .fnv_back_walk = vxlan_gpe_tunnel_back_walk, +}; #define foreach_gpe_copy_field \ _(vni) \ -_(protocol) \ +_(protocol) \ +_(mcast_sw_if_index) \ _(encap_fib_index) \ _(decap_fib_index) @@ -173,7 +264,7 @@ _(decap_fib_index) * @return rc * */ -int vxlan4_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, +int vxlan4_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, u8 protocol_override, uword encap_next_node) { u8 *rw = 0; @@ -201,7 +292,7 @@ int vxlan4_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, /* 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); + 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; @@ -230,7 +321,7 @@ int vxlan4_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, * @return rc * */ -int vxlan6_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, +int vxlan6_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, u8 protocol_override, uword encap_next_node) { u8 *rw = 0; @@ -258,7 +349,7 @@ int vxlan6_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, /* 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); + 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; @@ -279,6 +370,100 @@ int vxlan6_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, return (0); } +static void +hash_set_key_copy (uword ** h, void * key, uword v) { + size_t ksz = hash_header(*h)->user; + void * copy = clib_mem_alloc (ksz); + clib_memcpy (copy, key, ksz); + hash_set_mem (*h, copy, v); +} + +static void +hash_unset_key_free (uword ** h, void * key) { + hash_pair_t * hp = hash_get_pair_mem (*h, key); + ASSERT (hp); + key = uword_to_pointer (hp->key, void *); + hash_unset_mem (*h, key); + clib_mem_free (key); +} + +static uword +vtep_addr_ref(ip46_address_t *ip) +{ + uword *vtep = ip46_address_is_ip4(ip) ? + hash_get (vxlan_gpe_main.vtep4, ip->ip4.as_u32) : + hash_get_mem (vxlan_gpe_main.vtep6, &ip->ip6); + if (vtep) + return ++(*vtep); + ip46_address_is_ip4(ip) ? + hash_set (vxlan_gpe_main.vtep4, ip->ip4.as_u32, 1) : + hash_set_key_copy (&vxlan_gpe_main.vtep6, &ip->ip6, 1); + return 1; +} + +static uword +vtep_addr_unref(ip46_address_t *ip) +{ + uword *vtep = ip46_address_is_ip4(ip) ? + hash_get (vxlan_gpe_main.vtep4, ip->ip4.as_u32) : + hash_get_mem (vxlan_gpe_main.vtep6, &ip->ip6); + ASSERT(vtep); + if (--(*vtep) != 0) + return *vtep; + ip46_address_is_ip4(ip) ? + hash_unset (vxlan_gpe_main.vtep4, ip->ip4.as_u32) : + hash_unset_key_free (&vxlan_gpe_main.vtep6, &ip->ip6); + return 0; +} + +typedef CLIB_PACKED(union { + struct { + fib_node_index_t mfib_entry_index; + adj_index_t mcast_adj_index; + }; + u64 as_u64; +}) mcast_shared_t; + +static inline mcast_shared_t +mcast_shared_get(ip46_address_t * ip) +{ + ASSERT(ip46_address_is_multicast(ip)); + uword * p = hash_get_mem (vxlan_gpe_main.mcast_shared, ip); + ASSERT(p); + return (mcast_shared_t) { .as_u64 = *p }; +} + +static inline void +mcast_shared_add(ip46_address_t *remote, + fib_node_index_t mfei, + adj_index_t ai) +{ + mcast_shared_t new_ep = { + .mcast_adj_index = ai, + .mfib_entry_index = mfei, + }; + + hash_set_key_copy (&vxlan_gpe_main.mcast_shared, remote, new_ep.as_u64); +} + +static inline void +mcast_shared_remove(ip46_address_t *remote) +{ + mcast_shared_t ep = mcast_shared_get(remote); + + adj_unlock(ep.mcast_adj_index); + mfib_table_entry_delete_index(ep.mfib_entry_index, + MFIB_SOURCE_VXLAN_GPE); + + hash_unset_key_free (&vxlan_gpe_main.mcast_shared, remote); +} + +static inline fib_protocol_t +fib_ip_proto(bool is_ip6) +{ + return (is_ip6) ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4; +} + /** * @brief Add or Del a VXLAN GPE tunnel * @@ -291,9 +476,9 @@ int vxlan6_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, int vnet_vxlan_gpe_add_del_tunnel (vnet_vxlan_gpe_add_del_tunnel_args_t *a, u32 * sw_if_indexp) { - vxlan_gpe_main_t * gm = &vxlan_gpe_main; + vxlan_gpe_main_t * ngm = &vxlan_gpe_main; vxlan_gpe_tunnel_t *t = 0; - vnet_main_t * vnm = gm->vnet_main; + vnet_main_t * vnm = ngm->vnet_main; vnet_hw_interface_t * hi; uword * p; u32 hw_if_index = ~0; @@ -301,16 +486,16 @@ int vnet_vxlan_gpe_add_del_tunnel int rv; vxlan4_gpe_tunnel_key_t key4, *key4_copy; vxlan6_gpe_tunnel_key_t key6, *key6_copy; - hash_pair_t *hp; + u32 is_ip6 = a->is_ip6; - if (!a->is_ip6) + if (!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); + p = hash_get_mem(ngm->vxlan4_gpe_tunnel_by_key, &key4); } else { @@ -320,16 +505,18 @@ int vnet_vxlan_gpe_add_del_tunnel key6.remote.as_u64[1] = a->remote.ip6.as_u64[1]; key6.vni = clib_host_to_net_u32 (a->vni << 8); - p = hash_get_mem(gm->vxlan6_gpe_tunnel_by_key, &key6); + p = hash_get_mem(ngm->vxlan6_gpe_tunnel_by_key, &key6); } if (a->is_add) { + l2input_main_t * l2im = &l2input_main; + /* adding a tunnel: tunnel must not already exist */ if (p) - return VNET_API_ERROR_INVALID_VALUE; + return VNET_API_ERROR_TUNNEL_EXIST; - pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES); + pool_get_aligned (ngm->tunnels, t, CLIB_CACHE_LINE_BYTES); memset (t, 0, sizeof (*t)); /* copy from arg structure */ @@ -349,51 +536,167 @@ int vnet_vxlan_gpe_add_del_tunnel if (rv) { - pool_put (gm->tunnels, t); + pool_put (ngm->tunnels, t); return rv; } - if (!a->is_ip6) + if (!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); + hash_set_mem (ngm->vxlan4_gpe_tunnel_by_key, key4_copy, + t - ngm->tunnels); } else { key6_copy = clib_mem_alloc (sizeof (*key6_copy)); clib_memcpy (key6_copy, &key6, sizeof (*key6_copy)); - hash_set_mem (gm->vxlan6_gpe_tunnel_by_key, key6_copy, - t - gm->tunnels); + hash_set_mem (ngm->vxlan6_gpe_tunnel_by_key, key6_copy, + t - ngm->tunnels); } - if (vec_len (gm->free_vxlan_gpe_tunnel_hw_if_indices) > 0) + if (vec_len (ngm->free_vxlan_gpe_tunnel_hw_if_indices) > 0) { - hw_if_index = gm->free_vxlan_gpe_tunnel_hw_if_indices - [vec_len (gm->free_vxlan_gpe_tunnel_hw_if_indices)-1]; - _vec_len (gm->free_vxlan_gpe_tunnel_hw_if_indices) -= 1; + vnet_interface_main_t * im = &vnm->interface_main; + hw_if_index = ngm->free_vxlan_gpe_tunnel_hw_if_indices + [vec_len (ngm->free_vxlan_gpe_tunnel_hw_if_indices)-1]; + _vec_len (ngm->free_vxlan_gpe_tunnel_hw_if_indices) -= 1; hi = vnet_get_hw_interface (vnm, hw_if_index); - hi->dev_instance = t - gm->tunnels; + hi->dev_instance = t - ngm->tunnels; hi->hw_instance = hi->dev_instance; + /* clear old stats of freed tunnel before reuse */ + sw_if_index = hi->sw_if_index; + vnet_interface_counter_lock(im); + vlib_zero_combined_counter + (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX], sw_if_index); + vlib_zero_combined_counter + (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_RX], sw_if_index); + vlib_zero_simple_counter + (&im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP], sw_if_index); + vnet_interface_counter_unlock(im); } else { hw_if_index = vnet_register_interface - (vnm, vxlan_gpe_device_class.index, t - gm->tunnels, - vxlan_gpe_hw_class.index, t - gm->tunnels); + (vnm, vxlan_gpe_device_class.index, t - ngm->tunnels, + vxlan_gpe_hw_class.index, t - ngm->tunnels); hi = vnet_get_hw_interface (vnm, hw_if_index); hi->output_node_index = vxlan_gpe_encap_node.index; } t->hw_if_index = hw_if_index; t->sw_if_index = sw_if_index = hi->sw_if_index; - vec_validate_init_empty (gm->tunnel_index_by_sw_if_index, sw_if_index, ~0); - gm->tunnel_index_by_sw_if_index[sw_if_index] = t - gm->tunnels; + vec_validate_init_empty (ngm->tunnel_index_by_sw_if_index, sw_if_index, ~0); + ngm->tunnel_index_by_sw_if_index[sw_if_index] = t - ngm->tunnels; + + /* setup l2 input config with l2 feature and bd 0 to drop packet */ + vec_validate (l2im->configs, sw_if_index); + l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP; + l2im->configs[sw_if_index].bd_index = 0; + vnet_sw_interface_t * si = vnet_get_sw_interface (vnm, sw_if_index); + si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN; vnet_sw_interface_set_flags (vnm, hi->sw_if_index, VNET_SW_INTERFACE_FLAG_ADMIN_UP); + fib_node_init(&t->node, FIB_NODE_TYPE_VXLAN_GPE_TUNNEL); + fib_prefix_t tun_remote_pfx; + u32 encap_index = vxlan_gpe_encap_node.index; + vnet_flood_class_t flood_class = VNET_FLOOD_CLASS_TUNNEL_NORMAL; + + fib_prefix_from_ip46_addr(&t->remote, &tun_remote_pfx); + if (!ip46_address_is_multicast(&t->remote)) + { + /* Unicast tunnel - + * source the FIB entry for the tunnel's destination + * and become a child thereof. The tunnel will then get poked + * when the forwarding for the entry updates, and the tunnel can + * re-stack accordingly + */ + vtep_addr_ref(&t->local); + t->fib_entry_index = fib_table_entry_special_add + (t->encap_fib_index, &tun_remote_pfx, FIB_SOURCE_RR, + FIB_ENTRY_FLAG_NONE); + t->sibling_index = fib_entry_child_add + (t->fib_entry_index, FIB_NODE_TYPE_VXLAN_GPE_TUNNEL, t - ngm->tunnels); + vxlan_gpe_tunnel_restack_dpo(t); + } + else + { + /* Multicast tunnel - + * as the same mcast group can be used for mutiple mcast tunnels + * with different VNIs, create the output fib adjecency only if + * it does not already exist + */ + fib_protocol_t fp = fib_ip_proto(is_ip6); + + if (vtep_addr_ref(&t->remote) == 1) + { + fib_node_index_t mfei; + adj_index_t ai; + fib_route_path_t path = { + .frp_proto = fp, + .frp_addr = zero_addr, + .frp_sw_if_index = 0xffffffff, + .frp_fib_index = ~0, + .frp_weight = 0, + .frp_flags = FIB_ROUTE_PATH_LOCAL, + }; + const mfib_prefix_t mpfx = { + .fp_proto = fp, + .fp_len = (is_ip6 ? 128 : 32), + .fp_grp_addr = tun_remote_pfx.fp_addr, + }; + + /* + * Setup the (*,G) to receive traffic on the mcast group + * - the forwarding interface is for-us + * - the accepting interface is that from the API + */ + mfib_table_entry_path_update(t->encap_fib_index, + &mpfx, + MFIB_SOURCE_VXLAN_GPE, + &path, + MFIB_ITF_FLAG_FORWARD); + + path.frp_sw_if_index = a->mcast_sw_if_index; + path.frp_flags = FIB_ROUTE_PATH_FLAG_NONE; + mfei = mfib_table_entry_path_update(t->encap_fib_index, + &mpfx, + MFIB_SOURCE_VXLAN_GPE, + &path, + MFIB_ITF_FLAG_ACCEPT); + + /* + * Create the mcast adjacency to send traffic to the group + */ + ai = adj_mcast_add_or_lock(fp, + fib_proto_to_link(fp), + a->mcast_sw_if_index); + + /* + * create a new end-point + */ + mcast_shared_add(&t->remote, mfei, ai); + } + + dpo_id_t dpo = DPO_INVALID; + mcast_shared_t ep = mcast_shared_get(&t->remote); + + /* Stack shared mcast remote mac addr rewrite on encap */ + dpo_set (&dpo, DPO_ADJACENCY_MCAST, + fib_proto_to_dpo(fp), + ep.mcast_adj_index); + + dpo_stack_from_node (encap_index, &t->next_dpo, &dpo); + dpo_reset (&dpo); + flood_class = VNET_FLOOD_CLASS_TUNNEL_MASTER; + } + + /* Set vxlan tunnel output node */ + hi->output_node_index = encap_index; + + vnet_get_sw_interface (vnet_get_main(), sw_if_index)->flood_class = flood_class; } else { @@ -401,30 +704,36 @@ int vnet_vxlan_gpe_add_del_tunnel if (!p) return VNET_API_ERROR_NO_SUCH_ENTRY; - t = pool_elt_at_index (gm->tunnels, p[0]); + t = pool_elt_at_index (ngm->tunnels, p[0]); + sw_if_index = t->sw_if_index; 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); + vnet_sw_interface_t * si = vnet_get_sw_interface (vnm, t->sw_if_index); + si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN; + set_int_l2_mode(ngm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0, 0, 0, 0); + vec_add1 (ngm->free_vxlan_gpe_tunnel_hw_if_indices, t->hw_if_index); - gm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0; + ngm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0; - 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); - } + if (!is_ip6) + hash_unset (ngm->vxlan4_gpe_tunnel_by_key, key4.as_u64); 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); - } + hash_unset_key_free (&ngm->vxlan6_gpe_tunnel_by_key, &key6); + + if (!ip46_address_is_multicast(&t->remote)) + { + vtep_addr_unref(&t->local); + fib_entry_child_remove(t->fib_entry_index, t->sibling_index); + fib_table_entry_delete_index(t->fib_entry_index, FIB_SOURCE_RR); + } + else if (vtep_addr_unref(&t->remote) == 0) + { + mcast_shared_remove(&t->remote); + } + fib_node_deinit(&t->node); vec_free (t->rewrite); - pool_put (gm->tunnels, t); + pool_put (ngm->tunnels, t); } if (sw_if_indexp) @@ -443,8 +752,10 @@ vxlan_gpe_add_del_tunnel_command_fn (vlib_main_t * vm, ip46_address_t local, remote; u8 local_set = 0; u8 remote_set = 0; + u8 grp_set = 0; u8 ipv4_set = 0; u8 ipv6_set = 0; + u32 mcast_sw_if_index = ~0; u32 encap_fib_index = 0; u32 decap_fib_index = 0; u8 protocol = VXLAN_GPE_PROTOCOL_IP4; @@ -487,6 +798,22 @@ vxlan_gpe_add_del_tunnel_command_fn (vlib_main_t * vm, remote_set = 1; ipv6_set = 1; } + else if (unformat (line_input, "group %U %U", + unformat_ip4_address, &remote.ip4, + unformat_vnet_sw_interface, + vnet_get_main(), &mcast_sw_if_index)) + { + grp_set = remote_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "group %U %U", + unformat_ip6_address, &remote.ip6, + unformat_vnet_sw_interface, + vnet_get_main(), &mcast_sw_if_index)) + { + grp_set = remote_set = 1; + ipv6_set = 1; + } else if (unformat (line_input, "encap-vrf-id %d", &tmp)) { if (ipv6_set) @@ -543,6 +870,23 @@ vxlan_gpe_add_del_tunnel_command_fn (vlib_main_t * vm, goto done; } + if (grp_set && !ip46_address_is_multicast(&remote)) + { + error = clib_error_return (0, "tunnel group address not multicast"); + goto done; + } + + if (grp_set == 0 && ip46_address_is_multicast(&remote)) + { + error = clib_error_return (0, "remote address must be unicast"); + goto done; + } + + if (grp_set && mcast_sw_if_index == ~0) + { + error = clib_error_return (0, "tunnel nonexistent multicast device"); + goto done; + } if (ipv4_set && ipv6_set) { error = clib_error_return (0, "both IPv4 and IPv6 addresses specified"); @@ -552,7 +896,7 @@ vxlan_gpe_add_del_tunnel_command_fn (vlib_main_t * vm, if ((ipv4_set && memcmp(&local.ip4, &remote.ip4, sizeof(local.ip4)) == 0) || (ipv6_set && memcmp(&local.ip6, &remote.ip6, sizeof(local.ip6)) == 0)) { - error = clib_error_return (0, "src and dst addresses are identical"); + error = clib_error_return (0, "src and remote addresses are identical"); goto done; } @@ -604,15 +948,36 @@ done: return error; } +/*? + * Add or delete a VXLAN-GPE Tunnel. + * + * VXLAN-GPE provides the features needed to allow L2 bridge domains (BDs) + * to span multiple servers. This is done by building an L2 overlay on + * top of an L3 network underlay using VXLAN-GPE tunnels. + * + * This makes it possible for servers to be co-located in the same data + * center or be separated geographically as long as they are reachable + * through the underlay L3 network. + * + * You can refer to this kind of L2 overlay bridge domain as a VXLAN-GPE sengment. + * + * @cliexpar + * Example of how to create a VXLAN-GPE Tunnel: + * @cliexcmd{create vxlan-gpe tunnel local 10.0.3.1 local 10.0.3.3 vni 13 encap-vrf-id 7} + * Example of how to delete a VXLAN Tunnel: + * @cliexcmd{create vxlan tunnel src 10.0.3.1 remote 10.0.3.3 vni 13 del} + ?*/ +/* *INDENT-OFF* */ VLIB_CLI_COMMAND (create_vxlan_gpe_tunnel_command, static) = { .path = "create vxlan-gpe tunnel", .short_help = - "create vxlan-gpe tunnel local <local-addr> remote <remote-addr>" + "create vxlan-gpe tunnel local <local-addr> " + " {remote <remote-addr>|group <mcast-addr> <intf-name>}" " vni <nn> [next-ip4][next-ip6][next-ethernet][next-nsh]" - " [encap-vrf-id <nn>] [decap-vrf-id <nn>]" - " [del]\n", + " [encap-vrf-id <nn>] [decap-vrf-id <nn>] [del]\n", .function = vxlan_gpe_add_del_tunnel_command_fn, }; +/* *INDENT-ON* */ /** * @brief CLI function for showing VXLAN GPE tunnels @@ -629,13 +994,13 @@ show_vxlan_gpe_tunnel_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { - vxlan_gpe_main_t * gm = &vxlan_gpe_main; + vxlan_gpe_main_t * ngm = &vxlan_gpe_main; vxlan_gpe_tunnel_t * t; - if (pool_elts (gm->tunnels) == 0) + if (pool_elts (ngm->tunnels) == 0) vlib_cli_output (vm, "No vxlan-gpe tunnels configured."); - pool_foreach (t, gm->tunnels, + pool_foreach (t, ngm->tunnels, ({ vlib_cli_output (vm, "%U", format_vxlan_gpe_tunnel, t); })); @@ -643,10 +1008,194 @@ show_vxlan_gpe_tunnel_command_fn (vlib_main_t * vm, return 0; } +/*? + * Display all the VXLAN-GPE Tunnel entries. + * + * @cliexpar + * Example of how to display the VXLAN-GPE Tunnel entries: + * @cliexstart{show vxlan-gpe tunnel} + * [0] local 10.0.3.1 remote 10.0.3.3 vni 13 encap_fib_index 0 sw_if_index 5 decap_next l2 + * @cliexend + ?*/ +/* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_vxlan_gpe_tunnel_command, static) = { .path = "show vxlan-gpe", .function = show_vxlan_gpe_tunnel_command_fn, }; +/* *INDENT-ON* */ + +void vnet_int_vxlan_gpe_bypass_mode (u32 sw_if_index, + u8 is_ip6, + u8 is_enable) +{ + if (is_ip6) + vnet_feature_enable_disable ("ip6-unicast", "ip6-vxlan-gpe-bypass", + sw_if_index, is_enable, 0, 0); + else + vnet_feature_enable_disable ("ip4-unicast", "ip4-vxlan-gpe-bypass", + sw_if_index, is_enable, 0, 0); +} + + +static clib_error_t * +set_ip_vxlan_gpe_bypass (u32 is_ip6, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, * line_input = &_line_input; + vnet_main_t * vnm = vnet_get_main(); + clib_error_t * error = 0; + u32 sw_if_index, is_enable; + + sw_if_index = ~0; + is_enable = 1; + + if (! unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat_user (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else if (unformat (line_input, "del")) + is_enable = 0; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (~0 == sw_if_index) + { + error = clib_error_return (0, "unknown interface `%U'", + format_unformat_error, line_input); + goto done; + } + + vnet_int_vxlan_gpe_bypass_mode (sw_if_index, is_ip6, is_enable); + + done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +set_ip4_vxlan_gpe_bypass (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + return set_ip_vxlan_gpe_bypass (0, input, cmd); +} + +/*? + * This command adds the 'ip4-vxlan-gpe-bypass' graph node for a given interface. + * By adding the IPv4 vxlan-gpe-bypass graph node to an interface, the node checks + * for and validate input vxlan_gpe packet and bypass ip4-lookup, ip4-local, + * ip4-udp-lookup nodes to speedup vxlan_gpe packet forwarding. This node will + * cause extra overhead to for non-vxlan_gpe packets which is kept at a minimum. + * + * @cliexpar + * @parblock + * Example of graph node before ip4-vxlan-gpe-bypass is enabled: + * @cliexstart{show vlib graph ip4-vxlan-gpe-bypass} + * Name Next Previous + * ip4-vxlan-gpe-bypass error-drop [0] + * vxlan4-gpe-input [1] + * ip4-lookup [2] + * @cliexend + * + * Example of how to enable ip4-vxlan-gpe-bypass on an interface: + * @cliexcmd{set interface ip vxlan-gpe-bypass GigabitEthernet2/0/0} + * + * Example of graph node after ip4-vxlan-gpe-bypass is enabled: + * @cliexstart{show vlib graph ip4-vxlan-gpe-bypass} + * Name Next Previous + * ip4-vxlan-gpe-bypass error-drop [0] ip4-input + * vxlan4-gpe-input [1] ip4-input-no-checksum + * ip4-lookup [2] + * @cliexend + * + * Example of how to display the feature enabed on an interface: + * @cliexstart{show ip interface features GigabitEthernet2/0/0} + * IP feature paths configured on GigabitEthernet2/0/0... + * ... + * ipv4 unicast: + * ip4-vxlan-gpe-bypass + * ip4-lookup + * ... + * @cliexend + * + * Example of how to disable ip4-vxlan-gpe-bypass on an interface: + * @cliexcmd{set interface ip vxlan-gpe-bypass GigabitEthernet2/0/0 del} + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_ip_vxlan_gpe_bypass_command, static) = { + .path = "set interface ip vxlan-gpe-bypass", + .function = set_ip4_vxlan_gpe_bypass, + .short_help = "set interface ip vxlan-gpe-bypass <interface> [del]", +}; +/* *INDENT-ON* */ + +static clib_error_t * +set_ip6_vxlan_gpe_bypass (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + return set_ip_vxlan_gpe_bypass (1, input, cmd); +} + +/*? + * This command adds the 'ip6-vxlan-gpe-bypass' graph node for a given interface. + * By adding the IPv6 vxlan-gpe-bypass graph node to an interface, the node checks + * for and validate input vxlan_gpe packet and bypass ip6-lookup, ip6-local, + * ip6-udp-lookup nodes to speedup vxlan_gpe packet forwarding. This node will + * cause extra overhead to for non-vxlan_gpe packets which is kept at a minimum. + * + * @cliexpar + * @parblock + * Example of graph node before ip6-vxlan-gpe-bypass is enabled: + * @cliexstart{show vlib graph ip6-vxlan-gpe-bypass} + * Name Next Previous + * ip6-vxlan-gpe-bypass error-drop [0] + * vxlan6-gpe-input [1] + * ip6-lookup [2] + * @cliexend + * + * Example of how to enable ip6-vxlan-gpe-bypass on an interface: + * @cliexcmd{set interface ip6 vxlan-gpe-bypass GigabitEthernet2/0/0} + * + * Example of graph node after ip6-vxlan-gpe-bypass is enabled: + * @cliexstart{show vlib graph ip6-vxlan-gpe-bypass} + * Name Next Previous + * ip6-vxlan-gpe-bypass error-drop [0] ip6-input + * vxlan6-gpe-input [1] ip4-input-no-checksum + * ip6-lookup [2] + * @cliexend + * + * Example of how to display the feature enabed on an interface: + * @cliexstart{show ip interface features GigabitEthernet2/0/0} + * IP feature paths configured on GigabitEthernet2/0/0... + * ... + * ipv6 unicast: + * ip6-vxlan-gpe-bypass + * ip6-lookup + * ... + * @cliexend + * + * Example of how to disable ip6-vxlan-gpe-bypass on an interface: + * @cliexcmd{set interface ip6 vxlan-gpe-bypass GigabitEthernet2/0/0 del} + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_ip6_vxlan_gpe_bypass_command, static) = { + .path = "set interface ip6 vxlan-gpe-bypass", + .function = set_ip6_vxlan_gpe_bypass, + .short_help = "set interface ip vxlan-gpe-bypass <interface> [del]", +}; +/* *INDENT-ON* */ /** * @brief Feature init function for VXLAN GPE @@ -658,21 +1207,25 @@ VLIB_CLI_COMMAND (show_vxlan_gpe_tunnel_command, static) = { */ clib_error_t *vxlan_gpe_init (vlib_main_t *vm) { - vxlan_gpe_main_t *gm = &vxlan_gpe_main; + vxlan_gpe_main_t *ngm = &vxlan_gpe_main; - gm->vnet_main = vnet_get_main(); - gm->vlib_main = vm; + ngm->vnet_main = vnet_get_main(); + ngm->vlib_main = vm; - gm->vxlan4_gpe_tunnel_by_key + ngm->vxlan4_gpe_tunnel_by_key = hash_create_mem (0, sizeof(vxlan4_gpe_tunnel_key_t), sizeof (uword)); - gm->vxlan6_gpe_tunnel_by_key + ngm->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, + ngm->mcast_shared = hash_create_mem(0, + sizeof(ip46_address_t), + sizeof(mcast_shared_t)); + + 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, + udp_register_dst_port (vm, UDP_DST_PORT_VXLAN6_GPE, vxlan6_gpe_input_node.index, 0 /* is_ip4 */); /* Register the list of standard decap protocols supported */ @@ -682,6 +1235,9 @@ clib_error_t *vxlan_gpe_init (vlib_main_t *vm) VXLAN_GPE_INPUT_NEXT_IP6_INPUT); vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_ETHERNET, VXLAN_GPE_INPUT_NEXT_ETHERNET_INPUT); + + fib_node_register_type(FIB_NODE_TYPE_VXLAN_GPE_TUNNEL, &vxlan_gpe_vft); + return 0; } diff --git a/src/vnet/vxlan-gpe/vxlan_gpe.h b/src/vnet/vxlan-gpe/vxlan_gpe.h index e768d2309ff..c348b5d5aaa 100644 --- a/src/vnet/vxlan-gpe/vxlan_gpe.h +++ b/src/vnet/vxlan-gpe/vxlan_gpe.h @@ -25,11 +25,15 @@ #include <vnet/vnet.h> #include <vnet/ip/ip.h> #include <vnet/l2/l2_input.h> +#include <vnet/l2/l2_output.h> +#include <vnet/l2/l2_bd.h> #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/udp/udp.h> +#include <vnet/dpo/dpo.h> +#include <vnet/adj/adj_types.h> /** * @brief VXLAN GPE header struct @@ -94,11 +98,16 @@ typedef struct { /** encapsulated protocol */ u8 protocol; + /* FIB DPO for IP forwarding of VXLAN-GPE encap packet */ + dpo_id_t next_dpo; /** tunnel local address */ ip46_address_t local; /** tunnel remote address */ ip46_address_t remote; + /* mcast packet output intfc index (used only if dst is mcast) */ + u32 mcast_sw_if_index; + /** FIB indices - tunnel partner lookup here */ u32 encap_fib_index; /** FIB indices - inner IP packet lookup here */ @@ -120,6 +129,27 @@ typedef struct { /** Next node after VxLAN-GPE encap */ uword encap_next_node; + + /** + * Linkage into the FIB object graph + */ + fib_node_t node; + + /* + * The FIB entry for (depending on VXLAN-GPE tunnel is unicast or mcast) + * sending unicast VXLAN-GPE encap packets or receiving mcast VXLAN-GPE packets + */ + fib_node_index_t fib_entry_index; + adj_index_t mcast_adj_index; + + /** + * The tunnel is a child of the FIB entry for its desintion. This is + * so it receives updates when the forwarding information for that entry + * changes. + * The tunnels sibling index on the FIB entry's dependency list. + */ + u32 sibling_index; + } vxlan_gpe_tunnel_t; /** Flags for vxlan_gpe_tunnel_t */ @@ -158,6 +188,12 @@ typedef struct { /** lookup IPv6 VXLAN GPE tunnel by key */ uword * vxlan6_gpe_tunnel_by_key; + /* local VTEP IPs ref count used by vxlan-bypass node to check if + received VXLAN packet DIP matches any local VTEP address */ + uword * vtep4; /* local ip4 VTEPs keyed on their ip4 addr */ + uword * vtep6; /* local ip6 VTEPs keyed on their ip6 addr */ + /* mcast shared info */ + uword * mcast_shared; /* keyed on mcast ip46 addr */ /** Free vlib hw_if_indices */ u32 * free_vxlan_gpe_tunnel_hw_if_indices; @@ -187,6 +223,7 @@ typedef struct { u8 is_ip6; ip46_address_t local, remote; u8 protocol; + u32 mcast_sw_if_index; u32 encap_fib_index; u32 decap_fib_index; u32 vni; @@ -217,5 +254,6 @@ void vxlan_gpe_unregister_decap_protocol (u8 protocol_id, uword next_node_index) void vxlan_gpe_register_decap_protocol (u8 protocol_id, uword next_node_index); +void vnet_int_vxlan_gpe_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable); #endif /* included_vnet_vxlan_gpe_h */ diff --git a/src/vnet/vxlan-gpe/vxlan_gpe_api.c b/src/vnet/vxlan-gpe/vxlan_gpe_api.c index 0215054dff7..3675fc55cd8 100644 --- a/src/vnet/vxlan-gpe/vxlan_gpe_api.c +++ b/src/vnet/vxlan-gpe/vxlan_gpe_api.c @@ -22,6 +22,7 @@ #include <vnet/interface.h> #include <vnet/api_errno.h> +#include <vnet/feature/feature.h> #include <vnet/vxlan-gpe/vxlan_gpe.h> #include <vnet/fib/fib_table.h> @@ -44,10 +45,27 @@ #include <vlibapi/api_helper_macros.h> #define foreach_vpe_api_msg \ +_(SW_INTERFACE_SET_VXLAN_GPE_BYPASS, sw_interface_set_vxlan_gpe_bypass) \ _(VXLAN_GPE_ADD_DEL_TUNNEL, vxlan_gpe_add_del_tunnel) \ _(VXLAN_GPE_TUNNEL_DUMP, vxlan_gpe_tunnel_dump) \ static void + vl_api_sw_interface_set_vxlan_gpe_bypass_t_handler + (vl_api_sw_interface_set_vxlan_gpe_bypass_t * mp) +{ + vl_api_sw_interface_set_vxlan_gpe_bypass_reply_t *rmp; + int rv = 0; + u32 sw_if_index = ntohl (mp->sw_if_index); + + VALIDATE_SW_IF_INDEX (mp); + + vnet_int_vxlan_gpe_bypass_mode (sw_if_index, mp->is_ipv6, mp->enable); + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_SW_INTERFACE_SET_VXLAN_GPE_BYPASS_REPLY); +} + +static void vl_api_vxlan_gpe_add_del_tunnel_t_handler (vl_api_vxlan_gpe_add_del_tunnel_t * mp) { @@ -109,6 +127,7 @@ static void clib_memcpy (&(a->local.ip4), mp->local, 4); clib_memcpy (&(a->remote.ip4), mp->remote, 4); } + a->mcast_sw_if_index = ntohl (mp->mcast_sw_if_index); a->encap_fib_index = encap_fib_index; a->decap_fib_index = decap_fib_index; a->protocol = protocol; @@ -149,6 +168,7 @@ static void send_vxlan_gpe_tunnel_details rmp->encap_vrf_id = htonl (im4->fibs[t->encap_fib_index].ft_table_id); rmp->decap_vrf_id = htonl (im4->fibs[t->decap_fib_index].ft_table_id); } + rmp->mcast_sw_if_index = htonl (t->mcast_sw_if_index); rmp->vni = htonl (t->vni); rmp->protocol = t->protocol; rmp->sw_if_index = htonl (t->sw_if_index); |