summaryrefslogtreecommitdiffstats
path: root/src/vnet/udp
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2017-11-05 16:26:46 -0800
committerFlorin Coras <florin.coras@gmail.com>2017-11-07 16:13:42 +0000
commit810086d8fd08445919ae03bf36161037e53a712a (patch)
tree76a91d3ed49759ef3adae32066f9dcedd75df889 /src/vnet/udp
parent595992c5c3b5abbdb7e90e61acbee212f25ad59f (diff)
UDP Encapsulation.
A UDP-encap object that particiapates in the FIB graph and contributes DPO to teh output chain. It thereofre resembles a tunnel but without the interface. FIB paths (and henace routes) can then be created to egress through the UDP-encap. Said routes can have MPLS labels, hence this also allows MPLSoUPD. Encap is uni-directional. For decap, one still registers with the UDP port dispatcher. Change-Id: I23bd345523b20789a1de1b02022ea1148ca50797 Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'src/vnet/udp')
-rw-r--r--src/vnet/udp/udp.api67
-rw-r--r--src/vnet/udp/udp_api.c193
-rw-r--r--src/vnet/udp/udp_encap.c617
-rw-r--r--src/vnet/udp/udp_encap.h147
-rw-r--r--src/vnet/udp/udp_encap_node.c280
5 files changed, 1304 insertions, 0 deletions
diff --git a/src/vnet/udp/udp.api b/src/vnet/udp/udp.api
new file mode 100644
index 00000000000..25ee96489a2
--- /dev/null
+++ b/src/vnet/udp/udp.api
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** \file
+
+ This file defines vpp UDP control-plane API messages which are generally
+ called through a shared memory interface.
+*/
+
+vl_api_version 1.0.0
+
+/** \brief Add / del table request
+ A table can be added multiple times, but need be deleted only once.
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param is_ipv6 - V4 or V6 table
+ @param table_id - table ID associated with the encap destination
+*/
+autoreply define udp_encap_add_del
+{
+ u32 client_index;
+ u32 context;
+ u32 id;
+ u32 table_id;
+ u8 is_ip6;
+ u8 is_add;
+ u16 src_port;
+ u16 dst_port;
+ u8 src_ip[16];
+ u8 dst_ip[16];
+};
+
+define udp_encap_dump
+{
+ u32 client_index;
+ u32 context;
+};
+
+define udp_encap_details
+{
+ u32 context;
+ u32 id;
+ u32 table_id;
+ u8 is_ip6;
+ u16 src_port;
+ u16 dst_port;
+ u8 src_ip[16];
+ u8 dst_ip[16];
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/udp/udp_api.c b/src/vnet/udp/udp_api.c
new file mode 100644
index 00000000000..e65235a5396
--- /dev/null
+++ b/src/vnet/udp/udp_api.c
@@ -0,0 +1,193 @@
+/*
+ * 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 <vlibmemory/api.h>
+
+#include <vnet/udp/udp_encap.h>
+#include <vnet/fib/fib_table.h>
+
+#include <vnet/vnet_msg_enum.h>
+
+#define vl_typedefs /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <vnet/vnet_all_api_h.h>
+#undef vl_printfun
+
+#include <vlibapi/api_helper_macros.h>
+
+
+#define foreach_udp_api_msg \
+ _(UDP_ENCAP_ADD_DEL, udp_encap_add_del) \
+_(UDP_ENCAP_DUMP, udp_encap_dump)
+
+static void
+send_udp_encap_details (const udp_encap_t * ue,
+ unix_shared_memory_queue_t * q, u32 context)
+{
+ vl_api_udp_encap_details_t *mp;
+ fib_table_t *fib_table;
+
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_UDP_ENCAP_DETAILS);
+ mp->context = context;
+
+ mp->is_ip6 = (ue->ue_ip_proto == FIB_PROTOCOL_IP6);
+
+ if (FIB_PROTOCOL_IP4 == ue->ue_ip_proto)
+ {
+ clib_memcpy (mp->src_ip, &ue->ue_hdrs.ip4.ue_ip4.src_address, 4);
+ clib_memcpy (mp->dst_ip, &ue->ue_hdrs.ip4.ue_ip4.dst_address, 4);
+ mp->src_port = htons (ue->ue_hdrs.ip4.ue_udp.src_port);
+ mp->dst_port = htons (ue->ue_hdrs.ip4.ue_udp.dst_port);
+ }
+ else
+ {
+ clib_memcpy (mp->src_ip, &ue->ue_hdrs.ip6.ue_ip6.src_address, 16);
+ clib_memcpy (mp->dst_ip, &ue->ue_hdrs.ip6.ue_ip6.dst_address, 16);
+ mp->src_port = htons (ue->ue_hdrs.ip6.ue_udp.src_port);
+ mp->dst_port = htons (ue->ue_hdrs.ip6.ue_udp.dst_port);
+ }
+
+ fib_table = fib_table_get (ue->ue_fib_index, ue->ue_ip_proto);
+ mp->table_id = htonl (fib_table->ft_table_id);
+ mp->id = htonl (ue->ue_id);
+
+ vl_msg_api_send_shmem (q, (u8 *) & mp);
+}
+
+static void
+vl_api_udp_encap_dump_t_handler (vl_api_udp_encap_dump_t * mp,
+ vlib_main_t * vm)
+{
+ unix_shared_memory_queue_t *q;
+ udp_encap_t *ue;
+
+ q = vl_api_client_index_to_input_queue (mp->client_index);
+ if (q == 0)
+ return;
+
+ /* *INDENT-OFF* */
+ pool_foreach(ue, udp_encap_pool,
+ ({
+ send_udp_encap_details(ue, q, mp->context);
+ }));
+ /* *INDENT-OFF* */
+}
+
+static void
+vl_api_udp_encap_add_del_t_handler (vl_api_udp_encap_add_del_t * mp,
+ vlib_main_t * vm)
+{
+ vl_api_udp_encap_add_del_reply_t *rmp;
+ ip46_address_t src_ip, dst_ip;
+ u32 fib_index, table_id, ue_id;
+ fib_protocol_t fproto;
+ int rv = 0;
+
+ ue_id = ntohl(mp->id);
+ table_id = ntohl(mp->table_id);
+ fproto = (mp->is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
+
+ fib_index = fib_table_find(fproto, table_id);
+
+ if (~0 == fib_index)
+ {
+ rv = VNET_API_ERROR_NO_SUCH_TABLE;
+ goto done;
+ }
+
+ if (FIB_PROTOCOL_IP4 == fproto)
+ {
+ clib_memcpy(&src_ip.ip4, mp->src_ip, 4);
+ clib_memcpy(&dst_ip.ip4, mp->dst_ip, 4);
+ }
+ else
+ {
+ clib_memcpy(&src_ip.ip6, mp->src_ip, 16);
+ clib_memcpy(&dst_ip.ip6, mp->dst_ip, 16);
+ }
+
+ if (mp->is_add)
+ {
+ udp_encap_add_and_lock(ue_id, fproto, fib_index,
+ &src_ip, &dst_ip,
+ ntohs(mp->src_port),
+ ntohs(mp->dst_port),
+ UDP_ENCAP_FIXUP_NONE);
+ }
+ else
+ {
+ udp_encap_unlock(ue_id);
+ }
+
+ done:
+ REPLY_MACRO (VL_API_UDP_ENCAP_ADD_DEL_REPLY);
+}
+
+#define vl_msg_name_crc_list
+#include <vnet/udp/udp.api.h>
+#undef vl_msg_name_crc_list
+
+static void
+setup_message_id_table (api_main_t * am)
+{
+#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
+ foreach_vl_msg_name_crc_udp;
+#undef _
+}
+
+static clib_error_t *
+udp_api_hookup (vlib_main_t * vm)
+{
+ api_main_t *am = &api_main;
+
+#define _(N,n) \
+ vl_msg_api_set_handlers(VL_API_##N, #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_udp_api_msg;
+#undef _
+
+ /*
+ * Set up the (msg_name, crc, message-id) table
+ */
+ setup_message_id_table (am);
+
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (udp_api_hookup);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/udp/udp_encap.c b/src/vnet/udp/udp_encap.c
new file mode 100644
index 00000000000..98b824ba866
--- /dev/null
+++ b/src/vnet/udp/udp_encap.c
@@ -0,0 +1,617 @@
+/*
+ * 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/udp/udp_encap.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/dpo/drop_dpo.h>
+
+/**
+ * Registered DPO types for the IP header encapsulated, v4 or v6.
+ */
+dpo_type_t udp_encap_dpo_types[FIB_PROTOCOL_MAX];
+
+/**
+ * Hash DB to map from client ID to VPP index.
+ */
+uword *udp_encap_db;
+
+/**
+ * Pool of encaps
+ */
+udp_encap_t *udp_encap_pool;
+
+static udp_encap_t *
+udp_encap_get_w_id (u32 id)
+{
+ udp_encap_t *ue = NULL;
+ index_t uei;
+
+ uei = udp_encap_find (id);
+
+ if (INDEX_INVALID != uei)
+ {
+ ue = udp_encap_get (uei);
+ }
+
+ return (ue);
+}
+
+static void
+udp_encap_restack (udp_encap_t * ue)
+{
+ dpo_stack (udp_encap_dpo_types[ue->ue_ip_proto],
+ fib_proto_to_dpo (ue->ue_ip_proto),
+ &ue->ue_dpo,
+ fib_entry_contribute_ip_forwarding (ue->ue_fib_entry_index));
+}
+
+index_t
+udp_encap_add_and_lock (u32 id,
+ fib_protocol_t proto,
+ index_t fib_index,
+ const ip46_address_t * src_ip,
+ const ip46_address_t * dst_ip,
+ u16 src_port,
+ u16 dst_port, udp_encap_fixup_flags_t flags)
+{
+ udp_encap_t *ue;
+ index_t uei;
+
+ uei = udp_encap_find (id);
+
+ if (INDEX_INVALID == uei)
+ {
+ u8 pfx_len = 0;
+
+ pool_get (udp_encap_pool, ue);
+ uei = ue - udp_encap_pool;
+
+ hash_set (udp_encap_db, id, uei);
+
+ fib_node_init (&ue->ue_fib_node, FIB_NODE_TYPE_UDP_ENCAP);
+ fib_node_lock (&ue->ue_fib_node);
+ ue->ue_fib_index = fib_index;
+ ue->ue_flags = flags;
+ ue->ue_id = id;
+ ue->ue_ip_proto = proto;
+
+ switch (proto)
+ {
+ case FIB_PROTOCOL_IP4:
+ pfx_len = 32;
+ ue->ue_hdrs.ip4.ue_ip4.ip_version_and_header_length = 0x45;
+ ue->ue_hdrs.ip4.ue_ip4.ttl = 254;
+ ue->ue_hdrs.ip4.ue_ip4.protocol = IP_PROTOCOL_UDP;
+ ue->ue_hdrs.ip4.ue_ip4.src_address.as_u32 = src_ip->ip4.as_u32;
+ ue->ue_hdrs.ip4.ue_ip4.dst_address.as_u32 = dst_ip->ip4.as_u32;
+ ue->ue_hdrs.ip4.ue_ip4.checksum =
+ ip4_header_checksum (&ue->ue_hdrs.ip4.ue_ip4);
+ ue->ue_hdrs.ip4.ue_udp.src_port = clib_host_to_net_u16 (src_port);
+ ue->ue_hdrs.ip4.ue_udp.dst_port = clib_host_to_net_u16 (dst_port);
+
+ break;
+ case FIB_PROTOCOL_IP6:
+ pfx_len = 128;
+ ue->ue_hdrs.ip6.ue_ip6.ip_version_traffic_class_and_flow_label =
+ clib_host_to_net_u32 (6 << 28);
+ ue->ue_hdrs.ip6.ue_ip6.hop_limit = 255;
+ ue->ue_hdrs.ip6.ue_ip6.protocol = IP_PROTOCOL_UDP;
+ ue->ue_hdrs.ip6.ue_ip6.src_address.as_u64[0] =
+ src_ip->ip6.as_u64[0];
+ ue->ue_hdrs.ip6.ue_ip6.src_address.as_u64[1] =
+ src_ip->ip6.as_u64[1];
+ ue->ue_hdrs.ip6.ue_ip6.dst_address.as_u64[0] =
+ dst_ip->ip6.as_u64[0];
+ ue->ue_hdrs.ip6.ue_ip6.dst_address.as_u64[1] =
+ dst_ip->ip6.as_u64[1];
+ ue->ue_hdrs.ip6.ue_udp.src_port = clib_host_to_net_u16 (src_port);
+ ue->ue_hdrs.ip6.ue_udp.dst_port = clib_host_to_net_u16 (dst_port);
+
+ break;
+ default:
+ ASSERT (0);
+ }
+
+ /*
+ * track the destination address
+ */
+ fib_prefix_t dst_pfx = {
+ .fp_proto = proto,
+ .fp_len = pfx_len,
+ .fp_addr = *dst_ip,
+ };
+
+ ue->ue_fib_entry_index =
+ fib_table_entry_special_add (fib_index,
+ &dst_pfx,
+ FIB_SOURCE_RR, FIB_ENTRY_FLAG_NONE);
+ ue->ue_fib_sibling =
+ fib_entry_child_add (ue->ue_fib_entry_index,
+ FIB_NODE_TYPE_UDP_ENCAP, uei);
+
+ udp_encap_restack (ue);
+ }
+ else
+ {
+ /*
+ * existing entry. updates not supported yet
+ */
+ uei = INDEX_INVALID;
+ }
+ return (uei);
+}
+
+void
+udp_encap_contribute_forwarding (u32 id, dpo_proto_t proto, dpo_id_t * dpo)
+{
+ index_t uei;
+
+ uei = udp_encap_find (id);
+
+ if (INDEX_INVALID == uei)
+ {
+ dpo_copy (dpo, drop_dpo_get (proto));
+ }
+ else
+ {
+ udp_encap_t *ue;
+
+ ue = udp_encap_get (uei);
+
+ dpo_set (dpo, udp_encap_dpo_types[ue->ue_ip_proto], proto, uei);
+ }
+}
+
+index_t
+udp_encap_find (u32 id)
+{
+ uword *p;
+
+ p = hash_get (udp_encap_db, id);
+
+ if (NULL != p)
+ return p[0];
+
+ return INDEX_INVALID;
+}
+
+void
+udp_encap_lock (u32 id)
+{
+ udp_encap_t *ue;
+
+ ue = udp_encap_get_w_id (id);
+
+ if (NULL != ue)
+ {
+ fib_node_lock (&ue->ue_fib_node);
+ }
+}
+
+void
+udp_encap_unlock_w_index (index_t uei)
+{
+ udp_encap_t *ue;
+
+ if (INDEX_INVALID == uei)
+ {
+ return;
+ }
+
+ ue = udp_encap_get (uei);
+
+ if (NULL != ue)
+ {
+ fib_node_unlock (&ue->ue_fib_node);
+ }
+}
+
+void
+udp_encap_unlock (u32 id)
+{
+ udp_encap_t *ue;
+
+ ue = udp_encap_get_w_id (id);
+
+ if (NULL != ue)
+ {
+ fib_node_unlock (&ue->ue_fib_node);
+ }
+}
+
+static void
+udp_encap_dpo_lock (dpo_id_t * dpo)
+{
+ udp_encap_t *ue;
+
+ ue = udp_encap_get (dpo->dpoi_index);
+
+ fib_node_lock (&ue->ue_fib_node);
+}
+
+static void
+udp_encap_dpo_unlock (dpo_id_t * dpo)
+{
+ udp_encap_t *ue;
+
+ ue = udp_encap_get (dpo->dpoi_index);
+
+ fib_node_unlock (&ue->ue_fib_node);
+}
+
+static u8 *
+format_udp_encap_i (u8 * s, va_list * args)
+{
+ index_t uei = va_arg (*args, index_t);
+ u32 indent = va_arg (*args, u32);
+ u32 details = va_arg (*args, u32);
+ udp_encap_t *ue;
+
+ ue = udp_encap_get (uei);
+
+ // FIXME
+ s = format (s, "udp-ecap:[%d]: id:%d ip-fib-index:%d",
+ uei, ue->ue_id, ue->ue_fib_index);
+ if (FIB_PROTOCOL_IP4 == ue->ue_ip_proto)
+ {
+ s = format (s, "ip:[src:%U, dst:%U] udp:[src:%d, dst:%d]",
+ format_ip4_address,
+ &ue->ue_hdrs.ip4.ue_ip4.src_address,
+ format_ip4_address,
+ &ue->ue_hdrs.ip4.ue_ip4.dst_address,
+ clib_net_to_host_u16 (ue->ue_hdrs.ip4.ue_udp.src_port),
+ clib_net_to_host_u16 (ue->ue_hdrs.ip4.ue_udp.dst_port));
+ }
+ else
+ {
+ s = format (s, "ip:[src:%U, dst:%U] udp:[src:%d dst:%d]",
+ format_ip6_address,
+ &ue->ue_hdrs.ip6.ue_ip6.src_address,
+ format_ip6_address,
+ &ue->ue_hdrs.ip6.ue_ip6.dst_address,
+ clib_net_to_host_u16 (ue->ue_hdrs.ip6.ue_udp.src_port),
+ clib_net_to_host_u16 (ue->ue_hdrs.ip6.ue_udp.dst_port));
+ }
+ if (details)
+ {
+ s = format (s, " locks:%d", ue->ue_fib_node.fn_locks);
+ s = format (s, "\n%UStacked on:", format_white_space, indent + 1);
+ s = format (s, "\n%U%U",
+ format_white_space, indent + 2,
+ format_dpo_id, &ue->ue_dpo, indent + 3);
+ }
+ return (s);
+}
+
+static u8 *
+format_udp_encap_dpo (u8 * s, va_list * args)
+{
+ index_t uei = va_arg (*args, index_t);
+ u32 indent = va_arg (*args, u32);
+
+ return (format (s, "%U", format_udp_encap_i, uei, indent, 1));
+}
+
+u8 *
+format_udp_encap (u8 * s, va_list * args)
+{
+ u32 id = va_arg (*args, u32);
+ u32 details = va_arg (*args, u32);
+ index_t uei;
+
+ uei = udp_encap_find (id);
+
+ if (INDEX_INVALID == uei)
+ {
+ return (format (s, "Invalid udp-encap ID: %d", id));
+ }
+
+ return (format (s, "%U", format_udp_encap_i, uei, 0, details));
+}
+
+static udp_encap_t *
+udp_encap_from_fib_node (fib_node_t * node)
+{
+#if (CLIB_DEBUG > 0)
+ ASSERT (FIB_NODE_TYPE_UDP_ENCAP == node->fn_type);
+#endif
+ return ((udp_encap_t *) (((char *) node) -
+ STRUCT_OFFSET_OF (udp_encap_t, ue_fib_node)));
+}
+
+/**
+ * Function definition to backwalk a FIB node
+ */
+static fib_node_back_walk_rc_t
+udp_encap_fib_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
+{
+ udp_encap_restack (udp_encap_from_fib_node (node));
+
+ return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/**
+ * Function definition to get a FIB node from its index
+ */
+static fib_node_t *
+udp_encap_fib_node_get (fib_node_index_t index)
+{
+ udp_encap_t *ue;
+
+ ue = pool_elt_at_index (udp_encap_pool, index);
+
+ return (&ue->ue_fib_node);
+}
+
+/**
+ * Function definition to inform the FIB node that its last lock has gone.
+ */
+static void
+udp_encap_fib_last_lock_gone (fib_node_t * node)
+{
+ udp_encap_t *ue;
+
+ ue = udp_encap_from_fib_node (node);
+
+ /**
+ * reset the stacked DPO to unlock it
+ */
+ dpo_reset (&ue->ue_dpo);
+ hash_unset (udp_encap_db, ue->ue_id);
+
+ fib_entry_child_remove (ue->ue_fib_entry_index, ue->ue_fib_sibling);
+ fib_table_entry_delete_index (ue->ue_fib_entry_index, FIB_SOURCE_RR);
+
+
+ pool_put (udp_encap_pool, ue);
+}
+
+const static char *const udp4_encap_ip4_nodes[] = {
+ "udp4-encap",
+ NULL,
+};
+
+const static char *const udp4_encap_ip6_nodes[] = {
+ "udp4-encap",
+ NULL,
+};
+
+const static char *const udp4_encap_mpls_nodes[] = {
+ "udp4-encap",
+ NULL,
+};
+
+const static char *const udp6_encap_ip4_nodes[] = {
+ "udp6-encap",
+ NULL,
+};
+
+const static char *const udp6_encap_ip6_nodes[] = {
+ "udp6-encap",
+ NULL,
+};
+
+const static char *const udp6_encap_mpls_nodes[] = {
+ "udp6-encap",
+ NULL,
+};
+
+const static char *const *const udp4_encap_nodes[DPO_PROTO_NUM] = {
+ [DPO_PROTO_IP4] = udp4_encap_ip4_nodes,
+ [DPO_PROTO_IP6] = udp4_encap_ip6_nodes,
+ [DPO_PROTO_MPLS] = udp4_encap_mpls_nodes,
+};
+
+const static char *const *const udp6_encap_nodes[DPO_PROTO_NUM] = {
+ [DPO_PROTO_IP4] = udp6_encap_ip4_nodes,
+ [DPO_PROTO_IP6] = udp6_encap_ip6_nodes,
+ [DPO_PROTO_MPLS] = udp6_encap_mpls_nodes,
+};
+
+/*
+ * Virtual function table registered by UDP encaps
+ * for participation in the FIB object graph.
+ */
+const static fib_node_vft_t udp_encap_fib_vft = {
+ .fnv_get = udp_encap_fib_node_get,
+ .fnv_last_lock = udp_encap_fib_last_lock_gone,
+ .fnv_back_walk = udp_encap_fib_back_walk,
+};
+
+const static dpo_vft_t udp_encap_dpo_vft = {
+ .dv_lock = udp_encap_dpo_lock,
+ .dv_unlock = udp_encap_dpo_unlock,
+ .dv_format = format_udp_encap_dpo,
+ //.dv_mem_show = replicate_mem_show,
+};
+
+clib_error_t *
+udp_encap_init (vlib_main_t * vm)
+{
+ udp_encap_db = hash_create (0, sizeof (index_t));
+
+ fib_node_register_type (FIB_NODE_TYPE_UDP_ENCAP, &udp_encap_fib_vft);
+
+ udp_encap_dpo_types[FIB_PROTOCOL_IP4] =
+ dpo_register_new_type (&udp_encap_dpo_vft, udp4_encap_nodes);
+ udp_encap_dpo_types[FIB_PROTOCOL_IP6] =
+ dpo_register_new_type (&udp_encap_dpo_vft, udp6_encap_nodes);
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (udp_encap_init);
+
+clib_error_t *
+udp_encap_cli (vlib_main_t * vm,
+ unformat_input_t * main_input, vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ clib_error_t *error = NULL;
+ ip46_address_t src_ip, dst_ip;
+ u32 table_id, ue_id;
+ u32 src_port, dst_port;
+ udp_encap_fixup_flags_t flags;
+ fib_protocol_t fproto;
+ u8 is_del;
+
+ is_del = 0;
+ table_id = 0;
+ flags = UDP_ENCAP_FIXUP_NONE;
+ fproto = FIB_PROTOCOL_MAX;
+ dst_port = 0;
+ ue_id = ~0;
+
+ /* Get a line of input. */
+ if (!unformat_user (main_input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "id %d", &ue_id))
+ ;
+ else if (unformat (line_input, "add"))
+ is_del = 0;
+ else if (unformat (line_input, "del"))
+ is_del = 1;
+ else if (unformat (line_input, "%U %U",
+ unformat_ip4_address,
+ &src_ip.ip4, unformat_ip4_address, &dst_ip.ip4))
+ fproto = FIB_PROTOCOL_IP4;
+ else if (unformat (line_input, "%U %U",
+ unformat_ip6_address,
+ &src_ip.ip6, unformat_ip6_address, &dst_ip.ip6))
+ fproto = FIB_PROTOCOL_IP6;
+ else if (unformat (line_input, "%d %d", &src_port, &dst_port))
+ ;
+ else if (unformat (line_input, "%d", &dst_port))
+ ;
+ else if (unformat (line_input, "table-id %d", &table_id))
+ ;
+ else if (unformat (line_input, "src-port-is-entropy"))
+ flags |= UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY;
+ else
+ {
+ error = unformat_parse_error (line_input);
+ goto done;
+ }
+ }
+
+ if (~0 == ue_id)
+ {
+ error =
+ clib_error_return (0, "An ID for the UDP encap instance is required");
+ goto done;
+ }
+
+ if (!is_del && fproto != FIB_PROTOCOL_MAX)
+ {
+ u32 fib_index;
+ index_t uei;
+
+ fib_index = fib_table_find (fproto, table_id);
+
+ if (~0 == fib_index)
+ {
+ error = clib_error_return (0, "Nonexistent table id %d", table_id);
+ goto done;
+ }
+
+ uei = udp_encap_add_and_lock (ue_id, fproto, fib_index,
+ &src_ip, &dst_ip,
+ src_port, dst_port, flags);
+
+ if (INDEX_INVALID == uei)
+ {
+ error =
+ clib_error_return (0, "update to existing encap not supported %d",
+ ue_id);
+ goto done;
+ }
+ }
+ else if (is_del)
+ {
+ udp_encap_unlock (ue_id);
+ }
+ else
+ {
+ error =
+ clib_error_return (0,
+ "Some IP addresses would be usefull, don't you think?",
+ ue_id);
+ }
+
+done:
+ unformat_free (line_input);
+ return error;
+}
+
+clib_error_t *
+udp_encap_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ u32 ue_id;
+
+ ue_id = ~0;
+
+ /* Get a line of input. */
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%d", &ue_id))
+ ;
+ }
+
+ if (~0 == ue_id)
+ {
+ udp_encap_t *ue;
+
+ /* *INDENT-OFF* */
+ pool_foreach(ue, udp_encap_pool,
+ ({
+ vlib_cli_output(vm, "%U", format_udp_encap, ue->ue_id, 0);
+ }));
+ /* *INDENT-ON* */
+ }
+ else
+ {
+ vlib_cli_output (vm, "%U", format_udp_encap, ue_id, 1);
+ }
+
+ return NULL;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (udp_encap_add_command, static) = {
+ .path = "udp encap",
+ .short_help = "udp encap [add|del] <id ID> <src-ip> <dst-ip> [<src-port>] <dst-port> [src-port-is-entropy] [table-id <table>]",
+ .function = udp_encap_cli,
+ .is_mp_safe = 1,
+};
+VLIB_CLI_COMMAND (udp_encap_show_command, static) = {
+ .path = "show udp encap",
+ .short_help = "show udp encap [ID]",
+ .function = udp_encap_show,
+ .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/udp/udp_encap.h b/src/vnet/udp/udp_encap.h
new file mode 100644
index 00000000000..b8f329dcbee
--- /dev/null
+++ b/src/vnet/udp/udp_encap.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __UDP_ENCAP_H__
+#define __UDP_ENCAP_H__
+
+#include <vnet/ip/ip.h>
+#include <vnet/udp/udp.h>
+#include <vnet/fib/fib_node.h>
+
+/**
+ * UDP encapsualtion.
+ * A representation of the encapsulation of packets in UDP-over-IP.
+ * This is encapsulation only, there is no tunnel interface, hence
+ * it is uni-directional. For decap register a handler with the UDP port
+ * dispatcher.
+ */
+
+/**
+ * Fixup behaviour. Actions performed on the encap in the data-plance
+ */
+typedef enum udp_encap_fixup_flags_t_
+{
+ UDP_ENCAP_FIXUP_NONE = 0,
+ /**
+ * UDP source port contains an entropy/hash value for load-balancing by downstream peers.
+ */
+ UDP_ENCAP_FIXUP_UDP_SRC_PORT_ENTROPY = (1 << 0),
+} udp_encap_fixup_flags_t;
+
+/**
+ * The UDP encap represenation
+ */
+typedef struct udp_encap_t_
+{
+ /**
+ * The first cacheline contains the data used in the data-plane
+ */
+ CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+
+ /**
+ * The headers to paint, in packet painting order
+ */
+ union
+ {
+ struct
+ {
+ ip4_header_t ue_ip4;
+ udp_header_t ue_udp;
+ } __attribute__ ((packed)) ip4;
+ struct
+ {
+ ip6_header_t ue_ip6;
+ udp_header_t ue_udp;
+ } __attribute__ ((packed)) ip6;
+ } __attribute__ ((packed)) ue_hdrs;
+
+ /**
+ * Flags controlling fixup behaviour
+ */
+ udp_encap_fixup_flags_t ue_flags;
+
+ /**
+ * The DPO used to forward to the next node in the VLIB graph
+ */
+ dpo_id_t ue_dpo;
+
+ /**
+ * the protocol of the IP header imposed
+ */
+ fib_protocol_t ue_ip_proto;
+
+ CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);
+
+ /**
+ * linkage into the FIB graph
+ */
+ fib_node_t ue_fib_node;
+
+ /**
+ * The ID given by the user/client.
+ * This ID is used by the client for modifications.
+ */
+ u32 ue_id;
+
+ /**
+ * Tracking information for the IP destination
+ */
+ fib_node_index_t ue_fib_entry_index;
+ u32 ue_fib_sibling;
+
+ /**
+ * The FIB index in which the encap destination resides
+ */
+ index_t ue_fib_index;
+} udp_encap_t;
+
+extern index_t udp_encap_add_and_lock (u32 id,
+ fib_protocol_t proto,
+ index_t fib_index,
+ const ip46_address_t * src_ip,
+ const ip46_address_t * dst_ip,
+ u16 src_port,
+ u16 dst_port,
+ udp_encap_fixup_flags_t flags);
+
+extern index_t udp_encap_find (u32 id);
+extern void udp_encap_lock (u32 id);
+extern void udp_encap_unlock (u32 id);
+extern u8 *format_udp_encap (u8 * s, va_list * args);
+extern void udp_encap_unlock_w_index (index_t uei);
+extern void udp_encap_contribute_forwarding (u32 id,
+ dpo_proto_t proto,
+ dpo_id_t * dpo);
+
+/**
+ * Pool of encaps
+ */
+extern udp_encap_t *udp_encap_pool;
+
+static inline udp_encap_t *
+udp_encap_get (index_t uei)
+{
+ return (pool_elt_at_index (udp_encap_pool, uei));
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
+
+#endif
diff --git a/src/vnet/udp/udp_encap_node.c b/src/vnet/udp/udp_encap_node.c
new file mode 100644
index 00000000000..09a76b530f6
--- /dev/null
+++ b/src/vnet/udp/udp_encap_node.c
@@ -0,0 +1,280 @@
+/*
+ * 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/udp/udp_encap.h>
+
+typedef struct udp4_encap_trace_t_
+{
+ udp_header_t udp;
+ ip4_header_t ip;
+} udp4_encap_trace_t;
+
+typedef struct udp6_encap_trace_t_
+{
+ udp_header_t udp;
+ ip6_header_t ip;
+} udp6_encap_trace_t;
+
+static u8 *
+format_udp4_encap_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ udp4_encap_trace_t *t;
+
+ t = va_arg (*args, udp4_encap_trace_t *);
+
+ s = format (s, "%U\n %U",
+ format_ip4_header, &t->ip, sizeof (t->ip),
+ format_udp_header, &t->udp, sizeof (t->udp));
+ return (s);
+}
+
+static u8 *
+format_udp6_encap_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ udp6_encap_trace_t *t;
+
+ t = va_arg (*args, udp6_encap_trace_t *);
+
+ s = format (s, "%U\n %U",
+ format_ip6_header, &t->ip, sizeof (t->ip),
+ format_udp_header, &t->udp, sizeof (t->udp));
+ return (s);
+}
+
+always_inline uword
+udp_encap_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame, int is_encap_v6)
+{
+ u32 *from = vlib_frame_vector_args (frame);
+ u32 n_left_from, n_left_to_next, *to_next, next_index;
+
+ 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 >= 4 && n_left_to_next >= 2)
+ {
+ vlib_buffer_t *b0, *b1;
+ udp_encap_t *ue0, *ue1;
+ u32 bi0, next0, uei0;
+ u32 bi1, next1, uei1;
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, STORE);
+ vlib_prefetch_buffer_header (p3, STORE);
+ }
+
+ bi0 = to_next[0] = from[0];
+ bi1 = to_next[1] = from[1];
+
+ from += 2;
+ n_left_from -= 2;
+ to_next += 2;
+ n_left_to_next -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+
+ uei0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
+ uei1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
+
+ /* Rewrite packet header and updates lengths. */
+ ue0 = udp_encap_get (uei0);
+ ue1 = udp_encap_get (uei1);
+
+ /* Paint */
+ if (is_encap_v6)
+ {
+ const u8 n_bytes =
+ sizeof (udp_header_t) + sizeof (ip6_header_t);
+ ip_udp_encap_two (vm, b0, b1, (u8 *) & ue0->ue_hdrs,
+ (u8 *) & ue1->ue_hdrs, n_bytes, 0);
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ udp6_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->udp = ue0->ue_hdrs.ip6.ue_udp;
+ tr->ip = ue0->ue_hdrs.ip6.ue_ip6;
+ }
+ if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ udp6_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b1, sizeof (*tr));
+ tr->udp = ue1->ue_hdrs.ip6.ue_udp;
+ tr->ip = ue1->ue_hdrs.ip6.ue_ip6;
+ }
+ }
+ else
+ {
+ const u8 n_bytes =
+ sizeof (udp_header_t) + sizeof (ip4_header_t);
+
+ ip_udp_encap_two (vm, b0, b1,
+ (u8 *) & ue0->ue_hdrs,
+ (u8 *) & ue1->ue_hdrs, n_bytes, 1);
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ udp4_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->udp = ue0->ue_hdrs.ip4.ue_udp;
+ tr->ip = ue0->ue_hdrs.ip4.ue_ip4;
+ }
+ if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ udp4_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b1, sizeof (*tr));
+ tr->udp = ue1->ue_hdrs.ip4.ue_udp;
+ tr->ip = ue1->ue_hdrs.ip4.ue_ip4;
+ }
+ }
+
+ next0 = ue0->ue_dpo.dpoi_next_node;
+ next1 = ue1->ue_dpo.dpoi_next_node;
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ue0->ue_dpo.dpoi_index;
+ vnet_buffer (b1)->ip.adj_index[VLIB_TX] = ue1->ue_dpo.dpoi_index;
+
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, next0, next1);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0, next0, uei0;
+ vlib_buffer_t *b0;
+ udp_encap_t *ue0;
+
+ 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);
+
+ uei0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
+
+ /* Rewrite packet header and updates lengths. */
+ ue0 = udp_encap_get (uei0);
+
+ /* Paint */
+ if (is_encap_v6)
+ {
+ const u8 n_bytes =
+ sizeof (udp_header_t) + sizeof (ip6_header_t);
+ ip_udp_encap_one (vm, b0, (u8 *) & ue0->ue_hdrs.ip6, n_bytes,
+ 0);
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ udp6_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->udp = ue0->ue_hdrs.ip6.ue_udp;
+ tr->ip = ue0->ue_hdrs.ip6.ue_ip6;
+ }
+ }
+ else
+ {
+ const u8 n_bytes =
+ sizeof (udp_header_t) + sizeof (ip4_header_t);
+
+ ip_udp_encap_one (vm, b0, (u8 *) & ue0->ue_hdrs.ip4, n_bytes,
+ 1);
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ udp4_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->udp = ue0->ue_hdrs.ip4.ue_udp;
+ tr->ip = ue0->ue_hdrs.ip4.ue_ip4;
+ }
+ }
+
+ next0 = ue0->ue_dpo.dpoi_next_node;
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ue0->ue_dpo.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 frame->n_vectors;
+}
+
+static uword
+udp4_encap (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ return udp_encap_inline (vm, node, frame, 0);
+}
+
+static uword
+udp6_encap (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ return udp_encap_inline (vm, node, frame, 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (udp4_encap_node) = {
+ .function = udp4_encap,
+ .name = "udp4-encap",
+ .vector_size = sizeof (u32),
+
+ .format_trace = format_udp4_encap_trace,
+
+ .n_next_nodes = 0,
+};
+VLIB_NODE_FUNCTION_MULTIARCH (udp4_encap_node, udp4_encap);
+
+VLIB_REGISTER_NODE (udp6_encap_node) = {
+ .function = udp6_encap,
+ .name = "udp6-encap",
+ .vector_size = sizeof (u32),
+
+ .format_trace = format_udp6_encap_trace,
+
+ .n_next_nodes = 0,
+};
+VLIB_NODE_FUNCTION_MULTIARCH (udp6_encap_node, udp6_encap);
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */