diff options
Diffstat (limited to 'vnet/vnet/gre')
-rw-r--r-- | vnet/vnet/gre/gre.c | 192 | ||||
-rw-r--r-- | vnet/vnet/gre/gre.h | 42 | ||||
-rw-r--r-- | vnet/vnet/gre/interface.c | 480 | ||||
-rw-r--r-- | vnet/vnet/gre/node.c | 211 |
4 files changed, 638 insertions, 287 deletions
diff --git a/vnet/vnet/gre/gre.c b/vnet/vnet/gre/gre.c index f00977c8cd6..9f8adc79ff0 100644 --- a/vnet/vnet/gre/gre.c +++ b/vnet/vnet/gre/gre.c @@ -17,14 +17,10 @@ #include <vnet/vnet.h> #include <vnet/gre/gre.h> +#include <vnet/adj/adj.h> gre_main_t gre_main; -typedef CLIB_PACKED (struct { - ip4_header_t ip4; - gre_header_t gre; -}) ip4_and_gre_header_t; - typedef struct { union { ip4_and_gre_header_t ip4_and_gre; @@ -233,179 +229,39 @@ gre_interface_tx (vlib_main_t * vm, vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); /* - * As long as we have enough pkts left to process two pkts - * and prefetch two pkts... + * FIXME DUAL LOOP */ - while (n_left_from >= 4 && n_left_to_next >= 2) - { - vlib_buffer_t * b0, * b1; - ip4_header_t * ip0, * ip1; - ip4_and_gre_union_t * h0, * h1; - u32 bi0, next0, bi1, next1; - __attribute__((unused)) u8 error0, error1; - u16 gre_protocol0, gre_protocol1; - - /* Prefetch the 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); - - /* - * Prefetch packet data. We expect to overwrite - * the inbound L2 header with an ip header and a - * gre header. Might want to prefetch the last line - * of rewrite space as well; need profile data - */ - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); - CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); - } - - /* Pick up the next two buffer indices */ - bi0 = from[0]; - bi1 = from[1]; - - /* Speculatively enqueue them where we sent the last buffer */ - 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); - - vnet_buffer (b0)->sw_if_index[VLIB_TX] = t->outer_fib_index; - vnet_buffer (b1)->sw_if_index[VLIB_TX] = t->outer_fib_index; - - if (PREDICT_FALSE(t->teb)) - { - gre_protocol0 = clib_net_to_host_u16(GRE_PROTOCOL_teb); - gre_protocol1 = clib_net_to_host_u16(GRE_PROTOCOL_teb); - } - else - { - ip0 = vlib_buffer_get_current (b0); - gre_protocol0 = clib_net_to_host_u16 (0x800); - gre_protocol0 = - ((ip0->ip_version_and_header_length & 0xF0) == 0x60) ? - 0x86DD : gre_protocol0; - - ip1 = vlib_buffer_get_current (b1); - gre_protocol1 = clib_net_to_host_u16 (0x800); - gre_protocol1 = - ((ip1->ip_version_and_header_length & 0xF0) == 0x60) ? - 0x86DD : gre_protocol1; - } - - vlib_buffer_advance (b0, -sizeof(*h0)); - vlib_buffer_advance (b1, -sizeof(*h1)); - - h0 = vlib_buffer_get_current (b0); - h1 = vlib_buffer_get_current (b1); - h0->as_u64[0] = 0; - h0->as_u64[1] = 0; - h0->as_u64[2] = 0; - - h1->as_u64[0] = 0; - h1->as_u64[1] = 0; - h1->as_u64[2] = 0; - - ip0 = &h0->ip4_and_gre.ip4; - h0->ip4_and_gre.gre.protocol = gre_protocol0; - ip0->ip_version_and_header_length = 0x45; - ip0->ttl = 254; - ip0->protocol = IP_PROTOCOL_GRE; - - ip1 = &h1->ip4_and_gre.ip4; - h1->ip4_and_gre.gre.protocol = gre_protocol1; - ip1->ip_version_and_header_length = 0x45; - ip1->ttl = 254; - ip1->protocol = IP_PROTOCOL_GRE; - - ip0->length = - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); - ip1->length = - clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)); - ip0->src_address.as_u32 = t->tunnel_src.as_u32; - ip1->src_address.as_u32 = t->tunnel_src.as_u32; - ip0->dst_address.as_u32 = t->tunnel_dst.as_u32; - ip1->dst_address.as_u32 = t->tunnel_dst.as_u32; - ip0->checksum = ip4_header_checksum (ip0); - ip1->checksum = ip4_header_checksum (ip1); - - /* ip4_lookup will route to the tunnel partner */ - next0 = GRE_OUTPUT_NEXT_LOOKUP; - next1 = GRE_OUTPUT_NEXT_LOOKUP; - error0 = GRE_ERROR_NONE; - error1 = GRE_ERROR_NONE; - - /* - * Enqueue 2 pkts. This macro deals with next0 != next1, - * acquiring enqueue rights to the indicated next - * node input frame, etc. - */ - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } while (n_left_from > 0 && n_left_to_next > 0) { - vlib_buffer_t * b0; + u32 bi0, adj_index0, next0; + const ip_adjacency_t * adj0; + const dpo_id_t *dpo0; ip4_header_t * ip0; - ip4_and_gre_union_t * h0; - u32 bi0, next0; - __attribute__((unused)) u8 error0; - u16 gre_protocol0; - - bi0 = to_next[0] = from[0]; - from += 1; - n_left_from -= 1; - to_next += 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - - vnet_buffer (b0)->sw_if_index[VLIB_TX] = t->outer_fib_index; + vlib_buffer_t * b0; + + 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); ip0 = vlib_buffer_get_current (b0); - if (PREDICT_FALSE(t->teb)) - { - gre_protocol0 = clib_net_to_host_u16(GRE_PROTOCOL_teb); - } - else - { - gre_protocol0 = clib_net_to_host_u16 (0x800); - gre_protocol0 = - ((ip0->ip_version_and_header_length & 0xF0) == 0x60) ? - 0x86DD : gre_protocol0; - } - - vlib_buffer_advance (b0, -sizeof(*h0)); - - h0 = vlib_buffer_get_current (b0); - h0->as_u64[0] = 0; - h0->as_u64[1] = 0; - h0->as_u64[2] = 0; - - ip0 = &h0->ip4_and_gre.ip4; - h0->ip4_and_gre.gre.protocol = gre_protocol0; - ip0->ip_version_and_header_length = 0x45; - ip0->ttl = 254; - ip0->protocol = IP_PROTOCOL_GRE; + + /* Fixup the checksum and len fields in the LISP tunnel encap + * that was applied at the midchain node */ ip0->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); - ip0->src_address.as_u32 = t->tunnel_src.as_u32; - ip0->dst_address.as_u32 = t->tunnel_dst.as_u32; ip0->checksum = ip4_header_checksum (ip0); - next0 = GRE_OUTPUT_NEXT_LOOKUP; - error0 = GRE_ERROR_NONE; + /* 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)) { diff --git a/vnet/vnet/gre/gre.h b/vnet/vnet/gre/gre.h index ad599d2f09e..beb13d989ee 100644 --- a/vnet/vnet/gre/gre.h +++ b/vnet/vnet/gre/gre.h @@ -25,6 +25,7 @@ #include <vnet/ip/ip4_packet.h> #include <vnet/pg/pg.h> #include <vnet/ip/format.h> +#include <vnet/adj/adj_types.h> extern vnet_hw_interface_class_t gre_hw_interface_class; @@ -50,12 +51,44 @@ typedef struct { } gre_protocol_info_t; typedef struct { + /** + * Linkage into the FIB object graph + */ + fib_node_t node; + + /** + * The tunnel's source/local address + */ ip4_address_t tunnel_src; + /** + * The tunnel's destination/remote address + */ ip4_address_t tunnel_dst; + /** + * The FIB in which the src.dst address are present + */ u32 outer_fib_index; u32 hw_if_index; u32 sw_if_index; u8 teb; + + /** + * The FIB entry sourced by the tunnel for its destination prefix + */ + fib_node_index_t fib_entry_index; + + /** + * The tunnel is a child of the FIB entry for its desintion. This is + * so it receives updates when the forwarding information for that entry + * changes. + * The tunnels sibling index on the FIB entry's dependency list. + */ + u32 sibling_index; + + /** + * The index of the midchain adjacency created for this tunnel + */ + adj_index_t adj_index[FIB_LINK_NUM]; } gre_tunnel_t; typedef struct { @@ -80,6 +113,15 @@ typedef struct { vnet_main_t * vnet_main; } gre_main_t; +/** + * @brief IPv4 and GRE header. + * +*/ +typedef CLIB_PACKED (struct { + ip4_header_t ip4; + gre_header_t gre; +}) ip4_and_gre_header_t; + always_inline gre_protocol_info_t * gre_get_protocol_info (gre_main_t * em, gre_protocol_t protocol) { diff --git a/vnet/vnet/gre/interface.c b/vnet/vnet/gre/interface.c index 864c384b992..10e9ff9be8c 100644 --- a/vnet/vnet/gre/interface.c +++ b/vnet/vnet/gre/interface.c @@ -19,10 +19,24 @@ #include <vnet/pg/pg.h> #include <vnet/gre/gre.h> #include <vnet/ip/format.h> +#include <vnet/fib/ip4_fib.h> +#include <vnet/adj/adj_midchain.h> +#include <vnet/mpls/mpls.h> + +static inline u64 +gre_mk_key (const ip4_address_t *src, + const ip4_address_t *dst, + u32 out_fib_index) +{ + // FIXME. the fib index should be part of the key + return ((u64)src->as_u32 << 32 | (u64)dst->as_u32); +} -u8 * format_gre_tunnel (u8 * s, va_list * args) +static u8 * +format_gre_tunnel (u8 * s, va_list * args) { gre_tunnel_t * t = va_arg (*args, gre_tunnel_t *); + int detail = va_arg (*args, int); gre_main_t * gm = &gre_main; s = format (s, @@ -32,11 +46,193 @@ u8 * format_gre_tunnel (u8 * s, va_list * args) format_ip4_address, &t->tunnel_dst, (t->teb ? "teb" : "ip"), t->outer_fib_index); + if (detail) + { + s = format (s, "\n fib-entry:%d adj-ip4:%d adj-ip6:%d adj-mpls:%d", + t->fib_entry_index, + t->adj_index[FIB_LINK_IP4], + t->adj_index[FIB_LINK_IP6], + t->adj_index[FIB_LINK_MPLS]); + } + return s; } -int vnet_gre_add_del_tunnel - (vnet_gre_add_del_tunnel_args_t *a, u32 * sw_if_indexp) +static gre_tunnel_t * +gre_tunnel_db_find (const ip4_address_t *src, + const ip4_address_t *dst, + u32 out_fib_index) +{ + gre_main_t * gm = &gre_main; + uword * p; + u64 key; + + key = gre_mk_key(src, dst, out_fib_index); + + p = hash_get (gm->tunnel_by_key, key); + + if (NULL == p) + return (NULL); + + return (pool_elt_at_index (gm->tunnels, p[0])); +} + +static void +gre_tunnel_db_add (const gre_tunnel_t *t) +{ + gre_main_t * gm = &gre_main; + u64 key; + + key = gre_mk_key(&t->tunnel_src, &t->tunnel_dst, t->outer_fib_index); + hash_set (gm->tunnel_by_key, key, t - gm->tunnels); +} + +static void +gre_tunnel_db_remove (const gre_tunnel_t *t) +{ + gre_main_t * gm = &gre_main; + u64 key; + + key = gre_mk_key(&t->tunnel_src, &t->tunnel_dst, t->outer_fib_index); + hash_unset (gm->tunnel_by_key, key); +} + +static gre_tunnel_t * +gre_tunnel_from_fib_node (fib_node_t *node) +{ +#if (CLIB_DEBUG > 0) + ASSERT(FIB_NODE_TYPE_GRE_TUNNEL == node->fn_type); +#endif + return ((gre_tunnel_t*) (((char*)node) - + STRUCT_OFFSET_OF(gre_tunnel_t, node))); +} + +/* + * gre_tunnel_stack + * + * 'stack' (resolve the recursion for) the tunnel's midchain adjacency + */ +static void +gre_tunnel_stack (gre_tunnel_t *gt) +{ + fib_link_t linkt; + + /* + * find the adjacency that is contributed by the FIB entry + * that this tunnel resovles via, and use it as the next adj + * in the midchain + */ + FOR_EACH_FIB_LINK(linkt) + { + if (ADJ_INDEX_INVALID != gt->adj_index[linkt]) + { + adj_nbr_midchain_stack( + gt->adj_index[linkt], + fib_entry_contribute_ip_forwarding(gt->fib_entry_index)); + } + } +} + +/** + * Function definition to backwalk a FIB node + */ +static fib_node_back_walk_rc_t +gre_tunnel_back_walk (fib_node_t *node, + fib_node_back_walk_ctx_t *ctx) +{ + gre_tunnel_stack(gre_tunnel_from_fib_node(node)); + + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/** + * Function definition to get a FIB node from its index + */ +static fib_node_t* +gre_tunnel_fib_node_get (fib_node_index_t index) +{ + gre_tunnel_t * gt; + gre_main_t * gm; + + gm = &gre_main; + gt = pool_elt_at_index(gm->tunnels, index); + + return (>->node); +} + +/** + * Function definition to inform the FIB node that its last lock has gone. + */ +static void +gre_tunnel_last_lock_gone (fib_node_t *node) +{ + /* + * The MPLS GRE tunnel is a root of the graph. As such + * it never has children and thus is never locked. + */ + ASSERT(0); +} + +/* + * Virtual function table registered by MPLS GRE tunnels + * for participation in the FIB object graph. + */ +const static fib_node_vft_t gre_vft = { + .fnv_get = gre_tunnel_fib_node_get, + .fnv_last_lock = gre_tunnel_last_lock_gone, + .fnv_back_walk = gre_tunnel_back_walk, +}; + +static int +gre_proto_from_fib_link (fib_link_t link) +{ + switch (link) + { + case FIB_LINK_IP4: + return (GRE_PROTOCOL_ip4); + case FIB_LINK_IP6: + return (GRE_PROTOCOL_ip6); + case FIB_LINK_MPLS: + return (GRE_PROTOCOL_mpls_unicast); + } + ASSERT(0); + return (GRE_PROTOCOL_ip4); +} + +static u8 * +gre_rewrite (gre_tunnel_t * t, + fib_link_t link) +{ + ip4_and_gre_header_t * h0; + u8 * rewrite_data = 0; + + vec_validate_init_empty (rewrite_data, sizeof (*h0) - 1, 0); + + h0 = (ip4_and_gre_header_t *) rewrite_data; + + if (t->teb) + { + h0->gre.protocol = clib_net_to_host_u16(GRE_PROTOCOL_teb); + } + else + { + h0->gre.protocol = clib_host_to_net_u16(gre_proto_from_fib_link(link)); + } + + h0->ip4.ip_version_and_header_length = 0x45; + h0->ip4.ttl = 254; + h0->ip4.protocol = IP_PROTOCOL_GRE; + /* $$$ fixup ip4 header length and checksum after-the-fact */ + h0->ip4.src_address.as_u32 = t->tunnel_src.as_u32; + h0->ip4.dst_address.as_u32 = t->tunnel_dst.as_u32; + h0->ip4.checksum = ip4_header_checksum (&h0->ip4); + + return (rewrite_data); +} + +static int +vnet_gre_tunnel_add (vnet_gre_add_del_tunnel_args_t *a, + u32 * sw_if_indexp) { gre_main_t * gm = &gre_main; vnet_main_t * vnm = gm->vnet_main; @@ -44,49 +240,45 @@ int vnet_gre_add_del_tunnel gre_tunnel_t * t; vnet_hw_interface_t * hi; u32 hw_if_index, sw_if_index; - u32 slot; u32 outer_fib_index; - uword * p; - u64 key; u8 address[6]; clib_error_t *error; + fib_link_t linkt; + u8 *rewrite; - key = (u64)a->src.as_u32 << 32 | (u64)a->dst.as_u32; - p = hash_get (gm->tunnel_by_key, key); + outer_fib_index = ip4_fib_index_from_table_id(a->outer_fib_id); - if (a->is_add) { - /* check if same src/dst pair exists */ - if (p) - return VNET_API_ERROR_INVALID_VALUE; + if (~0 == outer_fib_index) + return VNET_API_ERROR_NO_SUCH_FIB; - p = hash_get (im->fib_index_by_table_id, a->outer_fib_id); - if (! p) - return VNET_API_ERROR_NO_SUCH_FIB; + t = gre_tunnel_db_find(&a->src, &a->dst, a->outer_fib_id); - outer_fib_index = p[0]; + if (NULL != t) + return VNET_API_ERROR_INVALID_VALUE; - pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES); - memset (t, 0, sizeof (*t)); + pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES); + memset (t, 0, sizeof (*t)); + fib_node_init(&t->node, FIB_NODE_TYPE_GRE_TUNNEL); - if (vec_len (gm->free_gre_tunnel_hw_if_indices) > 0) { - vnet_interface_main_t * im = &vnm->interface_main; + if (vec_len (gm->free_gre_tunnel_hw_if_indices) > 0) { + vnet_interface_main_t * im = &vnm->interface_main; - hw_if_index = gm->free_gre_tunnel_hw_if_indices + hw_if_index = gm->free_gre_tunnel_hw_if_indices [vec_len (gm->free_gre_tunnel_hw_if_indices)-1]; - _vec_len (gm->free_gre_tunnel_hw_if_indices) -= 1; + _vec_len (gm->free_gre_tunnel_hw_if_indices) -= 1; - hi = vnet_get_hw_interface (vnm, hw_if_index); - hi->dev_instance = t - gm->tunnels; - hi->hw_instance = hi->dev_instance; + hi = vnet_get_hw_interface (vnm, hw_if_index); + hi->dev_instance = t - gm->tunnels; + hi->hw_instance = hi->dev_instance; - /* clear old stats of freed tunnel before reuse */ - sw_if_index = hi->sw_if_index; - vnet_interface_counter_lock(im); - vlib_zero_combined_counter + /* clear old stats of freed tunnel before reuse */ + sw_if_index = hi->sw_if_index; + vnet_interface_counter_lock(im); + vlib_zero_combined_counter (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX], sw_if_index); - vlib_zero_combined_counter + vlib_zero_combined_counter (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_RX], sw_if_index); - vlib_zero_simple_counter + vlib_zero_simple_counter (&im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP], sw_if_index); vnet_interface_counter_unlock(im); } else { @@ -111,67 +303,186 @@ int vnet_gre_add_del_tunnel return VNET_API_ERROR_INVALID_REGISTRATION; } } else { - hw_if_index = vnet_register_interface - (vnm, gre_device_class.index, t - gm->tunnels, - gre_hw_interface_class.index, - t - gm->tunnels); + hw_if_index = vnet_register_interface + (vnm, gre_device_class.index, t - gm->tunnels, + gre_hw_interface_class.index, + t - gm->tunnels); } hi = vnet_get_hw_interface (vnm, hw_if_index); sw_if_index = hi->sw_if_index; } - t->hw_if_index = hw_if_index; - t->outer_fib_index = outer_fib_index; - t->sw_if_index = sw_if_index; + t->hw_if_index = hw_if_index; + t->outer_fib_index = outer_fib_index; + t->sw_if_index = sw_if_index; - vec_validate_init_empty (gm->tunnel_index_by_sw_if_index, sw_if_index, ~0); - gm->tunnel_index_by_sw_if_index[sw_if_index] = t - gm->tunnels; + vec_validate_init_empty (gm->tunnel_index_by_sw_if_index, sw_if_index, ~0); + gm->tunnel_index_by_sw_if_index[sw_if_index] = t - gm->tunnels; - vec_validate (im->fib_index_by_sw_if_index, sw_if_index); - im->fib_index_by_sw_if_index[sw_if_index] = t->outer_fib_index; + vec_validate (im->fib_index_by_sw_if_index, sw_if_index); + im->fib_index_by_sw_if_index[sw_if_index] = t->outer_fib_index; + ip4_sw_interface_enable_disable(sw_if_index, 1); - hi->min_packet_bytes = 64 + sizeof (gre_header_t) + sizeof (ip4_header_t); - hi->per_packet_overhead_bytes = + hi->min_packet_bytes = 64 + sizeof (gre_header_t) + sizeof (ip4_header_t); + hi->per_packet_overhead_bytes = /* preamble */ 8 + /* inter frame gap */ 12; - /* Standard default gre MTU. */ - hi->max_l3_packet_bytes[VLIB_RX] = hi->max_l3_packet_bytes[VLIB_TX] = 9000; + /* Standard default gre MTU. */ + hi->max_l3_packet_bytes[VLIB_RX] = hi->max_l3_packet_bytes[VLIB_TX] = 9000; + + clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src)); + clib_memcpy (&t->tunnel_dst, &a->dst, sizeof (t->tunnel_dst)); + + gre_tunnel_db_add(t); + + /* + * source the FIB entry for the tunnel's destination + * and become a child thereof. The tunnel will then get poked + * when the forwarding for the entry updates, and the tunnel can + * re-stack accordingly + */ + const fib_prefix_t tun_dst_pfx = { + .fp_len = 32, + .fp_proto = FIB_PROTOCOL_IP4, + .fp_addr = { + .ip4 = t->tunnel_dst, + } + }; + + t->fib_entry_index = + fib_table_entry_special_add(outer_fib_index, + &tun_dst_pfx, + FIB_SOURCE_RR, + FIB_ENTRY_FLAG_NONE, + ADJ_INDEX_INVALID); + t->sibling_index = + fib_entry_child_add(t->fib_entry_index, + FIB_NODE_TYPE_GRE_TUNNEL, + t - gm->tunnels); + + /* + * create and update the midchain adj this tunnel sources. + * We could be smarter here and trigger this on an interface proto enable, + * like we do for MPLS. + */ + for (linkt = FIB_LINK_IP4; linkt <= FIB_LINK_IP6; linkt++) + { + t->adj_index[linkt] = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4, + linkt, + &zero_addr, + sw_if_index); + + rewrite = gre_rewrite(t, linkt); + adj_nbr_midchain_update_rewrite(t->adj_index[linkt], + hi->tx_node_index, + rewrite); + vec_free(rewrite); + } + t->adj_index[FIB_LINK_MPLS] = ADJ_INDEX_INVALID; - t->teb = a->teb; - clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src)); - clib_memcpy (&t->tunnel_dst, &a->dst, sizeof (t->tunnel_dst)); + t->teb = a->teb; + clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src)); + clib_memcpy (&t->tunnel_dst, &a->dst, sizeof (t->tunnel_dst)); + gre_tunnel_stack(t); - hash_set (gm->tunnel_by_key, key, t - gm->tunnels); + if (sw_if_indexp) + *sw_if_indexp = sw_if_index; - slot = vlib_node_add_named_next_with_slot - (vnm->vlib_main, hi->tx_node_index, "ip4-lookup", GRE_OUTPUT_NEXT_LOOKUP); + return 0; +} - ASSERT (slot == GRE_OUTPUT_NEXT_LOOKUP); +static int +vnet_gre_tunnel_delete (vnet_gre_add_del_tunnel_args_t *a, + u32 * sw_if_indexp) +{ + gre_main_t * gm = &gre_main; + vnet_main_t * vnm = gm->vnet_main; + gre_tunnel_t * t; + fib_link_t linkt; + u32 sw_if_index; + + t = gre_tunnel_db_find(&a->src, &a->dst, a->outer_fib_id); - } else { /* !is_add => delete */ - /* tunnel needs to exist */ - if (! p) - return VNET_API_ERROR_NO_SUCH_ENTRY; + if (NULL == t) + return VNET_API_ERROR_NO_SUCH_ENTRY; - t = pool_elt_at_index (gm->tunnels, p[0]); + sw_if_index = t->sw_if_index; + vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */); + /* make sure tunnel is removed from l2 bd or xconnect */ + set_int_l2_mode(gm->vlib_main, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0); + vec_add1 (gm->free_gre_tunnel_hw_if_indices, t->hw_if_index); + gm->tunnel_index_by_sw_if_index[sw_if_index] = ~0; + ip4_sw_interface_enable_disable(sw_if_index, 0); - sw_if_index = t->sw_if_index; - vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */); - /* make sure tunnel is removed from l2 bd or xconnect */ - set_int_l2_mode(gm->vlib_main, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0); - vec_add1 (gm->free_gre_tunnel_hw_if_indices, t->hw_if_index); - gm->tunnel_index_by_sw_if_index[sw_if_index] = ~0; + fib_entry_child_remove(t->fib_entry_index, + t->sibling_index); + fib_table_entry_delete_index(t->fib_entry_index, + FIB_SOURCE_RR); - hash_unset (gm->tunnel_by_key, key); - pool_put (gm->tunnels, t); + FOR_EACH_FIB_LINK(linkt) + { + adj_unlock(t->adj_index[linkt]); } + gre_tunnel_db_remove(t); + fib_node_deinit(&t->node); + pool_put (gm->tunnels, t); + if (sw_if_indexp) *sw_if_indexp = sw_if_index; return 0; } +int +vnet_gre_add_del_tunnel (vnet_gre_add_del_tunnel_args_t *a, + u32 * sw_if_indexp) +{ + if (a->is_add) + return (vnet_gre_tunnel_add(a, sw_if_indexp)); + else + return (vnet_gre_tunnel_delete(a, sw_if_indexp)); +} + +static void +gre_sw_interface_mpls_state_change (u32 sw_if_index, + u32 is_enable) +{ + gre_main_t *gm = &gre_main; + vnet_hw_interface_t * hi; + gre_tunnel_t *t; + u8 *rewrite; + + if ((vec_len(gm->tunnel_index_by_sw_if_index) < sw_if_index) || + (~0 == gm->tunnel_index_by_sw_if_index[sw_if_index])) + return; + + t = pool_elt_at_index(gm->tunnels, + gm->tunnel_index_by_sw_if_index[sw_if_index]); + + if (is_enable) + { + hi = vnet_get_hw_interface (vnet_get_main(), t->hw_if_index); + t->adj_index[FIB_LINK_MPLS] = + adj_nbr_add_or_lock(FIB_PROTOCOL_IP4, + FIB_LINK_MPLS, + &zero_addr, + sw_if_index); + + rewrite = gre_rewrite(t, FIB_LINK_MPLS); + adj_nbr_midchain_update_rewrite(t->adj_index[FIB_LINK_MPLS], + hi->tx_node_index, + rewrite); + vec_free(rewrite); + } + else + { + adj_unlock(t->adj_index[FIB_LINK_MPLS]); + t->adj_index[FIB_LINK_MPLS] = ADJ_INDEX_INVALID; + } + + gre_tunnel_stack(t); +} static clib_error_t * create_gre_tunnel_command_fn (vlib_main_t * vm, @@ -216,13 +527,15 @@ create_gre_tunnel_command_fn (vlib_main_t * vm, return clib_error_return (0, "src and dst are identical"); memset (a, 0, sizeof (*a)); - a->is_add = is_add; a->outer_fib_id = outer_fib_id; a->teb = teb; clib_memcpy(&a->src, &src, sizeof(src)); clib_memcpy(&a->dst, &dst, sizeof(dst)); - rv = vnet_gre_add_del_tunnel (a, &sw_if_index); + if (is_add) + rv = vnet_gre_tunnel_add(a, &sw_if_index); + else + rv = vnet_gre_tunnel_delete(a, &sw_if_index); switch(rv) { @@ -255,14 +568,32 @@ show_gre_tunnel_command_fn (vlib_main_t * vm, { gre_main_t * gm = &gre_main; gre_tunnel_t * t; + u32 ti = ~0; if (pool_elts (gm->tunnels) == 0) vlib_cli_output (vm, "No GRE tunnels configured..."); - pool_foreach (t, gm->tunnels, - ({ - vlib_cli_output (vm, "%U", format_gre_tunnel, t); - })); + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%d", &ti)) + ; + else + break; + } + + if (~0 == ti) + { + pool_foreach (t, gm->tunnels, + ({ + vlib_cli_output (vm, "%U", format_gre_tunnel, t, 0); + })); + } + else + { + t = pool_elt_at_index(gm->tunnels, ti); + + vlib_cli_output (vm, "%U", format_gre_tunnel, t, 1); + } return 0; } @@ -275,6 +606,11 @@ VLIB_CLI_COMMAND (show_gre_tunnel_command, static) = { /* force inclusion from application's main.c */ clib_error_t *gre_interface_init (vlib_main_t *vm) { + vec_add1(mpls_main.mpls_interface_state_change_callbacks, + gre_sw_interface_mpls_state_change); + + fib_node_register_type(FIB_NODE_TYPE_GRE_TUNNEL, &gre_vft); + return 0; } VLIB_INIT_FUNCTION(gre_interface_init); diff --git a/vnet/vnet/gre/node.c b/vnet/vnet/gre/node.c index d5ea4b65ddb..b55f5511916 100644 --- a/vnet/vnet/gre/node.c +++ b/vnet/vnet/gre/node.c @@ -18,6 +18,7 @@ #include <vlib/vlib.h> #include <vnet/pg/pg.h> #include <vnet/gre/gre.h> +#include <vnet/mpls/mpls.h> #include <vppinfra/sparse_vec.h> #define foreach_gre_input_next \ @@ -25,7 +26,8 @@ _(PUNT, "error-punt") \ _(DROP, "error-drop") \ _(ETHERNET_INPUT, "ethernet-input") \ _(IP4_INPUT, "ip4-input") \ -_(IP6_INPUT, "ip6-input") +_(IP6_INPUT, "ip6-input") \ +_(MPLS_INPUT, "mpls-input") typedef enum { #define _(s,n) GRE_INPUT_NEXT_##s, @@ -66,13 +68,17 @@ gre_input (vlib_main_t * vm, vlib_frame_t * from_frame) { gre_main_t * gm = &gre_main; + mpls_main_t * mm = &mpls_main; + ip4_main_t * ip4m = &ip4_main; gre_input_runtime_t * rt = (void *) node->runtime_data; __attribute__((unused)) u32 n_left_from, next_index, * from, * to_next; u64 cached_tunnel_key = (u64) ~0; - u32 cached_tunnel_sw_if_index = 0, tunnel_sw_if_index; + u32 cached_tunnel_sw_if_index = 0, tunnel_sw_if_index = 0; u32 cached_tunnel_fib_index = 0, tunnel_fib_index; u32 cpu_index = os_get_cpu_number(); + u32 len; + vnet_interface_main_t *im = &gm->vnet_main->interface_main; from = vlib_frame_vector_args (from_frame); n_left_from = from_frame->n_vectors; @@ -141,7 +147,7 @@ gre_input (vlib_main_t * vm, /* Index sparse array with network byte order. */ protocol0 = h0->protocol; protocol1 = h1->protocol; - sparse_vec_index2 (rt->next_by_protocol, protocol0, protocol1, + sparse_vec_index2 (rt->next_by_protocol, protocol0, protocol1, &i0, &i1); next0 = vec_elt(rt->next_by_protocol, i0); next1 = vec_elt(rt->next_by_protocol, i1); @@ -154,10 +160,10 @@ gre_input (vlib_main_t * vm, version1 = clib_net_to_host_u16 (h1->flags_and_version); verr1 = version1 & GRE_VERSION_MASK; - b0->error = verr0 ? node->errors[GRE_ERROR_UNSUPPORTED_VERSION] + b0->error = verr0 ? node->errors[GRE_ERROR_UNSUPPORTED_VERSION] : b0->error; next0 = verr0 ? GRE_INPUT_NEXT_DROP : next0; - b1->error = verr1 ? node->errors[GRE_ERROR_UNSUPPORTED_VERSION] + b1->error = verr1 ? node->errors[GRE_ERROR_UNSUPPORTED_VERSION] : b1->error; next1 = verr1 ? GRE_INPUT_NEXT_DROP : next1; @@ -176,7 +182,6 @@ gre_input (vlib_main_t * vm, gre_tunnel_t * t; uword * p; - ip4_main_t * ip4m = &ip4_main; p = hash_get (gm->tunnel_by_key, key); if (!p) { @@ -199,19 +204,56 @@ gre_input (vlib_main_t * vm, tunnel_sw_if_index = cached_tunnel_sw_if_index; tunnel_fib_index = cached_tunnel_fib_index; } + } + else if (PREDICT_TRUE(next0 == GRE_INPUT_NEXT_MPLS_INPUT)) + { + u64 key = ((u64)(vnet_buffer(b0)->gre.dst) << 32) | + (u64)(vnet_buffer(b0)->gre.src); + + if (cached_tunnel_key != key) + { + vnet_hw_interface_t * hi; + mpls_gre_tunnel_t * t; + uword * p; - u32 len = vlib_buffer_length_in_chain (vm, b0); - vnet_interface_main_t *im = &gm->vnet_main->interface_main; - vlib_increment_combined_counter (im->combined_sw_if_counters - + VNET_INTERFACE_COUNTER_RX, - cpu_index, - tunnel_sw_if_index, - 1 /* packets */, - len /* bytes */); - - vnet_buffer(b0)->sw_if_index[VLIB_TX] = tunnel_fib_index; - vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; + p = hash_get (gm->tunnel_by_key, key); + if (!p) + { + next0 = GRE_INPUT_NEXT_DROP; + b0->error = node->errors[GRE_ERROR_NO_SUCH_TUNNEL]; + goto drop0; + } + t = pool_elt_at_index (mm->gre_tunnels, p[0]); + hi = vnet_get_hw_interface (gm->vnet_main, + t->hw_if_index); + tunnel_sw_if_index = hi->sw_if_index; + tunnel_fib_index = vec_elt (ip4m->fib_index_by_sw_if_index, + tunnel_sw_if_index); + + cached_tunnel_sw_if_index = tunnel_sw_if_index; + cached_tunnel_fib_index = tunnel_fib_index; + } + else + { + tunnel_sw_if_index = cached_tunnel_sw_if_index; + tunnel_fib_index = cached_tunnel_fib_index; + } } + else + { + next0 = GRE_INPUT_NEXT_DROP; + goto drop0; + } + len = vlib_buffer_length_in_chain (vm, b0); + vlib_increment_combined_counter (im->combined_sw_if_counters + + VNET_INTERFACE_COUNTER_RX, + cpu_index, + tunnel_sw_if_index, + 1 /* packets */, + len /* bytes */); + + vnet_buffer(b0)->sw_if_index[VLIB_TX] = tunnel_fib_index; + vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; drop0: if (PREDICT_FALSE(next1 == GRE_INPUT_NEXT_IP4_INPUT @@ -227,7 +269,6 @@ drop0: gre_tunnel_t * t; uword * p; - ip4_main_t * ip4m = &ip4_main; p = hash_get (gm->tunnel_by_key, key); if (!p) { @@ -250,23 +291,62 @@ drop0: tunnel_sw_if_index = cached_tunnel_sw_if_index; tunnel_fib_index = cached_tunnel_fib_index; } + } + else if (PREDICT_TRUE(next1 == GRE_INPUT_NEXT_MPLS_INPUT)) + { + u64 key = ((u64)(vnet_buffer(b1)->gre.dst) << 32) | + (u64)(vnet_buffer(b1)->gre.src); - u32 len = vlib_buffer_length_in_chain (vm, b1); - vnet_interface_main_t *im = &gm->vnet_main->interface_main; - vlib_increment_combined_counter (im->combined_sw_if_counters - + VNET_INTERFACE_COUNTER_RX, - cpu_index, - tunnel_sw_if_index, - 1 /* packets */, - len /* bytes */); - - vnet_buffer(b1)->sw_if_index[VLIB_TX] = tunnel_fib_index; - vnet_buffer(b1)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; + if (cached_tunnel_key != key) + { + vnet_hw_interface_t * hi; + mpls_gre_tunnel_t * t; + uword * p; + + ip4_main_t * ip4m = &ip4_main; + p = hash_get (gm->tunnel_by_key, key); + if (!p) + { + next1 = GRE_INPUT_NEXT_DROP; + b1->error = node->errors[GRE_ERROR_NO_SUCH_TUNNEL]; + goto drop1; + } + t = pool_elt_at_index (mm->gre_tunnels, p[0]); + hi = vnet_get_hw_interface (gm->vnet_main, + t->hw_if_index); + tunnel_sw_if_index = hi->sw_if_index; + tunnel_fib_index = vec_elt (ip4m->fib_index_by_sw_if_index, + tunnel_sw_if_index); + + cached_tunnel_sw_if_index = tunnel_sw_if_index; + cached_tunnel_fib_index = tunnel_fib_index; + } + else + { + tunnel_sw_if_index = cached_tunnel_sw_if_index; + tunnel_fib_index = cached_tunnel_fib_index; + } } + else + { + next1 = GRE_INPUT_NEXT_DROP; + goto drop1; + } + len = vlib_buffer_length_in_chain (vm, b1); + vlib_increment_combined_counter (im->combined_sw_if_counters + + VNET_INTERFACE_COUNTER_RX, + cpu_index, + tunnel_sw_if_index, + 1 /* packets */, + len /* bytes */); + + vnet_buffer(b1)->sw_if_index[VLIB_TX] = tunnel_fib_index; + vnet_buffer(b1)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; + drop1: - if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) { - gre_rx_trace_t *tr = vlib_add_trace (vm, node, + gre_rx_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); tr->tunnel_id = ~0; tr->length = ip0->length; @@ -274,9 +354,9 @@ drop1: tr->dst.as_u32 = ip0->dst_address.as_u32; } - if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) + if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) { - gre_rx_trace_t *tr = vlib_add_trace (vm, node, + gre_rx_trace_t *tr = vlib_add_trace (vm, node, b1, sizeof (*tr)); tr->tunnel_id = ~0; tr->length = ip1->length; @@ -336,6 +416,7 @@ drop1: /* For IP payload we need to find source interface so we can increase counters and help forward node to pick right FIB */ + /* RPF check for ip4/ip6 input */ if (PREDICT_FALSE(next0 == GRE_INPUT_NEXT_IP4_INPUT || next0 == GRE_INPUT_NEXT_IP6_INPUT || next0 == GRE_INPUT_NEXT_ETHERNET_INPUT)) @@ -349,7 +430,6 @@ drop1: gre_tunnel_t * t; uword * p; - ip4_main_t * ip4m = &ip4_main; p = hash_get (gm->tunnel_by_key, key); if (!p) { @@ -372,26 +452,63 @@ drop1: tunnel_sw_if_index = cached_tunnel_sw_if_index; tunnel_fib_index = cached_tunnel_fib_index; } + } + else if (PREDICT_TRUE(next0 == GRE_INPUT_NEXT_MPLS_INPUT)) + { + u64 key = ((u64)(vnet_buffer(b0)->gre.dst) << 32) | + (u64)(vnet_buffer(b0)->gre.src); - u32 len = vlib_buffer_length_in_chain (vm, b0); - vnet_interface_main_t *im = &gm->vnet_main->interface_main; - vlib_increment_combined_counter (im->combined_sw_if_counters - + VNET_INTERFACE_COUNTER_RX, - cpu_index, - tunnel_sw_if_index, - 1 /* packets */, - len /* bytes */); - - vnet_buffer(b0)->sw_if_index[VLIB_TX] = tunnel_fib_index; - vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; + if (cached_tunnel_key != key) + { + vnet_hw_interface_t * hi; + mpls_gre_tunnel_t * t; + uword * p; + + p = hash_get (gm->tunnel_by_key, key); + if (!p) + { + next0 = GRE_INPUT_NEXT_DROP; + b0->error = node->errors[GRE_ERROR_NO_SUCH_TUNNEL]; + goto drop; + } + t = pool_elt_at_index (mm->gre_tunnels, p[0]); + hi = vnet_get_hw_interface (gm->vnet_main, + t->hw_if_index); + tunnel_sw_if_index = hi->sw_if_index; + tunnel_fib_index = vec_elt (ip4m->fib_index_by_sw_if_index, + tunnel_sw_if_index); + + cached_tunnel_sw_if_index = tunnel_sw_if_index; + cached_tunnel_fib_index = tunnel_fib_index; + } + else + { + tunnel_sw_if_index = cached_tunnel_sw_if_index; + tunnel_fib_index = cached_tunnel_fib_index; + } + } + else + { + next0 = GRE_INPUT_NEXT_DROP; + goto drop; } + len = vlib_buffer_length_in_chain (vm, b0); + vlib_increment_combined_counter (im->combined_sw_if_counters + + VNET_INTERFACE_COUNTER_RX, + cpu_index, + tunnel_sw_if_index, + 1 /* packets */, + len /* bytes */); + + vnet_buffer(b0)->sw_if_index[VLIB_TX] = tunnel_fib_index; + vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index; drop: if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) { gre_rx_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); - tr->tunnel_id = ~0; + tr->tunnel_id = tunnel_sw_if_index; tr->length = ip0->length; tr->src.as_u32 = ip0->src_address.as_u32; tr->dst.as_u32 = ip0->dst_address.as_u32; @@ -509,7 +626,7 @@ static clib_error_t * gre_input_init (vlib_main_t * vm) ASSERT(ip4_input); ip6_input = vlib_get_node_by_name (vm, (u8 *)"ip6-input"); ASSERT(ip6_input); - mpls_unicast_input = vlib_get_node_by_name (vm, (u8 *)"mpls-gre-input"); + mpls_unicast_input = vlib_get_node_by_name (vm, (u8 *)"mpls-input"); ASSERT(mpls_unicast_input); gre_register_input_protocol (vm, GRE_PROTOCOL_teb, |