From a23197980e40d4d9414bcfaf59005a1dc2a89251 Mon Sep 17 00:00:00 2001 From: sreejith Date: Wed, 29 Mar 2017 01:15:02 -0400 Subject: Added vpp intial source code from master branch 17.01.1 Change-Id: I81bdace6f330825a1746a853766779dfb24765fd Signed-off-by: sreejith --- vpp/vnet/vnet/lisp-gpe/decap.c | 501 +++++++++++ vpp/vnet/vnet/lisp-gpe/dir.dox | 26 + vpp/vnet/vnet/lisp-gpe/interface.c | 709 +++++++++++++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe.c | 327 +++++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe.h | 257 ++++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_adjacency.c | 542 ++++++++++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_adjacency.h | 136 +++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_error.def | 18 + vpp/vnet/vnet/lisp-gpe/lisp_gpe_fwd_entry.c | 1053 +++++++++++++++++++++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_fwd_entry.h | 188 ++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_packet.h | 149 ++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.c | 278 ++++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.h | 157 ++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_tenant.c | 330 +++++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_tenant.h | 88 ++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_tunnel.c | 289 +++++++ vpp/vnet/vnet/lisp-gpe/lisp_gpe_tunnel.h | 89 ++ vpp/vnet/vnet/lisp-gpe/rfc.txt | 826 ++++++++++++++++++ 18 files changed, 5963 insertions(+) create mode 100644 vpp/vnet/vnet/lisp-gpe/decap.c create mode 100644 vpp/vnet/vnet/lisp-gpe/dir.dox create mode 100644 vpp/vnet/vnet/lisp-gpe/interface.c create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe.c create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe.h create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_adjacency.c create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_adjacency.h create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_error.def create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_fwd_entry.c create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_fwd_entry.h create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_packet.h create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.c create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.h create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_tenant.c create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_tenant.h create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_tunnel.c create mode 100644 vpp/vnet/vnet/lisp-gpe/lisp_gpe_tunnel.h create mode 100644 vpp/vnet/vnet/lisp-gpe/rfc.txt (limited to 'vpp/vnet/vnet/lisp-gpe') diff --git a/vpp/vnet/vnet/lisp-gpe/decap.c b/vpp/vnet/vnet/lisp-gpe/decap.c new file mode 100644 index 00000000..637d4a74 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/decap.c @@ -0,0 +1,501 @@ +/* + * 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. + */ +/** + * @file + * @brief L2 LISP-GPE decap code. + * + */ +#include +#include +#include + +typedef struct +{ + u32 next_index; + u32 tunnel_index; + u32 error; + lisp_gpe_header_t h; +} lisp_gpe_rx_trace_t; + +static u8 * +format_lisp_gpe_rx_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_rx_trace_t *t = va_arg (*args, lisp_gpe_rx_trace_t *); + + if (t->tunnel_index != ~0) + { + s = format (s, "LISP-GPE: tunnel %d next %d error %d", t->tunnel_index, + t->next_index, t->error); + } + else + { + s = format (s, "LISP-GPE: no tunnel next %d error %d\n", t->next_index, + t->error); + } + s = format (s, "\n %U", format_lisp_gpe_header_with_length, &t->h, + (u32) sizeof (t->h) /* max size */ ); + return s; +} + +static u32 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_L2_INPUT, + LISP_GPE_INPUT_NEXT_DROP +}; + +always_inline u32 +next_protocol_to_next_index (lisp_gpe_header_t * lgh, u8 * next_header) +{ + /* lisp-gpe router */ + if (PREDICT_TRUE ((lgh->flags & LISP_GPE_FLAGS_P) + && lgh->next_protocol < LISP_GPE_NEXT_PROTOS)) + return next_proto_to_next_index[lgh->next_protocol]; + /* legacy lisp router */ + else if ((lgh->flags & LISP_GPE_FLAGS_P) == 0) + { + ip4_header_t *iph = (ip4_header_t *) next_header; + if ((iph->ip_version_and_header_length & 0xF0) == 0x40) + return LISP_GPE_INPUT_NEXT_IP4_INPUT; + else if ((iph->ip_version_and_header_length & 0xF0) == 0x60) + return LISP_GPE_INPUT_NEXT_IP6_INPUT; + else + return LISP_GPE_INPUT_NEXT_DROP; + } + else + return LISP_GPE_INPUT_NEXT_DROP; +} + +always_inline tunnel_lookup_t * +next_index_to_iface (lisp_gpe_main_t * lgm, u32 next_index) +{ + if (LISP_GPE_INPUT_NEXT_IP4_INPUT == next_index + || LISP_GPE_INPUT_NEXT_IP6_INPUT == next_index) + return &lgm->l3_ifaces; + else if (LISP_GPE_INPUT_NEXT_L2_INPUT == next_index) + return &lgm->l2_ifaces; + clib_warning ("next_index not associated to an interface!"); + return 0; +} + +static_always_inline void +incr_decap_stats (vnet_main_t * vnm, u32 cpu_index, u32 length, + u32 sw_if_index, u32 * last_sw_if_index, u32 * n_packets, + u32 * n_bytes) +{ + vnet_interface_main_t *im; + + if (PREDICT_TRUE (sw_if_index == *last_sw_if_index)) + { + *n_packets += 1; + *n_bytes += length; + } + else + { + if (PREDICT_TRUE (*last_sw_if_index != ~0)) + { + im = &vnm->interface_main; + + vlib_increment_combined_counter (im->combined_sw_if_counters + + VNET_INTERFACE_COUNTER_RX, + cpu_index, *last_sw_if_index, + *n_packets, *n_bytes); + } + *last_sw_if_index = sw_if_index; + *n_packets = 1; + *n_bytes = length; + } +} + +/** + * @brief LISP-GPE decap dispatcher. + * @node lisp_gpe_input_inline + * + * LISP-GPE decap dispatcher. + * + * Decaps IP-UDP-LISP-GPE header and based on the next protocol and in the + * GPE header and the vni decides the next node to forward the packet to. + * + * @param[in] vm vlib_main_t corresponding to current thread. + * @param[in] node vlib_node_runtime_t data for this node. + * @param[in] frame vlib_frame_t whose contents should be dispatched. + * + * @return number of vectors in frame. + */ +static uword +lisp_gpe_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame, u8 is_v4) +{ + u32 n_left_from, next_index, *from, *to_next, cpu_index; + u32 n_bytes = 0, n_packets = 0, last_sw_if_index = ~0, drops = 0; + lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); + + cpu_index = os_get_cpu_number (); + 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; + ip4_udp_lisp_gpe_header_t *iul4_0, *iul4_1; + ip6_udp_lisp_gpe_header_t *iul6_0, *iul6_1; + lisp_gpe_header_t *lh0, *lh1; + u32 next0, next1, error0, error1; + uword *si0, *si1; + tunnel_lookup_t *tl0, *tl1; + + /* 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); + + /* udp leaves current_data pointing at the lisp header */ + if (is_v4) + { + vlib_buffer_advance (b0, + -(word) (sizeof (udp_header_t) + + sizeof (ip4_header_t))); + vlib_buffer_advance (b1, + -(word) (sizeof (udp_header_t) + + sizeof (ip4_header_t))); + + iul4_0 = vlib_buffer_get_current (b0); + iul4_1 = vlib_buffer_get_current (b1); + + /* pop (ip, udp, lisp-gpe) */ + vlib_buffer_advance (b0, sizeof (*iul4_0)); + vlib_buffer_advance (b1, sizeof (*iul4_1)); + + lh0 = &iul4_0->lisp; + lh1 = &iul4_1->lisp; + } + else + { + vlib_buffer_advance (b0, + -(word) (sizeof (udp_header_t) + + sizeof (ip6_header_t))); + vlib_buffer_advance (b1, + -(word) (sizeof (udp_header_t) + + sizeof (ip6_header_t))); + + iul6_0 = vlib_buffer_get_current (b0); + iul6_1 = vlib_buffer_get_current (b1); + + /* pop (ip, udp, lisp-gpe) */ + vlib_buffer_advance (b0, sizeof (*iul6_0)); + vlib_buffer_advance (b1, sizeof (*iul6_1)); + + lh0 = &iul6_0->lisp; + lh1 = &iul6_1->lisp; + } + + /* determine next_index from lisp-gpe header */ + next0 = next_protocol_to_next_index (lh0, + vlib_buffer_get_current (b0)); + next1 = next_protocol_to_next_index (lh1, + vlib_buffer_get_current (b1)); + + /* determine if tunnel is l2 or l3 */ + tl0 = next_index_to_iface (lgm, next0); + tl1 = next_index_to_iface (lgm, next1); + + /* 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 (tl0->sw_if_index_by_vni, + clib_net_to_host_u32 (lh0->iid)); + si1 = hash_get (tl1->sw_if_index_by_vni, + clib_net_to_host_u32 (lh1->iid)); + + + /* Required to make the l2 tag push / pop code work on l2 subifs */ + vnet_update_l2_len (b0); + vnet_update_l2_len (b1); + + if (si0) + { + incr_decap_stats (lgm->vnet_main, cpu_index, + vlib_buffer_length_in_chain (vm, b0), si0[0], + &last_sw_if_index, &n_packets, &n_bytes); + vnet_buffer (b0)->sw_if_index[VLIB_RX] = si0[0]; + error0 = 0; + } + else + { + next0 = LISP_GPE_INPUT_NEXT_DROP; + error0 = LISP_GPE_ERROR_NO_TUNNEL; + drops++; + } + + if (si1) + { + incr_decap_stats (lgm->vnet_main, cpu_index, + vlib_buffer_length_in_chain (vm, b1), si1[0], + &last_sw_if_index, &n_packets, &n_bytes); + vnet_buffer (b1)->sw_if_index[VLIB_RX] = si1[0]; + error1 = 0; + } + else + { + next1 = LISP_GPE_INPUT_NEXT_DROP; + error1 = LISP_GPE_ERROR_NO_TUNNEL; + drops++; + } + + b0->error = error0 ? node->errors[error0] : 0; + b1->error = error1 ? node->errors[error1] : 0; + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + lisp_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b0, + sizeof (*tr)); + tr->next_index = next0; + tr->error = error0; + tr->h = lh0[0]; + } + + if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) + { + lisp_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b1, + sizeof (*tr)); + tr->next_index = next1; + tr->error = error1; + tr->h = lh1[0]; + } + + 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) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0; + ip4_udp_lisp_gpe_header_t *iul4_0; + ip6_udp_lisp_gpe_header_t *iul6_0; + lisp_gpe_header_t *lh0; + u32 error0; + uword *si0; + tunnel_lookup_t *tl0; + + 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); + + /* udp leaves current_data pointing at the lisp header + * TODO: there's no difference in processing between v4 and v6 + * encapsulated packets so the code should be simplified if ip header + * info is not going to be used for dp smrs/dpsec */ + if (is_v4) + { + vlib_buffer_advance (b0, + -(word) (sizeof (udp_header_t) + + sizeof (ip4_header_t))); + + iul4_0 = vlib_buffer_get_current (b0); + + /* pop (ip, udp, lisp-gpe) */ + vlib_buffer_advance (b0, sizeof (*iul4_0)); + + lh0 = &iul4_0->lisp; + } + else + { + vlib_buffer_advance (b0, + -(word) (sizeof (udp_header_t) + + sizeof (ip6_header_t))); + + iul6_0 = vlib_buffer_get_current (b0); + + /* pop (ip, udp, lisp-gpe) */ + vlib_buffer_advance (b0, sizeof (*iul6_0)); + + lh0 = &iul6_0->lisp; + } + + /* TODO if security is to be implemented, something similar to RPF, + * probably we'd like to check that the peer is allowed to send us + * packets. For this, we should use the tunnel table OR check that + * we have a mapping for the source eid and that the outer source of + * the packet is one of its locators */ + + /* determine next_index from lisp-gpe header */ + next0 = next_protocol_to_next_index (lh0, + vlib_buffer_get_current (b0)); + + /* determine if tunnel is l2 or l3 */ + tl0 = next_index_to_iface (lgm, next0); + + /* 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. + * NOTE: vni uses only the first 24 bits */ + si0 = hash_get (tl0->sw_if_index_by_vni, + clib_net_to_host_u32 (lh0->iid << 8)); + + /* Required to make the l2 tag push / pop code work on l2 subifs */ + vnet_update_l2_len (b0); + + if (si0) + { + incr_decap_stats (lgm->vnet_main, cpu_index, + vlib_buffer_length_in_chain (vm, b0), si0[0], + &last_sw_if_index, &n_packets, &n_bytes); + vnet_buffer (b0)->sw_if_index[VLIB_RX] = si0[0]; + error0 = 0; + } + else + { + next0 = LISP_GPE_INPUT_NEXT_DROP; + error0 = LISP_GPE_ERROR_NO_TUNNEL; + drops++; + } + + /* TODO error handling if security is implemented */ + b0->error = error0 ? node->errors[error0] : 0; + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + lisp_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b0, + sizeof (*tr)); + tr->next_index = next0; + tr->error = error0; + tr->h = lh0[0]; + } + + 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); + } + + /* flush iface stats */ + incr_decap_stats (lgm->vnet_main, cpu_index, 0, ~0, &last_sw_if_index, + &n_packets, &n_bytes); + vlib_node_increment_counter (vm, lisp_gpe_ip4_input_node.index, + LISP_GPE_ERROR_NO_TUNNEL, drops); + return from_frame->n_vectors; +} + +static uword +lisp_gpe_ip4_input (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return lisp_gpe_input_inline (vm, node, from_frame, 1); +} + +static uword +lisp_gpe_ip6_input (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return lisp_gpe_input_inline (vm, node, from_frame, 0); +} + +static char *lisp_gpe_ip4_input_error_strings[] = { +#define lisp_gpe_error(n,s) s, +#include +#undef lisp_gpe_error +}; + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (lisp_gpe_ip4_input_node) = { + .function = lisp_gpe_ip4_input, + .name = "lisp-gpe-ip4-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + .n_next_nodes = LISP_GPE_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [LISP_GPE_INPUT_NEXT_##s] = n, + foreach_lisp_gpe_ip_input_next +#undef _ + }, + + .n_errors = ARRAY_LEN (lisp_gpe_ip4_input_error_strings), + .error_strings = lisp_gpe_ip4_input_error_strings, + + .format_buffer = format_lisp_gpe_header_with_length, + .format_trace = format_lisp_gpe_rx_trace, + // $$$$ .unformat_buffer = unformat_lisp_gpe_header, +}; +/* *INDENT-ON* */ + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (lisp_gpe_ip6_input_node) = { + .function = lisp_gpe_ip6_input, + .name = "lisp-gpe-ip6-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + .n_next_nodes = LISP_GPE_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [LISP_GPE_INPUT_NEXT_##s] = n, + foreach_lisp_gpe_ip_input_next +#undef _ + }, + + .n_errors = ARRAY_LEN (lisp_gpe_ip4_input_error_strings), + .error_strings = lisp_gpe_ip4_input_error_strings, + + .format_buffer = format_lisp_gpe_header_with_length, + .format_trace = format_lisp_gpe_rx_trace, + // $$$$ .unformat_buffer = unformat_lisp_gpe_header, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/dir.dox b/vpp/vnet/vnet/lisp-gpe/dir.dox new file mode 100644 index 00000000..afa6da9a --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/dir.dox @@ -0,0 +1,26 @@ +/* + * + * Copyright (c) 2013 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. + */ +/** + @dir + @brief LISP-GPE code. + + An implementation of LISP-GPE as per: + rfc-6830 + draft-lewis-lisp-gpe-02 + + See file: rfc.txt + +*/ \ No newline at end of file diff --git a/vpp/vnet/vnet/lisp-gpe/interface.c b/vpp/vnet/vnet/lisp-gpe/interface.c new file mode 100644 index 00000000..3288b241 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/interface.c @@ -0,0 +1,709 @@ +/* + * 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. + */ + +/** + * @file + * @brief Common utility functions for LISP-GPE interfaces. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief The VLIB node arc/edge from the interface's TX node, to the L2 + * load-balanceing node. Which is where all packets go + */ +static uword l2_arc_to_lb; + +#define foreach_lisp_gpe_tx_next \ + _(DROP, "error-drop") \ + _(IP4_LOOKUP, "ip4-lookup") \ + _(IP6_LOOKUP, "ip6-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; +} + +#define is_v4_packet(_h) ((*(u8*) _h) & 0xF0) == 0x40 + +/** + * @brief LISP-GPE interface TX (encap) function. + * @node lisp_gpe_interface_tx + * + * The LISP-GPE interface TX (encap) function. + * + * Looks up the associated tunnel based on the adjacency hit in the SD FIB + * and if the tunnel is multihomed it uses the flow hash to determine + * sub-tunnel, and rewrite string, to be used to encapsulate the packet. + * + * @param[in] vm vlib_main_t corresponding to the current thread. + * @param[in] node vlib_node_runtime_t data for this node. + * @param[in] frame vlib_frame_t whose contents should be dispatched. + * + * @return number of vectors in frame. + */ +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; + + 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 > 0 && n_left_to_next > 0) + { + u32 bi0, adj_index0, next0; + const ip_adjacency_t *adj0; + const dpo_id_t *dpo0; + vlib_buffer_t *b0; + u8 is_v4_0; + + 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); + + /* Fixup the checksum and len fields in the LISP tunnel encap + * that was applied at the midchain node */ + is_v4_0 = is_v4_packet (vlib_buffer_get_current (b0)); + ip_udp_fixup_one (lgm->vlib_main, b0, is_v4_0); + + /* Follow the DPO on which the midchain is stacked */ + adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; + adj0 = adj_get (adj_index0); + dpo0 = &adj0->sub_type.midchain.next_dpo; + next0 = dpo0->dpoi_next_node; + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_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 = adj_index0; + } + 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); + } + + 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); +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (lisp_gpe_device_class) = { + .name = "LISP_GPE", + .format_device_name = format_lisp_gpe_name, + .format_tx_trace = format_lisp_gpe_tx_trace, + .tx_function = lisp_gpe_interface_tx, +}; +/* *INDENT-ON* */ + +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; +} + +/* *INDENT-OFF* */ +VNET_HW_INTERFACE_CLASS (lisp_gpe_hw_class) = { + .name = "LISP_GPE", + .format_header = format_lisp_gpe_header_with_length, + .build_rewrite = lisp_gpe_build_rewrite, + .update_adjacency = lisp_gpe_update_adjacency, +}; +/* *INDENT-ON* */ + + +typedef struct +{ + u32 lb_index; +} l2_lisp_gpe_tx_trace_t; + +static u8 * +format_l2_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 *); + l2_lisp_gpe_tx_trace_t *t = va_arg (*args, l2_lisp_gpe_tx_trace_t *); + + s = format (s, "L2-LISP-GPE-TX: load-balance %d", t->lb_index); + return s; +} + +/** + * @brief LISP-GPE interface TX (encap) function for L2 overlays. + * @node l2_lisp_gpe_interface_tx + * + * The L2 LISP-GPE interface TX (encap) function. + * + * Uses bridge domain index, source and destination ethernet addresses to + * lookup tunnel. If the tunnel is multihomed a flow has is used to determine + * the sub-tunnel and therefore the rewrite string to be used to encapsulate + * the packets. + * + * @param[in] vm vlib_main_t corresponding to the current thread. + * @param[in] node vlib_node_runtime_t data for this node. + * @param[in] frame vlib_frame_t whose contents should be dispatched. + * + * @return number of vectors in frame. + */ +static uword +l2_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; + + 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 > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + u32 bi0, lbi0; + ethernet_header_t *e0; + + 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); + e0 = vlib_buffer_get_current (b0); + + vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_MAC; + + /* lookup dst + src mac */ + lbi0 = lisp_l2_fib_lookup (lgm, vnet_buffer (b0)->l2.bd_index, + e0->src_address, e0->dst_address); + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = lbi0; + + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + l2_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0, + sizeof (*tr)); + tr->lb_index = lbi0; + } + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, l2_arc_to_lb); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return from_frame->n_vectors; +} + +static u8 * +format_l2_lisp_gpe_name (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + return format (s, "l2_lisp_gpe%d", dev_instance); +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (l2_lisp_gpe_device_class,static) = { + .name = "L2_LISP_GPE", + .format_device_name = format_l2_lisp_gpe_name, + .format_tx_trace = format_l2_lisp_gpe_tx_trace, + .tx_function = l2_lisp_gpe_interface_tx, +}; +/* *INDENT-ON* */ + +static vnet_hw_interface_t * +lisp_gpe_create_iface (lisp_gpe_main_t * lgm, u32 vni, u32 dp_table, + vnet_device_class_t * dev_class, + tunnel_lookup_t * tuns) +{ + u32 flen; + u32 hw_if_index = ~0; + u8 *new_name; + vnet_hw_interface_t *hi; + vnet_main_t *vnm = lgm->vnet_main; + + /* create hw lisp_gpeX iface if needed, otherwise reuse existing */ + flen = vec_len (lgm->free_tunnel_hw_if_indices); + if (flen > 0) + { + hw_if_index = lgm->free_tunnel_hw_if_indices[flen - 1]; + _vec_len (lgm->free_tunnel_hw_if_indices) -= 1; + + hi = vnet_get_hw_interface (vnm, hw_if_index); + + /* rename interface */ + new_name = format (0, "%U", dev_class->format_device_name, vni); + + vec_add1 (new_name, 0); + vnet_rename_interface (vnm, hw_if_index, (char *) new_name); + vec_free (new_name); + + /* clear old stats of freed interface before reuse */ + vnet_interface_main_t *im = &vnm->interface_main; + vnet_interface_counter_lock (im); + vlib_zero_combined_counter (&im->combined_sw_if_counters + [VNET_INTERFACE_COUNTER_TX], + hi->sw_if_index); + vlib_zero_combined_counter (&im->combined_sw_if_counters + [VNET_INTERFACE_COUNTER_RX], + hi->sw_if_index); + vlib_zero_simple_counter (&im->sw_if_counters + [VNET_INTERFACE_COUNTER_DROP], + hi->sw_if_index); + vnet_interface_counter_unlock (im); + } + else + { + hw_if_index = vnet_register_interface (vnm, dev_class->index, vni, + lisp_gpe_hw_class.index, 0); + hi = vnet_get_hw_interface (vnm, hw_if_index); + } + + hash_set (tuns->hw_if_index_by_dp_table, dp_table, hw_if_index); + + /* set tunnel termination: post decap, packets are tagged as having been + * originated by lisp-gpe interface */ + hash_set (tuns->sw_if_index_by_vni, vni, hi->sw_if_index); + hash_set (tuns->vni_by_sw_if_index, hi->sw_if_index, vni); + + return hi; +} + +static void +lisp_gpe_remove_iface (lisp_gpe_main_t * lgm, u32 hi_index, u32 dp_table, + tunnel_lookup_t * tuns) +{ + vnet_main_t *vnm = lgm->vnet_main; + vnet_hw_interface_t *hi; + uword *vnip; + + hi = vnet_get_hw_interface (vnm, hi_index); + + /* 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 (tuns->hw_if_index_by_dp_table, dp_table); + vec_add1 (lgm->free_tunnel_hw_if_indices, hi->hw_if_index); + + /* clean tunnel termination and vni to sw_if_index binding */ + vnip = hash_get (tuns->vni_by_sw_if_index, hi->sw_if_index); + if (0 == vnip) + { + clib_warning ("No vni associated to interface %d", hi->sw_if_index); + return; + } + hash_unset (tuns->sw_if_index_by_vni, vnip[0]); + hash_unset (tuns->vni_by_sw_if_index, hi->sw_if_index); +} + +static void +lisp_gpe_iface_set_table (u32 sw_if_index, u32 table_id) +{ + fib_node_index_t fib_index; + + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id); + vec_validate (ip4_main.fib_index_by_sw_if_index, sw_if_index); + ip4_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; + ip4_sw_interface_enable_disable (sw_if_index, 1); + + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id); + vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index); + ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; + ip6_sw_interface_enable_disable (sw_if_index, 1); +} + +static void +lisp_gpe_tenant_del_default_routes (u32 table_id) +{ + fib_protocol_t proto; + + FOR_EACH_FIB_IP_PROTOCOL (proto) + { + fib_prefix_t prefix = { + .fp_proto = proto, + }; + u32 fib_index; + + fib_index = fib_table_find (prefix.fp_proto, table_id); + fib_table_entry_special_remove (fib_index, &prefix, FIB_SOURCE_LISP); + fib_table_unlock (fib_index, prefix.fp_proto); + } +} + +static void +lisp_gpe_tenant_add_default_routes (u32 table_id) +{ + fib_protocol_t proto; + + FOR_EACH_FIB_IP_PROTOCOL (proto) + { + fib_prefix_t prefix = { + .fp_proto = proto, + }; + u32 fib_index; + + /* + * Add a deafult route that results in a control plane punt DPO + */ + fib_index = fib_table_find_or_create_and_lock (prefix.fp_proto, table_id); + fib_table_entry_special_dpo_add (fib_index, &prefix, FIB_SOURCE_LISP, + FIB_ENTRY_FLAG_EXCLUSIVE, + lisp_cp_dpo_get (fib_proto_to_dpo + (proto))); + } +} + + +/** + * @brief Add/del LISP-GPE L3 interface. + * + * Creates LISP-GPE interface, sets ingress arcs from lisp_gpeX_lookup, + * installs default routes that attract all traffic with no more specific + * routes to lgpe-ipx-lookup, set egress arcs to ipx-lookup, sets + * the interface in the right vrf and enables it. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters to create interface. + * + * @return number of vectors in frame. + */ +u32 +lisp_gpe_add_l3_iface (lisp_gpe_main_t * lgm, u32 vni, u32 table_id) +{ + vnet_main_t *vnm = lgm->vnet_main; + tunnel_lookup_t *l3_ifaces = &lgm->l3_ifaces; + vnet_hw_interface_t *hi; + uword *hip, *si; + + hip = hash_get (l3_ifaces->hw_if_index_by_dp_table, table_id); + + if (hip) + { + clib_warning ("vrf %d already mapped to a vni", table_id); + return ~0; + } + + si = hash_get (l3_ifaces->sw_if_index_by_vni, vni); + + if (si) + { + clib_warning ("Interface for vni %d already exists", vni); + } + + /* create lisp iface and populate tunnel tables */ + hi = lisp_gpe_create_iface (lgm, vni, table_id, + &lisp_gpe_device_class, l3_ifaces); + + /* insert default routes that point to lisp-cp lookup */ + lisp_gpe_iface_set_table (hi->sw_if_index, table_id); + lisp_gpe_tenant_add_default_routes (table_id); + + /* 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); + + return (hi->sw_if_index); +} + +void +lisp_gpe_del_l3_iface (lisp_gpe_main_t * lgm, u32 vni, u32 table_id) +{ + vnet_main_t *vnm = lgm->vnet_main; + tunnel_lookup_t *l3_ifaces = &lgm->l3_ifaces; + vnet_hw_interface_t *hi; + uword *hip; + + hip = hash_get (l3_ifaces->hw_if_index_by_dp_table, table_id); + + if (hip == 0) + { + clib_warning ("The interface for vrf %d doesn't exist", table_id); + return; + } + + hi = vnet_get_hw_interface (vnm, hip[0]); + + lisp_gpe_remove_iface (lgm, hip[0], table_id, &lgm->l3_ifaces); + + /* unset default routes */ + ip4_sw_interface_enable_disable (hi->sw_if_index, 0); + ip6_sw_interface_enable_disable (hi->sw_if_index, 0); + lisp_gpe_tenant_del_default_routes (table_id); +} + +/** + * @brief Add/del LISP-GPE L2 interface. + * + * Creates LISP-GPE interface, sets it in L2 mode in the appropriate + * bridge domain, sets egress arcs and enables it. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters to create interface. + * + * @return number of vectors in frame. + */ +u32 +lisp_gpe_add_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id) +{ + vnet_main_t *vnm = lgm->vnet_main; + tunnel_lookup_t *l2_ifaces = &lgm->l2_ifaces; + vnet_hw_interface_t *hi; + uword *hip, *si; + u16 bd_index; + + bd_index = bd_find_or_add_bd_index (&bd_main, bd_id); + hip = hash_get (l2_ifaces->hw_if_index_by_dp_table, bd_index); + + if (hip) + { + clib_warning ("bridge domain %d already mapped to a vni", bd_id); + return ~0; + } + + si = hash_get (l2_ifaces->sw_if_index_by_vni, vni); + if (si) + { + clib_warning ("Interface for vni %d already exists", vni); + return ~0; + } + + /* create lisp iface and populate tunnel tables */ + hi = lisp_gpe_create_iface (lgm, vni, bd_index, + &l2_lisp_gpe_device_class, &lgm->l2_ifaces); + + /* 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); + + l2_arc_to_lb = vlib_node_add_named_next (vlib_get_main (), + hi->tx_node_index, + "l2-load-balance"); + + /* we're ready. add iface to l2 bridge domain */ + set_int_l2_mode (lgm->vlib_main, vnm, MODE_L2_BRIDGE, hi->sw_if_index, + bd_index, 0, 0, 0); + + return (hi->sw_if_index); +} + +/** + * @brief Add/del LISP-GPE L2 interface. + * + * Creates LISP-GPE interface, sets it in L2 mode in the appropriate + * bridge domain, sets egress arcs and enables it. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters to create interface. + * + * @return number of vectors in frame. + */ +void +lisp_gpe_del_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id) +{ + tunnel_lookup_t *l2_ifaces = &lgm->l2_ifaces; + u16 bd_index; + uword *hip; + + bd_index = bd_find_or_add_bd_index (&bd_main, bd_id); + hip = hash_get (l2_ifaces->hw_if_index_by_dp_table, bd_index); + + if (hip == 0) + { + clib_warning ("The interface for bridge domain %d doesn't exist", + bd_id); + return; + } + lisp_gpe_remove_iface (lgm, hip[0], bd_index, &lgm->l2_ifaces); +} + +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, vni, bd_id; + u8 vni_is_set = 0, vrf_is_set = 0, bd_index_is_set = 0; + + if (vnet_lisp_gpe_enable_disable_status () == 0) + { + return clib_error_return (0, "LISP is disabled"); + } + + /* 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)) + { + vrf_is_set = 1; + } + else if (unformat (line_input, "vni %d", &vni)) + { + vni_is_set = 1; + } + else if (unformat (line_input, "bd %d", &bd_id)) + { + bd_index_is_set = 1; + } + else + { + return clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + } + } + + if (vrf_is_set && bd_index_is_set) + return clib_error_return (0, + "Cannot set both vrf and brdige domain index!"); + + if (!vni_is_set) + return clib_error_return (0, "vni must be set!"); + + if (!vrf_is_set && !bd_index_is_set) + return clib_error_return (0, "vrf or bridge domain index must be set!"); + + if (bd_index_is_set) + { + if (is_add) + { + if (~0 == lisp_gpe_tenant_l2_iface_add_or_lock (vni, bd_id)) + return clib_error_return (0, "L2 interface not created"); + } + else + lisp_gpe_tenant_l2_iface_unlock (vni); + } + else + { + if (is_add) + { + if (~0 == lisp_gpe_tenant_l3_iface_add_or_lock (vni, table_id)) + return clib_error_return (0, "L3 interface not created"); + } + else + lisp_gpe_tenant_l3_iface_unlock (vni); + } + + return (NULL); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (add_del_lisp_gpe_iface_command, static) = { + .path = "lisp gpe iface", + .short_help = "lisp gpe iface add/del vni vrf ", + .function = lisp_gpe_add_del_iface_command_fn, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe.c b/vpp/vnet/vnet/lisp-gpe/lisp_gpe.c new file mode 100644 index 00000000..fbda8687 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe.c @@ -0,0 +1,327 @@ +/* + * 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. + */ +/** + * @file + * @brief Common utility functions for IPv4, IPv6 and L2 LISP-GPE tunnels. + * + */ + +#include +#include +#include +#include + +/** LISP-GPE global state */ +lisp_gpe_main_t lisp_gpe_main; + + +/** CLI command to add/del forwarding entry. */ +static clib_error_t * +lisp_gpe_add_del_fwd_entry_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; + ip_address_t lloc, rloc; + clib_error_t *error = 0; + gid_address_t _reid, *reid = &_reid, _leid, *leid = &_leid; + u8 reid_set = 0, leid_set = 0, is_negative = 0, vrf_set = 0, vni_set = 0; + u32 vni, vrf, action = ~0, p, w; + locator_pair_t pair, *pairs = 0; + int rv; + + /* 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, "del")) + is_add = 0; + else if (unformat (line_input, "add")) + is_add = 1; + else if (unformat (line_input, "leid %U", unformat_gid_address, leid)) + { + leid_set = 1; + } + else if (unformat (line_input, "reid %U", unformat_gid_address, reid)) + { + reid_set = 1; + } + else if (unformat (line_input, "vni %u", &vni)) + { + gid_address_vni (leid) = vni; + gid_address_vni (reid) = vni; + vni_set = 1; + } + else if (unformat (line_input, "vrf %u", &vrf)) + { + vrf_set = 1; + } + else if (unformat (line_input, "bd %u", &vrf)) + { + vrf_set = 1; + } + else if (unformat (line_input, "negative action %U", + unformat_negative_mapping_action, &action)) + { + is_negative = 1; + } + else if (unformat (line_input, "loc-pair %U %U p %d w %d", + unformat_ip_address, &lloc, + unformat_ip_address, &rloc, &p, &w)) + { + pair.lcl_loc = lloc; + pair.rmt_loc = rloc; + pair.priority = p; + pair.weight = w; + vec_add1 (pairs, pair); + } + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + unformat_free (line_input); + + if (!vni_set || !vrf_set) + { + error = clib_error_return (0, "vni and vrf must be set!"); + goto done; + } + + if (!reid_set) + { + error = clib_error_return (0, "remote eid must be set!"); + goto done; + } + + if (is_negative) + { + if (~0 == action) + { + error = clib_error_return (0, "no action set for negative tunnel!"); + goto done; + } + } + else + { + if (vec_len (pairs) == 0) + { + error = clib_error_return (0, "expected ip4/ip6 locators."); + goto done; + } + } + + if (!leid_set) + { + /* if leid not set, make sure it's the same AFI like reid */ + gid_address_type (leid) = gid_address_type (reid); + if (GID_ADDR_IP_PREFIX == gid_address_type (reid)) + gid_address_ip_version (leid) = gid_address_ip_version (reid); + } + + /* add fwd entry */ + vnet_lisp_gpe_add_del_fwd_entry_args_t _a, *a = &_a; + memset (a, 0, sizeof (a[0])); + + a->is_add = is_add; + a->is_negative = is_negative; + a->vni = vni; + a->table_id = vrf; + gid_address_copy (&a->lcl_eid, leid); + gid_address_copy (&a->rmt_eid, reid); + a->locator_pairs = pairs; + + rv = vnet_lisp_gpe_add_del_fwd_entry (a, 0); + if (0 != rv) + { + error = clib_error_return (0, "failed to %s gpe tunnel!", + is_add ? "add" : "delete"); + } + +done: + vec_free (pairs); + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (lisp_gpe_add_del_fwd_entry_command, static) = { + .path = "lisp gpe entry", + .short_help = "lisp gpe entry add/del vni vrf [leid ]" + "reid [loc-pair p w ] " + "[negative action ]", + .function = lisp_gpe_add_del_fwd_entry_command_fn, +}; +/* *INDENT-ON* */ + +/** Check if LISP-GPE is enabled. */ +u8 +vnet_lisp_gpe_enable_disable_status (void) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + + return lgm->is_en; +} + +/** Enable/disable LISP-GPE. */ +clib_error_t * +vnet_lisp_gpe_enable_disable (vnet_lisp_gpe_enable_disable_args_t * a) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + + if (a->is_en) + { + lgm->is_en = 1; + } + else + { + /* remove all entries */ + vnet_lisp_gpe_fwd_entry_flush (); + + /* disable all l3 ifaces */ + lisp_gpe_tenant_flush (); + + lgm->is_en = 0; + } + + return 0; +} + +/** CLI command to enable/disable LISP-GPE. */ +static clib_error_t * +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_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)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + 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_en = is_en; + return vnet_lisp_gpe_enable_disable (a); +} + +/* *INDENT-OFF* */ +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, +}; +/* *INDENT-ON* */ + +/** CLI command to show LISP-GPE interfaces. */ +static clib_error_t * +lisp_show_iface_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + hash_pair_t *p; + + vlib_cli_output (vm, "%=10s%=12s", "vrf", "hw_if_index"); + + /* *INDENT-OFF* */ + hash_foreach_pair (p, lgm->l3_ifaces.hw_if_index_by_dp_table, ({ + vlib_cli_output (vm, "%=10d%=10d", p->key, p->value[0]); + })); + /* *INDENT-ON* */ + + if (0 != lgm->l2_ifaces.hw_if_index_by_dp_table) + { + vlib_cli_output (vm, "%=10s%=12s", "bd_id", "hw_if_index"); + /* *INDENT-OFF* */ + hash_foreach_pair (p, lgm->l2_ifaces.hw_if_index_by_dp_table, ({ + vlib_cli_output (vm, "%=10d%=10d", p->key, p->value[0]); + })); + /* *INDENT-ON* */ + } + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (lisp_show_iface_command) = { + .path = "show lisp gpe interface", + .short_help = "show lisp gpe interface", + .function = lisp_show_iface_command_fn, +}; +/* *INDENT-ON* */ + +/** Format LISP-GPE status. */ +u8 * +format_vnet_lisp_gpe_status (u8 * s, va_list * args) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + return format (s, "%s", lgm->is_en ? "enabled" : "disabled"); +} + + +/** LISP-GPE init function. */ +clib_error_t * +lisp_gpe_init (vlib_main_t * vm) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + clib_error_t *error = 0; + + if ((error = vlib_call_init_function (vm, ip_main_init))) + return error; + + if ((error = vlib_call_init_function (vm, ip4_lookup_init))) + return error; + + lgm->vnet_main = vnet_get_main (); + lgm->vlib_main = vm; + lgm->im4 = &ip4_main; + lgm->im6 = &ip6_main; + lgm->lm4 = &ip4_main.lookup_main; + lgm->lm6 = &ip6_main.lookup_main; + + lgm->lisp_gpe_fwd_entries = + hash_create_mem (0, sizeof (lisp_gpe_fwd_entry_key_t), sizeof (uword)); + + udp_register_dst_port (vm, UDP_DST_PORT_lisp_gpe, + lisp_gpe_ip4_input_node.index, 1 /* is_ip4 */ ); + udp_register_dst_port (vm, UDP_DST_PORT_lisp_gpe6, + lisp_gpe_ip6_input_node.index, 0 /* is_ip4 */ ); + return 0; +} + +VLIB_INIT_FUNCTION (lisp_gpe_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe.h b/vpp/vnet/vnet/lisp-gpe/lisp_gpe.h new file mode 100644 index 00000000..bb0f788b --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe.h @@ -0,0 +1,257 @@ +/* + * 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. + */ +/** + * @file + * @brief LISP-GPE definitions. + */ + +#ifndef included_vnet_lisp_gpe_h +#define included_vnet_lisp_gpe_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** IP4-UDP-LISP encap header */ +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct { + ip4_header_t ip4; /* 20 bytes */ + udp_header_t udp; /* 8 bytes */ + lisp_gpe_header_t lisp; /* 8 bytes */ +}) ip4_udp_lisp_gpe_header_t; +/* *INDENT-ON* */ + +/** IP6-UDP-LISP encap header */ +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct { + ip6_header_t ip6; /* 40 bytes */ + udp_header_t udp; /* 8 bytes */ + lisp_gpe_header_t lisp; /* 8 bytes */ +}) ip6_udp_lisp_gpe_header_t; +/* *INDENT-ON* */ + +#define foreach_lisp_gpe_ip_input_next \ +_(DROP, "error-drop") \ +_(IP4_INPUT, "ip4-input") \ +_(IP6_INPUT, "ip6-input") \ +_(L2_INPUT, "l2-input") + +/** Enum of possible next nodes post LISP-GPE decap */ +typedef enum +{ +#define _(s,n) LISP_GPE_INPUT_NEXT_##s, + foreach_lisp_gpe_ip_input_next +#undef _ + LISP_GPE_INPUT_N_NEXT, +} lisp_gpe_input_next_t; + +typedef enum +{ +#define lisp_gpe_error(n,s) LISP_GPE_ERROR_##n, +#include +#undef lisp_gpe_error + LISP_GPE_N_ERROR, +} lisp_gpe_error_t; + +typedef struct tunnel_lookup +{ + /** Lookup lisp-gpe interfaces by dp table (eg. vrf/bridge index) */ + uword *hw_if_index_by_dp_table; + + /** lookup decap tunnel termination sw_if_index by vni and vice versa */ + uword *sw_if_index_by_vni; + + // FIXME - Need this? + uword *vni_by_sw_if_index; +} tunnel_lookup_t; + +/** LISP-GPE global state*/ +typedef struct lisp_gpe_main +{ + /** + * @brief DB of all forwarding entries. The Key is:{l-EID,r-EID,vni} + * where the EID encodes L2 or L3 + */ + uword *lisp_gpe_fwd_entries; + + /** + * @brief A Pool of all LISP forwarding entries + */ + struct lisp_gpe_fwd_entry_t_ *lisp_fwd_entry_pool; + + /** Free vlib hw_if_indices */ + u32 *free_tunnel_hw_if_indices; + + u8 is_en; + + /* L3 data structures + * ================== */ + tunnel_lookup_t l3_ifaces; + + /* L2 data structures + * ================== */ + + /** L2 LISP FIB */ + BVT (clib_bihash) l2_fib; + + tunnel_lookup_t l2_ifaces; + + /** Load-balance for a miss in the table */ + dpo_id_t l2_lb_cp_lkup; + + /** convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; + ip4_main_t *im4; + ip6_main_t *im6; + ip_lookup_main_t *lm4; + ip_lookup_main_t *lm6; +} lisp_gpe_main_t; + +/** LISP-GPE global state*/ +lisp_gpe_main_t lisp_gpe_main; + +always_inline lisp_gpe_main_t * +vnet_lisp_gpe_get_main () +{ + return &lisp_gpe_main; +} + + +extern vlib_node_registration_t lisp_gpe_ip4_input_node; +extern vlib_node_registration_t lisp_gpe_ip6_input_node; +extern vnet_hw_interface_class_t lisp_gpe_hw_class; + +u8 *format_lisp_gpe_header_with_length (u8 * s, va_list * args); + +/** Read LISP-GPE status */ +u8 vnet_lisp_gpe_enable_disable_status (void); + +u32 +lisp_gpe_l3_iface_find_or_create (lisp_gpe_main_t * lgm, + u32 overlay_table_id, u32 vni); + +/** Add/del LISP-GPE interface. */ +extern void lisp_gpe_del_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id); +extern u32 lisp_gpe_add_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id); +extern void lisp_gpe_del_l3_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id); +extern u32 lisp_gpe_add_l3_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id); + + +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, + FORWARD_NATIVE, + SEND_MAP_REQUEST, + DROP +} negative_fwd_actions_e; + +/** */ +typedef struct +{ + u8 is_add; + + /** type of mapping */ + u8 is_negative; + + /** action for negative mappings */ + negative_fwd_actions_e action; + + /** local eid */ + gid_address_t lcl_eid; + + /** remote eid */ + gid_address_t rmt_eid; + + /** vector of locator pairs */ + locator_pair_t *locator_pairs; + + /** FIB index to lookup remote locator at encap */ + u32 encap_fib_index; + + /** FIB index to lookup inner IP at decap */ + u32 decap_fib_index; + + /* TODO remove */ + u32 decap_next_index; + + /** VNI/tenant id in HOST byte order */ + u32 vni; + + /** vrf or bd where fwd entry should be inserted */ + union + { + /** table (vrf) id */ + u32 table_id; + + /** bridge domain id */ + u16 bd_id; + + /** generic access */ + u32 dp_table; + }; +} vnet_lisp_gpe_add_del_fwd_entry_args_t; + +#define foreach_lgpe_ip4_lookup_next \ + _(DROP, "error-drop") \ + _(LISP_CP_LOOKUP, "lisp-cp-lookup") + +typedef enum lgpe_ip4_lookup_next +{ +#define _(sym,str) LGPE_IP4_LOOKUP_NEXT_##sym, + foreach_lgpe_ip4_lookup_next +#undef _ + LGPE_IP4_LOOKUP_N_NEXT, +} lgpe_ip4_lookup_next_t; + +#define foreach_lgpe_ip6_lookup_next \ + _(DROP, "error-drop") \ + _(LISP_CP_LOOKUP, "lisp-cp-lookup") + +typedef enum lgpe_ip6_lookup_next +{ +#define _(sym,str) LGPE_IP6_LOOKUP_NEXT_##sym, + foreach_lgpe_ip6_lookup_next +#undef _ + LGPE_IP6_LOOKUP_N_NEXT, +} lgpe_ip6_lookup_next_t; + +u8 *format_vnet_lisp_gpe_status (u8 * s, va_list * args); + +#endif /* included_vnet_lisp_gpe_h */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_adjacency.c b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_adjacency.c new file mode 100644 index 00000000..8c96a25c --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_adjacency.c @@ -0,0 +1,542 @@ +/* + * 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. + */ +/** + * @file + * @brief Common utility functions for IPv4, IPv6 and L2 LISP-GPE adjacencys. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * Memory pool of all adjacencies + */ +static lisp_gpe_adjacency_t *lisp_adj_pool; + +/** + * Hash table of all adjacencies. key:{nh, itf} + * We never have an all zeros address since the interfaces are multi-access, + * therefore there is no ambiguity between a v4 and v6 next-hop, so we don't + * need to add the protocol to the key. + */ +static +BVT (clib_bihash) + lisp_adj_db; + +#define LISP_ADJ_SET_KEY(_key, _itf, _nh) \ +{ \ + _key.key[0] = (_nh)->ip.v6.as_u64[0]; \ + _key.key[1] = (_nh)->ip.v6.as_u64[1]; \ + _key.key[2] = (_itf); \ +} + + static index_t lisp_adj_find (const ip_address_t * addr, u32 sw_if_index) +{ + BVT (clib_bihash_kv) kv; + + LISP_ADJ_SET_KEY (kv, sw_if_index, addr); + + if (BV (clib_bihash_search) (&lisp_adj_db, &kv, &kv) < 0) + { + return (INDEX_INVALID); + } + else + { + return (kv.value); + } +} + +static void +lisp_adj_insert (const ip_address_t * addr, u32 sw_if_index, index_t ai) +{ + BVT (clib_bihash_kv) kv; + + LISP_ADJ_SET_KEY (kv, sw_if_index, addr); + kv.value = ai; + + BV (clib_bihash_add_del) (&lisp_adj_db, &kv, 1); +} + +static void +lisp_adj_remove (const ip_address_t * addr, u32 sw_if_index) +{ + BVT (clib_bihash_kv) kv; + + LISP_ADJ_SET_KEY (kv, sw_if_index, addr); + + BV (clib_bihash_add_del) (&lisp_adj_db, &kv, 0); +} + +static lisp_gpe_adjacency_t * +lisp_gpe_adjacency_get_i (index_t lai) +{ + return (pool_elt_at_index (lisp_adj_pool, lai)); +} + +fib_forward_chain_type_t +lisp_gpe_adj_get_fib_chain_type (const lisp_gpe_adjacency_t * ladj) +{ + switch (ip_addr_version (&ladj->remote_rloc)) + { + case IP4: + return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4); + case IP6: + return (FIB_FORW_CHAIN_TYPE_UNICAST_IP6); + default: + ASSERT (0); + break; + } + return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4); +} + +static void +ip46_address_to_ip_address (const ip46_address_t * a, ip_address_t * b) +{ + if (ip46_address_is_ip4 (a)) + { + memset (b, 0, sizeof (*b)); + ip_address_set (b, &a->ip4, IP4); + } + else + { + ip_address_set (b, &a->ip6, IP6); + } +} + +/** + * @brief Stack the tunnel's midchain on the IP forwarding chain of the via + */ +static void +lisp_gpe_adj_stack_one (lisp_gpe_adjacency_t * ladj, adj_index_t ai) +{ + const lisp_gpe_tunnel_t *lgt; + dpo_id_t tmp = DPO_INVALID; + + lgt = lisp_gpe_tunnel_get (ladj->tunnel_index); + fib_entry_contribute_forwarding (lgt->fib_entry_index, + lisp_gpe_adj_get_fib_chain_type (ladj), + &tmp); + + if (DPO_LOAD_BALANCE == tmp.dpoi_type) + { + /* + * post LISP rewrite we will load-balance. However, the LISP encap + * is always the same for this adjacency/tunnel and hence the IP/UDP src,dst + * hash is always the same result too. So we do that hash now and + * stack on the choice. + * If the choice is an incomplete adj then we will need a poke when + * it becomes complete. This happens since the adj update walk propagates + * as far a recursive paths. + */ + const dpo_id_t *choice; + load_balance_t *lb; + int hash; + + lb = load_balance_get (tmp.dpoi_index); + + if (IP4 == ip_addr_version (&ladj->remote_rloc)) + { + hash = ip4_compute_flow_hash ((ip4_header_t *) adj_get_rewrite (ai), + lb->lb_hash_config); + } + else + { + hash = ip6_compute_flow_hash ((ip6_header_t *) adj_get_rewrite (ai), + lb->lb_hash_config); + } + + choice = + load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1); + dpo_copy (&tmp, choice); + } + + adj_nbr_midchain_stack (ai, &tmp); + dpo_reset (&tmp); +} + +/** + * @brief Call back when restacking all adjacencies on a GRE interface + */ +static adj_walk_rc_t +lisp_gpe_adj_walk_cb (adj_index_t ai, void *ctx) +{ + lisp_gpe_adjacency_t *ladj = ctx; + + lisp_gpe_adj_stack_one (ladj, ai); + + return (ADJ_WALK_RC_CONTINUE); +} + +static void +lisp_gpe_adj_stack (lisp_gpe_adjacency_t * ladj) +{ + fib_protocol_t nh_proto; + ip46_address_t nh; + + ip_address_to_46 (&ladj->remote_rloc, &nh, &nh_proto); + + /* + * walk all the adjacencies on th lisp interface and restack them + */ + adj_nbr_walk_nh (ladj->sw_if_index, + nh_proto, &nh, lisp_gpe_adj_walk_cb, ladj); +} + +static lisp_gpe_next_protocol_e +lisp_gpe_adj_proto_from_vnet_link_type (vnet_link_t linkt) +{ + switch (linkt) + { + case VNET_LINK_IP4: + return (LISP_GPE_NEXT_PROTO_IP4); + case VNET_LINK_IP6: + return (LISP_GPE_NEXT_PROTO_IP6); + case VNET_LINK_ETHERNET: + return (LISP_GPE_NEXT_PROTO_ETHERNET); + default: + ASSERT (0); + } + return (LISP_GPE_NEXT_PROTO_IP4); +} + +#define is_v4_packet(_h) ((*(u8*) _h) & 0xF0) == 0x40 + +static void +lisp_gpe_fixup (vlib_main_t * vm, ip_adjacency_t * adj, vlib_buffer_t * b) +{ + /* Fixup the checksum and len fields in the LISP tunnel encap + * that was applied at the midchain node */ + ip_udp_fixup_one (vm, b, is_v4_packet (vlib_buffer_get_current (b))); +} + +/** + * @brief The LISP-GPE interface registered function to update, i.e. + * provide an rewrite string for, an adjacency. + */ +void +lisp_gpe_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai) +{ + const lisp_gpe_tunnel_t *lgt; + lisp_gpe_adjacency_t *ladj; + ip_adjacency_t *adj; + ip_address_t rloc; + vnet_link_t linkt; + index_t lai; + + adj = adj_get (ai); + ip46_address_to_ip_address (&adj->sub_type.nbr.next_hop, &rloc); + + /* + * find an existing or create a new adj + */ + lai = lisp_adj_find (&rloc, sw_if_index); + + ASSERT (INDEX_INVALID != lai); + + ladj = pool_elt_at_index (lisp_adj_pool, lai); + lgt = lisp_gpe_tunnel_get (ladj->tunnel_index); + linkt = adj_get_link_type (ai); + + adj_nbr_midchain_update_rewrite + (ai, lisp_gpe_fixup, + (VNET_LINK_ETHERNET == linkt ? + ADJ_MIDCHAIN_FLAG_NO_COUNT : + ADJ_MIDCHAIN_FLAG_NONE), + lisp_gpe_tunnel_build_rewrite + (lgt, ladj, lisp_gpe_adj_proto_from_vnet_link_type (linkt))); + + lisp_gpe_adj_stack_one (ladj, ai); +} + +u8 * +lisp_gpe_build_rewrite (vnet_main_t * vnm, + u32 sw_if_index, + vnet_link_t link_type, const void *dst_address) +{ + ASSERT (0); + return (NULL); +} + +index_t +lisp_gpe_adjacency_find_or_create_and_lock (const locator_pair_t * pair, + u32 overlay_table_id, u32 vni) +{ + const lisp_gpe_sub_interface_t *l3s; + const lisp_gpe_tunnel_t *lgt; + lisp_gpe_adjacency_t *ladj; + index_t lai, l3si; + + /* + * first find the L3 sub-interface that corresponds to the loacl-rloc and vni + */ + l3si = lisp_gpe_sub_interface_find_or_create_and_lock (&pair->lcl_loc, + overlay_table_id, + vni); + l3s = lisp_gpe_sub_interface_get (l3si); + + /* + * find an existing or create a new adj + */ + lai = lisp_adj_find (&pair->rmt_loc, l3s->sw_if_index); + + if (INDEX_INVALID == lai) + { + + pool_get (lisp_adj_pool, ladj); + memset (ladj, 0, sizeof (*ladj)); + lai = (ladj - lisp_adj_pool); + + ip_address_copy (&ladj->remote_rloc, &pair->rmt_loc); + ladj->vni = vni; + /* transfer the lock to the adj */ + ladj->lisp_l3_sub_index = l3si; + ladj->sw_if_index = l3s->sw_if_index; + + /* if vni is non-default */ + if (ladj->vni) + ladj->flags = LISP_GPE_FLAGS_I; + + /* work in lisp-gpe not legacy mode */ + ladj->flags |= LISP_GPE_FLAGS_P; + + /* + * find the tunnel that will provide the underlying transport + * and hence the rewrite. + * The RLOC FIB index is default table - always. + */ + ladj->tunnel_index = lisp_gpe_tunnel_find_or_create_and_lock (pair, 0); + + lgt = lisp_gpe_tunnel_get (ladj->tunnel_index); + + /* + * become of child of the RLOC FIB entry so we are updated when + * its reachability changes, allowing us to re-stack the midcahins + */ + ladj->fib_entry_child_index = fib_entry_child_add (lgt->fib_entry_index, + FIB_NODE_TYPE_LISP_ADJ, + lai); + + lisp_adj_insert (&ladj->remote_rloc, ladj->sw_if_index, lai); + } + else + { + /* unlock the interface from the find. */ + lisp_gpe_sub_interface_unlock (l3si); + ladj = lisp_gpe_adjacency_get_i (lai); + } + + ladj->locks++; + + return (lai); +} + +/** + * @brief Get a pointer to a tunnel from a pointer to a FIB node + */ +static lisp_gpe_adjacency_t * +lisp_gpe_adjacency_from_fib_node (const fib_node_t * node) +{ + return ((lisp_gpe_adjacency_t *) + ((char *) node - + STRUCT_OFFSET_OF (lisp_gpe_adjacency_t, fib_node))); +} + +static void +lisp_gpe_adjacency_last_lock_gone (lisp_gpe_adjacency_t * ladj) +{ + const lisp_gpe_tunnel_t *lgt; + + /* + * no children so we are not counting locks. no-op. + * at least not counting + */ + lisp_adj_remove (&ladj->remote_rloc, ladj->sw_if_index); + + /* + * unlock the resources this adj holds + */ + lgt = lisp_gpe_tunnel_get (ladj->tunnel_index); + + fib_entry_child_remove (lgt->fib_entry_index, ladj->fib_entry_child_index); + + lisp_gpe_tunnel_unlock (ladj->tunnel_index); + lisp_gpe_sub_interface_unlock (ladj->lisp_l3_sub_index); + + pool_put (lisp_adj_pool, ladj); +} + +void +lisp_gpe_adjacency_unlock (index_t lai) +{ + lisp_gpe_adjacency_t *ladj; + + ladj = lisp_gpe_adjacency_get_i (lai); + + ladj->locks--; + + if (0 == ladj->locks) + { + lisp_gpe_adjacency_last_lock_gone (ladj); + } +} + +const lisp_gpe_adjacency_t * +lisp_gpe_adjacency_get (index_t lai) +{ + return (lisp_gpe_adjacency_get_i (lai)); +} + + +/** + * @brief LISP GPE tunnel back walk + * + * The FIB entry through which this tunnel resolves has been updated. + * re-stack the midchain on the new forwarding. + */ +static fib_node_back_walk_rc_t +lisp_gpe_adjacency_back_walk (fib_node_t * node, + fib_node_back_walk_ctx_t * ctx) +{ + lisp_gpe_adj_stack (lisp_gpe_adjacency_from_fib_node (node)); + + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +static fib_node_t * +lisp_gpe_adjacency_get_fib_node (fib_node_index_t index) +{ + lisp_gpe_adjacency_t *ladj; + + ladj = pool_elt_at_index (lisp_adj_pool, index); + return (&ladj->fib_node); +} + +static void +lisp_gpe_adjacency_last_fib_lock_gone (fib_node_t * node) +{ + lisp_gpe_adjacency_last_lock_gone (lisp_gpe_adjacency_from_fib_node (node)); +} + +const static fib_node_vft_t lisp_gpe_tuennel_vft = { + .fnv_get = lisp_gpe_adjacency_get_fib_node, + .fnv_back_walk = lisp_gpe_adjacency_back_walk, + .fnv_last_lock = lisp_gpe_adjacency_last_fib_lock_gone, +}; + +u8 * +format_lisp_gpe_adjacency (u8 * s, va_list * args) +{ + lisp_gpe_adjacency_t *ladj = va_arg (*args, lisp_gpe_adjacency_t *); + lisp_gpe_adjacency_format_flags_t flags = + va_arg (*args, lisp_gpe_adjacency_format_flags_t); + + if (flags & LISP_GPE_ADJ_FORMAT_FLAG_DETAIL) + { + s = + format (s, "index %d locks:%d\n", ladj - lisp_adj_pool, ladj->locks); + } + + s = format (s, " vni: %d,", ladj->vni); + s = format (s, " remote-RLOC: %U,", format_ip_address, &ladj->remote_rloc); + + if (flags & LISP_GPE_ADJ_FORMAT_FLAG_DETAIL) + { + s = format (s, " %U\n", + format_lisp_gpe_sub_interface, + lisp_gpe_sub_interface_get (ladj->lisp_l3_sub_index)); + s = format (s, " %U\n", + format_lisp_gpe_tunnel, + lisp_gpe_tunnel_get (ladj->tunnel_index)); + } + else + { + s = format (s, " LISP L3 sub-interface index: %d,", + ladj->lisp_l3_sub_index); + s = format (s, " LISP tunnel index: %d", ladj->tunnel_index); + } + + + return (s); +} + +static clib_error_t * +lisp_gpe_adjacency_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + lisp_gpe_adjacency_t *ladj; + index_t index; + + if (pool_elts (lisp_adj_pool) == 0) + vlib_cli_output (vm, "No lisp-gpe Adjacencies"); + + if (unformat (input, "%d", &index)) + { + ladj = lisp_gpe_adjacency_get_i (index); + vlib_cli_output (vm, "%U", format_lisp_gpe_adjacency, ladj, + LISP_GPE_ADJ_FORMAT_FLAG_DETAIL); + } + else + { + /* *INDENT-OFF* */ + pool_foreach (ladj, lisp_adj_pool, + ({ + vlib_cli_output (vm, "[%d] %U\n", + ladj - lisp_adj_pool, + format_lisp_gpe_adjacency, ladj, + LISP_GPE_ADJ_FORMAT_FLAG_NONE); + })); + /* *INDENT-ON* */ + } + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_lisp_gpe_tunnel_command, static) = +{ + .path = "show lisp gpe adjacency", + .function = lisp_gpe_adjacency_show, +}; +/* *INDENT-ON* */ + +#define LISP_ADJ_NBR_DEFAULT_HASH_NUM_BUCKETS (256) +#define LISP_ADJ_NBR_DEFAULT_HASH_MEMORY_SIZE (1<<20) + +static clib_error_t * +lisp_gpe_adj_module_init (vlib_main_t * vm) +{ + BV (clib_bihash_init) (&lisp_adj_db, + "Adjacency Neighbour table", + LISP_ADJ_NBR_DEFAULT_HASH_NUM_BUCKETS, + LISP_ADJ_NBR_DEFAULT_HASH_MEMORY_SIZE); + + fib_node_register_type (FIB_NODE_TYPE_LISP_ADJ, &lisp_gpe_tuennel_vft); + return (NULL); +} + +VLIB_INIT_FUNCTION (lisp_gpe_adj_module_init) +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_adjacency.h b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_adjacency.h new file mode 100644 index 00000000..adc3acae --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_adjacency.h @@ -0,0 +1,136 @@ +/* + * 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. + */ +/** + * @file + * @brief Common utility functions for IPv4, IPv6 and L2 LISP-GPE adjacencys. + * + */ + +#ifndef LISP_GPE_ADJACENCY_H__ +#define LISP_GPE_ADJACENCY_H__ + +#include +#include + +/** + * @brief A LISP GPE Adjacency. + * + * A adjacency represents peer on an L3 sub-interface to which to send traffic. + * adjacencies are thus present in the EID space. + * The peer is identified by the key:{remote-rloc, sub-interface}, which is + * equivalent to the usal adjacency key {next-hop, interface}. So curiously + * the rloc address from the underlay is used as a next hop address in the overlay + * This is OK because: + * 1 - the RLOC is unique in the underlay AND there is only one underlay VRF per + * overlay + * 2 - the RLOC may overlap with an address in the overlay, but we do not create + * an adj-fib (i.e. a route in the overlay FIB for the rloc) + * + * + */ +typedef struct lisp_gpe_adjacency_t_ +{ + /** + * The LISP adj is a part of the FIB control plane graph. + */ + fib_node_t fib_node; + + /** + * remote RLOC. The adjacency's next-hop + */ + ip_address_t remote_rloc; + + /** + * The VNI. Used in combination with the local-rloc to get the sub-interface + */ + u32 vni; + + /** + * The number of locks/reference counts on the adjacency. + */ + u32 locks; + + /** + * The index of the LISP L3 subinterface + */ + u32 lisp_l3_sub_index; + + /** + * The SW IF index of the sub-interface this adjacency uses. + * Cached for convenience from the LISP L3 sub-interface + */ + u32 sw_if_index; + + /** + * The index of the LISP GPE tunnel that provides the transport + * in the underlay. + */ + u32 tunnel_index; + + /** + * This adjacency is a child of the FIB entry to reach the RLOC. + * This is so when the reachability of that RLOC changes, we can restack + * the FIB adjacnecies. + */ + u32 fib_entry_child_index; + + /** + * LISP header fields in HOST byte order + */ + u8 flags; + u8 ver_res; + u8 res; + u8 next_protocol; + +} lisp_gpe_adjacency_t; + +extern index_t lisp_gpe_adjacency_find_or_create_and_lock (const + locator_pair_t * + pair, + u32 rloc_fib_index, + u32 vni); + +extern void lisp_gpe_adjacency_unlock (index_t l3si); + +extern const lisp_gpe_adjacency_t *lisp_gpe_adjacency_get (index_t l3si); + +extern void lisp_gpe_update_adjacency (vnet_main_t * vnm, + u32 sw_if_index, adj_index_t ai); +extern u8 *lisp_gpe_build_rewrite (vnet_main_t * vnm, + u32 sw_if_index, + vnet_link_t link_type, + const void *dst_address); + + +/** + * @brief Flags for displaying the adjacency + */ +typedef enum lisp_gpe_adjacency_format_flags_t_ +{ + LISP_GPE_ADJ_FORMAT_FLAG_NONE, + LISP_GPE_ADJ_FORMAT_FLAG_DETAIL, +} lisp_gpe_adjacency_format_flags_t; + +extern u8 *format_lisp_gpe_adjacency (u8 * s, va_list * args); + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_error.def b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_error.def new file mode 100644 index 00000000..415fada7 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_error.def @@ -0,0 +1,18 @@ +/* + * 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") diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_fwd_entry.c b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_fwd_entry.c new file mode 100644 index 00000000..26a93a87 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_fwd_entry.c @@ -0,0 +1,1053 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Add route to IP4 or IP6 Destination FIB. + * + * Add a route to the destination FIB that results in the lookup + * in the SRC FIB. The SRC FIB is created is it does not yet exist. + * + * @param[in] dst_table_id Destination FIB Table-ID + * @param[in] dst_prefix Destination IP prefix. + * + * @return src_fib_index The index/ID of the SRC FIB created. + */ +static u32 +ip_dst_fib_add_route (u32 dst_fib_index, const ip_prefix_t * dst_prefix) +{ + fib_node_index_t src_fib_index; + fib_prefix_t dst_fib_prefix; + fib_node_index_t dst_fei; + + ASSERT (NULL != dst_prefix); + + ip_prefix_to_fib_prefix (dst_prefix, &dst_fib_prefix); + + /* + * lookup the destination prefix in the VRF table and retrieve the + * LISP associated data + */ + dst_fei = fib_table_lookup_exact_match (dst_fib_index, &dst_fib_prefix); + + /* + * If the FIB entry is not present, or not LISP sourced, add it + */ + if (dst_fei == FIB_NODE_INDEX_INVALID || + NULL == fib_entry_get_source_data (dst_fei, FIB_SOURCE_LISP)) + { + dpo_id_t src_lkup_dpo = DPO_INVALID; + + /* create a new src FIB. */ + src_fib_index = + fib_table_create_and_lock (dst_fib_prefix.fp_proto, + "LISP-src for [%d,%U]", + dst_fib_index, + format_fib_prefix, &dst_fib_prefix); + /* + * add src fib default route + */ + fib_prefix_t prefix = { + .fp_proto = dst_fib_prefix.fp_proto, + }; + fib_table_entry_special_dpo_add (src_fib_index, &prefix, + FIB_SOURCE_LISP, + FIB_ENTRY_FLAG_EXCLUSIVE, + lisp_cp_dpo_get (fib_proto_to_dpo + (dst_fib_prefix.fp_proto))); + /* + * create a data-path object to perform the source address lookup + * in the SRC FIB + */ + lookup_dpo_add_or_lock_w_fib_index (src_fib_index, + (ip_prefix_version (dst_prefix) == + IP6 ? DPO_PROTO_IP6 : + DPO_PROTO_IP4), + LOOKUP_INPUT_SRC_ADDR, + LOOKUP_TABLE_FROM_CONFIG, + &src_lkup_dpo); + + /* + * add the entry to the destination FIB that uses the lookup DPO + */ + dst_fei = fib_table_entry_special_dpo_add (dst_fib_index, + &dst_fib_prefix, + FIB_SOURCE_LISP, + FIB_ENTRY_FLAG_EXCLUSIVE, + &src_lkup_dpo); + + /* + * the DPO is locked by the FIB entry, and we have no further + * need for it. + */ + dpo_unlock (&src_lkup_dpo); + + /* + * save the SRC FIB index on the entry so we can retrieve it for + * subsequent routes. + */ + fib_entry_set_source_data (dst_fei, FIB_SOURCE_LISP, &src_fib_index); + } + else + { + /* + * destination FIB entry already present + */ + src_fib_index = *(u32 *) fib_entry_get_source_data (dst_fei, + FIB_SOURCE_LISP); + } + + return (src_fib_index); +} + +/** + * @brief Del route to IP4 or IP6 SD FIB. + * + * Remove routes from both destination and source FIBs. + * + * @param[in] src_fib_index The index/ID of the SRC FIB + * @param[in] src_prefix Source IP prefix. + * @param[in] dst_fib_index The index/ID of the DST FIB + * @param[in] dst_prefix Destination IP prefix. + */ +static void +ip_src_dst_fib_del_route (u32 src_fib_index, + const ip_prefix_t * src_prefix, + u32 dst_fib_index, const ip_prefix_t * dst_prefix) +{ + fib_prefix_t dst_fib_prefix, src_fib_prefix; + u8 have_default = 0; + u32 n_entries; + + ASSERT (NULL != dst_prefix); + ASSERT (NULL != src_prefix); + + ip_prefix_to_fib_prefix (dst_prefix, &dst_fib_prefix); + ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix); + + fib_table_entry_delete (src_fib_index, &src_fib_prefix, FIB_SOURCE_LISP); + + /* check if only default left or empty */ + fib_prefix_t default_pref = { + .fp_proto = dst_fib_prefix.fp_proto + }; + + if (fib_table_lookup_exact_match (src_fib_index, + &default_pref) != FIB_NODE_INDEX_INVALID) + have_default = 1; + + n_entries = fib_table_get_num_entries (src_fib_index, + src_fib_prefix.fp_proto, + FIB_SOURCE_LISP); + if (n_entries == 0 || (have_default && n_entries == 1)) + { + /* + * remove src FIB default route + */ + if (have_default) + fib_table_entry_special_remove (src_fib_index, &default_pref, + FIB_SOURCE_LISP); + + /* + * there's nothing left now, unlock the source FIB and the + * destination route + */ + fib_table_entry_special_remove (dst_fib_index, + &dst_fib_prefix, FIB_SOURCE_LISP); + fib_table_unlock (src_fib_index, src_fib_prefix.fp_proto); + } +} + +/** + * @brief Add route to IP4 or IP6 SRC FIB. + * + * Adds a route to in the LISP SRC FIB with the result of the route + * being the DPO passed. + * + * @param[in] src_fib_index The index/ID of the SRC FIB + * @param[in] src_prefix Source IP prefix. + * @param[in] src_dpo The DPO the route will link to. + */ +static void +ip_src_fib_add_route_w_dpo (u32 src_fib_index, + const ip_prefix_t * src_prefix, + const dpo_id_t * src_dpo) +{ + fib_prefix_t src_fib_prefix; + + ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix); + + /* + * add the entry into the source fib. + */ + fib_node_index_t src_fei; + + src_fei = fib_table_lookup_exact_match (src_fib_index, &src_fib_prefix); + + if (FIB_NODE_INDEX_INVALID == src_fei || + !fib_entry_is_sourced (src_fei, FIB_SOURCE_LISP)) + { + fib_table_entry_special_dpo_add (src_fib_index, + &src_fib_prefix, + FIB_SOURCE_LISP, + FIB_ENTRY_FLAG_EXCLUSIVE, src_dpo); + } +} + +static fib_route_path_t * +lisp_gpe_mk_fib_paths (const lisp_fwd_path_t * paths) +{ + const lisp_gpe_adjacency_t *ladj; + fib_route_path_t *rpaths = NULL; + u8 best_priority; + u32 ii; + + vec_validate (rpaths, vec_len (paths) - 1); + + best_priority = paths[0].priority; + + vec_foreach_index (ii, paths) + { + if (paths[0].priority != best_priority) + break; + + ladj = lisp_gpe_adjacency_get (paths[ii].lisp_adj); + + ip_address_to_46 (&ladj->remote_rloc, + &rpaths[ii].frp_addr, &rpaths[ii].frp_proto); + + rpaths[ii].frp_sw_if_index = ladj->sw_if_index; + rpaths[ii].frp_weight = (paths[ii].weight ? paths[ii].weight : 1); + } + + ASSERT (0 != vec_len (rpaths)); + + return (rpaths); +} + +/** + * @brief Add route to IP4 or IP6 SRC FIB. + * + * Adds a route to in the LISP SRC FIB for the tunnel. + * + * @param[in] src_fib_index The index/ID of the SRC FIB + * @param[in] src_prefix Source IP prefix. + * @param[in] paths The paths from which to construct the + * load balance + */ +static void +ip_src_fib_add_route (u32 src_fib_index, + const ip_prefix_t * src_prefix, + const lisp_fwd_path_t * paths) +{ + fib_prefix_t src_fib_prefix; + fib_route_path_t *rpaths; + + ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix); + + rpaths = lisp_gpe_mk_fib_paths (paths); + + fib_table_entry_update (src_fib_index, + &src_fib_prefix, + FIB_SOURCE_LISP, FIB_ENTRY_FLAG_NONE, rpaths); + vec_free (rpaths); +} + + +static void +create_fib_entries (lisp_gpe_fwd_entry_t * lfe) +{ + dpo_proto_t dproto; + + dproto = (ip_prefix_version (&lfe->key->rmt.ippref) == IP4 ? + DPO_PROTO_IP4 : DPO_PROTO_IP6); + + lfe->src_fib_index = ip_dst_fib_add_route (lfe->eid_fib_index, + &lfe->key->rmt.ippref); + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type) + { + dpo_id_t dpo = DPO_INVALID; + + switch (lfe->action) + { + case LISP_NO_ACTION: + /* TODO update timers? */ + case LISP_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 LISP_SEND_MAP_REQUEST: + /* insert tunnel that always sends map-request */ + dpo_copy (&dpo, lisp_cp_dpo_get (dproto)); + break; + case LISP_DROP: + /* for drop fwd entries, just add route, no need to add encap tunnel */ + dpo_copy (&dpo, drop_dpo_get (dproto)); + break; + } + ip_src_fib_add_route_w_dpo (lfe->src_fib_index, + &lfe->key->lcl.ippref, &dpo); + dpo_reset (&dpo); + } + else + { + ip_src_fib_add_route (lfe->src_fib_index, + &lfe->key->lcl.ippref, lfe->paths); + } +} + +static void +delete_fib_entries (lisp_gpe_fwd_entry_t * lfe) +{ + ip_src_dst_fib_del_route (lfe->src_fib_index, + &lfe->key->lcl.ippref, + lfe->eid_fib_index, &lfe->key->rmt.ippref); +} + +static void +gid_to_dp_address (gid_address_t * g, dp_address_t * d) +{ + switch (gid_address_type (g)) + { + case GID_ADDR_IP_PREFIX: + case GID_ADDR_SRC_DST: + ip_prefix_copy (&d->ippref, &gid_address_ippref (g)); + d->type = FID_ADDR_IP_PREF; + break; + case GID_ADDR_MAC: + default: + mac_copy (&d->mac, &gid_address_mac (g)); + d->type = FID_ADDR_MAC; + break; + } +} + +static lisp_gpe_fwd_entry_t * +find_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a, + lisp_gpe_fwd_entry_key_t * key) +{ + uword *p; + + memset (key, 0, sizeof (*key)); + + if (GID_ADDR_IP_PREFIX == gid_address_type (&a->rmt_eid)) + { + /* + * the ip version of the source is not set to ip6 when the + * source is all zeros. force it. + */ + ip_prefix_version (&gid_address_ippref (&a->lcl_eid)) = + ip_prefix_version (&gid_address_ippref (&a->rmt_eid)); + } + + gid_to_dp_address (&a->rmt_eid, &key->rmt); + gid_to_dp_address (&a->lcl_eid, &key->lcl); + key->vni = a->vni; + + p = hash_get_mem (lgm->lisp_gpe_fwd_entries, key); + + if (NULL != p) + { + return (pool_elt_at_index (lgm->lisp_fwd_entry_pool, p[0])); + } + return (NULL); +} + +static int +lisp_gpe_fwd_entry_path_sort (void *a1, void *a2) +{ + lisp_fwd_path_t *p1 = a1, *p2 = a2; + + return (p1->priority - p2->priority); +} + +static void +lisp_gpe_fwd_entry_mk_paths (lisp_gpe_fwd_entry_t * lfe, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_fwd_path_t *path; + u32 index; + + vec_validate (lfe->paths, vec_len (a->locator_pairs) - 1); + + vec_foreach_index (index, a->locator_pairs) + { + path = &lfe->paths[index]; + + path->priority = a->locator_pairs[index].priority; + path->weight = a->locator_pairs[index].weight; + + path->lisp_adj = + lisp_gpe_adjacency_find_or_create_and_lock (&a->locator_pairs + [index], + a->dp_table, lfe->key->vni); + } + vec_sort_with_function (lfe->paths, lisp_gpe_fwd_entry_path_sort); +} + +/** + * @brief Add/Delete LISP IP forwarding entry. + * + * creation of forwarding entries for IP LISP overlay: + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters for building the forwarding entry. + * + * @return 0 on success. + */ +static int +add_ip_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_gpe_fwd_entry_key_t key; + lisp_gpe_fwd_entry_t *lfe; + fib_protocol_t fproto; + + lfe = find_fwd_entry (lgm, a, &key); + + if (NULL != lfe) + /* don't support updates */ + return VNET_API_ERROR_INVALID_VALUE; + + pool_get (lgm->lisp_fwd_entry_pool, lfe); + memset (lfe, 0, sizeof (*lfe)); + lfe->key = clib_mem_alloc (sizeof (key)); + memcpy (lfe->key, &key, sizeof (key)); + + hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key, + lfe - lgm->lisp_fwd_entry_pool); + + fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ? + FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6); + + lfe->type = (a->is_negative ? + LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE : + LISP_GPE_FWD_ENTRY_TYPE_NORMAL); + lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni); + lfe->eid_table_id = a->table_id; + lfe->eid_fib_index = fib_table_find_or_create_and_lock (fproto, + lfe->eid_table_id); + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + lisp_gpe_fwd_entry_mk_paths (lfe, a); + } + + create_fib_entries (lfe); + + return (0); +} + +static void +del_ip_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe) +{ + lisp_fwd_path_t *path; + fib_protocol_t fproto; + + vec_foreach (path, lfe->paths) + { + lisp_gpe_adjacency_unlock (path->lisp_adj); + } + + delete_fib_entries (lfe); + + fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ? + FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6); + fib_table_unlock (lfe->eid_fib_index, fproto); + + hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key); + clib_mem_free (lfe->key); + pool_put (lgm->lisp_fwd_entry_pool, lfe); +} + +/** + * @brief Add/Delete LISP IP forwarding entry. + * + * removal of forwarding entries for IP LISP overlay: + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters for building the forwarding entry. + * + * @return 0 on success. + */ +static int +del_ip_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_gpe_fwd_entry_key_t key; + lisp_gpe_fwd_entry_t *lfe; + + lfe = find_fwd_entry (lgm, a, &key); + + if (NULL == lfe) + /* no such entry */ + return VNET_API_ERROR_INVALID_VALUE; + + del_ip_fwd_entry_i (lgm, lfe); + + return (0); +} + +static void +make_mac_fib_key (BVT (clib_bihash_kv) * kv, u16 bd_index, u8 src_mac[6], + u8 dst_mac[6]) +{ + kv->key[0] = (((u64) bd_index) << 48) | mac_to_u64 (dst_mac); + kv->key[1] = mac_to_u64 (src_mac); + kv->key[2] = 0; +} + +/** + * @brief Lookup L2 SD FIB entry + * + * Does a vni + dest + source lookup in the L2 LISP FIB. If the lookup fails + * it tries a second time with source set to 0 (i.e., a simple dest lookup). + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] bd_index Bridge domain index. + * @param[in] src_mac Source mac address. + * @param[in] dst_mac Destination mac address. + * + * @return index of mapping matching the lookup key. + */ +index_t +lisp_l2_fib_lookup (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6], + u8 dst_mac[6]) +{ + int rv; + BVT (clib_bihash_kv) kv, value; + + make_mac_fib_key (&kv, bd_index, src_mac, dst_mac); + rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value); + + /* no match, try with src 0, catch all for dst */ + if (rv != 0) + { + kv.key[1] = 0; + rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value); + if (rv == 0) + return value.value; + } + else + return value.value; + + return lisp_gpe_main.l2_lb_cp_lkup.dpoi_index; +} + +/** + * @brief Add/del L2 SD FIB entry + * + * Inserts value in L2 FIB keyed by vni + dest + source. If entry is + * overwritten the associated value is returned. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] bd_index Bridge domain index. + * @param[in] src_mac Source mac address. + * @param[in] dst_mac Destination mac address. + * @param[in] val Value to add. + * @param[in] is_add Add/del flag. + * + * @return ~0 or value of overwritten entry. + */ +static u32 +lisp_l2_fib_add_del_entry (u16 bd_index, u8 src_mac[6], + u8 dst_mac[6], const dpo_id_t * dpo, u8 is_add) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + BVT (clib_bihash_kv) kv, value; + u32 old_val = ~0; + + make_mac_fib_key (&kv, bd_index, src_mac, dst_mac); + + if (BV (clib_bihash_search) (&lgm->l2_fib, &kv, &value) == 0) + old_val = value.value; + + if (!is_add) + BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 0 /* is_add */ ); + else + { + kv.value = dpo->dpoi_index; + BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 1 /* is_add */ ); + } + return old_val; +} + +#define L2_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024) +#define L2_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20) + +static void +l2_fib_init (lisp_gpe_main_t * lgm) +{ + index_t lbi; + + BV (clib_bihash_init) (&lgm->l2_fib, "l2 fib", + 1 << max_log2 (L2_FIB_DEFAULT_HASH_NUM_BUCKETS), + L2_FIB_DEFAULT_HASH_MEMORY_SIZE); + + /* + * the result from a 'miss' in a L2 Table + */ + lbi = load_balance_create (1, DPO_PROTO_ETHERNET, 0); + load_balance_set_bucket (lbi, 0, lisp_cp_dpo_get (DPO_PROTO_ETHERNET)); + + dpo_set (&lgm->l2_lb_cp_lkup, DPO_LOAD_BALANCE, DPO_PROTO_ETHERNET, lbi); +} + +static void +del_l2_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe) +{ + lisp_fwd_path_t *path; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + vec_foreach (path, lfe->paths) + { + lisp_gpe_adjacency_unlock (path->lisp_adj); + } + fib_path_list_child_remove (lfe->l2.path_list_index, + lfe->l2.child_index); + } + + lisp_l2_fib_add_del_entry (lfe->l2.eid_bd_index, + fid_addr_mac (&lfe->key->lcl), + fid_addr_mac (&lfe->key->rmt), NULL, 0); + + hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key); + clib_mem_free (lfe->key); + pool_put (lgm->lisp_fwd_entry_pool, lfe); +} + +/** + * @brief Delete LISP L2 forwarding entry. + * + * Coordinates the removal of forwarding entries for L2 LISP overlay: + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters for building the forwarding entry. + * + * @return 0 on success. + */ +static int +del_l2_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_gpe_fwd_entry_key_t key; + lisp_gpe_fwd_entry_t *lfe; + + lfe = find_fwd_entry (lgm, a, &key); + + if (NULL == lfe) + return VNET_API_ERROR_INVALID_VALUE; + + del_l2_fwd_entry_i (lgm, lfe); + + return (0); +} + +/** + * @brief Construct and insert the forwarding information used by a L2 entry + */ +static void +lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_t * lfe) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + dpo_id_t dpo = DPO_INVALID; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + fib_path_list_contribute_forwarding (lfe->l2.path_list_index, + FIB_FORW_CHAIN_TYPE_ETHERNET, + &lfe->l2.dpo); + dpo_copy (&dpo, &lfe->l2.dpo); + } + else + { + dpo_copy (&dpo, &lgm->l2_lb_cp_lkup); + } + + /* add entry to l2 lisp fib */ + lisp_l2_fib_add_del_entry (lfe->l2.eid_bd_index, + fid_addr_mac (&lfe->key->lcl), + fid_addr_mac (&lfe->key->rmt), &dpo, 1); + + dpo_reset (&dpo); +} + +/** + * @brief Add LISP L2 forwarding entry. + * + * Coordinates the creation of forwarding entries for L2 LISP overlay: + * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters for building the forwarding entry. + * + * @return 0 on success. + */ +static int +add_l2_fwd_entry (lisp_gpe_main_t * lgm, + vnet_lisp_gpe_add_del_fwd_entry_args_t * a) +{ + lisp_gpe_fwd_entry_key_t key; + bd_main_t *bdm = &bd_main; + lisp_gpe_fwd_entry_t *lfe; + uword *bd_indexp; + + bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id); + if (!bd_indexp) + { + clib_warning ("bridge domain %d doesn't exist", a->bd_id); + return -1; + } + + lfe = find_fwd_entry (lgm, a, &key); + + if (NULL != lfe) + /* don't support updates */ + return VNET_API_ERROR_INVALID_VALUE; + + pool_get (lgm->lisp_fwd_entry_pool, lfe); + memset (lfe, 0, sizeof (*lfe)); + lfe->key = clib_mem_alloc (sizeof (key)); + memcpy (lfe->key, &key, sizeof (key)); + + hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key, + lfe - lgm->lisp_fwd_entry_pool); + + lfe->type = (a->is_negative ? + LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE : + LISP_GPE_FWD_ENTRY_TYPE_NORMAL); + lfe->l2.eid_bd_id = a->bd_id; + lfe->l2.eid_bd_index = bd_indexp[0]; + lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni); + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + fib_route_path_t *rpaths; + + /* + * Make the sorted array of LISP paths with their resp. adjacency + */ + lisp_gpe_fwd_entry_mk_paths (lfe, a); + + /* + * From the LISP paths, construct a FIB path list that will + * contribute a load-balance. + */ + rpaths = lisp_gpe_mk_fib_paths (lfe->paths); + + lfe->l2.path_list_index = + fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, rpaths); + + /* + * become a child of the path-list so we receive updates when + * its forwarding state changes. this includes an implicit lock. + */ + lfe->l2.child_index = + fib_path_list_child_add (lfe->l2.path_list_index, + FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, + lfe - lgm->lisp_fwd_entry_pool); + } + else + { + lfe->action = a->action; + } + + lisp_gpe_l2_update_fwding (lfe); + + return 0; +} + +/** + * @brief conver from the embedded fib_node_t struct to the LSIP entry + */ +static lisp_gpe_fwd_entry_t * +lisp_gpe_fwd_entry_from_fib_node (fib_node_t * node) +{ + return ((lisp_gpe_fwd_entry_t *) (((char *) node) - + STRUCT_OFFSET_OF (lisp_gpe_fwd_entry_t, + node))); +} + +/** + * @brief Function invoked during a backwalk of the FIB graph + */ +static fib_node_back_walk_rc_t +lisp_gpe_fib_node_back_walk (fib_node_t * node, + fib_node_back_walk_ctx_t * ctx) +{ + lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_from_fib_node (node)); + + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/** + * @brief Get a fib_node_t struct from the index of a LISP fwd entry + */ +static fib_node_t * +lisp_gpe_fwd_entry_get_fib_node (fib_node_index_t index) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + lisp_gpe_fwd_entry_t *lfe; + + lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, index); + + return (&(lfe->node)); +} + +/** + * @brief An indication from the graph that the last lock has gone + */ +static void +lisp_gpe_fwd_entry_fib_node_last_lock_gone (fib_node_t * node) +{ + /* We don't manage the locks of the LISP objects via the graph, since + * this object has no children. so this is a no-op. */ +} + +/** + * @brief Virtual function table to register with FIB for the LISP type + */ +const static fib_node_vft_t lisp_fwd_vft = { + .fnv_get = lisp_gpe_fwd_entry_get_fib_node, + .fnv_last_lock = lisp_gpe_fwd_entry_fib_node_last_lock_gone, + .fnv_back_walk = lisp_gpe_fib_node_back_walk, +}; + +/** + * @brief Forwarding entry create/remove dispatcher. + * + * Calls l2 or l3 forwarding entry add/del function based on input data. + * + * @param[in] a Forwarding entry parameters. + * @param[out] hw_if_indexp NOT USED + * + * @return 0 on success. + */ +int +vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, + u32 * hw_if_indexp) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + u8 type; + + if (vnet_lisp_gpe_enable_disable_status () == 0) + { + clib_warning ("LISP is disabled!"); + return VNET_API_ERROR_LISP_DISABLED; + } + + type = gid_address_type (&a->rmt_eid); + switch (type) + { + case GID_ADDR_IP_PREFIX: + if (a->is_add) + return add_ip_fwd_entry (lgm, a); + else + return del_ip_fwd_entry (lgm, a); + break; + case GID_ADDR_MAC: + if (a->is_add) + return add_l2_fwd_entry (lgm, a); + else + return del_l2_fwd_entry (lgm, a); + default: + clib_warning ("Forwarding entries for type %d not supported!", type); + return -1; + } +} + +/** + * @brief Flush all the forwrding entries + */ +void +vnet_lisp_gpe_fwd_entry_flush (void) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + lisp_gpe_fwd_entry_t *lfe; + + /* *INDENT-OFF* */ + pool_foreach (lfe, lgm->lisp_fwd_entry_pool, + ({ + switch (fid_addr_type(&lfe->key->rmt)) + { + case FID_ADDR_MAC: + del_l2_fwd_entry_i (lgm, lfe); + break; + case FID_ADDR_IP_PREF: + del_ip_fwd_entry_i (lgm, lfe); + break; + } + })); + /* *INDENT-ON* */ +} + +static u8 * +format_lisp_fwd_path (u8 * s, va_list ap) +{ + lisp_fwd_path_t *lfp = va_arg (ap, lisp_fwd_path_t *); + + s = format (s, "priority:%d weight:%d ", lfp->priority, lfp->weight); + s = format (s, "adj:[%U]\n", + format_lisp_gpe_adjacency, + lisp_gpe_adjacency_get (lfp->lisp_adj), + LISP_GPE_ADJ_FORMAT_FLAG_NONE); + + return (s); +} + +typedef enum lisp_gpe_fwd_entry_format_flag_t_ +{ + LISP_GPE_FWD_ENTRY_FORMAT_NONE = (0 << 0), + LISP_GPE_FWD_ENTRY_FORMAT_DETAIL = (1 << 1), +} lisp_gpe_fwd_entry_format_flag_t; + + +static u8 * +format_lisp_gpe_fwd_entry (u8 * s, va_list ap) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + lisp_gpe_fwd_entry_t *lfe = va_arg (ap, lisp_gpe_fwd_entry_t *); + lisp_gpe_fwd_entry_format_flag_t flags = + va_arg (ap, lisp_gpe_fwd_entry_format_flag_t); + + s = format (s, "VNI:%d VRF:%d EID: %U -> %U [index:%d]", + lfe->key->vni, lfe->eid_table_id, + format_fid_address, &lfe->key->lcl, + format_fid_address, &lfe->key->rmt, + lfe - lgm->lisp_fwd_entry_pool); + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type) + { + s = format (s, "\n Negative - action:%U", + format_negative_mapping_action, lfe->action); + } + else + { + lisp_fwd_path_t *path; + + s = format (s, "\n via:"); + vec_foreach (path, lfe->paths) + { + s = format (s, "\n %U", format_lisp_fwd_path, path); + } + } + + if (flags & LISP_GPE_FWD_ENTRY_FORMAT_DETAIL) + { + switch (fid_addr_type (&lfe->key->rmt)) + { + case FID_ADDR_MAC: + s = format (s, " fib-path-list:%d\n", lfe->l2.path_list_index); + s = format (s, " dpo:%U\n", format_dpo_id, &lfe->l2.dpo, 0); + break; + case FID_ADDR_IP_PREF: + break; + } + } + + return (s); +} + +static clib_error_t * +lisp_gpe_fwd_entry_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + lisp_gpe_fwd_entry_t *lfe; + index_t index; + u32 vni = ~0; + + if (unformat (input, "vni %d", &vni)) + ; + else if (unformat (input, "%d", &index)) + { + if (!pool_is_free_index (lgm->lisp_fwd_entry_pool, index)) + { + lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, index); + + vlib_cli_output (vm, "[%d@] %U", + index, + format_lisp_gpe_fwd_entry, lfe, + LISP_GPE_FWD_ENTRY_FORMAT_DETAIL); + } + else + { + vlib_cli_output (vm, "entry %d invalid", index); + } + + return (NULL); + } + + /* *INDENT-OFF* */ + pool_foreach (lfe, lgm->lisp_fwd_entry_pool, + ({ + if ((vni == ~0) || + (lfe->key->vni == vni)) + vlib_cli_output (vm, "%U", format_lisp_gpe_fwd_entry, lfe, + LISP_GPE_FWD_ENTRY_FORMAT_NONE); + })); + /* *INDENT-ON* */ + + return (NULL); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (lisp_gpe_fwd_entry_show_command, static) = { + .path = "show lisp gpe entry", + .short_help = "show lisp gpe entry vni vrf [leid ] reid ", + .function = lisp_gpe_fwd_entry_show, +}; +/* *INDENT-ON* */ + +clib_error_t * +lisp_gpe_fwd_entry_init (vlib_main_t * vm) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + clib_error_t *error = NULL; + + if ((error = vlib_call_init_function (vm, lisp_cp_dpo_module_init))) + return (error); + + l2_fib_init (lgm); + + fib_node_register_type (FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, &lisp_fwd_vft); + + return (error); +} + +VLIB_INIT_FUNCTION (lisp_gpe_fwd_entry_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_fwd_entry.h b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_fwd_entry.h new file mode 100644 index 00000000..f7923671 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_fwd_entry.h @@ -0,0 +1,188 @@ +/* + * 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. + */ +/** + * @file + * @brief LISP-GPE definitions. + */ + +#ifndef __LISP_GPE_FWD_ENTRY_H__ +#define __LISP_GPE_FWD_ENTRY_H__ + +#include + +/** + * @brief A path on which to forward lisp traffic + */ +typedef struct lisp_fwd_path_t_ +{ + /** + * The adjacency constructed for the locator pair + */ + index_t lisp_adj; + + /** + * Priority. Only the paths with the best priority will be installed in FIB + */ + u8 priority; + + /** + * [UE]CMP weigt for the path + */ + u8 weight; + +} lisp_fwd_path_t; + +/** + * @brief A Forwarding entry can be 'normal' or 'negative' + * Negative implies we deliberately want to add a FIB entry for an EID + * that results in 'special' behaviour determined by an 'action'. + * @normal means send it down some tunnels. + */ +typedef enum lisp_gpe_fwd_entry_type_t_ +{ + LISP_GPE_FWD_ENTRY_TYPE_NORMAL, + LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE, +} lisp_gpe_fwd_entry_type_t; + + +/** + * LISP-GPE fwd entry key + */ +typedef struct lisp_gpe_fwd_entry_key_t_ +{ + dp_address_t rmt; + dp_address_t lcl; + u32 vni; +} lisp_gpe_fwd_entry_key_t; + +/** + * @brief A LISP Forwarding Entry + * + * A forwarding entry is from a locai EID to a remote EID over a set of rloc pairs + */ +typedef struct lisp_gpe_fwd_entry_t_ +{ + /** + * This object joins the FIB control plane graph to receive updates to + * for changes to the graph. + */ + fib_node_t node; + + /** + * The Entry's key: {lEID,r-EID,vni} + */ + lisp_gpe_fwd_entry_key_t *key; + + /** + * The forwarding entry type + */ + lisp_gpe_fwd_entry_type_t type; + + /** + * The tenant the entry belongs to + */ + u32 tenant; + + /** + * The VRF (in the case of L3) or Bridge-Domain (for L2) index + */ + union + { + /** + * Fields relevant to an L2 entry + */ + struct + { + /** + * The VRF ID + */ + u32 eid_table_id; + + /** + * The FIB index for the overlay, i.e. the FIB in which the EIDs + * are present + */ + u32 eid_fib_index; + /** + * The SRC-FIB index for created for anding source-route entries + */ + u32 src_fib_index; + }; + /** + * Fields relevant to an L2 entry + */ + struct + { + /** + * The Bridge-Domain (for L2) index + */ + u32 eid_bd_id; + + /** + * The Bridge-domain index for the overlay EIDs + */ + u32 eid_bd_index; + + /** + * The path-list created for the forwarding + */ + fib_node_index_t path_list_index; + + /** + * Child index of this entry on the path-list + */ + u32 child_index; + + /** + * The DPO used to forward + */ + dpo_id_t dpo; + } l2; + }; + + union + { + /** + * @brief When the type is 'normal' + * The RLOC pair that form the route's paths. i.e. where to send + * packets for this route. + */ + lisp_fwd_path_t *paths; + + /** + * @brief When the type is negative. The action to take. + */ + negative_fwd_actions_e action; + }; +} lisp_gpe_fwd_entry_t; + +extern int +vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, + u32 * hw_if_indexp); + +extern void vnet_lisp_gpe_fwd_entry_flush (void); + +extern u32 lisp_l2_fib_lookup (lisp_gpe_main_t * lgm, + u16 bd_index, u8 src_mac[8], u8 dst_mac[8]); + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_packet.h b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_packet.h new file mode 100644 index 00000000..62ac9bd7 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_packet.h @@ -0,0 +1,149 @@ +/* + * 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. + */ +/** + * @file + * @brief LISP-GPE packet header structure + * + */ + +#ifndef included_lisp_gpe_packet_h +#define included_lisp_gpe_packet_h + +/* + * From draft-lewis-lisp-gpe-02.txt + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |N|L|E|V|I|P|R|O|Ver| Reserved | Next Protocol | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Instance ID/Locator-Status-Bits | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * N: The N-bit is the nonce-present bit. When this bit is set to 1, + * the low-order 24 bits of the first 32 bits of the LISP header + * contain a Nonce. See Section 6.3.1 for details. Both N- and + * V-bits MUST NOT be set in the same packet. If they are, a + * decapsulating ETR MUST treat the 'Nonce/Map-Version' field as + * having a Nonce value present. + * + * L: The L-bit is the 'Locator-Status-Bits' field enabled bit. When + * this bit is set to 1, the Locator-Status-Bits in the second + * 32 bits of the LISP header are in use. + * + * E: The E-bit is the echo-nonce-request bit. This bit MUST be ignored + * and has no meaning when the N-bit is set to 0. When the N-bit is + * set to 1 and this bit is set to 1, an ITR is requesting that the + * nonce value in the 'Nonce' field be echoed back in LISP- + * encapsulated packets when the ITR is also an ETR. See + * Section 6.3.1 for details. + * + * V: The V-bit is the Map-Version present bit. When this bit is set to + * 1, the N-bit MUST be 0. Refer to Section 6.6.3 for more details. + * + * I: The I-bit is the Instance ID bit. See Section 5.5 for more + * details. When this bit is set to 1, the 'Locator-Status-Bits' + * field is reduced to 8 bits and the high-order 24 bits are used as + * an Instance ID. If the L-bit is set to 0, then the low-order + * 8 bits are transmitted as zero and ignored on receipt. + * + * P Bit: Flag bit 5 is defined as the Next Protocol bit. The P bit + * MUST be set to 1 to indicate the presence of the 8 bit next + * protocol field. + * + * P = 0 indicates that the payload MUST conform to LISP as defined + * in [RFC6830]. + * + * Flag bit 5 was chosen as the P bit because this flag bit is + * currently unallocated in LISP [RFC6830]. + * + * O: Flag bit 7 is defined as the O bit. When the O bit is set to 1, the + * packet is an OAM packet and OAM processing MUST occur. The OAM + * protocol details are out of scope for this document. As with the + * P-bit, bit 7 is currently a reserved flag in [RFC6830]. + * + * Next Protocol Field: The lower 8 bits of the first word are used to + * carry a next protocol. This next protocol field contains the + * protocol of the encapsulated payload packet. + * + * LISP [RFC6830] uses the lower 16 bits of the first word for either + * a nonce, an echo-nonce ([RFC6830]) or to support map-versioning + * ([RFC6834]). These are all optional capabilities that are + * indicated by setting the N, E, and the V bit respectively. + * + * To maintain the desired data plane compatibility, when the P bit + * is set, the N, E, and V bits MUST be set to zero. + * + * A new protocol registry will be requested from IANA for the Next + * Protocol field. This draft defines the following Next Protocol + * values: + * + * 0x1 : IPv4 + * 0x2 : IPv6 + * 0x3 : Ethernet + * 0x4: Network Service Header + */ + +/** LISP-GPE header */ +typedef struct +{ + u8 flags; + u8 ver_res; + u8 res; + u8 next_protocol; + u32 iid; +} lisp_gpe_header_t; + +#define foreach_lisp_gpe_flag_bit \ +_(N, 0x80) \ +_(L, 0x40) \ +_(E, 0x20) \ +_(V, 0x10) \ +_(I, 0x08) \ +_(P, 0x04) \ +_(O, 0x01) + +typedef enum +{ +#define _(n,v) LISP_GPE_FLAGS_##n = v, + foreach_lisp_gpe_flag_bit +#undef _ +} vnet_lisp_gpe_flag_bit_t; + +#define LISP_GPE_VERSION 0x0 + +#define LISP_GPE_NEXT_PROTOCOL_IP4 0x1 +#define LISP_GPE_NEXT_PROTOCOL_IP6 0x2 +#define LISP_GPE_NEXT_PROTOCOL_ETHERNET 0x3 +#define LISP_GPE_NEXT_PROTOCOL_NSH 0x4 + +typedef enum +{ + LISP_GPE_NEXT_PROTO_IP4 = 1, + LISP_GPE_NEXT_PROTO_IP6, + LISP_GPE_NEXT_PROTO_ETHERNET, + LISP_GPE_NEXT_PROTO_NSH, + LISP_GPE_NEXT_PROTOS +} lisp_gpe_next_protocol_e; + +#endif /* included_lisp_gpe_packet_h */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.c b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.c new file mode 100644 index 00000000..5b69bd15 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.c @@ -0,0 +1,278 @@ +/* + * 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. + */ +/** + * @file + * @brief LISP sub-interfaces. + * + */ +#include +#include +#include +#include + +/** + * @brief Pool of all l3-sub-interfaces + */ +static lisp_gpe_sub_interface_t *lisp_gpe_sub_interface_pool; + +/** + * A DB of all LISP L3 sub-interfaces. The key is:{VNI,l-RLOC} + */ +static uword *lisp_gpe_sub_interfaces; + +/** + * A DB of all VNET L3 sub-interfaces. The key is:{VNI,l-RLOC} + * Used in the data-plane for interface lookup on decap. + */ +uword *lisp_gpe_sub_interfaces_sw_if_index; + +/** + * The next available sub-interface ID. FIXME + */ +static u32 lisp_gpe_sub_interface_id; + + +static index_t +lisp_gpe_sub_interface_db_find (const ip_address_t * lrloc, u32 vni) +{ + uword *p; + + lisp_gpe_sub_interface_key_t key = { + .local_rloc = *lrloc, + .vni = clib_host_to_net_u32 (vni), + }; + + p = hash_get_mem (lisp_gpe_sub_interfaces, &key); + + if (NULL == p) + return (INDEX_INVALID); + else + return (p[0]); +} + +static void +lisp_gpe_sub_interface_db_insert (const lisp_gpe_sub_interface_t * l3s) +{ + hash_set_mem (lisp_gpe_sub_interfaces, + &l3s->key, l3s - lisp_gpe_sub_interface_pool); + hash_set_mem (lisp_gpe_sub_interfaces_sw_if_index, + &l3s->key, l3s->sw_if_index); +} + +static void +lisp_gpe_sub_interface_db_remove (const lisp_gpe_sub_interface_t * l3s) +{ + hash_unset_mem (lisp_gpe_sub_interfaces, &l3s->key); + hash_unset_mem (lisp_gpe_sub_interfaces_sw_if_index, &l3s->key); +} + +lisp_gpe_sub_interface_t * +lisp_gpe_sub_interface_get_i (index_t l3si) +{ + return (pool_elt_at_index (lisp_gpe_sub_interface_pool, l3si)); +} + +static void +lisp_gpe_sub_interface_set_table (u32 sw_if_index, u32 table_id) +{ + fib_node_index_t fib_index; + + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id); + ASSERT (FIB_NODE_INDEX_INVALID != fib_index); + + vec_validate (ip4_main.fib_index_by_sw_if_index, sw_if_index); + ip4_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; + + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id); + ASSERT (FIB_NODE_INDEX_INVALID != fib_index); + + vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index); + ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index; +} + +static void +lisp_gpe_sub_interface_unset_table (u32 sw_if_index, u32 table_id) +{ + ip4_main.fib_index_by_sw_if_index[sw_if_index] = 0; + ip4_sw_interface_enable_disable (sw_if_index, 0); + + ip6_main.fib_index_by_sw_if_index[sw_if_index] = 0; + ip6_sw_interface_enable_disable (sw_if_index, 0); +} + +index_t +lisp_gpe_sub_interface_find_or_create_and_lock (const ip_address_t * lrloc, + u32 overlay_table_id, u32 vni) +{ + lisp_gpe_sub_interface_t *l3s; + index_t l3si; + + l3si = lisp_gpe_sub_interface_db_find (lrloc, clib_host_to_net_u32 (vni)); + + if (INDEX_INVALID == l3si) + { + u32 main_sw_if_index, sub_sw_if_index; + + /* + * find the main interface from the VNI + */ + main_sw_if_index = + lisp_gpe_tenant_l3_iface_add_or_lock (vni, overlay_table_id); + + vnet_sw_interface_t sub_itf_template = { + .type = VNET_SW_INTERFACE_TYPE_SUB, + .flood_class = VNET_FLOOD_CLASS_NORMAL, + .sup_sw_if_index = main_sw_if_index, + .sub.id = lisp_gpe_sub_interface_id++, + }; + + if (NULL != vnet_create_sw_interface (vnet_get_main (), + &sub_itf_template, + &sub_sw_if_index)) + return (INDEX_INVALID); + + pool_get (lisp_gpe_sub_interface_pool, l3s); + memset (l3s, 0, sizeof (*l3s)); + l3s->key = clib_mem_alloc (sizeof (*l3s->key)); + memset (l3s->key, 0, sizeof (*l3s->key)); + + l3s->key->local_rloc = *lrloc; + l3s->key->vni = clib_host_to_net_u32 (vni); + l3s->main_sw_if_index = main_sw_if_index; + l3s->sw_if_index = sub_sw_if_index; + l3s->eid_table_id = overlay_table_id; + + l3si = (l3s - lisp_gpe_sub_interface_pool); + + // FIXME. enable When we get an adj + ip6_sw_interface_enable_disable (l3s->sw_if_index, 1); + ip4_sw_interface_enable_disable (l3s->sw_if_index, 1); + + vnet_sw_interface_set_flags (vnet_get_main (), + l3s->sw_if_index, + VNET_SW_INTERFACE_FLAG_ADMIN_UP); + + lisp_gpe_sub_interface_db_insert (l3s); + } + else + { + l3s = lisp_gpe_sub_interface_get_i (l3si); + l3s->eid_table_id = overlay_table_id; + } + + lisp_gpe_sub_interface_set_table (l3s->sw_if_index, l3s->eid_table_id); + l3s->locks++; + + return (l3si); +} + +void +lisp_gpe_sub_interface_unlock (index_t l3si) +{ + lisp_gpe_sub_interface_t *l3s; + + l3s = lisp_gpe_sub_interface_get_i (l3si); + + l3s->locks--; + + if (0 == l3s->locks) + { + lisp_gpe_sub_interface_unset_table (l3s->sw_if_index, + l3s->eid_table_id); + + lisp_gpe_tenant_l3_iface_unlock (clib_net_to_host_u32 (l3s->key->vni)); + vnet_sw_interface_set_flags (vnet_get_main (), l3s->sw_if_index, 0); + vnet_delete_sub_interface (l3s->sw_if_index); + + lisp_gpe_sub_interface_db_remove (l3s); + + clib_mem_free (l3s->key); + pool_put (lisp_gpe_sub_interface_pool, l3s); + } +} + +const lisp_gpe_sub_interface_t * +lisp_gpe_sub_interface_get (index_t l3si) +{ + return (lisp_gpe_sub_interface_get_i (l3si)); +} + +u8 * +format_lisp_gpe_sub_interface (u8 * s, va_list ap) +{ + lisp_gpe_sub_interface_t *l3s = va_arg (ap, lisp_gpe_sub_interface_t *); + vnet_main_t *vnm = vnet_get_main (); + + s = format (s, "%=16U", + format_vnet_sw_interface_name, + vnm, vnet_get_sw_interface (vnm, l3s->sw_if_index)); + s = format (s, "%=10d", clib_net_to_host_u32 (l3s->key->vni)); + s = format (s, "%=12d", l3s->sw_if_index); + s = format (s, "%U", format_ip_address, &l3s->key->local_rloc); + + return (s); +} + +/** CLI command to show LISP-GPE interfaces. */ +static clib_error_t * +lisp_gpe_sub_interface_show (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + lisp_gpe_sub_interface_t *l3s; + + vlib_cli_output (vm, "%=16s%=10s%=12s%s", "Name", "VNI", "SW IF Index", + "local RLOC"); + + /* *INDENT-OFF* */ + pool_foreach (l3s, lisp_gpe_sub_interface_pool, + ({ + vlib_cli_output (vm, "%U", format_lisp_gpe_sub_interface, l3s); + })); + /* *INDENT-ON* */ + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (lisp_gpe_sub_interface_command) = { + .path = "show lisp gpe sub-interface", + .short_help = "show lisp gpe sub-interface", + .function = lisp_gpe_sub_interface_show, +}; +/* *INDENT-ON* */ + +static clib_error_t * +lisp_gpe_sub_interface_module_init (vlib_main_t * vm) +{ + lisp_gpe_sub_interfaces = + hash_create_mem (0, + sizeof (lisp_gpe_sub_interface_key_t), sizeof (uword)); + lisp_gpe_sub_interfaces_sw_if_index = + hash_create_mem (0, + sizeof (lisp_gpe_sub_interface_key_t), sizeof (uword)); + + return (NULL); +} + +VLIB_INIT_FUNCTION (lisp_gpe_sub_interface_module_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.h b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.h new file mode 100644 index 00000000..ad942f41 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.h @@ -0,0 +1,157 @@ +/* + * 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. + */ +/** + * @file + * @brief LISP sub-interfaces. + * + */ + +#ifndef __LISP_GPE_SUB_INTERFACE_H__ +#define __LISP_GPE_SUB_INTERFACE_H__ + +#include + +/** + * A Key for lookup in the L£ sub-interface DB + */ +typedef struct lisp_gpe_sub_interface_key_t_ +{ + /** + * The local-RLOC. This is the interface's 'source' address. + */ + ip_address_t local_rloc; + + /** + * The VNI. In network byte order! + */ + u32 vni; +} lisp_gpe_sub_interface_key_t; + +/** + * @brief A LISP L3 sub-interface + * + * A LISP sub-interface is a multi-access interface, whose local address is a + * single local-RLOC. Adjacencies that form on this sub-interface, represent + * remote RLOCs. + * This is analogous to an ethernet interface. + * As with all interface types it can only be present in one VRF, hence a + * LISP sub-interface is per-local-rloc and per-VNI. + */ +typedef struct lisp_gpe_sub_interface_t_ +{ + /** + * The interface's key inthe DB; rloc & vni; + * The key is allocated from the heap so it can be used in the hash-table. + * if it's part of the object, then it is subjet to realloc, which no-worky. + */ + lisp_gpe_sub_interface_key_t *key; + + /** + * The Table-ID in the overlay that this interface is bound to. + */ + u32 eid_table_id; + + /** + * A reference counting lock on the number of users of this interface. + * When this count drops to 0 the interface is deleted. + */ + u32 locks; + + /** + * The SW if index assigned to this sub-interface + */ + u32 sw_if_index; + + /** + * The SW IF index assigned to the main interface of which this is a sub. + */ + u32 main_sw_if_index; +} lisp_gpe_sub_interface_t; + +extern index_t lisp_gpe_sub_interface_find_or_create_and_lock (const + ip_address_t * + lrloc, + u32 + eid_table_id, + u32 vni); + +extern u8 *format_lisp_gpe_sub_interface (u8 * s, va_list ap); + +extern void lisp_gpe_sub_interface_unlock (index_t itf); + +extern const lisp_gpe_sub_interface_t *lisp_gpe_sub_interface_get (index_t + itf); + +/** + * A DB of all L3 sub-interfaces. The key is:{VNI,l-RLOC} + */ +extern uword *lisp_gpe_sub_interfaces_sw_if_index; + +/** + * @brief + * Get a VNET L3 interface matching the local-RLOC and VNI + * Called from the data-plane + */ +always_inline u32 +lisp_gpe_sub_interface_find_ip6 (const ip6_address_t * addr, u32 vni) +{ + lisp_gpe_sub_interface_key_t key; + const uword *p; + + key.local_rloc.ip.v6.as_u64[0] = addr->as_u64[0]; + key.local_rloc.ip.v6.as_u64[1] = addr->as_u64[1]; + key.local_rloc.version = IP6; + key.vni = vni; + + p = hash_get_mem (&lisp_gpe_sub_interfaces_sw_if_index, &key); + + if (NULL != p) + return p[0]; + + return (INDEX_INVALID); +} + +/** + * @brief + * Get a VNET L3 interface matching the local-RLOC and VNI + * Called from the data-plane + */ +always_inline index_t +lisp_gpe_sub_interface_find_ip4 (const ip4_address_t * addr, u32 vni) +{ + lisp_gpe_sub_interface_key_t key; + const uword *p; + + key.local_rloc.ip.v4.as_u32 = addr->as_u32; + key.local_rloc.version = IP4; + key.vni = vni; + + p = hash_get_mem (&lisp_gpe_sub_interfaces_sw_if_index, &key); + + if (NULL != p) + return p[0]; + + return (INDEX_INVALID); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ + +#endif diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tenant.c b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tenant.c new file mode 100644 index 00000000..6abb7731 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tenant.c @@ -0,0 +1,330 @@ +/* + * 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 + +/** + * The pool of all tenants + */ +static lisp_gpe_tenant_t *lisp_gpe_tenant_pool; + +/** + * The hash table of all tenants: key:{VNI}. + */ +uword *lisp_gpe_tenant_db; + +static lisp_gpe_tenant_t * +lisp_gpe_tenant_find (u32 vni) +{ + uword *p; + + p = hash_get (lisp_gpe_tenant_db, vni); + + if (NULL == p) + return (NULL); + + return (pool_elt_at_index (lisp_gpe_tenant_pool, p[0])); +} + +static lisp_gpe_tenant_t * +lisp_gpe_tenant_find_or_create_i (u32 vni) +{ + lisp_gpe_tenant_t *lt; + + lt = lisp_gpe_tenant_find (vni); + + if (NULL == lt) + { + pool_get (lisp_gpe_tenant_pool, lt); + memset (lt, 0, sizeof (*lt)); + + lt->lt_vni = vni; + lt->lt_table_id = ~0; + lt->lt_bd_id = ~0; + + hash_set (lisp_gpe_tenant_db, vni, lt - lisp_gpe_tenant_pool); + } + + return (lt); +} + +/** + * @brief Find or create a tenant for the given VNI + */ +u32 +lisp_gpe_tenant_find_or_create (u32 vni) +{ + lisp_gpe_tenant_t *lt; + + lt = lisp_gpe_tenant_find (vni); + + if (NULL == lt) + { + lt = lisp_gpe_tenant_find_or_create_i (vni); + } + + return (lt - lisp_gpe_tenant_pool); +} + +/** + * @brief If there are no more locks/users of te tenant, then delete it + */ +static void +lisp_gpe_tenant_delete_if_empty (lisp_gpe_tenant_t * lt) +{ + int i; + + for (i = 0; i < LISP_GPE_TENANT_LOCK_NUM; i++) + { + if (lt->lt_locks[i]) + return; + } + + hash_unset (lisp_gpe_tenant_db, lt->lt_vni); + pool_put (lisp_gpe_tenant_pool, lt); +} + +/** + * @brief Add/create and lock a new or find and lock the existing L3 + * interface for the tenant + * + * @paran vni The tenant's VNI + * @param table_id the Tenant's L3 table ID. + * + * @return the SW IF index of the L3 interface + */ +u32 +lisp_gpe_tenant_l3_iface_add_or_lock (u32 vni, u32 table_id) +{ + lisp_gpe_tenant_t *lt; + + lt = lisp_gpe_tenant_find_or_create_i (vni); + + if (~0 == lt->lt_table_id) + lt->lt_table_id = table_id; + + ASSERT (lt->lt_table_id == table_id); + + if (0 == lt->lt_locks[LISP_GPE_TENANT_LOCK_L3_IFACE]) + { + /* create the l3 interface since there are currently no users of it */ + lt->lt_l3_sw_if_index = + lisp_gpe_add_l3_iface (&lisp_gpe_main, vni, table_id); + } + + lt->lt_locks[LISP_GPE_TENANT_LOCK_L3_IFACE]++; + + return (lt->lt_l3_sw_if_index); +} + +/** + * @brief Release the lock held on the tenant's L3 interface + */ +void +lisp_gpe_tenant_l3_iface_unlock (u32 vni) +{ + lisp_gpe_tenant_t *lt; + + lt = lisp_gpe_tenant_find (vni); + + if (NULL == lt) + { + clib_warning ("No tenant for VNI %d", vni); + return; + } + + if (0 == lt->lt_locks[LISP_GPE_TENANT_LOCK_L3_IFACE]) + { + clib_warning ("No L3 interface for tenant VNI %d", vni); + return; + } + + lt->lt_locks[LISP_GPE_TENANT_LOCK_L3_IFACE]--; + + if (0 == lt->lt_locks[LISP_GPE_TENANT_LOCK_L3_IFACE]) + { + /* the last user has gone, so delete the l3 interface */ + lisp_gpe_del_l3_iface (&lisp_gpe_main, vni, lt->lt_table_id); + } + + /* + * If there are no more locks on any tenant managed resource, then + * this tenant is toast. + */ + lisp_gpe_tenant_delete_if_empty (lt); +} + +/** + * @brief Add/create and lock a new or find and lock the existing L2 + * interface for the tenant + * + * @paran vni The tenant's VNI + * @param table_id the Tenant's L2 Bridge Domain ID. + * + * @return the SW IF index of the L2 interface + */ +u32 +lisp_gpe_tenant_l2_iface_add_or_lock (u32 vni, u32 bd_id) +{ + lisp_gpe_tenant_t *lt; + + lt = lisp_gpe_tenant_find_or_create_i (vni); + + if (NULL == lt) + { + clib_warning ("No tenant for VNI %d", vni); + return ~0; + } + + if (~0 == lt->lt_bd_id) + lt->lt_bd_id = bd_id; + + ASSERT (lt->lt_bd_id == bd_id); + + if (0 == lt->lt_locks[LISP_GPE_TENANT_LOCK_L2_IFACE]) + { + /* create the l2 interface since there are currently no users of it */ + lt->lt_l2_sw_if_index = + lisp_gpe_add_l2_iface (&lisp_gpe_main, vni, bd_id); + } + + lt->lt_locks[LISP_GPE_TENANT_LOCK_L2_IFACE]++; + + return (lt->lt_l2_sw_if_index); +} + +/** + * @brief Release the lock held on the tenant's L3 interface + */ +void +lisp_gpe_tenant_l2_iface_unlock (u32 vni) +{ + lisp_gpe_tenant_t *lt; + + lt = lisp_gpe_tenant_find (vni); + + if (NULL == lt) + { + clib_warning ("No tenant for VNI %d", vni); + return; + } + + if (0 == lt->lt_locks[LISP_GPE_TENANT_LOCK_L2_IFACE]) + { + clib_warning ("No L2 interface for tenant VNI %d", vni); + return; + } + + lt->lt_locks[LISP_GPE_TENANT_LOCK_L2_IFACE]--; + + if (0 == lt->lt_locks[LISP_GPE_TENANT_LOCK_L2_IFACE]) + { + /* the last user has gone, so delete the l2 interface */ + lisp_gpe_del_l2_iface (&lisp_gpe_main, vni, lt->lt_bd_id); + } + + /* + * If there are no more locks on any tenant managed resource, then + * this tenant is toast. + */ + lisp_gpe_tenant_delete_if_empty (lt); +} + +/** + * @brief get a const pointer to the tenant object + */ +const lisp_gpe_tenant_t * +lisp_gpe_tenant_get (u32 index) +{ + return (pool_elt_at_index (lisp_gpe_tenant_pool, index)); +} + +/** + * @brief Flush/delete ALL the tenants + */ +void +lisp_gpe_tenant_flush (void) +{ + lisp_gpe_tenant_t *lt; + + /* *INDENT-OFF* */ + pool_foreach(lt, lisp_gpe_tenant_pool, + ({ + lisp_gpe_tenant_l2_iface_unlock(lt->lt_vni); + lisp_gpe_tenant_l3_iface_unlock(lt->lt_vni); + })); + /* *INDENT-ON* */ +} + +/** + * @brif Show/display one tenant + */ +static u8 * +format_lisp_gpe_tenant (u8 * s, va_list ap) +{ + const lisp_gpe_tenant_t *lt = va_arg (ap, lisp_gpe_tenant_t *); + + s = format (s, "VNI:%d ", lt->lt_vni); + + if (lt->lt_table_id != ~0) + { + s = format (s, "VRF:%d ", lt->lt_table_id); + s = format (s, "L3-SW-IF:%d ", lt->lt_l3_sw_if_index); + } + + if (lt->lt_bd_id != ~0) + { + s = format (s, "BD-ID:%d ", lt->lt_bd_id); + s = format (s, "L2-SW-IF:%d ", lt->lt_l2_sw_if_index); + } + + return (s); +} + +/** + * @brief CLI command to show LISP-GPE tenant. + */ +static clib_error_t * +lisp_gpe_tenant_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + lisp_gpe_tenant_t *lt; + + /* *INDENT-OFF* */ + pool_foreach (lt, lisp_gpe_tenant_pool, + ({ + vlib_cli_output (vm, "%U", format_lisp_gpe_tenant, lt); + })); + /* *INDENT-ON* */ + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (lisp_gpe_tenant_command) = { + .path = "show lisp gpe tenant", + .short_help = "show lisp gpe tenant", + .function = lisp_gpe_tenant_show, +}; +/* *INDENT-ON* */ + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tenant.h b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tenant.h new file mode 100644 index 00000000..5db7dde8 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tenant.h @@ -0,0 +1,88 @@ +/* + * 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 __LISP_GPE_TENANT_H__ +#define __LISP_GPE_TENANT_H__ + +#include + +/** + * Refernece counting lock types on the tenant. + * When all of these counters drop to zero, we no longer need the tenant. + */ +typedef enum lisp_gpe_tenant_lock_t_ +{ + LISP_GPE_TENANT_LOCK_L2_IFACE, + LISP_GPE_TENANT_LOCK_L3_IFACE, + LISP_GPE_TENANT_LOCK_NUM, +} lisp_gpe_tenant_lock_t; + +/** + * @brief Representation of the data associated with a LISP overlay tenant + * + * This object exists to manage the shared resources of the L2 and L3 interface + * of a given tenant. + */ +typedef struct lisp_gpe_tenant_t_ +{ + /** + * The VNI is the identifier of the tenant + */ + u32 lt_vni; + + /** + * The tenant can have both L2 and L3 services enabled. + */ + u32 lt_table_id; + u32 lt_bd_id; + + /** + * The number of locks on the tenant's L3 interface. + */ + u32 lt_locks[LISP_GPE_TENANT_LOCK_NUM]; + + /** + * The L3 SW interface index + */ + u32 lt_l3_sw_if_index; + + /** + * The L2 SW interface index + */ + u32 lt_l2_sw_if_index; + +} lisp_gpe_tenant_t; + +extern u32 lisp_gpe_tenant_find_or_create (u32 vni); + +extern u32 lisp_gpe_tenant_l3_iface_add_or_lock (u32 vni, u32 vrf); +extern void lisp_gpe_tenant_l3_iface_unlock (u32 vni); + +extern u32 lisp_gpe_tenant_l2_iface_add_or_lock (u32 vni, u32 vrf); +extern void lisp_gpe_tenant_l2_iface_unlock (u32 vni); + +extern const lisp_gpe_tenant_t *lisp_gpe_tenant_get (u32 index); + +extern void lisp_gpe_tenant_flush (void); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ + +#endif diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tunnel.c b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tunnel.c new file mode 100644 index 00000000..e4e59707 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tunnel.c @@ -0,0 +1,289 @@ +/* + * 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. + */ +/** + * @file + * @brief Common utility functions for IPv4, IPv6 and L2 LISP-GPE tunnels. + * + */ +#include +#include +#include + +#include + +/** + * @brief Pool of all LISP tunnels + */ +static lisp_gpe_tunnel_t *lisp_gpe_tunnel_pool; + +/** + * @brief a DB of all tunnels + */ +static uword *lisp_gpe_tunnel_db; + +/** + * @brief Compute IP-UDP-GPE sub-tunnel encap/rewrite header. + * + * @param[in] t Parent of the sub-tunnel. + * @param[in] st Sub-tunnel. + * @param[in] lp Local and remote locators used in the encap header. + * + * @return 0 on success. + */ +u8 * +lisp_gpe_tunnel_build_rewrite (const lisp_gpe_tunnel_t * lgt, + const lisp_gpe_adjacency_t * ladj, + lisp_gpe_next_protocol_e payload_proto) +{ + lisp_gpe_header_t *lisp0; + u8 *rw = 0; + int len; + + if (IP4 == ip_addr_version (&lgt->key->lcl)) + { + ip4_udp_lisp_gpe_header_t *h0; + ip4_header_t *ip0; + + len = sizeof (*h0); + + vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES); + + h0 = (ip4_udp_lisp_gpe_header_t *) rw; + + /* Fixed portion of the (outer) ip4 header */ + ip0 = &h0->ip4; + ip0->ip_version_and_header_length = 0x45; + ip0->ttl = 254; + ip0->protocol = IP_PROTOCOL_UDP; + + /* we fix up the ip4 header length and checksum after-the-fact */ + ip_address_copy_addr (&ip0->src_address, &lgt->key->lcl); + ip_address_copy_addr (&ip0->dst_address, &lgt->key->rmt); + ip0->checksum = ip4_header_checksum (ip0); + + /* UDP header, randomize src port on something, maybe? */ + h0->udp.src_port = clib_host_to_net_u16 (4341); + h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_lisp_gpe); + + /* LISP-gpe header */ + lisp0 = &h0->lisp; + } + else + { + ip6_udp_lisp_gpe_header_t *h0; + ip6_header_t *ip0; + + len = sizeof (*h0); + + vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES); + + h0 = (ip6_udp_lisp_gpe_header_t *) rw; + + /* Fixed portion of the (outer) ip6 header */ + ip0 = &h0->ip6; + ip0->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 (0x6 << 28); + ip0->hop_limit = 254; + ip0->protocol = IP_PROTOCOL_UDP; + + /* we fix up the ip6 header length after-the-fact */ + ip_address_copy_addr (&ip0->src_address, &lgt->key->lcl); + ip_address_copy_addr (&ip0->dst_address, &lgt->key->rmt); + + /* UDP header, randomize src port on something, maybe? */ + h0->udp.src_port = clib_host_to_net_u16 (4341); + h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_lisp_gpe); + + /* LISP-gpe header */ + lisp0 = &h0->lisp; + } + + lisp0->flags = ladj->flags; + lisp0->ver_res = 0; + lisp0->res = 0; + lisp0->next_protocol = payload_proto; + lisp0->iid = clib_host_to_net_u32 (ladj->vni) >> 8; /* first 24 bits only */ + + return (rw); +} + +static lisp_gpe_tunnel_t * +lisp_gpe_tunnel_db_find (const lisp_gpe_tunnel_key_t * key) +{ + uword *p; + + p = hash_get_mem (lisp_gpe_tunnel_db, (void *) key); + + if (NULL != p) + { + return (pool_elt_at_index (lisp_gpe_tunnel_pool, p[0])); + } + return (NULL); +} + +lisp_gpe_tunnel_t * +lisp_gpe_tunnel_get_i (index_t lgti) +{ + return (pool_elt_at_index (lisp_gpe_tunnel_pool, lgti)); +} + +index_t +lisp_gpe_tunnel_find_or_create_and_lock (const locator_pair_t * pair, + u32 rloc_fib_index) +{ + lisp_gpe_tunnel_key_t key = { + .lcl = pair->lcl_loc, + .rmt = pair->rmt_loc, + .fib_index = rloc_fib_index, + }; + lisp_gpe_tunnel_t *lgt; + fib_prefix_t pfx; + + lgt = lisp_gpe_tunnel_db_find (&key); + + if (NULL == lgt) + { + pool_get (lisp_gpe_tunnel_pool, lgt); + memset (lgt, 0, sizeof (*lgt)); + + lgt->key = clib_mem_alloc (sizeof (*lgt->key)); + memset (lgt->key, 0, sizeof (*lgt->key)); + + lgt->key->rmt = pair->rmt_loc; + lgt->key->lcl = pair->lcl_loc; + lgt->key->fib_index = rloc_fib_index; + + /* + * source the FIB entry for the RLOC so we can track its forwarding + * chain + */ + ip_address_to_fib_prefix (&lgt->key->rmt, &pfx); + + lgt->fib_entry_index = fib_table_entry_special_add (rloc_fib_index, + &pfx, + FIB_SOURCE_RR, + FIB_ENTRY_FLAG_NONE, + ADJ_INDEX_INVALID); + + hash_set_mem (lisp_gpe_tunnel_db, &lgt->key, + (lgt - lisp_gpe_tunnel_pool)); + } + + lgt->locks++; + + return (lgt - lisp_gpe_tunnel_pool); +} + +void +lisp_gpe_tunnel_unlock (index_t lgti) +{ + lisp_gpe_tunnel_t *lgt; + + lgt = lisp_gpe_tunnel_get_i (lgti); + lgt->locks--; + + if (0 == lgt->locks) + { + hash_unset_mem (lisp_gpe_tunnel_db, &lgt->key); + clib_mem_free (lgt->key); + pool_put (lisp_gpe_tunnel_pool, lgt); + } +} + +const lisp_gpe_tunnel_t * +lisp_gpe_tunnel_get (index_t lgti) +{ + return (lisp_gpe_tunnel_get_i (lgti)); +} + +/** Format LISP-GPE tunnel. */ +u8 * +format_lisp_gpe_tunnel (u8 * s, va_list * args) +{ + lisp_gpe_tunnel_t *lgt = va_arg (*args, lisp_gpe_tunnel_t *); + + s = format (s, "tunnel %d\n", lgt - lisp_gpe_tunnel_pool); + s = format (s, " fib-index: %d, locks:%d \n", + lgt->key->fib_index, lgt->locks); + s = format (s, " lisp ver 0\n"); + + s = format (s, " locator-pair:\n"); + s = format (s, " local: %U remote: %U\n", + format_ip_address, &lgt->key->lcl, + format_ip_address, &lgt->key->rmt); + s = format (s, " RLOC FIB entry: %d\n", lgt->fib_entry_index); + + return s; +} + +/** + * CLI command to show LISP-GPE tunnels. + */ +static clib_error_t * +show_lisp_gpe_tunnel_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + lisp_gpe_tunnel_t *lgt; + index_t index; + + if (pool_elts (lisp_gpe_tunnel_pool) == 0) + vlib_cli_output (vm, "No lisp-gpe tunnels configured..."); + + if (unformat (input, "%d", &index)) + { + lgt = lisp_gpe_tunnel_get_i (index); + vlib_cli_output (vm, "%U", format_lisp_gpe_tunnel, lgt); + } + else + { + /* *INDENT-OFF* */ + pool_foreach (lgt, lisp_gpe_tunnel_pool, + ({ + vlib_cli_output (vm, "%U", format_lisp_gpe_tunnel, lgt); + })); + /* *INDENT-ON* */ + } + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_lisp_gpe_tunnel_command, static) = +{ + .path = "show lisp gpe tunnel", + .function = show_lisp_gpe_tunnel_command_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +lisp_gpe_tunnel_module_init (vlib_main_t * vm) +{ + lisp_gpe_tunnel_db = hash_create_mem (0, + sizeof (lisp_gpe_tunnel_key_t), + sizeof (uword)); + + return (NULL); +} + +VLIB_INIT_FUNCTION (lisp_gpe_tunnel_module_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tunnel.h b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tunnel.h new file mode 100644 index 00000000..333d2882 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/lisp_gpe_tunnel.h @@ -0,0 +1,89 @@ +/* + * 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. + */ +/** + * @file + * @brief Common utility functions for IPv4, IPv6 and L2 LISP-GPE tunnels. + * + */ + +#ifndef LISP_GPE_TUNNEL_H__ +#define LISP_GPE_TUNNEL_H__ + +#include +#include + +/** + * Forward declaration + */ +struct lisp_gpe_adjacency_t_; + +/** + * A Key for a tunnel + */ +typedef struct lisp_gpe_tunnel_key_t_ +{ + ip_address_t rmt; + ip_address_t lcl; + u32 fib_index; +} lisp_gpe_tunnel_key_t; + +/** + * @brief A LISP GPE Tunnel. + * + * A tunnel represents an associatation between a local and remote RLOC. + * As such it represents a unique LISP rewrite. + */ +typedef struct lisp_gpe_tunnel_t_ +{ + /** + * RLOC pair and rloc fib_index. This is the tunnel's key. + */ + lisp_gpe_tunnel_key_t *key; + + /** + * number of reference counting locks + */ + u32 locks; + + /** + * the FIB entry through which the remote rloc is reachable + s */ + fib_node_index_t fib_entry_index; +} lisp_gpe_tunnel_t; + +extern index_t lisp_gpe_tunnel_find_or_create_and_lock (const locator_pair_t * + pair, + u32 rloc_fib_index); + +extern void lisp_gpe_tunnel_unlock (index_t lgti); + +extern const lisp_gpe_tunnel_t *lisp_gpe_tunnel_get (index_t lgti); + +extern u8 *lisp_gpe_tunnel_build_rewrite (const lisp_gpe_tunnel_t * lgt, + const struct lisp_gpe_adjacency_t_ + *ladj, + lisp_gpe_next_protocol_e + payload_proto); +extern u8 *format_lisp_gpe_tunnel (u8 * s, va_list * args); + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/vpp/vnet/vnet/lisp-gpe/rfc.txt b/vpp/vnet/vnet/lisp-gpe/rfc.txt new file mode 100644 index 00000000..5e3da150 --- /dev/null +++ b/vpp/vnet/vnet/lisp-gpe/rfc.txt @@ -0,0 +1,826 @@ +Network Working Group D. Lewis +Internet-Draft Cisco Systems, Inc. +Intended status: Informational P. Agarwal +Expires: January 5, 2015 Broadcom + L. Kreeger + F. Maino + P. Quinn + M. Smith + N. Yadav + Cisco Systems, Inc. + July 4, 2014 + + + LISP Generic Protocol Extension + draft-lewis-lisp-gpe-02.txt + +Abstract + + This draft describes extending the Locator/ID Separation Protocol + (LISP) [RFC6830], via changes to the LISP header, with three new + capabilities: support for multi-protocol encapsulation, operations, + administration and management (OAM) signaling, and explicit + versioning. + +Status of this Memo + + This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at http://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on January 5, 2015. + +Copyright Notice + + Copyright (c) 2014 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + + + +Lewis, et al. Expires January 5, 2015 [Page 1] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 2. LISP Header Without Protocol Extensions . . . . . . . . . . . 4 + 3. Generic Protocol Extension for LISP (LISP-gpe) . . . . . . . . 5 + 3.1. Multi Protocol Support . . . . . . . . . . . . . . . . . . 5 + 3.2. OAM Support . . . . . . . . . . . . . . . . . . . . . . . 6 + 3.3. Version Bits . . . . . . . . . . . . . . . . . . . . . . . 6 + 4. Backward Compatibility . . . . . . . . . . . . . . . . . . . . 8 + 4.1. LISP-gpe Routers to (legacy) LISP Routers . . . . . . . . 8 + 4.2. (legacy) LISP Routers to LISP-gpe Routers . . . . . . . . 8 + 4.3. Type of Service . . . . . . . . . . . . . . . . . . . . . 8 + 4.4. VLAN Identifier (VID) . . . . . . . . . . . . . . . . . . 8 + 5. LISP-gpe Examples . . . . . . . . . . . . . . . . . . . . . . 9 + 6. Security Considerations . . . . . . . . . . . . . . . . . . . 11 + 7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 12 + 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 13 + 9. References . . . . . . . . . . . . . . . . . . . . . . . . . . 14 + 9.1. Normative References . . . . . . . . . . . . . . . . . . . 14 + 9.2. Informative References . . . . . . . . . . . . . . . . . . 14 + Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 15 + + + + + + + + + + + + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 2] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + +1. Introduction + + LISP [RFC6830] defines an encapsulation format that carries IPv4 or + IPv6 (henceforth referred to as IP) packets in a LISP header and + outer UDP/IP transport. + + The LISP header does not specify the protocol being encapsulated and + therefore is currently limited to encapsulating only IP packet + payloads. Other protocols, most notably VXLAN [VXLAN] (which defines + a similar header format to LISP), are used to encapsulate L2 + protocols such as Ethernet. LISP [RFC6830] can be extended to + indicate the inner protocol, enabling the encapsulation of Ethernet, + IP or any other desired protocol all the while ensuring compatibility + with existing LISP [RFC6830] deployments. + + As LISP is deployed, there's also the need to provide increased + visibility and diagnostic capabilities within the overlay. + + This document describes extending LISP ([RFC6830]) via the following + changes: + + Next Protocol Bit (P bit): A reserved flag bit is allocated, and set + in the LISP-gpe header to indicate that a next protocol field is + present. + + OAM Flag Bit (O bit): A reserved flag bit is allocated, and set in + the LISP-gpe header, to indicate that the packet is an OAM packet. + + Version: Two reserved bits are allocated, and set in the LISP-gpe + header, to indicate LISP-gpe protocol version. + + Next protocol: An 8 bit next protocol field is present in the LISP- + gpe header. + + + + + + + + + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 3] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + +2. LISP Header Without Protocol Extensions + + As described in the introduction, the LISP header has no protocol + identifier that indicates the type of payload being carried by LISP. + Because of this, LISP is limited to an IP payload. Furthermore, the + LISP header has no mechanism to signal OAM packets. + + The LISP header contains flags (some defined, some reserved), a + Nonce/Map-version field and an instance ID/Locator-status-bit field. + The flags provide flexibility to define how the reserved bits can be + used to change the definition of the LISP header. + + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |N|L|E|V|I|flags| Nonce/Map-Version | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Instance ID/Locator-Status-Bits | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + Figure 1: LISP Header + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 4] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + +3. Generic Protocol Extension for LISP (LISP-gpe) + +3.1. Multi Protocol Support + + This draft defines the following changes to the LISP header in order + to support multi-protocol encapsulation. + + P Bit: Flag bit 5 is defined as the Next Protocol bit. The P bit + MUST be set to 1 to indicate the presence of the 8 bit next + protocol field. + + P = 0 indicates that the payload MUST conform to LISP as defined + in [RFC6830]. + + Flag bit 5 was chosen as the P bit because this flag bit is + currently unallocated in LISP [RFC6830]. + + Next Protocol Field: The lower 8 bits of the first word are used to + carry a next protocol. This next protocol field contains the + protocol of the encapsulated payload packet. + + LISP [RFC6830] uses the lower 16 bits of the first word for either + a nonce, an echo-nonce ([RFC6830]) or to support map-versioning + ([RFC6834]). These are all optional capabilities that are + indicated by setting the N, E, and the V bit respectively. + + To maintain the desired data plane compatibility, when the P bit + is set, the N, E, and V bits MUST be set to zero. + + A new protocol registry will be requested from IANA for the Next + Protocol field. This draft defines the following Next Protocol + values: + + 0x1 : IPv4 + + 0x2 : IPv6 + + 0x3 : Ethernet + + 0x4: Network Service Header + + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 5] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |N|L|E|V|I|P|R|R| Reserved | Next Protocol | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Instance ID/Locator-Status-Bits | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + Figure 2: LISP-gpe Next Protocol (P=1) + +3.2. OAM Support + + Flag bit 7 is defined as the O bit. When the O bit is set to 1, the + packet is an OAM packet and OAM processing MUST occur. The OAM + protocol details are out of scope for this document. As with the + P-bit, bit 7 is currently a reserved flag in [RFC6830]. + + + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |N|L|E|V|I|P|R|O| Reserved | Next Protocol | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Instance ID/Locator-Status-Bits | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + Figure 3: LISP-gpe OAM bit (P=1) + +3.3. Version Bits + + LISP-gpe bits8 and 9 are defined as version bits. The version field + is used to ensure backward compatibility going forward with future + LISP-gpe updates. + + The initial version for LISP-gpe is 0. + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 6] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |N|L|E|V|I|P|R|O|Ver| Reserved | Next Protocol | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Instance ID/Locator-Status-Bits | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + Figure 4: LISP-gpe Version bits (P=1) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 7] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + +4. Backward Compatibility + + Undefined (in RFC6830) flag bits 5 and 7, LISP-gpe P and O bits, were + selected to ensure compatibility with existing LISP [RFC6830] + deployments. + + Similarly, using P = 0 to indicate that the format of the header and + payload conforms to [RFC6830] ensures compatibility with existing + LISP hardware forwarding platforms. + +4.1. LISP-gpe Routers to (legacy) LISP Routers + + A LISP-gpe router MUST not encapsulate non-IP packet nor OAM packets + to a LISP router. A method for determining the capabilities of a + LISP router (gpe or "legacy") is out of the scope of this draft. + + When encapsulating IP packets to a LISP router the P bit SHOULD be + set to 1 and the UDP port MUST be set to 4341. OAM bit MUST be set + to 0. The Next Protocol field SHOULD be 0x1 (IPv4) or 0x2 (IPv6). + The (legacy) LISP router will ignore the P bit and the protocol type + field. The (legacy) LISP router will treat the packet as a LISP + packet and inspect the first nibble of the payload to determine the + IP version. + + When the P bit is set, the N, E, and V bits MUST be set to zero. The + receiving (legacy) LISP router will ignore N, E and V bits, when the + P bit is set. + +4.2. (legacy) LISP Routers to LISP-gpe Routers + + When a LISP-gpe router receives a packet from a (legacy) LISP router, + the P bit MUST not be set and the UDP port MUST be 4341. The payload + MUST be IP, and the LISP-gpe router will inspect the first nibble of + the payload to determine IP version. + +4.3. Type of Service + + When a LISP-gpe router performs Ethernet encapsulation, the inner + 802.1Q [IEEE8021Q] priority code point (PCP) field MAY be mapped from + the encapsulated frame to the Type of Service field in the outer IPv4 + header, or in the case of IPv6 the 'Traffic Class' field. + +4.4. VLAN Identifier (VID) + + When a LISP-gpe router performs Ethernet encapsulation, the inner + header 802.1Q [IEEE8021Q] VLAN Identifier (VID) MAY be mapped to, or + used to determine the LISP Instance ID field. + + + + +Lewis, et al. Expires January 5, 2015 [Page 8] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + +5. LISP-gpe Examples + + This section provides two examples of IP protocols, and one example + of Ethernet encapsulated LISP-gpe using the generic extension + described in this document. + + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |N|L|E|V|I|1|0|0|0| Reserved | NP = IPv4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Instance ID/Locator-Status-Bits | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Original IPv4 Packet | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + Figure 5: IPv4 and LISP-gpe + + + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |N|L|E|V|I|1|0|0|0| Reserved | NP = IPv6 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Instance ID/Locator-Status-Bits | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Original IPv6 Packet | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + Figure 6: IPv6 and LISP-gpe + + + + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 9] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |N|L|E|V|I|1|0|0|0| Reserved | NP = Ethernet | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Instance ID/Locator-Status-Bits | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Original Ethernet Frame | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + Figure 7: Ethernet and LISP-gpe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 10] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + +6. Security Considerations + + LISP-gpe security considerations are similar to the LISP security + considerations documented at length in LISP [RFC6830]. With LISP- + gpe, issues such as dataplane spoofing, flooding, and traffic + redirection are dependent on the particular protocol payload + encapsulated. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 11] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + +7. Acknowledgments + + A special thank you goes to Dino Farinacci for his guidance and + detailed review. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 12] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + +8. IANA Considerations + + IANA is requested to set up a registry of "Next Protocol". These are + 8-bit values. Next Protocol values 0, 1, 2, 3 and 4 are defined in + this draft. New values are assigned via Standards Action [RFC5226]. + + +---------------+-------------+---------------+ + | Next Protocol | Description | Reference | + +---------------+-------------+---------------+ + | 0 | Reserved | This document | + | | | | + | 1 | IPv4 | This document | + | | | | + | 2 | IPv6 | This document | + | | | | + | 3 | Ethernet | This document | + | | | | + | 4 | NSH | This document | + | | | | + | 5..253 | Unassigned | | + +---------------+-------------+---------------+ + + Table 1 + + There are ten bits at the beginning of the LISP-gpe header. New + bits are assigned via Standards Action [RFC5226]. + + Bits 0-3 - Assigned by LISP [RFC6830] + Bit 4 - Instance ID (I bit) + Bit 5 - Next Protocol (P bit) + Bit 6 - Reserved + Bit 7 - OAM (O bit) + Bits 8-9 - Version + + + + + + + + + + + + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 13] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + +9. References + +9.1. Normative References + + [RFC0768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, + August 1980. + + [RFC0791] Postel, J., "Internet Protocol", STD 5, RFC 791, + September 1981. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an + IANA Considerations Section in RFCs", BCP 26, RFC 5226, + May 2008. + +9.2. Informative References + + [ETYPES] The IEEE Registration Authority, "IEEE 802 Numbers", 2012, + . + + [IEEE8021Q] + The IEEE Computer Society, "Media Access Control (MAC) + Bridges and Virtual Bridge Local Area Networks", August + 2012, . + + [RFC1700] Reynolds, J. and J. Postel, "Assigned Numbers", RFC 1700, + October 1994. + + [RFC6830] Farinacci, D., Fuller, V., Meyer, D., and D. Lewis, "The + Locator/ID Separation Protocol (LISP)", RFC 6830, + January 2013. + + [RFC6834] Iannone, L., Saucez, D., and O. Bonaventure, "Locator/ID + Separation Protocol (LISP) Map-Versioning", RFC 6834, + January 2013. + + [VXLAN] Dutt, D., Mahalingam, M., Duda, K., Agarwal, P., Kreeger, + L., Sridhar, T., Bursell, M., and C. Wright, "VXLAN: A + Framework for Overlaying Virtualized Layer 2 Networks over + Layer 3 Networks", 2013. + + + + + + + +Lewis, et al. Expires January 5, 2015 [Page 14] + +Internet-Draft LISP Generic Protocol Extension July 2014 + + +Authors' Addresses + + Darrel Lewis + Cisco Systems, Inc. + + Email: darlewis@cisco.com + + + Puneet Agarwal + Broadcom + + Email: pagarwal@broadcom.com + + + Larry Kreeger + Cisco Systems, Inc. + + Email: kreeger@cisco.com + + + Fabio Maino + Cisco Systems, Inc. + + Email: fmaino@cisco.com + + + Paul Quinn + Cisco Systems, Inc. + + Email: paulq@cisco.com + + + Michael Smith + Cisco Systems, Inc. + + Email: michsmit@cisco.com + + + Navindra Yadav + Cisco Systems, Inc. + + Email: nyadav@cisco.com -- cgit 1.2.3-korg