aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorDamjan Marion <damarion@cisco.com>2016-12-19 23:05:39 +0100
committerDamjan Marion <damarion@cisco.com>2016-12-28 12:25:14 +0100
commit7cd468a3d7dee7d6c92f69a0bb7061ae208ec727 (patch)
tree5de62f8dbd3a752f5a676ca600e43d2652d1ff1a /src/plugins
parent696f1adec0df3b8f161862566dd9c86174302658 (diff)
Reorganize source tree to use single autotools instance
Change-Id: I7b51f88292e057c6443b12224486f2d0c9f8ae23 Signed-off-by: Damjan Marion <damarion@cisco.com>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/Makefile.am60
-rw-r--r--src/plugins/flowperpkt.am38
-rw-r--r--src/plugins/flowperpkt/flowperpkt.api42
-rw-r--r--src/plugins/flowperpkt/flowperpkt.c671
-rw-r--r--src/plugins/flowperpkt/flowperpkt.h90
-rw-r--r--src/plugins/flowperpkt/flowperpkt_all_api_h.h18
-rw-r--r--src/plugins/flowperpkt/flowperpkt_msg_enum.h31
-rw-r--r--src/plugins/flowperpkt/flowperpkt_plugin_doc.md13
-rw-r--r--src/plugins/flowperpkt/flowperpkt_test.c234
-rw-r--r--src/plugins/flowperpkt/l2_node.c561
-rw-r--r--src/plugins/flowperpkt/node.c574
-rw-r--r--src/plugins/ila.am20
-rw-r--r--src/plugins/ila/ila.c1070
-rw-r--r--src/plugins/ila/ila.h116
-rw-r--r--src/plugins/sixrd.am26
-rw-r--r--src/plugins/sixrd/ip4_sixrd.c127
-rw-r--r--src/plugins/sixrd/ip6_sixrd.c129
-rw-r--r--src/plugins/sixrd/sixrd.c369
-rw-r--r--src/plugins/sixrd/sixrd.h141
-rw-r--r--src/plugins/sixrd/sixrd_dpo.c132
-rw-r--r--src/plugins/sixrd/sixrd_dpo.h61
21 files changed, 4523 insertions, 0 deletions
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
new file mode 100644
index 00000000000..ffc4b3abddc
--- /dev/null
+++ b/src/plugins/Makefile.am
@@ -0,0 +1,60 @@
+
+# Copyright (c) <current-year> <your-organization>
+# 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.
+
+AUTOMAKE_OPTIONS = foreign subdir-objects
+
+AM_CFLAGS = -Wall -I${top_srcdir} -I${top_builddir}
+AM_LDFLAGS = -module -shared -avoid-version
+SUFFIXES = .api.h .api .api.json
+API_FILES =
+BUILT_SOURCES =
+vppplugins_LTLIBRARIES =
+vppapitestplugins_LTLIBRARIES =
+noinst_HEADERS =
+
+vppapitestpluginsdir = ${libdir}/vpp_api_test_plugins
+vpppluginsdir = ${libdir}/vpp_plugins
+
+if ENABLE_FLOWPERPKT_PLUGIN
+include flowperpkt.am
+endif
+
+if ENABLE_ILA_PLUGIN
+include ila.am
+endif
+
+if ENABLE_SIXRD_PLUGIN
+include sixrd.am
+endif
+
+include ../suffix-rules.mk
+
+# Remove *.la files
+install-data-hook:
+ @-(cd $(vpppluginsdir) && $(RM) $(vppplugins_LTLIBRARIES))
+ @-(cd $(vppapitestpluginsdir) && $(RM) $(vppapitestplugins_LTLIBRARIES))
+
+###############################################################################
+# API
+###############################################################################
+
+apidir = $(prefix)/share/vpp/api/plugins
+
+api_DATA = \
+ $(patsubst %.api,%.api.json,$(API_FILES))
+
+BUILT_SOURCES += \
+ $(patsubst %.api,%.api.json,$(API_FILES)) \
+ $(patsubst %.api,%.api.h,$(API_FILES))
+
diff --git a/src/plugins/flowperpkt.am b/src/plugins/flowperpkt.am
new file mode 100644
index 00000000000..a400603a71f
--- /dev/null
+++ b/src/plugins/flowperpkt.am
@@ -0,0 +1,38 @@
+
+# Copyright (c) <current-year> <your-organization>
+# 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.
+
+vppplugins_LTLIBRARIES += flowperpkt_plugin.la
+vppapitestplugins_LTLIBRARIES += flowperpkt_test_plugin.la
+
+flowperpkt_plugin_la_SOURCES = flowperpkt/flowperpkt.c \
+ flowperpkt/l2_node.c \
+ flowperpkt/node.c \
+ flowperpkt/flowperpkt_plugin.api.h
+
+BUILT_SOURCES += \
+ flowperpkt/flowperpkt.api.h \
+ flowperpkt/flowperpkt.api.json
+
+noinst_HEADERS += \
+ flowperpkt/flowperpkt_all_api_h.h \
+ flowperpkt/flowperpkt_msg_enum.h \
+ flowperpkt/flowperpkt.api.h
+
+flowperpkt_test_plugin_la_SOURCES = \
+ flowperpkt/flowperpkt_test.c \
+ flowperpkt/flowperpkt_plugin.api.h
+
+API_FILES += flowperpkt/flowperpkt.api
+
+# vi:syntax=automake
diff --git a/src/plugins/flowperpkt/flowperpkt.api b/src/plugins/flowperpkt/flowperpkt.api
new file mode 100644
index 00000000000..fa878f21ed3
--- /dev/null
+++ b/src/plugins/flowperpkt/flowperpkt.api
@@ -0,0 +1,42 @@
+/* Define a simple enable-disable binary API to control the feature */
+
+/** \file
+ This file defines the vpp control-plane API messages
+ used to control the flowperpkt plugin
+*/
+
+/** \brief Enable / disable per-packet IPFIX recording on an interface
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param is_add - add address if non-zero, else delete
+ @param is_ipv6 - if non-zero the address is ipv6, else ipv4
+ @param sw_if_index - index of the interface
+*/
+manual_print define flowperpkt_tx_interface_add_del
+{
+ /* Client identifier, set from api_main.my_client_index */
+ u32 client_index;
+
+ /* Arbitrary context, so client can match reply to request */
+ u32 context;
+
+ /* Enable / disable the feature */
+ u8 is_add;
+ u8 which; /* 0 = ipv4, 1 = l2, 2 = ipv6 (not yet implemented) */
+
+ /* Interface handle */
+ u32 sw_if_index;
+};
+
+/** \brief Reply to enable/disable per-packet IPFIX recording messages
+ @param context - returned sender context, to match reply w/ request
+ @param retval - return code
+*/
+define flowperpkt_tx_interface_add_del_reply
+{
+ /* From the request */
+ u32 context;
+
+ /* Return value, zero means all OK */
+ i32 retval;
+};
diff --git a/src/plugins/flowperpkt/flowperpkt.c b/src/plugins/flowperpkt/flowperpkt.c
new file mode 100644
index 00000000000..fb71d5b0ffc
--- /dev/null
+++ b/src/plugins/flowperpkt/flowperpkt.c
@@ -0,0 +1,671 @@
+/*
+ * flowperpkt.c - per-packet data capture flow report plugin
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file
+ * @brief Per-packet IPFIX flow record generator plugin
+ *
+ * This file implements vpp plugin registration mechanics,
+ * debug CLI, and binary API handling.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+#include <flowperpkt/flowperpkt.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vlibsocket/api.h>
+
+/* define message IDs */
+#include <flowperpkt/flowperpkt_msg_enum.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <flowperpkt/flowperpkt_all_api_h.h>
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <flowperpkt/flowperpkt_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 <flowperpkt/flowperpkt_all_api_h.h>
+#undef vl_printfun
+
+flowperpkt_main_t flowperpkt_main;
+
+/* Get the API version number */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <flowperpkt/flowperpkt_all_api_h.h>
+#undef vl_api_version
+
+/* Define the per-interface configurable features */
+/* *INDENT-OFF* */
+VNET_FEATURE_INIT (flow_perpacket_ipv4, static) =
+{
+ .arc_name = "ip4-output",
+ .node_name = "flowperpkt-ipv4",
+ .runs_before = VNET_FEATURES ("interface-output"),
+};
+
+VNET_FEATURE_INIT (flow_perpacket_l2, static) =
+{
+ .arc_name = "interface-output",
+ .node_name = "flowperpkt-l2",
+ .runs_before = VNET_FEATURES ("interface-tx"),
+};
+/* *INDENT-ON* */
+
+/*
+ * A handy macro to set up a message reply.
+ * Assumes that the following variables are available:
+ * mp - pointer to request message
+ * rmp - pointer to reply message type
+ * rv - return value
+ */
+#define REPLY_MACRO(t) \
+do { \
+ unix_shared_memory_queue_t * q = \
+ vl_api_client_index_to_input_queue (mp->client_index); \
+ if (!q) \
+ return; \
+ \
+ rmp = vl_msg_api_alloc (sizeof (*rmp)); \
+ rmp->_vl_msg_id = ntohs((t)+fm->msg_id_base); \
+ rmp->context = mp->context; \
+ rmp->retval = ntohl(rv); \
+ \
+ vl_msg_api_send_shmem (q, (u8 *)&rmp); \
+} while(0);
+
+/* Macro to finish up custom dump fns */
+#define FINISH \
+ vec_add1 (s, 0); \
+ vl_print (handle, (char *)s); \
+ vec_free (s); \
+ return handle;
+
+#define VALIDATE_SW_IF_INDEX(mp) \
+ do { u32 __sw_if_index = ntohl(mp->sw_if_index); \
+ vnet_main_t *__vnm = vnet_get_main(); \
+ if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
+ __sw_if_index)) { \
+ rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \
+ goto bad_sw_if_index; \
+ } \
+} while(0);
+
+#define BAD_SW_IF_INDEX_LABEL \
+do { \
+bad_sw_if_index: \
+ ; \
+} while (0);
+
+/**
+ * @brief Create an IPFIX template packet rewrite string
+ * @param frm flow_report_main_t *
+ * @param fr flow_report_t *
+ * @param collector_address ip4_address_t * the IPFIX collector address
+ * @param src_address ip4_address_t * the source address we should use
+ * @param collector_port u16 the collector port we should use, host byte order
+ * @returns u8 * vector containing the indicated IPFIX template packet
+ */
+static inline u8 *
+flowperpkt_template_rewrite_inline (flow_report_main_t * frm,
+ flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address,
+ u16 collector_port, int variant)
+{
+ ip4_header_t *ip;
+ udp_header_t *udp;
+ ipfix_message_header_t *h;
+ ipfix_set_header_t *s;
+ ipfix_template_header_t *t;
+ ipfix_field_specifier_t *f;
+ ipfix_field_specifier_t *first_field;
+ u8 *rewrite = 0;
+ ip4_ipfix_template_packet_t *tp;
+ u32 field_count = 0;
+ flow_report_stream_t *stream;
+ flowperpkt_main_t *fm = &flowperpkt_main;
+
+ stream = &frm->streams[fr->stream_index];
+
+ if (variant == FLOW_VARIANT_IPV4)
+ {
+ /*
+ * ip4 Supported Fields:
+ *
+ * ingressInterface, TLV type 10, u32
+ * egressInterface, TLV type 14, u32
+ * sourceIpv4Address, TLV type 8, u32
+ * destinationIPv4Address, TLV type 12, u32
+ * ipClassOfService, TLV type 5, u8
+ * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
+ * Implementation: f64 nanoseconds since VPP started
+ * warning: wireshark doesn't really understand this TLV
+ * dataLinkFrameSize, TLV type 312, u16
+ * warning: wireshark doesn't understand this TLV at all
+ */
+
+ /* Currently 7 fields */
+ field_count += 7;
+
+ /* allocate rewrite space */
+ vec_validate_aligned
+ (rewrite,
+ sizeof (ip4_ipfix_template_packet_t)
+ + field_count * sizeof (ipfix_field_specifier_t) - 1,
+ CLIB_CACHE_LINE_BYTES);
+ }
+ else if (variant == FLOW_VARIANT_L2)
+ {
+ /*
+ * L2 Supported Fields:
+ *
+ * ingressInterface, TLV type 10, u32
+ * egressInterface, TLV type 14, u32
+ * sourceMacAddress, TLV type 56, u8[6] we hope
+ * destinationMacAddress, TLV type 57, u8[6] we hope
+ * ethernetType, TLV type 256, u16
+ * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
+ * Implementation: f64 nanoseconds since VPP started
+ * warning: wireshark doesn't really understand this TLV
+ * dataLinkFrameSize, TLV type 312, u16
+ * warning: wireshark doesn't understand this TLV at all
+ */
+
+ /* Currently 7 fields */
+ field_count += 7;
+
+ /* allocate rewrite space */
+ vec_validate_aligned
+ (rewrite,
+ sizeof (ip4_ipfix_template_packet_t)
+ + field_count * sizeof (ipfix_field_specifier_t) - 1,
+ CLIB_CACHE_LINE_BYTES);
+ }
+
+ tp = (ip4_ipfix_template_packet_t *) rewrite;
+ ip = (ip4_header_t *) & tp->ip4;
+ udp = (udp_header_t *) (ip + 1);
+ h = (ipfix_message_header_t *) (udp + 1);
+ s = (ipfix_set_header_t *) (h + 1);
+ t = (ipfix_template_header_t *) (s + 1);
+ first_field = f = (ipfix_field_specifier_t *) (t + 1);
+
+ ip->ip_version_and_header_length = 0x45;
+ ip->ttl = 254;
+ ip->protocol = IP_PROTOCOL_UDP;
+ ip->src_address.as_u32 = src_address->as_u32;
+ ip->dst_address.as_u32 = collector_address->as_u32;
+ udp->src_port = clib_host_to_net_u16 (stream->src_port);
+ udp->dst_port = clib_host_to_net_u16 (collector_port);
+ udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
+
+ /* FIXUP: message header export_time */
+ /* FIXUP: message header sequence_number */
+ h->domain_id = clib_host_to_net_u32 (stream->domain_id);
+
+ /* Add TLVs to the template */
+ if (variant == FLOW_VARIANT_IPV4)
+ {
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , ingressInterface,
+ 4);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , egressInterface,
+ 4);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , sourceIPv4Address,
+ 4);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , destinationIPv4Address, 4);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , ipClassOfService,
+ 1);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
+ 8);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
+ 2);
+ f++;
+ }
+ else if (variant == FLOW_VARIANT_L2)
+ {
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , ingressInterface,
+ 4);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , egressInterface,
+ 4);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , sourceMacAddress,
+ 6);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , destinationMacAddress, 6);
+ f++;
+ f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ethernetType,
+ 2);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
+ 8);
+ f++;
+ f->e_id_length =
+ ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
+ 2);
+ f++;
+ }
+
+ /* Extend in the obvious way, right here... */
+
+ /* Back to the template packet... */
+ ip = (ip4_header_t *) & tp->ip4;
+ udp = (udp_header_t *) (ip + 1);
+
+ ASSERT (f - first_field);
+ /* Field count in this template */
+ t->id_count = ipfix_id_count (fr->template_id, f - first_field);
+
+ if (variant == FLOW_VARIANT_IPV4)
+ fm->ipv4_report_id = fr->template_id;
+ else if (variant == FLOW_VARIANT_L2)
+ fm->l2_report_id = fr->template_id;
+
+ /* set length in octets */
+ s->set_id_length =
+ ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
+
+ /* message length in octets */
+ h->version_length = version_length ((u8 *) f - (u8 *) h);
+
+ ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
+ ip->checksum = ip4_header_checksum (ip);
+
+ return rewrite;
+}
+
+u8 *
+flowperpkt_template_rewrite_ipv4 (flow_report_main_t * frm,
+ flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address,
+ u16 collector_port)
+{
+ return flowperpkt_template_rewrite_inline
+ (frm, fr, collector_address, src_address, collector_port,
+ FLOW_VARIANT_IPV4);
+}
+
+u8 *
+flowperpkt_template_rewrite_l2 (flow_report_main_t * frm,
+ flow_report_t * fr,
+ ip4_address_t * collector_address,
+ ip4_address_t * src_address,
+ u16 collector_port)
+{
+ return flowperpkt_template_rewrite_inline
+ (frm, fr, collector_address, src_address, collector_port,
+ FLOW_VARIANT_L2);
+}
+
+
+/**
+ * @brief Flush accumulated data
+ * @param frm flow_report_main_t *
+ * @param fr flow_report_t *
+ * @param f vlib_frame_t *
+ *
+ * <em>Notes:</em>
+ * This function must simply return the incoming frame, or no template packets
+ * will be sent.
+ */
+vlib_frame_t *
+flowperpkt_data_callback_ipv4 (flow_report_main_t * frm,
+ flow_report_t * fr,
+ vlib_frame_t * f, u32 * to_next,
+ u32 node_index)
+{
+ flowperpkt_flush_callback_ipv4 ();
+ return f;
+}
+
+vlib_frame_t *
+flowperpkt_data_callback_l2 (flow_report_main_t * frm,
+ flow_report_t * fr,
+ vlib_frame_t * f, u32 * to_next, u32 node_index)
+{
+ flowperpkt_flush_callback_l2 ();
+ return f;
+}
+
+/**
+ * @brief configure / deconfigure the IPFIX flow-per-packet
+ * @param fm flowperpkt_main_t * fm
+ * @param sw_if_index u32 the desired interface
+ * @param is_add int 1 to enable the feature, 0 to disable it
+ * @returns 0 if successful, non-zero otherwise
+ */
+
+static int flowperpkt_tx_interface_add_del_feature
+ (flowperpkt_main_t * fm, u32 sw_if_index, int which, int is_add)
+{
+ flow_report_main_t *frm = &flow_report_main;
+ vnet_flow_report_add_del_args_t _a, *a = &_a;
+ int rv;
+
+ if (which == FLOW_VARIANT_IPV4 && !fm->ipv4_report_created)
+ {
+ memset (a, 0, sizeof (*a));
+ a->rewrite_callback = flowperpkt_template_rewrite_ipv4;
+ a->flow_data_callback = flowperpkt_data_callback_ipv4;
+ a->is_add = 1;
+ a->domain_id = 1; /*$$$$ config parameter */
+ a->src_port = 4739; /*$$$$ config parameter */
+ fm->ipv4_report_created = 1;
+
+ rv = vnet_flow_report_add_del (frm, a);
+ if (rv)
+ {
+ clib_warning ("vnet_flow_report_add_del returned %d", rv);
+ return -1;
+ }
+ }
+ else if (which == FLOW_VARIANT_L2 && !fm->l2_report_created)
+ {
+ memset (a, 0, sizeof (*a));
+ a->rewrite_callback = flowperpkt_template_rewrite_l2;
+ a->flow_data_callback = flowperpkt_data_callback_l2;
+ a->is_add = 1;
+ a->domain_id = 1; /*$$$$ config parameter */
+ a->src_port = 4739; /*$$$$ config parameter */
+ fm->l2_report_created = 1;
+
+ rv = vnet_flow_report_add_del (frm, a);
+ if (rv)
+ {
+ clib_warning ("vnet_flow_report_add_del returned %d", rv);
+ return -1;
+ }
+ }
+
+ if (which == FLOW_VARIANT_IPV4)
+ vnet_feature_enable_disable ("ip4-output", "flowperpkt-ipv4",
+ sw_if_index, is_add, 0, 0);
+ else if (which == FLOW_VARIANT_L2)
+ vnet_feature_enable_disable ("interface-output", "flowperpkt-l2",
+ sw_if_index, is_add, 0, 0);
+
+ return 0;
+}
+
+/**
+ * @brief API message handler
+ * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
+ */
+void vl_api_flowperpkt_tx_interface_add_del_t_handler
+ (vl_api_flowperpkt_tx_interface_add_del_t * mp)
+{
+ flowperpkt_main_t *fm = &flowperpkt_main;
+ vl_api_flowperpkt_tx_interface_add_del_reply_t *rmp;
+ u32 sw_if_index = ntohl (mp->sw_if_index);
+ int rv = 0;
+
+ VALIDATE_SW_IF_INDEX (mp);
+
+ if (mp->which != FLOW_VARIANT_IPV4 && mp->which != FLOW_VARIANT_L2)
+ {
+ rv = VNET_API_ERROR_UNIMPLEMENTED;
+ goto out;
+ }
+
+ rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, mp->which,
+ mp->is_add);
+out:
+ BAD_SW_IF_INDEX_LABEL;
+
+ REPLY_MACRO (VL_API_FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY);
+}
+
+/**
+ * @brief API message custom-dump function
+ * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
+ * @param handle void * print function handle
+ * @returns u8 * output string
+ */
+static void *vl_api_flowperpkt_tx_interface_add_del_t_print
+ (vl_api_flowperpkt_tx_interface_add_del_t * mp, void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: flowperpkt_tx_interface_add_del ");
+ s = format (s, "sw_if_index %d is_add %d which %d ",
+ clib_host_to_net_u32 (mp->sw_if_index),
+ (int) mp->is_add, (int) mp->which);
+ FINISH;
+}
+
+/* List of message types that this plugin understands */
+#define foreach_flowperpkt_plugin_api_msg \
+_(FLOWPERPKT_TX_INTERFACE_ADD_DEL, flowperpkt_tx_interface_add_del)
+
+/**
+ * @brief plugin-api required function
+ * @param vm vlib_main_t * vlib main data structure pointer
+ * @param h vlib_plugin_handoff_t * handoff structure
+ * @param from_early_init int notused
+ *
+ * <em>Notes:</em>
+ * This routine exists to convince the vlib plugin framework that
+ * we haven't accidentally copied a random .dll into the plugin directory.
+ *
+ * Also collects global variable pointers passed from the vpp engine
+ */
+clib_error_t *
+vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
+ int from_early_init)
+{
+ flowperpkt_main_t *fm = &flowperpkt_main;
+ clib_error_t *error = 0;
+
+ fm->vlib_main = vm;
+ fm->vnet_main = h->vnet_main;
+
+ return error;
+}
+
+static clib_error_t *
+flowperpkt_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ flowperpkt_main_t *fm = &flowperpkt_main;
+ u32 sw_if_index = ~0;
+ int is_add = 1;
+ u8 which = FLOW_VARIANT_IPV4;
+
+ int rv;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "disable"))
+ is_add = 0;
+ else if (unformat (input, "%U", unformat_vnet_sw_interface,
+ fm->vnet_main, &sw_if_index));
+ else if (unformat (input, "l2"))
+ which = FLOW_VARIANT_L2;
+ else
+ break;
+ }
+
+ if (sw_if_index == ~0)
+ return clib_error_return (0, "Please specify an interface...");
+
+ rv =
+ flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, which, is_add);
+ switch (rv)
+ {
+ case 0:
+ break;
+
+ case VNET_API_ERROR_INVALID_SW_IF_INDEX:
+ return clib_error_return
+ (0, "Invalid interface, only works on physical ports");
+ break;
+
+ case VNET_API_ERROR_UNIMPLEMENTED:
+ return clib_error_return (0, "ip6 not supported");
+ break;
+
+ default:
+ return clib_error_return (0, "flowperpkt_enable_disable returned %d",
+ rv);
+ }
+ return 0;
+}
+
+/*?
+ * '<em>flowperpkt feature add-del</em>' commands to enable/disable
+ * per-packet IPFIX flow record generation on an interface
+ *
+ * @cliexpar
+ * @parblock
+ * To enable per-packet IPFIX flow-record generation on an interface:
+ * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0}
+ *
+ * To disable per-packet IPFIX flow-record generation on an interface:
+ * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0 disable}
+ * @cliexend
+ * @endparblock
+?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (flowperpkt_enable_disable_command, static) = {
+ .path = "flowperpkt feature add-del",
+ .short_help =
+ "flowperpkt feature add-del <interface-name> [disable]",
+ .function = flowperpkt_tx_interface_add_del_feature_command_fn,
+};
+/* *INDENT-ON* */
+
+/**
+ * @brief Set up the API message handling tables
+ * @param vm vlib_main_t * vlib main data structure pointer
+ * @returns 0 to indicate all is well
+ */
+static clib_error_t *
+flowperpkt_plugin_api_hookup (vlib_main_t * vm)
+{
+ flowperpkt_main_t *fm = &flowperpkt_main;
+#define _(N,n) \
+ vl_msg_api_set_handlers((VL_API_##N + fm->msg_id_base), \
+ #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_flowperpkt_plugin_api_msg;
+#undef _
+
+ return 0;
+}
+
+#define vl_msg_name_crc_list
+#include <flowperpkt/flowperpkt_all_api_h.h>
+#undef vl_msg_name_crc_list
+
+static void
+setup_message_id_table (flowperpkt_main_t * fm, api_main_t * am)
+{
+#define _(id,n,crc) \
+ vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + fm->msg_id_base);
+ foreach_vl_msg_name_crc_flowperpkt;
+#undef _
+}
+
+/**
+ * @brief Set up the API message handling tables
+ * @param vm vlib_main_t * vlib main data structure pointer
+ * @returns 0 to indicate all is well, or a clib_error_t
+ */
+static clib_error_t *
+flowperpkt_init (vlib_main_t * vm)
+{
+ flowperpkt_main_t *fm = &flowperpkt_main;
+ vlib_thread_main_t *tm = &vlib_thread_main;
+ clib_error_t *error = 0;
+ u32 num_threads;
+ u8 *name;
+
+ /* Construct the API name */
+ name = format (0, "flowperpkt_%08x%c", api_version, 0);
+
+ /* Ask for a correctly-sized block of API message decode slots */
+ fm->msg_id_base = vl_msg_api_get_msg_ids
+ ((char *) name, VL_MSG_FIRST_AVAILABLE);
+
+ /* Hook up message handlers */
+ error = flowperpkt_plugin_api_hookup (vm);
+
+ /* Add our API messages to the global name_crc hash table */
+ setup_message_id_table (fm, &api_main);
+
+ vec_free (name);
+
+ /* Decide how many worker threads we have */
+ num_threads = 1 /* main thread */ + tm->n_eal_threads;
+
+ /* Allocate per worker thread vectors */
+ vec_validate (fm->ipv4_buffers_per_worker, num_threads - 1);
+ vec_validate (fm->l2_buffers_per_worker, num_threads - 1);
+ vec_validate (fm->ipv4_frames_per_worker, num_threads - 1);
+ vec_validate (fm->l2_frames_per_worker, num_threads - 1);
+ vec_validate (fm->ipv4_next_record_offset_per_worker, num_threads - 1);
+ vec_validate (fm->l2_next_record_offset_per_worker, num_threads - 1);
+
+ /* Set up time reference pair */
+ fm->vlib_time_0 = vlib_time_now (vm);
+ fm->nanosecond_time_0 = unix_time_now_nsec ();
+
+ return error;
+}
+
+VLIB_INIT_FUNCTION (flowperpkt_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/flowperpkt/flowperpkt.h b/src/plugins/flowperpkt/flowperpkt.h
new file mode 100644
index 00000000000..20f6939dda5
--- /dev/null
+++ b/src/plugins/flowperpkt/flowperpkt.h
@@ -0,0 +1,90 @@
+/*
+ * flowperpkt.h - skeleton vpp engine plug-in header file
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * 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 __included_flowperpkt_h__
+#define __included_flowperpkt_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+
+#include <vppinfra/hash.h>
+#include <vppinfra/error.h>
+#include <vnet/flow/flow_report.h>
+#include <vnet/flow/flow_report_classify.h>
+
+/**
+ * @file
+ * @brief flow-per-packet plugin header file
+ */
+typedef struct
+{
+ /** API message ID base */
+ u16 msg_id_base;
+
+ /** Have the reports [templates] been created? */
+ int ipv4_report_created;
+ int l2_report_created;
+
+ /** stream/template IDs */
+ u16 ipv4_report_id;
+ u16 l2_report_id;
+
+ /** ipfix buffers under construction, per-worker thread */
+ vlib_buffer_t **ipv4_buffers_per_worker;
+ vlib_buffer_t **l2_buffers_per_worker;
+
+ /** frames containing ipfix buffers, per-worker thread */
+ vlib_frame_t **ipv4_frames_per_worker;
+ vlib_frame_t **l2_frames_per_worker;
+
+ /** next record offset, per worker thread */
+ u16 *ipv4_next_record_offset_per_worker;
+ u16 *l2_next_record_offset_per_worker;
+
+ /** Time reference pair */
+ u64 nanosecond_time_0;
+ f64 vlib_time_0;
+
+ /** convenience vlib_main_t pointer */
+ vlib_main_t *vlib_main;
+ /** convenience vnet_main_t pointer */
+ vnet_main_t *vnet_main;
+} flowperpkt_main_t;
+
+typedef enum
+{
+ FLOW_VARIANT_IPV4,
+ FLOW_VARIANT_L2,
+ FLOW_N_VARIANTS,
+} flowperpkt_variant_t;
+
+extern flowperpkt_main_t flowperpkt_main;
+
+extern vlib_node_registration_t flowperpkt_ipv4_node;
+
+void flowperpkt_flush_callback_ipv4 (void);
+void flowperpkt_flush_callback_l2 (void);
+
+#endif /* __included_flowperpkt_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/flowperpkt/flowperpkt_all_api_h.h b/src/plugins/flowperpkt/flowperpkt_all_api_h.h
new file mode 100644
index 00000000000..329c375abca
--- /dev/null
+++ b/src/plugins/flowperpkt/flowperpkt_all_api_h.h
@@ -0,0 +1,18 @@
+/*
+ * flowperpkt_all_api_h.h - plug-in api #include file
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* Include the generated file, see BUILT_SOURCES in Makefile.am */
+#include <flowperpkt/flowperpkt.api.h>
diff --git a/src/plugins/flowperpkt/flowperpkt_msg_enum.h b/src/plugins/flowperpkt/flowperpkt_msg_enum.h
new file mode 100644
index 00000000000..3177e77a63b
--- /dev/null
+++ b/src/plugins/flowperpkt/flowperpkt_msg_enum.h
@@ -0,0 +1,31 @@
+/*
+ * flowperpkt_msg_enum.h - vpp engine plug-in message enumeration
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * 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 included_flowperpkt_msg_enum_h
+#define included_flowperpkt_msg_enum_h
+
+#include <vppinfra/byte_order.h>
+
+#define vl_msg_id(n,h) n,
+typedef enum
+{
+#include <flowperpkt/flowperpkt_all_api_h.h>
+ /* We'll want to know how many messages IDs we need... */
+ VL_MSG_FIRST_AVAILABLE,
+} vl_msg_id_t;
+#undef vl_msg_id
+
+#endif /* included_flowperpkt_msg_enum_h */
diff --git a/src/plugins/flowperpkt/flowperpkt_plugin_doc.md b/src/plugins/flowperpkt/flowperpkt_plugin_doc.md
new file mode 100644
index 00000000000..ed76c45c2dc
--- /dev/null
+++ b/src/plugins/flowperpkt/flowperpkt_plugin_doc.md
@@ -0,0 +1,13 @@
+Per-packet IPFIX flow record plugin {#flowperpkt_plugin_doc}
+===================================
+
+## Introduction
+
+This plugin generates one ipfix record entry per packet transmitted
+on interfaces which have the feature enabled
+
+## Sample configuration
+
+set ipfix exporter collector 192.168.6.2 src 192.168.6.1 template-interval 20 port 4739 path-mtu 1500
+
+flowperpkt feature add-del GigabitEthernet2/3/0
diff --git a/src/plugins/flowperpkt/flowperpkt_test.c b/src/plugins/flowperpkt/flowperpkt_test.c
new file mode 100644
index 00000000000..716818ffe0a
--- /dev/null
+++ b/src/plugins/flowperpkt/flowperpkt_test.c
@@ -0,0 +1,234 @@
+/*
+ * flowperpkt.c - skeleton vpp-api-test plug-in
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * 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 <vat/vat.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vlibsocket/api.h>
+#include <vppinfra/error.h>
+
+/**
+ * @file vpp_api_test plugin
+ */
+
+uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
+
+/* Declare message IDs */
+#include <flowperpkt/flowperpkt_msg_enum.h>
+
+/* define message structures */
+#define vl_typedefs
+#include <flowperpkt/flowperpkt_all_api_h.h>
+#undef vl_typedefs
+
+/* declare message handlers for each api */
+
+#define vl_endianfun /* define message structures */
+#include <flowperpkt/flowperpkt_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <flowperpkt/flowperpkt_all_api_h.h>
+#undef vl_printfun
+
+/* Get the API version number. */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <flowperpkt/flowperpkt_all_api_h.h>
+#undef vl_api_version
+
+typedef struct
+{
+ /** API message ID base */
+ u16 msg_id_base;
+ /** vat_main_t pointer */
+ vat_main_t *vat_main;
+} flowperpkt_test_main_t;
+
+flowperpkt_test_main_t flowperpkt_test_main;
+
+#define foreach_standard_reply_retval_handler \
+_(flowperpkt_tx_interface_add_del_reply)
+
+#define _(n) \
+ static void vl_api_##n##_t_handler \
+ (vl_api_##n##_t * mp) \
+ { \
+ vat_main_t * vam = flowperpkt_test_main.vat_main; \
+ i32 retval = ntohl(mp->retval); \
+ if (vam->async_mode) { \
+ vam->async_errors += (retval < 0); \
+ } else { \
+ vam->retval = retval; \
+ vam->result_ready = 1; \
+ } \
+ }
+foreach_standard_reply_retval_handler;
+#undef _
+
+/*
+ * Table of message reply handlers, must include boilerplate handlers
+ * we just generated
+ */
+#define foreach_vpe_api_reply_msg \
+_(FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY, \
+ flowperpkt_tx_interface_add_del_reply)
+
+
+/* M: construct, but don't yet send a message */
+
+#define M(T,t) \
+do { \
+ vam->result_ready = 0; \
+ mp = vl_msg_api_alloc(sizeof(*mp)); \
+ memset (mp, 0, sizeof (*mp)); \
+ mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \
+ mp->client_index = vam->my_client_index; \
+} while(0);
+
+#define M2(T,t,n) \
+do { \
+ vam->result_ready = 0; \
+ mp = vl_msg_api_alloc(sizeof(*mp)+(n)); \
+ memset (mp, 0, sizeof (*mp)); \
+ mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \
+ mp->client_index = vam->my_client_index; \
+} while(0);
+
+/* S: send a message */
+#define S (vl_msg_api_send_shmem (vam->vl_input_queue, (u8 *)&mp))
+
+/* W: wait for results, with timeout */
+#define W \
+do { \
+ timeout = vat_time_now (vam) + 1.0; \
+ \
+ while (vat_time_now (vam) < timeout) { \
+ if (vam->result_ready == 1) { \
+ return (vam->retval); \
+ } \
+ } \
+ return -99; \
+} while(0);
+
+static int
+api_flowperpkt_tx_interface_add_del (vat_main_t * vam)
+{
+ flowperpkt_test_main_t *sm = &flowperpkt_test_main;
+ unformat_input_t *i = vam->input;
+ f64 timeout;
+ int enable_disable = 1;
+ u8 which = 0; /* ipv4 by default */
+ u32 sw_if_index = ~0;
+ vl_api_flowperpkt_tx_interface_add_del_t *mp;
+
+ /* Parse args required to build the message */
+ while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
+ ;
+ else if (unformat (i, "sw_if_index %d", &sw_if_index))
+ ;
+ else if (unformat (i, "disable"))
+ enable_disable = 0;
+ else if (unformat (i, "l2"))
+ which = 1;
+ else
+ break;
+ }
+
+ if (sw_if_index == ~0)
+ {
+ errmsg ("missing interface name / explicit sw_if_index number \n");
+ return -99;
+ }
+
+ /* Construct the API message */
+ M (FLOWPERPKT_TX_INTERFACE_ADD_DEL, flowperpkt_tx_interface_add_del);
+ mp->sw_if_index = ntohl (sw_if_index);
+ mp->is_add = enable_disable;
+ mp->which = which;
+
+ /* send it... */
+ S;
+
+ /* Wait for a reply... */
+ W;
+}
+
+/*
+ * List of messages that the api test plugin sends,
+ * and that the data plane plugin processes
+ */
+#define foreach_vpe_api_msg \
+_(flowperpkt_tx_interface_add_del, "<intfc> [disable]")
+
+void
+vat_api_hookup (vat_main_t * vam)
+{
+ flowperpkt_test_main_t *sm = &flowperpkt_test_main;
+ /* Hook up handlers for replies from the data plane plug-in */
+#define _(N,n) \
+ vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \
+ #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_vpe_api_reply_msg;
+#undef _
+
+ /* API messages we can send */
+#define _(n,h) hash_set_mem (vam->function_by_name, #n, api_##n);
+ foreach_vpe_api_msg;
+#undef _
+
+ /* Help strings */
+#define _(n,h) hash_set_mem (vam->help_by_name, #n, h);
+ foreach_vpe_api_msg;
+#undef _
+}
+
+clib_error_t *
+vat_plugin_register (vat_main_t * vam)
+{
+ flowperpkt_test_main_t *sm = &flowperpkt_test_main;
+ u8 *name;
+
+ sm->vat_main = vam;
+
+ /* Ask the vpp engine for the first assigned message-id */
+ name = format (0, "flowperpkt_%08x%c", api_version, 0);
+ sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);
+
+ /* Don't attempt to hook up API messages if the data plane plugin is AWOL */
+ if (sm->msg_id_base != (u16) ~ 0)
+ vat_api_hookup (vam);
+
+ vec_free (name);
+
+ return 0;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/flowperpkt/l2_node.c b/src/plugins/flowperpkt/l2_node.c
new file mode 100644
index 00000000000..1c2f681e1e1
--- /dev/null
+++ b/src/plugins/flowperpkt/l2_node.c
@@ -0,0 +1,561 @@
+/*
+ * l2_node.c - l2 ipfix-per-packet graph node
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * 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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <flowperpkt/flowperpkt.h>
+
+/**
+ * @file l2 flow record generator graph node
+ */
+
+typedef struct
+{
+ /** interface handle */
+ u32 rx_sw_if_index;
+ u32 tx_sw_if_index;
+ /** src and dst L2 addresses */
+ u8 src_mac[6];
+ u8 dst_mac[6];
+ /** Ethertype */
+ u16 ethertype;
+ /** packet timestamp */
+ u64 timestamp;
+ /** size of the buffer */
+ u16 buffer_size;
+} flowperpkt_l2_trace_t;
+
+/* packet trace format function */
+static u8 *
+format_flowperpkt_l2_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 *);
+ flowperpkt_l2_trace_t *t = va_arg (*args, flowperpkt_l2_trace_t *);
+
+ s = format (s,
+ "FLOWPERPKT-L2: rx_sw_if_index %d, tx_sw_if_index %d, src %U dst %U ethertype %0x2, timestamp %lld, size %d",
+ t->rx_sw_if_index, t->tx_sw_if_index,
+ format_ethernet_address, &t->src_mac,
+ format_ethernet_address, &t->dst_mac,
+ t->ethertype, t->timestamp, t->buffer_size);
+ return s;
+}
+
+vlib_node_registration_t flowperpkt_l2_node;
+
+/* No counters at the moment */
+#define foreach_flowperpkt_l2_error
+
+typedef enum
+{
+#define _(sym,str) FLOWPERPKT_ERROR_##sym,
+ foreach_flowperpkt_l2_error
+#undef _
+ FLOWPERPKT_N_ERROR,
+} flowperpkt_l2_error_t;
+
+static char *flowperpkt_l2_error_strings[] = {
+#define _(sym,string) string,
+ foreach_flowperpkt_l2_error
+#undef _
+};
+
+typedef enum
+{
+ FLOWPERPKT_L2_NEXT_DROP,
+ FLOWPERPKT_L2_NEXT_IP4_LOOKUP,
+ FLOWPERPKT_L2_N_NEXT,
+} flowperpkt_l2_next_t;
+
+/**
+ * @brief add an entry to the flow record under construction
+ * @param vm vlib_main_t * current worker thread main structure pointer
+ * @param fm flowperpkt_main_t * flow-per-packet main structure pointer
+ * @param sw_if_index u32 interface handle
+ * @param tos u8 ToS bits from the packet
+ * @param timestamp u64 timestamp, nanoseconds since 1/1/70
+ * @param length u16 ip length of the packet
+ * @param do_flush int 1 = flush all cached records, 0 = construct a record
+ */
+
+static inline void
+add_to_flow_record_l2 (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ flowperpkt_main_t * fm,
+ u32 rx_sw_if_index, u32 tx_sw_if_index,
+ u8 * src_mac, u8 * dst_mac,
+ u16 ethertype, u64 timestamp, u16 length, int do_flush)
+{
+ u32 my_cpu_number = vm->cpu_index;
+ flow_report_main_t *frm = &flow_report_main;
+ ip4_header_t *ip;
+ udp_header_t *udp;
+ ip4_ipfix_template_packet_t *tp;
+ ipfix_message_header_t *h;
+ ipfix_set_header_t *s;
+ vlib_frame_t *f;
+ vlib_buffer_t *b0;
+ u16 offset;
+ u32 bi0;
+ vlib_buffer_free_list_t *fl;
+
+ /* Find or allocate a buffer */
+ b0 = fm->l2_buffers_per_worker[my_cpu_number];
+
+ /* Need to allocate a buffer? */
+ if (PREDICT_FALSE (b0 == 0))
+ {
+ /* Nothing to flush */
+ if (do_flush)
+ return;
+
+ /* $$$$ drop counter? */
+ if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
+ return;
+
+ /* Initialize the buffer */
+ b0 = fm->l2_buffers_per_worker[my_cpu_number] =
+ vlib_get_buffer (vm, bi0);
+ fl =
+ vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
+ vlib_buffer_init_for_free_list (b0, fl);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+ offset = 0;
+ }
+ else
+ {
+ /* use the current buffer */
+ bi0 = vlib_get_buffer_index (vm, b0);
+ offset = fm->l2_next_record_offset_per_worker[my_cpu_number];
+ }
+
+ /* Find or allocate a frame */
+ f = fm->l2_frames_per_worker[my_cpu_number];
+ if (PREDICT_FALSE (f == 0))
+ {
+ u32 *to_next;
+ f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
+ fm->l2_frames_per_worker[my_cpu_number] = f;
+
+ /* Enqueue the buffer */
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+ }
+
+ /* Fresh packet, construct header */
+ if (PREDICT_FALSE (offset == 0))
+ {
+ flow_report_stream_t *stream;
+
+ stream = &frm->streams[0];
+
+ b0->current_data = 0;
+ b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) +
+ sizeof (*s);
+ b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VLIB_BUFFER_FLOW_REPORT);
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
+
+ tp = vlib_buffer_get_current (b0);
+ ip = (ip4_header_t *) & tp->ip4;
+ udp = (udp_header_t *) (ip + 1);
+ h = (ipfix_message_header_t *) (udp + 1);
+ s = (ipfix_set_header_t *) (h + 1);
+
+ ip->ip_version_and_header_length = 0x45;
+ ip->ttl = 254;
+ ip->protocol = IP_PROTOCOL_UDP;
+ ip->flags_and_fragment_offset = 0;
+ ip->src_address.as_u32 = frm->src_address.as_u32;
+ ip->dst_address.as_u32 = frm->ipfix_collector.as_u32;
+ udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
+ udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
+ udp->checksum = 0;
+
+ /* FIXUP: message header export_time */
+ h->export_time = (u32)
+ (((f64) frm->unix_time_0) +
+ (vlib_time_now (frm->vlib_main) - frm->vlib_time_0));
+ h->export_time = clib_host_to_net_u32 (h->export_time);
+ h->domain_id = clib_host_to_net_u32 (stream->domain_id);
+
+ /* FIXUP: message header sequence_number */
+ h->sequence_number = stream->sequence_number++;
+ h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
+
+ offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
+ }
+
+ /* Add data, unless we're flushing stale data */
+ if (PREDICT_TRUE (do_flush == 0))
+ {
+
+ /* Add data */
+ /* Ingress interface */
+ {
+ u32 ingress_interface = clib_host_to_net_u32 (rx_sw_if_index);
+ clib_memcpy (b0->data + offset, &ingress_interface,
+ sizeof (ingress_interface));
+ offset += sizeof (ingress_interface);
+ }
+ /* Egress interface */
+ {
+ u32 egress_interface = clib_host_to_net_u32 (tx_sw_if_index);
+ clib_memcpy (b0->data + offset, &egress_interface,
+ sizeof (egress_interface));
+ offset += sizeof (egress_interface);
+ }
+ /* src mac address */
+ {
+ clib_memcpy (b0->data + offset, src_mac, 6);
+ offset += 6;
+ }
+ /* dst mac address */
+ {
+ clib_memcpy (b0->data + offset, dst_mac, 6);
+ offset += 6;
+ }
+
+ /* ethertype */
+ b0->data[offset++] = ethertype >> 8;
+ b0->data[offset++] = ethertype & 0xFF;
+
+ /* Timestamp */
+ clib_memcpy (b0->data + offset, &timestamp, sizeof (f64));
+ offset += sizeof (f64);
+
+ /* pkt size */
+ {
+ u16 pkt_size = clib_host_to_net_u16 (length);
+ clib_memcpy (b0->data + offset, &pkt_size, sizeof (pkt_size));
+ offset += sizeof (pkt_size);
+ }
+
+ b0->current_length +=
+ /* 2*sw_if_index + 2*mac + ethertype + timestamp + length = 32 */
+ 2 * sizeof (u32) + 12 + sizeof (u16) + sizeof (f64) + sizeof (u16);
+
+ }
+ /* Time to flush the buffer? */
+ if (PREDICT_FALSE
+ (do_flush || (offset + 2 * sizeof (u32) + 12 + sizeof (u16) +
+ +sizeof (f64) + sizeof (u16)) > frm->path_mtu))
+ {
+ tp = vlib_buffer_get_current (b0);
+ ip = (ip4_header_t *) & tp->ip4;
+ udp = (udp_header_t *) (ip + 1);
+ h = (ipfix_message_header_t *) (udp + 1);
+ s = (ipfix_set_header_t *) (h + 1);
+
+ s->set_id_length = ipfix_set_id_length (fm->l2_report_id,
+ b0->current_length -
+ (sizeof (*ip) + sizeof (*udp) +
+ sizeof (*h)));
+ h->version_length = version_length (b0->current_length -
+ (sizeof (*ip) + sizeof (*udp)));
+
+ ip->length = clib_host_to_net_u16 (b0->current_length);
+
+ ip->checksum = ip4_header_checksum (ip);
+ udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
+
+ if (frm->udp_checksum)
+ {
+ /* RFC 7011 section 10.3.2. */
+ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+ }
+
+ ASSERT (ip->checksum == ip4_header_checksum (ip));
+
+ if (PREDICT_FALSE (vlib_get_trace_count (vm, node) > 0))
+ {
+ vlib_trace_buffer (vm, node, FLOWPERPKT_L2_NEXT_IP4_LOOKUP, b0,
+ 0 /* follow chain */ );
+ flowperpkt_l2_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ memset (t, 0, sizeof (*t));
+ t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+ t->buffer_size = b0->current_length;
+ }
+
+ vlib_put_frame_to_node (vm, ip4_lookup_node.index,
+ fm->l2_frames_per_worker[my_cpu_number]);
+ fm->l2_frames_per_worker[my_cpu_number] = 0;
+ fm->l2_buffers_per_worker[my_cpu_number] = 0;
+ offset = 0;
+ }
+
+ fm->l2_next_record_offset_per_worker[my_cpu_number] = offset;
+}
+
+void
+flowperpkt_flush_callback_l2 (void)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ flowperpkt_main_t *fm = &flowperpkt_main;
+ vlib_node_runtime_t *node;
+ node = vlib_node_get_runtime (vm, flowperpkt_l2_node.index);
+
+ add_to_flow_record_l2 (vm, node, fm, 0 /* rx_sw_if_index */ ,
+ 0 /* tx_sw_if_index */ ,
+ 0 /* src mac */ ,
+ 0 /* dst mac */ ,
+ 0 /* ethertype */ ,
+ 0ULL /* timestamp */ ,
+ 0 /* length */ ,
+ 1 /* do_flush */ );
+}
+
+
+static uword
+flowperpkt_l2_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, *to_next;
+ flowperpkt_l2_next_t next_index;
+ flowperpkt_main_t *fm = &flowperpkt_main;
+ u64 now;
+
+ now = (u64) ((vlib_time_now (vm) - fm->vlib_time_0) * 1e9);
+ now += fm->nanosecond_time_0;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ u32 next0 = FLOWPERPKT_L2_NEXT_DROP;
+ u32 next1 = FLOWPERPKT_L2_NEXT_DROP;
+ ethernet_header_t *eh0, *eh1;
+ u16 len0, len1;
+ u32 bi0, bi1;
+ vlib_buffer_t *b0, *b1;
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+
+ CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
+ CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
+ }
+
+ /* speculatively enqueue b0 and b1 to the current next frame */
+ to_next[0] = bi0 = from[0];
+ to_next[1] = bi1 = from[1];
+ from += 2;
+ to_next += 2;
+ n_left_from -= 2;
+ n_left_to_next -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+
+ vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ &next0, b0);
+ vnet_feature_next (vnet_buffer (b1)->sw_if_index[VLIB_TX],
+ &next1, b1);
+
+ eh0 = vlib_buffer_get_current (b0);
+ len0 = vlib_buffer_length_in_chain (vm, b0);
+
+ if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0))
+ add_to_flow_record_l2 (vm, node, fm,
+ vnet_buffer (b0)->sw_if_index[VLIB_RX],
+ vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ eh0->src_address,
+ eh0->dst_address,
+ eh0->type, now, len0, 0 /* flush */ );
+
+ eh1 = vlib_buffer_get_current (b0);
+ len1 = vlib_buffer_length_in_chain (vm, b0);
+
+ if (PREDICT_TRUE ((b1->flags & VLIB_BUFFER_FLOW_REPORT) == 0))
+ add_to_flow_record_l2 (vm, node, fm,
+ vnet_buffer (b1)->sw_if_index[VLIB_RX],
+ vnet_buffer (b1)->sw_if_index[VLIB_TX],
+ eh1->src_address,
+ eh1->dst_address,
+ eh1->type, now, len1, 0 /* flush */ );
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
+ {
+ if (b0->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ flowperpkt_l2_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+ clib_memcpy (t->src_mac, eh0->src_address, 6);
+ clib_memcpy (t->dst_mac, eh0->dst_address, 6);
+ t->ethertype = clib_net_to_host_u16 (eh0->type);
+ t->timestamp = now;
+ t->buffer_size = len0;
+ }
+ if (b1->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ flowperpkt_l2_trace_t *t =
+ vlib_add_trace (vm, node, b1, sizeof (*t));
+ t->rx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+ t->tx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
+ clib_memcpy (t->src_mac, eh1->src_address, 6);
+ clib_memcpy (t->dst_mac, eh1->dst_address, 6);
+ t->ethertype = clib_net_to_host_u16 (eh1->type);
+ t->timestamp = now;
+ t->buffer_size = len1;
+ }
+ }
+
+ /* verify speculative enqueues, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, next0, next1);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0;
+ vlib_buffer_t *b0;
+ u32 next0 = FLOWPERPKT_L2_NEXT_DROP;
+ ethernet_header_t *eh0;
+ u16 len0;
+
+ /* speculatively enqueue b0 to the current next frame */
+ 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);
+
+ vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ &next0, b0);
+
+ eh0 = vlib_buffer_get_current (b0);
+ len0 = vlib_buffer_length_in_chain (vm, b0);
+
+ if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0))
+ add_to_flow_record_l2 (vm, node, fm,
+ vnet_buffer (b0)->sw_if_index[VLIB_RX],
+ vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ eh0->src_address,
+ eh0->dst_address,
+ eh0->type, now, len0, 0 /* flush */ );
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ flowperpkt_l2_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+ clib_memcpy (t->src_mac, eh0->src_address, 6);
+ clib_memcpy (t->dst_mac, eh0->dst_address, 6);
+ t->ethertype = clib_net_to_host_u16 (eh0->type);
+ t->timestamp = now;
+ t->buffer_size = len0;
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ 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;
+}
+
+/**
+ * @brief IPFIX l2 flow-per-packet graph node
+ * @node flowperpkt-l2
+ *
+ * This is the IPFIX flow-record-per-packet node.
+ *
+ * @param vm vlib_main_t corresponding to the current thread.
+ * @param node vlib_node_runtime_t data for this node.
+ * @param frame vlib_frame_t whose contents should be dispatched.
+ *
+ * @par Graph mechanics: buffer metadata, next index usage
+ *
+ * <em>Uses:</em>
+ * - <code>vnet_buffer(b)->ip.save_rewrite_length</code>
+ * - tells the node the length of the rewrite which was applied in
+ * ip4/6_rewrite_inline, allows the code to find the IP header without
+ * having to parse L2 headers, or make stupid assumptions about their
+ * length.
+ * - <code>vnet_buffer(b)->flags & VLIB_BUFFER_FLOW_REPORT</code>
+ * - Used to suppress flow record generation for flow record packets.
+ *
+ * <em>Sets:</em>
+ * - <code>vnet_buffer(b)->flags & VLIB_BUFFER_FLOW_REPORT</code>
+ * - To suppress flow record generation for flow record packets
+ *
+ * <em>Next Index:</em>
+ * - Next configured output feature on the interface, usually
+ * "interface-output." Generated flow records head for ip4-lookup
+ */
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (flowperpkt_l2_node) = {
+ .function = flowperpkt_l2_node_fn,
+ .name = "flowperpkt-l2",
+ .vector_size = sizeof (u32),
+ .format_trace = format_flowperpkt_l2_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(flowperpkt_l2_error_strings),
+ .error_strings = flowperpkt_l2_error_strings,
+
+ .n_next_nodes = FLOWPERPKT_L2_N_NEXT,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [FLOWPERPKT_L2_NEXT_DROP] = "error-drop",
+ [FLOWPERPKT_L2_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/flowperpkt/node.c b/src/plugins/flowperpkt/node.c
new file mode 100644
index 00000000000..f77f087dc78
--- /dev/null
+++ b/src/plugins/flowperpkt/node.c
@@ -0,0 +1,574 @@
+/*
+ * node.c - ipv4 ipfix-per-packet graph node
+ *
+ * Copyright (c) <current-year> <your-organization>
+ * 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 <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <flowperpkt/flowperpkt.h>
+
+/**
+ * @file ipv4 flow record generator graph node
+ */
+
+typedef struct
+{
+ /** interface handle */
+ u32 rx_sw_if_index;
+ u32 tx_sw_if_index;
+ u32 src_address;
+ u32 dst_address;
+ /** ToS bits */
+ u8 tos;
+ /** packet timestamp */
+ u64 timestamp;
+ /** size of the buffer */
+ u16 buffer_size;
+} flowperpkt_ipv4_trace_t;
+
+/* packet trace format function */
+static u8 *
+format_flowperpkt_ipv4_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 *);
+ flowperpkt_ipv4_trace_t *t = va_arg (*args, flowperpkt_ipv4_trace_t *);
+
+ s = format (s,
+ "FLOWPERPKT-V4: rx_sw_if_index %d, tx_sw_if_index %d, src %U dst %U tos %0x2, timestamp %lld, size %d",
+ t->rx_sw_if_index, t->tx_sw_if_index,
+ format_ip4_address, &t->src_address,
+ format_ip4_address, &t->dst_address,
+ t->tos, t->timestamp, t->buffer_size);
+ return s;
+}
+
+vlib_node_registration_t flowperpkt_ipv4_node;
+
+/* No counters at the moment */
+#define foreach_flowperpkt_ipv4_error
+
+typedef enum
+{
+#define _(sym,str) FLOWPERPKT_ERROR_##sym,
+ foreach_flowperpkt_ipv4_error
+#undef _
+ FLOWPERPKT_N_ERROR,
+} flowperpkt_ipv4_error_t;
+
+static char *flowperpkt_ipv4_error_strings[] = {
+#define _(sym,string) string,
+ foreach_flowperpkt_ipv4_error
+#undef _
+};
+
+typedef enum
+{
+ FLOWPERPKT_IPV4_NEXT_DROP,
+ FLOWPERPKT_IPV4_NEXT_LOOKUP,
+ FLOWPERPKT_IPV4_N_NEXT,
+} flowperpkt_ipv4_next_t;
+
+/**
+ * @brief add an entry to the flow record under construction
+ * @param vm vlib_main_t * current worker thread main structure pointer
+ * @param fm flowperpkt_main_t * flow-per-packet main structure pointer
+ * @param sw_if_index u32 interface handle
+ * @param tos u8 ToS bits from the packet
+ * @param timestamp u64 timestamp, nanoseconds since 1/1/70
+ * @param length u16 ip length of the packet
+ * @param do_flush int 1 = flush all cached records, 0 = construct a record
+ */
+
+static inline void
+add_to_flow_record_ipv4 (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ flowperpkt_main_t * fm,
+ u32 rx_sw_if_index, u32 tx_sw_if_index,
+ u32 src_address, u32 dst_address,
+ u8 tos, u64 timestamp, u16 length, int do_flush)
+{
+ u32 my_cpu_number = vm->cpu_index;
+ flow_report_main_t *frm = &flow_report_main;
+ ip4_header_t *ip;
+ udp_header_t *udp;
+ ip4_ipfix_template_packet_t *tp;
+ ipfix_message_header_t *h;
+ ipfix_set_header_t *s;
+ vlib_frame_t *f;
+ vlib_buffer_t *b0;
+ u16 offset;
+ u32 bi0;
+ vlib_buffer_free_list_t *fl;
+
+ /* Find or allocate a buffer */
+ b0 = fm->ipv4_buffers_per_worker[my_cpu_number];
+
+ /* Need to allocate a buffer? */
+ if (PREDICT_FALSE (b0 == 0))
+ {
+ /* Nothing to flush */
+ if (do_flush)
+ return;
+
+ /* $$$$ drop counter? */
+ if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
+ return;
+
+ /* Initialize the buffer */
+ b0 = fm->ipv4_buffers_per_worker[my_cpu_number] =
+ vlib_get_buffer (vm, bi0);
+ fl =
+ vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
+ vlib_buffer_init_for_free_list (b0, fl);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+ offset = 0;
+ }
+ else
+ {
+ /* use the current buffer */
+ bi0 = vlib_get_buffer_index (vm, b0);
+ offset = fm->ipv4_next_record_offset_per_worker[my_cpu_number];
+ }
+
+ /* Find or allocate a frame */
+ f = fm->ipv4_frames_per_worker[my_cpu_number];
+ if (PREDICT_FALSE (f == 0))
+ {
+ u32 *to_next;
+ f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
+ fm->ipv4_frames_per_worker[my_cpu_number] = f;
+
+ /* Enqueue the buffer */
+ to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+ }
+
+ /* Fresh packet, construct header */
+ if (PREDICT_FALSE (offset == 0))
+ {
+ flow_report_stream_t *stream;
+
+ stream = &frm->streams[0];
+
+ b0->current_data = 0;
+ b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) +
+ sizeof (*s);
+ b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VLIB_BUFFER_FLOW_REPORT);
+ vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
+
+ tp = vlib_buffer_get_current (b0);
+ ip = (ip4_header_t *) & tp->ip4;
+ udp = (udp_header_t *) (ip + 1);
+ h = (ipfix_message_header_t *) (udp + 1);
+ s = (ipfix_set_header_t *) (h + 1);
+
+ ip->ip_version_and_header_length = 0x45;
+ ip->ttl = 254;
+ ip->protocol = IP_PROTOCOL_UDP;
+ ip->flags_and_fragment_offset = 0;
+ ip->src_address.as_u32 = frm->src_address.as_u32;
+ ip->dst_address.as_u32 = frm->ipfix_collector.as_u32;
+ udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
+ udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
+ udp->checksum = 0;
+
+ /* FIXUP: message header export_time */
+ h->export_time = (u32)
+ (((f64) frm->unix_time_0) +
+ (vlib_time_now (frm->vlib_main) - frm->vlib_time_0));
+ h->export_time = clib_host_to_net_u32 (h->export_time);
+ h->domain_id = clib_host_to_net_u32 (stream->domain_id);
+
+ /* FIXUP: message header sequence_number */
+ h->sequence_number = stream->sequence_number++;
+ h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
+
+ offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
+ }
+
+ /* Add data, unless we're flushing stale data */
+ if (PREDICT_TRUE (do_flush == 0))
+ {
+
+ /* Add data */
+ /* Ingress interface */
+ {
+ u32 ingress_interface = clib_host_to_net_u32 (rx_sw_if_index);
+ clib_memcpy (b0->data + offset, &ingress_interface,
+ sizeof (ingress_interface));
+ offset += sizeof (ingress_interface);
+ }
+ /* Egress interface */
+ {
+ u32 egress_interface = clib_host_to_net_u32 (tx_sw_if_index);
+ clib_memcpy (b0->data + offset, &egress_interface,
+ sizeof (egress_interface));
+ offset += sizeof (egress_interface);
+ }
+ /* ip4 src address */
+ {
+ clib_memcpy (b0->data + offset, &src_address, sizeof (src_address));
+ offset += sizeof (src_address);
+ }
+ /* ip4 dst address */
+ {
+ clib_memcpy (b0->data + offset, &dst_address, sizeof (dst_address));
+ offset += sizeof (dst_address);
+ }
+
+ /* ToS */
+ b0->data[offset++] = tos;
+
+ /* Timestamp */
+ clib_memcpy (b0->data + offset, &timestamp, sizeof (f64));
+ offset += sizeof (f64);
+
+ /* pkt size */
+ {
+ u16 pkt_size = clib_host_to_net_u16 (length);
+ clib_memcpy (b0->data + offset, &pkt_size, sizeof (pkt_size));
+ offset += sizeof (pkt_size);
+ }
+
+ b0->current_length +=
+ /* sw_if_index + tos + timestamp + length = 15 */
+ 4 * sizeof (u32) + sizeof (u8) + sizeof (f64) + sizeof (u16);
+
+ }
+ /* Time to flush the buffer? */
+ if (PREDICT_FALSE
+ (do_flush || (offset + 4 * sizeof (u32) + sizeof (u8)
+ + sizeof (f64) + sizeof (u16)) > frm->path_mtu))
+ {
+ tp = vlib_buffer_get_current (b0);
+ ip = (ip4_header_t *) & tp->ip4;
+ udp = (udp_header_t *) (ip + 1);
+ h = (ipfix_message_header_t *) (udp + 1);
+ s = (ipfix_set_header_t *) (h + 1);
+
+ s->set_id_length = ipfix_set_id_length (fm->ipv4_report_id,
+ b0->current_length -
+ (sizeof (*ip) + sizeof (*udp) +
+ sizeof (*h)));
+ h->version_length = version_length (b0->current_length -
+ (sizeof (*ip) + sizeof (*udp)));
+
+ ip->length = clib_host_to_net_u16 (b0->current_length);
+
+ ip->checksum = ip4_header_checksum (ip);
+ udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
+
+ if (frm->udp_checksum)
+ {
+ /* RFC 7011 section 10.3.2. */
+ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
+ if (udp->checksum == 0)
+ udp->checksum = 0xffff;
+ }
+
+ ASSERT (ip->checksum == ip4_header_checksum (ip));
+
+ if (PREDICT_FALSE (vlib_get_trace_count (vm, node) > 0))
+ {
+ vlib_trace_buffer (vm, node, FLOWPERPKT_IPV4_NEXT_LOOKUP, b0,
+ 0 /* follow chain */ );
+ flowperpkt_ipv4_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+ t->src_address = 0;
+ t->dst_address = 0;
+ t->tos = 0;
+ t->timestamp = 0;
+ t->buffer_size = b0->current_length;
+ }
+
+ vlib_put_frame_to_node (vm, ip4_lookup_node.index,
+ fm->ipv4_frames_per_worker[my_cpu_number]);
+ fm->ipv4_frames_per_worker[my_cpu_number] = 0;
+ fm->ipv4_buffers_per_worker[my_cpu_number] = 0;
+ offset = 0;
+ }
+
+ fm->ipv4_next_record_offset_per_worker[my_cpu_number] = offset;
+}
+
+void
+flowperpkt_flush_callback_ipv4 (void)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ flowperpkt_main_t *fm = &flowperpkt_main;
+ vlib_node_runtime_t *node;
+ node = vlib_node_get_runtime (vm, flowperpkt_ipv4_node.index);
+
+ add_to_flow_record_ipv4 (vm, node, fm, 0 /* rx_sw_if_index */ ,
+ 0 /* tx_sw_if_index */ ,
+ 0 /* src_address */ ,
+ 0 /* dst_address */ ,
+ 0 /* ToS */ ,
+ 0ULL /* timestamp */ ,
+ 0 /* length */ ,
+ 1 /* do_flush */ );
+}
+
+
+static uword
+flowperpkt_ipv4_node_fn (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, *to_next;
+ flowperpkt_ipv4_next_t next_index;
+ flowperpkt_main_t *fm = &flowperpkt_main;
+ u64 now;
+
+ now = (u64) ((vlib_time_now (vm) - fm->vlib_time_0) * 1e9);
+ now += fm->nanosecond_time_0;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from >= 4 && n_left_to_next >= 2)
+ {
+ u32 next0 = FLOWPERPKT_IPV4_NEXT_DROP;
+ u32 next1 = FLOWPERPKT_IPV4_NEXT_DROP;
+ ip4_header_t *ip0, *ip1;
+ u16 len0, len1;
+ u32 bi0, bi1;
+ vlib_buffer_t *b0, *b1;
+
+ /* Prefetch next iteration. */
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+
+ CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
+ CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
+ }
+
+ /* speculatively enqueue b0 and b1 to the current next frame */
+ to_next[0] = bi0 = from[0];
+ to_next[1] = bi1 = from[1];
+ from += 2;
+ to_next += 2;
+ n_left_from -= 2;
+ n_left_to_next -= 2;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ b1 = vlib_get_buffer (vm, bi1);
+
+ vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ &next0, b0);
+ vnet_feature_next (vnet_buffer (b1)->sw_if_index[VLIB_TX],
+ &next1, b1);
+
+ ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) +
+ vnet_buffer (b0)->ip.save_rewrite_length);
+
+ len0 = vlib_buffer_length_in_chain (vm, b0);
+
+ if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0))
+ add_to_flow_record_ipv4 (vm, node, fm,
+ vnet_buffer (b0)->sw_if_index[VLIB_RX],
+ vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ ip0->src_address.as_u32,
+ ip0->dst_address.as_u32,
+ ip0->tos, now, len0, 0 /* flush */ );
+
+ ip1 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b1) +
+ vnet_buffer (b1)->ip.save_rewrite_length);
+ len1 = vlib_buffer_length_in_chain (vm, b1);
+
+ if (PREDICT_TRUE ((b1->flags & VLIB_BUFFER_FLOW_REPORT) == 0))
+ add_to_flow_record_ipv4 (vm, node, fm,
+ vnet_buffer (b1)->sw_if_index[VLIB_RX],
+ vnet_buffer (b1)->sw_if_index[VLIB_TX],
+ ip1->src_address.as_u32,
+ ip1->dst_address.as_u32,
+ ip1->tos, now, len1, 0 /* flush */ );
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
+ {
+ if (b0->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ flowperpkt_ipv4_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+ t->src_address = ip0->src_address.as_u32;
+ t->dst_address = ip0->dst_address.as_u32;
+ t->tos = ip0->tos;
+ t->timestamp = now;
+ t->buffer_size = len0;
+ }
+ if (b1->flags & VLIB_BUFFER_IS_TRACED)
+ {
+ flowperpkt_ipv4_trace_t *t =
+ vlib_add_trace (vm, node, b1, sizeof (*t));
+ t->rx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
+ t->tx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
+ t->src_address = ip1->src_address.as_u32;
+ t->dst_address = ip1->dst_address.as_u32;
+ t->tos = ip1->tos;
+ t->timestamp = now;
+ t->buffer_size = len1;
+ }
+ }
+
+ /* verify speculative enqueues, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, bi1, next0, next1);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0;
+ vlib_buffer_t *b0;
+ u32 next0 = FLOWPERPKT_IPV4_NEXT_DROP;
+ ip4_header_t *ip0;
+ u16 len0;
+
+ /* speculatively enqueue b0 to the current next frame */
+ 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);
+
+ vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ &next0, b0);
+
+ ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) +
+ vnet_buffer (b0)->ip.save_rewrite_length);
+ /*
+ * egressInterface, TLV type 14, u32
+ * ipClassOfService, TLV type 5, u8
+ * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
+ * Implementation: f64 nanoseconds since VPP started
+ * dataLinkFrameSize, TLV type 312, u16
+ */
+ len0 = vlib_buffer_length_in_chain (vm, b0);
+
+ if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0))
+ add_to_flow_record_ipv4 (vm, node, fm,
+ vnet_buffer (b0)->sw_if_index[VLIB_RX],
+ vnet_buffer (b0)->sw_if_index[VLIB_TX],
+ ip0->src_address.as_u32,
+ ip0->dst_address.as_u32,
+ ip0->tos, now, len0, 0 /* flush */ );
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ flowperpkt_ipv4_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+ t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+ t->src_address = ip0->src_address.as_u32;
+ t->dst_address = ip0->dst_address.as_u32;
+ t->tos = ip0->tos;
+ t->timestamp = now;
+ t->buffer_size = len0;
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ 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;
+}
+
+/**
+ * @brief IPFIX ipv4 flow-per-packet graph node
+ * @node flowperpkt-ipv4
+ *
+ * This is the IPFIX flow-record-per-packet node.
+ *
+ * @param vm vlib_main_t corresponding to the current thread.
+ * @param node vlib_node_runtime_t data for this node.
+ * @param frame vlib_frame_t whose contents should be dispatched.
+ *
+ * @par Graph mechanics: buffer metadata, next index usage
+ *
+ * <em>Uses:</em>
+ * - <code>vnet_buffer(b)->ip.save_rewrite_length</code>
+ * - tells the node the length of the rewrite which was applied in
+ * ip4/6_rewrite_inline, allows the code to find the IP header without
+ * having to parse L2 headers, or make stupid assumptions about their
+ * length.
+ * - <code>vnet_buffer(b)->flags & VLIB_BUFFER_FLOW_REPORT</code>
+ * - Used to suppress flow record generation for flow record packets.
+ *
+ * <em>Sets:</em>
+ * - <code>vnet_buffer(b)->flags & VLIB_BUFFER_FLOW_REPORT</code>
+ * - To suppress flow record generation for flow record packets
+ *
+ * <em>Next Index:</em>
+ * - Next configured output feature on the interface, usually
+ * "interface-output." Generated flow records head for ip4-lookup
+ */
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (flowperpkt_ipv4_node) = {
+ .function = flowperpkt_ipv4_node_fn,
+ .name = "flowperpkt-ipv4",
+ .vector_size = sizeof (u32),
+ .format_trace = format_flowperpkt_ipv4_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = ARRAY_LEN(flowperpkt_ipv4_error_strings),
+ .error_strings = flowperpkt_ipv4_error_strings,
+
+ .n_next_nodes = FLOWPERPKT_IPV4_N_NEXT,
+
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [FLOWPERPKT_IPV4_NEXT_DROP] = "error-drop",
+ /* Used only to trace ipfix data packets */
+ [FLOWPERPKT_IPV4_NEXT_LOOKUP] = "ip4-lookup",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/ila.am b/src/plugins/ila.am
new file mode 100644
index 00000000000..d900f3eb307
--- /dev/null
+++ b/src/plugins/ila.am
@@ -0,0 +1,20 @@
+# Copyright (c) 2016 Cisco Systems, Inc.
+# 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.
+
+vppplugins_LTLIBRARIES += ila_plugin.la
+
+ila_plugin_la_SOURCES = ila/ila.c
+
+noinst_HEADERS += ila/ila.h
+
+# vi:syntax=automake
diff --git a/src/plugins/ila/ila.c b/src/plugins/ila/ila.c
new file mode 100644
index 00000000000..336f4cf560c
--- /dev/null
+++ b/src/plugins/ila/ila.c
@@ -0,0 +1,1070 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ila/ila.h>
+#include <vnet/plugin/plugin.h>
+#include <vnet/ip/lookup.h>
+#include <vnet/dpo/dpo.h>
+#include <vnet/fib/fib_table.h>
+
+static ila_main_t ila_main;
+
+#define ILA_TABLE_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
+#define ILA_TABLE_DEFAULT_HASH_MEMORY_SIZE (32<<20)
+
+#define foreach_ila_error \
+ _(NONE, "valid ILA packets")
+
+typedef enum {
+#define _(sym,str) ILA_ERROR_##sym,
+ foreach_ila_error
+#undef _
+ ILA_N_ERROR,
+} ila_error_t;
+
+static char *ila_error_strings[] = {
+#define _(sym,string) string,
+ foreach_ila_error
+#undef _
+};
+
+typedef enum {
+ ILA_ILA2SIR_NEXT_DROP,
+ ILA_ILA2SIR_N_NEXT,
+} ila_ila2sir_next_t;
+
+typedef struct {
+ u32 ila_index;
+ ip6_address_t initial_dst;
+ u32 adj_index;
+} ila_ila2sir_trace_t;
+
+static ila_entry_t ila_sir2ila_default_entry = {
+ .csum_mode = ILA_CSUM_MODE_NO_ACTION,
+ .type = ILA_TYPE_IID,
+ .dir = ILA_DIR_ILA2SIR, //Will pass the packet with no
+};
+
+/**
+ * @brief Dynamically registered DPO Type for ILA
+ */
+static dpo_type_t ila_dpo_type;
+
+/**
+ * @brief Dynamically registered FIB node type for ILA
+ */
+static fib_node_type_t ila_fib_node_type;
+
+u8 *
+format_half_ip6_address (u8 * s, va_list * va)
+{
+ u64 v = clib_net_to_host_u64 (va_arg (*va, u64));
+
+ return format (s, "%04x:%04x:%04x:%04x",
+ v >> 48, (v >> 32) & 0xffff, (v >> 16) & 0xffff, v & 0xffff);
+
+}
+
+u8 *
+format_ila_direction (u8 * s, va_list * args)
+{
+ ila_direction_t t = va_arg (*args, ila_direction_t);
+#define _(i,n,st) \
+ if (t == ILA_DIR_##i) \
+ return format(s, st);
+ ila_foreach_direction
+#undef _
+ return format (s, "invalid_ila_direction");
+}
+
+static u8 *
+format_csum_mode (u8 * s, va_list * va)
+{
+ ila_csum_mode_t csum_mode = va_arg (*va, ila_csum_mode_t);
+ char *txt;
+
+ switch (csum_mode)
+ {
+#define _(i,n,st) \
+ case ILA_CSUM_MODE_##i: \
+ txt = st; \
+ break;
+ ila_csum_foreach_type
+#undef _
+ default:
+ txt = "invalid_ila_csum_mode";
+ break;
+ }
+ return format (s, txt);
+}
+
+u8 *
+format_ila_type (u8 * s, va_list * args)
+{
+ ila_type_t t = va_arg (*args, ila_type_t);
+#define _(i,n,st) \
+ if (t == ILA_TYPE_##i) \
+ return format(s, st);
+ ila_foreach_type
+#undef _
+ return format (s, "invalid_ila_type");
+}
+
+static u8 *
+format_ila_entry (u8 * s, va_list * va)
+{
+ vnet_main_t *vnm = va_arg (*va, vnet_main_t *);
+ ila_entry_t *e = va_arg (*va, ila_entry_t *);
+
+ if (!e)
+ {
+ return format (s, "%-15s%=40s%=40s%+16s%+18s%+11s", "Type", "SIR Address",
+ "ILA Address", "Checksum Mode", "Direction", "Next DPO");
+ }
+ else if (vnm)
+ {
+ if (ip6_address_is_zero(&e->next_hop))
+ {
+ return format (s, "%-15U%=40U%=40U%18U%11U%s",
+ format_ila_type, e->type,
+ format_ip6_address, &e->sir_address,
+ format_ip6_address, &e->ila_address,
+ format_csum_mode, e->csum_mode,
+ format_ila_direction, e->dir,
+ "n/a");
+ }
+ else
+ {
+ return format (s, "%-15U%=40U%=40U%18U%11U%U",
+ format_ila_type, e->type,
+ format_ip6_address, &e->sir_address,
+ format_ip6_address, &e->ila_address,
+ format_csum_mode, e->csum_mode,
+ format_ila_direction, e->dir,
+ format_dpo_id, &e->ila_dpo, 0);
+ }
+ }
+
+ return NULL;
+}
+
+u8 *
+format_ila_ila2sir_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 *);
+ ila_ila2sir_trace_t *t = va_arg (*args, ila_ila2sir_trace_t *);
+ return format (s,
+ "ILA -> SIR adj index: %d entry index: %d initial_dst: %U",
+ t->adj_index, t->ila_index, format_ip6_address,
+ &t->initial_dst);
+}
+
+static uword
+unformat_ila_direction (unformat_input_t * input, va_list * args)
+{
+ ila_direction_t *result = va_arg (*args, ila_direction_t *);
+#define _(i,n,s) \
+ if (unformat(input, s)) \
+ { \
+ *result = ILA_DIR_##i; \
+ return 1;\
+ }
+
+ ila_foreach_direction
+#undef _
+ return 0;
+}
+
+static uword
+unformat_ila_type (unformat_input_t * input, va_list * args)
+{
+ ila_type_t *result = va_arg (*args, ila_type_t *);
+#define _(i,n,s) \
+ if (unformat(input, s)) \
+ { \
+ *result = ILA_TYPE_##i; \
+ return 1;\
+ }
+
+ ila_foreach_type
+#undef _
+ return 0;
+}
+
+static uword
+unformat_ila_csum_mode (unformat_input_t * input, va_list * args)
+{
+ ila_csum_mode_t *result = va_arg (*args, ila_csum_mode_t *);
+ if (unformat (input, "none") || unformat (input, "no-action"))
+ {
+ *result = ILA_CSUM_MODE_NO_ACTION;
+ return 1;
+ }
+ if (unformat (input, "neutral-map"))
+ {
+ *result = ILA_CSUM_MODE_NEUTRAL_MAP;
+ return 1;
+ }
+ if (unformat (input, "adjust-transport"))
+ {
+ *result = ILA_CSUM_MODE_ADJUST_TRANSPORT;
+ return 1;
+ }
+ return 0;
+}
+
+static uword
+unformat_half_ip6_address (unformat_input_t * input, va_list * args)
+{
+ u64 *result = va_arg (*args, u64 *);
+ u32 a[4];
+
+ if (!unformat (input, "%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3]))
+ return 0;
+
+ if (a[0] > 0xFFFF || a[1] > 0xFFFF || a[2] > 0xFFFF || a[3] > 0xFFFF)
+ return 0;
+
+ *result = clib_host_to_net_u64 ((((u64) a[0]) << 48) |
+ (((u64) a[1]) << 32) |
+ (((u64) a[2]) << 16) | (((u64) a[3])));
+
+ return 1;
+}
+
+static vlib_node_registration_t ila_ila2sir_node;
+
+static uword
+ila_ila2sir (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
+ ila_main_t *ilm = &ila_main;
+
+ from = vlib_frame_vector_args (frame);
+ 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)
+ {
+ u32 pi0, pi1;
+ vlib_buffer_t *p0, *p1;
+ ila_entry_t *ie0, *ie1;
+ ip6_header_t *ip60, *ip61;
+ ip6_address_t *sir_address0, *sir_address1;
+
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+ CLIB_PREFETCH (p2->data, sizeof (ip6_header_t), LOAD);
+ CLIB_PREFETCH (p3->data, sizeof (ip6_header_t), LOAD);
+ }
+
+ pi0 = to_next[0] = from[0];
+ pi1 = to_next[1] = from[1];
+ from += 2;
+ n_left_from -= 2;
+ to_next += 2;
+ n_left_to_next -= 2;
+
+ p0 = vlib_get_buffer (vm, pi0);
+ p1 = vlib_get_buffer (vm, pi1);
+ ip60 = vlib_buffer_get_current (p0);
+ ip61 = vlib_buffer_get_current (p1);
+ sir_address0 = &ip60->dst_address;
+ sir_address1 = &ip61->dst_address;
+ ie0 = pool_elt_at_index (ilm->entries,
+ vnet_buffer (p0)->ip.adj_index[VLIB_TX]);
+ ie1 = pool_elt_at_index (ilm->entries,
+ vnet_buffer (p1)->ip.adj_index[VLIB_TX]);
+
+ if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ ila_ila2sir_trace_t *tr =
+ vlib_add_trace (vm, node, p0, sizeof (*tr));
+ tr->ila_index = ie0 - ilm->entries;
+ tr->initial_dst = ip60->dst_address;
+ tr->adj_index = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
+ }
+
+ if (PREDICT_FALSE (p1->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ ila_ila2sir_trace_t *tr =
+ vlib_add_trace (vm, node, p1, sizeof (*tr));
+ tr->ila_index = ie1 - ilm->entries;
+ tr->initial_dst = ip61->dst_address;
+ tr->adj_index = vnet_buffer (p1)->ip.adj_index[VLIB_TX];
+ }
+
+ sir_address0 = (ie0->dir != ILA_DIR_SIR2ILA) ? &ie0->sir_address : sir_address0;
+ sir_address1 = (ie1->dir != ILA_DIR_SIR2ILA) ? &ie1->sir_address : sir_address1;
+ ip60->dst_address.as_u64[0] = sir_address0->as_u64[0];
+ ip60->dst_address.as_u64[1] = sir_address0->as_u64[1];
+ ip61->dst_address.as_u64[0] = sir_address1->as_u64[0];
+ ip61->dst_address.as_u64[1] = sir_address1->as_u64[1];
+
+ vnet_buffer (p0)->ip.adj_index[VLIB_TX] = ie0->ila_dpo.dpoi_index;
+ vnet_buffer (p1)->ip.adj_index[VLIB_TX] = ie1->ila_dpo.dpoi_index;
+
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
+ n_left_to_next, pi0, pi1,
+ ie0->ila_dpo.dpoi_next_node,
+ ie1->ila_dpo.dpoi_next_node);
+ }
+
+ /* Single loop */
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 pi0;
+ vlib_buffer_t *p0;
+ ila_entry_t *ie0;
+ ip6_header_t *ip60;
+ ip6_address_t *sir_address0;
+
+ pi0 = to_next[0] = from[0];
+ from += 1;
+ n_left_from -= 1;
+ to_next += 1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer (vm, pi0);
+ ip60 = vlib_buffer_get_current (p0);
+ sir_address0 = &ip60->dst_address;
+ ie0 = pool_elt_at_index (ilm->entries,
+ vnet_buffer (p0)->ip.adj_index[VLIB_TX]);
+
+ if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ ila_ila2sir_trace_t *tr =
+ vlib_add_trace (vm, node, p0, sizeof (*tr));
+ tr->ila_index = ie0 ? (ie0 - ilm->entries) : ~0;
+ tr->initial_dst = ip60->dst_address;
+ tr->adj_index = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
+ }
+
+ sir_address0 = (ie0->dir != ILA_DIR_SIR2ILA) ? &ie0->sir_address : sir_address0;
+ ip60->dst_address.as_u64[0] = sir_address0->as_u64[0];
+ ip60->dst_address.as_u64[1] = sir_address0->as_u64[1];
+ vnet_buffer (p0)->ip.adj_index[VLIB_TX] = ie0->ila_dpo.dpoi_index;
+
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+ n_left_to_next, pi0,
+ ie0->ila_dpo.dpoi_next_node);
+ }
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+/** *INDENT-OFF* */
+VLIB_REGISTER_NODE (ila_ila2sir_node, static) =
+{
+ .function = ila_ila2sir,
+ .name = "ila-to-sir",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ila_ila2sir_trace,
+ .n_errors = ILA_N_ERROR,
+ .error_strings = ila_error_strings,
+ .n_next_nodes = ILA_ILA2SIR_N_NEXT,
+ .next_nodes =
+ {
+ [ILA_ILA2SIR_NEXT_DROP] = "error-drop"
+ },
+};
+/** *INDENT-ON* */
+
+typedef enum
+{
+ ILA_SIR2ILA_NEXT_DROP,
+ ILA_SIR2ILA_N_NEXT,
+} ila_sir2ila_next_t;
+
+typedef struct
+{
+ u32 ila_index;
+ ip6_address_t initial_dst;
+} ila_sir2ila_trace_t;
+
+u8 *
+format_ila_sir2ila_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 *);
+ ila_sir2ila_trace_t *t = va_arg (*args, ila_sir2ila_trace_t *);
+
+ return format (s, "SIR -> ILA entry index: %d initial_dst: %U",
+ t->ila_index, format_ip6_address, &t->initial_dst);
+}
+
+static vlib_node_registration_t ila_sir2ila_node;
+
+static uword
+ila_sir2ila (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
+ ila_main_t *ilm = &ila_main;
+
+ from = vlib_frame_vector_args (frame);
+ 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)
+ {
+ u32 pi0, pi1;
+ vlib_buffer_t *p0, *p1;
+ ip6_header_t *ip60, *ip61;
+ u32 next0 = ILA_SIR2ILA_NEXT_DROP;
+ u32 next1 = ILA_SIR2ILA_NEXT_DROP;
+ BVT (clib_bihash_kv) kv0, value0;
+ BVT (clib_bihash_kv) kv1, value1;
+ ila_entry_t *ie0 = &ila_sir2ila_default_entry;
+ ila_entry_t *ie1 = &ila_sir2ila_default_entry;
+ ip6_address_t *ila_address0, *ila_address1;
+
+ {
+ vlib_buffer_t *p2, *p3;
+
+ p2 = vlib_get_buffer (vm, from[2]);
+ p3 = vlib_get_buffer (vm, from[3]);
+
+ vlib_prefetch_buffer_header (p2, LOAD);
+ vlib_prefetch_buffer_header (p3, LOAD);
+ CLIB_PREFETCH (p2->data, sizeof (ip6_header_t), LOAD);
+ CLIB_PREFETCH (p3->data, sizeof (ip6_header_t), LOAD);
+ }
+
+ pi0 = to_next[0] = from[0];
+ pi1 = to_next[1] = from[1];
+ from += 2;
+ n_left_from -= 2;
+ to_next += 2;
+ n_left_to_next -= 2;
+
+ p0 = vlib_get_buffer (vm, pi0);
+ p1 = vlib_get_buffer (vm, pi1);
+ ip60 = vlib_buffer_get_current (p0);
+ ip61 = vlib_buffer_get_current (p1);
+ ila_address0 = &ip60->dst_address;
+ ila_address1 = &ip61->dst_address;
+ kv0.key[0] = ip60->dst_address.as_u64[0];
+ kv0.key[1] = ip60->dst_address.as_u64[1];
+ kv0.key[2] = 0;
+ kv1.key[0] = ip61->dst_address.as_u64[0];
+ kv1.key[1] = ip61->dst_address.as_u64[1];
+ kv1.key[2] = 0;
+
+ if (PREDICT_TRUE((BV (clib_bihash_search)
+ (&ilm->id_to_entry_table, &kv0, &value0)) == 0)) {
+ ie0 = &ilm->entries[value0.value];
+ ila_address0 = (ie0->dir != ILA_DIR_ILA2SIR) ? &ie0->ila_address : ila_address0;
+ }
+
+ if ((BV (clib_bihash_search)
+ (&ilm->id_to_entry_table, &kv1, &value1)) == 0) {
+ ie1 = &ilm->entries[value1.value];
+ ila_address1 = (ie1->dir != ILA_DIR_ILA2SIR) ? &ie1->ila_address : ila_address1;
+ }
+
+ if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ ila_sir2ila_trace_t *tr =
+ vlib_add_trace (vm, node, p0, sizeof (*tr));
+ tr->ila_index =
+ (ie0 != &ila_sir2ila_default_entry) ? (ie0 - ilm->entries) : ~0;
+ tr->initial_dst = ip60->dst_address;
+ }
+
+ if (PREDICT_FALSE (p1->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ ila_sir2ila_trace_t *tr =
+ vlib_add_trace (vm, node, p1, sizeof (*tr));
+ tr->ila_index =
+ (ie1 != &ila_sir2ila_default_entry) ? (ie1 - ilm->entries) : ~0;
+ tr->initial_dst = ip61->dst_address;
+ }
+
+ ip60->dst_address.as_u64[0] = ila_address0->as_u64[0];
+ ip60->dst_address.as_u64[1] = ila_address0->as_u64[1];
+ ip61->dst_address.as_u64[0] = ila_address1->as_u64[0];
+ ip61->dst_address.as_u64[1] = ila_address1->as_u64[1];
+
+ vnet_feature_next (vnet_buffer (p0)->sw_if_index[VLIB_RX], &next0, p0);
+ vnet_feature_next (vnet_buffer (p1)->sw_if_index[VLIB_RX], &next1, p1);
+
+ vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
+ n_left_to_next, pi0, pi1, next0,
+ next1);
+ }
+
+ /* Single loop */
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 pi0;
+ vlib_buffer_t *p0;
+ ip6_header_t *ip60;
+ u32 next0 = ILA_SIR2ILA_NEXT_DROP;
+ BVT (clib_bihash_kv) kv0, value0;
+ ila_entry_t *ie0 = &ila_sir2ila_default_entry;
+ ip6_address_t *ila_address0;
+
+ pi0 = to_next[0] = from[0];
+ from += 1;
+ n_left_from -= 1;
+ to_next += 1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer (vm, pi0);
+ ip60 = vlib_buffer_get_current (p0);
+ ila_address0 = &ip60->dst_address;
+
+ kv0.key[0] = ip60->dst_address.as_u64[0];
+ kv0.key[1] = ip60->dst_address.as_u64[1];
+ kv0.key[2] = 0;
+
+ if (PREDICT_TRUE((BV (clib_bihash_search)
+ (&ilm->id_to_entry_table, &kv0, &value0)) == 0)) {
+ ie0 = &ilm->entries[value0.value];
+ ila_address0 = (ie0->dir != ILA_DIR_ILA2SIR) ? &ie0->ila_address : ila_address0;
+ }
+
+ if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ ila_sir2ila_trace_t *tr =
+ vlib_add_trace (vm, node, p0, sizeof (*tr));
+ tr->ila_index =
+ (ie0 != &ila_sir2ila_default_entry) ? (ie0 - ilm->entries) : ~0;
+ tr->initial_dst = ip60->dst_address;
+ }
+
+ //This operation should do everything for any type (except vnid4 obviously)
+ ip60->dst_address.as_u64[0] = ila_address0->as_u64[0];
+ ip60->dst_address.as_u64[1] = ila_address0->as_u64[1];
+
+ vnet_feature_next (vnet_buffer (p0)->sw_if_index[VLIB_RX], &next0, p0);
+
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+ n_left_to_next, pi0, next0);
+ }
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+/** *INDENT-OFF* */
+VLIB_REGISTER_NODE (ila_sir2ila_node, static) =
+{
+ .function = ila_sir2ila,.name = "sir-to-ila",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ila_sir2ila_trace,
+ .n_errors = ILA_N_ERROR,
+ .error_strings = ila_error_strings,
+ .n_next_nodes = ILA_SIR2ILA_N_NEXT,
+ .next_nodes =
+ {
+ [ILA_SIR2ILA_NEXT_DROP] = "error-drop"
+ },
+};
+/** *INDENT-ON* */
+
+/** *INDENT-OFF* */
+VNET_FEATURE_INIT (ila_sir2ila, static) =
+{
+ .arc_name = "ip6-unicast",
+ .node_name = "sir-to-ila",
+ .runs_before = VNET_FEATURES ("ip6-lookup"),
+};
+/** *INDENT-ON* */
+
+static void
+ila_entry_stack (ila_entry_t *ie)
+{
+ /*
+ * restack on the next-hop's FIB entry
+ */
+ dpo_stack(ila_dpo_type,
+ DPO_PROTO_IP6,
+ &ie->ila_dpo,
+ fib_entry_contribute_ip_forwarding(
+ ie->next_hop_fib_entry_index));
+}
+
+int
+ila_add_del_entry (ila_add_del_entry_args_t * args)
+{
+ ila_main_t *ilm = &ila_main;
+ BVT (clib_bihash_kv) kv, value;
+
+ //Sanity check
+ if (args->type == ILA_TYPE_IID || args->type == ILA_TYPE_LUID)
+ {
+ if ((args->sir_address.as_u8[8] >> 5) != args->type)
+ {
+ clib_warning ("Incorrect SIR address (ILA type mismatch %d %d)",
+ args->sir_address.as_u8[8] >> 1, args->type);
+ return -1;
+ }
+ if (args->sir_address.as_u8[8] & 0x10)
+ {
+ clib_warning ("Checksum bit should not be set in SIR address");
+ return -1;
+ }
+ }
+ else if (args->type == ILA_TYPE_VNIDM)
+ {
+ if (args->sir_address.as_u8[0] != 0xff ||
+ (args->sir_address.as_u8[1] & 0xf0) != 0xf0)
+ {
+ clib_warning ("SIR multicast address must start with fff");
+ return -1;
+ }
+ if (args->sir_address.as_u16[1] || args->sir_address.as_u16[2] ||
+ args->sir_address.as_u16[3] || args->sir_address.as_u16[4] ||
+ args->sir_address.as_u16[5] || (args->sir_address.as_u8[12] & 0xf0))
+ {
+ clib_warning ("SIR multicast address must start with fff");
+ return -1;
+ }
+ }
+
+ if (!args->is_del)
+ {
+ ila_entry_t *e;
+ pool_get (ilm->entries, e);
+ e->type = args->type;
+ e->sir_address = args->sir_address;
+ e->next_hop = args->next_hop_address;
+ e->csum_mode = args->csum_mode;
+ e->dir = args->dir;
+
+ //Construct ILA address
+ switch (e->type)
+ {
+ case ILA_TYPE_IID:
+ e->ila_address = e->sir_address;
+ break;
+ case ILA_TYPE_LUID:
+ e->ila_address.as_u64[0] = args->locator;
+ e->ila_address.as_u64[1] = args->sir_address.as_u64[1];
+ break;
+ case ILA_TYPE_VNID6:
+ e->ila_address.as_u64[0] = args->locator;
+ e->ila_address.as_u8[8] = (ILA_TYPE_VNID6 << 1);
+ e->ila_address.as_u32[2] |= args->vnid;
+ e->ila_address.as_u32[3] = args->sir_address.as_u32[3];
+ break;
+ case ILA_TYPE_VNIDM:
+ e->ila_address.as_u64[0] = args->locator;
+ e->ila_address.as_u8[8] = (ILA_TYPE_VNIDM << 1);
+ e->ila_address.as_u32[2] |= args->vnid;
+ e->ila_address.as_u32[3] = args->sir_address.as_u32[3];
+ e->ila_address.as_u8[12] |= args->sir_address.as_u8[2] << 4;
+ break;
+ case ILA_TYPE_VNID4:
+ clib_warning ("ILA type '%U' is not supported", format_ila_type,
+ e->type);
+ return -1;
+ }
+
+ //Modify ILA checksum if necessary
+ if (e->csum_mode == ILA_CSUM_MODE_NEUTRAL_MAP)
+ {
+ ip_csum_t csum = e->ila_address.as_u16[7];
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ csum = ip_csum_sub_even (csum, e->sir_address.as_u32[i]);
+ csum = ip_csum_add_even (csum, e->ila_address.as_u32[i]);
+ }
+ csum = ip_csum_add_even (csum, clib_host_to_net_u16 (0x1000));
+ e->ila_address.as_u16[7] = ip_csum_fold (csum);
+ e->ila_address.as_u8[8] |= 0x10;
+ }
+
+ //Create entry with the sir address
+ kv.key[0] = e->sir_address.as_u64[0];
+ kv.key[1] = e->sir_address.as_u64[1];
+ kv.key[2] = 0;
+ kv.value = e - ilm->entries;
+ BV (clib_bihash_add_del) (&ilm->id_to_entry_table, &kv,
+ 1 /* is_add */ );
+
+ if (!ip6_address_is_zero(&e->next_hop))
+ {
+ /*
+ * become a child of the FIB netry for the next-hop
+ * so we are informed when its forwarding changes
+ */
+ fib_prefix_t next_hop = {
+ .fp_addr = {
+ .ip6 = e->next_hop,
+ },
+ .fp_len = 128,
+ .fp_proto = FIB_PROTOCOL_IP6,
+ };
+
+ e->next_hop_fib_entry_index =
+ fib_table_entry_special_add(0,
+ &next_hop,
+ FIB_SOURCE_RR,
+ FIB_ENTRY_FLAG_NONE,
+ ADJ_INDEX_INVALID);
+ e->next_hop_child_index =
+ fib_entry_child_add(e->next_hop_fib_entry_index,
+ ila_fib_node_type,
+ e - ilm->entries);
+
+ /*
+ * Create a route that results in the ILA entry
+ */
+ dpo_id_t dpo = DPO_INVALID;
+ fib_prefix_t pfx = {
+ .fp_addr = {
+ .ip6 = e->ila_address,
+ },
+ .fp_len = 128,
+ .fp_proto = FIB_PROTOCOL_IP6,
+ };
+
+ dpo_set(&dpo, ila_dpo_type, DPO_PROTO_IP6, e - ilm->entries);
+
+ fib_table_entry_special_dpo_add(0,
+ &pfx,
+ FIB_SOURCE_PLUGIN_HI,
+ FIB_ENTRY_FLAG_EXCLUSIVE,
+ &dpo);
+ dpo_reset(&dpo);
+
+ /*
+ * finally stack the ILA entry so it will forward to the next-hop
+ */
+ ila_entry_stack (e);
+ }
+ }
+ else
+ {
+ ila_entry_t *e;
+ kv.key[0] = args->sir_address.as_u64[0];
+ kv.key[1] = args->sir_address.as_u64[1];
+ kv.key[2] = 0;
+
+ if ((BV (clib_bihash_search) (&ilm->id_to_entry_table, &kv, &value) <
+ 0))
+ {
+ return -1;
+ }
+
+ e = &ilm->entries[value.value];
+
+ if (!ip6_address_is_zero(&e->next_hop))
+ {
+ fib_prefix_t pfx = {
+ .fp_addr = {
+ .ip6 = e->ila_address,
+ },
+ .fp_len = 128,
+ .fp_proto = FIB_PROTOCOL_IP6,
+ };
+
+ fib_table_entry_special_remove(0, &pfx, FIB_SOURCE_PLUGIN_HI);
+ /*
+ * remove this ILA entry as child of the FIB netry for the next-hop
+ */
+ fib_entry_child_remove(e->next_hop_fib_entry_index,
+ e->next_hop_child_index);
+ fib_table_entry_delete_index(e->next_hop_fib_entry_index,
+ FIB_SOURCE_RR);
+ e->next_hop_fib_entry_index = FIB_NODE_INDEX_INVALID;
+ }
+ dpo_reset (&e->ila_dpo);
+
+ BV (clib_bihash_add_del) (&ilm->id_to_entry_table, &kv,
+ 0 /* is_add */ );
+ pool_put (ilm->entries, e);
+ }
+ return 0;
+}
+
+int
+ila_interface (u32 sw_if_index, u8 disable)
+{
+ vnet_feature_enable_disable ("ip4-unicast", "sir-to-ila", sw_if_index,
+ !disable, 0, 0);
+ return 0;
+}
+
+clib_error_t *
+vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
+ int from_early_init)
+{
+ clib_error_t *error = 0;
+
+ return error;
+}
+
+u8 *format_ila_dpo (u8 * s, va_list * va)
+{
+ index_t index = va_arg (*va, index_t);
+ CLIB_UNUSED(u32 indent) = va_arg (*va, u32);
+ ila_main_t *ilm = &ila_main;
+ ila_entry_t *ie = pool_elt_at_index (ilm->entries, index);
+ return format(s, "ILA: idx:%d sir:%U",
+ index,
+ format_ip6_address, &ie->sir_address);
+}
+
+/**
+ * @brief no-op lock function.
+ * The lifetime of the ILA entry is managed by the control plane
+ */
+static void
+ila_dpo_lock (dpo_id_t *dpo)
+{
+}
+
+/**
+ * @brief no-op unlock function.
+ * The lifetime of the ILA entry is managed by the control plane
+ */
+static void
+ila_dpo_unlock (dpo_id_t *dpo)
+{
+}
+
+const static dpo_vft_t ila_vft = {
+ .dv_lock = ila_dpo_lock,
+ .dv_unlock = ila_dpo_unlock,
+ .dv_format = format_ila_dpo,
+};
+const static char* const ila_ip6_nodes[] =
+{
+ "ila-to-sir",
+ NULL,
+};
+const static char* const * const ila_nodes[DPO_PROTO_NUM] =
+{
+ [DPO_PROTO_IP6] = ila_ip6_nodes,
+};
+
+static fib_node_t *
+ila_fib_node_get_node (fib_node_index_t index)
+{
+ ila_main_t *ilm = &ila_main;
+ ila_entry_t *ie = pool_elt_at_index (ilm->entries, index);
+
+ return (&ie->ila_fib_node);
+}
+
+/**
+ * @brief no-op unlock function.
+ * The lifetime of the ILA entry is managed by the control plane
+ */
+static void
+ila_fib_node_last_lock_gone (fib_node_t *node)
+{
+}
+
+static ila_entry_t *
+ila_entry_from_fib_node (fib_node_t *node)
+{
+ return ((ila_entry_t*)(((char*)node) -
+ STRUCT_OFFSET_OF(ila_entry_t, ila_fib_node)));
+}
+
+/**
+ * @brief
+ * Callback function invoked when the forwarding changes for the ILA next-hop
+ */
+static fib_node_back_walk_rc_t
+ila_fib_node_back_walk_notify (fib_node_t *node,
+ fib_node_back_walk_ctx_t *ctx)
+{
+ ila_entry_stack(ila_entry_from_fib_node(node));
+
+ return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/*
+ * ILA's FIB graph node virtual function table
+ */
+static const fib_node_vft_t ila_fib_node_vft = {
+ .fnv_get = ila_fib_node_get_node,
+ .fnv_last_lock = ila_fib_node_last_lock_gone,
+ .fnv_back_walk = ila_fib_node_back_walk_notify,
+};
+
+clib_error_t *
+ila_init (vlib_main_t * vm)
+{
+ ila_main_t *ilm = &ila_main;
+ ilm->entries = NULL;
+
+ ilm->lookup_table_nbuckets = ILA_TABLE_DEFAULT_HASH_NUM_BUCKETS;
+ ilm->lookup_table_nbuckets = 1 << max_log2 (ilm->lookup_table_nbuckets);
+ ilm->lookup_table_size = ILA_TABLE_DEFAULT_HASH_MEMORY_SIZE;
+
+ BV (clib_bihash_init) (&ilm->id_to_entry_table,
+ "ila id to entry index table",
+ ilm->lookup_table_nbuckets, ilm->lookup_table_size);
+
+ ila_dpo_type = dpo_register_new_type(&ila_vft, ila_nodes);
+ ila_fib_node_type = fib_node_register_new_type(&ila_fib_node_vft);
+
+ return NULL;
+}
+
+VLIB_INIT_FUNCTION (ila_init);
+
+static clib_error_t *
+ila_entry_command_fn (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ ila_add_del_entry_args_t args = { 0 };
+ u8 next_hop_set = 0;
+ int ret;
+
+ args.type = ILA_TYPE_IID;
+ args.csum_mode = ILA_CSUM_MODE_NO_ACTION;
+ args.local_adj_index = ~0;
+ args.dir = ILA_DIR_BIDIR;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "type %U", unformat_ila_type, &args.type))
+ ;
+ else if (unformat
+ (line_input, "sir-address %U", unformat_ip6_address,
+ &args.sir_address))
+ ;
+ else if (unformat
+ (line_input, "locator %U", unformat_half_ip6_address,
+ &args.locator))
+ ;
+ else if (unformat
+ (line_input, "csum-mode %U", unformat_ila_csum_mode,
+ &args.csum_mode))
+ ;
+ else if (unformat (line_input, "vnid %x", &args.vnid))
+ ;
+ else if (unformat
+ (line_input, "next-hop %U", unformat_ip6_address,
+ &args.next_hop_address))
+ ;
+ else if (unformat
+ (line_input, "direction %U", unformat_ila_direction, &args.dir))
+ next_hop_set = 1;
+ else if (unformat (line_input, "del"))
+ args.is_del = 1;
+ else
+ return clib_error_return (0, "parse error: '%U'",
+ format_unformat_error, line_input);
+ }
+
+ unformat_free (line_input);
+
+ if (!next_hop_set)
+ return clib_error_return (0, "Specified a next hop");
+
+ if ((ret = ila_add_del_entry (&args)))
+ return clib_error_return (0, "ila_add_del_entry returned error %d", ret);
+
+ return NULL;
+}
+
+VLIB_CLI_COMMAND (ila_entry_command, static) =
+{
+ .path = "ila entry",
+ .short_help = "ila entry [type <type>] [sir-address <address>] [locator <locator>] [vnid <hex-vnid>]"
+ " [adj-index <adj-index>] [next-hop <next-hop>] [direction (bidir|sir2ila|ila2sir)]"
+ " [csum-mode (no-action|neutral-map|transport-adjust)] [del]",
+ .function = ila_entry_command_fn,
+};
+
+static clib_error_t *
+ila_interface_command_fn (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 sw_if_index = ~0;
+ u8 disable = 0;
+
+ if (!unformat (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
+ {
+ return clib_error_return (0, "Invalid interface name");
+ }
+
+ if (unformat (input, "disable"))
+ {
+ disable = 1;
+ }
+
+ int ret;
+ if ((ret = ila_interface (sw_if_index, disable)))
+ return clib_error_return (0, "ila_interface returned error %d", ret);
+
+ return NULL;
+}
+
+VLIB_CLI_COMMAND (ila_interface_command, static) =
+{
+ .path = "ila interface",
+ .short_help = "ila interface <interface-name> [disable]",
+ .function = ila_interface_command_fn,
+};
+
+static clib_error_t *
+ila_show_entries_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ ila_main_t *ilm = &ila_main;
+ ila_entry_t *e;
+
+ vlib_cli_output (vm, " %U\n", format_ila_entry, vnm, NULL);
+ pool_foreach (e, ilm->entries,
+ ({
+ vlib_cli_output (vm, " %U\n", format_ila_entry, vnm, e);
+ }));
+
+ return NULL;
+}
+
+VLIB_CLI_COMMAND (ila_show_entries_command, static) =
+{
+ .path = "show ila entries",
+ .short_help = "show ila entries",
+ .function = ila_show_entries_command_fn,
+};
diff --git a/src/plugins/ila/ila.h b/src/plugins/ila/ila.h
new file mode 100644
index 00000000000..26620983823
--- /dev/null
+++ b/src/plugins/ila/ila.h
@@ -0,0 +1,116 @@
+/*
+ * 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 ILA_H
+#define ILA_H
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/fib/fib_node.h>
+
+#include <vppinfra/bihash_24_8.h>
+#include <vppinfra/bihash_template.h>
+
+#define ila_foreach_type \
+ _(IID, 0, "iid") \
+ _(LUID, 1, "luid") \
+ _(VNID4, 2, "vnid-ip4") \
+ _(VNID6, 3, "vnid-ip6") \
+ _(VNIDM, 4, "vnid-multicast")
+
+typedef enum {
+#define _(i,n,s) ILA_TYPE_##i = n,
+ ila_foreach_type
+#undef _
+} ila_type_t;
+
+#define ila_csum_foreach_type \
+_(NO_ACTION, 0, "no-action") \
+_(NEUTRAL_MAP, 1, "neutral-map") \
+_(ADJUST_TRANSPORT, 2, "adjust-transport")
+
+typedef enum {
+#define _(i,n,s) ILA_CSUM_MODE_##i = n,
+ ila_csum_foreach_type
+#undef _
+ ILA_CSUM_N_TYPES
+} ila_csum_mode_t;
+
+#define ila_foreach_direction \
+_(BIDIR, 0, "bidir") \
+_(SIR2ILA, 1, "sir2ila") \
+_(ILA2SIR, 2, "ila2sir")
+
+typedef enum {
+#define _(i,n,s) ILA_DIR_##i = n,
+ ila_foreach_direction
+#undef _
+} ila_direction_t;
+
+typedef struct {
+ /**
+ * Fib Node base class
+ */
+ fib_node_t ila_fib_node;
+ ila_type_t type;
+ ip6_address_t sir_address;
+ ip6_address_t ila_address;
+ ip6_address_t next_hop;
+ ila_csum_mode_t csum_mode;
+ ila_direction_t dir;
+
+ /**
+ * The FIB entry index for the next-hop
+ */
+ fib_node_index_t next_hop_fib_entry_index;
+
+ /**
+ * The child index on the FIB entry
+ */
+ u32 next_hop_child_index;
+
+ /**
+ * The next DPO in the grpah to follow
+ */
+ dpo_id_t ila_dpo;
+} ila_entry_t;
+
+typedef struct {
+ ila_entry_t *entries; //Pool of ILA entries
+
+ u64 lookup_table_nbuckets;
+ u64 lookup_table_size;
+ clib_bihash_24_8_t id_to_entry_table;
+
+ u32 ip6_lookup_next_index;
+} ila_main_t;
+
+
+typedef struct {
+ ila_type_t type;
+ ip6_address_t sir_address;
+ ip6_address_t next_hop_address;
+ u64 locator;
+ u32 vnid;
+ u32 local_adj_index;
+ ila_csum_mode_t csum_mode;
+ ila_direction_t dir;
+ u8 is_del;
+} ila_add_del_entry_args_t;
+
+int ila_add_del_entry (ila_add_del_entry_args_t * args);
+int ila_interface (u32 sw_if_index, u8 disable);
+
+#endif //ILA_H
diff --git a/src/plugins/sixrd.am b/src/plugins/sixrd.am
new file mode 100644
index 00000000000..0de4508831e
--- /dev/null
+++ b/src/plugins/sixrd.am
@@ -0,0 +1,26 @@
+# Copyright (c) 2015 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.
+
+libsixrd_plugin_la_SOURCES = \
+ sixrd/sixrd.c \
+ sixrd/sixrd_dpo.c \
+ sixrd/ip4_sixrd.c \
+ sixrd/ip6_sixrd.c
+
+noinst_HEADERS += \
+ sixrd/sixrd.h \
+ sixrd/sixrd_dpo.h
+
+vppplugins_LTLIBRARIES += libsixrd_plugin.la
+
+# vi:syntax=automake
diff --git a/src/plugins/sixrd/ip4_sixrd.c b/src/plugins/sixrd/ip4_sixrd.c
new file mode 100644
index 00000000000..2fb8015d994
--- /dev/null
+++ b/src/plugins/sixrd/ip4_sixrd.c
@@ -0,0 +1,127 @@
+/*---------------------------------------------------------------------------
+ * Copyright (c) 2009-2014 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 "sixrd.h"
+
+static vlib_node_registration_t ip4_sixrd_node;
+
+typedef enum {
+ IP4_SIXRD_NEXT_IP6_LOOKUP,
+ IP4_SIXRD_NEXT_DROP,
+ IP4_SIXRD_N_NEXT,
+} ip4_sixrd_next_t;
+
+/*
+ * ip4_sixrd_sec_check
+ */
+static_always_inline void
+ip4_sixrd_sec_check (sixrd_domain_t *d, ip4_address_t sa4, ip6_address_t sa6, u8 *error)
+{
+ u32 a = sixrd_get_addr(d, sa6.as_u64[0]);
+ clib_warning("Security check: %U %U", format_ip4_address, &a, format_ip4_address, &sa4);
+ if (PREDICT_FALSE(sixrd_get_addr(d, sa6.as_u64[0]) != sa4.as_u32))
+ *error = SIXRD_ERROR_SEC_CHECK;
+}
+
+/*
+ * ip4_sixrd
+ */
+static uword
+ip4_sixrd (vlib_main_t *vm,
+ vlib_node_runtime_t *node,
+ vlib_frame_t *frame)
+{
+ u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
+ vlib_node_runtime_t *error_node = vlib_node_get_runtime(vm, ip4_sixrd_node.index);
+ u32 decap = 0;
+
+ from = vlib_frame_vector_args(frame);
+ 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);
+
+ /* Single loop */
+ while (n_left_from > 0 && n_left_to_next > 0) {
+ u32 pi0;
+ vlib_buffer_t *p0;
+ u8 error0 = SIXRD_ERROR_NONE;
+ sixrd_domain_t *d0 = 0;
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ u32 sixrd_domain_index0 = ~0;
+ u32 next0;
+
+ pi0 = to_next[0] = from[0];
+ from += 1;
+ n_left_from -= 1;
+ to_next +=1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer(vm, pi0);
+ ip40 = vlib_buffer_get_current(p0);
+
+ /* Throw away anything that isn't IP in IP. */
+ if (PREDICT_TRUE(ip40->protocol == IP_PROTOCOL_IPV6 && clib_net_to_host_u16(ip40->length) >= 60)) {
+ vlib_buffer_advance(p0, sizeof(ip4_header_t));
+ ip60 = vlib_buffer_get_current(p0);
+ d0 = ip4_sixrd_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], (ip6_address_t *)&ip60->src_address,
+ &sixrd_domain_index0, &error0);
+ } else {
+ error0 = SIXRD_ERROR_BAD_PROTOCOL;
+ }
+ if (d0) {
+ /* SIXRD inbound security check */
+ ip4_sixrd_sec_check(d0, ip40->src_address, ip60->src_address, &error0);
+ }
+
+ next0 = error0 == SIXRD_ERROR_NONE ? IP4_SIXRD_NEXT_IP6_LOOKUP : IP4_SIXRD_NEXT_DROP;
+
+ if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
+ sixrd_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
+ tr->sixrd_domain_index = sixrd_domain_index0;
+ }
+
+ p0->error = error_node->errors[error0];
+ if (PREDICT_TRUE(error0 == SIXRD_ERROR_NONE)) decap++;
+ vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
+
+ }
+ vlib_put_next_frame(vm, node, next_index, n_left_to_next);
+ }
+ vlib_node_increment_counter(vm, ip4_sixrd_node.index, SIXRD_ERROR_DECAPSULATED, decap);
+
+ return frame->n_vectors;
+}
+
+static char *sixrd_error_strings[] = {
+#define _(sym,string) string,
+ foreach_sixrd_error
+#undef _
+};
+
+VLIB_REGISTER_NODE(ip4_sixrd_node,static) = {
+ .function = ip4_sixrd,
+ .name = "ip4-sixrd",
+ .vector_size = sizeof(u32),
+ .format_trace = format_sixrd_trace,
+ .n_errors = SIXRD_N_ERROR,
+ .error_strings = sixrd_error_strings,
+ .n_next_nodes = IP4_SIXRD_N_NEXT,
+ .next_nodes = {
+ [IP4_SIXRD_NEXT_IP6_LOOKUP] = "ip6-lookup",
+ [IP4_SIXRD_NEXT_DROP] = "error-drop",
+ },
+};
diff --git a/src/plugins/sixrd/ip6_sixrd.c b/src/plugins/sixrd/ip6_sixrd.c
new file mode 100644
index 00000000000..36f3fab320b
--- /dev/null
+++ b/src/plugins/sixrd/ip6_sixrd.c
@@ -0,0 +1,129 @@
+/*---------------------------------------------------------------------------
+ * Copyright (c) 2009-2014 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.
+ *---------------------------------------------------------------------------
+ */
+/*
+ * Defines used for testing various optimisation schemes
+ */
+#define SIXRD_ENCAP_DUAL 0
+
+#include "sixrd.h"
+
+static vlib_node_registration_t ip6_sixrd_node;
+
+typedef enum {
+ IP6_SIXRD_NEXT_IP4_LOOKUP,
+ IP6_SIXRD_NEXT_DROP,
+ IP6_SIXRD_N_NEXT,
+} ip6_sixrd_next_t;
+
+/*
+ * ip6_sixrd
+ */
+static uword
+ip6_sixrd (vlib_main_t *vm,
+ vlib_node_runtime_t *node,
+ vlib_frame_t *frame)
+{
+ u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
+ vlib_node_runtime_t *error_node = vlib_node_get_runtime(vm, ip6_sixrd_node.index);
+ u32 encap = 0;
+ from = vlib_frame_vector_args(frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0) {
+ vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0) {
+ u32 pi0;
+ vlib_buffer_t *p0;
+ sixrd_domain_t *d0;
+ u8 error0 = SIXRD_ERROR_NONE;
+ ip6_header_t *ip60;
+ ip4_header_t *ip4h0;
+ u32 next0 = IP6_SIXRD_NEXT_IP4_LOOKUP;
+ u32 sixrd_domain_index0 = ~0;
+
+ pi0 = to_next[0] = from[0];
+ from += 1;
+ n_left_from -= 1;
+ to_next +=1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer(vm, pi0);
+ ip60 = vlib_buffer_get_current(p0);
+ // p0->current_length = clib_net_to_host_u16(ip40->length);
+ d0 = ip6_sixrd_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], &sixrd_domain_index0);
+ ASSERT(d0);
+
+ /* SIXRD calc */
+ u64 dal60 = clib_net_to_host_u64(ip60->dst_address.as_u64[0]);
+ u32 da40 = sixrd_get_addr(d0, dal60);
+ u16 len = clib_net_to_host_u16(ip60->payload_length) + 60;
+ if (da40 == 0) error0 = SIXRD_ERROR_UNKNOWN;
+
+ /* construct ipv4 header */
+ vlib_buffer_advance(p0, - (sizeof(ip4_header_t)));
+ ip4h0 = vlib_buffer_get_current(p0);
+ vnet_buffer(p0)->sw_if_index[VLIB_TX] = (u32)~0;
+ ip4h0->ip_version_and_header_length = 0x45;
+ ip4h0->tos = 0;
+ ip4h0->length = clib_host_to_net_u16(len);
+ ip4h0->fragment_id = 0;
+ ip4h0->flags_and_fragment_offset = 0;
+ ip4h0->ttl = 0x40;
+ ip4h0->protocol = IP_PROTOCOL_IPV6;
+ ip4h0->src_address = d0->ip4_src;
+ ip4h0->dst_address.as_u32 = clib_host_to_net_u32(da40);
+ ip4h0->checksum = ip4_header_checksum(ip4h0);
+
+ next0 = error0 == SIXRD_ERROR_NONE ? IP6_SIXRD_NEXT_IP4_LOOKUP : IP6_SIXRD_NEXT_DROP;
+
+ if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
+ sixrd_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
+ tr->sixrd_domain_index = sixrd_domain_index0;
+ }
+
+ p0->error = error_node->errors[error0];
+ if (PREDICT_TRUE(error0 == SIXRD_ERROR_NONE)) encap++;
+
+ vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
+ }
+ vlib_put_next_frame(vm, node, next_index, n_left_to_next);
+ }
+ vlib_node_increment_counter(vm, ip6_sixrd_node.index, SIXRD_ERROR_ENCAPSULATED, encap);
+
+ return frame->n_vectors;
+}
+
+static char *sixrd_error_strings[] = {
+#define _(sym,string) string,
+ foreach_sixrd_error
+#undef _
+};
+
+VLIB_REGISTER_NODE(ip6_sixrd_node,static) = {
+ .function = ip6_sixrd,
+ .name = "ip6-sixrd",
+ .vector_size = sizeof(u32),
+ .format_trace = format_sixrd_trace,
+ .n_errors = SIXRD_N_ERROR,
+ .error_strings = sixrd_error_strings,
+ .n_next_nodes = IP6_SIXRD_N_NEXT,
+ .next_nodes = {
+ [IP6_SIXRD_NEXT_IP4_LOOKUP] = "ip4-lookup",
+ [IP6_SIXRD_NEXT_DROP] = "error-drop",
+ },
+};
diff --git a/src/plugins/sixrd/sixrd.c b/src/plugins/sixrd/sixrd.c
new file mode 100644
index 00000000000..66e631a2b6a
--- /dev/null
+++ b/src/plugins/sixrd/sixrd.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2015 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 "sixrd.h"
+#include <vnet/plugin/plugin.h>
+
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/adj/adj.h>
+
+/*
+ * This code supports the following sixrd modes:
+ *
+ * 32 EA bits (Complete IPv4 address is embedded):
+ * ea_bits_len = 32
+ * IPv4 suffix is embedded:
+ * ea_bits_len = < 32
+ * No embedded address bits (1:1 mode):
+ * ea_bits_len = 0
+ */
+
+int
+sixrd_create_domain (ip6_address_t *ip6_prefix,
+ u8 ip6_prefix_len,
+ ip4_address_t *ip4_prefix,
+ u8 ip4_prefix_len,
+ ip4_address_t *ip4_src,
+ u32 *sixrd_domain_index,
+ u16 mtu)
+{
+ dpo_id_t dpo_v6 = DPO_INVALID, dpo_v4 = DPO_INVALID;
+ sixrd_main_t *mm = &sixrd_main;
+ fib_node_index_t fei;
+ sixrd_domain_t *d;
+
+ /* Get domain index */
+ pool_get_aligned(mm->domains, d, CLIB_CACHE_LINE_BYTES);
+ memset(d, 0, sizeof (*d));
+ *sixrd_domain_index = d - mm->domains;
+
+ /* Init domain struct */
+ d->ip4_prefix.as_u32 = ip4_prefix->as_u32;
+ d->ip4_prefix_len = ip4_prefix_len;
+ d->ip6_prefix = *ip6_prefix;
+ d->ip6_prefix_len = ip6_prefix_len;
+ d->ip4_src = *ip4_src;
+ d->mtu = mtu;
+
+ if (ip4_prefix_len < 32)
+ d->shift = 64 - ip6_prefix_len + (32 - ip4_prefix_len);
+
+ /* Create IPv6 route/adjacency */
+ fib_prefix_t pfx6 = {
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_len = d->ip6_prefix_len,
+ .fp_addr = {
+ .ip6 = d->ip6_prefix,
+ },
+ };
+ sixrd_dpo_create(DPO_PROTO_IP6,
+ *sixrd_domain_index,
+ &dpo_v6);
+ fib_table_entry_special_dpo_add(0, &pfx6,
+ FIB_SOURCE_SIXRD,
+ FIB_ENTRY_FLAG_EXCLUSIVE,
+ &dpo_v6);
+ dpo_reset (&dpo_v6);
+
+ /*
+ * Multiple SIXRD domains may share same source IPv4 TEP
+ * In this case the route will exist and be SixRD sourced.
+ * Find the adj (if any) already contributed and modify it
+ */
+ fib_prefix_t pfx4 = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 32,
+ .fp_addr = {
+ .ip4 = d->ip4_src,
+ },
+ };
+ fei = fib_table_lookup_exact_match(0, &pfx4);
+
+ if (FIB_NODE_INDEX_INVALID != fei)
+ {
+ dpo_id_t dpo = DPO_INVALID;
+
+ if (fib_entry_get_dpo_for_source (fei, FIB_SOURCE_SIXRD, &dpo))
+ {
+ /*
+ * modify the existing adj to indicate it's shared
+ * skip to route add.
+ * It is locked to pair with the unlock below.
+ */
+ const dpo_id_t *sd_dpo;
+ sixrd_dpo_t *sd;
+
+ ASSERT(DPO_LOAD_BALANCE == dpo.dpoi_type);
+
+ sd_dpo = load_balance_get_bucket(dpo.dpoi_index, 0);
+ sd = sixrd_dpo_get (sd_dpo->dpoi_index);
+
+ sd->sd_domain = ~0;
+ dpo_copy (&dpo_v4, sd_dpo);
+ dpo_reset (&dpo);
+
+ goto route_add;
+ }
+ }
+ /* first time addition of the route */
+ sixrd_dpo_create(DPO_PROTO_IP4,
+ *sixrd_domain_index,
+ &dpo_v4);
+
+route_add:
+ /*
+ * Create ip4 route. This is a reference counted add. If the prefix
+ * already exists and is SixRD sourced, it is now SixRD source n+1 times
+ * and will need to be removed n+1 times.
+ */
+ fib_table_entry_special_dpo_add(0, &pfx4,
+ FIB_SOURCE_SIXRD,
+ FIB_ENTRY_FLAG_EXCLUSIVE,
+ &dpo_v4);
+ dpo_reset (&dpo_v4);
+
+ return 0;
+}
+
+/*
+ * sixrd_delete_domain
+ */
+int
+sixrd_delete_domain (u32 sixrd_domain_index)
+{
+ sixrd_main_t *mm = &sixrd_main;
+ sixrd_domain_t *d;
+
+ if (pool_is_free_index(mm->domains, sixrd_domain_index)) {
+ clib_warning("SIXRD domain delete: domain does not exist: %d",
+ sixrd_domain_index);
+ return -1;
+ }
+
+ d = pool_elt_at_index(mm->domains, sixrd_domain_index);
+
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 32,
+ .fp_addr = {
+ .ip4 = d->ip4_src,
+ },
+ };
+ fib_table_entry_special_remove(0, &pfx, FIB_SOURCE_SIXRD);
+
+ fib_prefix_t pfx6 = {
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_len = d->ip6_prefix_len,
+ .fp_addr = {
+ .ip6 = d->ip6_prefix,
+ },
+ };
+ fib_table_entry_special_remove(0, &pfx6, FIB_SOURCE_SIXRD);
+
+ pool_put(mm->domains, d);
+
+ return 0;
+}
+
+static clib_error_t *
+sixrd_add_domain_command_fn (vlib_main_t *vm,
+ unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ ip4_address_t ip4_prefix;
+ ip6_address_t ip6_prefix;
+ ip4_address_t ip4_src;
+ u32 ip6_prefix_len=0, ip4_prefix_len=0, sixrd_domain_index;
+ u32 num_m_args = 0;
+ /* Optional arguments */
+ u32 mtu = 0;
+
+ /* Get a line of input. */
+ if (!unformat_user(input, unformat_line_input, line_input))
+ return 0;
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
+ if (unformat(line_input, "ip6-pfx %U/%d", unformat_ip6_address, &ip6_prefix, &ip6_prefix_len))
+ num_m_args++;
+ else if (unformat(line_input, "ip4-pfx %U/%d", unformat_ip4_address, &ip4_prefix, &ip4_prefix_len))
+ num_m_args++;
+ else if (unformat(line_input, "ip4-src %U", unformat_ip4_address, &ip4_src))
+ num_m_args++;
+ else if (unformat(line_input, "mtu %d", &mtu))
+ num_m_args++;
+ else
+ return clib_error_return(0, "unknown input `%U'",
+ format_unformat_error, input);
+ }
+ unformat_free(line_input);
+
+ if (num_m_args < 3)
+ return clib_error_return(0, "mandatory argument(s) missing");
+
+ sixrd_create_domain(&ip6_prefix, ip6_prefix_len, &ip4_prefix, ip4_prefix_len,
+ &ip4_src, &sixrd_domain_index, mtu);
+
+ return 0;
+}
+
+static clib_error_t *
+sixrd_del_domain_command_fn (vlib_main_t *vm,
+ unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u32 num_m_args = 0;
+ u32 sixrd_domain_index;
+
+ /* Get a line of input. */
+ if (! unformat_user(input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
+ if (unformat(line_input, "index %d", &sixrd_domain_index))
+ num_m_args++;
+ else
+ return clib_error_return(0, "unknown input `%U'",
+ format_unformat_error, input);
+ }
+ unformat_free(line_input);
+
+ if (num_m_args != 1)
+ return clib_error_return(0, "mandatory argument(s) missing");
+
+ sixrd_delete_domain(sixrd_domain_index);
+
+ return 0;
+}
+
+static u8 *
+format_sixrd_domain (u8 *s, va_list *args)
+{
+ sixrd_domain_t *d = va_arg(*args, sixrd_domain_t *);
+ sixrd_main_t *mm = &sixrd_main;
+
+ s = format(s,
+ "[%d] ip6-pfx %U/%d ip4-pfx %U/%d ip4-src %U mtu %d",
+ d - mm->domains,
+ format_ip6_address, &d->ip6_prefix, d->ip6_prefix_len,
+ format_ip4_address, &d->ip4_prefix, d->ip4_prefix_len,
+ format_ip4_address, &d->ip4_src, d->mtu);
+
+ return s;
+}
+
+static clib_error_t *
+show_sixrd_domain_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
+{
+ sixrd_main_t *mm = &sixrd_main;
+ sixrd_domain_t *d;
+
+ if (pool_elts(mm->domains) == 0)
+ vlib_cli_output(vm, "No SIXRD domains are configured...");
+
+ pool_foreach(d, mm->domains, ({vlib_cli_output(vm, "%U", format_sixrd_domain, d);}));
+
+ return 0;
+
+}
+
+static clib_error_t *
+show_sixrd_stats_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
+{
+ sixrd_main_t *mm = &sixrd_main;
+ sixrd_domain_t *d;
+ int domains = 0, domaincount = 0;
+ if (pool_elts (mm->domains) == 0)
+ vlib_cli_output (vm, "No SIXRD domains are configured...");
+
+ pool_foreach(d, mm->domains, ({
+ domains += sizeof(*d);
+ domaincount++;
+ }));
+
+ vlib_cli_output(vm, "SIXRD domains structure: %d\n", sizeof (sixrd_domain_t));
+ vlib_cli_output(vm, "SIXRD domains: %d (%d bytes)\n", domaincount, domains);
+
+ return 0;
+}
+
+/*
+ * packet trace format function
+ */
+u8 *
+format_sixrd_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 *);
+ sixrd_trace_t *t = va_arg (*args, sixrd_trace_t *);
+ u32 sixrd_domain_index = t->sixrd_domain_index;
+
+ s = format(s, "SIXRD domain index: %d", sixrd_domain_index);
+
+ return s;
+}
+
+VLIB_CLI_COMMAND(sixrd_add_domain_command, static) = {
+ .path = "sixrd add domain",
+ .short_help =
+ "sixrd add domain ip6-pfx <ip6-pfx> ip4-pfx <ip4-pfx> ip4-src <ip4-addr>",
+ .function = sixrd_add_domain_command_fn,
+};
+
+VLIB_CLI_COMMAND(sixrd_del_command, static) = {
+ .path = "sixrd del domain",
+ .short_help =
+ "sixrd del domain index <domain>",
+ .function = sixrd_del_domain_command_fn,
+};
+
+VLIB_CLI_COMMAND(show_sixrd_domain_command, static) = {
+ .path = "show sixrd domain",
+ .function = show_sixrd_domain_command_fn,
+};
+
+VLIB_CLI_COMMAND(show_sixrd_stats_command, static) = {
+ .path = "show sixrd stats",
+ .function = show_sixrd_stats_command_fn,
+};
+
+/*
+ * This routine exists to convince the vlib plugin framework that
+ * we haven't accidentally copied a random .dll into the plugin directory.
+ *
+ * Also collects global variable pointers passed from the vpp engine
+ */
+clib_error_t *
+vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
+ int from_early_init)
+{
+ clib_error_t * error = 0;
+ sixrd_main_t *mm = &sixrd_main;
+
+ mm->vnet_main = vnet_get_main();
+ mm->vlib_main = vm;
+
+ return error;
+}
+
+static clib_error_t * sixrd_init (vlib_main_t * vm)
+{
+ sixrd_dpo_module_init ();
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (sixrd_init);
diff --git a/src/plugins/sixrd/sixrd.h b/src/plugins/sixrd/sixrd.h
new file mode 100644
index 00000000000..56714c9e3bd
--- /dev/null
+++ b/src/plugins/sixrd/sixrd.h
@@ -0,0 +1,141 @@
+/*---------------------------------------------------------------------------
+ * Copyright (c) 2009-2014 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 <stdbool.h>
+#include <vppinfra/error.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/fib/ip6_fib.h>
+
+#include "sixrd_dpo.h"
+
+int sixrd_create_domain(ip6_address_t *ip6_prefix, u8 ip6_prefix_len,
+ ip4_address_t *ip4_prefix, u8 ip4_prefix_len,
+ ip4_address_t *ip4_src, u32 *sixrd_domain_index, u16 mtu);
+int sixrd_delete_domain(u32 sixrd_domain_index);
+u8 *format_sixrd_trace(u8 *s, va_list *args);
+
+typedef struct {
+ ip6_address_t ip6_prefix;
+ ip4_address_t ip4_prefix;
+ ip4_address_t ip4_src;
+ u8 ip6_prefix_len;
+ u8 ip4_prefix_len;
+
+ /* helpers */
+ u8 shift;
+
+ u16 mtu;
+} sixrd_domain_t;
+
+typedef struct {
+ /* pool of SIXRD domains */
+ sixrd_domain_t *domains;
+
+ /* convenience */
+ vlib_main_t *vlib_main;
+ vnet_main_t *vnet_main;
+} sixrd_main_t;
+
+#define foreach_sixrd_error \
+ /* Must be first. */ \
+ _(NONE, "valid SIXRD packets") \
+ _(BAD_PROTOCOL, "bad protocol") \
+ _(WRONG_ICMP_TYPE, "wrong icmp type") \
+ _(SEC_CHECK, "security check failed") \
+ _(ICMP, "unable to translate ICMP") \
+ _(UNKNOWN, "unknown") \
+ _(NO_DOMAIN, "no domain") \
+ _(ENCAPSULATED, "encapsulated") \
+ _(DECAPSULATED, "decapsulated") \
+ _(TRANSLATED_4TO6, "translated 4 to 6") \
+ _(TRANSLATED_6TO4, "translated 6 to 4") \
+ _(FRAGMENT, "fragment handling error") \
+ _(FRAGMENT_QUEUED, "dropped, missing first fragment") \
+ _(FRAGMENTED, "packets requiring fragmentation") \
+ _(FRAGMENT_PARTS, "fragment parts") \
+ _(MALFORMED, "malformed packet")
+
+typedef enum {
+#define _(sym,str) SIXRD_ERROR_##sym,
+ foreach_sixrd_error
+#undef _
+ SIXRD_N_ERROR,
+ } sixrd_error_t;
+
+typedef struct {
+ u32 sixrd_domain_index;
+} sixrd_trace_t;
+
+sixrd_main_t sixrd_main;
+
+/*
+ * sixrd_get_addr
+ */
+static_always_inline u32
+sixrd_get_addr (sixrd_domain_t *d, u64 dal)
+{
+
+ /* 1:1 mode */
+ if (d->ip4_prefix_len == 32) return (d->ip4_prefix.as_u32);
+
+ /* Grab 32 - ip4_prefix_len bits out of IPv6 address from offset ip6_prefix_len */
+ return (d->ip4_prefix.as_u32 | (u32)(dal >> d->shift));
+}
+
+/*
+ * Get the SIXRD domain from an IPv6 lookup adjacency.
+ */
+static_always_inline sixrd_domain_t *
+ip6_sixrd_get_domain (u32 sdi, u32 *sixrd_domain_index)
+{
+ sixrd_main_t *mm = &sixrd_main;
+ sixrd_dpo_t *sd;
+
+ sd = sixrd_dpo_get(sdi);
+
+ ASSERT(sd);
+ *sixrd_domain_index = sd->sd_domain;
+ return pool_elt_at_index(mm->domains, *sixrd_domain_index);
+}
+
+/*
+ * Get the SIXRD domain from an IPv4 lookup adjacency.
+ * If the IPv4 address is not shared, no lookup is required.
+ * The IPv6 address is used otherwise.
+ */
+static_always_inline sixrd_domain_t *
+ip4_sixrd_get_domain (u32 sdi, ip6_address_t *addr,
+ u32 *sixrd_domain_index, u8 *error)
+{
+ sixrd_main_t *mm = &sixrd_main;
+ sixrd_dpo_t *sd;
+
+ sd = sixrd_dpo_get(sdi);
+ *sixrd_domain_index = sd->sd_domain;
+ if (*sixrd_domain_index != ~0)
+ return pool_elt_at_index(mm->domains, *sixrd_domain_index);
+
+ u32 lbi = ip6_fib_table_fwding_lookup(&ip6_main, 0, addr);
+ const dpo_id_t *dpo = load_balance_get_bucket(lbi, 0);
+ if (PREDICT_TRUE(dpo->dpoi_type == sixrd_dpo_type))
+ {
+ sd = sixrd_dpo_get(dpo->dpoi_index);
+ *sixrd_domain_index = sd->sd_domain;
+ return pool_elt_at_index(mm->domains, *sixrd_domain_index);
+ }
+ *error = SIXRD_ERROR_NO_DOMAIN;
+ return NULL;
+}
diff --git a/src/plugins/sixrd/sixrd_dpo.c b/src/plugins/sixrd/sixrd_dpo.c
new file mode 100644
index 00000000000..88a079350a3
--- /dev/null
+++ b/src/plugins/sixrd/sixrd_dpo.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sixrd_dpo.h"
+#include <vnet/ip/ip.h>
+
+/**
+ * pool of all MPLS Label DPOs
+ */
+sixrd_dpo_t *sixrd_dpo_pool;
+
+/**
+ * The register SIXRD DPO type
+ */
+dpo_type_t sixrd_dpo_type;
+
+static sixrd_dpo_t *
+sixrd_dpo_alloc (void)
+{
+ sixrd_dpo_t *sd;
+
+ pool_get_aligned(sixrd_dpo_pool, sd, CLIB_CACHE_LINE_BYTES);
+ memset(sd, 0, sizeof(*sd));
+
+ return (sd);
+}
+
+static index_t
+sixrd_dpo_get_index (sixrd_dpo_t *sd)
+{
+ return (sd - sixrd_dpo_pool);
+}
+
+void
+sixrd_dpo_create (dpo_proto_t dproto,
+ u32 domain_index,
+ dpo_id_t *dpo)
+{
+ sixrd_dpo_t *sd;
+
+ sd = sixrd_dpo_alloc();
+ sd->sd_domain = domain_index;
+ sd->sd_proto = dproto;
+
+ dpo_set(dpo,
+ sixrd_dpo_type,
+ dproto,
+ sixrd_dpo_get_index(sd));
+}
+
+u8*
+format_sixrd_dpo (u8 *s, va_list *args)
+{
+ index_t index = va_arg (*args, index_t);
+ CLIB_UNUSED(u32 indent) = va_arg (*args, u32);
+ sixrd_dpo_t *sd;
+
+ sd = sixrd_dpo_get(index);
+
+ return (format(s, "sixrd:[%d]:%U domain:%d",
+ index,
+ format_dpo_proto, sd->sd_proto,
+ sd->sd_domain));
+}
+
+
+static void
+sixrd_dpo_lock (dpo_id_t *dpo)
+{
+ sixrd_dpo_t *sd;
+
+ sd = sixrd_dpo_get(dpo->dpoi_index);
+
+ sd->sd_locks++;
+}
+
+static void
+sixrd_dpo_unlock (dpo_id_t *dpo)
+{
+ sixrd_dpo_t *sd;
+
+ sd = sixrd_dpo_get(dpo->dpoi_index);
+
+ sd->sd_locks--;
+
+ if (0 == sd->sd_locks)
+ {
+ pool_put(sixrd_dpo_pool, sd);
+ }
+}
+
+const static dpo_vft_t sd_vft = {
+ .dv_lock = sixrd_dpo_lock,
+ .dv_unlock = sixrd_dpo_unlock,
+ .dv_format = format_sixrd_dpo,
+};
+
+const static char* const sixrd_ip4_nodes[] =
+{
+ "ip4-sixrd",
+ NULL,
+};
+const static char* const sixrd_ip6_nodes[] =
+{
+ "ip6-sixrd",
+ NULL,
+};
+
+const static char* const * const sixrd_nodes[DPO_PROTO_NUM] =
+{
+ [DPO_PROTO_IP4] = sixrd_ip4_nodes,
+ [DPO_PROTO_IP6] = sixrd_ip6_nodes,
+ [DPO_PROTO_MPLS] = NULL,
+};
+
+void
+sixrd_dpo_module_init (void)
+{
+ sixrd_dpo_type = dpo_register_new_type(&sd_vft, sixrd_nodes);
+}
diff --git a/src/plugins/sixrd/sixrd_dpo.h b/src/plugins/sixrd/sixrd_dpo.h
new file mode 100644
index 00000000000..17142288451
--- /dev/null
+++ b/src/plugins/sixrd/sixrd_dpo.h
@@ -0,0 +1,61 @@
+/*
+ * 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 __SIXRD_DPO_H__
+#define __SIXRD_DPO_H__
+
+#include <vnet/vnet.h>
+#include <vnet/dpo/dpo.h>
+
+/**
+ * A representation of a 6RD DPO
+ */
+typedef struct sixrd_dpo_t
+{
+ /**
+ * The dat-plane protocol
+ */
+ dpo_proto_t sd_proto;
+
+ /**
+ * the SIXRD domain index
+ */
+ u32 sd_domain;
+
+ /**
+ * Number of locks/users of the label
+ */
+ u16 sd_locks;
+} sixrd_dpo_t;
+
+extern void sixrd_dpo_create (dpo_proto_t dproto,
+ u32 domain_index,
+ dpo_id_t *dpo);
+
+/*
+ * Encapsulation violation for fast data-path access
+ */
+extern sixrd_dpo_t *sixrd_dpo_pool;
+extern dpo_type_t sixrd_dpo_type;
+
+static inline sixrd_dpo_t *
+sixrd_dpo_get (index_t index)
+{
+ return (pool_elt_at_index(sixrd_dpo_pool, index));
+}
+
+extern void sixrd_dpo_module_init(void);
+
+#endif