From: Masih Nilforoush Date: Thu, 27 Feb 2025 13:44:44 +0000 (+0100) Subject: gre: Add support for GRE keys in the GRE plugin X-Git-Tag: v26.02-rc0~217 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F40%2F42440%2F25;p=vpp.git gre: Add support for GRE keys in the GRE plugin The added feature enables the GRE plugin to create tunnels between the same endpoints, distinguishing them by the "key" value. It uses the standard 'key' parameter in the GRE header. Changes have been made to add support for CLI and API to create tunnels with a "key" value. The CLI syntax is as follows: create gre tunnel src src_IP_Address dst dst_IP_Address key key_value All existing GRE functionalities, such as tunnel type and mode, remain unchanged. GRE key support has been implemented for all non-ERSPAN tunnel types, including both IPv4 and IPv6. Additionally, modifications were made to the GRE packet header, data structure, and inbound/outbound packet processing to accommodate key configuration through CLI and API. Type: feature Change-Id: I222d585007fa264e7cc12c79d6ba9c63c044f133 Signed-off-by: Masih Nilforoush --- diff --git a/src/plugins/gre/FEATURE.yaml b/src/plugins/gre/FEATURE.yaml index 4b35b870dc3..029a59a40f6 100644 --- a/src/plugins/gre/FEATURE.yaml +++ b/src/plugins/gre/FEATURE.yaml @@ -6,8 +6,7 @@ features: - Encap/Decap flags to control the copying of DSCP, ECN, DF from overlay to underlay and vice-versa. - L2 tunnels -missing: - - GRE keys + - GRE keys for IPv4 and IPv6 description: "An implementation of Generic Routing Encapsulation (GRE)" state: production properties: [API, CLI, MULTITHREAD] diff --git a/src/plugins/gre/gre.api b/src/plugins/gre/gre.api index 9c69ba4007d..1f680c8d895 100644 --- a/src/plugins/gre/gre.api +++ b/src/plugins/gre/gre.api @@ -20,6 +20,17 @@ import "vnet/interface_types.api"; import "vnet/tunnel/tunnel_types.api"; import "vnet/ip/ip_types.api"; +/* Enable the in-progress messages */ +option status = "in_progress"; + +/* + * Define the service relationships + */ +service { + rpc gre_tunnel_dump returns gre_tunnel_dump_reply events gre_tunnel_details; + rpc gre_tunnel_dump_v2 returns gre_tunnel_dump_v2_reply events gre_tunnel_details_v2; +}; + /** \brief A GRE tunnel type */ enum gre_tunnel_type : u8 @@ -55,6 +66,32 @@ typedef gre_tunnel vl_api_address_t dst; }; +/** \brief A composite type uniquely defining a GRE tunnel with key support. + @param type - tunnel type (see enum definition), 0: L3, 1: TEB, 2: ERSPAN + @param mode - P2P or P2MP + @param flags - to control encap/decap behaviour + @param session_id - session for ERSPAN tunnel, range 0-1023 + @param instance - optional unique custom device instance, else ~0. + @param outer_table_id - Encap FIB table ID + @param sw_if_index - ignored on create/delete, present in details. + @param src - Source IP address + @param dst - Destination IP address, can be multicast + @param key - GRE key value (RFC 2890), 0 for no key +*/ +typedef gre_tunnel_v2 +{ + vl_api_gre_tunnel_type_t type; + vl_api_tunnel_mode_t mode; + vl_api_tunnel_encap_decap_flags_t flags; + u16 session_id; + u32 instance; + u32 outer_table_id; + vl_api_interface_index_t sw_if_index; + vl_api_address_t src; + vl_api_address_t dst; + u32 key; +}; + /** \brief Add or delete a single GRE tunnel. @param client_index - opaque cookie to identify the sender. @param context - sender context, to match reply w/ request. @@ -81,6 +118,32 @@ define gre_tunnel_add_del_reply vl_api_interface_index_t sw_if_index; }; +/** \brief Add or delete a single GRE tunnel with key support. + @param client_index - opaque cookie to identify the sender. + @param context - sender context, to match reply w/ request. + @param is_add - add if true, delete if false. + @param tunnel - tunnel definition to add or delete (with key field). +*/ +define gre_tunnel_add_del_v2 +{ + u32 client_index; + u32 context; + bool is_add; + vl_api_gre_tunnel_v2_t tunnel; +}; + +/** \brief Add or delete a single GRE tunnel with key support. + @param context - sender context, to match reply w/ request. + @param retval - return code for the request. + @param sw_if_index - the interface corresponding to the affected tunnel. +*/ +define gre_tunnel_add_del_v2_reply +{ + u32 context; + i32 retval; + vl_api_interface_index_t sw_if_index; +}; + /** \brief Dump details of all or just a single GRE tunnel. @param client_index - opaque cookie to identify the sender. @param context - sender context, to match reply w/ request. @@ -93,6 +156,29 @@ define gre_tunnel_dump vl_api_interface_index_t sw_if_index; }; +/** \brief Reply for gre_tunnel_dump - empty since actual data is sent via gre_tunnel_details. + @param context - sender context, to match reply w/ request. + @param retval - return code for the request. +*/ +define gre_tunnel_dump_reply +{ + u32 context; + i32 retval; +}; + +/** \brief Dump details of all or just a single GRE tunnel with key support. + @param client_index - opaque cookie to identify the sender. + @param context - sender context, to match reply w/ request. + @param sw_if_index - filter for tunnel of this interface index, ~0 for all. +*/ +autoreply define gre_tunnel_dump_v2 +{ + u32 client_index; + u32 context; + vl_api_interface_index_t sw_if_index; +}; + + /** \brief Details response for one of the requested GRE tunnels. @param context - sender context, to match reply w/ request. @param tunnel - definition of the dumped tunnel. @@ -103,6 +189,16 @@ define gre_tunnel_details vl_api_gre_tunnel_t tunnel; }; +/** \brief Details response for one of the requested GRE tunnels with key support. + @param context - sender context, to match reply w/ request. + @param tunnel - definition of the dumped tunnel (with key field). +*/ +define gre_tunnel_details_v2 +{ + u32 context; + vl_api_gre_tunnel_v2_t tunnel; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/plugins/gre/gre.c b/src/plugins/gre/gre.c index ce11ee9ecb2..21a628b4493 100644 --- a/src/plugins/gre/gre.c +++ b/src/plugins/gre/gre.c @@ -232,7 +232,11 @@ gre_build_rewrite (vnet_main_t *vnm, u32 sw_if_index, vnet_link_t link_type, if (!is_ipv6) { - vec_validate (rewrite, sizeof (*h4) - 1); + /* Allocate space for maximum header size including key */ + if (gre_key_is_valid (t->gre_key)) + vec_validate (rewrite, sizeof (*h4) + sizeof (gre_key_t) - 1); + else + vec_validate (rewrite, sizeof (*h4) - 1); h4 = (ip4_and_gre_header_t *) rewrite; gre = &h4->gre; h4->ip4.ip_version_and_header_length = 0x45; @@ -245,7 +249,11 @@ gre_build_rewrite (vnet_main_t *vnm, u32 sw_if_index, vnet_link_t link_type, } else { - vec_validate (rewrite, sizeof (*h6) - 1); + /* Allocate space for maximum header size including key */ + if (gre_key_is_valid (t->gre_key)) + vec_validate (rewrite, sizeof (*h6) + sizeof (gre_key_t) - 1); + else + vec_validate (rewrite, sizeof (*h6) - 1); h6 = (ip6_and_gre_header_t *) rewrite; gre = &h6->gre; h6->ip6.ip_version_traffic_class_and_flow_label = @@ -265,9 +273,18 @@ gre_build_rewrite (vnet_main_t *vnm, u32 sw_if_index, vnet_link_t link_type, gre->flags_and_version = clib_host_to_net_u16 (GRE_FLAGS_SEQUENCE); } else - gre->protocol = - clib_host_to_net_u16 (gre_proto_from_vnet_link (link_type)); - + { + gre->protocol = + clib_host_to_net_u16 (gre_proto_from_vnet_link (link_type)); + gre->flags_and_version = 0; // Clear flags first + /* Add key only for non-ERSPAN tunnels */ + if (gre_key_is_valid (t->gre_key)) + { + gre_header_with_key_t *grek = (gre_header_with_key_t *) gre; + grek->flags_and_version = clib_host_to_net_u16 (GRE_FLAGS_KEY); + grek->key = clib_host_to_net_u32 (t->gre_key); + } + } return (rewrite); } diff --git a/src/plugins/gre/gre.h b/src/plugins/gre/gre.h index ce57454f9b7..03f0df163da 100644 --- a/src/plugins/gre/gre.h +++ b/src/plugins/gre/gre.h @@ -31,7 +31,7 @@ extern vnet_hw_interface_class_t mgre_hw_interface_class; typedef enum { -#define gre_error(n,s) GRE_ERROR_##n, +#define gre_error(n, s) GRE_ERROR_##n, #include #undef gre_error GRE_N_ERROR, @@ -45,10 +45,10 @@ typedef enum * and output of mirrored packet from a L2 network only. There is * no support for receiving ERSPAN packets from a GRE ERSPAN tunnel */ -#define foreach_gre_tunnel_type \ - _(L3, "L3") \ - _(TEB, "TEB") \ - _(ERSPAN, "ERSPAN") \ +#define foreach_gre_tunnel_type \ + _ (L3, "L3") \ + _ (TEB, "TEB") \ + _ (ERSPAN, "ERSPAN") /** * @brief The GRE tunnel type @@ -60,8 +60,22 @@ typedef enum gre_tunnel_type_t_ #undef _ } __clib_packed gre_tunnel_type_t; -extern u8 *format_gre_tunnel_type (u8 * s, va_list * args); +/** + * @brief GRE key type (RFC 2890) + */ +typedef u32 gre_key_t; + +/** + * @brief Check if a GRE key is valid (non-zero) + */ +#define gre_key_is_valid(_key) ((_key) != 0) +extern u8 *format_gre_tunnel_type (u8 *s, va_list *args); + +/** + * @brief Format a GRE key for display + */ +format_function_t format_gre_key; /** * A GRE payload protocol registration @@ -94,15 +108,16 @@ typedef struct gre_tunnel_key_common_t_ struct { u32 fib_index; + gre_key_t gre_key; u16 session_id; gre_tunnel_type_t type; tunnel_mode_t mode; }; - u64 as_u64; + u64 as_u64[2]; }; -} gre_tunnel_key_common_t; +} __clib_packed gre_tunnel_key_common_t; -STATIC_ASSERT_SIZEOF (gre_tunnel_key_common_t, sizeof (u64)); +STATIC_ASSERT_SIZEOF (gre_tunnel_key_common_t, 2 * sizeof (u64)); /** * @brief Key for a IPv4 GRE Tunnel @@ -126,7 +141,7 @@ typedef struct gre_tunnel_key4_t_ gre_tunnel_key_common_t gtk_common; } __attribute__ ((packed)) gre_tunnel_key4_t; -STATIC_ASSERT_SIZEOF (gre_tunnel_key4_t, 2 * sizeof (u64)); +STATIC_ASSERT_SIZEOF (gre_tunnel_key4_t, 3 * sizeof (u64)); /** * @brief Key for a IPv6 GRE Tunnel @@ -144,7 +159,7 @@ typedef struct gre_tunnel_key6_t_ gre_tunnel_key_common_t gtk_common; } __attribute__ ((packed)) gre_tunnel_key6_t; -STATIC_ASSERT_SIZEOF (gre_tunnel_key6_t, 5 * sizeof (u64)); +STATIC_ASSERT_SIZEOF (gre_tunnel_key6_t, 6 * sizeof (u64)); /** * Union of the two possible key types @@ -205,6 +220,8 @@ typedef struct u32 sw_if_index; gre_tunnel_type_t type; tunnel_mode_t mode; + gre_key_t gre_key; + tunnel_encap_decap_flags_t flags; /** @@ -220,14 +237,14 @@ typedef struct /** * GRE header sequence number (SN) used for ERSPAN type 2 header, must be * bumped automically to be thread safe. As multiple GRE tunnels are created - * for the same fib-idx/DIP/SIP with different ERSPAN session number, they all - * share the same SN which is kept per FIB/DIP/SIP, as specified by RFC2890. + * for the same fib-idx/DIP/SIP with different ERSPAN session number, they + * all share the same SN which is kept per FIB/DIP/SIP, as specified by + * RFC2890. */ gre_sn_t *gre_sn; - - u32 dev_instance; /* Real device instance in tunnel vector */ - u32 user_instance; /* Instance name being shown to user */ + u32 dev_instance; /* Real device instance in tunnel vector */ + u32 user_instance; /* Instance name being shown to user */ } gre_tunnel_t; typedef struct @@ -307,7 +324,7 @@ typedef CLIB_PACKED (struct { }) ip6_and_gre_header_t; always_inline gre_protocol_info_t * -gre_get_protocol_info (gre_main_t * em, gre_protocol_t protocol) +gre_get_protocol_info (gre_main_t *em, gre_protocol_t protocol) { uword *p = hash_get (em->protocol_info_by_protocol, protocol); return p ? vec_elt_at_index (em->protocol_infos, p[0]) : 0; @@ -315,12 +332,11 @@ gre_get_protocol_info (gre_main_t * em, gre_protocol_t protocol) extern gre_main_t gre_main; -extern clib_error_t *gre_interface_admin_up_down (vnet_main_t * vnm, +extern clib_error_t *gre_interface_admin_up_down (vnet_main_t *vnm, u32 hw_if_index, u32 flags); extern void gre_tunnel_stack (adj_index_t ai); -extern void gre_update_adj (vnet_main_t * vnm, - u32 sw_if_index, adj_index_t ai); +extern void gre_update_adj (vnet_main_t *vnm, u32 sw_if_index, adj_index_t ai); typedef struct mgre_walk_ctx_t_ { @@ -350,12 +366,12 @@ unformat_function_t unformat_gre_protocol_net_byte_order; unformat_function_t unformat_gre_header; unformat_function_t unformat_pg_gre_header; -void -gre_register_input_protocol (vlib_main_t * vm, gre_protocol_t protocol, - u32 node_index, gre_tunnel_type_t tunnel_type); +void gre_register_input_protocol (vlib_main_t *vm, gre_protocol_t protocol, + u32 node_index, + gre_tunnel_type_t tunnel_type); /* manually added to the interface output node in gre.c */ -#define GRE_OUTPUT_NEXT_LOOKUP 1 +#define GRE_OUTPUT_NEXT_LOOKUP 1 typedef struct { @@ -367,61 +383,62 @@ typedef struct ip46_address_t src, dst; u32 outer_table_id; u16 session_id; + gre_key_t gre_key; tunnel_encap_decap_flags_t flags; } vnet_gre_tunnel_add_del_args_t; -extern int vnet_gre_tunnel_add_del (vnet_gre_tunnel_add_del_args_t * a, - u32 * sw_if_indexp); +extern int vnet_gre_tunnel_add_del (vnet_gre_tunnel_add_del_args_t *a, + u32 *sw_if_indexp); static inline void -gre_mk_key4 (ip4_address_t src, - ip4_address_t dst, - u32 fib_index, - gre_tunnel_type_t ttype, - tunnel_mode_t tmode, u16 session_id, gre_tunnel_key4_t * key) +gre_mk_key4 (ip4_address_t src, ip4_address_t dst, u32 fib_index, + gre_tunnel_type_t ttype, tunnel_mode_t tmode, u16 session_id, + gre_key_t gre_key, gre_tunnel_key4_t *key) { + clib_memset (key, 0, sizeof (*key)); // Zero entire structure first key->gtk_src = src; key->gtk_dst = dst; key->gtk_common.type = ttype; key->gtk_common.mode = tmode; key->gtk_common.fib_index = fib_index; key->gtk_common.session_id = session_id; + key->gtk_common.gre_key = gre_key; } static inline int -gre_match_key4 (const gre_tunnel_key4_t * key1, - const gre_tunnel_key4_t * key2) +gre_match_key4 (const gre_tunnel_key4_t *key1, const gre_tunnel_key4_t *key2) { return ((key1->gtk_as_u64 == key2->gtk_as_u64) && - (key1->gtk_common.as_u64 == key2->gtk_common.as_u64)); + (key1->gtk_common.as_u64[0] == key2->gtk_common.as_u64[0]) && + (key1->gtk_common.as_u64[1] == key2->gtk_common.as_u64[1])); } static inline void -gre_mk_key6 (const ip6_address_t * src, - const ip6_address_t * dst, - u32 fib_index, - gre_tunnel_type_t ttype, - tunnel_mode_t tmode, u16 session_id, gre_tunnel_key6_t * key) +gre_mk_key6 (const ip6_address_t *src, const ip6_address_t *dst, u32 fib_index, + gre_tunnel_type_t ttype, tunnel_mode_t tmode, u16 session_id, + gre_key_t gre_key, gre_tunnel_key6_t *key) { + clib_memset (key, 0, sizeof (*key)); // Zero entire structure first key->gtk_src = *src; key->gtk_dst = *dst; key->gtk_common.type = ttype; key->gtk_common.mode = tmode; key->gtk_common.fib_index = fib_index; key->gtk_common.session_id = session_id; + key->gtk_common.gre_key = gre_key; } static inline int -gre_match_key6 (const gre_tunnel_key6_t * key1, - const gre_tunnel_key6_t * key2) +gre_match_key6 (const gre_tunnel_key6_t *key1, const gre_tunnel_key6_t *key2) { return (ip6_address_is_equal (&key1->gtk_src, &key2->gtk_src) && ip6_address_is_equal (&key1->gtk_dst, &key2->gtk_dst) && - (key1->gtk_common.as_u64 == key2->gtk_common.as_u64)); + (key1->gtk_common.as_u64[0] == key2->gtk_common.as_u64[0]) && + (key1->gtk_common.as_u64[1] == key2->gtk_common.as_u64[1])); } static inline void -gre_mk_sn_key (const gre_tunnel_t * gt, gre_sn_key_t * key) +gre_mk_sn_key (const gre_tunnel_t *gt, gre_sn_key_t *key) { key->src = gt->tunnel_src; key->dst = gt->tunnel_dst.fp_addr; diff --git a/src/plugins/gre/gre_api.c b/src/plugins/gre/gre_api.c index 5149f92fb80..168f2e153fa 100644 --- a/src/plugins/gre/gre_api.c +++ b/src/plugins/gre/gre_api.c @@ -114,6 +114,7 @@ vl_api_gre_tunnel_add_del_t_handler (vl_api_gre_tunnel_add_del_t *mp) a->session_id = ntohs (mp->tunnel.session_id); a->outer_table_id = ntohl (mp->tunnel.outer_table_id); a->flags = flags; + a->gre_key = 0; // No key in the v1 API rv = vnet_gre_tunnel_add_del (a, &sw_if_index); @@ -122,6 +123,61 @@ out: ({ rmp->sw_if_index = ntohl (sw_if_index); })); } +static void +vl_api_gre_tunnel_add_del_v2_t_handler (vl_api_gre_tunnel_add_del_v2_t *mp) +{ + vnet_gre_tunnel_add_del_args_t _a = {}, *a = &_a; + vl_api_gre_tunnel_add_del_v2_reply_t *rmp; + tunnel_encap_decap_flags_t flags; + u32 sw_if_index = ~0; + ip46_type_t itype[2]; + int rv = 0; + + itype[0] = ip_address_decode (&mp->tunnel.src, &a->src); + itype[1] = ip_address_decode (&mp->tunnel.dst, &a->dst); + + if (itype[0] != itype[1]) + { + rv = VNET_API_ERROR_INVALID_PROTOCOL; + goto out; + } + + if (ip46_address_is_equal (&a->src, &a->dst)) + { + rv = VNET_API_ERROR_SAME_SRC_DST; + goto out; + } + + rv = gre_tunnel_type_decode (mp->tunnel.type, &a->type); + + if (rv) + goto out; + + rv = tunnel_mode_decode (mp->tunnel.mode, &a->mode); + + if (rv) + goto out; + + rv = tunnel_encap_decap_flags_decode (mp->tunnel.flags, &flags); + + if (rv) + goto out; + + a->is_add = mp->is_add; + a->is_ipv6 = (itype[0] == IP46_TYPE_IP6); + a->instance = ntohl (mp->tunnel.instance); + a->session_id = ntohs (mp->tunnel.session_id); + a->outer_table_id = ntohl (mp->tunnel.outer_table_id); + a->flags = flags; + a->gre_key = ntohl (mp->tunnel.key); // Key field present in v2 API + + rv = vnet_gre_tunnel_add_del (a, &sw_if_index); + +out: + REPLY_MACRO2 (VL_API_GRE_TUNNEL_ADD_DEL_V2_REPLY, + ({ rmp->sw_if_index = ntohl (sw_if_index); })); +} + static void send_gre_tunnel_details (gre_tunnel_t *t, vl_api_gre_tunnel_dump_t *mp) { @@ -145,6 +201,31 @@ send_gre_tunnel_details (gre_tunnel_t *t, vl_api_gre_tunnel_dump_t *mp) })); } +static void +send_gre_tunnel_details_v2 (gre_tunnel_t *t, vl_api_gre_tunnel_dump_v2_t *mp) +{ + vl_api_gre_tunnel_details_v2_t *rmp; + + REPLY_MACRO_DETAILS2 ( + VL_API_GRE_TUNNEL_DETAILS_V2, ({ + ip_address_encode (&t->tunnel_src, IP46_TYPE_ANY, &rmp->tunnel.src); + ip_address_encode (&t->tunnel_dst.fp_addr, IP46_TYPE_ANY, + &rmp->tunnel.dst); + + rmp->tunnel.outer_table_id = htonl ( + fib_table_get_table_id (t->outer_fib_index, t->tunnel_dst.fp_proto)); + + rmp->tunnel.type = gre_tunnel_type_encode (t->type); + rmp->tunnel.mode = tunnel_mode_encode (t->mode); + rmp->tunnel.flags = tunnel_encap_decap_flags_encode (t->flags); + rmp->tunnel.instance = htonl (t->user_instance); + rmp->tunnel.sw_if_index = htonl (t->sw_if_index); + rmp->tunnel.session_id = htons (t->session_id); + rmp->tunnel.key = + htonl (t->gre_key); // Include the GRE key in the v2 API + })); +} + static void vl_api_gre_tunnel_dump_t_handler (vl_api_gre_tunnel_dump_t *mp) { @@ -179,6 +260,40 @@ vl_api_gre_tunnel_dump_t_handler (vl_api_gre_tunnel_dump_t *mp) } } +static void +vl_api_gre_tunnel_dump_v2_t_handler (vl_api_gre_tunnel_dump_v2_t *mp) +{ + vl_api_registration_t *reg; + gre_main_t *gm = &gre_main; + gre_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, gm->tunnels) + { + send_gre_tunnel_details_v2 (t, mp); + } + } + + else + { + if ((sw_if_index >= vec_len (gm->tunnel_index_by_sw_if_index)) || + (~0 == gm->tunnel_index_by_sw_if_index[sw_if_index])) + { + return; + } + t = &gm->tunnels[gm->tunnel_index_by_sw_if_index[sw_if_index]]; + send_gre_tunnel_details_v2 (t, mp); + } +} + /* * gre_api_hookup * Add vpe's API message handlers to the table. diff --git a/src/plugins/gre/interface.c b/src/plugins/gre/interface.c index bd9a6078502..414a7f81712 100644 --- a/src/plugins/gre/interface.c +++ b/src/plugins/gre/interface.c @@ -43,6 +43,20 @@ format_gre_tunnel_type (u8 *s, va_list *args) return (s); } +/** + * @brief Format a GRE key for display + */ +u8 * +format_gre_key (u8 *s, va_list *args) +{ + gre_key_t key = va_arg (*args, gre_key_t); + + if (!gre_key_is_valid (key)) + return format (s, "INVALID"); + else + return format (s, "%u", key); +} + static u8 * format_gre_tunnel (u8 *s, va_list *args) { @@ -57,6 +71,9 @@ format_gre_tunnel (u8 *s, va_list *args) s = format (s, "payload %U ", format_gre_tunnel_type, t->type); s = format (s, "%U ", format_tunnel_mode, t->mode); + if (gre_key_is_valid (t->gre_key)) + s = format (s, "key %U ", format_gre_key, t->gre_key); + if (t->type == GRE_TUNNEL_TYPE_ERSPAN) s = format (s, "session %d ", t->session_id); @@ -76,13 +93,13 @@ gre_tunnel_db_find (const vnet_gre_tunnel_add_del_args_t *a, if (!a->is_ipv6) { gre_mk_key4 (a->src.ip4, a->dst.ip4, outer_fib_index, a->type, a->mode, - a->session_id, &key->gtk_v4); + a->session_id, a->gre_key, &key->gtk_v4); p = hash_get_mem (gm->tunnel_by_key4, &key->gtk_v4); } else { gre_mk_key6 (&a->src.ip6, &a->dst.ip6, outer_fib_index, a->type, a->mode, - a->session_id, &key->gtk_v6); + a->session_id, a->gre_key, &key->gtk_v6); p = hash_get_mem (gm->tunnel_by_key6, &key->gtk_v6); } @@ -247,11 +264,11 @@ gre_teib_mk_key (const gre_tunnel_t *t, const teib_entry_t *ne, if (FIB_PROTOCOL_IP4 == nh->fp_proto) gre_mk_key4 (t->tunnel_src.ip4, nh->fp_addr.ip4, teib_entry_get_fib_index (ne), t->type, TUNNEL_MODE_P2P, 0, - &key->gtk_v4); + t->gre_key, &key->gtk_v4); else gre_mk_key6 (&t->tunnel_src.ip6, &nh->fp_addr.ip6, teib_entry_get_fib_index (ne), t->type, TUNNEL_MODE_P2P, 0, - &key->gtk_v6); + t->gre_key, &key->gtk_v6); } /** @@ -373,6 +390,8 @@ vnet_gre_tunnel_add (vnet_gre_tunnel_add_del_args_t *a, u32 outer_fib_index, pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES); clib_memset (t, 0, sizeof (*t)); + // added for GRE Key - only mark as present if key is non-zero + t->gre_key = a->gre_key; /* Reconcile the real dev_instance and a possible requested instance */ u32 t_idx = t - gm->tunnels; /* tunnel index (or instance) */ @@ -646,7 +665,7 @@ create_gre_tunnel_command_fn (vlib_main_t *vm, unformat_input_t *input, u8 is_add = 1; u32 sw_if_index; clib_error_t *error = NULL; - + u32 key = 0; // added GRE key /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return 0; @@ -672,6 +691,8 @@ create_gre_tunnel_command_fn (vlib_main_t *vm, unformat_input_t *input, else if (unformat (line_input, "flags %U", unformat_tunnel_encap_decap_flags, &flags)) ; + else if (unformat (line_input, "key %u", &key)) + ; else { error = clib_error_return (0, "unknown input `%U'", @@ -713,6 +734,7 @@ create_gre_tunnel_command_fn (vlib_main_t *vm, unformat_input_t *input, a->is_ipv6 = !ip46_address_is_ip4 (&src); a->instance = instance; a->flags = flags; + a->gre_key = key; clib_memcpy (&a->src, &src, sizeof (a->src)); clib_memcpy (&a->dst, &dst, sizeof (a->dst)); @@ -756,7 +778,8 @@ VLIB_CLI_COMMAND (create_gre_tunnel_command, static) = { .path = "create gre tunnel", .short_help = "create gre tunnel src dst [instance ] " "[outer-fib-id ] [teb | erspan ] [del] " - "[multipoint]", + "[multipoint]" + "[key ]", .function = create_gre_tunnel_command_fn, }; diff --git a/src/plugins/gre/node.c b/src/plugins/gre/node.c index 5235888cc6f..541cae31a49 100644 --- a/src/plugins/gre/node.c +++ b/src/plugins/gre/node.c @@ -102,7 +102,7 @@ gre_tunnel_get (const gre_main_t *gm, vlib_node_runtime_t *node, { const uword *p; p = is_ipv6 ? hash_get_mem (gm->tunnel_by_key6, &key->gtk_v6) : - hash_get_mem (gm->tunnel_by_key4, &key->gtk_v4); + hash_get_mem (gm->tunnel_by_key4, &key->gtk_v4); if (PREDICT_FALSE (!p)) { *next = GRE_INPUT_NEXT_DROP; @@ -172,8 +172,17 @@ gre_input (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, ip6[1] = vlib_buffer_get_current (b[1]); gre[0] = (void *) (ip6[0] + 1); gre[1] = (void *) (ip6[1] + 1); - vlib_buffer_advance (b[0], sizeof (*ip6[0]) + sizeof (*gre[0])); - vlib_buffer_advance (b[1], sizeof (*ip6[0]) + sizeof (*gre[0])); + /* Calculate total header size for each packet */ + u16 gre_hdr_size0 = sizeof (*gre[0]); + u16 gre_hdr_size1 = sizeof (*gre[1]); + if (gre[0]->flags_and_version & clib_host_to_net_u16 (GRE_FLAGS_KEY)) + gre_hdr_size0 += sizeof (gre_key_t); + if (gre[1]->flags_and_version & clib_host_to_net_u16 (GRE_FLAGS_KEY)) + gre_hdr_size1 += sizeof (gre_key_t); + + /* Single buffer advance for each packet */ + vlib_buffer_advance (b[0], sizeof (*ip6[0]) + gre_hdr_size0); + vlib_buffer_advance (b[1], sizeof (*ip6[1]) + gre_hdr_size1); } else { @@ -182,8 +191,30 @@ gre_input (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, ip4[1] = vlib_buffer_get_current (b[1]); gre[0] = (void *) (ip4[0] + 1); gre[1] = (void *) (ip4[1] + 1); - vlib_buffer_advance (b[0], sizeof (*ip4[0]) + sizeof (*gre[0])); - vlib_buffer_advance (b[1], sizeof (*ip4[0]) + sizeof (*gre[0])); + /* Calculate total header size for each packet */ + u16 gre_hdr_size0 = sizeof (*gre[0]); + u16 gre_hdr_size1 = sizeof (*gre[1]); + if (gre[0]->flags_and_version & clib_host_to_net_u16 (GRE_FLAGS_KEY)) + gre_hdr_size0 += sizeof (gre_key_t); + if (gre[1]->flags_and_version & clib_host_to_net_u16 (GRE_FLAGS_KEY)) + gre_hdr_size1 += sizeof (gre_key_t); + + /* Single buffer advance for each packet */ + vlib_buffer_advance (b[0], sizeof (*ip4[0]) + gre_hdr_size0); + vlib_buffer_advance (b[1], sizeof (*ip4[1]) + gre_hdr_size1); + } + + /* GRE key processing here */ + gre_key_t gre_key[2] = { 0, 0 }; + if (gre[0]->flags_and_version & clib_host_to_net_u16 (GRE_FLAGS_KEY)) + { + gre_header_with_key_t *grek = (gre_header_with_key_t *) gre[0]; + gre_key[0] = clib_net_to_host_u32 (grek->key); + } + if (gre[1]->flags_and_version & clib_host_to_net_u16 (GRE_FLAGS_KEY)) + { + gre_header_with_key_t *grek = (gre_header_with_key_t *) gre[1]; + gre_key[1] = clib_net_to_host_u32 (grek->key); } if (PREDICT_TRUE (cached_protocol == gre[0]->protocol)) @@ -215,11 +246,11 @@ gre_input (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, type[1] = ni[1].tunnel_type; b[0]->error = nidx[0] == SPARSE_VEC_INVALID_INDEX ? - node->errors[GRE_ERROR_UNKNOWN_PROTOCOL] : - node->errors[GRE_ERROR_NONE]; + node->errors[GRE_ERROR_UNKNOWN_PROTOCOL] : + node->errors[GRE_ERROR_NONE]; b[1]->error = nidx[1] == SPARSE_VEC_INVALID_INDEX ? - node->errors[GRE_ERROR_UNKNOWN_PROTOCOL] : - node->errors[GRE_ERROR_NONE]; + node->errors[GRE_ERROR_UNKNOWN_PROTOCOL] : + node->errors[GRE_ERROR_NONE]; version[0] = clib_net_to_host_u16 (gre[0]->flags_and_version); version[1] = clib_net_to_host_u16 (gre[1]->flags_and_version); @@ -241,10 +272,10 @@ gre_input (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, { gre_mk_key6 (&ip6[0]->dst_address, &ip6[0]->src_address, vnet_buffer (b[0])->ip.fib_index, type[0], - TUNNEL_MODE_P2P, 0, &key[0].gtk_v6); + TUNNEL_MODE_P2P, 0, gre_key[0], &key[0].gtk_v6); gre_mk_key6 (&ip6[1]->dst_address, &ip6[1]->src_address, vnet_buffer (b[1])->ip.fib_index, type[1], - TUNNEL_MODE_P2P, 0, &key[1].gtk_v6); + TUNNEL_MODE_P2P, 0, gre_key[1], &key[1].gtk_v6); matched[0] = gre_match_key6 (&cached_key.gtk_v6, &key[0].gtk_v6); matched[1] = gre_match_key6 (&cached_key.gtk_v6, &key[1].gtk_v6); } @@ -252,10 +283,10 @@ gre_input (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, { gre_mk_key4 (ip4[0]->dst_address, ip4[0]->src_address, vnet_buffer (b[0])->ip.fib_index, type[0], - TUNNEL_MODE_P2P, 0, &key[0].gtk_v4); + TUNNEL_MODE_P2P, 0, gre_key[0], &key[0].gtk_v4); gre_mk_key4 (ip4[1]->dst_address, ip4[1]->src_address, vnet_buffer (b[1])->ip.fib_index, type[1], - TUNNEL_MODE_P2P, 0, &key[1].gtk_v4); + TUNNEL_MODE_P2P, 0, gre_key[1], &key[1].gtk_v4); matched[0] = gre_match_key4 (&cached_key.gtk_v4, &key[0].gtk_v4); matched[1] = gre_match_key4 (&cached_key.gtk_v4, &key[1].gtk_v4); } @@ -328,14 +359,33 @@ gre_input (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, /* ip6_local hands us the ip header, not the gre header */ ip6[0] = vlib_buffer_get_current (b[0]); gre[0] = (void *) (ip6[0] + 1); - vlib_buffer_advance (b[0], sizeof (*ip6[0]) + sizeof (*gre[0])); + + /* Calculate total header size */ + u16 gre_hdr_size = sizeof (*gre[0]); + if (gre[0]->flags_and_version & clib_host_to_net_u16 (GRE_FLAGS_KEY)) + gre_hdr_size += sizeof (gre_key_t); + /* Single buffer advance */ + vlib_buffer_advance (b[0], sizeof (*ip6[0]) + gre_hdr_size); } else { /* ip4_local hands us the ip header, not the gre header */ ip4[0] = vlib_buffer_get_current (b[0]); gre[0] = (void *) (ip4[0] + 1); - vlib_buffer_advance (b[0], sizeof (*ip4[0]) + sizeof (*gre[0])); + /* Calculate total header size */ + u16 gre_hdr_size = sizeof (*gre[0]); + if (gre[0]->flags_and_version & clib_host_to_net_u16 (GRE_FLAGS_KEY)) + gre_hdr_size += sizeof (gre_key_t); + /* Single buffer advance */ + vlib_buffer_advance (b[0], sizeof (*ip4[0]) + gre_hdr_size); + } + + /* GRE key processing here */ + gre_key_t gre_key = 0; + if (gre[0]->flags_and_version & clib_host_to_net_u16 (GRE_FLAGS_KEY)) + { + gre_header_with_key_t *grek = (gre_header_with_key_t *) gre[0]; + gre_key = clib_net_to_host_u32 (grek->key); } if (PREDICT_TRUE (cached_protocol == gre[0]->protocol)) @@ -354,8 +404,8 @@ gre_input (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, type[0] = ni[0].tunnel_type; b[0]->error = nidx[0] == SPARSE_VEC_INVALID_INDEX ? - node->errors[GRE_ERROR_UNKNOWN_PROTOCOL] : - node->errors[GRE_ERROR_NONE]; + node->errors[GRE_ERROR_UNKNOWN_PROTOCOL] : + node->errors[GRE_ERROR_NONE]; version[0] = clib_net_to_host_u16 (gre[0]->flags_and_version); version[0] &= GRE_VERSION_MASK; @@ -370,14 +420,14 @@ gre_input (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, { gre_mk_key6 (&ip6[0]->dst_address, &ip6[0]->src_address, vnet_buffer (b[0])->ip.fib_index, type[0], - TUNNEL_MODE_P2P, 0, &key[0].gtk_v6); + TUNNEL_MODE_P2P, 0, gre_key, &key[0].gtk_v6); matched[0] = gre_match_key6 (&cached_key.gtk_v6, &key[0].gtk_v6); } else { gre_mk_key4 (ip4[0]->dst_address, ip4[0]->src_address, vnet_buffer (b[0])->ip.fib_index, type[0], - TUNNEL_MODE_P2P, 0, &key[0].gtk_v4); + TUNNEL_MODE_P2P, 0, gre_key, &key[0].gtk_v4); matched[0] = gre_match_key4 (&cached_key.gtk_v4, &key[0].gtk_v4); } @@ -435,46 +485,46 @@ static char *gre_error_strings[] = { }; VLIB_REGISTER_NODE (gre4_input_node) = { - .name = "gre4-input", - /* Takes a vector of packets. */ - .vector_size = sizeof (u32), + .name = "gre4-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), - .n_errors = GRE_N_ERROR, - .error_strings = gre_error_strings, + .n_errors = GRE_N_ERROR, + .error_strings = gre_error_strings, - .n_next_nodes = GRE_INPUT_N_NEXT, - .next_nodes = { + .n_next_nodes = GRE_INPUT_N_NEXT, + .next_nodes = { #define _(s, n) [GRE_INPUT_NEXT_##s] = n, - foreach_gre_input_next + foreach_gre_input_next #undef _ - }, + }, - .format_buffer = format_gre_header_with_length, - .format_trace = format_gre_rx_trace, - .unformat_buffer = unformat_gre_header, -}; + .format_buffer = format_gre_header_with_length, + .format_trace = format_gre_rx_trace, + .unformat_buffer = unformat_gre_header, + }; VLIB_REGISTER_NODE (gre6_input_node) = { - .name = "gre6-input", - /* Takes a vector of packets. */ - .vector_size = sizeof (u32), + .name = "gre6-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), - .runtime_data_bytes = sizeof (gre_input_runtime_t), + .runtime_data_bytes = sizeof (gre_input_runtime_t), - .n_errors = GRE_N_ERROR, - .error_strings = gre_error_strings, + .n_errors = GRE_N_ERROR, + .error_strings = gre_error_strings, - .n_next_nodes = GRE_INPUT_N_NEXT, - .next_nodes = { + .n_next_nodes = GRE_INPUT_N_NEXT, + .next_nodes = { #define _(s, n) [GRE_INPUT_NEXT_##s] = n, - foreach_gre_input_next + foreach_gre_input_next #undef _ - }, + }, - .format_buffer = format_gre_header_with_length, - .format_trace = format_gre_rx_trace, - .unformat_buffer = unformat_gre_header, -}; + .format_buffer = format_gre_header_with_length, + .format_trace = format_gre_rx_trace, + .unformat_buffer = unformat_gre_header, + }; #ifndef CLIB_MARCH_VARIANT void diff --git a/src/vnet/gre/packet.h b/src/vnet/gre/packet.h index bbda2df3f68..e80a36a8dbd 100644 --- a/src/vnet/gre/packet.h +++ b/src/vnet/gre/packet.h @@ -18,18 +18,18 @@ * limitations under the License. */ -#define foreach_gre_protocol \ -_ (0x0800, ip4) \ -_ (0x86DD, ip6) \ -_ (0x6558, teb) \ -_ (0x0806, arp) \ -_ (0x8847, mpls_unicast) \ -_ (0x88BE, erspan) \ -_ (0x894F, nsh) +#define foreach_gre_protocol \ + _ (0x0800, ip4) \ + _ (0x86DD, ip6) \ + _ (0x6558, teb) \ + _ (0x0806, arp) \ + _ (0x8847, mpls_unicast) \ + _ (0x88BE, erspan) \ + _ (0x894F, nsh) typedef enum { -#define _(n,f) GRE_PROTOCOL_##f = n, +#define _(n, f) GRE_PROTOCOL_##f = n, foreach_gre_protocol #undef _ } gre_protocol_t; @@ -42,26 +42,33 @@ typedef struct #define GRE_FLAGS_CHECKSUM (1 << 15) /* deprecated, according to rfc2784 */ -#define GRE_FLAGS_ROUTING (1 << 14) -#define GRE_FLAGS_KEY (1 << 13) -#define GRE_FLAGS_SEQUENCE (1 << 12) +#define GRE_FLAGS_ROUTING (1 << 14) +#define GRE_FLAGS_KEY (1 << 13) +#define GRE_FLAGS_SEQUENCE (1 << 12) #define GRE_FLAGS_STRICT_SOURCE_ROUTE (1 << 11) /* version 1 is PPTP which we don't support */ #define GRE_SUPPORTED_VERSION 0 -#define GRE_VERSION_MASK 0x7 +#define GRE_VERSION_MASK 0x7 /* 0x800 for ip4, etc. */ u16 protocol; } gre_header_t; +typedef struct +{ + u16 flags_and_version; + u16 protocol; + u32 key; +} gre_header_with_key_t; + /* From draft-foschiano-erspan-03.txt Different frame variants known as "ERSPAN Types" can be distinguished based on the GRE "Protocol Type" field value: Type I and II's value is 0x88BE while Type III's is 0x22EB [ETYPES]. - GRE header for ERSPAN Type II encapsulation (8 octets [34:41]) + GRE header for ERSPAN Type II encapsulation (8 octets [34:41]) 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -80,7 +87,7 @@ typedef struct The ERSPAN Type II feature header is described below: - ERSPAN Type II header (8 octets [42:49]) + ERSPAN Type II header (8 octets [42:49]) 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -92,50 +99,50 @@ typedef struct The various fields of the above header are described in this table: Field Position Length Definition - [octet:bit] (bits) + [octet:bit] (bits) Ver [42:0] 4 ERSPAN Encapsulation version. - This indicates the version of - the ERSPAN encapsulation - specification. Set to 0x1 for - Type II. + This indicates the version of + the ERSPAN encapsulation + specification. Set to 0x1 for + Type II. VLAN [42:4] 12 Original VLAN of the frame, - mirrored from the source. - If the En field is set to 11, - the value of VLAN is undefined. + mirrored from the source. + If the En field is set to 11, + the value of VLAN is undefined. COS [44:0] 3 Original class of service of the - frame, mirrored from the source. + frame, mirrored from the source. En [44:3] 2 The trunk encapsulation type - associated with the ERSPAN source - port for ingress ERSPAN traffic. + associated with the ERSPAN source + port for ingress ERSPAN traffic. - The possible values are: - 00-originally without VLAN tag - 01-originally ISL encapsulated - 10-originally 802.1Q encapsulated - 11-VLAN tag preserved in frame. + The possible values are: + 00-originally without VLAN tag + 01-originally ISL encapsulated + 10-originally 802.1Q encapsulated + 11-VLAN tag preserved in frame. T [44:5] 1 This bit indicates that the frame - copy encapsulated in the ERSPAN - packet has been truncated. This - occurs if the ERSPAN encapsulated - frame exceeds the configured MTU. + copy encapsulated in the ERSPAN + packet has been truncated. This + occurs if the ERSPAN encapsulated + frame exceeds the configured MTU. Session ID [44:6] 10 Identification associated with (ERSPAN ID) each ERSPAN session. Must be - unique between the source and the - receiver(s). (See section below.) + unique between the source and the + receiver(s). (See section below.) Reserved [46:0] 12 All bits are set to zero Index [47:4] 20 A 20 bit index/port number - associated with the ERSPAN - traffic's port and - direction (ingress/egress). N.B.: - This field is platform dependent. + associated with the ERSPAN + traffic's port and + direction (ingress/egress). N.B.: + This field is platform dependent. */ typedef CLIB_PACKED (struct { @@ -157,7 +164,6 @@ typedef CLIB_PACKED (struct { erspan_t2_t erspan; }) erspan_t2_header_t; - /* u64 template for ERSPAN type 2 header with both EN bits set */ #define ERSPAN_HDR2 0x1000180000000000ul diff --git a/test/test_gre.py b/test/test_gre.py index 8b2851baea2..51a15b02fdc 100644 --- a/test/test_gre.py +++ b/test/test_gre.py @@ -168,6 +168,25 @@ class TestGRE(VppTestCase): pkts.append(p) return pkts + def create_tunnel_stream_4o4_with_key( + self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip, key + ): + pkts = [] + for i in range(0, 257): + info = self.create_packet_info(src_if, src_if) + payload = self.info_to_payload(info) + p = ( + Ether(dst=src_if.local_mac, src=src_if.remote_mac) + / IP(src=tunnel_src, dst=tunnel_dst) + / GRE(key=key) + / IP(src=src_ip, dst=dst_ip) + / UDP(sport=1234, dport=1234) + / Raw(payload) + ) + info.data = p.copy() + pkts.append(p) + return pkts + def create_tunnel_stream_6o4(self, src_if, tunnel_src, tunnel_dst, src_ip, dst_ip): pkts = [] for i in range(0, 257): @@ -271,6 +290,60 @@ class TestGRE(VppTestCase): self.logger.error(ppp("Tx:", tx)) raise + def verify_tunneled_4o4_with_key( + self, src_if, capture, sent, tunnel_src, tunnel_dst, key, dscp=0, ecn=0 + ): + """ + Verify GRE encapsulation with key field: + - Basic GRE tunnel checks (source, destination, etc.) + - Verify GRE flags indicate key field is present (0x2000) + - Verify the GRE key value matches what we configured + """ + self.assertEqual(len(capture), len(sent)) + tos = (dscp << 2) | ecn + + for tx, rx in zip(sent, capture): + try: + + tx_ip = tx[IP] + rx_ip = rx[IP] + + # Verify tunnel outer header + self.assertEqual(rx_ip.src, tunnel_src) + self.assertEqual(rx_ip.dst, tunnel_dst) + self.assertEqual(rx_ip.tos, tos) + self.assertEqual(rx_ip.len, len(rx_ip)) + + # Verify GRE header + rx_gre = rx[GRE] + + # Check if GRE flags has the KEY bit set (0x2000) + # In Scapy GRE, this should be reflected in the 'flags' field or through presence of 'key' field + self.assertTrue( + hasattr(rx_gre, "key"), "GRE key field not present in packet" + ) + + # Verify the key value is correct + self.assertEqual( + rx_gre.key, + key, + f"GRE key value mismatch: expected {key}, got {rx_gre.key}", + ) + self.logger.info(f"Verified GRE key: {rx_gre.key}") + + # Verify inner packet data + rx_inner_ip = rx_gre[IP] + self.assertEqual(rx_inner_ip.src, tx_ip.src) + self.assertEqual(rx_inner_ip.dst, tx_ip.dst) + + # IP processing post pop has decremented the TTL + self.assertEqual(rx_inner_ip.ttl + 1, tx_ip.ttl) + + except: + self.logger.error(ppp("Rx:", rx)) + self.logger.error(ppp("Tx:", tx)) + raise + def verify_tunneled_6o6( self, src_if, capture, sent, tunnel_src, tunnel_dst, dscp=0, ecn=0 ): @@ -718,6 +791,55 @@ class TestGRE(VppTestCase): self.pg0.unconfig_ip6() + def test_gre_with_key(self): + """GRE IPv4 tunnel with Key Tests""" + + # Use a key value for the GRE tunnel + key_value = 123 + + # Create a GRE interface with key support + gre_if = VppGreInterface(self, self.pg0.local_ip4, "1.1.1.2", gre_key=key_value) + gre_if.add_vpp_config() + + # Configure the interface using VPP methods + gre_if.admin_up() + gre_if.config_ip4() + + # Create route via tunnel + route_via_tun = VppIpRoute( + self, "4.4.4.4", 32, [VppRoutePath("0.0.0.0", gre_if.sw_if_index)] + ) + route_via_tun.add_vpp_config() + + # Add a route that resolves the tunnel's destination + route_tun_dst = VppIpRoute( + self, + "1.1.1.2", + 32, + [VppRoutePath(self.pg0.remote_ip4, self.pg0.sw_if_index)], + ) + route_tun_dst.add_vpp_config() + + # Send packets that should be routed via the tunnel + tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4") + rx = self.send_and_expect(self.pg0, tx, self.pg0) + + # Verify GRE encapsulation with key using the verification function + self.logger.info(f"Verifying GRE encapsulation with key {key_value}") + self.verify_tunneled_4o4_with_key( + self.pg0, # source interface + rx, # received packets + tx, # sent packets + self.pg0.local_ip4, # tunnel source + "1.1.1.2", # tunnel destination + key_value, # GRE key value + ) + + # Cleanup + route_tun_dst.remove_vpp_config() + route_via_tun.remove_vpp_config() + gre_if.remove_vpp_config() + def test_gre6(self): """GRE IPv6 tunnel Tests""" diff --git a/test/vpp_gre_interface.py b/test/vpp_gre_interface.py index a40e8531a61..0249ae46a1c 100644 --- a/test/vpp_gre_interface.py +++ b/test/vpp_gre_interface.py @@ -17,6 +17,7 @@ class VppGreInterface(VppInterface): mode=None, flags=0, session=0, + gre_key=0, ): """Create VPP GRE interface""" super(VppGreInterface, self).__init__(test) @@ -24,6 +25,7 @@ class VppGreInterface(VppInterface): self.t_dst = dst_ip self.t_outer_table = outer_table_id self.t_session = session + self.t_gre_key = gre_key # Added GRE key field self.t_flags = flags self.t_type = type if not self.t_type: @@ -33,19 +35,38 @@ class VppGreInterface(VppInterface): self.t_mode = VppEnum.vl_api_tunnel_mode_t.TUNNEL_API_MODE_P2P def add_vpp_config(self): - r = self.test.vapi.gre_tunnel_add_del( - is_add=1, - tunnel={ - "src": self.t_src, - "dst": self.t_dst, - "outer_table_id": self.t_outer_table, - "instance": 0xFFFFFFFF, - "type": self.t_type, - "mode": self.t_mode, - "flags": self.t_flags, - "session_id": self.t_session, - }, - ) + # If we need a key, use the v2 API + if self.t_gre_key != 0: + r = self.test.vapi.gre_tunnel_add_del_v2( + is_add=1, + tunnel={ + "src": self.t_src, + "dst": self.t_dst, + "outer_table_id": self.t_outer_table, + "instance": 0xFFFFFFFF, + "type": self.t_type, + "mode": self.t_mode, + "flags": self.t_flags, + "session_id": self.t_session, + "key": self.t_gre_key, + }, + ) + else: + # Use regular v1 API for tunnels without key + r = self.test.vapi.gre_tunnel_add_del( + is_add=1, + tunnel={ + "src": self.t_src, + "dst": self.t_dst, + "outer_table_id": self.t_outer_table, + "instance": 0xFFFFFFFF, + "type": self.t_type, + "mode": self.t_mode, + "flags": self.t_flags, + "session_id": self.t_session, + }, + ) + self.set_sw_if_index(r.sw_if_index) self.generate_remote_hosts() self.test.registry.register(self, self.test.logger) @@ -53,25 +74,75 @@ class VppGreInterface(VppInterface): def remove_vpp_config(self): self.unconfig() - self.test.vapi.gre_tunnel_add_del( - is_add=0, - tunnel={ - "src": self.t_src, - "dst": self.t_dst, - "outer_table_id": self.t_outer_table, - "instance": 0xFFFFFFFF, - "type": self.t_type, - "mode": self.t_mode, - "flags": self.t_flags, - "session_id": self.t_session, - }, - ) + + # Use appropriate API based on whether tunnel has a key + if self.t_gre_key != 0: + # Use v2 API for tunnels with keys + self.test.vapi.gre_tunnel_add_del_v2( + is_add=0, + tunnel={ + "src": self.t_src, + "dst": self.t_dst, + "outer_table_id": self.t_outer_table, + "instance": 0xFFFFFFFF, + "type": self.t_type, + "mode": self.t_mode, + "flags": self.t_flags, + "session_id": self.t_session, + "key": self.t_gre_key, + }, + ) + else: + # Use v1 API for tunnels without keys + self.test.vapi.gre_tunnel_add_del( + is_add=0, + tunnel={ + "src": self.t_src, + "dst": self.t_dst, + "outer_table_id": self.t_outer_table, + "instance": 0xFFFFFFFF, + "type": self.t_type, + "mode": self.t_mode, + "flags": self.t_flags, + "session_id": self.t_session, + }, + ) def object_id(self): return "gre-%d" % self.sw_if_index def query_vpp_config(self): - return self.test.vapi.gre_tunnel_dump(sw_if_index=self._sw_if_index) + try: + # Use appropriate API based on whether tunnel has a key + if hasattr(self, "t_gre_key") and self.t_gre_key != 0: + dump = self.test.vapi.gre_tunnel_dump_v2(sw_if_index=self.sw_if_index) + else: + dump = self.test.vapi.gre_tunnel_dump(sw_if_index=self.sw_if_index) + + # Validate dump data matches this tunnel's configuration + for entry in dump: + # Skip non-tunnel entries (like int values) + if not hasattr(entry, "sw_if_index"): + continue + + # Compare tunnel parameters + key_match = True + if hasattr(self, "t_gre_key") and self.t_gre_key != 0: + # For tunnels with keys, also validate the key value + key_match = hasattr(entry, "key") and entry.key == self.t_gre_key + + if ( + entry.sw_if_index == self.sw_if_index + and str(entry.src) == str(self.t_src) + and str(entry.dst) == str(self.t_dst) + and entry.type == self.t_type + and key_match + ): + return True + + return False + except Exception: + return False @property def remote_ip(self):