The offload should be handled by gso node or by the NIC if the latter has the relevant capabilities. But ip midchain is missing the support for buffer offload metadata in case of GSO packet. This patch adds the relevant support to add the buffer metadata if the packet is GSO/IPIP to be handled accordingly. Type: improvement Change-Id: I17f5d71bf4c5f43a85ca3f2fbebfa1426b42ef69 Signed-off-by: Arthur de Kerhor <arthurdekerhor@gmail.com> Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
918 lines
24 KiB
C
918 lines
24 KiB
C
/*
|
|
* ipip.c: ipip
|
|
*
|
|
* Copyright (c) 2018 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 aipiped 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 <stddef.h>
|
|
#include <vnet/adj/adj_midchain.h>
|
|
#include <vnet/ipip/ipip.h>
|
|
#include <vnet/vnet.h>
|
|
#include <vnet/adj/adj_nbr.h>
|
|
#include <vnet/adj/adj_midchain.h>
|
|
#include <vnet/fib/ip4_fib.h>
|
|
#include <vnet/fib/ip6_fib.h>
|
|
#include <vnet/ip/format.h>
|
|
#include <vnet/ipip/ipip.h>
|
|
#include <vnet/teib/teib.h>
|
|
#include <vnet/tunnel/tunnel_dp.h>
|
|
|
|
ipip_main_t ipip_main;
|
|
|
|
/* Packet trace structure */
|
|
typedef struct
|
|
{
|
|
u32 tunnel_id;
|
|
u32 length;
|
|
ip46_address_t src;
|
|
ip46_address_t dst;
|
|
} ipip_tx_trace_t;
|
|
|
|
u8 *
|
|
format_ipip_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 *);
|
|
ipip_tx_trace_t *t = va_arg (*args, ipip_tx_trace_t *);
|
|
|
|
s =
|
|
format (s, "IPIP: tunnel %d len %d src %U dst %U", t->tunnel_id,
|
|
t->length, format_ip46_address, &t->src, IP46_TYPE_ANY,
|
|
format_ip46_address, &t->dst, IP46_TYPE_ANY);
|
|
return s;
|
|
}
|
|
|
|
static u8 *
|
|
ipip_build_rewrite (vnet_main_t * vnm, u32 sw_if_index,
|
|
vnet_link_t link_type, const void *dst_address)
|
|
{
|
|
const ip46_address_t *dst;
|
|
ip4_header_t *ip4;
|
|
ip6_header_t *ip6;
|
|
u8 *rewrite = NULL;
|
|
ipip_tunnel_t *t;
|
|
|
|
dst = dst_address;
|
|
t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
|
|
|
|
if (!t)
|
|
/* not one of ours */
|
|
return (0);
|
|
|
|
switch (t->transport)
|
|
{
|
|
case IPIP_TRANSPORT_IP4:
|
|
vec_validate (rewrite, sizeof (*ip4) - 1);
|
|
ip4 = (ip4_header_t *) rewrite;
|
|
ip4->ip_version_and_header_length = 0x45;
|
|
ip4->ttl = 64;
|
|
/* fixup ip4 header length, protocol and checksum after-the-fact */
|
|
ip4->src_address.as_u32 = t->tunnel_src.ip4.as_u32;
|
|
ip4->dst_address.as_u32 = dst->ip4.as_u32;
|
|
if (!(t->flags & TUNNEL_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP))
|
|
ip4_header_set_dscp (ip4, t->dscp);
|
|
if (t->flags & TUNNEL_ENCAP_DECAP_FLAG_ENCAP_SET_DF)
|
|
ip4_header_set_df (ip4);
|
|
|
|
switch (link_type)
|
|
{
|
|
case VNET_LINK_IP6:
|
|
ip4->protocol = IP_PROTOCOL_IPV6;
|
|
break;
|
|
case VNET_LINK_IP4:
|
|
ip4->protocol = IP_PROTOCOL_IP_IN_IP;
|
|
break;
|
|
case VNET_LINK_MPLS:
|
|
ip4->protocol = IP_PROTOCOL_MPLS_IN_IP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ip4->checksum = ip4_header_checksum (ip4);
|
|
break;
|
|
|
|
case IPIP_TRANSPORT_IP6:
|
|
vec_validate (rewrite, sizeof (*ip6) - 1);
|
|
ip6 = (ip6_header_t *) rewrite;
|
|
ip6->ip_version_traffic_class_and_flow_label =
|
|
clib_host_to_net_u32 (6 << 28);
|
|
ip6->hop_limit = 64;
|
|
/* fixup ip6 header length and protocol after-the-fact */
|
|
ip6->src_address.as_u64[0] = t->tunnel_src.ip6.as_u64[0];
|
|
ip6->src_address.as_u64[1] = t->tunnel_src.ip6.as_u64[1];
|
|
ip6->dst_address.as_u64[0] = dst->ip6.as_u64[0];
|
|
ip6->dst_address.as_u64[1] = dst->ip6.as_u64[1];
|
|
if (!(t->flags & TUNNEL_ENCAP_DECAP_FLAG_ENCAP_COPY_DSCP))
|
|
ip6_set_dscp_network_order (ip6, t->dscp);
|
|
|
|
switch (link_type)
|
|
{
|
|
case VNET_LINK_IP6:
|
|
ip6->protocol = IP_PROTOCOL_IPV6;
|
|
break;
|
|
case VNET_LINK_IP4:
|
|
ip6->protocol = IP_PROTOCOL_IP_IN_IP;
|
|
break;
|
|
case VNET_LINK_MPLS:
|
|
ip6->protocol = IP_PROTOCOL_MPLS_IN_IP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return (rewrite);
|
|
}
|
|
|
|
static void
|
|
ipip64_fixup (vlib_main_t * vm, const ip_adjacency_t * adj, vlib_buffer_t * b,
|
|
const void *data)
|
|
{
|
|
tunnel_encap_decap_flags_t flags;
|
|
ip4_header_t *ip4;
|
|
|
|
flags = pointer_to_uword (data);
|
|
|
|
ip4 = vlib_buffer_get_current (b);
|
|
ip4->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b));
|
|
tunnel_encap_fixup_6o4 (flags, ((ip6_header_t *) (ip4 + 1)), ip4);
|
|
|
|
if (PREDICT_FALSE (b->flags & VNET_BUFFER_F_GSO))
|
|
{
|
|
vnet_buffer2 (b)->outer_l3_hdr_offset = (u8 *) ip4 - b->data;
|
|
vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_OUTER_IP_CKSUM |
|
|
VNET_BUFFER_OFFLOAD_F_TNL_IPIP);
|
|
}
|
|
else
|
|
ip4->checksum = ip4_header_checksum (ip4);
|
|
}
|
|
|
|
static void
|
|
ipip44_fixup (vlib_main_t * vm, const ip_adjacency_t * adj, vlib_buffer_t * b,
|
|
const void *data)
|
|
{
|
|
tunnel_encap_decap_flags_t flags;
|
|
ip4_header_t *ip4;
|
|
|
|
flags = pointer_to_uword (data);
|
|
|
|
ip4 = vlib_buffer_get_current (b);
|
|
ip4->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b));
|
|
tunnel_encap_fixup_4o4 (flags, ip4 + 1, ip4);
|
|
|
|
if (PREDICT_FALSE (b->flags & VNET_BUFFER_F_GSO))
|
|
{
|
|
vnet_buffer2 (b)->outer_l3_hdr_offset = (u8 *) ip4 - b->data;
|
|
vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_OUTER_IP_CKSUM |
|
|
VNET_BUFFER_OFFLOAD_F_TNL_IPIP);
|
|
}
|
|
else
|
|
ip4->checksum = ip4_header_checksum (ip4);
|
|
}
|
|
|
|
static void
|
|
ipip46_fixup (vlib_main_t * vm, const ip_adjacency_t * adj, vlib_buffer_t * b,
|
|
const void *data)
|
|
{
|
|
tunnel_encap_decap_flags_t flags;
|
|
ip6_header_t *ip6;
|
|
|
|
flags = pointer_to_uword (data);
|
|
|
|
/* Must set locally originated otherwise we're not allowed to
|
|
fragment the packet later */
|
|
b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
|
|
|
|
ip6 = vlib_buffer_get_current (b);
|
|
ip6->payload_length =
|
|
clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b) -
|
|
sizeof (*ip6));
|
|
tunnel_encap_fixup_4o6 (flags, b, ((ip4_header_t *) (ip6 + 1)), ip6);
|
|
|
|
if (PREDICT_FALSE (b->flags & VNET_BUFFER_F_GSO))
|
|
{
|
|
vnet_buffer2 (b)->outer_l3_hdr_offset = (u8 *) ip6 - b->data;
|
|
vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_TNL_IPIP);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ipip66_fixup (vlib_main_t * vm,
|
|
const ip_adjacency_t * adj, vlib_buffer_t * b, const void *data)
|
|
{
|
|
tunnel_encap_decap_flags_t flags;
|
|
ip6_header_t *ip6;
|
|
|
|
flags = pointer_to_uword (data);
|
|
|
|
/* Must set locally originated otherwise we're not allowed to
|
|
fragment the packet later */
|
|
b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
|
|
|
|
ip6 = vlib_buffer_get_current (b);
|
|
ip6->payload_length =
|
|
clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b) -
|
|
sizeof (*ip6));
|
|
tunnel_encap_fixup_6o6 (flags, ip6 + 1, ip6);
|
|
|
|
if (PREDICT_FALSE (b->flags & VNET_BUFFER_F_GSO))
|
|
{
|
|
vnet_buffer2 (b)->outer_l3_hdr_offset = (u8 *) ip6 - b->data;
|
|
vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_TNL_IPIP);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ipipm6_fixup (vlib_main_t *vm, const ip_adjacency_t *adj, vlib_buffer_t *b,
|
|
const void *data)
|
|
{
|
|
tunnel_encap_decap_flags_t flags;
|
|
ip6_header_t *ip6;
|
|
|
|
flags = pointer_to_uword (data);
|
|
|
|
/* Must set locally originated otherwise we're not allowed to
|
|
fragment the packet later and we'll get an unwanted hop-limt
|
|
decrement */
|
|
b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
|
|
|
|
ip6 = vlib_buffer_get_current (b);
|
|
ip6->payload_length =
|
|
clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b) - sizeof (*ip6));
|
|
tunnel_encap_fixup_mplso6 (flags, b, (mpls_unicast_header_t *) (ip6 + 1),
|
|
ip6);
|
|
|
|
if (PREDICT_FALSE (b->flags & VNET_BUFFER_F_GSO))
|
|
{
|
|
vnet_buffer2 (b)->outer_l3_hdr_offset = (u8 *) ip6 - b->data;
|
|
vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_TNL_IPIP);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ipipm4_fixup (vlib_main_t *vm, const ip_adjacency_t *adj, vlib_buffer_t *b,
|
|
const void *data)
|
|
{
|
|
tunnel_encap_decap_flags_t flags;
|
|
ip4_header_t *ip4;
|
|
|
|
flags = pointer_to_uword (data);
|
|
|
|
/* Must set locally originated otherwise we'll do a TTL decrement
|
|
* during ip4-rewrite */
|
|
b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
|
|
|
|
ip4 = vlib_buffer_get_current (b);
|
|
ip4->length =
|
|
clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b) - sizeof (*ip4));
|
|
tunnel_encap_fixup_mplso4 (flags, (mpls_unicast_header_t *) (ip4 + 1), ip4);
|
|
|
|
if (PREDICT_FALSE (b->flags & VNET_BUFFER_F_GSO))
|
|
{
|
|
vnet_buffer2 (b)->outer_l3_hdr_offset = (u8 *) ip4 - b->data;
|
|
vnet_buffer_offload_flags_set (b, VNET_BUFFER_OFFLOAD_F_OUTER_IP_CKSUM |
|
|
VNET_BUFFER_OFFLOAD_F_TNL_IPIP);
|
|
}
|
|
else
|
|
ip4->checksum = ip4_header_checksum (ip4);
|
|
}
|
|
|
|
static void
|
|
ipip_tunnel_stack (adj_index_t ai)
|
|
{
|
|
ip_adjacency_t *adj;
|
|
ipip_tunnel_t *t;
|
|
u32 sw_if_index;
|
|
|
|
adj = adj_get (ai);
|
|
sw_if_index = adj->rewrite_header.sw_if_index;
|
|
|
|
t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
|
|
if (!t)
|
|
return;
|
|
|
|
if ((vnet_hw_interface_get_flags (vnet_get_main (), t->hw_if_index) &
|
|
VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
|
|
{
|
|
adj_midchain_delegate_unstack (ai);
|
|
}
|
|
else
|
|
{
|
|
fib_prefix_t dst = {
|
|
.fp_len = t->transport == IPIP_TRANSPORT_IP6 ? 128 : 32,
|
|
.fp_proto = (t->transport == IPIP_TRANSPORT_IP6 ?
|
|
FIB_PROTOCOL_IP6 :
|
|
FIB_PROTOCOL_IP4),
|
|
.fp_addr = t->tunnel_dst
|
|
};
|
|
|
|
adj_midchain_delegate_stack (ai, t->fib_index, &dst);
|
|
}
|
|
}
|
|
|
|
static adj_walk_rc_t
|
|
ipip_adj_walk_cb (adj_index_t ai, void *ctx)
|
|
{
|
|
ipip_tunnel_stack (ai);
|
|
|
|
return (ADJ_WALK_RC_CONTINUE);
|
|
}
|
|
|
|
static void
|
|
ipip_tunnel_restack (ipip_tunnel_t * gt)
|
|
{
|
|
fib_protocol_t proto;
|
|
|
|
/*
|
|
* walk all the adjacencies on th IPIP interface and restack them
|
|
*/
|
|
FOR_EACH_FIB_IP_PROTOCOL (proto)
|
|
{
|
|
adj_nbr_walk (gt->sw_if_index, proto, ipip_adj_walk_cb, NULL);
|
|
}
|
|
}
|
|
|
|
static adj_midchain_fixup_t
|
|
ipip_get_fixup (const ipip_tunnel_t * t, vnet_link_t lt, adj_flags_t * aflags)
|
|
{
|
|
if (t->transport == IPIP_TRANSPORT_IP6 && lt == VNET_LINK_IP6)
|
|
return (ipip66_fixup);
|
|
if (t->transport == IPIP_TRANSPORT_IP6 && lt == VNET_LINK_IP4)
|
|
return (ipip46_fixup);
|
|
if (t->transport == IPIP_TRANSPORT_IP6 && lt == VNET_LINK_MPLS)
|
|
return (ipipm6_fixup);
|
|
if (t->transport == IPIP_TRANSPORT_IP4 && lt == VNET_LINK_IP6)
|
|
return (ipip64_fixup);
|
|
if (t->transport == IPIP_TRANSPORT_IP4 && lt == VNET_LINK_MPLS)
|
|
return (ipipm4_fixup);
|
|
if (t->transport == IPIP_TRANSPORT_IP4 && lt == VNET_LINK_IP4)
|
|
{
|
|
*aflags = *aflags | ADJ_FLAG_MIDCHAIN_FIXUP_IP4O4_HDR;
|
|
return (ipip44_fixup);
|
|
}
|
|
|
|
ASSERT (0);
|
|
return (ipip44_fixup);
|
|
}
|
|
|
|
void
|
|
ipip_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
|
|
{
|
|
adj_midchain_fixup_t fixup;
|
|
ipip_tunnel_t *t;
|
|
adj_flags_t af;
|
|
|
|
af = ADJ_FLAG_NONE;
|
|
t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
|
|
if (!t)
|
|
return;
|
|
|
|
/*
|
|
* the user has not requested that the load-balancing be based on
|
|
* a flow hash of the inner packet. so use the stacking to choose
|
|
* a path.
|
|
*/
|
|
if (!(t->flags & TUNNEL_ENCAP_DECAP_FLAG_ENCAP_INNER_HASH))
|
|
af |= ADJ_FLAG_MIDCHAIN_IP_STACK;
|
|
|
|
fixup = ipip_get_fixup (t, adj_get_link_type (ai), &af);
|
|
adj_nbr_midchain_update_rewrite
|
|
(ai, fixup,
|
|
uword_to_pointer (t->flags, void *), af,
|
|
ipip_build_rewrite (vnm, sw_if_index,
|
|
adj_get_link_type (ai), &t->tunnel_dst));
|
|
ipip_tunnel_stack (ai);
|
|
}
|
|
|
|
typedef struct mipip_walk_ctx_t_
|
|
{
|
|
const ipip_tunnel_t *t;
|
|
const teib_entry_t *ne;
|
|
} mipip_walk_ctx_t;
|
|
|
|
static adj_walk_rc_t
|
|
mipip_mk_complete_walk (adj_index_t ai, void *data)
|
|
{
|
|
adj_midchain_fixup_t fixup;
|
|
mipip_walk_ctx_t *ctx = data;
|
|
adj_flags_t af;
|
|
|
|
af = ADJ_FLAG_NONE;
|
|
fixup = ipip_get_fixup (ctx->t, adj_get_link_type (ai), &af);
|
|
|
|
/*
|
|
* the user has not requested that the load-balancing be based on
|
|
* a flow hash of the inner packet. so use the stacking to choose
|
|
* a path.
|
|
*/
|
|
if (!(ctx->t->flags & TUNNEL_ENCAP_DECAP_FLAG_ENCAP_INNER_HASH))
|
|
af |= ADJ_FLAG_MIDCHAIN_IP_STACK;
|
|
|
|
adj_nbr_midchain_update_rewrite
|
|
(ai, fixup,
|
|
uword_to_pointer (ctx->t->flags, void *),
|
|
af, ipip_build_rewrite (vnet_get_main (),
|
|
ctx->t->sw_if_index,
|
|
adj_get_link_type (ai),
|
|
&teib_entry_get_nh (ctx->ne)->fp_addr));
|
|
|
|
teib_entry_adj_stack (ctx->ne, ai);
|
|
|
|
return (ADJ_WALK_RC_CONTINUE);
|
|
}
|
|
|
|
static adj_walk_rc_t
|
|
mipip_mk_incomplete_walk (adj_index_t ai, void *data)
|
|
{
|
|
adj_midchain_fixup_t fixup;
|
|
ipip_tunnel_t *t = data;
|
|
adj_flags_t af;
|
|
|
|
af = ADJ_FLAG_NONE;
|
|
fixup = ipip_get_fixup (t, adj_get_link_type (ai), &af);
|
|
|
|
adj_nbr_midchain_update_rewrite (ai, fixup, NULL, ADJ_FLAG_NONE, NULL);
|
|
|
|
adj_midchain_delegate_unstack (ai);
|
|
|
|
return (ADJ_WALK_RC_CONTINUE);
|
|
}
|
|
|
|
void
|
|
mipip_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
|
|
{
|
|
ipip_main_t *gm = &ipip_main;
|
|
adj_midchain_fixup_t fixup;
|
|
ip_adjacency_t *adj;
|
|
teib_entry_t *ne;
|
|
ipip_tunnel_t *t;
|
|
adj_flags_t af;
|
|
u32 ti;
|
|
|
|
af = ADJ_FLAG_NONE;
|
|
adj = adj_get (ai);
|
|
ti = gm->tunnel_index_by_sw_if_index[sw_if_index];
|
|
t = pool_elt_at_index (gm->tunnels, ti);
|
|
|
|
ne = teib_entry_find_46 (sw_if_index,
|
|
adj->ia_nh_proto, &adj->sub_type.nbr.next_hop);
|
|
|
|
if (NULL == ne)
|
|
{
|
|
// no TEIB entry to provide the next-hop
|
|
fixup = ipip_get_fixup (t, adj_get_link_type (ai), &af);
|
|
adj_nbr_midchain_update_rewrite
|
|
(ai, fixup, uword_to_pointer (t->flags, void *), ADJ_FLAG_NONE, NULL);
|
|
return;
|
|
}
|
|
|
|
mipip_walk_ctx_t ctx = {
|
|
.t = t,
|
|
.ne = ne
|
|
};
|
|
adj_nbr_walk_nh (sw_if_index,
|
|
adj->ia_nh_proto,
|
|
&adj->sub_type.nbr.next_hop, mipip_mk_complete_walk, &ctx);
|
|
}
|
|
|
|
static u8 *
|
|
format_ipip_tunnel_name (u8 * s, va_list * args)
|
|
{
|
|
u32 dev_instance = va_arg (*args, u32);
|
|
ipip_main_t *gm = &ipip_main;
|
|
ipip_tunnel_t *t;
|
|
|
|
if (dev_instance >= vec_len (gm->tunnels))
|
|
return format (s, "<improperly-referenced>");
|
|
|
|
t = pool_elt_at_index (gm->tunnels, dev_instance);
|
|
return format (s, "ipip%d", t->user_instance);
|
|
}
|
|
|
|
static u8 *
|
|
format_ipip_device (u8 * s, va_list * args)
|
|
{
|
|
u32 dev_instance = va_arg (*args, u32);
|
|
CLIB_UNUSED (int verbose) = va_arg (*args, int);
|
|
|
|
s = format (s, "IPIP tunnel: id %d\n", dev_instance);
|
|
return s;
|
|
}
|
|
|
|
static clib_error_t *
|
|
ipip_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
|
|
{
|
|
vnet_hw_interface_t *hi;
|
|
ipip_tunnel_t *t;
|
|
|
|
hi = vnet_get_hw_interface (vnm, hw_if_index);
|
|
|
|
t = ipip_tunnel_db_find_by_sw_if_index (hi->sw_if_index);
|
|
if (!t)
|
|
return 0;
|
|
|
|
if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
|
|
vnet_hw_interface_set_flags (vnm, hw_if_index,
|
|
VNET_HW_INTERFACE_FLAG_LINK_UP);
|
|
else
|
|
vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */ );
|
|
|
|
ipip_tunnel_restack (t);
|
|
|
|
return /* no error */ 0;
|
|
}
|
|
|
|
static int
|
|
ipip_tunnel_desc (u32 sw_if_index,
|
|
ip46_address_t * src, ip46_address_t * dst, u8 * is_l2)
|
|
{
|
|
ipip_tunnel_t *t;
|
|
|
|
t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
|
|
if (!t)
|
|
return -1;
|
|
|
|
*src = t->tunnel_src;
|
|
*dst = t->tunnel_dst;
|
|
*is_l2 = 0;
|
|
|
|
return (0);
|
|
}
|
|
|
|
VNET_DEVICE_CLASS(ipip_device_class) = {
|
|
.name = "IPIP tunnel device",
|
|
.format_device_name = format_ipip_tunnel_name,
|
|
.format_device = format_ipip_device,
|
|
.format_tx_trace = format_ipip_tx_trace,
|
|
.admin_up_down_function = ipip_interface_admin_up_down,
|
|
.ip_tun_desc = ipip_tunnel_desc,
|
|
#ifdef SOON
|
|
.clear counter = 0;
|
|
#endif
|
|
};
|
|
|
|
VNET_HW_INTERFACE_CLASS(ipip_hw_interface_class) = {
|
|
.name = "IPIP",
|
|
//.format_header = format_ipip_header_with_length,
|
|
//.unformat_header = unformat_ipip_header,
|
|
.build_rewrite = ipip_build_rewrite,
|
|
.update_adjacency = ipip_update_adj,
|
|
.flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
|
|
};
|
|
|
|
VNET_HW_INTERFACE_CLASS(mipip_hw_interface_class) = {
|
|
.name = "mIPIP",
|
|
//.format_header = format_ipip_header_with_length,
|
|
//.unformat_header = unformat_ipip_header,
|
|
.build_rewrite = ipip_build_rewrite,
|
|
.update_adjacency = mipip_update_adj,
|
|
.flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA,
|
|
};
|
|
|
|
ipip_tunnel_t *
|
|
ipip_tunnel_db_find (const ipip_tunnel_key_t * key)
|
|
{
|
|
ipip_main_t *gm = &ipip_main;
|
|
uword *p;
|
|
|
|
p = hash_get_mem (gm->tunnel_by_key, key);
|
|
if (!p)
|
|
return (NULL);
|
|
return (pool_elt_at_index (gm->tunnels, p[0]));
|
|
}
|
|
|
|
ipip_tunnel_t *
|
|
ipip_tunnel_db_find_by_sw_if_index (u32 sw_if_index)
|
|
{
|
|
ipip_main_t *gm = &ipip_main;
|
|
if (vec_len (gm->tunnel_index_by_sw_if_index) <= sw_if_index)
|
|
return NULL;
|
|
u32 ti = gm->tunnel_index_by_sw_if_index[sw_if_index];
|
|
if (ti == ~0)
|
|
return NULL;
|
|
return pool_elt_at_index (gm->tunnels, ti);
|
|
}
|
|
|
|
void
|
|
ipip_tunnel_db_add (ipip_tunnel_t * t, const ipip_tunnel_key_t * key)
|
|
{
|
|
ipip_main_t *gm = &ipip_main;
|
|
|
|
hash_set_mem_alloc (&gm->tunnel_by_key, key, t->dev_instance);
|
|
}
|
|
|
|
void
|
|
ipip_tunnel_db_remove (ipip_tunnel_t * t, const ipip_tunnel_key_t * key)
|
|
{
|
|
ipip_main_t *gm = &ipip_main;
|
|
|
|
hash_unset_mem_free (&gm->tunnel_by_key, key);
|
|
}
|
|
|
|
void
|
|
ipip_mk_key_i (ipip_transport_t transport,
|
|
ipip_mode_t mode,
|
|
const ip46_address_t * src,
|
|
const ip46_address_t * dst,
|
|
u32 fib_index, ipip_tunnel_key_t * key)
|
|
{
|
|
key->transport = transport;
|
|
key->mode = mode;
|
|
key->src = *src;
|
|
key->dst = *dst;
|
|
key->fib_index = fib_index;
|
|
key->__pad = 0;;
|
|
}
|
|
|
|
void
|
|
ipip_mk_key (const ipip_tunnel_t * t, ipip_tunnel_key_t * key)
|
|
{
|
|
ipip_mk_key_i (t->transport, t->mode,
|
|
&t->tunnel_src, &t->tunnel_dst, t->fib_index, key);
|
|
}
|
|
|
|
static void
|
|
ipip_teib_mk_key (const ipip_tunnel_t * t,
|
|
const teib_entry_t * ne, ipip_tunnel_key_t * key)
|
|
{
|
|
const fib_prefix_t *nh;
|
|
|
|
nh = teib_entry_get_nh (ne);
|
|
|
|
/* construct the key using mode P2P so it can be found in the DP */
|
|
ipip_mk_key_i (t->transport, IPIP_MODE_P2P,
|
|
&t->tunnel_src, &nh->fp_addr,
|
|
teib_entry_get_fib_index (ne), key);
|
|
}
|
|
|
|
static void
|
|
ipip_teib_entry_added (const teib_entry_t * ne)
|
|
{
|
|
ipip_main_t *gm = &ipip_main;
|
|
const ip_address_t *nh;
|
|
ipip_tunnel_key_t key;
|
|
ipip_tunnel_t *t;
|
|
u32 sw_if_index;
|
|
u32 t_idx;
|
|
|
|
sw_if_index = teib_entry_get_sw_if_index (ne);
|
|
if (vec_len (gm->tunnel_index_by_sw_if_index) < sw_if_index)
|
|
return;
|
|
|
|
t_idx = gm->tunnel_index_by_sw_if_index[sw_if_index];
|
|
|
|
if (INDEX_INVALID == t_idx)
|
|
return;
|
|
|
|
t = pool_elt_at_index (gm->tunnels, t_idx);
|
|
|
|
ipip_teib_mk_key (t, ne, &key);
|
|
ipip_tunnel_db_add (t, &key);
|
|
|
|
// update the rewrites for each of the adjacencies for this next-hop
|
|
mipip_walk_ctx_t ctx = {
|
|
.t = t,
|
|
.ne = ne
|
|
};
|
|
nh = teib_entry_get_peer (ne);
|
|
adj_nbr_walk_nh (teib_entry_get_sw_if_index (ne),
|
|
(AF_IP4 == ip_addr_version (nh) ?
|
|
FIB_PROTOCOL_IP4 :
|
|
FIB_PROTOCOL_IP6),
|
|
&ip_addr_46 (nh), mipip_mk_complete_walk, &ctx);
|
|
}
|
|
|
|
static void
|
|
ipip_teib_entry_deleted (const teib_entry_t * ne)
|
|
{
|
|
ipip_main_t *gm = &ipip_main;
|
|
const ip_address_t *nh;
|
|
ipip_tunnel_key_t key;
|
|
ipip_tunnel_t *t;
|
|
u32 sw_if_index;
|
|
u32 t_idx;
|
|
|
|
sw_if_index = teib_entry_get_sw_if_index (ne);
|
|
if (vec_len (gm->tunnel_index_by_sw_if_index) < sw_if_index)
|
|
return;
|
|
|
|
t_idx = gm->tunnel_index_by_sw_if_index[sw_if_index];
|
|
|
|
if (INDEX_INVALID == t_idx)
|
|
return;
|
|
|
|
t = pool_elt_at_index (gm->tunnels, t_idx);
|
|
|
|
ipip_teib_mk_key (t, ne, &key);
|
|
ipip_tunnel_db_remove (t, &key);
|
|
|
|
nh = teib_entry_get_peer (ne);
|
|
|
|
/* make all the adjacencies incomplete */
|
|
adj_nbr_walk_nh (teib_entry_get_sw_if_index (ne),
|
|
(AF_IP4 == ip_addr_version (nh) ?
|
|
FIB_PROTOCOL_IP4 :
|
|
FIB_PROTOCOL_IP6),
|
|
&ip_addr_46 (nh), mipip_mk_incomplete_walk, t);
|
|
}
|
|
|
|
static walk_rc_t
|
|
ipip_tunnel_delete_teib_walk (index_t nei, void *ctx)
|
|
{
|
|
ipip_tunnel_t *t = ctx;
|
|
ipip_tunnel_key_t key;
|
|
|
|
ipip_teib_mk_key (t, teib_entry_get (nei), &key);
|
|
ipip_tunnel_db_remove (t, &key);
|
|
|
|
return (WALK_CONTINUE);
|
|
}
|
|
|
|
static walk_rc_t
|
|
ipip_tunnel_add_teib_walk (index_t nei, void *ctx)
|
|
{
|
|
ipip_tunnel_t *t = ctx;
|
|
ipip_tunnel_key_t key;
|
|
|
|
ipip_teib_mk_key (t, teib_entry_get (nei), &key);
|
|
ipip_tunnel_db_add (t, &key);
|
|
|
|
return (WALK_CONTINUE);
|
|
}
|
|
|
|
int
|
|
ipip_add_tunnel (ipip_transport_t transport,
|
|
u32 instance, ip46_address_t * src, ip46_address_t * dst,
|
|
u32 fib_index, tunnel_encap_decap_flags_t flags,
|
|
ip_dscp_t dscp, tunnel_mode_t tmode, u32 * sw_if_indexp)
|
|
{
|
|
ipip_main_t *gm = &ipip_main;
|
|
vnet_main_t *vnm = gm->vnet_main;
|
|
ipip_tunnel_t *t;
|
|
vnet_hw_interface_t *hi;
|
|
u32 hw_if_index, sw_if_index;
|
|
ipip_tunnel_key_t key;
|
|
ipip_mode_t mode;
|
|
|
|
if (tmode == TUNNEL_MODE_MP && !ip46_address_is_zero (dst))
|
|
return (VNET_API_ERROR_INVALID_DST_ADDRESS);
|
|
|
|
mode = (tmode == TUNNEL_MODE_P2P ? IPIP_MODE_P2P : IPIP_MODE_P2MP);
|
|
ipip_mk_key_i (transport, mode, src, dst, fib_index, &key);
|
|
|
|
t = ipip_tunnel_db_find (&key);
|
|
if (t)
|
|
{
|
|
if (sw_if_indexp)
|
|
sw_if_indexp[0] = t->sw_if_index;
|
|
return VNET_API_ERROR_IF_ALREADY_EXISTS;
|
|
}
|
|
|
|
pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES);
|
|
clib_memset (t, 0, sizeof (*t));
|
|
|
|
/* Reconcile the real dev_instance and a possible requested instance */
|
|
u32 t_idx = t - gm->tunnels; /* tunnel index (or instance) */
|
|
u32 u_idx = instance; /* user specified instance */
|
|
if (u_idx == ~0)
|
|
u_idx = t_idx;
|
|
if (hash_get (gm->instance_used, u_idx))
|
|
{
|
|
pool_put (gm->tunnels, t);
|
|
return VNET_API_ERROR_INSTANCE_IN_USE;
|
|
}
|
|
hash_set (gm->instance_used, u_idx, 1);
|
|
|
|
t->dev_instance = t_idx; /* actual */
|
|
t->user_instance = u_idx; /* name */
|
|
|
|
hw_if_index = vnet_register_interface (vnm, ipip_device_class.index, t_idx,
|
|
(mode == IPIP_MODE_P2P ?
|
|
ipip_hw_interface_class.index :
|
|
mipip_hw_interface_class.index),
|
|
t_idx);
|
|
|
|
hi = vnet_get_hw_interface (vnm, hw_if_index);
|
|
sw_if_index = hi->sw_if_index;
|
|
|
|
t->mode = mode;
|
|
t->hw_if_index = hw_if_index;
|
|
t->fib_index = fib_index;
|
|
t->sw_if_index = sw_if_index;
|
|
t->dscp = dscp;
|
|
t->flags = flags;
|
|
t->transport = transport;
|
|
|
|
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_idx;
|
|
|
|
if (t->transport == IPIP_TRANSPORT_IP4)
|
|
hi->frame_overhead = sizeof (ip4_header_t);
|
|
else
|
|
hi->frame_overhead = sizeof (ip6_header_t);
|
|
|
|
hi->min_frame_size = hi->frame_overhead + 64;
|
|
|
|
/* Standard default ipip MTU. */
|
|
vnet_sw_interface_set_mtu (vnm, sw_if_index, 9000);
|
|
vnet_set_interface_l3_output_node (gm->vlib_main, sw_if_index,
|
|
(u8 *) "tunnel-output");
|
|
|
|
t->tunnel_src = *src;
|
|
t->tunnel_dst = *dst;
|
|
|
|
ipip_tunnel_db_add (t, &key);
|
|
|
|
if (t->mode == IPIP_MODE_P2MP)
|
|
teib_walk_itf (t->sw_if_index, ipip_tunnel_add_teib_walk, t);
|
|
|
|
if (sw_if_indexp)
|
|
*sw_if_indexp = sw_if_index;
|
|
|
|
if (t->transport == IPIP_TRANSPORT_IP6 && !gm->ip6_protocol_registered)
|
|
{
|
|
ip6_register_protocol (IP_PROTOCOL_IP_IN_IP, ipip6_input_node.index);
|
|
ip6_register_protocol (IP_PROTOCOL_MPLS_IN_IP, ipip6_input_node.index);
|
|
ip6_register_protocol (IP_PROTOCOL_IPV6, ipip6_input_node.index);
|
|
gm->ip6_protocol_registered = true;
|
|
}
|
|
else if (t->transport == IPIP_TRANSPORT_IP4 && !gm->ip4_protocol_registered)
|
|
{
|
|
ip4_register_protocol (IP_PROTOCOL_IP_IN_IP, ipip4_input_node.index);
|
|
ip4_register_protocol (IP_PROTOCOL_MPLS_IN_IP, ipip4_input_node.index);
|
|
ip4_register_protocol (IP_PROTOCOL_IPV6, ipip4_input_node.index);
|
|
gm->ip4_protocol_registered = true;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ipip_del_tunnel (u32 sw_if_index)
|
|
{
|
|
ipip_main_t *gm = &ipip_main;
|
|
vnet_main_t *vnm = gm->vnet_main;
|
|
ipip_tunnel_t *t;
|
|
ipip_tunnel_key_t key;
|
|
|
|
t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
|
|
if (t == NULL)
|
|
return VNET_API_ERROR_NO_SUCH_ENTRY;
|
|
|
|
if (t->mode == IPIP_MODE_P2MP)
|
|
teib_walk_itf (t->sw_if_index, ipip_tunnel_delete_teib_walk, t);
|
|
|
|
vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ );
|
|
vnet_reset_interface_l3_output_node (gm->vlib_main, t->sw_if_index);
|
|
gm->tunnel_index_by_sw_if_index[sw_if_index] = ~0;
|
|
vnet_delete_hw_interface (vnm, t->hw_if_index);
|
|
hash_unset (gm->instance_used, t->user_instance);
|
|
|
|
ipip_mk_key (t, &key);
|
|
ipip_tunnel_db_remove (t, &key);
|
|
pool_put (gm->tunnels, t);
|
|
|
|
return 0;
|
|
}
|
|
|
|
const static teib_vft_t ipip_teib_vft = {
|
|
.nv_added = ipip_teib_entry_added,
|
|
.nv_deleted = ipip_teib_entry_deleted,
|
|
};
|
|
|
|
static clib_error_t *
|
|
ipip_init (vlib_main_t * vm)
|
|
{
|
|
ipip_main_t *gm = &ipip_main;
|
|
|
|
clib_memset (gm, 0, sizeof (gm[0]));
|
|
gm->vlib_main = vm;
|
|
gm->vnet_main = vnet_get_main ();
|
|
gm->tunnel_by_key =
|
|
hash_create_mem (0, sizeof (ipip_tunnel_key_t), sizeof (uword));
|
|
|
|
teib_register (&ipip_teib_vft);
|
|
|
|
return 0;
|
|
}
|
|
|
|
VLIB_INIT_FUNCTION (ipip_init);
|
|
|
|
/*
|
|
* fd.io coding-style-patch-verification: ON
|
|
*
|
|
* Local Variables:
|
|
* eval: (c-set-style "gnu")
|
|
* End:
|
|
*/
|