From 5f8f6173328f8d77feea5fd100e150c3094c11f0 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Thu, 18 Apr 2019 10:23:56 +0000 Subject: [PATCH] gre: Multi-point interfaces Type: feature Change-Id: I0129ad6ace44a50a8a3b26db8e445cd06b2b49e8 Signed-off-by: Neale Ranns --- MAINTAINERS | 5 ++ src/vat/api_format.c | 12 +-- src/vnet/CMakeLists.txt | 16 ++++ 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 ++++++++++++++++++-------------- src/vnet/interface.c | 10 +++ src/vnet/interface.h | 7 ++ src/vnet/interface_funcs.h | 1 + src/vnet/nhrp/nhrp.api | 57 +++++++++++++ src/vnet/nhrp/nhrp.c | 196 +++++++++++++++++++++++++++++++++++++++++++++ src/vnet/nhrp/nhrp.h | 62 ++++++++++++++ src/vnet/nhrp/nhrp_api.c | 134 +++++++++++++++++++++++++++++++ src/vnet/nhrp/nhrp_cli.c | 194 ++++++++++++++++++++++++++++++++++++++++++++ src/vpp/api/custom_dump.c | 4 +- test/test_gre.py | 80 +++++++++++++++++- test/vpp_gre_interface.py | 26 +++--- test/vpp_nhrp.py | 51 ++++++++++++ test/vpp_papi_provider.py | 10 ++- 21 files changed, 1081 insertions(+), 142 deletions(-) create mode 100644 src/vnet/nhrp/nhrp.api create mode 100644 src/vnet/nhrp/nhrp.c create mode 100644 src/vnet/nhrp/nhrp.h create mode 100644 src/vnet/nhrp/nhrp_api.c create mode 100644 src/vnet/nhrp/nhrp_cli.c create mode 100644 test/vpp_nhrp.py diff --git a/MAINTAINERS b/MAINTAINERS index fc7c430da0a..3d84bfb55ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -208,6 +208,11 @@ M: Florin Coras F: src/vnet/lisp-cp/ F: src/vnet/lisp-gpe/ +VNET GRE +I: gre +M: Neale Ranns +F: src/vnet/gre/ + VNET GSO I: gso M: Andrew Yourtchenko diff --git a/src/vat/api_format.c b/src/vat/api_format.c index 924d9c60290..e362672bbb2 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -12651,7 +12651,7 @@ api_gre_tunnel_add_del (vat_main_t * vam) u8 is_add = 1; u8 src_set = 0; u8 dst_set = 0; - u32 outer_fib_id = 0; + u32 outer_table_id = 0; u32 session_id = 0; u32 instance = ~0; int ret; @@ -12672,7 +12672,7 @@ api_gre_tunnel_add_del (vat_main_t * vam) { dst_set = 1; } - else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id)) + else if (unformat (line_input, "outer-table-id %d", &outer_table_id)) ; else if (unformat (line_input, "teb")) t_type = GRE_API_TUNNEL_TYPE_TEB; @@ -12702,7 +12702,7 @@ api_gre_tunnel_add_del (vat_main_t * vam) clib_memcpy (&mp->tunnel.dst, &dst, sizeof (mp->tunnel.dst)); mp->tunnel.instance = htonl (instance); - mp->tunnel.outer_fib_id = htonl (outer_fib_id); + mp->tunnel.outer_table_id = htonl (outer_table_id); mp->is_add = is_add; mp->tunnel.session_id = htons ((u16) session_id); mp->tunnel.type = htonl (t_type); @@ -12722,7 +12722,7 @@ static void vl_api_gre_tunnel_details_t_handler ntohl (mp->tunnel.instance), format_vl_api_address, &mp->tunnel.src, format_vl_api_address, &mp->tunnel.dst, - mp->tunnel.type, ntohl (mp->tunnel.outer_fib_id), + mp->tunnel.type, ntohl (mp->tunnel.outer_table_id), ntohl (mp->tunnel.session_id)); } @@ -12747,8 +12747,8 @@ static void vl_api_gre_tunnel_details_t_handler_json vat_json_object_add_address (node, "src", &mp->tunnel.src); vat_json_object_add_address (node, "dst", &mp->tunnel.dst); vat_json_object_add_uint (node, "tunnel_type", mp->tunnel.type); - vat_json_object_add_uint (node, "outer_fib_id", - ntohl (mp->tunnel.outer_fib_id)); + vat_json_object_add_uint (node, "outer_table_id", + ntohl (mp->tunnel.outer_table_id)); vat_json_object_add_uint (node, "session_id", mp->tunnel.session_id); } diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt index 658e8d9a696..11827734a60 100644 --- a/src/vnet/CMakeLists.txt +++ b/src/vnet/CMakeLists.txt @@ -1491,6 +1491,22 @@ list(APPEND VNET_HEADERS list(APPEND VNET_API_FILES syslog/syslog.api) +############################################################################## +# NHRP +############################################################################## + +list (APPEND VNET_SOURCES + nhrp/nhrp_api.c + nhrp/nhrp_cli.c + nhrp/nhrp.c +) + +list(APPEND VNET_HEADERS + nhrp/nhrp.h +) + +list(APPEND VNET_API_FILES nhrp/nhrp.api) + ############################################################################## # VNET Library ############################################################################## 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"); diff --git a/src/vnet/interface.c b/src/vnet/interface.c index 51c5d825812..b2166dcebae 100644 --- a/src/vnet/interface.c +++ b/src/vnet/interface.c @@ -1235,6 +1235,16 @@ vnet_sw_interface_is_p2p (vnet_main_t * vnm, u32 sw_if_index) return (hc->flags & VNET_HW_INTERFACE_CLASS_FLAG_P2P); } +int +vnet_sw_interface_is_nbma (vnet_main_t * vnm, u32 sw_if_index) +{ + vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index); + vnet_hw_interface_class_t *hc = + vnet_get_hw_interface_class (vnm, hw->hw_class_index); + + return (hc->flags & VNET_HW_INTERFACE_CLASS_FLAG_NBMA); +} + clib_error_t * vnet_interface_init (vlib_main_t * vm) { diff --git a/src/vnet/interface.h b/src/vnet/interface.h index 2bfb8db7444..ee64a81d250 100644 --- a/src/vnet/interface.h +++ b/src/vnet/interface.h @@ -373,6 +373,10 @@ typedef enum vnet_hw_interface_class_flags_t_ * @brief a point 2 point interface */ VNET_HW_INTERFACE_CLASS_FLAG_P2P = (1 << 0), + /** + * @brief a non-broadcast multiple access interface + */ + VNET_HW_INTERFACE_CLASS_FLAG_NBMA = (1 << 1), } vnet_hw_interface_class_flags_t; /* Layer-2 (e.g. Ethernet) interface class. */ @@ -496,6 +500,9 @@ typedef enum vnet_hw_interface_flags_t_ /* gso */ VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO = (1 << 18), + + /* non-broadcast multiple access */ + VNET_HW_INTERFACE_FLAG_NBMA = (1 << 19), } vnet_hw_interface_flags_t; #define VNET_HW_INTERFACE_FLAG_DUPLEX_SHIFT 1 diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h index 388a438c02b..f5c66d447de 100644 --- a/src/vnet/interface_funcs.h +++ b/src/vnet/interface_funcs.h @@ -239,6 +239,7 @@ clib_error_t *vnet_create_sw_interface (vnet_main_t * vnm, void vnet_delete_hw_interface (vnet_main_t * vnm, u32 hw_if_index); void vnet_delete_sw_interface (vnet_main_t * vnm, u32 sw_if_index); int vnet_sw_interface_is_p2p (vnet_main_t * vnm, u32 sw_if_index); +int vnet_sw_interface_is_nbma (vnet_main_t * vnm, u32 sw_if_index); always_inline vnet_sw_interface_flags_t vnet_sw_interface_get_flags (vnet_main_t * vnm, u32 sw_if_index) diff --git a/src/vnet/nhrp/nhrp.api b/src/vnet/nhrp/nhrp.api new file mode 100644 index 00000000000..de0630c9c88 --- /dev/null +++ b/src/vnet/nhrp/nhrp.api @@ -0,0 +1,57 @@ +/* Hey Emacs use -*- mode: C -*- */ +/* + * Copyright (c) 2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +option version = "1.0.0"; + +import "vnet/ip/ip_types.api"; +import "vnet/interface_types.api"; + +/** \brief NHRP Entry + @param sw_if_index +*/ +typedef nhrp_entry +{ + vl_api_interface_index_t sw_if_index; + vl_api_address_t peer; + vl_api_address_t nh; + u32 nh_table_id; +}; + +autoreply define nhrp_entry_add_del +{ + u32 client_index; + u32 context; + u8 is_add; + vl_api_nhrp_entry_t entry; +}; + +define nhrp_dump +{ + u32 client_index; + u32 context; +}; + +define nhrp_details +{ + u32 context; + vl_api_nhrp_entry_t entry; +}; + +/* + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/nhrp/nhrp.c b/src/vnet/nhrp/nhrp.c new file mode 100644 index 00000000000..6a616ba273d --- /dev/null +++ b/src/vnet/nhrp/nhrp.c @@ -0,0 +1,196 @@ +/* + * nhrp.h: next-hop resolution + * + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +static uword *nhrp_db; +static nhrp_entry_t *nhrp_pool; + +void +nhrp_entry_adj_stack (const nhrp_entry_t * ne, adj_index_t ai) +{ + adj_midchain_delegate_stack (ai, ne->ne_fib_index, &ne->ne_nh); +} + +static adj_walk_rc_t +nhrp_entry_add_adj_walk (adj_index_t ai, void *ctx) +{ + nhrp_entry_adj_stack (ctx, ai); + + return (ADJ_WALK_RC_CONTINUE); +} + +static adj_walk_rc_t +nhrp_entry_del_adj_walk (adj_index_t ai, void *ctx) +{ + adj_midchain_delegate_unstack (ai); + + return (ADJ_WALK_RC_CONTINUE); +} + +nhrp_entry_t * +nhrp_entry_get (index_t nei) +{ + return pool_elt_at_index (nhrp_pool, nei); +} + +nhrp_entry_t * +nhrp_entry_find (u32 sw_if_index, const ip46_address_t * peer) +{ + nhrp_key_t nk = { + .nk_peer = *peer, + .nk_sw_if_index = sw_if_index, + }; + uword *p; + + p = hash_get_mem (nhrp_db, &nk); + + if (NULL != p) + return nhrp_entry_get (p[0]); + + return (NULL); +} + +int +nhrp_entry_add (u32 sw_if_index, + const ip46_address_t * peer, + u32 nh_table_id, const ip46_address_t * nh) +{ + fib_protocol_t fproto; + nhrp_entry_t *ne; + u32 fib_index; + index_t nei; + + fproto = (ip46_address_is_ip4 (nh) ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6); + + fib_index = fib_table_find (fproto, nh_table_id); + + if (~0 == fib_index) + { + return (VNET_API_ERROR_NO_SUCH_FIB); + } + + ne = nhrp_entry_find (sw_if_index, peer); + + if (NULL == ne) + { + nhrp_key_t nk = { + .nk_peer = *peer, + .nk_sw_if_index = sw_if_index, + }; + nhrp_entry_t *ne; + + pool_get_zero (nhrp_pool, ne); + + nei = ne - nhrp_pool; + ne->ne_key = clib_mem_alloc (sizeof (*ne->ne_key)); + clib_memcpy (ne->ne_key, &nk, sizeof (*ne->ne_key)); + + ip46_address_copy (&ne->ne_nh.fp_addr, nh); + ne->ne_nh.fp_proto = fproto; + ne->ne_nh.fp_len = (ne->ne_nh.fp_proto == FIB_PROTOCOL_IP4 ? 32 : 128); + ne->ne_fib_index = fib_index; + + hash_set_mem (nhrp_db, ne->ne_key, nei); + + adj_nbr_walk_nh (sw_if_index, + ne->ne_nh.fp_proto, + &ne->ne_key->nk_peer, nhrp_entry_add_adj_walk, ne); + } + else + return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS); + + return 0; +} + +int +nhrp_entry_del (u32 sw_if_index, const ip46_address_t * peer) +{ + nhrp_entry_t *ne; + + ne = nhrp_entry_find (sw_if_index, peer); + + if (ne != NULL) + { + hash_unset_mem (nhrp_db, ne->ne_key); + + adj_nbr_walk_nh (sw_if_index, + ne->ne_nh.fp_proto, + &ne->ne_key->nk_peer, nhrp_entry_del_adj_walk, ne); + + clib_mem_free (ne->ne_key); + pool_put (nhrp_pool, ne); + } + else + return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS); + + return 0; +} + +u8 * +format_nhrp_entry (u8 * s, va_list * args) +{ + index_t nei = va_arg (*args, index_t); + vnet_main_t *vnm = vnet_get_main (); + nhrp_entry_t *ne; + + ne = nhrp_entry_get (nei); + + s = format (s, "[%d] ", nei); + s = format (s, "%U:%U ", format_vnet_sw_if_index_name, + vnm, ne->ne_key->nk_sw_if_index, + format_ip46_address, &ne->ne_key->nk_peer, IP46_TYPE_ANY); + s = format (s, "via %d:%U", + fib_table_get_table_id (ne->ne_fib_index, ne->ne_nh.fp_proto), + format_fib_prefix, &ne->ne_nh); + + return (s); +} + +void +nhrp_walk (nhrp_walk_cb_t fn, void *ctx) +{ + index_t nei; + + /* *INDENT-OFF* */ + pool_foreach_index(nei, nhrp_pool, + ({ + fn(nei, ctx); + })); + /* *INDENT-ON* */ +} + +static clib_error_t * +nhrp_init (vlib_main_t * vm) +{ + nhrp_db = hash_create_mem (0, sizeof (nhrp_key_t), sizeof (u32)); + + return (NULL); +} + +VLIB_INIT_FUNCTION (nhrp_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/nhrp/nhrp.h b/src/vnet/nhrp/nhrp.h new file mode 100644 index 00000000000..fa842feb658 --- /dev/null +++ b/src/vnet/nhrp/nhrp.h @@ -0,0 +1,62 @@ +/* + * nhrp.h: next-hop resolution + * + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NHRP_H__ +#define __NHRP_H__ + +#include + +typedef struct nhrp_key_t_ +{ + ip46_address_t nk_peer; + u32 nk_sw_if_index; +} nhrp_key_t; + +typedef struct nhrp_entry_t_ +{ + nhrp_key_t *ne_key; + fib_prefix_t ne_nh; + u32 ne_fib_index; +} nhrp_entry_t; + +extern u8 *format_nhrp_entry (u8 * s, va_list * args); + +extern int nhrp_entry_add (u32 sw_if_index, + const ip46_address_t * peer, + u32 nh_table_id, const ip46_address_t * nh); + +extern int nhrp_entry_del (u32 sw_if_index, const ip46_address_t * peer); + +extern nhrp_entry_t *nhrp_entry_find (u32 sw_if_index, + const ip46_address_t * peer); +extern nhrp_entry_t *nhrp_entry_get (index_t nei); + +extern void nhrp_entry_adj_stack (const nhrp_entry_t * ne, adj_index_t ai); + +typedef walk_rc_t (*nhrp_walk_cb_t) (index_t nei, void *ctx); + +extern void nhrp_walk (nhrp_walk_cb_t fn, void *ctx); + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/nhrp/nhrp_api.c b/src/vnet/nhrp/nhrp_api.c new file mode 100644 index 00000000000..6f07eb700e4 --- /dev/null +++ b/src/vnet/nhrp/nhrp_api.c @@ -0,0 +1,134 @@ +/* + *------------------------------------------------------------------ + * nhrp_api.c - nhrp api + * + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------ + */ + +#include +#include + +#include +#include +#include +#include + +/* define message IDs */ +#include +#include +#include + +static u32 nhrp_base_msg_id; +#define REPLY_MSG_ID_BASE nhrp_base_msg_id + +#include + +static void +vl_api_nhrp_entry_add_del_t_handler (vl_api_nhrp_entry_add_del_t * mp) +{ + vl_api_nhrp_entry_add_del_reply_t *rmp; + ip46_address_t peer, nh; + int rv; + + VALIDATE_SW_IF_INDEX ((&mp->entry)); + + ip_address_decode (&mp->entry.peer, &peer); + ip_address_decode (&mp->entry.nh, &nh); + + if (mp->is_add) + rv = nhrp_entry_add (ntohl (mp->entry.sw_if_index), &peer, + ntohl (mp->entry.nh_table_id), &nh); + else + rv = nhrp_entry_del (ntohl (mp->entry.sw_if_index), &peer); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_NHRP_ENTRY_ADD_DEL_REPLY); +} + +typedef struct vl_api_nhrp_send_t_ +{ + vl_api_registration_t *reg; + u32 context; +} vl_api_nhrp_send_t; + +static walk_rc_t +vl_api_nhrp_send_one (index_t nei, void *arg) +{ + vl_api_nhrp_details_t *mp; + vl_api_nhrp_send_t *ctx = arg; + const nhrp_entry_t *ne; + + mp = vl_msg_api_alloc (sizeof (*mp)); + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_NHRP_DETAILS + REPLY_MSG_ID_BASE); + mp->context = ctx->context; + + ne = nhrp_entry_get (nei); + + ip_address_encode (&ne->ne_key->nk_peer, IP46_TYPE_ANY, &mp->entry.peer); + ip_address_encode (&ne->ne_nh.fp_addr, IP46_TYPE_ANY, &mp->entry.nh); + mp->entry.nh_table_id = + htonl (fib_table_get_table_id (ne->ne_fib_index, ne->ne_nh.fp_proto)); + mp->entry.sw_if_index = htonl (ne->ne_key->nk_sw_if_index); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +static void +vl_api_nhrp_dump_t_handler (vl_api_nhrp_dump_t * mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + vl_api_nhrp_send_t ctx = { + .reg = reg, + .context = mp->context, + }; + + nhrp_walk (vl_api_nhrp_send_one, &ctx); +} + +/* + * nhrp_api_hookup + * Add vpe's API message handlers to the table. + * vlib has already mapped shared memory and + * added the client registration handlers. + * See .../vlib-api/vlibmemory/memclnt_vlib.c:memclnt_process() + */ +#include + +static clib_error_t * +nhrp_api_hookup (vlib_main_t * vm) +{ + nhrp_base_msg_id = setup_message_id_table (); + + return (NULL); +} + +VLIB_API_INIT_FUNCTION (nhrp_api_hookup); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/nhrp/nhrp_cli.c b/src/vnet/nhrp/nhrp_cli.c new file mode 100644 index 00000000000..654c750197e --- /dev/null +++ b/src/vnet/nhrp/nhrp_cli.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +static clib_error_t * +nhrp_add (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + ip46_address_t peer = ip46_address_initializer; + ip46_address_t nh = ip46_address_initializer; + u32 sw_if_index, nh_table_id; + clib_error_t *error = NULL; + int rv; + + sw_if_index = ~0; + nh_table_id = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U", unformat_vnet_sw_interface, + vnet_get_main (), &sw_if_index)) + ; + else if (unformat (line_input, "peer %U", unformat_ip46_address, &peer)) + ; + else if (unformat (line_input, "nh %U", unformat_ip46_address, &nh)) + ; + else if (unformat (line_input, "nh-table-id %d", &nh_table_id)) + ; + else + { + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (~0 == sw_if_index) + { + error = clib_error_return (0, "interface required'", + format_unformat_error, line_input); + goto done; + } + if (ip46_address_is_zero (&peer)) + { + error = clib_error_return (0, "peer required'", + format_unformat_error, line_input); + goto done; + } + if (ip46_address_is_zero (&nh)) + { + error = clib_error_return (0, "next-hop required'", + format_unformat_error, line_input); + goto done; + } + + rv = nhrp_entry_add (sw_if_index, &peer, nh_table_id, &nh); + + if (rv) + { + error = clib_error_return_code (NULL, rv, 0, + "NRHP error", + format_unformat_error, line_input); + } + +done: + unformat_free (line_input); + + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (nhrp_create_command, static) = { + .path = "create nhrp", + .short_help = "create nhrp peer nh [nh-table-id ]", + .function = nhrp_add, +}; +/* *INDENT-ON* */ + +static clib_error_t * +nhrp_del (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + ip46_address_t peer = ip46_address_initializer; + clib_error_t *error = NULL; + u32 sw_if_index; + int rv; + + sw_if_index = ~0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U", unformat_vnet_sw_interface, + vnet_get_main (), &sw_if_index)) + ; + else if (unformat (line_input, "peer %U", unformat_ip46_address, &peer)) + ; + else + { + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (~0 == sw_if_index) + { + error = clib_error_return (0, "interface required'", + format_unformat_error, line_input); + } + if (ip46_address_is_zero (&peer)) + { + error = clib_error_return (0, "peer required'", + format_unformat_error, line_input); + goto done; + } + + rv = nhrp_entry_del (sw_if_index, &peer); + + if (rv) + { + error = clib_error_return_code (NULL, rv, 0, + "NRHP error", + format_unformat_error, line_input); + } + +done: + unformat_free (line_input); + + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (nhrp_delete_command, static) = { + .path = "delete nhrp", + .short_help = "delete nhrp peer ", + .function = nhrp_del, +}; +/* *INDENT-ON* */ + +static walk_rc_t +nhrp_show_one (index_t nei, void *ctx) +{ + vlib_cli_output (ctx, "%U", format_nhrp_entry, nei); + + return (WALK_CONTINUE); +} + + +static clib_error_t * +nhrp_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + nhrp_walk (nhrp_show_one, vm); + return (NULL); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (nhrp_show_command, static) = { + .path = "show nhrp", + .short_help = "show nhrp", + .function = nhrp_show, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index 4421cc9671f..982d66dec85 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -1792,8 +1792,8 @@ static void *vl_api_gre_tunnel_add_del_t_print if (mp->tunnel.type == GRE_API_TUNNEL_TYPE_ERSPAN) s = format (s, "erspan %d ", (mp->tunnel.session_id)); - if (mp->tunnel.outer_fib_id) - s = format (s, "outer-fib-id %d ", (mp->tunnel.outer_fib_id)); + if (mp->tunnel.outer_table_id) + s = format (s, "outer-table-id %d ", mp->tunnel.outer_table_id); if (mp->is_add == 0) s = format (s, "del "); diff --git a/test/test_gre.py b/test/test_gre.py index 6828ac6f919..d3d12cc26bb 100644 --- a/test/test_gre.py +++ b/test/test_gre.py @@ -12,6 +12,7 @@ from scapy.volatile import RandMAC, RandIP from framework import VppTestCase, VppTestRunner from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint from vpp_gre_interface import VppGreInterface +from vpp_nhrp import VppNhrp from vpp_ip import DpoProto from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, FibPathProto from util import ppp, ppc @@ -79,7 +80,7 @@ class TestGRE(VppTestCase): super(TestGRE, self).setUp() # create 3 pg interfaces - set one in a non-default table. - self.create_pg_interfaces(range(3)) + self.create_pg_interfaces(range(5)) self.tbl = VppIpTable(self, 1) self.tbl.add_vpp_config() @@ -94,6 +95,10 @@ class TestGRE(VppTestCase): self.pg1.resolve_arp() self.pg2.config_ip6() self.pg2.resolve_ndp() + self.pg3.config_ip4() + self.pg3.resolve_arp() + self.pg4.config_ip4() + self.pg4.resolve_arp() def tearDown(self): for i in self.pg_interfaces: @@ -701,7 +706,7 @@ class TestGRE(VppTestCase): # gre_if = VppGreInterface(self, self.pg1.local_ip4, "2.2.2.2", - outer_fib_id=1) + outer_table_id=1) gre_if.add_vpp_config() gre_if.admin_up() gre_if.config_ip4() @@ -968,6 +973,77 @@ class TestGRE(VppTestCase): route_via_tun_2.remove_vpp_config() gre_if.remove_vpp_config() + def test_mgre(self): + """ mGRE IPv4 tunnel Tests """ + + for itf in self.pg_interfaces[3:]: + # + # one underlay nh for each overlay/tunnel peer + # + itf.generate_remote_hosts(4) + itf.configure_ipv4_neighbors() + + # + # Create an L3 GRE tunnel. + # - set it admin up + # - assign an IP Addres + # - Add a route via the tunnel + # + gre_if = VppGreInterface(self, + itf.local_ip4, + "0.0.0.0", + mode=(VppEnum.vl_api_gre_tunnel_mode_t. + GRE_API_TUNNEL_MODE_MP)) + gre_if.add_vpp_config() + gre_if.admin_up() + gre_if.config_ip4() + gre_if.generate_remote_hosts(4) + + # + # for-each peer + # + for ii in range(1, 4): + route_addr = "4.4.4.%d" % ii + + # + # route traffic via the peer + # + route_via_tun = VppIpRoute( + self, route_addr, 32, + [VppRoutePath(gre_if._remote_hosts[ii].ip4, + gre_if.sw_if_index)]) + route_via_tun.add_vpp_config() + + # + # Add a NHRP entry resolves the peer + # + nhrp = VppNhrp(self, gre_if, + gre_if._remote_hosts[ii].ip4, + itf._remote_hosts[ii].ip4) + nhrp.add_vpp_config() + + # + # Send a packet stream that is routed into the tunnel + # - packets are GRE encapped + # + tx = self.create_stream_ip4(self.pg0, "5.5.5.5", route_addr) + rx = self.send_and_expect(self.pg0, tx, itf) + self.verify_tunneled_4o4(self.pg0, rx, tx, + itf.local_ip4, + gre_if._remote_hosts[ii].ip4) + + # + # delete and re-add the NHRP + # + nhrp.remove_vpp_config() + self.send_and_assert_no_replies(self.pg0, tx) + + nhrp.add_vpp_config() + rx = self.send_and_expect(self.pg0, tx, itf) + self.verify_tunneled_4o4(self.pg0, rx, tx, + itf.local_ip4, + gre_if._remote_hosts[ii].ip4) + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_gre_interface.py b/test/vpp_gre_interface.py index 333fc0306ea..905c3832532 100644 --- a/test/vpp_gre_interface.py +++ b/test/vpp_gre_interface.py @@ -9,25 +9,32 @@ class VppGreInterface(VppInterface): VPP GRE interface """ - def __init__(self, test, src_ip, dst_ip, outer_fib_id=0, type=None, + def __init__(self, test, src_ip, dst_ip, outer_table_id=0, + type=None, mode=None, session=0): """ Create VPP GRE interface """ super(VppGreInterface, self).__init__(test) self.t_src = src_ip self.t_dst = dst_ip - self.t_outer_fib = outer_fib_id + self.t_outer_table = outer_table_id self.t_session = session self.t_type = type if not self.t_type: self.t_type = (VppEnum.vl_api_gre_tunnel_type_t. GRE_API_TUNNEL_TYPE_L3) + self.t_mode = mode + if not self.t_mode: + self.t_mode = (VppEnum.vl_api_gre_tunnel_mode_t. + GRE_API_TUNNEL_MODE_P2P) def add_vpp_config(self): - r = self.test.vapi.gre_tunnel_add_del(self.t_src, - self.t_dst, - outer_fib_id=self.t_outer_fib, - tunnel_type=self.t_type, - session_id=self.t_session) + r = self.test.vapi.gre_tunnel_add_del( + self.t_src, + self.t_dst, + outer_table_id=self.t_outer_table, + type=self.t_type, + mode=self.t_mode, + 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) @@ -37,8 +44,9 @@ class VppGreInterface(VppInterface): self.unconfig() self.test.vapi.gre_tunnel_add_del(self.t_src, self.t_dst, - outer_fib_id=self.t_outer_fib, - tunnel_type=self.t_type, + outer_table_id=self.t_outer_table, + type=self.t_type, + mode=self.t_mode, session_id=self.t_session, is_add=0) diff --git a/test/vpp_nhrp.py b/test/vpp_nhrp.py new file mode 100644 index 00000000000..e04e3054fd2 --- /dev/null +++ b/test/vpp_nhrp.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +""" + NHRP objects +""" + +from vpp_object import VppObject + + +def find_nhrp(test, ne): + ns = test.vapi.nhrp_dump() + for n in ns: + if ne.peer == str(n.entry.peer) \ + and ne.itf._sw_if_index == n.entry.sw_if_index: + return True + return False + + +class VppNhrp(VppObject): + + def __init__(self, test, itf, peer, nh, table_id=0): + self._test = test + self.table_id = table_id + self.peer = peer + self.itf = itf + self.nh = nh + + def add_vpp_config(self): + r = self._test.vapi.nhrp_entry_add_del( + is_add=1, + entry={ + 'nh_table_id': self.table_id, + 'sw_if_index': self.itf.sw_if_index, + 'peer': self.peer, + 'nh': self.nh, + }) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + r = self._test.vapi.nhrp_entry_add_del( + is_add=0, + entry={ + 'nh_table_id': self.table_id, + 'sw_if_index': self.itf.sw_if_index, + 'peer': self.peer, + }) + + def query_vpp_config(self): + return find_nhrp(self._test, self) + + def object_id(self): + return ("nhrp-%s-%s" % (self.itf, self.peer)) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 556145ac97e..65b58f15f52 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -530,8 +530,9 @@ class VppPapiProvider(object): def gre_tunnel_add_del(self, src, dst, - outer_fib_id=0, - tunnel_type=0, + outer_table_id=0, + type=0, + mode=0, instance=0xFFFFFFFF, session_id=0, is_add=1): @@ -552,11 +553,12 @@ class VppPapiProvider(object): {'is_add': is_add, 'tunnel': { - 'type': tunnel_type, + 'type': type, + 'mode': mode, 'instance': instance, 'src': src, 'dst': dst, - 'outer_fib_id': outer_fib_id, + 'outer_table_id': outer_table_id, 'session_id': session_id} } ) -- 2.16.6