From 5f8f6173328f8d77feea5fd100e150c3094c11f0 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Thu, 18 Apr 2019 10:23:56 +0000 Subject: gre: Multi-point interfaces Type: feature Change-Id: I0129ad6ace44a50a8a3b26db8e445cd06b2b49e8 Signed-off-by: Neale Ranns --- src/vnet/gre/gre.api | 23 +++++--- src/vnet/gre/gre.c | 61 +++++++++++++++++++-- src/vnet/gre/gre.h | 57 +++++++++++-------- src/vnet/gre/gre_api.c | 77 ++++++++++++++++++-------- src/vnet/gre/interface.c | 140 ++++++++++++++++++++++++++--------------------- 5 files changed, 239 insertions(+), 119 deletions(-) (limited to 'src/vnet/gre') diff --git a/src/vnet/gre/gre.api b/src/vnet/gre/gre.api index cb0bb736c4f..d79beebb94d 100644 --- a/src/vnet/gre/gre.api +++ b/src/vnet/gre/gre.api @@ -14,20 +14,30 @@ * limitations under the License. */ -option version = "2.0.0"; +option version = "2.0.1"; import "vnet/interface_types.api"; import "vnet/ip/ip_types.api"; /** \brief A GRE tunnel type */ -enum gre_tunnel_type +enum gre_tunnel_type : u8 { GRE_API_TUNNEL_TYPE_L3 = 0, GRE_API_TUNNEL_TYPE_TEB, GRE_API_TUNNEL_TYPE_ERSPAN, }; +/** \brief A GRE tunnel mode +*/ +enum gre_tunnel_mode : u8 +{ + /* point-to-point */ + GRE_API_TUNNEL_MODE_P2P, + /* multi-point */ + GRE_API_TUNNEL_MODE_MP, +}; + /** \brief A GRE tunnel @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -35,18 +45,17 @@ enum gre_tunnel_type @param instance - optional unique custom device instance, else ~0. @param src - Source IP address @param dst - Destination IP address, can be multicast - @param outer_fib_id - Encap FIB table ID + @param outer_table_id - Encap FIB table ID @param session_id - session for ERSPAN tunnel, range 0-1023 @param sw_if_index - ignored on create/delete, present in details. */ typedef gre_tunnel { - u32 client_index; - u32 context; - u16 session_id; vl_api_gre_tunnel_type_t type; + vl_api_gre_tunnel_mode_t mode; + u16 session_id; u32 instance; - u32 outer_fib_id; + u32 outer_table_id; vl_api_interface_index_t sw_if_index; vl_api_address_t src; vl_api_address_t dst; diff --git a/src/vnet/gre/gre.c b/src/vnet/gre/gre.c index 4f16b5db387..f06f19fab2a 100644 --- a/src/vnet/gre/gre.c +++ b/src/vnet/gre/gre.c @@ -18,6 +18,7 @@ #include #include #include +#include extern gre_main_t gre_main; @@ -213,6 +214,7 @@ gre_build_rewrite (vnet_main_t * vnm, vnet_link_t link_type, const void *dst_address) { gre_main_t *gm = &gre_main; + const ip46_address_t *dst; ip4_and_gre_header_t *h4; ip6_and_gre_header_t *h6; gre_header_t *gre; @@ -221,6 +223,7 @@ gre_build_rewrite (vnet_main_t * vnm, u32 ti; u8 is_ipv6; + dst = dst_address; ti = gm->tunnel_index_by_sw_if_index[sw_if_index]; if (~0 == ti) @@ -241,7 +244,7 @@ gre_build_rewrite (vnet_main_t * vnm, h4->ip4.protocol = IP_PROTOCOL_GRE; /* fixup ip4 header length and checksum after-the-fact */ h4->ip4.src_address.as_u32 = t->tunnel_src.ip4.as_u32; - h4->ip4.dst_address.as_u32 = t->tunnel_dst.fp_addr.ip4.as_u32; + h4->ip4.dst_address.as_u32 = dst->ip4.as_u32; h4->ip4.checksum = ip4_header_checksum (&h4->ip4); } else @@ -256,8 +259,8 @@ gre_build_rewrite (vnet_main_t * vnm, /* fixup ip6 header length and checksum after-the-fact */ h6->ip6.src_address.as_u64[0] = t->tunnel_src.ip6.as_u64[0]; h6->ip6.src_address.as_u64[1] = t->tunnel_src.ip6.as_u64[1]; - h6->ip6.dst_address.as_u64[0] = t->tunnel_dst.fp_addr.ip6.as_u64[0]; - h6->ip6.dst_address.as_u64[1] = t->tunnel_dst.fp_addr.ip6.as_u64[1]; + h6->ip6.dst_address.as_u64[0] = dst->ip6.as_u64[0]; + h6->ip6.dst_address.as_u64[1] = dst->ip6.as_u64[1]; } if (PREDICT_FALSE (t->type == GRE_TUNNEL_TYPE_ERSPAN)) @@ -322,10 +325,51 @@ gre_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai) adj_nbr_midchain_update_rewrite (ai, !is_ipv6 ? gre4_fixup : gre6_fixup, NULL, af, - gre_build_rewrite (vnm, sw_if_index, adj_get_link_type (ai), NULL)); + gre_build_rewrite (vnm, sw_if_index, adj_get_link_type (ai), + &t->tunnel_dst.fp_addr)); gre_tunnel_stack (ai); } + +static adj_walk_rc_t +mgre_mk_complete_walk (adj_index_t ai, void *ctx) +{ + nhrp_entry_adj_stack (ctx, ai); + + return (ADJ_WALK_RC_CONTINUE); +} + +void +mgre_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai) +{ + gre_main_t *gm = &gre_main; + ip_adjacency_t *adj; + nhrp_entry_t *ne; + gre_tunnel_t *t; + adj_flags_t af; + u8 is_ipv6; + u32 ti; + + adj = adj_get (ai); + ti = gm->tunnel_index_by_sw_if_index[sw_if_index]; + t = pool_elt_at_index (gm->tunnels, ti); + is_ipv6 = t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0; + af = ADJ_FLAG_MIDCHAIN_IP_STACK; + + adj_nbr_midchain_update_rewrite + (ai, !is_ipv6 ? gre4_fixup : gre6_fixup, NULL, af, + gre_build_rewrite (vnm, sw_if_index, adj_get_link_type (ai), + &adj->sub_type.nbr.next_hop)); + + ne = nhrp_entry_find (sw_if_index, &adj->sub_type.nbr.next_hop); + + if (NULL == ne) + // no NHRP entry to provide the next-hop + return; + + adj_nbr_walk_nh (sw_if_index, t->tunnel_dst.fp_proto, + &adj->sub_type.nbr.next_hop, mgre_mk_complete_walk, ne); +} #endif /* CLIB_MARCH_VARIANT */ typedef enum @@ -574,6 +618,15 @@ VNET_HW_INTERFACE_CLASS (gre_hw_interface_class) = { .update_adjacency = gre_update_adj, .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P, }; + +VNET_HW_INTERFACE_CLASS (mgre_hw_interface_class) = { + .name = "mGRE", + .format_header = format_gre_header_with_length, + .unformat_header = unformat_gre_header, + .build_rewrite = gre_build_rewrite, + .update_adjacency = mgre_update_adj, + .flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA, +}; /* *INDENT-ON* */ #endif /* CLIB_MARCH_VARIANT */ diff --git a/src/vnet/gre/gre.h b/src/vnet/gre/gre.h index 99fe4ace5b4..11bcddd51b3 100644 --- a/src/vnet/gre/gre.h +++ b/src/vnet/gre/gre.h @@ -26,6 +26,7 @@ #include extern vnet_hw_interface_class_t gre_hw_interface_class; +extern vnet_hw_interface_class_t mgre_hw_interface_class; typedef enum { @@ -35,35 +36,43 @@ typedef enum GRE_N_ERROR, } gre_error_t; +/** + * L3: GRE (i.e. this tunnel is in L3 mode) + * TEB: Transparent Ethernet Bridging - the tunnel is in L2 mode + * ERSPAN: type 2 - the tunnel is for port mirror SPAN output. Each tunnel is + * associated with a session ID and expected to be used for encap + * 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") \ + /** * @brief The GRE tunnel type */ typedef enum gre_tunnel_type_t_ { - /** - * L3 GRE (i.e. this tunnel is in L3 mode) - */ - GRE_TUNNEL_TYPE_L3 = 0, - /** - * Transparent Ethernet Bridging - the tunnel is in L2 mode - */ - GRE_TUNNEL_TYPE_TEB = 1, - /** - * ERSPAN type 2 - the tunnel is for port mirror SPAN output. Each tunnel is - * associated with a session ID and expected to be used for encap and output - * of mirrored packet from a L2 network only. There is no support for - * receiving ERSPAN packets from a GRE ERSPAN tunnel in VPP. - */ - GRE_TUNNEL_TYPE_ERSPAN = 2, -} gre_tunnel_type_t; +#define _(n, s) GRE_TUNNEL_TYPE_##n, + foreach_gre_tunnel_type +#undef _ +} __clib_packed gre_tunnel_type_t; -#define GRE_TUNNEL_TYPE_N (GRE_TUNNEL_TYPE_ERSPAN + 1) +extern u8 *format_gre_tunnel_type (u8 * s, va_list * args); -#define GRE_TUNNEL_TYPE_NAMES { \ - [GRE_TUNNEL_TYPE_L3] = "L3", \ - [GRE_TUNNEL_TYPE_TEB] = "TEB", \ - [GRE_TUNNEL_TYPE_ERSPAN] = "ERSPAN", \ -} +#define foreach_gre_tunnel_mode \ + _(P2P, "point-to-point") \ + _(MP, "multi-point") \ + +typedef enum gre_tunnel_mode_t_ +{ +#define _(n, s) GRE_TUNNEL_MODE_##n, + foreach_gre_tunnel_mode +#undef _ +} __clib_packed gre_tunnel_mode_t; + +extern u8 *format_gre_tunnel_mode (u8 * s, va_list * args); /** * A GRE payload protocol registration @@ -201,6 +210,7 @@ typedef struct u32 hw_if_index; u32 sw_if_index; gre_tunnel_type_t type; + gre_tunnel_mode_t mode; /** * an L2 tunnel always rquires an L2 midchain. cache here for DP. @@ -348,10 +358,11 @@ typedef struct { u8 is_add; gre_tunnel_type_t type; + gre_tunnel_mode_t mode; u8 is_ipv6; u32 instance; ip46_address_t src, dst; - u32 outer_fib_id; + u32 outer_table_id; u16 session_id; } vnet_gre_tunnel_add_del_args_t; diff --git a/src/vnet/gre/gre_api.c b/src/vnet/gre/gre_api.c index 0d6c33bfda6..9ee9f19f99f 100644 --- a/src/vnet/gre/gre_api.c +++ b/src/vnet/gre/gre_api.c @@ -52,19 +52,14 @@ _(GRE_TUNNEL_DUMP, gre_tunnel_dump) static int gre_tunnel_type_decode (vl_api_gre_tunnel_type_t in, gre_tunnel_type_t * out) { - in = clib_net_to_host_u32 (in); - switch (in) { - case GRE_API_TUNNEL_TYPE_L3: - *out = GRE_TUNNEL_TYPE_L3; - return (0); - case GRE_API_TUNNEL_TYPE_TEB: - *out = GRE_TUNNEL_TYPE_TEB; - return (0); - case GRE_API_TUNNEL_TYPE_ERSPAN: - *out = GRE_TUNNEL_TYPE_ERSPAN; - return (0); +#define _(n, v) \ + case GRE_API_TUNNEL_TYPE_##n: \ + *out = GRE_TUNNEL_TYPE_##n; \ + return (0); + foreach_gre_tunnel_type +#undef _ } return (VNET_API_ERROR_INVALID_VALUE); @@ -77,18 +72,47 @@ gre_tunnel_type_encode (gre_tunnel_type_t in) switch (in) { - case GRE_TUNNEL_TYPE_L3: - out = GRE_API_TUNNEL_TYPE_L3; - break; - case GRE_TUNNEL_TYPE_TEB: - out = GRE_API_TUNNEL_TYPE_TEB; - break; - case GRE_TUNNEL_TYPE_ERSPAN: - out = GRE_API_TUNNEL_TYPE_ERSPAN; - break; +#define _(n, v) \ + case GRE_TUNNEL_TYPE_##n: \ + out = GRE_API_TUNNEL_TYPE_##n; \ + break; + foreach_gre_tunnel_type +#undef _ + } + + return (out); +} + +static int +gre_tunnel_mode_decode (vl_api_gre_tunnel_mode_t in, gre_tunnel_mode_t * out) +{ + switch (in) + { +#define _(n, v) \ + case GRE_API_TUNNEL_MODE_##n: \ + *out = GRE_TUNNEL_MODE_##n; \ + return (0); + foreach_gre_tunnel_mode +#undef _ } - out = clib_net_to_host_u32 (out); + return (VNET_API_ERROR_INVALID_VALUE_2); +} + +static vl_api_gre_tunnel_mode_t +gre_tunnel_mode_encode (gre_tunnel_mode_t in) +{ + vl_api_gre_tunnel_mode_t out = GRE_API_TUNNEL_MODE_P2P; + + switch (in) + { +#define _(n, v) \ + case GRE_TUNNEL_MODE_##n: \ + out = GRE_API_TUNNEL_MODE_##n; \ + break; + foreach_gre_tunnel_mode +#undef _ + } return (out); } @@ -119,6 +143,11 @@ static void vl_api_gre_tunnel_add_del_t_handler rv = gre_tunnel_type_decode (mp->tunnel.type, &a->type); + if (rv) + goto out; + + rv = gre_tunnel_mode_decode (mp->tunnel.mode, &a->mode); + if (rv) goto out; @@ -126,7 +155,7 @@ static void vl_api_gre_tunnel_add_del_t_handler a->is_ipv6 = (itype[0] == IP46_TYPE_IP6); a->instance = ntohl (mp->tunnel.instance); a->session_id = ntohs (mp->tunnel.session_id); - a->outer_fib_id = ntohl (mp->tunnel.outer_fib_id); + a->outer_table_id = ntohl (mp->tunnel.outer_table_id); rv = vnet_gre_tunnel_add_del (a, &sw_if_index); @@ -151,14 +180,16 @@ static void send_gre_tunnel_details 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_fib_id = + 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 = gre_tunnel_mode_encode (t->mode); 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->context = context; vl_api_send_msg (reg, (u8 *) rmp); diff --git a/src/vnet/gre/interface.c b/src/vnet/gre/interface.c index ea93536fb27..927f34ef694 100644 --- a/src/vnet/gre/interface.c +++ b/src/vnet/gre/interface.c @@ -26,7 +26,39 @@ #include #include -static const char *gre_tunnel_type_names[] = GRE_TUNNEL_TYPE_NAMES; +u8 * +format_gre_tunnel_type (u8 * s, va_list * args) +{ + gre_tunnel_type_t type = va_arg (*args, int); + + switch (type) + { +#define _(n, v) case GRE_TUNNEL_TYPE_##n: \ + s = format (s, "%s", v); \ + break; + foreach_gre_tunnel_type +#undef _ + } + + return (s); +} + +u8 * +format_gre_tunnel_mode (u8 * s, va_list * args) +{ + gre_tunnel_mode_t mode = va_arg (*args, int); + + switch (mode) + { +#define _(n, v) case GRE_TUNNEL_MODE_##n: \ + s = format (s, "%s", v); \ + break; + foreach_gre_tunnel_mode +#undef _ + } + + return (s); +} static u8 * format_gre_tunnel (u8 * s, va_list * args) @@ -39,7 +71,8 @@ format_gre_tunnel (u8 * s, va_list * args) format_ip46_address, &t->tunnel_dst.fp_addr, IP46_TYPE_ANY, t->outer_fib_index, t->sw_if_index); - s = format (s, "payload %s ", gre_tunnel_type_names[t->type]); + s = format (s, "payload %U ", format_gre_tunnel_type, t->type); + s = format (s, "%U ", format_gre_tunnel_mode, t->mode); if (t->type == GRE_TUNNEL_TYPE_ERSPAN) s = format (s, "session %d ", t->session_id); @@ -209,13 +242,21 @@ vnet_gre_tunnel_add (vnet_gre_tunnel_add_del_args_t * a, t->user_instance = u_idx; /* name */ t->type = a->type; + t->mode = a->mode; if (t->type == GRE_TUNNEL_TYPE_ERSPAN) t->session_id = a->session_id; if (t->type == GRE_TUNNEL_TYPE_L3) - hw_if_index = vnet_register_interface (vnm, gre_device_class.index, t_idx, - gre_hw_interface_class.index, - t_idx); + { + if (t->mode == GRE_TUNNEL_MODE_P2P) + hw_if_index = + vnet_register_interface (vnm, gre_device_class.index, t_idx, + gre_hw_interface_class.index, t_idx); + else + hw_if_index = + vnet_register_interface (vnm, gre_device_class.index, t_idx, + mgre_hw_interface_class.index, t_idx); + } else { /* Default MAC address (d00b:eed0:0000 + sw_if_index) */ @@ -372,9 +413,9 @@ vnet_gre_tunnel_add_del (vnet_gre_tunnel_add_del_args_t * a, u32 outer_fib_index; if (!a->is_ipv6) - outer_fib_index = ip4_fib_index_from_table_id (a->outer_fib_id); + outer_fib_index = ip4_fib_index_from_table_id (a->outer_table_id); else - outer_fib_index = ip6_fib_index_from_table_id (a->outer_fib_id); + outer_fib_index = ip6_fib_index_from_table_id (a->outer_table_id); if (~0 == outer_fib_index) return VNET_API_ERROR_NO_SUCH_FIB; @@ -428,18 +469,17 @@ create_gre_tunnel_command_fn (vlib_main_t * vm, { unformat_input_t _line_input, *line_input = &_line_input; vnet_gre_tunnel_add_del_args_t _a, *a = &_a; - ip46_address_t src, dst; + ip46_address_t src = ip46_address_initializer, dst = + ip46_address_initializer; u32 instance = ~0; - u32 outer_fib_id = 0; + u32 outer_table_id = 0; gre_tunnel_type_t t_type = GRE_TUNNEL_TYPE_L3; + gre_tunnel_mode_t t_mode = GRE_TUNNEL_MODE_P2P; u32 session_id = 0; int rv; - u32 num_m_args = 0; u8 is_add = 1; u32 sw_if_index; clib_error_t *error = NULL; - u8 ipv4_set = 0; - u8 ipv6_set = 0; /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) @@ -451,32 +491,14 @@ create_gre_tunnel_command_fn (vlib_main_t * vm, is_add = 0; else if (unformat (line_input, "instance %d", &instance)) ; - else - if (unformat (line_input, "src %U", unformat_ip4_address, &src.ip4)) - { - num_m_args++; - ipv4_set = 1; - } - else - if (unformat (line_input, "dst %U", unformat_ip4_address, &dst.ip4)) - { - num_m_args++; - ipv4_set = 1; - } - else - if (unformat (line_input, "src %U", unformat_ip6_address, &src.ip6)) - { - num_m_args++; - ipv6_set = 1; - } - else - if (unformat (line_input, "dst %U", unformat_ip6_address, &dst.ip6)) - { - num_m_args++; - ipv6_set = 1; - } - else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id)) + else if (unformat (line_input, "src %U", unformat_ip46_address, &src)) + ; + else if (unformat (line_input, "dst %U", unformat_ip46_address, &dst)) ; + else if (unformat (line_input, "outer-table-id %d", &outer_table_id)) + ; + else if (unformat (line_input, "multipoint")) + t_mode = GRE_TUNNEL_MODE_MP; else if (unformat (line_input, "teb")) t_type = GRE_TUNNEL_TYPE_TEB; else if (unformat (line_input, "erspan %d", &session_id)) @@ -489,47 +511,41 @@ create_gre_tunnel_command_fn (vlib_main_t * vm, } } - if (num_m_args < 2) + if (ip46_address_is_equal (&src, &dst)) { - error = clib_error_return (0, "mandatory argument(s) missing"); + error = clib_error_return (0, "src and dst are identical"); goto done; } - if ((ipv4_set && memcmp (&src.ip4, &dst.ip4, sizeof (src.ip4)) == 0) || - (ipv6_set && memcmp (&src.ip6, &dst.ip6, sizeof (src.ip6)) == 0)) + if (t_mode != GRE_TUNNEL_MODE_MP && ip46_address_is_zero (&dst)) { - error = clib_error_return (0, "src and dst are identical"); + error = clib_error_return (0, "destination address not specified"); goto done; } - if (ipv4_set && ipv6_set) - return clib_error_return (0, "both IPv4 and IPv6 addresses specified"); + if (ip46_address_is_zero (&src)) + { + error = clib_error_return (0, "source address not specified"); + goto done; + } - if ((ipv4_set && memcmp (&dst.ip4, &zero_addr.ip4, sizeof (dst.ip4)) == 0) - || (ipv6_set - && memcmp (&dst.ip6, &zero_addr.ip6, sizeof (dst.ip6)) == 0)) + if (ip46_address_is_ip4 (&src) != ip46_address_is_ip4 (&dst)) { - error = clib_error_return (0, "dst address cannot be zero"); + error = + clib_error_return (0, "src and dst address must be the same AF"); goto done; } clib_memset (a, 0, sizeof (*a)); a->is_add = is_add; - a->outer_fib_id = outer_fib_id; + a->outer_table_id = outer_table_id; a->type = t_type; + a->mode = t_mode; a->session_id = session_id; - a->is_ipv6 = ipv6_set; + a->is_ipv6 = !ip46_address_is_ip4 (&src); a->instance = instance; - if (!ipv6_set) - { - clib_memcpy (&a->src.ip4, &src.ip4, sizeof (src.ip4)); - clib_memcpy (&a->dst.ip4, &dst.ip4, sizeof (dst.ip4)); - } - else - { - clib_memcpy (&a->src.ip6, &src.ip6, sizeof (src.ip6)); - clib_memcpy (&a->dst.ip6, &dst.ip6, sizeof (dst.ip6)); - } + clib_memcpy (&a->src, &src, sizeof (a->src)); + clib_memcpy (&a->dst, &dst, sizeof (a->dst)); rv = vnet_gre_tunnel_add_del (a, &sw_if_index); @@ -543,8 +559,8 @@ create_gre_tunnel_command_fn (vlib_main_t * vm, error = clib_error_return (0, "GRE tunnel already exists..."); goto done; case VNET_API_ERROR_NO_SUCH_FIB: - error = clib_error_return (0, "outer fib ID %d doesn't exist\n", - outer_fib_id); + error = clib_error_return (0, "outer table ID %d doesn't exist\n", + outer_table_id); goto done; case VNET_API_ERROR_NO_SUCH_ENTRY: error = clib_error_return (0, "GRE tunnel doesn't exist"); -- cgit 1.2.3-korg