From 839dcc0fb7313638d9b8f52a9db81350dddfe461 Mon Sep 17 00:00:00 2001 From: Artem Glazychev Date: Tue, 1 Dec 2020 02:39:21 +0700 Subject: [PATCH] vxlan: add udp-port configuration support Type: improvement Signed-off-by: Artem Glazychev Change-Id: Ie30d51ab4df5599b52f7335f863b930cd69dbdc1 --- src/vnet/vxlan/decap.c | 70 +++++++++++++++++---- src/vnet/vxlan/vxlan.api | 58 +++++++++++++++++- src/vnet/vxlan/vxlan.c | 93 +++++++++++++++++----------- src/vnet/vxlan/vxlan.h | 12 +++- src/vnet/vxlan/vxlan_api.c | 147 +++++++++++++++++++++++++++++++++++++++++---- test/test_vxlan.py | 147 ++++++++++++++++++++++++++++++++++++--------- test/test_vxlan6.py | 112 ++++++++++++++++++++++++++++++---- test/vpp_vxlan_tunnel.py | 31 ++++++++-- 8 files changed, 564 insertions(+), 106 deletions(-) diff --git a/src/vnet/vxlan/decap.c b/src/vnet/vxlan/decap.c index e838c038f93..4678aa31219 100644 --- a/src/vnet/vxlan/decap.c +++ b/src/vnet/vxlan/decap.c @@ -68,12 +68,15 @@ vxlan4_find_tunnel (vxlan_main_t * vxm, last_tunnel_cache4 * cache, if (PREDICT_FALSE (vxlan0->flags != VXLAN_FLAGS_I)) return decap_bad_flags; - /* Make sure VXLAN tunnel exist according to packet S/D IP, VRF, and VNI */ + /* Make sure VXLAN tunnel exist according to packet S/D IP, UDP port, VRF, + * and VNI */ u32 dst = ip4_0->dst_address.as_u32; u32 src = ip4_0->src_address.as_u32; + udp_header_t *udp = ip4_next_header (ip4_0); vxlan4_tunnel_key_t key4 = { .key[0] = ((u64) dst << 32) | src, - .key[1] = ((u64) fib_index << 32) | vxlan0->vni_reserved, + .key[1] = ((u64) udp->dst_port << 48) | ((u64) fib_index << 32) | + vxlan0->vni_reserved, }; if (PREDICT_TRUE @@ -127,11 +130,14 @@ vxlan6_find_tunnel (vxlan_main_t * vxm, last_tunnel_cache6 * cache, if (PREDICT_FALSE (vxlan0->flags != VXLAN_FLAGS_I)) return decap_bad_flags; - /* Make sure VXLAN tunnel exist according to packet SIP and VNI */ + /* Make sure VXLAN tunnel exist according to packet SIP, UDP port, VRF, and + * VNI */ + udp_header_t *udp = ip6_next_header (ip6_0); vxlan6_tunnel_key_t key6 = { .key[0] = ip6_0->src_address.as_u64[0], .key[1] = ip6_0->src_address.as_u64[1], - .key[2] = (((u64) fib_index) << 32) | vxlan0->vni_reserved, + .key[2] = ((u64) udp->dst_port << 48) | ((u64) fib_index << 32) | + vxlan0->vni_reserved, }; if (PREDICT_FALSE @@ -460,6 +466,9 @@ ip_vxlan_bypass_inline (vlib_main_t * vm, matching a local VTEP address */ vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + last_tunnel_cache4 last4; + last_tunnel_cache6 last6; + #ifdef CLIB_HAVE_VEC512 vtep4_cache_t vtep4_u512; clib_memset (&vtep4_u512, 0, sizeof (vtep4_u512)); @@ -475,9 +484,15 @@ ip_vxlan_bypass_inline (vlib_main_t * vm, ip4_forward_next_trace (vm, node, frame, VLIB_TX); if (is_ip4) - vtep4_key_init (&last_vtep4); + { + vtep4_key_init (&last_vtep4); + clib_memset (&last4, 0xff, sizeof last4); + } else - vtep6_key_init (&last_vtep6); + { + vtep6_key_init (&last_vtep6); + clib_memset (&last6, 0xff, sizeof last6); + } while (n_left_from > 0) { @@ -489,11 +504,13 @@ ip_vxlan_bypass_inline (vlib_main_t * vm, ip4_header_t *ip40, *ip41; ip6_header_t *ip60, *ip61; udp_header_t *udp0, *udp1; + vxlan_header_t *vxlan0, *vxlan1; 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; + u32 stats_if0 = ~0, stats_if1 = ~0; /* Prefetch next iteration. */ { @@ -551,8 +568,17 @@ ip_vxlan_bypass_inline (vlib_main_t * vm, else udp0 = ip6_next_header (ip60); - if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_vxlan)) - goto exit0; /* not VXLAN packet */ + u32 fi0 = vlib_buffer_get_ip_fib_index (b0, is_ip4); + vxlan0 = vlib_buffer_get_current (b0) + sizeof (udp_header_t) + + sizeof (ip4_header_t); + + vxlan_decap_info_t di0 = + is_ip4 ? + vxlan4_find_tunnel (vxm, &last4, fi0, ip40, vxlan0, &stats_if0) : + vxlan6_find_tunnel (vxm, &last6, fi0, ip60, vxlan0, &stats_if0); + + if (PREDICT_FALSE (di0.sw_if_index == ~0)) + goto exit0; /* unknown interface */ /* Validate DIP against VTEPs */ if (is_ip4) @@ -630,8 +656,17 @@ ip_vxlan_bypass_inline (vlib_main_t * vm, else udp1 = ip6_next_header (ip61); - if (udp1->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_vxlan)) - goto exit1; /* not VXLAN packet */ + u32 fi1 = vlib_buffer_get_ip_fib_index (b1, is_ip4); + vxlan1 = vlib_buffer_get_current (b1) + sizeof (udp_header_t) + + sizeof (ip4_header_t); + + vxlan_decap_info_t di1 = + is_ip4 ? + vxlan4_find_tunnel (vxm, &last4, fi1, ip41, vxlan1, &stats_if1) : + vxlan6_find_tunnel (vxm, &last6, fi1, ip61, vxlan1, &stats_if1); + + if (PREDICT_FALSE (di1.sw_if_index == ~0)) + goto exit1; /* unknown interface */ /* Validate DIP against VTEPs */ if (is_ip4) @@ -711,9 +746,11 @@ ip_vxlan_bypass_inline (vlib_main_t * vm, ip4_header_t *ip40; ip6_header_t *ip60; udp_header_t *udp0; + vxlan_header_t *vxlan0; u32 bi0, ip_len0, udp_len0, flags0, next0; i32 len_diff0; u8 error0, good_udp0, proto0; + u32 stats_if0 = ~0; bi0 = to_next[0] = from[0]; from += 1; @@ -746,8 +783,17 @@ ip_vxlan_bypass_inline (vlib_main_t * vm, else udp0 = ip6_next_header (ip60); - if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_vxlan)) - goto exit; /* not VXLAN packet */ + u32 fi0 = vlib_buffer_get_ip_fib_index (b0, is_ip4); + vxlan0 = vlib_buffer_get_current (b0) + sizeof (udp_header_t) + + sizeof (ip4_header_t); + + vxlan_decap_info_t di0 = + is_ip4 ? + vxlan4_find_tunnel (vxm, &last4, fi0, ip40, vxlan0, &stats_if0) : + vxlan6_find_tunnel (vxm, &last6, fi0, ip60, vxlan0, &stats_if0); + + if (PREDICT_FALSE (di0.sw_if_index == ~0)) + goto exit; /* unknown interface */ /* Validate DIP against VTEPs */ if (is_ip4) diff --git a/src/vnet/vxlan/vxlan.api b/src/vnet/vxlan/vxlan.api index 907d7d32ce7..2370fb5157c 100644 --- a/src/vnet/vxlan/vxlan.api +++ b/src/vnet/vxlan/vxlan.api @@ -13,7 +13,7 @@ * limitations under the License. */ -option version = "2.0.0"; +option version = "2.1.0"; import "vnet/interface_types.api"; import "vnet/ip/ip_types.api"; @@ -44,12 +44,48 @@ define vxlan_add_del_tunnel u32 vni; }; +/** \brief Create or delete a VXLAN tunnel + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - Use 1 to create the tunnel, 0 to remove it + @param instance - optional unique custom device instance, else ~0. + @param src_address - Source IP address + @param dst_address - Destination IP address, can be multicast + @param src_port - Source UDP port. It is not included in sent packets. Used only for port registration + @param dst_port - Destination UDP port + @param mcast_sw_if_index - Interface for multicast destination + @param encap_vrf_id - Encap route table FIB index + @param decap_next_index - index of decap next graph node + @param vni - The VXLAN Network Identifier, uint24 +*/ +define vxlan_add_del_tunnel_v2 +{ + u32 client_index; + u32 context; + bool is_add [default=true]; + u32 instance [default=0xffffffff]; /* If non-~0, specifies a custom dev instance */ + vl_api_address_t src_address; + vl_api_address_t dst_address; + u16 src_port; + u16 dst_port; + vl_api_interface_index_t mcast_sw_if_index; + u32 encap_vrf_id; + u32 decap_next_index; + u32 vni; +}; + define vxlan_add_del_tunnel_reply { u32 context; i32 retval; vl_api_interface_index_t sw_if_index; }; +define vxlan_add_del_tunnel_v2_reply +{ + u32 context; + i32 retval; + vl_api_interface_index_t sw_if_index; +}; define vxlan_tunnel_dump { @@ -57,6 +93,12 @@ define vxlan_tunnel_dump u32 context; vl_api_interface_index_t sw_if_index; }; +define vxlan_tunnel_v2_dump +{ + u32 client_index; + u32 context; + vl_api_interface_index_t sw_if_index; +}; define vxlan_tunnel_details { @@ -70,6 +112,20 @@ define vxlan_tunnel_details u32 decap_next_index; u32 vni; }; +define vxlan_tunnel_v2_details +{ + u32 context; + vl_api_interface_index_t sw_if_index; + u32 instance; + vl_api_address_t src_address; + vl_api_address_t dst_address; + u16 src_port; + u16 dst_port; + vl_api_interface_index_t mcast_sw_if_index; + u32 encap_vrf_id; + u32 decap_next_index; + u32 vni; +}; /** \brief Interface set vxlan-bypass request @param client_index - opaque cookie to identify the sender diff --git a/src/vnet/vxlan/vxlan.c b/src/vnet/vxlan/vxlan.c index b2329d815ff..97098b83774 100644 --- a/src/vnet/vxlan/vxlan.c +++ b/src/vnet/vxlan/vxlan.c @@ -70,11 +70,12 @@ format_vxlan_tunnel (u8 * s, va_list * args) vxlan_tunnel_t *t = va_arg (*args, vxlan_tunnel_t *); s = format (s, - "[%d] instance %d src %U dst %U vni %d fib-idx %d sw-if-idx %d ", - t->dev_instance, t->user_instance, - format_ip46_address, &t->src, IP46_TYPE_ANY, - format_ip46_address, &t->dst, IP46_TYPE_ANY, - t->vni, t->encap_fib_index, t->sw_if_index); + "[%d] instance %d src %U dst %U src_port %d dst_port %d vni %d " + "fib-idx %d sw-if-idx %d ", + t->dev_instance, t->user_instance, format_ip46_address, &t->src, + IP46_TYPE_ANY, format_ip46_address, &t->dst, IP46_TYPE_ANY, + t->src_port, t->dst_port, t->vni, t->encap_fib_index, + t->sw_if_index); s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index); @@ -236,14 +237,15 @@ const static fib_node_vft_t vxlan_vft = { .fnv_back_walk = vxlan_tunnel_back_walk, }; - -#define foreach_copy_field \ -_(vni) \ -_(mcast_sw_if_index) \ -_(encap_fib_index) \ -_(decap_next_index) \ -_(src) \ -_(dst) +#define foreach_copy_field \ + _ (vni) \ + _ (mcast_sw_if_index) \ + _ (encap_fib_index) \ + _ (decap_next_index) \ + _ (src) \ + _ (dst) \ + _ (src_port) \ + _ (dst_port) static void vxlan_rewrite (vxlan_tunnel_t * t, bool is_ip6) @@ -288,8 +290,8 @@ vxlan_rewrite (vxlan_tunnel_t * t, bool is_ip6) } /* UDP header, randomize src port on something, maybe? */ - udp->src_port = clib_host_to_net_u16 (4789); - udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_vxlan); + udp->src_port = clib_host_to_net_u16 (t->src_port); + udp->dst_port = clib_host_to_net_u16 (t->dst_port); /* VXLAN header */ vnet_set_vni_and_flags (vxlan, t->vni); @@ -365,15 +367,23 @@ int vnet_vxlan_add_del_tunnel vlib_main_t *vm = vlib_get_main (); u8 hw_addr[6]; + /* Set udp-ports */ + if (a->src_port == 0) + a->src_port = is_ip6 ? UDP_DST_PORT_vxlan6 : UDP_DST_PORT_vxlan; + + if (a->dst_port == 0) + a->dst_port = is_ip6 ? UDP_DST_PORT_vxlan6 : UDP_DST_PORT_vxlan; + int not_found; if (!is_ip6) { /* ip4 mcast is indexed by mcast addr only */ key4.key[0] = ip46_address_is_multicast (&a->dst) ? - a->dst.ip4.as_u32 : - a->dst.ip4.as_u32 | (((u64) a->src.ip4.as_u32) << 32); - key4.key[1] = (((u64) a->encap_fib_index) << 32) - | clib_host_to_net_u32 (a->vni << 8); + a->dst.ip4.as_u32 : + a->dst.ip4.as_u32 | (((u64) a->src.ip4.as_u32) << 32); + key4.key[1] = ((u64) clib_host_to_net_u16 (a->src_port) << 48) | + (((u64) a->encap_fib_index) << 32) | + clib_host_to_net_u32 (a->vni << 8); not_found = clib_bihash_search_inline_16_8 (&vxm->vxlan4_tunnel_by_key, &key4); p = (void *) &key4.value; @@ -382,8 +392,9 @@ int vnet_vxlan_add_del_tunnel { key6.key[0] = a->dst.ip6.as_u64[0]; key6.key[1] = a->dst.ip6.as_u64[1]; - key6.key[2] = (((u64) a->encap_fib_index) << 32) - | clib_host_to_net_u32 (a->vni << 8); + key6.key[2] = (((u64) clib_host_to_net_u16 (a->src_port) << 48) | + ((u64) a->encap_fib_index) << 32) | + clib_host_to_net_u32 (a->vni << 8); not_found = clib_bihash_search_inline_24_8 (&vxm->vxlan6_tunnel_by_key, &key6); p = (void *) &key6.value; @@ -649,11 +660,11 @@ int vnet_vxlan_add_del_tunnel if (a->is_add) { /* register udp ports */ - if (!is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_vxlan, 1)) - udp_register_dst_port (vxm->vlib_main, UDP_DST_PORT_vxlan, + if (!is_ip6 && !udp_is_valid_dst_port (a->src_port, 1)) + udp_register_dst_port (vxm->vlib_main, a->src_port, vxlan4_input_node.index, 1); - if (is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_vxlan6, 0)) - udp_register_dst_port (vxm->vlib_main, UDP_DST_PORT_vxlan6, + if (is_ip6 && !udp_is_valid_dst_port (a->src_port, 0)) + udp_register_dst_port (vxm->vlib_main, a->src_port, vxlan6_input_node.index, 0); } @@ -711,6 +722,8 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm, u32 mcast_sw_if_index = ~0; u32 decap_next_index = VXLAN_INPUT_NEXT_L2_INPUT; u32 vni = 0; + u32 src_port = 0; + u32 dst_port = 0; u32 table_id; clib_error_t *parse_error = NULL; @@ -756,6 +769,10 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm, ; else if (unformat (line_input, "vni %d", &vni)) ; + else if (unformat (line_input, "src_port %d", &src_port)) + ; + else if (unformat (line_input, "dst_port %d", &dst_port)) + ; else { parse_error = clib_error_return (0, "parse error: '%U'", @@ -855,11 +872,17 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm, * * @cliexpar * Example of how to create a VXLAN Tunnel: - * @cliexcmd{create vxlan tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 encap-vrf-id 7} + * @cliexcmd{create vxlan tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 encap-vrf-id + 7} * Example of how to create a VXLAN Tunnel with a known name, vxlan_tunnel42: * @cliexcmd{create vxlan tunnel src 10.0.3.1 dst 10.0.3.3 instance 42} - * Example of how to create a multicast VXLAN Tunnel with a known name, vxlan_tunnel23: - * @cliexcmd{create vxlan tunnel src 10.0.3.1 group 239.1.1.1 GigabitEthernet0/8/0 instance 23} + * Example of how to create a multicast VXLAN Tunnel with a known name, + vxlan_tunnel23: + * @cliexcmd{create vxlan tunnel src 10.0.3.1 group 239.1.1.1 + GigabitEthernet0/8/0 instance 23} + * Example of how to create a VXLAN Tunnel with custom udp-ports: + * @cliexcmd{create vxlan tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 src_port + 59000 dst_port 59001} * Example of how to delete a VXLAN Tunnel: * @cliexcmd{create vxlan tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 del} ?*/ @@ -867,10 +890,11 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm, VLIB_CLI_COMMAND (create_vxlan_tunnel_command, static) = { .path = "create vxlan tunnel", .short_help = - "create vxlan tunnel src " - " {dst |group } vni " - " [instance ]" - " [encap-vrf-id ] [decap-next [l2|node ]] [del]", + "create vxlan tunnel src " + " {dst |group } vni " + " [instance ]" + " [encap-vrf-id ] [decap-next [l2|node ]] [del]" + " [src_port ] [dst_port ]", .function = vxlan_add_del_tunnel_command_fn, }; /* *INDENT-ON* */ @@ -922,7 +946,8 @@ show_vxlan_tunnel_command_fn (vlib_main_t * vm, * @cliexpar * Example of how to display the VXLAN Tunnel entries: * @cliexstart{show vxlan tunnel} - * [0] src 10.0.3.1 dst 10.0.3.3 vni 13 encap_fib_index 0 sw_if_index 5 decap_next l2 + * [0] src 10.0.3.1 dst 10.0.3.3 src_port 4789 dst_port 4789 vni 13 + encap_fib_index 0 sw_if_index 5 decap_next l2 * @cliexend ?*/ /* *INDENT-OFF* */ @@ -1155,7 +1180,7 @@ vnet_vxlan_add_del_rx_flow (u32 hw_if_index, u32 t_index, int is_add) .dst_addr.addr = t->src.ip4, .src_addr.mask.as_u32 = ~0, .dst_addr.mask.as_u32 = ~0, - .dst_port.port = UDP_DST_PORT_vxlan, + .dst_port.port = t->src_port, .dst_port.mask = 0xFF, .vni = t->vni, } diff --git a/src/vnet/vxlan/vxlan.h b/src/vnet/vxlan/vxlan.h index 1c77d77ee51..48c61461e7c 100644 --- a/src/vnet/vxlan/vxlan.h +++ b/src/vnet/vxlan/vxlan.h @@ -93,6 +93,10 @@ typedef struct ip46_address_t src; ip46_address_t dst; + /* udp-ports */ + u16 src_port; + u16 dst_port; + /* mcast packet output intfc index (used only if dst is mcast) */ u32 mcast_sw_if_index; @@ -159,8 +163,10 @@ typedef struct vxlan_tunnel_t *tunnels; /* lookup tunnel by key */ - clib_bihash_16_8_t vxlan4_tunnel_by_key; /* keyed on ipv4.dst + fib + vni */ - clib_bihash_24_8_t vxlan6_tunnel_by_key; /* keyed on ipv6.dst + fib + vni */ + clib_bihash_16_8_t + vxlan4_tunnel_by_key; /* keyed on ipv4.dst + src_port + fib + vni */ + clib_bihash_24_8_t + vxlan6_tunnel_by_key; /* keyed on ipv6.dst + src_port + fib + vni */ /* local VTEP IPs ref count used by vxlan-bypass node to check if received VXLAN packet DIP matches any local VTEP address */ @@ -208,6 +214,8 @@ typedef struct u32 encap_fib_index; u32 decap_next_index; u32 vni; + u16 src_port; + u16 dst_port; } vnet_vxlan_add_del_tunnel_args_t; int vnet_vxlan_add_del_tunnel diff --git a/src/vnet/vxlan/vxlan_api.c b/src/vnet/vxlan/vxlan_api.c index 0e51f3dab5a..6975d834e2a 100644 --- a/src/vnet/vxlan/vxlan_api.c +++ b/src/vnet/vxlan/vxlan_api.c @@ -27,6 +27,7 @@ #include #include +#include #include @@ -46,11 +47,13 @@ #include -#define foreach_vpe_api_msg \ -_(SW_INTERFACE_SET_VXLAN_BYPASS, sw_interface_set_vxlan_bypass) \ -_(VXLAN_ADD_DEL_TUNNEL, vxlan_add_del_tunnel) \ -_(VXLAN_TUNNEL_DUMP, vxlan_tunnel_dump) \ -_(VXLAN_OFFLOAD_RX, vxlan_offload_rx) +#define foreach_vpe_api_msg \ + _ (SW_INTERFACE_SET_VXLAN_BYPASS, sw_interface_set_vxlan_bypass) \ + _ (VXLAN_ADD_DEL_TUNNEL, vxlan_add_del_tunnel) \ + _ (VXLAN_TUNNEL_DUMP, vxlan_tunnel_dump) \ + _ (VXLAN_ADD_DEL_TUNNEL_V2, vxlan_add_del_tunnel_v2) \ + _ (VXLAN_TUNNEL_V2_DUMP, vxlan_tunnel_v2_dump) \ + _ (VXLAN_OFFLOAD_RX, vxlan_offload_rx) static void vl_api_vxlan_offload_rx_t_handler (vl_api_vxlan_offload_rx_t * mp) @@ -159,6 +162,8 @@ static void vl_api_vxlan_add_del_tunnel_t_handler .vni = ntohl (mp->vni), .dst = dst, .src = src, + .dst_port = is_ipv6 ? UDP_DST_PORT_vxlan6 : UDP_DST_PORT_vxlan, + .src_port = is_ipv6 ? UDP_DST_PORT_vxlan6 : UDP_DST_PORT_vxlan, }; /* Check src & dst are different */ @@ -178,12 +183,73 @@ static void vl_api_vxlan_add_del_tunnel_t_handler rv = vnet_vxlan_add_del_tunnel (&a, &sw_if_index); out: - /* *INDENT-OFF* */ REPLY_MACRO2(VL_API_VXLAN_ADD_DEL_TUNNEL_REPLY, ({ rmp->sw_if_index = ntohl (sw_if_index); })); - /* *INDENT-ON* */ +} + +static void +vl_api_vxlan_add_del_tunnel_v2_t_handler (vl_api_vxlan_add_del_tunnel_v2_t *mp) +{ + vl_api_vxlan_add_del_tunnel_v2_reply_t *rmp; + int rv = 0; + bool is_ipv6; + u32 fib_index; + ip46_address_t src, dst; + + ip_address_decode (&mp->src_address, &src); + ip_address_decode (&mp->dst_address, &dst); + + if (ip46_address_is_ip4 (&src) != ip46_address_is_ip4 (&dst)) + { + rv = VNET_API_ERROR_INVALID_VALUE; + goto out; + } + + is_ipv6 = !ip46_address_is_ip4 (&src); + + fib_index = + fib_table_find (fib_ip_proto (is_ipv6), ntohl (mp->encap_vrf_id)); + if (fib_index == ~0) + { + rv = VNET_API_ERROR_NO_SUCH_FIB; + goto out; + } + + vnet_vxlan_add_del_tunnel_args_t a = { + .is_add = mp->is_add, + .is_ip6 = is_ipv6, + .instance = ntohl (mp->instance), + .mcast_sw_if_index = ntohl (mp->mcast_sw_if_index), + .encap_fib_index = fib_index, + .decap_next_index = ntohl (mp->decap_next_index), + .vni = ntohl (mp->vni), + .dst = dst, + .src = src, + .dst_port = ntohs (mp->dst_port), + .src_port = ntohs (mp->src_port), + }; + + /* Check src & dst are different */ + if (ip46_address_cmp (&a.dst, &a.src) == 0) + { + rv = VNET_API_ERROR_SAME_SRC_DST; + goto out; + } + if (ip46_address_is_multicast (&a.dst) && + !vnet_sw_if_index_is_api_valid (a.mcast_sw_if_index)) + { + rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; + goto out; + } + + u32 sw_if_index = ~0; + rv = vnet_vxlan_add_del_tunnel (&a, &sw_if_index); + +out: + REPLY_MACRO2 (VL_API_VXLAN_ADD_DEL_TUNNEL_V2_REPLY, + ({ rmp->sw_if_index = ntohl (sw_if_index); })); } static void send_vxlan_tunnel_details @@ -231,12 +297,8 @@ static void vl_api_vxlan_tunnel_dump_t_handler if (~0 == sw_if_index) { - /* *INDENT-OFF* */ pool_foreach (t, vxm->tunnels) - { send_vxlan_tunnel_details(t, reg, mp->context); - } - /* *INDENT-ON* */ } else { @@ -250,6 +312,69 @@ static void vl_api_vxlan_tunnel_dump_t_handler } } +static void +send_vxlan_tunnel_v2_details (vxlan_tunnel_t *t, vl_api_registration_t *reg, + u32 context) +{ + vl_api_vxlan_tunnel_v2_details_t *rmp; + ip4_main_t *im4 = &ip4_main; + ip6_main_t *im6 = &ip6_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_VXLAN_TUNNEL_V2_DETAILS); + + ip_address_encode (&t->src, IP46_TYPE_ANY, &rmp->src_address); + ip_address_encode (&t->dst, IP46_TYPE_ANY, &rmp->dst_address); + rmp->src_port = htons (t->src_port); + rmp->dst_port = htons (t->dst_port); + + if (ip46_address_is_ip4 (&t->dst)) + rmp->encap_vrf_id = htonl (im4->fibs[t->encap_fib_index].ft_table_id); + else + rmp->encap_vrf_id = htonl (im6->fibs[t->encap_fib_index].ft_table_id); + + rmp->instance = htonl (t->user_instance); + rmp->mcast_sw_if_index = htonl (t->mcast_sw_if_index); + rmp->vni = htonl (t->vni); + rmp->decap_next_index = htonl (t->decap_next_index); + rmp->sw_if_index = htonl (t->sw_if_index); + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_vxlan_tunnel_v2_dump_t_handler (vl_api_vxlan_tunnel_v2_dump_t *mp) +{ + vl_api_registration_t *reg; + vxlan_main_t *vxm = &vxlan_main; + vxlan_tunnel_t *t; + u32 sw_if_index; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + sw_if_index = ntohl (mp->sw_if_index); + + if (~0 == sw_if_index) + { + pool_foreach (t, vxm->tunnels) + send_vxlan_tunnel_v2_details (t, reg, mp->context); + } + else + { + if ((sw_if_index >= vec_len (vxm->tunnel_index_by_sw_if_index)) || + (~0 == vxm->tunnel_index_by_sw_if_index[sw_if_index])) + { + return; + } + t = &vxm->tunnels[vxm->tunnel_index_by_sw_if_index[sw_if_index]]; + send_vxlan_tunnel_v2_details (t, reg, mp->context); + } +} + /* * vpe_api_hookup * Add vpe's API message handlers to the table. diff --git a/test/test_vxlan.py b/test/test_vxlan.py index dc48f539f6e..028275ccedf 100644 --- a/test/test_vxlan.py +++ b/test/test_vxlan.py @@ -7,7 +7,7 @@ from framework import VppTestCase, VppTestRunner from template_bd import BridgeDomain from scapy.layers.l2 import Ether -from scapy.packet import Raw +from scapy.packet import Raw, bind_layers from scapy.layers.inet import IP, UDP from scapy.layers.vxlan import VXLAN @@ -79,14 +79,14 @@ class TestVxlan(BridgeDomain, VppTestCase): self.assertEqual(pkt[IP].dst, type(self).mcast_ip4) # Verify UDP destination port is VXLAN 4789, source UDP port could be # arbitrary. - self.assertEqual(pkt[UDP].dport, type(self).dport) + self.assertEqual(pkt[UDP].dport, self.dport) # Verify UDP checksum self.assert_udp_checksum_valid(pkt) # Verify VNI self.assertEqual(pkt[VXLAN].vni, vni) @classmethod - def create_vxlan_flood_test_bd(cls, vni, n_ucast_tunnels): + def create_vxlan_flood_test_bd(cls, vni, n_ucast_tunnels, port): # Create 10 ucast vxlan tunnels under bd ip_range_start = 10 ip_range_end = ip_range_start + n_ucast_tunnels @@ -101,12 +101,13 @@ class TestVxlan(BridgeDomain, VppTestCase): rip.add_vpp_config() r = VppVxlanTunnel(cls, src=cls.pg0.local_ip4, + src_port=port, dst_port=port, dst=dest_ip4, vni=vni) r.add_vpp_config() cls.vapi.sw_interface_set_l2_bridge(r.sw_if_index, bd_id=vni) @classmethod - def add_del_shared_mcast_dst_load(cls, is_add): + def add_del_shared_mcast_dst_load(cls, port, is_add): """ add or del tunnels sharing the same mcast dst to test vxlan ref_count mechanism @@ -116,6 +117,7 @@ class TestVxlan(BridgeDomain, VppTestCase): vni_end = vni_start + n_shared_dst_tunnels for vni in range(vni_start, vni_end): r = VppVxlanTunnel(cls, src=cls.pg0.local_ip4, + src_port=port, dst_port=port, dst=cls.mcast_ip4, mcast_sw_if_index=1, vni=vni) if is_add: r.add_vpp_config() @@ -125,15 +127,15 @@ class TestVxlan(BridgeDomain, VppTestCase): r.remove_vpp_config() @classmethod - def add_shared_mcast_dst_load(cls): - cls.add_del_shared_mcast_dst_load(is_add=1) + def add_shared_mcast_dst_load(cls, port): + cls.add_del_shared_mcast_dst_load(port=port, is_add=1) @classmethod - def del_shared_mcast_dst_load(cls): - cls.add_del_shared_mcast_dst_load(is_add=0) + def del_shared_mcast_dst_load(cls, port): + cls.add_del_shared_mcast_dst_load(port=port, is_add=0) @classmethod - def add_del_mcast_tunnels_load(cls, is_add): + def add_del_mcast_tunnels_load(cls, port, is_add): """ add or del tunnels to test vxlan stability """ @@ -144,6 +146,7 @@ class TestVxlan(BridgeDomain, VppTestCase): ip_range_end): vni = bytearray(socket.inet_pton(socket.AF_INET, dest_ip4))[3] r = VppVxlanTunnel(cls, src=cls.pg0.local_ip4, + src_port=port, dst_port=port, dst=dest_ip4, mcast_sw_if_index=1, vni=vni) if is_add: r.add_vpp_config() @@ -151,12 +154,12 @@ class TestVxlan(BridgeDomain, VppTestCase): r.remove_vpp_config() @classmethod - def add_mcast_tunnels_load(cls): - cls.add_del_mcast_tunnels_load(is_add=1) + def add_mcast_tunnels_load(cls, port): + cls.add_del_mcast_tunnels_load(port=port, is_add=1) @classmethod - def del_mcast_tunnels_load(cls): - cls.add_del_mcast_tunnels_load(is_add=0) + def del_mcast_tunnels_load(cls, port): + cls.add_del_mcast_tunnels_load(port=port, is_add=0) # Class method to start the VXLAN test case. # Overrides setUpClass method in VppTestCase class. @@ -168,7 +171,6 @@ class TestVxlan(BridgeDomain, VppTestCase): super(TestVxlan, cls).setUpClass() try: - cls.dport = 4789 cls.flags = 0x8 # Create 2 pg interfaces. @@ -195,12 +197,17 @@ class TestVxlan(BridgeDomain, VppTestCase): def setUp(self): super(TestVxlan, self).setUp() + + def createVxLANInterfaces(self, port=4789): # Create VXLAN VTEP on VPP pg0, and put vxlan_tunnel0 and pg1 # into BD. + self.dport = port + self.single_tunnel_vni = 0x12345 self.single_tunnel_bd = 1 r = VppVxlanTunnel(self, src=self.pg0.local_ip4, dst=self.pg0.remote_ip4, + src_port=self.dport, dst_port=self.dport, vni=self.single_tunnel_vni) r.add_vpp_config() self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index, @@ -212,8 +219,10 @@ class TestVxlan(BridgeDomain, VppTestCase): self.n_ucast_tunnels = 10 self.mcast_flood_bd = 2 self.create_vxlan_flood_test_bd(self.mcast_flood_bd, - self.n_ucast_tunnels) + self.n_ucast_tunnels, + self.dport) r = VppVxlanTunnel(self, src=self.pg0.local_ip4, dst=self.mcast_ip4, + src_port=self.dport, dst_port=self.dport, mcast_sw_if_index=1, vni=self.mcast_flood_bd) r.add_vpp_config() self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index, @@ -222,29 +231,23 @@ class TestVxlan(BridgeDomain, VppTestCase): rx_sw_if_index=self.pg2.sw_if_index, bd_id=self.mcast_flood_bd) # Add and delete mcast tunnels to check stability - self.add_shared_mcast_dst_load() - self.add_mcast_tunnels_load() - self.del_shared_mcast_dst_load() - self.del_mcast_tunnels_load() + self.add_shared_mcast_dst_load(self.dport) + self.add_mcast_tunnels_load(self.dport) + self.del_shared_mcast_dst_load(self.dport) + self.del_mcast_tunnels_load(self.dport) # Setup vni 3 to test unicast flooding self.ucast_flood_bd = 3 self.create_vxlan_flood_test_bd(self.ucast_flood_bd, - self.n_ucast_tunnels) + self.n_ucast_tunnels, + self.dport) self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=self.pg3.sw_if_index, bd_id=self.ucast_flood_bd) - def test_decap(self): - """ Decapsulation test - from BridgeDoman - """ - super(TestVxlan, self).test_decap() - - def test_encap_big_packet(self): - """ Encapsulation test send big frame from pg1 - Verify receipt of encapsulated frames on pg0 - """ + # Set scapy listen custom port for VxLAN + bind_layers(UDP, VXLAN, dport=self.dport) + def encap_big_packet(self): self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [1500, 0, 0, 0]) frame = (Ether(src='00:00:00:00:00:02', dst='00:00:00:00:00:01') / @@ -269,9 +272,93 @@ class TestVxlan(BridgeDomain, VppTestCase): # TODO: Scapy bug? # self.assert_eq_pkts(payload, frame) + """ + Tests with default port (4789) + """ + def test_decap(self): + """ Decapsulation test + from BridgeDoman + """ + self.createVxLANInterfaces() + super(TestVxlan, self).test_decap() + + def test_encap(self): + """ Encapsulation test + from BridgeDoman + """ + self.createVxLANInterfaces() + super(TestVxlan, self).test_encap() + + def test_encap_big_packet(self): + """ Encapsulation test send big frame from pg1 + Verify receipt of encapsulated frames on pg0 + """ + self.createVxLANInterfaces() + self.encap_big_packet() + + def test_ucast_flood(self): + """ Unicast flood test + from BridgeDoman + """ + self.createVxLANInterfaces() + super(TestVxlan, self).test_ucast_flood() + + def test_mcast_flood(self): + """ Multicast flood test + from BridgeDoman + """ + self.createVxLANInterfaces() + super(TestVxlan, self).test_mcast_flood() + + def test_mcast_rcv(self): + """ Multicast receive test + from BridgeDoman + """ + self.createVxLANInterfaces() + super(TestVxlan, self).test_mcast_rcv() + + """ + Tests with custom port + """ + def test_decap_custom_port(self): + """ Decapsulation test custom port + from BridgeDoman + """ + self.createVxLANInterfaces(1111) + super(TestVxlan, self).test_decap() + + def test_encap_custom_port(self): + """ Encapsulation test custom port + from BridgeDoman + """ + self.createVxLANInterfaces(1111) + super(TestVxlan, self).test_encap() + + def test_ucast_flood_custom_port(self): + """ Unicast flood test custom port + from BridgeDoman + """ + self.createVxLANInterfaces(1111) + super(TestVxlan, self).test_ucast_flood() + + def test_mcast_flood_custom_port(self): + """ Multicast flood test custom port + from BridgeDoman + """ + self.createVxLANInterfaces(1111) + super(TestVxlan, self).test_mcast_flood() + + def test_mcast_rcv_custom_port(self): + """ Multicast receive test custom port + from BridgeDoman + """ + self.createVxLANInterfaces(1111) + super(TestVxlan, self).test_mcast_rcv() + # Method to define VPP actions before tear down of the test case. # Overrides tearDown method in VppTestCase class. # @param self The object pointer. + def tearDown(self): super(TestVxlan, self).tearDown() diff --git a/test/test_vxlan6.py b/test/test_vxlan6.py index d9e2d8159d1..123cce9b7ba 100644 --- a/test/test_vxlan6.py +++ b/test/test_vxlan6.py @@ -6,7 +6,7 @@ from framework import VppTestCase, VppTestRunner from template_bd import BridgeDomain from scapy.layers.l2 import Ether -from scapy.packet import Raw +from scapy.packet import Raw, bind_layers from scapy.layers.inet6 import IP, IPv6, UDP from scapy.layers.vxlan import VXLAN @@ -80,14 +80,14 @@ class TestVxlan6(BridgeDomain, VppTestCase): self.assertEqual(pkt[IPv6].dst, type(self).mcast_ip6) # Verify UDP destination port is VXLAN 4789, source UDP port could be # arbitrary. - self.assertEqual(pkt[UDP].dport, type(self).dport) + self.assertEqual(pkt[UDP].dport, self.dport) # Verify UDP checksum self.assert_udp_checksum_valid(pkt, ignore_zero_checksum=False) # Verify VNI self.assertEqual(pkt[VXLAN].vni, vni) @classmethod - def create_vxlan_flood_test_bd(cls, vni, n_ucast_tunnels): + def create_vxlan_flood_test_bd(cls, vni, n_ucast_tunnels, port): # Create 10 ucast vxlan tunnels under bd start = 10 end = start + n_ucast_tunnels @@ -98,6 +98,7 @@ class TestVxlan6(BridgeDomain, VppTestCase): register=False) rip.add_vpp_config() r = VppVxlanTunnel(cls, src=cls.pg0.local_ip6, + src_port=port, dst_port=port, dst=dest_ip6, vni=vni) r.add_vpp_config() cls.vapi.sw_interface_set_l2_bridge(r.sw_if_index, bd_id=vni) @@ -120,7 +121,6 @@ class TestVxlan6(BridgeDomain, VppTestCase): super(TestVxlan6, cls).setUpClass() try: - cls.dport = 4789 cls.flags = 0x8 # Create 2 pg interfaces. @@ -147,12 +147,17 @@ class TestVxlan6(BridgeDomain, VppTestCase): def setUp(self): super(TestVxlan6, self).setUp() + + def createVxLANInterfaces(self, port=4789): # Create VXLAN VTEP on VPP pg0, and put vxlan_tunnel0 and pg1 # into BD. + self.dport = port + self.single_tunnel_vni = 0x12345 self.single_tunnel_bd = 1 r = VppVxlanTunnel(self, src=self.pg0.local_ip6, dst=self.pg0.remote_ip6, + src_port=self.dport, dst_port=self.dport, vni=self.single_tunnel_vni) r.add_vpp_config() self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index, @@ -164,8 +169,10 @@ class TestVxlan6(BridgeDomain, VppTestCase): self.n_ucast_tunnels = 10 self.mcast_flood_bd = 2 self.create_vxlan_flood_test_bd(self.mcast_flood_bd, - self.n_ucast_tunnels) + self.n_ucast_tunnels, + self.dport) r = VppVxlanTunnel(self, src=self.pg0.local_ip6, dst=self.mcast_ip6, + src_port=self.dport, dst_port=self.dport, mcast_sw_if_index=1, vni=self.mcast_flood_bd) r.add_vpp_config() self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index, @@ -176,10 +183,14 @@ class TestVxlan6(BridgeDomain, VppTestCase): # Setup vni 3 to test unicast flooding self.ucast_flood_bd = 3 self.create_vxlan_flood_test_bd(self.ucast_flood_bd, - self.n_ucast_tunnels) + self.n_ucast_tunnels, + self.dport) self.vapi.sw_interface_set_l2_bridge( rx_sw_if_index=self.pg3.sw_if_index, bd_id=self.ucast_flood_bd) + # Set scapy listen custom port for VxLAN + bind_layers(UDP, VXLAN, dport=self.dport) + # Method to define VPP actions before tear down of the test case. # Overrides tearDown method in VppTestCase class. # @param self The object pointer. @@ -192,11 +203,7 @@ class TestVxlan6(BridgeDomain, VppTestCase): self.logger.info(self.vapi.cli("show bridge-domain 3 detail")) self.logger.info(self.vapi.cli("show vxlan tunnel")) - def test_encap_fragmented_packet(self): - """ Encapsulation test send fragments from pg1 - Verify receipt of encapsulated frames on pg0 - """ - + def encap_fragmented_packet(self): frame = (Ether(src='00:00:00:00:00:02', dst='00:00:00:00:00:01') / IP(src='4.3.2.1', dst='1.2.3.4') / UDP(sport=20000, dport=10000) / @@ -221,6 +228,89 @@ class TestVxlan6(BridgeDomain, VppTestCase): self.assertEqual(Ether(raw(frame))[IP], reassembled[IP]) + """ + Tests with default port (4789) + """ + def test_decap(self): + """ Decapsulation test + from BridgeDoman + """ + self.createVxLANInterfaces() + super(TestVxlan6, self).test_decap() + + def test_encap(self): + """ Encapsulation test + from BridgeDoman + """ + self.createVxLANInterfaces() + super(TestVxlan6, self).test_encap() + + def test_encap_fragmented_packet(self): + """ Encapsulation test send fragments from pg1 + Verify receipt of encapsulated frames on pg0 + """ + self.createVxLANInterfaces() + self.encap_fragmented_packet() + + def test_ucast_flood(self): + """ Unicast flood test + from BridgeDoman + """ + self.createVxLANInterfaces() + super(TestVxlan6, self).test_ucast_flood() + + def test_mcast_flood(self): + """ Multicast flood test + from BridgeDoman + """ + self.createVxLANInterfaces() + super(TestVxlan6, self).test_mcast_flood() + + def test_mcast_rcv(self): + """ Multicast receive test + from BridgeDoman + """ + self.createVxLANInterfaces() + super(TestVxlan6, self).test_mcast_rcv() + + """ + Tests with custom port + """ + def test_decap_custom_port(self): + """ Decapsulation test custom port + from BridgeDoman + """ + self.createVxLANInterfaces(1111) + super(TestVxlan6, self).test_decap() + + def test_encap_custom_port(self): + """ Encapsulation test custom port + from BridgeDoman + """ + self.createVxLANInterfaces(1111) + super(TestVxlan6, self).test_encap() + + def test_ucast_flood_custom_port(self): + """ Unicast flood test custom port + from BridgeDoman + """ + self.createVxLANInterfaces(1111) + super(TestVxlan6, self).test_ucast_flood() + + def test_mcast_flood_custom_port(self): + """ Multicast flood test custom port + from BridgeDoman + """ + self.createVxLANInterfaces(1111) + super(TestVxlan6, self).test_mcast_flood() + + def test_mcast_rcv_custom_port(self): + """ Multicast receive test custom port + from BridgeDoman + """ + self.createVxLANInterfaces(1111) + super(TestVxlan6, self).test_mcast_rcv() + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_vxlan_tunnel.py b/test/vpp_vxlan_tunnel.py index 0f6ecb771f5..dce5ab86c07 100644 --- a/test/vpp_vxlan_tunnel.py +++ b/test/vpp_vxlan_tunnel.py @@ -3,13 +3,26 @@ from vpp_papi import VppEnum INDEX_INVALID = 0xffffffff +DEFAULT_PORT = 4789 +UNDEFINED_PORT = 0 -def find_vxlan_tunnel(test, src, dst, vni): - ts = test.vapi.vxlan_tunnel_dump(INDEX_INVALID) +def find_vxlan_tunnel(test, src, dst, s_port, d_port, vni): + ts = test.vapi.vxlan_tunnel_v2_dump(INDEX_INVALID) + + src_port = DEFAULT_PORT + if s_port != UNDEFINED_PORT: + src_port = s_port + + dst_port = DEFAULT_PORT + if d_port != UNDEFINED_PORT: + dst_port = d_port + for t in ts: if src == str(t.src_address) and \ dst == str(t.dst_address) and \ + src_port == t.src_port and \ + dst_port == t.dst_port and \ t.vni == vni: return t.sw_if_index return INDEX_INVALID @@ -20,7 +33,9 @@ class VppVxlanTunnel(VppInterface): VPP VXLAN interface """ - def __init__(self, test, src, dst, vni, mcast_itf=None, + def __init__(self, test, src, dst, vni, + src_port=UNDEFINED_PORT, dst_port=UNDEFINED_PORT, + mcast_itf=None, mcast_sw_if_index=INDEX_INVALID, decap_next_index=INDEX_INVALID, encap_vrf_id=None, instance=0xffffffff): @@ -29,6 +44,8 @@ class VppVxlanTunnel(VppInterface): self.src = src self.dst = dst self.vni = vni + self.src_port = src_port + self.dst_port = dst_port self.mcast_itf = mcast_itf self.mcast_sw_if_index = mcast_sw_if_index self.encap_vrf_id = encap_vrf_id @@ -39,8 +56,9 @@ class VppVxlanTunnel(VppInterface): self.mcast_sw_if_index = self.mcast_itf.sw_if_index def add_vpp_config(self): - reply = self.test.vapi.vxlan_add_del_tunnel( + reply = self.test.vapi.vxlan_add_del_tunnel_v2( is_add=1, src_address=self.src, dst_address=self.dst, vni=self.vni, + src_port=self.src_port, dst_port=self.dst_port, mcast_sw_if_index=self.mcast_sw_if_index, encap_vrf_id=self.encap_vrf_id, instance=self.instance, decap_next_index=self.decap_next_index) @@ -48,8 +66,9 @@ class VppVxlanTunnel(VppInterface): self._test.registry.register(self, self._test.logger) def remove_vpp_config(self): - self.test.vapi.vxlan_add_del_tunnel( + self.test.vapi.vxlan_add_del_tunnel_v2( is_add=0, src_address=self.src, dst_address=self.dst, vni=self.vni, + src_port=self.src_port, dst_port=self.dst_port, mcast_sw_if_index=self.mcast_sw_if_index, encap_vrf_id=self.encap_vrf_id, instance=self.instance, decap_next_index=self.decap_next_index) @@ -58,6 +77,8 @@ class VppVxlanTunnel(VppInterface): return (INDEX_INVALID != find_vxlan_tunnel(self._test, self.src, self.dst, + self.src_port, + self.dst_port, self.vni)) def object_id(self): -- 2.16.6