diff options
author | Neale Ranns <nranns@cisco.com> | 2019-04-18 10:23:56 +0000 |
---|---|---|
committer | Ole Trøan <otroan@employees.org> | 2019-12-04 22:45:11 +0000 |
commit | 5f8f6173328f8d77feea5fd100e150c3094c11f0 (patch) | |
tree | 16849c6e7619b227a93ce9846f344da2cc96ef2d /src/vnet | |
parent | 79619c10142e15754e2f0b2ba26c20d415e7c36f (diff) |
gre: Multi-point interfaces
Type: feature
Change-Id: I0129ad6ace44a50a8a3b26db8e445cd06b2b49e8
Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'src/vnet')
-rw-r--r-- | src/vnet/CMakeLists.txt | 16 | ||||
-rw-r--r-- | src/vnet/gre/gre.api | 23 | ||||
-rw-r--r-- | src/vnet/gre/gre.c | 61 | ||||
-rw-r--r-- | src/vnet/gre/gre.h | 57 | ||||
-rw-r--r-- | src/vnet/gre/gre_api.c | 77 | ||||
-rw-r--r-- | src/vnet/gre/interface.c | 140 | ||||
-rw-r--r-- | src/vnet/interface.c | 10 | ||||
-rw-r--r-- | src/vnet/interface.h | 7 | ||||
-rw-r--r-- | src/vnet/interface_funcs.h | 1 | ||||
-rw-r--r-- | src/vnet/nhrp/nhrp.api | 57 | ||||
-rw-r--r-- | src/vnet/nhrp/nhrp.c | 196 | ||||
-rw-r--r-- | src/vnet/nhrp/nhrp.h | 62 | ||||
-rw-r--r-- | src/vnet/nhrp/nhrp_api.c | 134 | ||||
-rw-r--r-- | src/vnet/nhrp/nhrp_cli.c | 194 |
14 files changed, 916 insertions, 119 deletions
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 @@ -1492,6 +1492,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 <vnet/vnet.h> #include <vnet/gre/gre.h> #include <vnet/adj/adj_midchain.h> +#include <vnet/nhrp/nhrp.h> 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 <vnet/adj/adj_types.h> extern vnet_hw_interface_class_t gre_hw_interface_class; +extern vnet_hw_interface_class_t mgre_hw_interface_class; typedef enum { @@ -36,34 +37,42 @@ typedef enum } 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); } @@ -122,11 +146,16 @@ static void vl_api_gre_tunnel_add_del_t_handler if (rv) goto out; + rv = gre_tunnel_mode_decode (mp->tunnel.mode, &a->mode); + + 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_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 <vnet/mpls/mpls.h> #include <vnet/l2/l2_input.h> -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 <vnet/nhrp/nhrp.h> +#include <vnet/fib/fib_table.h> +#include <vnet/adj/adj_midchain.h> + +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 <vnet/ip/ip.h> + +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 <vnet/vnet.h> +#include <vlibmemory/api.h> + +#include <vnet/api_errno.h> +#include <vnet/nhrp/nhrp.h> +#include <vnet/ip/ip_types_api.h> +#include <vnet/fib/fib_table.h> + +/* define message IDs */ +#include <vnet/format_fns.h> +#include <vnet/nhrp/nhrp.api_enum.h> +#include <vnet/nhrp/nhrp.api_types.h> + +static u32 nhrp_base_msg_id; +#define REPLY_MSG_ID_BASE nhrp_base_msg_id + +#include <vlibapi/api_helper_macros.h> + +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 <vnet/nhrp/nhrp.api.c> + +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 <vnet/nhrp/nhrp.h> + +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 <interface> peer <addr> nh <addr> [nh-table-id <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 <interface> peer <addr>", + .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: + */ |