/* * gre_interface.c: gre interfaces * * Copyright (c) 2012 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 #include #include #include 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); } static u8 * format_gre_tunnel (u8 * s, va_list * args) { gre_tunnel_t *t = va_arg (*args, gre_tunnel_t *); s = format (s, "[%d] instance %d src %U dst %U fib-idx %d sw-if-idx %d ", t->dev_instance, t->user_instance, format_ip46_address, &t->tunnel_src, IP46_TYPE_ANY, format_ip46_address, &t->tunnel_dst.fp_addr, IP46_TYPE_ANY, t->outer_fib_index, t->sw_if_index); s = format (s, "payload %U ", format_gre_tunnel_type, t->type); s = format (s, "%U ", format_tunnel_mode, t->mode); if (t->type == GRE_TUNNEL_TYPE_ERSPAN) s = format (s, "session %d ", t->session_id); if (t->type != GRE_TUNNEL_TYPE_L3) s = format (s, "l2-adj-idx %d ", t->l2_adj_index); return s; } static gre_tunnel_t * gre_tunnel_db_find (const vnet_gre_tunnel_add_del_args_t * a, u32 outer_fib_index, gre_tunnel_key_t * key) { gre_main_t *gm = &gre_main; uword *p; if (!a->is_ipv6) { gre_mk_key4 (a->src.ip4, a->dst.ip4, outer_fib_index, a->type, a->mode, a->session_id, &key->gtk_v4); p = hash_get_mem (gm->tunnel_by_key4, &key->gtk_v4); } else { gre_mk_key6 (&a->src.ip6, &a->dst.ip6, outer_fib_index, a->type, a->mode, a->session_id, &key->gtk_v6); p = hash_get_mem (gm->tunnel_by_key6, &key->gtk_v6); } if (NULL == p) return (NULL); return (pool_elt_at_index (gm->tunnels, p[0])); } static void gre_tunnel_db_add (gre_tunnel_t * t, gre_tunnel_key_t * key) { gre_main_t *gm = &gre_main; if (t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6) { hash_set_mem_alloc (&gm->tunnel_by_key6, &key->gtk_v6, t->dev_instance); } else { hash_set_mem_alloc (&gm->tunnel_by_key4, &key->gtk_v4, t->dev_instance); } } static void gre_tunnel_db_remove (gre_tunnel_t * t, gre_tunnel_key_t * key) { gre_main_t *gm = &gre_main; if (t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6) { hash_unset_mem_free (&gm->tunnel_by_key6, &key->gtk_v6); } else { hash_unset_mem_free (&gm->tunnel_by_key4, &key->gtk_v4); } } /** * gre_tunnel_stack * * 'stack' (resolve the recursion for) the tunnel's midchain adjacency */ void gre_tunnel_stack (adj_index_t ai) { gre_main_t *gm = &gre_main; ip_adjacency_t *adj; gre_tunnel_t *gt; u32 sw_if_index; adj = adj_get (ai); sw_if_index = adj->rewrite_header.sw_if_index; if ((vec_len (gm->tunnel_index_by_sw_if_index) <= sw_if_index) || (~0 == gm->tunnel_index_by_sw_if_index[sw_if_index])) return; gt = pool_elt_at_index (gm->tunnels, gm->tunnel_index_by_sw_if_index[sw_if_index]); if ((vnet_hw_interface_get_flags (vnet_get_main (), gt->hw_if_index) & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0) { adj_midchain_delegate_unstack (ai); } else { adj_midchain_delegate_stack (ai, gt->outer_fib_index, >->tunnel_dst); } } /** * @brief Call back when restacking all adjacencies on a GRE interface */ static adj_walk_rc_t gre_adj_walk_cb (adj_index_t ai, void *ctx) { gre_tunnel_stack (ai); return (ADJ_WALK_RC_CONTINUE); } static void gre_tunnel_restack (gre_tunnel_t * gt) { fib_protocol_t proto; /* * walk all the adjacencies on th GRE interface and restack them */ FOR_EACH_FIB_IP_PROTOCOL (proto) { adj_nbr_walk (gt->sw_if_index, proto, gre_adj_walk_cb, NULL); } } static void gre_teib_mk_key (const gre_tunnel_t * t, const teib_entry_t * ne, gre_tunnel_key_t * key) { const fib_prefix_t *nh; nh = teib_entry_get_nh (ne); /* construct the key using mode P2P so it can be found in the DP */ if (FIB_PROTOCOL_IP4 == nh->fp_proto) gre_mk_key4 (t->tunnel_src.ip4, nh->fp_addr.ip4, teib_entry_get_fib_index (ne), t->type, TUNNEL_MODE_P2P, 0, &key->gtk_v4); else gre_mk_key6 (&t->tunnel_src.ip6, &nh->fp_addr.ip6, teib_entry_get_fib_index (ne), t->type, TUNNEL_MODE_P2P, 0, &key->gtk_v6); } /** * An NHRP entry has been added */ static void gre_teib_entry_added (const teib_entry_t * ne) { gre_main_t *gm = &gre_main; const ip_address_t *nh; gre_tunnel_key_t key; gre_tunnel_t *t; u32 sw_if_index; u32 t_idx; sw_if_index = teib_entry_get_sw_if_index (ne); if (vec_len (gm->tunnel_index_by_sw_if_index) < sw_if_index) re
/*
 * 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.
 */
 
lisp_gpe_error (ENCAPSULATED, "good packets encapsulated")
lisp_gpe_error (DECAPSULATED, "good packets decapsulated")
lisp_gpe_error (NO_TUNNEL, "tunnel does not exist")
o done; } } if (ip46_address_is_equal (&src, &dst)) { error = clib_error_return (0, "src and dst are identical"); goto done; } if (t_mode != TUNNEL_MODE_MP && ip46_address_is_zero (&dst)) { error = clib_error_return (0, "destination address not specified"); goto done; } if (ip46_address_is_zero (&src)) { error = clib_error_return (0, "source address not specified"); goto done; } if (ip46_address_is_ip4 (&src) != ip46_address_is_ip4 (&dst)) { 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_table_id = outer_table_id; a->type = t_type; a->mode = t_mode; a->session_id = session_id; a->is_ipv6 = !ip46_address_is_ip4 (&src); a->instance = instance; 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); switch (rv) { case 0: vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index); break; case VNET_API_ERROR_IF_ALREADY_EXISTS: error = clib_error_return (0, "GRE tunnel already exists..."); goto done; case VNET_API_ERROR_NO_SUCH_FIB: 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"); goto done; case VNET_API_ERROR_INVALID_SESSION_ID: error = clib_error_return (0, "session ID %d out of range\n", session_id); goto done; case VNET_API_ERROR_INSTANCE_IN_USE: error = clib_error_return (0, "Instance is in use"); goto done; default: error = clib_error_return (0, "vnet_gre_tunnel_add_del returned %d", rv); goto done; } done: unformat_free (line_input); return error; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (create_gre_tunnel_command, static) = { .path = "create gre tunnel", .short_help = "create gre tunnel src dst [instance ] " "[outer-fib-id ] [teb | erspan ] [del] " "[multipoint]", .function = create_gre_tunnel_command_fn, }; /* *INDENT-ON* */ static clib_error_t * show_gre_tunnel_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { gre_main_t *gm = &gre_main; gre_tunnel_t *t; u32 ti = ~0; if (pool_elts (gm->tunnels) == 0) vlib_cli_output (vm, "No GRE tunnels configured..."); while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "%d", &ti)) ; else break; } if (~0 == ti) { /* *INDENT-OFF* */ pool_foreach (t, gm->tunnels, ({ vlib_cli_output (vm, "%U", format_gre_tunnel, t); })); /* *INDENT-ON* */ } else { t = pool_elt_at_index (gm->tunnels, ti); vlib_cli_output (vm, "%U", format_gre_tunnel, t); } return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_gre_tunnel_command, static) = { .path = "show gre tunnel", .function = show_gre_tunnel_command_fn, }; /* *INDENT-ON* */ const static teib_vft_t gre_teib_vft = { .nv_added = gre_teib_entry_added, .nv_deleted = gre_teib_entry_deleted, }; /* force inclusion from application's main.c */ clib_error_t * gre_interface_init (vlib_main_t * vm) { teib_register (&gre_teib_vft); return (NULL); } VLIB_INIT_FUNCTION (gre_interface_init); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */