diff options
author | 2016-04-21 00:45:40 +0200 | |
---|---|---|
committer | 2016-04-25 13:05:49 +0000 | |
commit | 577c3553dd6939e998c78c5a389750aac3a67f8b (patch) | |
tree | 3a95da61f5cfca2d1629a8011d2a6b57ae19bad2 /vnet | |
parent | 405e41b50e336dccfdeeafae93bf4453774ecfec (diff) |
Convert lisp-gpe encap to interface tx node
With this change, one lisp-gpe interface is created per vrf/overlay
tenant and its tx node is used as encapsulator (or tunnel ingress). For
all intents and purposes, the tx node inherits all functions previously
performed by the lisp-gpe-encap node and it maintains said node's
position in lisp-gpe's data-path graph. Chiefly, this opens the
possibility to chain interface features, like IPSec, transparently with
LISP. Furthermore, it brings basic data plane support for vrfs and LISP
instance-ids (or virtual network instances as per RFC7364).
Other changes include improvements to lisp-gpe enable and disable
sequences and corresponding API/VAT fixes.
Change-Id: I085500450660a976b587b1a720e282f6e728d580
Signed-off-by: Florin Coras <fcoras@cisco.com>
Diffstat (limited to 'vnet')
-rw-r--r-- | vnet/Makefile.am | 2 | ||||
-rw-r--r-- | vnet/vnet/lisp-cp/control.c | 100 | ||||
-rw-r--r-- | vnet/vnet/lisp-cp/control.h | 10 | ||||
-rw-r--r-- | vnet/vnet/lisp-gpe/decap.c | 65 | ||||
-rw-r--r-- | vnet/vnet/lisp-gpe/encap.c | 232 | ||||
-rw-r--r-- | vnet/vnet/lisp-gpe/interface.c | 516 | ||||
-rw-r--r-- | vnet/vnet/lisp-gpe/lisp_gpe.c | 425 | ||||
-rw-r--r-- | vnet/vnet/lisp-gpe/lisp_gpe.h | 36 | ||||
-rw-r--r-- | vnet/vnet/lisp-gpe/lisp_gpe_error.def | 1 |
9 files changed, 856 insertions, 531 deletions
diff --git a/vnet/Makefile.am b/vnet/Makefile.am index 2ce40f3e..f7c1f30b 100644 --- a/vnet/Makefile.am +++ b/vnet/Makefile.am @@ -477,7 +477,7 @@ endif libvnet_la_SOURCES += \ vnet/lisp-gpe/lisp_gpe.c \ - vnet/lisp-gpe/encap.c \ + vnet/lisp-gpe/interface.c \ vnet/lisp-gpe/decap.c nobase_include_HEADERS += \ diff --git a/vnet/vnet/lisp-cp/control.c b/vnet/vnet/lisp-cp/control.c index 3448fb3e..f067cf6d 100644 --- a/vnet/vnet/lisp-cp/control.c +++ b/vnet/vnet/lisp-cp/control.c @@ -18,7 +18,8 @@ #include <vnet/lisp-cp/lisp_msg_serdes.h> #include <vnet/lisp-gpe/lisp_gpe.h> -/* Adds mapping to map-cache but does NOT program LISP forwarding */ +/* Stores mapping in map-cache. It does NOT program data plane forwarding for + * remote/learned mappings. */ int vnet_lisp_add_del_mapping (vnet_lisp_add_del_mapping_args_t * a, u32 * map_index_result) @@ -32,11 +33,12 @@ vnet_lisp_add_del_mapping (vnet_lisp_add_del_mapping_args_t * a, if (a->is_add) { /* TODO check if overwriting and take appropriate actions */ - if (mi != GID_LOOKUP_MISS) { + if (mi != GID_LOOKUP_MISS) + { clib_warning("eid %U found in the eid-table", format_ip_address, &a->deid); return VNET_API_ERROR_VALUE_EXIST; - } + } pool_get(lcm->mapping_pool, m); m->eid = a->deid; @@ -65,18 +67,17 @@ vnet_lisp_add_del_mapping (vnet_lisp_add_del_mapping_args_t * a, { /* mark as local */ vec_add1(lcm->local_mappings_indexes, map_index); - - /* XXX do something else? */ } map_index_result[0] = map_index; } else { - if (mi == GID_LOOKUP_MISS) { + if (mi == GID_LOOKUP_MISS) + { clib_warning("eid %U not found in the eid-table", format_ip_address, &a->deid); return VNET_API_ERROR_INVALID_VALUE; - } + } /* clear locator-set to eids binding */ eid_indexes = vec_elt_at_index(lcm->locator_set_to_eids, @@ -115,6 +116,66 @@ vnet_lisp_add_del_mapping (vnet_lisp_add_del_mapping_args_t * a, return 0; } +/* Stores mapping in map-cache and programs data plane for local mappings. */ +int +vnet_lisp_add_del_local_mapping (vnet_lisp_add_del_mapping_args_t * a, + u32 * map_index_result) +{ + uword * table_id, * refc; + u32 rv; + vnet_lisp_gpe_add_del_iface_args_t _ai, *ai = &_ai; + lisp_cp_main_t * lcm = vnet_lisp_cp_get_main (); + + /* store/remove mapping from map-cache */ + rv = vnet_lisp_add_del_mapping (a, map_index_result); + if (rv) + return rv; + + table_id = hash_get(lcm->table_id_by_vni, /* default for now */ 0); + + if (!table_id) + { + clib_warning ("vni %d not associated to a vrf!", 0); + return VNET_API_ERROR_INVALID_VALUE; + } + + refc = hash_get(lcm->dp_if_refcount_by_vni, 0); + + /* enable/disable data-plane interface */ + if (a->is_add) + { + /* create interface or update refcount */ + if (!refc) + { + ai->is_add = 1; + ai->vni = 0; /* default for now, pass vni as parameter */ + ai->table_id = table_id[0]; + vnet_lisp_gpe_add_del_iface (ai, 0); + } + else + { + refc[0]++; + } + } + else + { + /* since this is a remove for an existing eid, the iface should exist */ + ASSERT(refc != 0); + refc[0]--; + + /* remove iface if needed */ + if (refc[0] == 0) + { + ai->is_add = 0; + ai->vni = 0; /* default for now, pass vni as parameter */ + ai->table_id = table_id[0]; + vnet_lisp_gpe_add_del_iface (ai, 0); + } + } + + return rv; +} + static clib_error_t * lisp_add_del_local_eid_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -171,7 +232,7 @@ lisp_add_del_local_eid_command_fn (vlib_main_t * vm, unformat_input_t * input, a->locator_set_index = locator_set_index; a->local = 1; - vnet_lisp_add_del_mapping (a, &map_index); + vnet_lisp_add_del_local_mapping (a, &map_index); done: vec_free(eids); return error; @@ -650,7 +711,8 @@ lisp_add_del_locator_set_command_fn (vlib_main_t * vm, unformat_input_t * input, VLIB_CLI_COMMAND (lisp_cp_add_del_locator_set_command) = { .path = "lisp locator-set", - .short_help = "lisp locator-set add/del <name> <iface-name> <priority> <weight>", + .short_help = "lisp locator-set add/del <name> iface <iface-name> " + "<priority> <weight>", .function = lisp_add_del_locator_set_command_fn, }; @@ -1251,7 +1313,7 @@ del_fwd_entry (lisp_cp_main_t * lcm, u32 src_map_index, a->is_add = 0; a->dlocator = fe->dst_loc; a->slocator = fe->src_loc; - a->iid = 0; // XXX should be part of mapping/eid + a->vni = 0; // XXX should be part of mapping/eid gid_address_copy(&a->deid, &fe->deid); vnet_lisp_gpe_add_del_fwd_entry (a, &sw_if_index); @@ -1268,8 +1330,9 @@ add_fwd_entry (lisp_cp_main_t* lcm, u32 src_map_index, u32 dst_map_index) locator_set_t * dst_ls, * src_ls; u32 i, minp = ~0; locator_t * dl = 0; - uword * feip = 0; + uword * feip = 0, * tidp; vnet_lisp_gpe_add_del_fwd_entry_args_t _a, * a = &_a; + memset (a, 0, sizeof(*a)); /* remove entry if it already exists */ @@ -1334,10 +1397,20 @@ add_fwd_entry (lisp_cp_main_t* lcm, u32 src_map_index, u32 dst_map_index) } gid_address_copy (&a->deid, &dst_map->eid); - a->iid = 0; // XXX should be part of mapping/eid + a->vni = 0; // XXX should be part of mapping/eid + + tidp = hash_get(lcm->table_id_by_vni, a->vni); + if (!tidp) + { + clib_warning("vni %d not associated to a vrf!", a->vni); + return; + } + a->table_id = tidp[0]; + u8 ipver = ip_prefix_version(&gid_address_ippref(&a->deid)); a->decap_next_index = (ipver == IP4) ? LISP_GPE_INPUT_NEXT_IP4_INPUT : LISP_GPE_INPUT_NEXT_IP6_INPUT; + /* XXX tunnels work only with IP4 now */ vnet_lisp_gpe_add_del_fwd_entry (a, &sw_if_index); @@ -1611,6 +1684,9 @@ lisp_cp_init (vlib_main_t *vm) gid_dictionary_init (&lcm->mapping_index_by_gid); gid_dictionary_init (&lcm->mapping_index_by_gid); + /* default vrf mapped to vni 0 */ + hash_set(lcm->table_id_by_vni, 0, 0); + udp_register_dst_port (vm, UDP_DST_PORT_lisp_cp, lisp_cp_input_node.index, 1 /* is_ip4 */); udp_register_dst_port (vm, UDP_DST_PORT_lisp_cp6, diff --git a/vnet/vnet/lisp-cp/control.h b/vnet/vnet/lisp-cp/control.h index 53d90fca..f87a6d5e 100644 --- a/vnet/vnet/lisp-cp/control.h +++ b/vnet/vnet/lisp-cp/control.h @@ -91,6 +91,12 @@ typedef struct /* vector of map-resolver addresses */ ip_address_t * map_resolvers; + /* Lookup vrf by vni */ + uword * table_id_by_vni; + + /* Number of src prefixes in a vni that use an interface */ + uword * dp_if_refcount_by_vni; + /* commodity */ ip4_main_t * im4; ip6_main_t * im6; @@ -122,7 +128,6 @@ typedef struct int vnet_lisp_add_del_locator_set (vnet_lisp_add_del_locator_set_args_t * a, u32 * ls_index); - int vnet_lisp_add_del_locator_set_name (vnet_lisp_add_del_locator_set_args_t * a, u32 * ls_index); @@ -146,6 +151,9 @@ typedef struct int vnet_lisp_add_del_mapping (vnet_lisp_add_del_mapping_args_t *a, u32 * map_index); +int +vnet_lisp_add_del_local_mapping (vnet_lisp_add_del_mapping_args_t * a, + u32 * map_index_result); typedef struct { diff --git a/vnet/vnet/lisp-gpe/decap.c b/vnet/vnet/lisp-gpe/decap.c index 356dbf2e..5d88462b 100644 --- a/vnet/vnet/lisp-gpe/decap.c +++ b/vnet/vnet/lisp-gpe/decap.c @@ -52,7 +52,6 @@ next_proto_to_next_index[LISP_GPE_NEXT_PROTOS] ={ LISP_GPE_INPUT_NEXT_DROP, LISP_GPE_INPUT_NEXT_IP4_INPUT, LISP_GPE_INPUT_NEXT_IP6_INPUT, - LISP_GPE_INPUT_NEXT_DROP, LISP_GPE_INPUT_NEXT_DROP }; @@ -85,6 +84,7 @@ lisp_gpe_input (vlib_main_t * vm, vlib_node_runtime_t * node, u32 n_left_from, next_index, * from, * to_next; lisp_gpe_tunnel_key_t last_key; u32 pkts_decapsulated = 0; + lisp_gpe_main_t * lgm = &lisp_gpe_main; memset (&last_key, 0xff, sizeof (last_key)); @@ -104,8 +104,8 @@ lisp_gpe_input (vlib_main_t * vm, vlib_node_runtime_t * node, u32 bi0, bi1; vlib_buffer_t * b0, * b1; ip4_udp_lisp_gpe_header_t * iul0, * iul1; - u32 error0, error1; - u32 next0, next1; + u32 next0, next1, error0, error1; + uword * si0, * si1; next0 = next1 = LISP_GPE_INPUT_NEXT_IP4_INPUT; @@ -156,14 +156,35 @@ lisp_gpe_input (vlib_main_t * vm, vlib_node_runtime_t * node, vnet_update_l2_len (b0); vnet_update_l2_len (b1); - /* TODO hash to map iid to fib */ - vnet_buffer(b0)->sw_if_index[VLIB_TX] = iul0->lisp.iid; - vnet_buffer(b1)->sw_if_index[VLIB_TX] = iul1->lisp.iid; + /* map iid/vni to lisp-gpe sw_if_index which is used by ipx_input to + * decide the rx vrf and the input features to be applied */ + si0 = hash_get(lgm->tunnel_term_sw_if_index_by_vni, iul0->lisp.iid); + si1 = hash_get(lgm->tunnel_term_sw_if_index_by_vni, iul1->lisp.iid); - pkts_decapsulated += 2; + if (si0) + { + vnet_buffer(b0)->sw_if_index[VLIB_RX] = si0[0]; + pkts_decapsulated++; + error0 = 0; + } + else + { + next0 = LISP_GPE_INPUT_NEXT_DROP; + error0 = LISP_GPE_ERROR_NO_SUCH_TUNNEL; + } + + if (si1) + { + vnet_buffer(b1)->sw_if_index[VLIB_RX] = si1[0]; + pkts_decapsulated++; + error0 = 0; + } + else + { + next1 = LISP_GPE_INPUT_NEXT_DROP; + error1 = LISP_GPE_ERROR_NO_SUCH_TUNNEL; + } - /* TODO error handling if security is implemented */ - error0 = error1 = 0; b0->error = error0 ? node->errors[error0] : 0; b1->error = error1 ? node->errors[error1] : 0; @@ -197,6 +218,7 @@ lisp_gpe_input (vlib_main_t * vm, vlib_node_runtime_t * node, u32 next0; ip4_udp_lisp_gpe_header_t * iul0; u32 error0; + uword * si0; bi0 = from[0]; to_next[0] = bi0; @@ -228,18 +250,29 @@ lisp_gpe_input (vlib_main_t * vm, vlib_node_runtime_t * node, /* Required to make the l2 tag push / pop code work on l2 subifs */ vnet_update_l2_len (b0); - /* TODO hash to map iid to fib */ - vnet_buffer(b0)->sw_if_index[VLIB_TX] = iul0->lisp.iid; - pkts_decapsulated ++; + /* map iid/vni to lisp-gpe sw_if_index which is used by ipx_input to + * decide the rx vrf and the input features to be applied */ + si0 = hash_get(lgm->tunnel_term_sw_if_index_by_vni, iul0->lisp.iid); + + if (si0) + { + vnet_buffer(b0)->sw_if_index[VLIB_RX] = si0[0]; + pkts_decapsulated++; + error0 = 0; + } + else + { + next0 = LISP_GPE_INPUT_NEXT_DROP; + error0 = LISP_GPE_ERROR_NO_SUCH_TUNNEL; + } /* TODO error handling if security is implemented */ - error0 = 0; b0->error = error0 ? node->errors[error0] : 0; - if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) { - lisp_gpe_rx_trace_t *tr - = vlib_add_trace (vm, node, b0, sizeof (*tr)); + lisp_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b0, + sizeof(*tr)); tr->next_index = next0; tr->error = error0; tr->h = iul0->lisp; diff --git a/vnet/vnet/lisp-gpe/encap.c b/vnet/vnet/lisp-gpe/encap.c deleted file mode 100644 index a8158782..00000000 --- a/vnet/vnet/lisp-gpe/encap.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * 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 <vppinfra/error.h> -#include <vppinfra/hash.h> -#include <vnet/vnet.h> -#include <vnet/ip/ip.h> -#include <vnet/ip/udp.h> -#include <vnet/ethernet/ethernet.h> -#include <vnet/lisp-gpe/lisp_gpe.h> - -/* Statistics (not really errors) */ -#define foreach_lisp_gpe_encap_error \ -_(ENCAPSULATED, "good packets encapsulated") - -static char * lisp_gpe_encap_error_strings[] = { -#define _(sym,string) string, - foreach_lisp_gpe_encap_error -#undef _ -}; - -typedef enum { -#define _(sym,str) LISP_GPE_ENCAP_ERROR_##sym, - foreach_lisp_gpe_encap_error -#undef _ - LISP_GPE_ENCAP_N_ERROR, -} lisp_gpe_encap_error_t; - -typedef enum -{ - LISP_GPE_ENCAP_NEXT_DROP, - LISP_GPE_ENCAP_NEXT_IP4_LOOKUP, - LISP_GPE_ENCAP_N_NEXT, -} lisp_gpe_encap_next_t; - -typedef struct -{ - u32 tunnel_index; -} lisp_gpe_encap_trace_t; - -u8 * -format_lisp_gpe_encap_trace (u8 * s, va_list * args) -{ - CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); - CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); - lisp_gpe_encap_trace_t * t = va_arg (*args, lisp_gpe_encap_trace_t *); - - s = format (s, "LISP-GPE-ENCAP: tunnel %d", t->tunnel_index); - return s; -} - -static uword -lisp_gpe_encap (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * from_frame) -{ - u32 n_left_from, next_index, * from, * to_next; - lisp_gpe_main_t * lgm = &lisp_gpe_main; - u32 pkts_encapsulated = 0; - - from = vlib_frame_vector_args (from_frame); - n_left_from = from_frame->n_vectors; - - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, - to_next, n_left_to_next); - - while (n_left_from >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - vlib_buffer_t * b0, * b1; - u32 next0, next1; - u32 adj_index0, adj_index1, tunnel_index0, tunnel_index1; - ip_adjacency_t * adj0, * adj1; - lisp_gpe_tunnel_t * t0, * t1; - - next0 = next1 = LISP_GPE_ENCAP_NEXT_IP4_LOOKUP; - - /* Prefetch next iteration. */ - { - vlib_buffer_t * p2, *p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header(p2, LOAD); - vlib_prefetch_buffer_header(p3, LOAD); - - CLIB_PREFETCH(p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); - CLIB_PREFETCH(p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); - } - - bi0 = from[0]; - bi1 = from[1]; - to_next[0] = bi0; - to_next[1] = bi1; - from += 2; - to_next += 2; - n_left_to_next -= 2; - n_left_from -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - /* Get adjacency and from it the tunnel_index */ - adj_index0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX]; - adj_index1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX]; - - adj0 = ip_get_adjacency (lgm->lookup_main, adj_index0); - adj1 = ip_get_adjacency (lgm->lookup_main, adj_index1); - - tunnel_index0 = adj0->rewrite_header.sw_if_index; - tunnel_index1 = adj1->rewrite_header.sw_if_index; - - t0 = pool_elt_at_index (lgm->tunnels, tunnel_index0); - t1 = pool_elt_at_index (lgm->tunnels, tunnel_index1); - - ASSERT(t0 != 0); - ASSERT(t1 != 0); - - ASSERT (sizeof(ip4_udp_lisp_gpe_header_t) == 36); - ip4_udp_encap_two (vm, b0, b1, t0->rewrite, t1->rewrite, 36); - - /* Reset to look up tunnel partner in the configured FIB */ - vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index; - vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->encap_fib_index; - - if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) - { - lisp_gpe_encap_trace_t *tr = vlib_add_trace (vm, node, b0, - sizeof(*tr)); - tr->tunnel_index = t0 - lgm->tunnels; - } - if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) - { - lisp_gpe_encap_trace_t *tr = vlib_add_trace (vm, node, b1, - sizeof(*tr)); - tr->tunnel_index = t1 - lgm->tunnels; - } - - pkts_encapsulated += 2; - - vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, - n_left_to_next, bi0, bi1, next0, - next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - vlib_buffer_t * b0; - u32 bi0, adj_index0, tunnel_index0; - u32 next0 = LISP_GPE_ENCAP_NEXT_IP4_LOOKUP; - lisp_gpe_tunnel_t * t0 = 0; - ip_adjacency_t * adj0; - - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - - /* Get adjacency and from it the tunnel_index */ - adj_index0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX]; - adj0 = ip_get_adjacency (lgm->lookup_main, adj_index0); - - tunnel_index0 = adj0->rewrite_header.sw_if_index; - t0 = pool_elt_at_index (lgm->tunnels, tunnel_index0); - - ASSERT(t0 != 0); - - ASSERT (sizeof(ip4_udp_lisp_gpe_header_t) == 36); - ip4_udp_encap_one (vm, b0, t0->rewrite, 36); - - /* Reset to look up tunnel partner in the configured FIB */ - vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index; - - pkts_encapsulated++; - - if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) - { - lisp_gpe_encap_trace_t *tr = vlib_add_trace (vm, node, b0, - sizeof(*tr)); - tr->tunnel_index = t0 - lgm->tunnels; - } - vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, - n_left_to_next, bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - vlib_node_increment_counter (vm, node->node_index, - LISP_GPE_ENCAP_ERROR_ENCAPSULATED, - pkts_encapsulated); - return from_frame->n_vectors; -} - -VLIB_REGISTER_NODE (lisp_gpe_encap_node) = { - .function = lisp_gpe_encap, - .name = "lisp-gpe-encap", - .vector_size = sizeof (u32), - .format_trace = format_lisp_gpe_encap_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(lisp_gpe_encap_error_strings), - .error_strings = lisp_gpe_encap_error_strings, - - .n_next_nodes = LISP_GPE_ENCAP_N_NEXT, - - .next_nodes = { - [LISP_GPE_ENCAP_NEXT_DROP] = "error-drop", - [LISP_GPE_ENCAP_NEXT_IP4_LOOKUP] = "ip4-lookup", - }, -}; diff --git a/vnet/vnet/lisp-gpe/interface.c b/vnet/vnet/lisp-gpe/interface.c new file mode 100644 index 00000000..0f7f6fca --- /dev/null +++ b/vnet/vnet/lisp-gpe/interface.c @@ -0,0 +1,516 @@ +/* + * 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 <vppinfra/error.h> +#include <vppinfra/hash.h> +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/ip/udp.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/lisp-gpe/lisp_gpe.h> + +#define foreach_lisp_gpe_tx_next \ + _(DROP, "error-drop") \ + _(IP4_LOOKUP, "ip4-lookup") + +typedef enum +{ +#define _(sym,str) LISP_GPE_TX_NEXT_##sym, + foreach_lisp_gpe_tx_next +#undef _ + LISP_GPE_TX_N_NEXT, +} lisp_gpe_tx_next_t; + +typedef struct +{ + u32 tunnel_index; +} lisp_gpe_tx_trace_t; + +u8 * +format_lisp_gpe_tx_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + lisp_gpe_tx_trace_t * t = va_arg (*args, lisp_gpe_tx_trace_t *); + + s = format (s, "LISP-GPE-TX: tunnel %d", t->tunnel_index); + return s; +} + +static uword +lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + u32 n_left_from, next_index, * from, * to_next; + lisp_gpe_main_t * lgm = &lisp_gpe_main; + u32 pkts_encapsulated = 0; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, * b1; + u32 next0, next1; + u32 adj_index0, adj_index1, tunnel_index0, tunnel_index1; + ip_adjacency_t * adj0, * adj1; + lisp_gpe_tunnel_t * t0, * t1; + + next0 = next1 = LISP_GPE_TX_NEXT_IP4_LOOKUP; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header(p2, LOAD); + vlib_prefetch_buffer_header(p3, LOAD); + + CLIB_PREFETCH(p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH(p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + } + + bi0 = from[0]; + bi1 = from[1]; + to_next[0] = bi0; + to_next[1] = bi1; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + /* Get adjacency and from it the tunnel_index */ + adj_index0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX]; + adj_index1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX]; + + adj0 = ip_get_adjacency (lgm->lookup_main, adj_index0); + adj1 = ip_get_adjacency (lgm->lookup_main, adj_index1); + + tunnel_index0 = adj0->rewrite_header.node_index; + tunnel_index1 = adj1->rewrite_header.node_index; + + t0 = pool_elt_at_index (lgm->tunnels, tunnel_index0); + t1 = pool_elt_at_index (lgm->tunnels, tunnel_index1); + + ASSERT(t0 != 0); + ASSERT(t1 != 0); + + ASSERT (sizeof(ip4_udp_lisp_gpe_header_t) == 36); + ip4_udp_encap_two (vm, b0, b1, t0->rewrite, t1->rewrite, 36); + + /* Reset to look up tunnel partner in the configured FIB */ + vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index; + vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->encap_fib_index; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0, + sizeof(*tr)); + tr->tunnel_index = t0 - lgm->tunnels; + } + if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) + { + lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b1, + sizeof(*tr)); + tr->tunnel_index = t1 - lgm->tunnels; + } + + pkts_encapsulated += 2; + + vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, + n_left_to_next, bi0, bi1, next0, + next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t * b0; + u32 bi0, adj_index0, tunnel_index0; + u32 next0 = LISP_GPE_TX_NEXT_IP4_LOOKUP; + lisp_gpe_tunnel_t * t0 = 0; + ip_adjacency_t * adj0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + /* Get adjacency and from it the tunnel_index */ + adj_index0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX]; + adj0 = ip_get_adjacency (lgm->lookup_main, adj_index0); + + tunnel_index0 = adj0->rewrite_header.node_index; + t0 = pool_elt_at_index (lgm->tunnels, tunnel_index0); + + ASSERT(t0 != 0); + + ASSERT (sizeof(ip4_udp_lisp_gpe_header_t) == 36); + ip4_udp_encap_one (vm, b0, t0->rewrite, 36); + + /* Reset to look up tunnel partner in the configured FIB */ + vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index; + + pkts_encapsulated++; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0, + sizeof(*tr)); + tr->tunnel_index = t0 - lgm->tunnels; + } + vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + vlib_node_increment_counter (vm, node->node_index, + LISP_GPE_ERROR_ENCAPSULATED, pkts_encapsulated); + return from_frame->n_vectors; +} + +static u8 * +format_lisp_gpe_name (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + return format (s, "lisp_gpe%d", dev_instance); +} + +VNET_DEVICE_CLASS (lisp_gpe_device_class,static) = { + .name = "LISP_GPE", + .format_device_name = format_lisp_gpe_name, + .format_tx_trace = format_lisp_gpe_tx_trace, + .tx_function = lisp_gpe_interface_tx, + .no_flatten_output_chains = 1, +}; + +static uword +dummy_set_rewrite (vnet_main_t * vnm, u32 sw_if_index, u32 l3_type, + void * dst_address, void * rewrite, uword max_rewrite_bytes) +{ + return 0; +} + +u8 * +format_lisp_gpe_header_with_length (u8 * s, va_list * args) +{ + lisp_gpe_header_t * h = va_arg (*args, lisp_gpe_header_t *); + u32 max_header_bytes = va_arg (*args, u32); + u32 header_bytes; + + header_bytes = sizeof (h[0]); + if (max_header_bytes != 0 && header_bytes > max_header_bytes) + return format (s, "lisp-gpe header truncated"); + + s = format (s, "flags: "); +#define _(n,v) if (h->flags & v) s = format (s, "%s ", #n); + foreach_lisp_gpe_flag_bit; +#undef _ + + s = format (s, "\n ver_res %d res %d next_protocol %d iid %d(%x)", + h->ver_res, h->res, h->next_protocol, + clib_net_to_host_u32 (h->iid), + clib_net_to_host_u32 (h->iid)); + return s; +} + +VNET_HW_INTERFACE_CLASS (lisp_gpe_hw_class) = { + .name = "LISP_GPE", + .format_header = format_lisp_gpe_header_with_length, + .set_rewrite = dummy_set_rewrite, +}; + +int +add_del_ip_prefix_route (ip_prefix_t * dst_prefix, u32 table_id, + ip_adjacency_t * add_adj, u8 is_add, u32 * adj_index) +{ + uword * p; + + if (ip_prefix_version(dst_prefix) == IP4) + { + ip4_main_t * im4 = &ip4_main; + ip4_add_del_route_args_t a; + ip4_address_t addr = ip_prefix_v4(dst_prefix); + + memset(&a, 0, sizeof(a)); + a.flags = IP4_ROUTE_FLAG_TABLE_ID; + a.table_index_or_table_id = table_id; + a.adj_index = ~0; + a.dst_address_length = ip_prefix_len(dst_prefix); + a.dst_address = addr; + a.flags |= is_add ? IP4_ROUTE_FLAG_ADD : IP4_ROUTE_FLAG_DEL; + a.add_adj = add_adj; + a.n_add_adj = 1; + ip4_add_del_route (im4, &a); + + if (is_add) + { + p = ip4_get_route (im4, table_id, 0, addr.as_u8, + ip_prefix_len(dst_prefix)); + if (p == 0) + { + clib_warning("Failed to insert route for eid %U!", + format_ip4_address_and_length, addr.as_u8, + ip_prefix_len(dst_prefix)); + return -1; + } + adj_index[0] = p[0]; + } + } + else + { + ip6_main_t * im6 = &ip6_main; + ip6_add_del_route_args_t a; + ip6_address_t addr = ip_prefix_v6(dst_prefix); + + memset(&a, 0, sizeof(a)); + a.flags = IP6_ROUTE_FLAG_TABLE_ID; + a.table_index_or_table_id = table_id; + a.adj_index = ~0; + a.dst_address_length = ip_prefix_len(dst_prefix); + a.dst_address = addr; + a.flags |= is_add ? IP6_ROUTE_FLAG_ADD : IP6_ROUTE_FLAG_DEL; + a.add_adj = add_adj; + a.n_add_adj = 1; + + ip6_add_del_route (im6, &a); + + if (is_add) + { + adj_index[0] = ip6_get_route (im6, table_id, 0, &addr, + ip_prefix_len(dst_prefix)); + if (adj_index[0] == 0) + { + clib_warning("Failed to insert route for eid %U!", + format_ip6_address_and_length, addr.as_u8, + ip_prefix_len(dst_prefix)); + return -1; + } + } + } + return 0; +} + +static void +add_del_lisp_gpe_default_route (u32 table_id, u8 is_v4, u8 is_add) +{ + lisp_gpe_main_t * lgm = &lisp_gpe_main; + ip_adjacency_t adj; + ip_prefix_t prefix; + u32 adj_index = 0; + + /* setup adjacency */ + memset (&adj, 0, sizeof(adj)); + + adj.n_adj = 1; + adj.explicit_fib_index = ~0; + adj.lookup_next_index = lgm->ip4_lookup_next_lgpe_ip4_lookup; + /* default route has tunnel_index ~0 */ + adj.rewrite_header.sw_if_index = ~0; + + /* set prefix to 0/0 */ + memset(&prefix, 0, sizeof(prefix)); + ip_prefix_version(&prefix) = is_v4 ? IP4 : IP6; + + /* add/delete route for prefix */ + add_del_ip_prefix_route (&prefix, table_id, &adj, is_add, &adj_index); +} + +static void +lisp_gpe_iface_set_table (u32 sw_if_index, u32 table_id, u8 is_ip4) +{ + if (is_ip4) + { + ip4_main_t * im4 = &ip4_main; + ip4_fib_t * fib; + fib = find_ip4_fib_by_table_index_or_id (im4, table_id, + IP4_ROUTE_FLAG_TABLE_ID); + + /* fib's created if it doesn't exist */ + ASSERT(fib != 0); + + vec_validate(im4->fib_index_by_sw_if_index, sw_if_index); + im4->fib_index_by_sw_if_index[sw_if_index] = fib->index; + } + else + { + ip6_main_t * im6 = &ip6_main; + ip6_fib_t * fib; + fib = find_ip6_fib_by_table_index_or_id (im6, table_id, + IP6_ROUTE_FLAG_TABLE_ID); + + /* fib's created if it doesn't exist */ + ASSERT(fib != 0); + + vec_validate(im6->fib_index_by_sw_if_index, sw_if_index); + im6->fib_index_by_sw_if_index[sw_if_index] = fib->index; + } +} + +void +vnet_lisp_gpe_add_del_iface (vnet_lisp_gpe_add_del_iface_args_t * a, + u32 * hw_if_indexp) +{ + lisp_gpe_main_t * lgm = &lisp_gpe_main; + vnet_main_t * vnm = lgm->vnet_main; + vnet_hw_interface_t * hi; + u32 hw_if_index = ~0, lookup_next_index, flen; + uword * hip, * vni; + + hip = hash_get(lgm->lisp_gpe_hw_if_index_by_table_id, a->table_id); + + if (a->is_add) + { + if (hip) + { + clib_warning ("Interface for vrf %d already exists", a->table_id); + return; + } + + /* create hw lisp_gpeX iface if needed, otherwise reuse existing */ + flen = vec_len(lgm->free_lisp_gpe_tunnel_hw_if_indices); + if (flen > 0) + { + hw_if_index = lgm->free_lisp_gpe_tunnel_hw_if_indices[flen - 1]; + _vec_len(lgm->free_lisp_gpe_tunnel_hw_if_indices) -= 1; + } + else + { + hw_if_index = vnet_register_interface (vnm, + lisp_gpe_device_class.index, + a->table_id, + lisp_gpe_hw_class.index, 0); + } + + hi = vnet_get_hw_interface (vnm, hw_if_index); + hash_set(lgm->lisp_gpe_hw_if_index_by_table_id, a->table_id, hw_if_index); + + /* set tunnel termination: post decap, packets are tagged as having been + * originated by lisp-gpe interface */ + hash_set(lgm->tunnel_term_sw_if_index_by_vni, a->vni, hi->sw_if_index); + hash_set(lgm->vni_by_tunnel_term_sw_if_index, hi->sw_if_index, a->vni); + + /* set ingress arc from lgpe_ip4_lookup */ + lookup_next_index = vlib_node_add_next (lgm->vlib_main, + lgpe_ip4_lookup_node.index, + hi->output_node_index); + hash_set(lgm->lgpe_ip4_lookup_next_index_by_table_id, a->table_id, + lookup_next_index); + + /* insert default routes that point to lgpe-ipx-lookup */ + add_del_lisp_gpe_default_route (a->table_id, /* is_v4 */1, 1); + add_del_lisp_gpe_default_route (a->table_id, /* is_v4 */0, 1); + + /* set egress arcs */ +#define _(sym,str) vlib_node_add_named_next_with_slot (vnm->vlib_main, \ + hi->tx_node_index, str, LISP_GPE_TX_NEXT_##sym); + foreach_lisp_gpe_tx_next +#undef _ + + /* set interface in appropriate v4 and v6 FIBs */ + lisp_gpe_iface_set_table (hi->sw_if_index, a->table_id, 1); + lisp_gpe_iface_set_table (hi->sw_if_index, a->table_id, 0); + + /* enable interface */ + vnet_sw_interface_set_flags (vnm, hi->sw_if_index, + VNET_SW_INTERFACE_FLAG_ADMIN_UP); + vnet_hw_interface_set_flags (vnm, hi->hw_if_index, + VNET_HW_INTERFACE_FLAG_LINK_UP); + } + else + { + if (hip == 0) + { + clib_warning("The interface for vrf %d doesn't exist", a->table_id); + return; + } + hi = vnet_get_hw_interface (vnm, hip[0]); + + /* disable interface */ + vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 0/* down */); + vnet_hw_interface_set_flags (vnm, hi->hw_if_index, 0/* down */); + hash_unset(lgm->lisp_gpe_hw_if_index_by_table_id, a->table_id); + vec_add1(lgm->free_lisp_gpe_tunnel_hw_if_indices, hi->hw_if_index); + + /* clean tunnel termination and vni to sw_if_index binding */ + vni = hash_get(lgm->vni_by_tunnel_term_sw_if_index, hi->sw_if_index); + hash_unset(lgm->tunnel_term_sw_if_index_by_vni, vni[0]); + hash_unset(lgm->vni_by_tunnel_term_sw_if_index, hi->sw_if_index); + + /* unset default routes */ + add_del_lisp_gpe_default_route (a->table_id, /* is_v4 */1, 0); + add_del_lisp_gpe_default_route (a->table_id, /* is_v4 */0, 0); + } +} + +static clib_error_t * +lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, * line_input = &_line_input; + u8 is_add = 1; + u32 table_id; + + vnet_lisp_gpe_add_del_iface_args_t _a, * a = &_a; + + /* 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, "add")) + is_add = 1; + else if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "vrf %d", &table_id)) + ; + else + { + return clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + } + } + + a->is_add = is_add; + a->table_id = table_id; + vnet_lisp_gpe_add_del_iface (a, 0); + return 0; +} + +VLIB_CLI_COMMAND (add_del_lisp_gpe_iface_command, static) = { + .path = "lisp gpe iface", + .short_help = "lisp gpe iface add/del table-index <table_index> vrf <vrf>", + .function = lisp_gpe_add_del_iface_command_fn, +}; diff --git a/vnet/vnet/lisp-gpe/lisp_gpe.c b/vnet/vnet/lisp-gpe/lisp_gpe.c index b8072494..fd1e1a46 100644 --- a/vnet/vnet/lisp-gpe/lisp_gpe.c +++ b/vnet/vnet/lisp-gpe/lisp_gpe.c @@ -177,7 +177,7 @@ ip4_sd_fib_clear_src_fib (lisp_gpe_main_t * lgm, ip4_fib_t * fib) int ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix, - ip_prefix_t * src_prefix, u32 table_index, + ip_prefix_t * src_prefix, u32 table_id, ip_adjacency_t * add_adj, u8 is_add) { uword * p; @@ -196,7 +196,7 @@ ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix, memset(&src, 0, sizeof(src)); /* lookup dst adj */ - p = ip4_get_route (lgm->im4, table_index, 0, dst.as_u8, dst_address_length); + p = ip4_get_route (lgm->im4, table_id, 0, dst.as_u8, dst_address_length); if (is_add) { @@ -209,7 +209,7 @@ ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix, memset(&a, 0, sizeof(a)); a.flags = IP4_ROUTE_FLAG_TABLE_ID; - a.table_index_or_table_id = table_index; /* vrf */ + a.table_index_or_table_id = table_id; /* vrf */ a.adj_index = ~0; a.dst_address_length = dst_address_length; a.dst_address = dst; @@ -220,7 +220,7 @@ ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix, ip4_add_del_route (lgm->im4, &a); /* lookup dst adj to obtain the adj index */ - p = ip4_get_route (lgm->im4, table_index, 0, dst.as_u8, + p = ip4_get_route (lgm->im4, table_id, 0, dst.as_u8, dst_address_length); if (p == 0) { @@ -283,7 +283,7 @@ ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix, /* .. and remove dst route */ memset(&a, 0, sizeof(a)); a.flags = IP4_ROUTE_FLAG_TABLE_ID; - a.table_index_or_table_id = table_index; /* vrf */ + a.table_index_or_table_id = table_id; /* vrf */ a.adj_index = ~0; a.dst_address_length = dst_address_length; a.dst_address = dst; @@ -297,7 +297,7 @@ ip4_sd_fib_add_del_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix, static void * ip4_sd_fib_get_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix, - ip_prefix_t * src_prefix, u32 table_index) + ip_prefix_t * src_prefix, u32 table_id) { uword * p; ip4_address_t dst = ip_prefix_v4(dst_prefix), src; @@ -313,7 +313,7 @@ ip4_sd_fib_get_route (lisp_gpe_main_t * lgm, ip_prefix_t * dst_prefix, memset(&src, 0, sizeof(src)); /* lookup dst adj */ - p = ip4_get_route (lgm->im4, table_index, 0, dst.as_u8, dst_address_length); + p = ip4_get_route (lgm->im4, table_id, 0, dst.as_u8, dst_address_length); if (p == 0) return p; @@ -326,7 +326,6 @@ typedef enum { LGPE_IP4_LOOKUP_NEXT_DROP, LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP, - LGPE_IP4_LOOKUP_NEXT_LGPE_ENCAP, LGPE_IP4_LOOKUP_N_NEXT, } lgpe_ip4_lookup_next_t; @@ -468,6 +467,12 @@ lgpe_ip4_lookup (vlib_main_t * vm, vlib_node_runtime_t * node, next0 = src_adj0->lookup_next_index; next1 = src_adj1->lookup_next_index; + + /* prepare buffer for lisp-gpe output node */ + vnet_buffer (b0)->sw_if_index[VLIB_TX] = + src_adj0->rewrite_header.sw_if_index; + vnet_buffer (b1)->sw_if_index[VLIB_TX] = + src_adj1->rewrite_header.sw_if_index; } else { @@ -479,6 +484,7 @@ lgpe_ip4_lookup (vlib_main_t * vm, vlib_node_runtime_t * node, src_adj0 = ip_get_adjacency (lgm->lookup_main, src_adj_index0); next0 = src_adj0->lookup_next_index; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = src_adj_index0; } if (src_fib_index1 != (u32) ~0) { @@ -488,6 +494,7 @@ lgpe_ip4_lookup (vlib_main_t * vm, vlib_node_runtime_t * node, src_adj1 = ip_get_adjacency (lgm->lookup_main, src_adj_index1); next1 = src_adj1->lookup_next_index; + vnet_buffer (b1)->sw_if_index[VLIB_TX] = src_adj_index1; } } @@ -530,6 +537,9 @@ lgpe_ip4_lookup (vlib_main_t * vm, vlib_node_runtime_t * node, src_adj0 = ip_get_adjacency (lgm->lookup_main, src_adj_index0); next0 = src_adj0->lookup_next_index; + /* prepare packet for lisp-gpe output node */ + vnet_buffer (b0)->sw_if_index[VLIB_TX] = + src_adj0->rewrite_header.sw_if_index; done: vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, bi0, next0); @@ -551,7 +561,6 @@ VLIB_REGISTER_NODE (lgpe_ip4_lookup_node) = { .next_nodes = { [LGPE_IP4_LOOKUP_NEXT_DROP] = "error-drop", [LGPE_IP4_LOOKUP_NEXT_LISP_CP_LOOKUP] = "lisp-cp-lookup", - [LGPE_IP4_LOOKUP_NEXT_LGPE_ENCAP] = "lisp-gpe-encap", }, }; @@ -592,7 +601,7 @@ lisp_gpe_rewrite (lisp_gpe_tunnel_t * t) lisp0->ver_res = t->ver_res; lisp0->res = t->res; lisp0->next_protocol = t->next_protocol; - lisp0->iid = clib_host_to_net_u32 (t->iid); + lisp0->iid = clib_host_to_net_u32 (t->vni); t->rewrite = rw; return 0; @@ -615,7 +624,7 @@ _(flags) \ _(next_protocol) \ _(ver_res) \ _(res) \ -_(iid) +_(vni) static u32 add_del_tunnel (vnet_lisp_gpe_add_del_fwd_entry_args_t *a, u32 * tun_index_res) @@ -629,7 +638,7 @@ add_del_tunnel (vnet_lisp_gpe_add_del_fwd_entry_args_t *a, u32 * tun_index_res) memset(&key, 0, sizeof(key)); gid_address_copy(&key.eid, &a->deid); key.dst_loc = ip_addr_v4(&a->dlocator).as_u32; - key.iid = clib_host_to_net_u32 (a->iid); + key.iid = clib_host_to_net_u32 (a->vni); p = mhash_get (&lgm->lisp_gpe_tunnel_by_key, &key); @@ -688,6 +697,41 @@ add_del_tunnel (vnet_lisp_gpe_add_del_fwd_entry_args_t *a, u32 * tun_index_res) return 0; } +static int +add_del_negative_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + ip_adjacency_t adj; + /* setup adjacency for eid */ + memset (&adj, 0, sizeof(adj)); + adj.n_adj = 1; + adj.explicit_fib_index = ~0; + + ip_prefix_t * dpref = &gid_address_ippref(&a->deid); + ip_prefix_t * spref = &gid_address_ippref(&a->seid); + + switch (a->action) + { + case NO_ACTION: + /* TODO update timers? */ + case FORWARD_NATIVE: + /* TODO check if route/next-hop for eid exists in fib and add + * more specific for the eid with the next-hop found */ + case SEND_MAP_REQUEST: + /* TODO insert tunnel that always sends map-request */ + case DROP: + /* for drop fwd entries, just add route, no need to add encap tunnel */ + adj.lookup_next_index = LGPE_IP4_LOOKUP_NEXT_DROP; + + /* add/delete route for prefix */ + return ip4_sd_fib_add_del_route (lgm, dpref, spref, a->table_id, &adj, + a->is_add); + break; + default: + return -1; + } +} + int vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, u32 * hw_if_indexp) @@ -695,66 +739,59 @@ vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, lisp_gpe_main_t * lgm = &lisp_gpe_main; ip_adjacency_t adj, * adjp; u32 * adj_index, rv, tun_index = ~0; - ip_prefix_t * dpref = &gid_address_ippref(&a->deid); - ip_prefix_t * spref = &gid_address_ippref(&a->seid); + ip_prefix_t * dpref, * spref; + uword * lookup_next_index, * lgpe_sw_if_index; + + /* treat negative fwd entries separately */ + if (a->is_negative) + return add_del_negative_fwd_entry (lgm, a); + + /* add/del tunnel to tunnels pool and prepares rewrite */ + rv = add_del_tunnel (a, &tun_index); + if (rv) + return rv; + + dpref = &gid_address_ippref(&a->deid); + spref = &gid_address_ippref(&a->seid); /* setup adjacency for eid */ memset (&adj, 0, sizeof(adj)); adj.n_adj = 1; adj.explicit_fib_index = ~0; - /* treat negative fwd entries separately */ - if (a->is_negative) + if (a->is_add) { - switch (a->action) - { - case NO_ACTION: - /* TODO update timers? */ - case FORWARD_NATIVE: - /* TODO check if route/next-hop for eid exists in fib and add - * more specific for the eid with the next-hop found */ - case SEND_MAP_REQUEST: - /* TODO insert tunnel that always sends map-request */ - case DROP: - /* for drop fwd entries, just add route, no need to add encap tunnel */ - adj.lookup_next_index = LGPE_IP4_LOOKUP_NEXT_DROP; - - /* add/delete route for prefix */ - rv = ip4_sd_fib_add_del_route (lgm, dpref, spref, a->iid, &adj, - a->is_add); - return rv; - break; - default: - return -1; - } + /* send packets that hit this adj to lisp-gpe interface output node in + * requested vrf. */ + lookup_next_index = hash_get(lgm->lgpe_ip4_lookup_next_index_by_table_id, + a->table_id); + lgpe_sw_if_index = hash_get(lgm->lisp_gpe_hw_if_index_by_table_id, + a->table_id); + + /* the assumption is that the interface must've been created before + * programming the dp */ + ASSERT(lookup_next_index != 0); + ASSERT(lgpe_sw_if_index != 0); + + adj.lookup_next_index = lookup_next_index[0]; + adj.rewrite_header.node_index = tun_index; + adj.rewrite_header.sw_if_index = lgpe_sw_if_index[0]; } - /* send packets that hit this adj to lisp-gpe encap */ - adj.lookup_next_index = LGPE_IP4_LOOKUP_NEXT_LGPE_ENCAP; + /* add/delete route for prefix */ + rv = ip4_sd_fib_add_del_route (lgm, dpref, spref, a->table_id, &adj, + a->is_add); - /* add/delete route for prefix - * TODO use hash to decide fib instead of using iid in clear */ - rv = ip4_sd_fib_add_del_route (lgm, dpref, spref, a->iid, &adj, a->is_add); - - if (rv) - return rv; - - /* add/del tunnel to tunnels pool */ - rv = add_del_tunnel (a, &tun_index); - - /* reuse sw_if_index for storing the tunnel index */ - if (a->is_add) + /* check that everything worked */ + if (CLIB_DEBUG && a->is_add) { - adj_index = ip4_sd_fib_get_route(lgm, dpref, spref, a->iid); - if (!adj_index) - { - clib_warning("Failed to insert fwd entry! For %U", - format_ip4_address_and_length, ip_prefix_v4(dpref), - ip_prefix_len(dpref)); - return -1; - } + adj_index = ip4_sd_fib_get_route (lgm, dpref, spref, a->table_id); + ASSERT(adj_index != 0); + adjp = ip_get_adjacency (lgm->lookup_main, adj_index[0]); - adjp->rewrite_header.sw_if_index = tun_index; + + ASSERT(adjp != 0); + ASSERT(adjp->rewrite_header.node_index == tun_index); } return rv; @@ -810,7 +847,8 @@ lisp_gpe_add_del_fwd_entry_command_fn (vlib_main_t * vm, if (vec_len (eids) != vec_len (slocators)) { - error = clib_error_return (0, "number of eids not equal to that of locators."); + error = clib_error_return (0, "number of eids not equal to that of " + "locators."); goto done; } @@ -837,105 +875,11 @@ lisp_gpe_add_del_fwd_entry_command_fn (vlib_main_t * vm, VLIB_CLI_COMMAND (add_del_lisp_gpe_mapping_tunnel_command, static) = { .path = "lisp gpe maptunnel", - .short_help = "lisp gpe maptunnel eid <eid> sloc <src-locator> dloc <dst-locator> [del]", + .short_help = "lisp gpe maptunnel eid <eid> sloc <src-locator> " + "dloc <dst-locator> [del]", .function = lisp_gpe_add_del_fwd_entry_command_fn, }; -int -add_del_ip_prefix_route (ip_prefix_t * dst_prefix, u32 table_id, - ip_adjacency_t * add_adj, u8 is_add, u32 * adj_index) -{ - uword * p; - - if (ip_prefix_version(dst_prefix) == IP4) - { - ip4_main_t * im4 = &ip4_main; - ip4_add_del_route_args_t a; - ip4_address_t addr = ip_prefix_v4(dst_prefix); - - memset(&a, 0, sizeof(a)); - a.flags = IP4_ROUTE_FLAG_TABLE_ID; - a.table_index_or_table_id = table_id; - a.adj_index = ~0; - a.dst_address_length = ip_prefix_len(dst_prefix); - a.dst_address = addr; - a.flags |= is_add ? IP4_ROUTE_FLAG_ADD : IP4_ROUTE_FLAG_DEL; - a.add_adj = add_adj; - a.n_add_adj = 1; - ip4_add_del_route (im4, &a); - - if (is_add) - { - p = ip4_get_route (im4, table_id, 0, addr.as_u8, - ip_prefix_len(dst_prefix)); - if (p == 0) - { - clib_warning("Failed to insert route for eid %U!", - format_ip4_address_and_length, addr.as_u8, - ip_prefix_len(dst_prefix)); - return -1; - } - adj_index[0] = p[0]; - } - } - else - { - ip6_main_t * im6 = &ip6_main; - ip6_add_del_route_args_t a; - ip6_address_t addr = ip_prefix_v6(dst_prefix); - - memset(&a, 0, sizeof(a)); - a.flags = IP6_ROUTE_FLAG_TABLE_ID; - a.table_index_or_table_id = table_id; - a.adj_index = ~0; - a.dst_address_length = ip_prefix_len(dst_prefix); - a.dst_address = addr; - a.flags |= is_add ? IP6_ROUTE_FLAG_ADD : IP6_ROUTE_FLAG_DEL; - a.add_adj = add_adj; - a.n_add_adj = 1; - - ip6_add_del_route (im6, &a); - - if (is_add) - { - adj_index[0] = ip6_get_route (im6, table_id, 0, &addr, - ip_prefix_len(dst_prefix)); - if (adj_index[0] == 0) - { - clib_warning("Failed to insert route for eid %U!", - format_ip6_address_and_length, addr.as_u8, - ip_prefix_len(dst_prefix)); - return -1; - } - } - } - return 0; -} - -static void -add_del_lisp_gpe_default_route (u8 is_v4, u8 is_add) -{ - lisp_gpe_main_t * lgm = &lisp_gpe_main; - ip_adjacency_t adj; - ip_prefix_t prefix; - u32 adj_index = 0; - - /* setup adjacency */ - memset (&adj, 0, sizeof(adj)); - adj.n_adj = 1; - adj.explicit_fib_index = ~0; - adj.lookup_next_index = lgm->ip4_lookup_next_lgpe_ip4_lookup; - /* default route has tunnel_index ~0 */ - adj.rewrite_header.sw_if_index = ~0; - - /* set prefix to 0/0 */ - memset(&prefix, 0, sizeof(prefix)); - ip_prefix_version(&prefix) = is_v4 ? IP4 : IP6; - - /* add/delete route for prefix XXX default table only */ - add_del_ip_prefix_route (&prefix, 0, &adj, is_add, &adj_index); -} - static u8 * format_decap_next (u8 * s, va_list * args) { @@ -949,8 +893,6 @@ format_decap_next (u8 * s, va_list * args) return format (s, "ip4"); case LISP_GPE_INPUT_NEXT_IP6_INPUT: return format (s, "ip6"); - case LISP_GPE_INPUT_NEXT_LISP_GPE_ENCAP: - return format (s, "nsh-lisp-gpe"); default: return format (s, "unknown %d", next_index); } @@ -981,7 +923,7 @@ format_lisp_gpe_tunnel (u8 * s, va_list * args) s = format (s, "next_protocol %d ver_res %x res %x\n", t->next_protocol, t->ver_res, t->res); - s = format (s, "iid %d (0x%x)\n", t->iid, t->iid); + s = format (s, "iid %d (0x%x)\n", t->vni, t->vni); return s; } @@ -1009,106 +951,74 @@ VLIB_CLI_COMMAND (show_lisp_gpe_tunnel_command, static) = { .function = show_lisp_gpe_tunnel_command_fn, }; -static u8 * -format_lisp_gpe_name (u8 * s, va_list * args) -{ - u32 dev_instance = va_arg (*args, u32); - return format (s, "lisp_gpe_tunnel%d", dev_instance); -} - -static uword -dummy_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - clib_warning("you shouldn't be here, leaking buffers..."); - return frame->n_vectors; -} - -VNET_DEVICE_CLASS (lisp_gpe_device_class,static) = { - .name = "LISP_GPE", - .format_device_name = format_lisp_gpe_name, - .format_tx_trace = format_lisp_gpe_encap_trace, - .tx_function = dummy_interface_tx, -}; - -static uword -dummy_set_rewrite (vnet_main_t * vnm, u32 sw_if_index, u32 l3_type, - void * dst_address, void * rewrite, uword max_rewrite_bytes) -{ - return 0; -} - -u8 * -format_lisp_gpe_header_with_length (u8 * s, va_list * args) -{ - lisp_gpe_header_t * h = va_arg (*args, lisp_gpe_header_t *); - u32 max_header_bytes = va_arg (*args, u32); - u32 header_bytes; - - header_bytes = sizeof (h[0]); - if (max_header_bytes != 0 && header_bytes > max_header_bytes) - return format (s, "gre-nsh header truncated"); - - s = format (s, "flags: "); -#define _(n,v) if (h->flags & v) s = format (s, "%s ", #n); - foreach_lisp_gpe_flag_bit; -#undef _ - - s = format (s, "\n ver_res %d res %d next_protocol %d iid %d(%x)", - h->ver_res, h->res, h->next_protocol, - clib_net_to_host_u32 (h->iid), - clib_net_to_host_u32 (h->iid)); - return s; -} - -VNET_HW_INTERFACE_CLASS (lisp_gpe_hw_class) = { - .name = "LISP_GPE", - .format_header = format_lisp_gpe_header_with_length, - .set_rewrite = dummy_set_rewrite, -}; - -void -vnet_lisp_gpe_add_del_iface (vnet_lisp_gpe_add_del_iface_args_t * a, - u32 * hw_if_indexp) +clib_error_t * +vnet_lisp_gpe_enable_disable (vnet_lisp_gpe_enable_disable_args_t * a) { lisp_gpe_main_t * lgm = &lisp_gpe_main; vnet_main_t * vnm = lgm->vnet_main; - vnet_hw_interface_t * hi; - u32 hw_if_index = ~0; - if (a->is_add) + if (a->is_en) { - /* create hw lisp_gpe0 iface */ - hw_if_index = vnet_register_interface (vnm, lisp_gpe_device_class.index, 0, - lisp_gpe_hw_class.index, 0); - - hi = vnet_get_hw_interface (vnm, hw_if_index); - hi->output_node_index = lisp_gpe_encap_node.index; - lgm->lisp_gpe_hw_if_index = hw_if_index; - /* add lgpe_ip4_lookup as possible next_node for ip4 lookup */ - lgm->ip4_lookup_next_lgpe_ip4_lookup = vlib_node_add_next ( - vnm->vlib_main, ip4_lookup_node.index, lgpe_ip4_lookup_node.index); - - /* insert default routes that points at lisp-gpe-encap */ - add_del_lisp_gpe_default_route(/* is_v4 */1, 1); - add_del_lisp_gpe_default_route(/* is_v4 */0, 1); + if (lgm->ip4_lookup_next_lgpe_ip4_lookup == ~0) + { + lgm->ip4_lookup_next_lgpe_ip4_lookup = vlib_node_add_next ( + vnm->vlib_main, ip4_lookup_node.index, + lgpe_ip4_lookup_node.index); + } + else + { + /* ask cp to re-add ifaces and defaults */ + } } else { - vnet_sw_interface_set_flags (vnm, lgm->lisp_gpe_hw_if_index, - 0 /* down */); + CLIB_UNUSED(uword * val); + hash_pair_t * p; + u32 * table_ids = 0, * table_id; + lisp_gpe_tunnel_key_t * tunnels = 0, * tunnel; + vnet_lisp_gpe_add_del_fwd_entry_args_t _at, * at = &_at; + vnet_lisp_gpe_add_del_iface_args_t _ai, * ai= &_ai; + + /* remove all tunnels */ + mhash_foreach(tunnel, val, &lgm->lisp_gpe_tunnel_by_key, ({ + vec_add1(tunnels, tunnel[0]); + })); + + vec_foreach(tunnel, tunnels) { + memset(at, 0, sizeof(at[0])); + at->is_add = 0; + gid_address_copy(&at->deid, &tunnel->eid); + ip_addr_v4(&at->dlocator).as_u32= tunnel->dst_loc; + vnet_lisp_gpe_add_del_fwd_entry (at, 0); + } + vec_free(tunnels); + + /* disable all ifaces */ + hash_foreach_pair(p, lgm->lisp_gpe_hw_if_index_by_table_id, ({ + vec_add1(table_ids, p->key); + })); + + vec_foreach(table_id, table_ids) { + ai->is_add = 0; + ai->table_id = table_id[0]; + + /* disables interface and removes defaults */ + vnet_lisp_gpe_add_del_iface(ai, 0); + } + vec_free(table_ids); } + + return 0; } static clib_error_t * -lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) +lisp_gpe_enable_disable_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) { unformat_input_t _line_input, * line_input = &_line_input; - u8 is_add = 1; - vnet_lisp_gpe_add_del_iface_args_t _a, * a = &_a; + u8 is_en = 1; + vnet_lisp_gpe_enable_disable_args_t _a, * a = &_a; /* Get a line of input. */ if (! unformat_user (input, unformat_line_input, line_input)) @@ -1116,26 +1026,24 @@ lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { - if (unformat (line_input, "up")) - is_add = 1; - else if (unformat (line_input, "down")) - is_add = 0; + if (unformat (line_input, "enable")) + is_en = 1; + else if (unformat (line_input, "disable")) + is_en = 0; else { return clib_error_return (0, "parse error: '%U'", format_unformat_error, line_input); } } - - a->is_add = is_add; - vnet_lisp_gpe_add_del_iface (a, 0); - return 0; + a->is_en = is_en; + return vnet_lisp_gpe_enable_disable (a); } -VLIB_CLI_COMMAND (add_del_lisp_gpe_iface_command, static) = { - .path = "lisp gpe iface", - .short_help = "lisp gpe iface [del]", - .function = lisp_gpe_add_del_iface_command_fn, +VLIB_CLI_COMMAND (enable_disable_lisp_gpe_command, static) = { + .path = "lisp gpe", + .short_help = "lisp gpe [enable|disable]", + .function = lisp_gpe_enable_disable_command_fn, }; clib_error_t * @@ -1154,7 +1062,8 @@ lisp_gpe_init (vlib_main_t *vm) lgm->vlib_main = vm; lgm->im4 = &ip4_main; lgm->lookup_main = &ip4_main.lookup_main; - + lgm->ip4_lookup_next_lgpe_ip4_lookup = ~0; + mhash_init (&lgm->lisp_gpe_tunnel_by_key, sizeof(uword), sizeof(lisp_gpe_tunnel_key_t)); diff --git a/vnet/vnet/lisp-gpe/lisp_gpe.h b/vnet/vnet/lisp-gpe/lisp_gpe.h index 7e86d57f..1452b798 100644 --- a/vnet/vnet/lisp-gpe/lisp_gpe.h +++ b/vnet/vnet/lisp-gpe/lisp_gpe.h @@ -74,15 +74,14 @@ typedef struct u8 ver_res; u8 res; u8 next_protocol; - u32 iid; + u32 vni; } lisp_gpe_tunnel_t; #define foreach_lisp_gpe_input_next \ _(DROP, "error-drop") \ _(IP4_INPUT, "ip4-input") \ _(IP6_INPUT, "ip6-input") \ -_(ETHERNET_INPUT, "ethernet-input") \ -_(LISP_GPE_ENCAP, "lisp-gpe-encap") +_(ETHERNET_INPUT, "ethernet-input") typedef enum { #define _(s,n) LISP_GPE_INPUT_NEXT_##s, @@ -96,7 +95,7 @@ typedef enum { #include <vnet/lisp-gpe/lisp_gpe_error.def> #undef lisp_gpe_error LISP_GPE_N_ERROR, -} lisp_gpe_input_error_t; +} lisp_gpe_error_t; /* As a first step, reuse v4 fib. The goal of the typedef is to shield * consumers from future updates that may result in the lisp ip4 fib diverging @@ -114,13 +113,18 @@ typedef struct lisp_gpe_main /* lookup tunnel by key */ mhash_t lisp_gpe_tunnel_by_key; - /* lookup tunnel by adjacency index */ - uword * lisp_gpe_tunnel_by_adj_index; + /* lookup decap tunnel termination sw_if_index by vni and vice versa */ + uword * tunnel_term_sw_if_index_by_vni; + uword * vni_by_tunnel_term_sw_if_index; /* Free vlib hw_if_indices */ u32 * free_lisp_gpe_tunnel_hw_if_indices; - u32 lisp_gpe_hw_if_index; + /* Lookup lisp-gpe interfaces by vrf */ + uword * lisp_gpe_hw_if_index_by_table_id; + + /* Lookup lgpe_ip4_lookup_next by vrf */ + uword * lgpe_ip4_lookup_next_index_by_table_id; /* next node indexes that points ip4 lookup to lisp gpe lookup and lisp cp */ u32 ip4_lookup_next_lgpe_ip4_lookup; @@ -136,10 +140,9 @@ lisp_gpe_main_t lisp_gpe_main; extern vlib_node_registration_t lgpe_ip4_lookup_node; extern vlib_node_registration_t lisp_gpe_input_node; -extern vlib_node_registration_t lisp_gpe_encap_node; u8 * -format_lisp_gpe_encap_trace (u8 * s, va_list * args); +format_lisp_gpe_tx_trace (u8 * s, va_list * args); u8 * format_lisp_gpe_header_with_length (u8 * s, va_list * args); @@ -154,7 +157,7 @@ typedef struct u8 ver_res; u8 res; u8 next_protocol; - u32 iid; /* host byte order */ + u32 vni; /* host byte order */ } vnet_lisp_gpe_add_del_tunnel_args_t; int @@ -164,12 +167,22 @@ vnet_lisp_gpe_add_del_tunnel (vnet_lisp_gpe_add_del_tunnel_args_t *a, typedef struct { u8 is_add; + u32 table_id; /* vrf */ + u32 vni; /* host byte order */ } vnet_lisp_gpe_add_del_iface_args_t; void vnet_lisp_gpe_add_del_iface (vnet_lisp_gpe_add_del_iface_args_t *a, u32 * hw_if_indexp); +typedef struct +{ + u8 is_en; +} vnet_lisp_gpe_enable_disable_args_t; + +clib_error_t * +vnet_lisp_gpe_enable_disable (vnet_lisp_gpe_enable_disable_args_t *a); + typedef enum { NO_ACTION, @@ -194,7 +207,8 @@ typedef struct u8 ver_res; u8 res; u8 next_protocol; - u32 iid; /* host byte order */ + u32 vni; /* host byte order */ + u32 table_id; } vnet_lisp_gpe_add_del_fwd_entry_args_t; int diff --git a/vnet/vnet/lisp-gpe/lisp_gpe_error.def b/vnet/vnet/lisp-gpe/lisp_gpe_error.def index f002ecae..c4c3f75d 100644 --- a/vnet/vnet/lisp-gpe/lisp_gpe_error.def +++ b/vnet/vnet/lisp-gpe/lisp_gpe_error.def @@ -13,5 +13,6 @@ * limitations under the License. */ +lisp_gpe_error (ENCAPSULATED, "good packets encapsulated") lisp_gpe_error (DECAPSULATED, "good packets decapsulated") lisp_gpe_error (NO_SUCH_TUNNEL, "no such tunnel packets") |