diff options
author | Florin Coras <fcoras@cisco.com> | 2017-01-26 14:25:34 -0800 |
---|---|---|
committer | Neale Ranns <nranns@cisco.com> | 2017-02-13 08:51:30 +0000 |
commit | ce1b4c7f05ce28d7b73eb7ed0a8ea4bd483f09e9 (patch) | |
tree | 5e7f6cab01b2df39d12d3865af7c809916c51d63 | |
parent | fdd81af6afe6c782ad2c1a139210378badec626b (diff) |
Basic support for LISP-GPE encapsulated NSH packets
Change-Id: I97fedb0f70dd18ed9bbe985407cc5fe714e8a2e2
Signed-off-by: Florin Coras <fcoras@cisco.com>
28 files changed, 944 insertions, 22 deletions
diff --git a/src/vnet.am b/src/vnet.am index 9b148f69ffa..a8cc696f383 100644 --- a/src/vnet.am +++ b/src/vnet.am @@ -958,12 +958,14 @@ libvnet_la_SOURCES += \ vnet/adj/adj_midchain.c \ vnet/adj/adj_mcast.c \ vnet/adj/adj_l2.c \ + vnet/adj/adj_nsh.c \ vnet/adj/adj.c nobase_include_HEADERS += \ vnet/adj/adj.h \ vnet/adj/adj_types.h \ vnet/adj/adj_glean.h \ + vnet/adj/adj_nsh.h \ vnet/adj/adj_nbr.h ######################################## diff --git a/src/vnet/adj/adj_internal.h b/src/vnet/adj/adj_internal.h index ece59121e9c..306686255b8 100644 --- a/src/vnet/adj/adj_internal.h +++ b/src/vnet/adj/adj_internal.h @@ -20,7 +20,7 @@ #include <vnet/ip/ip.h> #include <vnet/mpls/mpls.h> #include <vnet/adj/adj_l2.h> - +#include <vnet/adj/adj_nsh.h> /** * big switch to turn on Adjacency debugging @@ -53,6 +53,8 @@ adj_get_rewrite_node (vnet_link_t linkt) return (mpls_output_node.index); case VNET_LINK_ETHERNET: return (adj_l2_rewrite_node.index); + case VNET_LINK_NSH: + return (adj_nsh_rewrite_node.index); case VNET_LINK_ARP: break; } diff --git a/src/vnet/adj/adj_midchain.c b/src/vnet/adj/adj_midchain.c index 8c6ab5aa17b..35cdb003708 100644 --- a/src/vnet/adj/adj_midchain.c +++ b/src/vnet/adj/adj_midchain.c @@ -16,6 +16,7 @@ #include <vnet/adj/adj_nbr.h> #include <vnet/adj/adj_internal.h> #include <vnet/adj/adj_l2.h> +#include <vnet/adj/adj_nsh.h> #include <vnet/adj/adj_midchain.h> #include <vnet/ethernet/arp_packet.h> #include <vnet/dpo/drop_dpo.h> @@ -308,6 +309,18 @@ VNET_FEATURE_INIT (adj_midchain_tx_no_count_ethernet, static) = { .runs_before = VNET_FEATURES ("error-drop"), .feature_index_ptr = &adj_midchain_tx_no_count_feature_node[VNET_LINK_ETHERNET], }; +VNET_FEATURE_INIT (adj_midchain_tx_nsh, static) = { + .arc_name = "nsh-output", + .node_name = "adj-midchain-tx", + .runs_before = VNET_FEATURES ("error-drop"), + .feature_index_ptr = &adj_midchain_tx_feature_node[VNET_LINK_NSH], +}; +VNET_FEATURE_INIT (adj_midchain_tx_no_count_nsh, static) = { + .arc_name = "nsh-output", + .node_name = "adj-midchain-tx-no-count", + .runs_before = VNET_FEATURES ("error-drop"), + .feature_index_ptr = &adj_midchain_tx_no_count_feature_node[VNET_LINK_NSH], +}; static inline u32 adj_get_midchain_node (vnet_link_t link) @@ -321,6 +334,8 @@ adj_get_midchain_node (vnet_link_t link) return (mpls_midchain_node.index); case VNET_LINK_ETHERNET: return (adj_l2_midchain_node.index); + case VNET_LINK_NSH: + return (adj_nsh_midchain_node.index); case VNET_LINK_ARP: break; } @@ -354,6 +369,11 @@ adj_midchain_get_feature_arc_index_for_link_type (const ip_adjacency_t *adj) arc = ethernet_main.output_feature_arc_index; break; } + case VNET_LINK_NSH: + { + arc = nsh_main_dummy.output_feature_arc_index; + break; + } case VNET_LINK_ARP: ASSERT(0); break; diff --git a/src/vnet/adj/adj_nsh.c b/src/vnet/adj/adj_nsh.c new file mode 100644 index 00000000000..9a0f9d8b9d6 --- /dev/null +++ b/src/vnet/adj/adj_nsh.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vnet/vnet.h> +#include <vnet/adj/adj_nsh.h> +#include <vnet/ip/ip.h> + +nsh_main_dummy_t nsh_main_dummy; + +/** + * @brief Trace data for a NSH Midchain + */ +typedef struct adj_nsh_trace_t_ { + /** Adjacency index taken. */ + u32 adj_index; +} adj_nsh_trace_t; + +static u8 * +format_adj_nsh_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 *); + adj_nsh_trace_t * t = va_arg (*args, adj_nsh_trace_t *); + + s = format (s, "adj-idx %d : %U", + t->adj_index, + format_ip_adjacency, t->adj_index, FORMAT_IP_ADJACENCY_NONE); + return s; +} + +typedef enum adj_nsh_rewrite_next_t_ +{ + ADJ_NSH_REWRITE_NEXT_DROP, +} adj_gpe_rewrite_next_t; + +always_inline uword +adj_nsh_rewrite_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_midchain) +{ + u32 * from = vlib_frame_vector_args (frame); + u32 n_left_from, n_left_to_next, * to_next, next_index; + u32 cpu_index = os_get_cpu_number(); + + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + ip_adjacency_t * adj0; + vlib_buffer_t * p0; + char *h0; + u32 pi0, rw_len0, adj_index0, next0 = 0; + u32 tx_sw_if_index0; + + pi0 = to_next[0] = from[0]; + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + p0 = vlib_get_buffer (vm, pi0); + h0 = vlib_buffer_get_current (p0); + + adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX]; + + /* We should never rewrite a pkt using the MISS adjacency */ + ASSERT(adj_index0); + + adj0 = adj_get (adj_index0); + + /* Guess we are only writing on simple IP4 header. */ + vnet_rewrite_one_header(adj0[0], h0, sizeof(ip4_header_t)); + + /* Update packet buffer attributes/set output interface. */ + rw_len0 = adj0[0].rewrite_header.data_bytes; + vnet_buffer(p0)->ip.save_rewrite_length = rw_len0; + + vlib_increment_combined_counter(&adjacency_counters, + cpu_index, + adj_index0, + /* packet increment */ 0, + /* byte increment */ rw_len0); + + /* Check MTU of outgoing interface. */ + if (PREDICT_TRUE((vlib_buffer_length_in_chain (vm, p0) <= + adj0[0].rewrite_header.max_l3_packet_bytes))) + { + /* Don't adjust the buffer for ttl issue; icmp-error node wants + * to see the IP headerr */ + p0->current_data -= rw_len0; + p0->current_length += rw_len0; + tx_sw_if_index0 = adj0[0].rewrite_header.sw_if_index; + + if (is_midchain) + { + adj0->sub_type.midchain.fixup_func(vm, adj0, p0); + } + + vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0; + + /* + * Follow the feature ARC. this will result eventually in + * the midchain-tx node + */ + vnet_feature_arc_start (nsh_main_dummy.output_feature_arc_index, + tx_sw_if_index0, &next0, p0); + } + else + { + /* can't fragment NSH */ + next0 = ADJ_NSH_REWRITE_NEXT_DROP; + } + + if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) + { + adj_nsh_trace_t *tr = vlib_add_trace (vm, node, + p0, sizeof (*tr)); + tr->adj_index = vnet_buffer(p0)->ip.adj_index[VLIB_TX]; + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + pi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +static uword +adj_nsh_rewrite (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return adj_nsh_rewrite_inline (vm, node, frame, 0); +} + +static uword +adj_nsh_midchain (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return adj_nsh_rewrite_inline (vm, node, frame, 1); +} + +VLIB_REGISTER_NODE (adj_nsh_rewrite_node) = { + .function = adj_nsh_rewrite, + .name = "adj-nsh-rewrite", + .vector_size = sizeof (u32), + + .format_trace = format_adj_nsh_trace, + + .n_next_nodes = 1, + .next_nodes = { + [ADJ_NSH_REWRITE_NEXT_DROP] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (adj_nsh_rewrite_node, adj_nsh_rewrite) + +VLIB_REGISTER_NODE (adj_nsh_midchain_node) = { + .function = adj_nsh_midchain, + .name = "adj-nsh-midchain", + .vector_size = sizeof (u32), + + .format_trace = format_adj_nsh_trace, + + .n_next_nodes = 1, + .next_nodes = { + [ADJ_NSH_REWRITE_NEXT_DROP] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (adj_nsh_midchain_node, adj_nsh_midchain) + +/* Built-in ip4 tx feature path definition */ +/* *INDENT-OFF* */ +VNET_FEATURE_ARC_INIT (nsh_output, static) = +{ + .arc_name = "nsh-output", + .start_nodes = VNET_FEATURES ("adj-nsh-midchain"), + .arc_index_ptr = &nsh_main_dummy.output_feature_arc_index, +}; + +VNET_FEATURE_INIT (nsh_tx_drop, static) = +{ + .arc_name = "nsh-output", + .node_name = "error-drop", + .runs_before = 0, /* not before any other features */ +}; +/* *INDENT-ON* */ diff --git a/src/vnet/adj/adj_nsh.h b/src/vnet/adj/adj_nsh.h new file mode 100644 index 00000000000..5501fbb9bdc --- /dev/null +++ b/src/vnet/adj/adj_nsh.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017 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 __ADJ_NSH_H__ +#define __ADJ_NSH_H__ + +#include <vnet/adj/adj.h> + +extern vlib_node_registration_t adj_nsh_midchain_node; +extern vlib_node_registration_t adj_nsh_rewrite_node; + +typedef struct _nsh_main_dummy +{ + u8 output_feature_arc_index; +} nsh_main_dummy_t; + +extern nsh_main_dummy_t nsh_main_dummy; + +#endif diff --git a/src/vnet/dpo/dpo.c b/src/vnet/dpo/dpo.c index cc2fa0eb91c..d8e075a7f2f 100644 --- a/src/vnet/dpo/dpo.c +++ b/src/vnet/dpo/dpo.c @@ -98,6 +98,8 @@ vnet_link_to_dpo_proto (vnet_link_t linkt) return (DPO_PROTO_MPLS); case VNET_LINK_ETHERNET: return (DPO_PROTO_ETHERNET); + case VNET_LINK_NSH: + return (DPO_PROTO_NSH); case VNET_LINK_ARP: break; } diff --git a/src/vnet/dpo/dpo.h b/src/vnet/dpo/dpo.h index aff4e1b82cc..48b92d3d3d6 100644 --- a/src/vnet/dpo/dpo.h +++ b/src/vnet/dpo/dpo.h @@ -67,9 +67,10 @@ typedef enum dpo_proto_t_ DPO_PROTO_IP6, DPO_PROTO_ETHERNET, DPO_PROTO_MPLS, + DPO_PROTO_NSH, } __attribute__((packed)) dpo_proto_t; -#define DPO_PROTO_NUM ((dpo_proto_t)(DPO_PROTO_MPLS+1)) +#define DPO_PROTO_NUM ((dpo_proto_t)(DPO_PROTO_NSH+1)) #define DPO_PROTO_NONE ((dpo_proto_t)(DPO_PROTO_NUM+1)) #define DPO_PROTOS { \ @@ -77,11 +78,12 @@ typedef enum dpo_proto_t_ [DPO_PROTO_IP6] = "ip6", \ [DPO_PROTO_ETHERNET] = "ethernet", \ [DPO_PROTO_MPLS] = "mpls", \ + [DPO_PROTO_NSH] = "nsh", \ } #define FOR_EACH_DPO_PROTO(_proto) \ for (_proto = DPO_PROTO_IP4; \ - _proto <= DPO_PROTO_MPLS; \ + _proto <= DPO_PROTO_NSH; \ _proto++) /** diff --git a/src/vnet/fib/fib_entry_delegate.c b/src/vnet/fib/fib_entry_delegate.c index efe402d1482..70840b160a1 100644 --- a/src/vnet/fib/fib_entry_delegate.c +++ b/src/vnet/fib/fib_entry_delegate.c @@ -122,6 +122,8 @@ fib_entry_chain_type_to_delegate_type (fib_forward_chain_type_t fct) case FIB_FORW_CHAIN_TYPE_MCAST_IP4: case FIB_FORW_CHAIN_TYPE_MCAST_IP6: break; + case FIB_FORW_CHAIN_TYPE_NSH: + return (FIB_ENTRY_DELEGATE_CHAIN_NSH); } ASSERT(0); return (FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4); @@ -142,6 +144,8 @@ fib_entry_delegate_type_to_chain_type (fib_entry_delegate_type_t fdt) return (FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS); case FIB_ENTRY_DELEGATE_CHAIN_ETHERNET: return (FIB_FORW_CHAIN_TYPE_ETHERNET); + case FIB_ENTRY_DELEGATE_CHAIN_NSH: + return (FIB_FORW_CHAIN_TYPE_NSH); case FIB_ENTRY_DELEGATE_COVERED: case FIB_ENTRY_DELEGATE_ATTACHED_IMPORT: case FIB_ENTRY_DELEGATE_ATTACHED_EXPORT: diff --git a/src/vnet/fib/fib_entry_delegate.h b/src/vnet/fib/fib_entry_delegate.h index 6d3a6549f32..d9183c5f181 100644 --- a/src/vnet/fib/fib_entry_delegate.h +++ b/src/vnet/fib/fib_entry_delegate.h @@ -35,6 +35,7 @@ typedef enum fib_entry_delegate_type_t_ { FIB_ENTRY_DELEGATE_CHAIN_MPLS_EOS = FIB_FORW_CHAIN_TYPE_MPLS_EOS, FIB_ENTRY_DELEGATE_CHAIN_MPLS_NON_EOS = FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS, FIB_ENTRY_DELEGATE_CHAIN_ETHERNET = FIB_FORW_CHAIN_TYPE_ETHERNET, + FIB_ENTRY_DELEGATE_CHAIN_NSH = FIB_FORW_CHAIN_TYPE_NSH, /** * Dependency list of covered entries. * these are more specific entries that are interested in changes @@ -51,7 +52,7 @@ typedef enum fib_entry_delegate_type_t_ { #define FOR_EACH_DELEGATE_CHAIN(_entry, _fdt, _fed, _body) \ { \ for (_fdt = FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4; \ - _fdt <= FIB_ENTRY_DELEGATE_CHAIN_ETHERNET; \ + _fdt <= FIB_ENTRY_DELEGATE_CHAIN_NSH; \ _fdt++) \ { \ _fed = fib_entry_delegate_get(_entry, _fdt); \ diff --git a/src/vnet/fib/fib_entry_src.c b/src/vnet/fib/fib_entry_src.c index d54787cd4d1..57109153b57 100644 --- a/src/vnet/fib/fib_entry_src.c +++ b/src/vnet/fib/fib_entry_src.c @@ -355,6 +355,7 @@ fib_entry_src_collect_forwarding (fib_node_index_t pl_index, break; } case FIB_FORW_CHAIN_TYPE_ETHERNET: + case FIB_FORW_CHAIN_TYPE_NSH: ASSERT(0); break; } diff --git a/src/vnet/fib/fib_path.c b/src/vnet/fib/fib_path.c index 080057f37a9..aa545b5ed45 100644 --- a/src/vnet/fib/fib_path.c +++ b/src/vnet/fib/fib_path.c @@ -1755,6 +1755,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index, case FIB_FORW_CHAIN_TYPE_MPLS_EOS: case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS: case FIB_FORW_CHAIN_TYPE_ETHERNET: + case FIB_FORW_CHAIN_TYPE_NSH: { adj_index_t ai; @@ -1787,6 +1788,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index, case FIB_FORW_CHAIN_TYPE_MCAST_IP4: case FIB_FORW_CHAIN_TYPE_MCAST_IP6: case FIB_FORW_CHAIN_TYPE_ETHERNET: + case FIB_FORW_CHAIN_TYPE_NSH: ASSERT(0); break; } @@ -1809,6 +1811,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index, case FIB_FORW_CHAIN_TYPE_MCAST_IP4: case FIB_FORW_CHAIN_TYPE_MCAST_IP6: case FIB_FORW_CHAIN_TYPE_ETHERNET: + case FIB_FORW_CHAIN_TYPE_NSH: ASSERT(0); break; } @@ -1824,6 +1827,7 @@ fib_path_contribute_forwarding (fib_node_index_t path_index, case FIB_FORW_CHAIN_TYPE_UNICAST_IP6: case FIB_FORW_CHAIN_TYPE_MPLS_EOS: case FIB_FORW_CHAIN_TYPE_ETHERNET: + case FIB_FORW_CHAIN_TYPE_NSH: break; case FIB_FORW_CHAIN_TYPE_MCAST_IP4: case FIB_FORW_CHAIN_TYPE_MCAST_IP6: diff --git a/src/vnet/fib/fib_types.c b/src/vnet/fib/fib_types.c index 3ecb38e8b6c..2837a59db01 100644 --- a/src/vnet/fib/fib_types.c +++ b/src/vnet/fib/fib_types.c @@ -279,6 +279,8 @@ fib_forw_chain_type_from_dpo_proto (dpo_proto_t proto) return (FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS); case DPO_PROTO_ETHERNET: return (FIB_FORW_CHAIN_TYPE_ETHERNET); + case DPO_PROTO_NSH: + return (FIB_FORW_CHAIN_TYPE_NSH); } ASSERT(0); return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4); @@ -297,6 +299,8 @@ fib_forw_chain_type_to_link_type (fib_forward_chain_type_t fct) return (VNET_LINK_IP6); case FIB_FORW_CHAIN_TYPE_ETHERNET: return (VNET_LINK_ETHERNET); + case FIB_FORW_CHAIN_TYPE_NSH: + return (VNET_LINK_NSH); case FIB_FORW_CHAIN_TYPE_MPLS_EOS: /* * insufficient information to to convert @@ -322,6 +326,8 @@ fib_forw_chain_type_to_dpo_proto (fib_forward_chain_type_t fct) return (DPO_PROTO_IP6); case FIB_FORW_CHAIN_TYPE_ETHERNET: return (DPO_PROTO_ETHERNET); + case FIB_FORW_CHAIN_TYPE_NSH: + return (DPO_PROTO_NSH); case FIB_FORW_CHAIN_TYPE_MPLS_EOS: case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS: return (DPO_PROTO_MPLS); diff --git a/src/vnet/fib/fib_types.h b/src/vnet/fib/fib_types.h index c51bc9c0722..05e0e0af4c9 100644 --- a/src/vnet/fib/fib_types.h +++ b/src/vnet/fib/fib_types.h @@ -105,10 +105,14 @@ typedef enum fib_forward_chain_type_t_ { FIB_FORW_CHAIN_TYPE_MCAST_IP6, /** * Contribute an object that is to be used to forward Ethernet packets. + */ + FIB_FORW_CHAIN_TYPE_ETHERNET, + /** + * Contribute an object that is to be used to forward NSH packets. * This is last in the list since it is not valid for many FIB objects, * and thus their array of per-chain-type DPOs can be sized smaller. */ - FIB_FORW_CHAIN_TYPE_ETHERNET, + FIB_FORW_CHAIN_TYPE_NSH, } __attribute__ ((packed)) fib_forward_chain_type_t; #define FIB_FORW_CHAINS { \ @@ -119,14 +123,15 @@ typedef enum fib_forward_chain_type_t_ { [FIB_FORW_CHAIN_TYPE_MCAST_IP6] = "multicast-ip6", \ [FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS] = "mpls-neos", \ [FIB_FORW_CHAIN_TYPE_MPLS_EOS] = "mpls-eos", \ + [FIB_FORW_CHAIN_TYPE_NSH] = "nsh", \ } -#define FIB_FORW_CHAIN_NUM (FIB_FORW_CHAIN_TYPE_MPLS_ETHERNET+1) +#define FIB_FORW_CHAIN_NUM (FIB_FORW_CHAIN_TYPE_NSH+1) #define FIB_FORW_CHAIN_MPLS_NUM (FIB_FORW_CHAIN_TYPE_MPLS_EOS+1) #define FOR_EACH_FIB_FORW_CHAIN(_item) \ for (_item = FIB_FORW_CHAIN_TYPE_UNICAST_IP4; \ - _item <= FIB_FORW_CHAIN_TYPE_ETHERNET; \ + _item <= FIB_FORW_CHAIN_TYPE_NSH; \ _item++) #define FOR_EACH_FIB_FORW_MPLS_CHAIN(_item) \ diff --git a/src/vnet/gre/gre.c b/src/vnet/gre/gre.c index 0faed13eb29..cd43a3afce9 100644 --- a/src/vnet/gre/gre.c +++ b/src/vnet/gre/gre.c @@ -177,6 +177,9 @@ gre_proto_from_vnet_link (vnet_link_t link) return (GRE_PROTOCOL_teb); case VNET_LINK_ARP: return (GRE_PROTOCOL_arp); + case VNET_LINK_NSH: + ASSERT(0); + break; } ASSERT(0); return (GRE_PROTOCOL_ip4); diff --git a/src/vnet/interface.c b/src/vnet/interface.c index 9454ac183b1..2a1e70e8927 100644 --- a/src/vnet/interface.c +++ b/src/vnet/interface.c @@ -1364,6 +1364,7 @@ vnet_link_to_l3_proto (vnet_link_t link) case VNET_LINK_ARP: return (VNET_L3_PACKET_TYPE_ARP); case VNET_LINK_ETHERNET: + case VNET_LINK_NSH: ASSERT (0); break; } diff --git a/src/vnet/interface.h b/src/vnet/interface.h index d42e5fda84c..7b791751392 100644 --- a/src/vnet/interface.h +++ b/src/vnet/interface.h @@ -240,6 +240,7 @@ typedef enum vnet_link_t_ VNET_LINK_MPLS, VNET_LINK_ETHERNET, VNET_LINK_ARP, + VNET_LINK_NSH, } __attribute__ ((packed)) vnet_link_t; #define VNET_LINKS { \ @@ -248,13 +249,14 @@ typedef enum vnet_link_t_ [VNET_LINK_IP6] = "ipv6", \ [VNET_LINK_MPLS] = "mpls", \ [VNET_LINK_ARP] = "arp", \ + [VNET_LINK_NSH] = "nsh", \ } /** * @brief Number of link types. Not part of the enum so it does not have to be included in * switch statements */ -#define VNET_LINK_NUM (VNET_LINK_ARP+1) +#define VNET_LINK_NUM (VNET_LINK_NSH+1) /** * @brief Convert a link to to an Ethertype diff --git a/src/vnet/lisp-cp/control.c b/src/vnet/lisp-cp/control.c index cc73dfc5f02..f0383e16f05 100644 --- a/src/vnet/lisp-cp/control.c +++ b/src/vnet/lisp-cp/control.c @@ -2700,6 +2700,11 @@ get_src_and_dst_eids_from_buffer (lisp_cp_main_t * lcm, vlib_buffer_t * b, gid_address_vni (dst) = vni; gid_address_vni (src) = vni; } + else if (LISP_AFI_LCAF == type) + { + /* Eventually extend this to support NSH and other */ + ASSERT (0); + } } static uword @@ -2818,6 +2823,14 @@ lisp_cp_lookup_l2 (vlib_main_t * vm, return (lisp_cp_lookup_inline (vm, node, from_frame, LISP_AFI_MAC)); } +static uword +lisp_cp_lookup_nsh (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * from_frame) +{ + /* TODO decide if NSH should be propagated as LCAF or not */ + return (lisp_cp_lookup_inline (vm, node, from_frame, LISP_AFI_LCAF)); +} + /* *INDENT-OFF* */ VLIB_REGISTER_NODE (lisp_cp_lookup_ip4_node) = { .function = lisp_cp_lookup_ip4, @@ -2875,6 +2888,25 @@ VLIB_REGISTER_NODE (lisp_cp_lookup_l2_node) = { }; /* *INDENT-ON* */ +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (lisp_cp_lookup_nsh_node) = { + .function = lisp_cp_lookup_nsh, + .name = "lisp-cp-lookup-nsh", + .vector_size = sizeof (u32), + .format_trace = format_lisp_cp_lookup_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = LISP_CP_LOOKUP_N_ERROR, + .error_strings = lisp_cp_lookup_error_strings, + + .n_next_nodes = LISP_CP_LOOKUP_N_NEXT, + + .next_nodes = { + [LISP_CP_LOOKUP_NEXT_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + /* lisp_cp_input statistics */ #define foreach_lisp_cp_input_error \ _(DROP, "drop") \ diff --git a/src/vnet/lisp-cp/lisp_api.c b/src/vnet/lisp-cp/lisp_api.c index a877540b3ed..78d32e17aa6 100644 --- a/src/vnet/lisp-cp/lisp_api.c +++ b/src/vnet/lisp-cp/lisp_api.c @@ -714,6 +714,8 @@ fid_type_to_api_type (fid_address_t * fid) case FID_ADDR_MAC: return 2; + case FID_ADDR_NSH: + return 3; } return ~0; diff --git a/src/vnet/lisp-cp/lisp_cp_dpo.c b/src/vnet/lisp-cp/lisp_cp_dpo.c index 185b07a2c1b..848f621e35f 100644 --- a/src/vnet/lisp-cp/lisp_cp_dpo.c +++ b/src/vnet/lisp-cp/lisp_cp_dpo.c @@ -79,12 +79,17 @@ const static char *const lisp_cp_ethernet_nodes[] = { NULL, }; +const static char *const lisp_cp_nsh_nodes[] = { + "lisp-cp-lookup-nsh", + NULL, +}; const static char *const *const lisp_cp_nodes[DPO_PROTO_NUM] = { [DPO_PROTO_IP4] = lisp_cp_ip4_nodes, [DPO_PROTO_IP6] = lisp_cp_ip6_nodes, [DPO_PROTO_ETHERNET] = lisp_cp_ethernet_nodes, [DPO_PROTO_MPLS] = NULL, + [DPO_PROTO_NSH] = lisp_cp_nsh_nodes, }; clib_error_t * diff --git a/src/vnet/lisp-cp/lisp_types.c b/src/vnet/lisp-cp/lisp_types.c index 748905d2fa4..4a3d05b70ed 100644 --- a/src/vnet/lisp-cp/lisp_types.c +++ b/src/vnet/lisp-cp/lisp_types.c @@ -202,6 +202,20 @@ format_mac_address (u8 * s, va_list * args) a[0], a[1], a[2], a[3], a[4], a[5]); } +uword +unformat_nsh_address (unformat_input_t * input, va_list * args) +{ + nsh_t *a = va_arg (*args, nsh_t *); + return unformat (input, "SPI:%d SI:%d", &a->spi, &a->si); +} + +u8 * +format_nsh_address (u8 * s, va_list * args) +{ + nsh_t *a = va_arg (*args, nsh_t *); + return format (s, "SPI:%d SI:%d", a->spi, a->si); +} + u8 * format_fid_address (u8 * s, va_list * args) { @@ -211,9 +225,10 @@ format_fid_address (u8 * s, va_list * args) { case FID_ADDR_IP_PREF: return format (s, "%U", format_ip_prefix, &fid_addr_ippref (a)); - case FID_ADDR_MAC: return format (s, "%U", format_mac_address, &fid_addr_mac (a)); + case FID_ADDR_NSH: + return format (s, "%U", format_nsh_address, &fid_addr_nsh (a)); default: clib_warning ("Can't format fid address type %d!", fid_addr_type (a)); @@ -239,6 +254,8 @@ format_gid_address (u8 * s, va_list * args) case GID_ADDR_MAC: return format (s, "[%d] %U", gid_address_vni (a), format_mac_address, &gid_address_mac (a)); + case GID_ADDR_NSH: + return format (s, "%U", format_nsh_address, &gid_address_nsh (a)); default: clib_warning ("Can't format gid type %d", type); return 0; @@ -252,6 +269,7 @@ unformat_fid_address (unformat_input_t * i, va_list * args) fid_address_t *a = va_arg (*args, fid_address_t *); ip_prefix_t ippref; u8 mac[6] = { 0 }; + nsh_t nsh; if (unformat (i, "%U", unformat_ip_prefix, &ippref)) { @@ -263,6 +281,11 @@ unformat_fid_address (unformat_input_t * i, va_list * args) fid_addr_type (a) = FID_ADDR_MAC; mac_copy (fid_addr_mac (a), mac); } + else if (unformat (i, "%U", unformat_nsh_address, &nsh)) + { + fid_addr_type (a) = FID_ADDR_NSH; + nsh_copy (&fid_addr_nsh (a), mac); + } else return 0; @@ -301,6 +324,7 @@ unformat_gid_address (unformat_input_t * input, va_list * args) u8 mac[6] = { 0 }; ip_prefix_t ippref; fid_address_t sim1, sim2; + nsh_t nsh; memset (&ippref, 0, sizeof (ippref)); memset (&sim1, 0, sizeof (sim1)); @@ -323,6 +347,11 @@ unformat_gid_address (unformat_input_t * input, va_list * args) mac_copy (gid_address_mac (a), mac); gid_address_type (a) = GID_ADDR_MAC; } + else if (unformat (input, "%U", unformat_nsh_address, &nsh)) + { + nsh_copy (&gid_address_nsh (a), &nsh); + gid_address_type (a) = GID_ADDR_NSH; + } else return 0; @@ -588,6 +617,10 @@ fid_addr_parse (u8 * p, fid_address_t * a) case FID_ADDR_IP_PREF: return ip_address_parse (p, afi, ip_addr); + + case FID_ADDR_NSH: + ASSERT (0); + break; } return ~0; } @@ -918,6 +951,12 @@ mac_copy (void *dst, void *src) } void +nsh_copy (void *dst, void *src) +{ + clib_memcpy (dst, src, sizeof (nsh_t)); +} + +void sd_copy (void *dst, void *src) { clib_memcpy (dst, src, sizeof (source_dest_t)); @@ -1083,6 +1122,8 @@ fid_address_length (fid_address_t * a) return ip_prefix_length (&fid_addr_ippref (a)); case FID_ADDR_MAC: return 0; + case FID_ADDR_NSH: + return 0; } return 0; } diff --git a/src/vnet/lisp-cp/lisp_types.h b/src/vnet/lisp-cp/lisp_types.h index ac58b894c2d..e43f5ab030e 100644 --- a/src/vnet/lisp-cp/lisp_types.h +++ b/src/vnet/lisp-cp/lisp_types.h @@ -89,6 +89,7 @@ typedef enum GID_ADDR_LCAF, GID_ADDR_MAC, GID_ADDR_SRC_DST, + GID_ADDR_NSH, GID_ADDR_NO_ADDRESS, GID_ADDR_TYPES } gid_address_type_t; @@ -106,7 +107,8 @@ typedef enum typedef enum fid_addr_type_t_ { FID_ADDR_IP_PREF, - FID_ADDR_MAC + FID_ADDR_MAC, + FID_ADDR_NSH } __attribute__ ((packed)) fid_addr_type_t; /* flat address type */ @@ -116,6 +118,7 @@ typedef struct { ip_prefix_t ippref; u8 mac[6]; + u32 nsh; }; fid_addr_type_t type; } fid_address_t; @@ -124,6 +127,7 @@ typedef fid_address_t dp_address_t; #define fid_addr_ippref(_a) (_a)->ippref #define fid_addr_mac(_a) (_a)->mac +#define fid_addr_nsh(_a) (_a)->nsh #define fid_addr_type(_a) (_a)->type u8 *format_fid_address (u8 * s, va_list * args); @@ -155,6 +159,12 @@ typedef struct typedef struct { + u32 spi; + u8 si; +} nsh_t; + +typedef struct +{ /* the union needs to be at the beginning! */ union { @@ -177,6 +187,7 @@ typedef struct _gid_address_t lcaf_t lcaf; u8 mac[6]; source_dest_t sd; + nsh_t nsh; }; u8 type; u32 vni; @@ -232,6 +243,7 @@ void gid_address_ip_set (gid_address_t * dst, void *src, u8 version); #define gid_address_ip_version(_a) ip_addr_version(&gid_address_ip(_a)) #define gid_address_lcaf(_a) (_a)->lcaf #define gid_address_mac(_a) (_a)->mac +#define gid_address_nsh(_a) (_a)->nsh #define gid_address_vni(_a) (_a)->vni #define gid_address_vni_mask(_a) (_a)->vni_mask #define gid_address_sd_dst_ippref(_a) sd_dst_ippref(&(_a)->sd) @@ -249,6 +261,7 @@ void gid_address_ip_set (gid_address_t * dst, void *src, u8 version); _(ip_prefix) \ _(lcaf) \ _(mac) \ + _(nsh) \ _(sd) /* *INDENT-OFF* */ diff --git a/src/vnet/lisp-gpe/interface.c b/src/vnet/lisp-gpe/interface.c index 3288b2414b7..d12dc362dac 100644 --- a/src/vnet/lisp-gpe/interface.c +++ b/src/vnet/lisp-gpe/interface.c @@ -201,7 +201,7 @@ VNET_HW_INTERFACE_CLASS (lisp_gpe_hw_class) = { typedef struct { - u32 lb_index; + u32 dpo_index; } l2_lisp_gpe_tx_trace_t; static u8 * @@ -211,7 +211,7 @@ format_l2_lisp_gpe_tx_trace (u8 * s, va_list * args) 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); + s = format (s, "L2-LISP-GPE-TX: load-balance %d", t->dpo_index); return s; } @@ -278,7 +278,7 @@ l2_lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node, { l2_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); - tr->lb_index = lbi0; + tr->dpo_index = lbi0; } vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, bi0, l2_arc_to_lb); @@ -306,6 +306,110 @@ VNET_DEVICE_CLASS (l2_lisp_gpe_device_class,static) = { }; /* *INDENT-ON* */ +typedef struct +{ + u32 dpo_index; +} nsh_lisp_gpe_tx_trace_t; + +u8 * +format_nsh_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 *); + nsh_lisp_gpe_tx_trace_t *t = va_arg (*args, nsh_lisp_gpe_tx_trace_t *); + + s = format (s, "NSH-GPE-TX: tunnel %d", t->dpo_index); + return s; +} + +/** + * @brief LISP-GPE interface TX for NSH overlays. + * @node nsh_lisp_gpe_interface_tx + * + * The NSH LISP-GPE interface TX function. + * + * @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 +nsh_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; + u32 *nsh0, next0; + const dpo_id_t *dpo0; + + 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); + nsh0 = vlib_buffer_get_current (b0); + + vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_LCAF; + + /* lookup SPI + SI (second word of the NSH header). + * NB: Load balancing was done by the control plane */ + dpo0 = lisp_nsh_fib_lookup (lgm, nsh0[1]); + + 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)) + { + nsh_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0, + sizeof (*tr)); + tr->dpo_index = dpo0->dpoi_index; + } + 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_nsh_lisp_gpe_name (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + return format (s, "nsh_lisp_gpe%d", dev_instance); +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (nsh_lisp_gpe_device_class,static) = { + .name = "NSH_LISP_GPE", + .format_device_name = format_nsh_lisp_gpe_name, + .format_tx_trace = format_nsh_lisp_gpe_tx_trace, + .tx_function = nsh_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, @@ -615,6 +719,72 @@ lisp_gpe_del_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id) lisp_gpe_remove_iface (lgm, hip[0], bd_index, &lgm->l2_ifaces); } +/** + * @brief Add LISP-GPE NSH interface. + * + * Creates LISP-GPE interface, sets it in L3 mode. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] a Parameters to create interface. + * + * @return sw_if_index. + */ +u32 +lisp_gpe_add_nsh_iface (lisp_gpe_main_t * lgm) +{ + vnet_main_t *vnm = lgm->vnet_main; + tunnel_lookup_t *nsh_ifaces = &lgm->nsh_ifaces; + vnet_hw_interface_t *hi; + uword *hip, *si; + + hip = hash_get (nsh_ifaces->hw_if_index_by_dp_table, 0); + + if (hip) + { + clib_warning ("NSH interface 0 already exists"); + return ~0; + } + + si = hash_get (nsh_ifaces->sw_if_index_by_vni, 0); + if (si) + { + clib_warning ("NSH interface already exists"); + return ~0; + } + + /* create lisp iface and populate tunnel tables */ + hi = lisp_gpe_create_iface (lgm, 0, 0, + &nsh_lisp_gpe_device_class, &lgm->nsh_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); + + return (hi->sw_if_index); +} + +/** + * @brief Del LISP-GPE NSH interface. + * + */ +void +lisp_gpe_del_nsh_iface (lisp_gpe_main_t * lgm) +{ + tunnel_lookup_t *nsh_ifaces = &lgm->nsh_ifaces; + uword *hip; + + hip = hash_get (nsh_ifaces->hw_if_index_by_dp_table, 0); + + if (hip == 0) + { + clib_warning ("The NSH 0 interface doesn't exist"); + return; + } + lisp_gpe_remove_iface (lgm, hip[0], 0, &lgm->nsh_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) @@ -623,6 +793,7 @@ lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * 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; + u8 nsh_iface = 0; if (vnet_lisp_gpe_enable_disable_status () == 0) { @@ -651,6 +822,10 @@ lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input, { bd_index_is_set = 1; } + else if (unformat (line_input, "nsh")) + { + nsh_iface = 1; + } else { return clib_error_return (0, "parse error: '%U'", @@ -689,6 +864,21 @@ lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input, lisp_gpe_tenant_l3_iface_unlock (vni); } + if (nsh_iface) + { + if (is_add) + { + if (~0 == lisp_gpe_add_nsh_iface (&lisp_gpe_main)) + { + return clib_error_return (0, "NSH interface not created"); + } + else + { + lisp_gpe_del_nsh_iface (&lisp_gpe_main); + } + } + } + return (NULL); } diff --git a/src/vnet/lisp-gpe/lisp_gpe.c b/src/vnet/lisp-gpe/lisp_gpe.c index e78d45c9020..e76c03f0033 100644 --- a/src/vnet/lisp-gpe/lisp_gpe.c +++ b/src/vnet/lisp-gpe/lisp_gpe.c @@ -151,6 +151,7 @@ lisp_gpe_add_del_fwd_entry_command_fn (vlib_main_t * vm, gid_address_copy (&a->lcl_eid, leid); gid_address_copy (&a->rmt_eid, reid); a->locator_pairs = pairs; + a->action = action; rv = vnet_lisp_gpe_add_del_fwd_entry (a, 0); if (0 != rv) @@ -291,7 +292,6 @@ format_vnet_lisp_gpe_status (u8 * s, va_list * args) return format (s, "%s", lgm->is_en ? "enabled" : "disabled"); } - /** LISP-GPE init function. */ clib_error_t * lisp_gpe_init (vlib_main_t * vm) diff --git a/src/vnet/lisp-gpe/lisp_gpe.h b/src/vnet/lisp-gpe/lisp_gpe.h index 3288c99ffee..e92df38542c 100644 --- a/src/vnet/lisp-gpe/lisp_gpe.h +++ b/src/vnet/lisp-gpe/lisp_gpe.h @@ -119,6 +119,15 @@ typedef struct lisp_gpe_main /** Load-balance for a miss in the table */ dpo_id_t l2_lb_cp_lkup; + /* NSH data structures + * ================== */ + + BVT (clib_bihash) nsh_fib; + + tunnel_lookup_t nsh_ifaces; + + const dpo_id_t *nsh_cp_lkup; + /** convenience */ vlib_main_t *vlib_main; vnet_main_t *vnet_main; diff --git a/src/vnet/lisp-gpe/lisp_gpe_adjacency.c b/src/vnet/lisp-gpe/lisp_gpe_adjacency.c index 8c96a25cc5d..1dbf8677f71 100644 --- a/src/vnet/lisp-gpe/lisp_gpe_adjacency.c +++ b/src/vnet/lisp-gpe/lisp_gpe_adjacency.c @@ -211,6 +211,8 @@ lisp_gpe_adj_proto_from_vnet_link_type (vnet_link_t linkt) return (LISP_GPE_NEXT_PROTO_IP6); case VNET_LINK_ETHERNET: return (LISP_GPE_NEXT_PROTO_ETHERNET); + case VNET_LINK_NSH: + return (LISP_GPE_NEXT_PROTO_NSH); default: ASSERT (0); } @@ -254,14 +256,14 @@ lisp_gpe_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai) 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_tunnel_build_rewrite (lgt, ladj, + lisp_gpe_adj_proto_from_vnet_link_type + (linkt))); lisp_gpe_adj_stack_one (ladj, ai); } diff --git a/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.c b/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.c index 7ad8679e8c0..e51b585e1be 100644 --- a/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.c +++ b/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.c @@ -340,10 +340,14 @@ gid_to_dp_address (gid_address_t * g, dp_address_t * d) 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; + case GID_ADDR_NSH: + default: + d->nsh = gid_address_nsh (g).spi << 8 | gid_address_nsh (g).si; + d->type = FID_ADDR_NSH; + break; } } @@ -671,7 +675,7 @@ del_l2_fwd_entry (lisp_gpe_main_t * lgm, } /** - * @brief Construct and insert the forwarding information used by a L2 entry + * @brief Construct and insert the forwarding information used by an L2 entry */ static void lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_t * lfe) @@ -688,7 +692,16 @@ lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_t * lfe) } else { - dpo_copy (&dpo, &lgm->l2_lb_cp_lkup); + switch (lfe->action) + { + case SEND_MAP_REQUEST: + dpo_copy (&dpo, &lgm->l2_lb_cp_lkup); + break; + case NO_ACTION: + case FORWARD_NATIVE: + case DROP: + dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_ETHERNET)); + } } /* add entry to l2 lisp fib */ @@ -785,6 +798,276 @@ add_l2_fwd_entry (lisp_gpe_main_t * lgm, } /** + * @brief Lookup NSH SD FIB entry + * + * Does an SPI+SI lookup in the NSH LISP FIB. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] spi_si SPI + SI. + * + * @return next node index. + */ +const dpo_id_t * +lisp_nsh_fib_lookup (lisp_gpe_main_t * lgm, u32 spi_si) +{ + int rv; + BVT (clib_bihash_kv) kv, value; + + memset (&kv, 0, sizeof (kv)); + kv.key[0] = spi_si; + rv = BV (clib_bihash_search_inline_2) (&lgm->nsh_fib, &kv, &value); + + if (rv != 0) + { + return lgm->nsh_cp_lkup; + } + else + { + lisp_gpe_fwd_entry_t *lfe; + lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, value.value); + return &lfe->nsh.choice; + } +} + +/** + * @brief Add/del NSH FIB entry + * + * Inserts value in NSH FIB keyed by SPI+SI. If entry is + * overwritten the associated value is returned. + * + * @param[in] lgm Reference to @ref lisp_gpe_main_t. + * @param[in] spi_si SPI + SI. + * @param[in] dpo Load balanced mapped to SPI + SI + * + * @return ~0 or value of overwritten entry. + */ +static u32 +lisp_nsh_fib_add_del_entry (u32 spi_si, u32 lfei, u8 is_add) +{ + lisp_gpe_main_t *lgm = &lisp_gpe_main; + BVT (clib_bihash_kv) kv, value; + u32 old_val = ~0; + + memset (&kv, 0, sizeof (kv)); + kv.key[0] = spi_si; + kv.value = 0ULL; + + if (BV (clib_bihash_search) (&lgm->nsh_fib, &kv, &value) == 0) + old_val = value.value; + + if (!is_add) + BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 0 /* is_add */ ); + else + { + kv.value = lfei; + BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 1 /* is_add */ ); + } + return old_val; +} + +#define NSH_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024) +#define NSH_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20) + +static void +nsh_fib_init (lisp_gpe_main_t * lgm) +{ + BV (clib_bihash_init) (&lgm->nsh_fib, "nsh fib", + 1 << max_log2 (NSH_FIB_DEFAULT_HASH_NUM_BUCKETS), + NSH_FIB_DEFAULT_HASH_MEMORY_SIZE); + + /* + * the result from a 'miss' in a NSH Table + */ + lgm->nsh_cp_lkup = lisp_cp_dpo_get (DPO_PROTO_NSH); +} + +static void +del_nsh_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->nsh.path_list_index, + lfe->nsh.child_index); + dpo_reset (&lfe->nsh.choice); + } + + lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt), (u32) ~ 0, 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 NSH forwarding entry. + * + * Coordinates the removal of forwarding entries for NSH 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_nsh_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_nsh_fwd_entry_i (lgm, lfe); + + return (0); +} + +/** + * @brief Construct and insert the forwarding information used by an NSH entry + */ +static void +lisp_gpe_nsh_update_fwding (lisp_gpe_fwd_entry_t * lfe) +{ + lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); + dpo_id_t dpo = DPO_INVALID; + vnet_hw_interface_t *hi; + uword *hip; + + if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) + { + fib_path_list_contribute_forwarding (lfe->nsh.path_list_index, + FIB_FORW_CHAIN_TYPE_NSH, + &lfe->nsh.dpo); + + /* + * LISP encap is always the same for this SPI+SI so we do that hash now + * and stack on the choice. + */ + if (DPO_LOAD_BALANCE == lfe->nsh.dpo.dpoi_type) + { + const dpo_id_t *tmp; + const load_balance_t *lb; + int hash; + + lb = load_balance_get (lfe->nsh.dpo.dpoi_index); + hash = fid_addr_nsh (&lfe->key->rmt) % lb->lb_n_buckets; + tmp = + load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1); + + dpo_copy (&dpo, tmp); + } + } + else + { + switch (lfe->action) + { + case SEND_MAP_REQUEST: + dpo_copy (&dpo, lgm->nsh_cp_lkup); + break; + case NO_ACTION: + case FORWARD_NATIVE: + case DROP: + dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_NSH)); + } + } + + /* We have only one nsh-lisp interface (no NSH virtualization) */ + hip = hash_get (lgm->nsh_ifaces.hw_if_index_by_dp_table, 0); + hi = vnet_get_hw_interface (lgm->vnet_main, hip[0]); + + dpo_stack_from_node (hi->tx_node_index, &lfe->nsh.choice, &dpo); + + /* add entry to nsh lisp fib */ + lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt), + lfe - lgm->lisp_fwd_entry_pool, 1); + + dpo_reset (&dpo); +} + +/** + * @brief Add LISP NSH 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_nsh_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) + /* 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->tenant = 0; + + 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->nsh.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->nsh.child_index = + fib_path_list_child_add (lfe->nsh.path_list_index, + FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, + lfe - lgm->lisp_fwd_entry_pool); + } + else + { + lfe->action = a->action; + } + + lisp_gpe_nsh_update_fwding (lfe); + + return 0; +} + +/** * @brief conver from the embedded fib_node_t struct to the LSIP entry */ static lisp_gpe_fwd_entry_t * @@ -802,7 +1085,12 @@ 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)); + lisp_gpe_fwd_entry_t *lfe = lisp_gpe_fwd_entry_from_fib_node (node); + + if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_MAC) + lisp_gpe_l2_update_fwding (lfe); + else if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_NSH) + lisp_gpe_nsh_update_fwding (lfe); return (FIB_NODE_BACK_WALK_CONTINUE); } @@ -877,6 +1165,11 @@ vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, return add_l2_fwd_entry (lgm, a); else return del_l2_fwd_entry (lgm, a); + case GID_ADDR_NSH: + if (a->is_add) + return add_nsh_fwd_entry (lgm, a); + else + return del_nsh_fwd_entry (lgm, a); default: clib_warning ("Forwarding entries for type %d not supported!", type); return -1; @@ -903,6 +1196,9 @@ vnet_lisp_gpe_fwd_entry_flush (void) case FID_ADDR_IP_PREF: del_ip_fwd_entry_i (lgm, lfe); break; + case FID_ADDR_NSH: + del_nsh_fwd_entry_i (lgm, lfe); + break; } })); /* *INDENT-ON* */ @@ -967,6 +1263,10 @@ format_lisp_gpe_fwd_entry (u8 * s, va_list ap) 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_NSH: + s = format (s, " fib-path-list:%d\n", lfe->nsh.path_list_index); + s = format (s, " dpo:%U\n", format_dpo_id, &lfe->nsh.dpo, 0); + break; case FID_ADDR_IP_PREF: break; } @@ -1036,6 +1336,7 @@ lisp_gpe_fwd_entry_init (vlib_main_t * vm) return (error); l2_fib_init (lgm); + nsh_fib_init (lgm); fib_node_register_type (FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, &lisp_fwd_vft); diff --git a/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.h b/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.h index f79236711ea..d58895a3e28 100644 --- a/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.h +++ b/src/vnet/lisp-gpe/lisp_gpe_fwd_entry.h @@ -81,7 +81,7 @@ typedef struct lisp_gpe_fwd_entry_t_ fib_node_t node; /** - * The Entry's key: {lEID,r-EID,vni} + * The Entry's key: {lEID,rEID,vni} */ lisp_gpe_fwd_entry_key_t *key; @@ -150,6 +150,33 @@ typedef struct lisp_gpe_fwd_entry_t_ */ dpo_id_t dpo; } l2; + + /** + * Fields relevant to an NSH entry + */ + struct + { + /** + * 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 contributed by NSH + */ + dpo_id_t dpo; + + /** + * The DPO used for forwarding. Obtained after stacking tx node + * onto lb choice + */ + dpo_id_t choice; + } nsh; }; union @@ -177,6 +204,8 @@ 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]); +extern const dpo_id_t *lisp_nsh_fib_lookup (lisp_gpe_main_t * lgm, + u32 spi_si); #endif /* diff --git a/src/vnet/mfib/mfib_entry.c b/src/vnet/mfib/mfib_entry.c index f1b6e8ee84e..acbe90bb109 100644 --- a/src/vnet/mfib/mfib_entry.c +++ b/src/vnet/mfib/mfib_entry.c @@ -465,6 +465,7 @@ mfib_entry_src_collect_forwarding (fib_node_index_t pl_index, case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS: case FIB_FORW_CHAIN_TYPE_MPLS_EOS: case FIB_FORW_CHAIN_TYPE_ETHERNET: + case FIB_FORW_CHAIN_TYPE_NSH: ASSERT(0); break; } |