From: Vladislav Grishenko Date: Wed, 22 Jun 2022 19:45:16 +0000 (+0500) Subject: udp: add udp encap source port entropy support X-Git-Tag: v23.06-rc0~227 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=5c801b362a536fcae704c50bf1573362d372bb3c;p=vpp.git udp: add udp encap source port entropy support Encode entropy value in UDP source port when requested per RFC 7510. CLI already has "src-port-is-entropy", use zero UDP source port in API to avoid breaking changes, since zero port is not something to be used in wild. Also, mark UDP encapsualtion API as mp-safe as already done for CLI. Type: feature Change-Id: Ieb61ee11e058179ed566ff1f251a3391eb169d52 Signed-off-by: Vladislav Grishenko --- diff --git a/src/plugins/lisp/lisp-gpe/lisp_gpe_adjacency.c b/src/plugins/lisp/lisp-gpe/lisp_gpe_adjacency.c index dd8a252f378..7c857b92575 100644 --- a/src/plugins/lisp/lisp-gpe/lisp_gpe_adjacency.c +++ b/src/plugins/lisp/lisp-gpe/lisp_gpe_adjacency.c @@ -285,7 +285,8 @@ lisp_gpe_fixup (vlib_main_t * vm, /* Fixup the checksum and len fields in the LISP tunnel encap * that was applied at the midchain node */ - ip_udp_fixup_one (vm, b, is_v4_packet (vlib_buffer_get_current (b))); + ip_udp_fixup_one (vm, b, is_v4_packet (vlib_buffer_get_current (b)), + UDP_ENCAP_FIXUP_NONE); } /** diff --git a/src/vnet/udp/udp.api b/src/vnet/udp/udp.api index 02176be7c2b..6b468be461a 100644 --- a/src/vnet/udp/udp.api +++ b/src/vnet/udp/udp.api @@ -32,7 +32,7 @@ import "vnet/ip/ip_types.api"; * @param dst_ip - Encap destination address * @param src_ip - Encap source address * @param dst_port - Encap destination port - * @param src_port - Encap source port + * @param src_port - Encap source port, 0 for entopy per rfc7510 * @param id - VPP assigned id; ignored in add message, set in dump */ typedef udp_encap diff --git a/src/vnet/udp/udp_api.c b/src/vnet/udp/udp_api.c index 0f2d014946f..ae6b5bb5807 100644 --- a/src/vnet/udp/udp_api.c +++ b/src/vnet/udp/udp_api.c @@ -99,6 +99,7 @@ vl_api_udp_encap_add_t_handler (vl_api_udp_encap_add_t *mp) { vl_api_udp_encap_add_reply_t *rmp; ip46_address_t src_ip, dst_ip; + udp_encap_fixup_flags_t flags; u32 fib_index, table_id; fib_protocol_t fproto; ip46_type_t itype; @@ -119,11 +120,13 @@ vl_api_udp_encap_add_t_handler (vl_api_udp_encap_add_t *mp) goto done; } - uei = udp_encap_add_and_lock (fproto, fib_index, - &src_ip, &dst_ip, + flags = UDP_ENCAP_FIXUP_NONE; + if (mp->udp_encap.src_port == 0) + flags |= UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY; + + uei = udp_encap_add_and_lock (fproto, fib_index, &src_ip, &dst_ip, ntohs (mp->udp_encap.src_port), - ntohs (mp->udp_encap.dst_port), - UDP_ENCAP_FIXUP_NONE); + ntohs (mp->udp_encap.dst_port), flags); done: /* *INDENT-OFF* */ @@ -189,11 +192,19 @@ vl_api_udp_decap_add_del_t_handler (vl_api_udp_decap_add_del_t *mp) static clib_error_t * udp_api_hookup (vlib_main_t * vm) { + api_main_t *am = vlibapi_get_main (); + /* * Set up the (msg_name, crc, message-id) table */ REPLY_MSG_ID_BASE = setup_message_id_table (); + /* Mark these APIs as mp safe */ + vl_api_set_msg_thread_safe (am, REPLY_MSG_ID_BASE + VL_API_UDP_ENCAP_ADD, 1); + vl_api_set_msg_thread_safe (am, REPLY_MSG_ID_BASE + VL_API_UDP_ENCAP_DEL, 1); + vl_api_set_msg_thread_safe (am, REPLY_MSG_ID_BASE + VL_API_UDP_ENCAP_DUMP, + 1); + return 0; } diff --git a/src/vnet/udp/udp_encap.c b/src/vnet/udp/udp_encap.c index a0f5a50c223..ac1f855b4a0 100644 --- a/src/vnet/udp/udp_encap.c +++ b/src/vnet/udp/udp_encap.c @@ -195,6 +195,20 @@ udp_encap_dpo_unlock (dpo_id_t * dpo) fib_node_unlock (&ue->ue_fib_node); } +u8 * +format_udp_encap_fixup_flags (u8 *s, va_list *args) +{ + udp_encap_fixup_flags_t flags = va_arg (*args, udp_encap_fixup_flags_t); + + if (flags == UDP_ENCAP_FIXUP_NONE) + return format (s, "none"); + + if (flags & UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY) + s = format (s, "%s", "src-port-is-entropy"); + + return (s); +} + static u8 * format_udp_encap_i (u8 * s, va_list * args) { @@ -210,23 +224,21 @@ format_udp_encap_i (u8 * s, va_list * args) s = format (s, "udp-encap:[%d]: ip-fib-index:%d ", uei, ue->ue_fib_index); if (FIB_PROTOCOL_IP4 == ue->ue_ip_proto) { - s = format (s, "ip:[src:%U, dst:%U] udp:[src:%d, dst:%d]", - format_ip4_address, - &ue->ue_hdrs.ip4.ue_ip4.src_address, - format_ip4_address, - &ue->ue_hdrs.ip4.ue_ip4.dst_address, + s = format (s, "ip:[src:%U, dst:%U] udp:[src:%d, dst:%d] flags:%U", + format_ip4_address, &ue->ue_hdrs.ip4.ue_ip4.src_address, + format_ip4_address, &ue->ue_hdrs.ip4.ue_ip4.dst_address, clib_net_to_host_u16 (ue->ue_hdrs.ip4.ue_udp.src_port), - clib_net_to_host_u16 (ue->ue_hdrs.ip4.ue_udp.dst_port)); + clib_net_to_host_u16 (ue->ue_hdrs.ip4.ue_udp.dst_port), + format_udp_encap_fixup_flags, ue->ue_flags); } else { - s = format (s, "ip:[src:%U, dst:%U] udp:[src:%d dst:%d]", - format_ip6_address, - &ue->ue_hdrs.ip6.ue_ip6.src_address, - format_ip6_address, - &ue->ue_hdrs.ip6.ue_ip6.dst_address, + s = format (s, "ip:[src:%U, dst:%U] udp:[src:%d dst:%d] flags:%U", + format_ip6_address, &ue->ue_hdrs.ip6.ue_ip6.src_address, + format_ip6_address, &ue->ue_hdrs.ip6.ue_ip6.dst_address, clib_net_to_host_u16 (ue->ue_hdrs.ip6.ue_udp.src_port), - clib_net_to_host_u16 (ue->ue_hdrs.ip6.ue_udp.dst_port)); + clib_net_to_host_u16 (ue->ue_hdrs.ip6.ue_udp.dst_port), + format_udp_encap_fixup_flags, ue->ue_flags); } vlib_get_combined_counter (&(udp_encap_counters), uei, &to); s = format (s, " to:[%Ld:%Ld]]", to.packets, to.bytes); @@ -553,10 +565,12 @@ udp_encap_show (vlib_main_t * vm, /* *INDENT-OFF* */ VLIB_CLI_COMMAND (udp_encap_add_command, static) = { .path = "udp encap", - .short_help = "udp encap [add|del] [] [src-port-is-entropy] [table-id ]", + .short_help = "udp encap [add|del] [] " + " [src-port-is-entropy] [table-id
]", .function = udp_encap_cli, .is_mp_safe = 1, }; + VLIB_CLI_COMMAND (udp_encap_show_command, static) = { .path = "show udp encap", .short_help = "show udp encap [ID]", diff --git a/src/vnet/udp/udp_encap.h b/src/vnet/udp/udp_encap.h index 648e3b59e6d..c8b42ffa92c 100644 --- a/src/vnet/udp/udp_encap.h +++ b/src/vnet/udp/udp_encap.h @@ -115,6 +115,7 @@ extern index_t udp_encap_add_and_lock (fib_protocol_t proto, extern void udp_encap_lock (index_t uei); extern void udp_encap_unlock (index_t uei); extern u8 *format_udp_encap (u8 * s, va_list * args); +extern u8 *format_udp_encap_fixup_flags (u8 *s, va_list *args); extern void udp_encap_contribute_forwarding (index_t uei, dpo_proto_t proto, dpo_id_t * dpo); diff --git a/src/vnet/udp/udp_encap_node.c b/src/vnet/udp/udp_encap_node.c index 1ebe79532f4..639ac23dfef 100644 --- a/src/vnet/udp/udp_encap_node.c +++ b/src/vnet/udp/udp_encap_node.c @@ -20,12 +20,16 @@ typedef struct udp4_encap_trace_t_ { udp_header_t udp; ip4_header_t ip; + u32 flow_hash; + udp_encap_fixup_flags_t flags; } udp4_encap_trace_t; typedef struct udp6_encap_trace_t_ { udp_header_t udp; ip6_header_t ip; + u32 flow_hash; + udp_encap_fixup_flags_t flags; } udp6_encap_trace_t; extern vlib_combined_counter_main_t udp_encap_counters; @@ -35,13 +39,16 @@ format_udp4_encap_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + u32 indent = format_get_indent (s); udp4_encap_trace_t *t; t = va_arg (*args, udp4_encap_trace_t *); - s = format (s, "%U\n %U", - format_ip4_header, &t->ip, sizeof (t->ip), - format_udp_header, &t->udp, sizeof (t->udp)); + s = format (s, "flags: %U, flow hash: 0x%08x\n%U%U\n%U%U", + format_udp_encap_fixup_flags, t->flags, t->flow_hash, + format_white_space, indent, format_ip4_header, &t->ip, + sizeof (t->ip), format_white_space, indent, format_udp_header, + &t->udp, sizeof (t->udp)); return (s); } @@ -50,13 +57,16 @@ format_udp6_encap_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + u32 indent = format_get_indent (s); udp6_encap_trace_t *t; t = va_arg (*args, udp6_encap_trace_t *); - s = format (s, "%U\n %U", - format_ip6_header, &t->ip, sizeof (t->ip), - format_udp_header, &t->udp, sizeof (t->udp)); + s = format (s, "flags: %U, flow hash: 0x%08x\n%U%U\n%U%U", + format_udp_encap_fixup_flags, t->flags, t->flow_hash, + format_white_space, indent, format_ip6_header, &t->ip, + sizeof (t->ip), format_white_space, indent, format_udp_header, + &t->udp, sizeof (t->udp)); return (s); } @@ -127,13 +137,16 @@ udp_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, sizeof (udp_header_t) + sizeof (ip6_header_t); ip_udp_encap_two (vm, b0, b1, (u8 *) &ue0->ue_hdrs, (u8 *) &ue1->ue_hdrs, n_bytes, encap_family, - payload_family); + payload_family, ue0->ue_flags, ue1->ue_flags); + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { udp6_encap_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); tr->udp = ue0->ue_hdrs.ip6.ue_udp; tr->ip = ue0->ue_hdrs.ip6.ue_ip6; + tr->flags = ue0->ue_flags; + tr->flow_hash = vnet_buffer (b0)->ip.flow_hash; } if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) { @@ -141,6 +154,8 @@ udp_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_add_trace (vm, node, b1, sizeof (*tr)); tr->udp = ue1->ue_hdrs.ip6.ue_udp; tr->ip = ue1->ue_hdrs.ip6.ue_ip6; + tr->flags = ue1->ue_flags; + tr->flow_hash = vnet_buffer (b1)->ip.flow_hash; } } else @@ -150,7 +165,7 @@ udp_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, ip_udp_encap_two (vm, b0, b1, (u8 *) &ue0->ue_hdrs, (u8 *) &ue1->ue_hdrs, n_bytes, encap_family, - payload_family); + payload_family, ue0->ue_flags, ue1->ue_flags); if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { @@ -158,6 +173,8 @@ udp_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_add_trace (vm, node, b0, sizeof (*tr)); tr->udp = ue0->ue_hdrs.ip4.ue_udp; tr->ip = ue0->ue_hdrs.ip4.ue_ip4; + tr->flags = ue0->ue_flags; + tr->flow_hash = vnet_buffer (b0)->ip.flow_hash; } if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) { @@ -165,6 +182,8 @@ udp_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_add_trace (vm, node, b1, sizeof (*tr)); tr->udp = ue1->ue_hdrs.ip4.ue_udp; tr->ip = ue1->ue_hdrs.ip4.ue_ip4; + tr->flags = ue1->ue_flags; + tr->flow_hash = vnet_buffer (b1)->ip.flow_hash; } } @@ -208,7 +227,7 @@ udp_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, const u8 n_bytes = sizeof (udp_header_t) + sizeof (ip6_header_t); ip_udp_encap_one (vm, b0, (u8 *) &ue0->ue_hdrs.ip6, n_bytes, - encap_family, payload_family); + encap_family, payload_family, ue0->ue_flags); if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { @@ -216,6 +235,8 @@ udp_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_add_trace (vm, node, b0, sizeof (*tr)); tr->udp = ue0->ue_hdrs.ip6.ue_udp; tr->ip = ue0->ue_hdrs.ip6.ue_ip6; + tr->flags = ue0->ue_flags; + tr->flow_hash = vnet_buffer (b0)->ip.flow_hash; } } else @@ -224,7 +245,7 @@ udp_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, sizeof (udp_header_t) + sizeof (ip4_header_t); ip_udp_encap_one (vm, b0, (u8 *) &ue0->ue_hdrs.ip4, n_bytes, - encap_family, payload_family); + encap_family, payload_family, ue0->ue_flags); if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { @@ -232,6 +253,8 @@ udp_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_add_trace (vm, node, b0, sizeof (*tr)); tr->udp = ue0->ue_hdrs.ip4.ue_udp; tr->ip = ue0->ue_hdrs.ip4.ue_ip4; + tr->flags = ue0->ue_flags; + tr->flow_hash = vnet_buffer (b0)->ip.flow_hash; } } diff --git a/src/vnet/udp/udp_inlines.h b/src/vnet/udp/udp_inlines.h index 025809e1873..f0dd44f48b5 100644 --- a/src/vnet/udp/udp_inlines.h +++ b/src/vnet/udp/udp_inlines.h @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include always_inline void * vlib_buffer_push_udp (vlib_buffer_t * b, u16 sp, u16 dp, u8 offload_csum) @@ -42,8 +45,39 @@ vlib_buffer_push_udp (vlib_buffer_t * b, u16 sp, u16 dp, u8 offload_csum) return uh; } +/* + * Encode udp source port entropy value per + * https://datatracker.ietf.org/doc/html/rfc7510#section-3 + */ +always_inline u16 +ip_udp_sport_entropy (vlib_buffer_t *b0) +{ + u16 port = clib_host_to_net_u16 (0x03 << 14); + port |= vnet_buffer (b0)->ip.flow_hash & 0xffff; + return port; +} + +always_inline u32 +ip_udp_compute_flow_hash (vlib_buffer_t *b0, u8 is_ip4) +{ + ip4_header_t *ip4; + ip6_header_t *ip6; + + if (is_ip4) + { + ip4 = (ip4_header_t *) (b0->data + vnet_buffer (b0)->l3_hdr_offset); + return ip4_compute_flow_hash (ip4, IP_FLOW_HASH_DEFAULT); + } + else + { + ip6 = (ip6_header_t *) (b0->data + vnet_buffer (b0)->l3_hdr_offset); + return ip6_compute_flow_hash (ip6, IP_FLOW_HASH_DEFAULT); + } +} + always_inline void -ip_udp_fixup_one (vlib_main_t * vm, vlib_buffer_t * b0, u8 is_ip4) +ip_udp_fixup_one (vlib_main_t *vm, vlib_buffer_t *b0, u8 is_ip4, + u8 sport_entropy) { u16 new_l0; udp_header_t *udp0; @@ -71,6 +105,9 @@ ip_udp_fixup_one (vlib_main_t * vm, vlib_buffer_t * b0, u8 is_ip4) new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) - sizeof (*ip0)); udp0->length = new_l0; + + if (sport_entropy) + udp0->src_port = ip_udp_sport_entropy (b0); } else { @@ -87,6 +124,9 @@ ip_udp_fixup_one (vlib_main_t * vm, vlib_buffer_t * b0, u8 is_ip4) udp0 = (udp_header_t *) (ip0 + 1); udp0->length = new_l0; + if (sport_entropy) + udp0->src_port = ip_udp_sport_entropy (b0); + udp0->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0); ASSERT (bogus0 == 0); @@ -99,13 +139,20 @@ ip_udp_fixup_one (vlib_main_t * vm, vlib_buffer_t * b0, u8 is_ip4) always_inline void ip_udp_encap_one (vlib_main_t *vm, vlib_buffer_t *b0, u8 *ec0, word ec_len, ip_address_family_t encap_family, - ip_address_family_t payload_family) + ip_address_family_t payload_family, + udp_encap_fixup_flags_t flags) { + u8 sport_entropy = (flags & UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY) != 0; if (payload_family < N_AF) { vnet_calc_checksums_inline (vm, b0, payload_family == AF_IP4, payload_family == AF_IP6); + + /* Сalculate flow hash to be used for entropy */ + if (sport_entropy && 0 == vnet_buffer (b0)->ip.flow_hash) + vnet_buffer (b0)->ip.flow_hash = + ip_udp_compute_flow_hash (b0, payload_family == AF_IP4); } vlib_buffer_advance (b0, -ec_len); @@ -118,7 +165,7 @@ ip_udp_encap_one (vlib_main_t *vm, vlib_buffer_t *b0, u8 *ec0, word ec_len, /* Apply the encap string. */ clib_memcpy_fast (ip0, ec0, ec_len); - ip_udp_fixup_one (vm, b0, 1); + ip_udp_fixup_one (vm, b0, 1, sport_entropy); } else { @@ -128,7 +175,7 @@ ip_udp_encap_one (vlib_main_t *vm, vlib_buffer_t *b0, u8 *ec0, word ec_len, /* Apply the encap string. */ clib_memcpy_fast (ip0, ec0, ec_len); - ip_udp_fixup_one (vm, b0, 0); + ip_udp_fixup_one (vm, b0, 0, sport_entropy); } } @@ -136,16 +183,28 @@ always_inline void ip_udp_encap_two (vlib_main_t *vm, vlib_buffer_t *b0, vlib_buffer_t *b1, u8 *ec0, u8 *ec1, word ec_len, ip_address_family_t encap_family, - ip_address_family_t payload_family) + ip_address_family_t payload_family, + udp_encap_fixup_flags_t flags0, + udp_encap_fixup_flags_t flags1) { u16 new_l0, new_l1; udp_header_t *udp0, *udp1; int payload_ip4 = (payload_family == AF_IP4); + int sport_entropy0 = (flags0 & UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY) != 0; + int sport_entropy1 = (flags1 & UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY) != 0; if (payload_family < N_AF) { vnet_calc_checksums_inline (vm, b0, payload_ip4, !payload_ip4); vnet_calc_checksums_inline (vm, b1, payload_ip4, !payload_ip4); + + /* Сalculate flow hash to be used for entropy */ + if (sport_entropy0 && 0 == vnet_buffer (b0)->ip.flow_hash) + vnet_buffer (b0)->ip.flow_hash = + ip_udp_compute_flow_hash (b0, payload_ip4); + if (sport_entropy1 && 0 == vnet_buffer (b1)->ip.flow_hash) + vnet_buffer (b1)->ip.flow_hash = + ip_udp_compute_flow_hash (b1, payload_ip4); } vlib_buffer_advance (b0, -ec_len); @@ -195,6 +254,11 @@ ip_udp_encap_two (vlib_main_t *vm, vlib_buffer_t *b0, vlib_buffer_t *b1, sizeof (*ip1)); udp0->length = new_l0; udp1->length = new_l1; + + if (sport_entropy0) + udp0->src_port = ip_udp_sport_entropy (b0); + if (sport_entropy1) + udp1->src_port = ip_udp_sport_entropy (b1); } else { @@ -222,6 +286,11 @@ ip_udp_encap_two (vlib_main_t *vm, vlib_buffer_t *b0, vlib_buffer_t *b1, udp0->length = new_l0; udp1->length = new_l1; + if (sport_entropy0) + udp0->src_port = ip_udp_sport_entropy (b0); + if (sport_entropy1) + udp1->src_port = ip_udp_sport_entropy (b1); + udp0->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0, &bogus0); udp1->checksum = diff --git a/src/vnet/vxlan-gpe/encap.c b/src/vnet/vxlan-gpe/encap.c index 35a5529e80b..824f10f4fb7 100644 --- a/src/vnet/vxlan-gpe/encap.c +++ b/src/vnet/vxlan-gpe/encap.c @@ -96,7 +96,7 @@ vxlan_gpe_encap_one_inline (vxlan_gpe_main_t *ngm, vlib_buffer_t *b0, ASSERT (sizeof (ip6_vxlan_gpe_header_t) == 56); ip_udp_encap_one (ngm->vlib_main, b0, t0->rewrite, t0->rewrite_size, af, - N_AF); + N_AF, UDP_ENCAP_FIXUP_NONE); next0[0] = t0->encap_next_node; } @@ -123,9 +123,9 @@ vxlan_gpe_encap_two_inline (vxlan_gpe_main_t *ngm, vlib_buffer_t *b0, ASSERT (sizeof (ip6_vxlan_gpe_header_t) == 56); ip_udp_encap_one (ngm->vlib_main, b0, t0->rewrite, t0->rewrite_size, af, - N_AF); + N_AF, UDP_ENCAP_FIXUP_NONE); ip_udp_encap_one (ngm->vlib_main, b1, t1->rewrite, t1->rewrite_size, af, - N_AF); + N_AF, UDP_ENCAP_FIXUP_NONE); next0[0] = next1[0] = t0->encap_next_node; } diff --git a/test/test_udp.py b/test/test_udp.py index c7e97c641b8..ebc99e85388 100644 --- a/test/test_udp.py +++ b/test/test_udp.py @@ -25,6 +25,8 @@ from scapy.layers.inet6 import IPv6 from scapy.contrib.mpls import MPLS NUM_PKTS = 67 +ENTROPY_PORT_MIN = 0x3 << 14 +ENTROPY_PORT_MAX = 0xFFFF @tag_fixme_vpp_workers @@ -78,16 +80,22 @@ class TestUdpEncap(VppTestCase): i.admin_down() super(TestUdpEncap, self).tearDown() - def validate_outer4(self, rx, encap_obj): + def validate_outer4(self, rx, encap_obj, sport_entropy=False): self.assertEqual(rx[IP].src, encap_obj.src_ip_s) self.assertEqual(rx[IP].dst, encap_obj.dst_ip_s) - self.assertEqual(rx[UDP].sport, encap_obj.src_port) + if sport_entropy: + self.assert_in_range(rx[UDP].sport, ENTROPY_PORT_MIN, ENTROPY_PORT_MAX) + else: + self.assertEqual(rx[UDP].sport, encap_obj.src_port) self.assertEqual(rx[UDP].dport, encap_obj.dst_port) - def validate_outer6(self, rx, encap_obj): + def validate_outer6(self, rx, encap_obj, sport_entropy=False): self.assertEqual(rx[IPv6].src, encap_obj.src_ip_s) self.assertEqual(rx[IPv6].dst, encap_obj.dst_ip_s) - self.assertEqual(rx[UDP].sport, encap_obj.src_port) + if sport_entropy: + self.assert_in_range(rx[UDP].sport, ENTROPY_PORT_MIN, ENTROPY_PORT_MAX) + else: + self.assertEqual(rx[UDP].sport, encap_obj.src_port) self.assertEqual(rx[UDP].dport, encap_obj.dst_port) def validate_inner4(self, rx, tx, ttl=None): @@ -343,6 +351,193 @@ class TestUdpEncap(VppTestCase): self.validate_inner4(p, p_4omo4, ttl=63) self.assertEqual(udp_encap_1.get_stats()["packets"], 2 * NUM_PKTS) + def test_udp_encap_entropy(self): + """UDP Encap src port entropy test""" + + # + # construct a UDP encap object through each of the peers + # v4 through the first two peers, v6 through the second. + # use zero source port to enable entropy per rfc7510. + # + udp_encap_0 = VppUdpEncap(self, self.pg0.local_ip4, self.pg0.remote_ip4, 0, 440) + udp_encap_1 = VppUdpEncap( + self, self.pg1.local_ip4, self.pg1.remote_ip4, 0, 441, table_id=1 + ) + udp_encap_2 = VppUdpEncap( + self, self.pg2.local_ip6, self.pg2.remote_ip6, 0, 442, table_id=2 + ) + udp_encap_3 = VppUdpEncap( + self, self.pg3.local_ip6, self.pg3.remote_ip6, 0, 443, table_id=3 + ) + udp_encap_0.add_vpp_config() + udp_encap_1.add_vpp_config() + udp_encap_2.add_vpp_config() + udp_encap_3.add_vpp_config() + + self.logger.info(self.vapi.cli("sh udp encap")) + + self.assertTrue(find_udp_encap(self, udp_encap_0)) + self.assertTrue(find_udp_encap(self, udp_encap_1)) + self.assertTrue(find_udp_encap(self, udp_encap_2)) + self.assertTrue(find_udp_encap(self, udp_encap_3)) + + # + # Routes via each UDP encap object - all combinations of v4 and v6. + # + route_4o4 = VppIpRoute( + self, + "1.1.0.1", + 24, + [ + VppRoutePath( + "0.0.0.0", + 0xFFFFFFFF, + type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP, + next_hop_id=udp_encap_0.id, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP4, + ) + ], + table_id=1, + ) + route_4o6 = VppIpRoute( + self, + "1.1.2.1", + 32, + [ + VppRoutePath( + "0.0.0.0", + 0xFFFFFFFF, + type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP, + next_hop_id=udp_encap_2.id, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP4, + ) + ], + ) + route_6o4 = VppIpRoute( + self, + "2001::1", + 128, + [ + VppRoutePath( + "0.0.0.0", + 0xFFFFFFFF, + type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP, + next_hop_id=udp_encap_1.id, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP6, + ) + ], + ) + route_6o6 = VppIpRoute( + self, + "2001::3", + 128, + [ + VppRoutePath( + "0.0.0.0", + 0xFFFFFFFF, + type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP, + next_hop_id=udp_encap_3.id, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP6, + ) + ], + ) + route_4o4.add_vpp_config() + route_4o6.add_vpp_config() + route_6o6.add_vpp_config() + route_6o4.add_vpp_config() + + # + # 4o4 encap + # + p_4o4 = [] + for i in range(NUM_PKTS): + p_4o4.append( + Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) + / IP(src="2.2.2.2", dst="1.1.0.1") + / UDP(sport=1234 + i, dport=1234) + / Raw(b"\xa5" * 100) + ) + rx = self.send_and_expect(self.pg1, p_4o4, self.pg0) + sports = set() + for i, p in enumerate(rx): + self.validate_outer4(p, udp_encap_0, True) + sports.add(p["UDP"].sport) + p = IP(p["UDP"].payload.load) + self.validate_inner4(p, p_4o4[i]) + self.assertEqual(udp_encap_0.get_stats()["packets"], NUM_PKTS) + self.assertGreater( + len(sports), 1, "source port {} is not an entropy value".format(sports) + ) + + # + # 4o6 encap + # + p_4o6 = [] + for i in range(NUM_PKTS): + p_4o6.append( + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) + / IP(src="2.2.2.2", dst="1.1.2.1") + / UDP(sport=1234 + i, dport=1234) + / Raw(b"\xa5" * 100) + ) + rx = self.send_and_expect(self.pg0, p_4o6, self.pg2) + sports = set() + for p in rx: + self.validate_outer6(p, udp_encap_2, True) + sports.add(p["UDP"].sport) + p = IP(p["UDP"].payload.load) + self.validate_inner4(p, p_4o6[i]) + self.assertEqual(udp_encap_2.get_stats()["packets"], NUM_PKTS) + self.assertGreater( + len(sports), 1, "source port {} is not an entropy value".format(sports) + ) + + # + # 6o4 encap + # + p_6o4 = [] + for i in range(NUM_PKTS): + p_6o4.append( + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) + / IPv6(src="2001::100", dst="2001::1") + / UDP(sport=1234 + i, dport=1234) + / Raw(b"\xa5" * 100) + ) + rx = self.send_and_expect(self.pg0, p_6o4, self.pg1) + sports = set() + for p in rx: + self.validate_outer4(p, udp_encap_1, True) + sports.add(p["UDP"].sport) + p = IPv6(p["UDP"].payload.load) + self.validate_inner6(p, p_6o4[i]) + self.assertEqual(udp_encap_1.get_stats()["packets"], NUM_PKTS) + self.assertGreater( + len(sports), 1, "source port {} is not an entropy value".format(sports) + ) + + # + # 6o6 encap + # + p_6o6 = [] + for i in range(NUM_PKTS): + p_6o6.append( + Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) + / IPv6(src="2001::100", dst="2001::3") + / UDP(sport=1234 + i, dport=1234) + / Raw(b"\xa5" * 100) + ) + rx = self.send_and_expect(self.pg0, p_6o6, self.pg3) + sports = set() + for p in rx: + self.validate_outer6(p, udp_encap_3, True) + sports.add(p["UDP"].sport) + p = IPv6(p["UDP"].payload.load) + self.validate_inner6(p, p_6o6[i]) + self.assertEqual(udp_encap_3.get_stats()["packets"], NUM_PKTS) + self.assertGreater( + len(sports), 1, "source port {} is not an entropy value".format(sports) + ) + def test_udp_decap(self): """UDP Decap test""" #