diff options
author | Gabriel Ganne <gabriel.ganne@qosmos.com> | 2016-10-25 10:14:23 +0200 |
---|---|---|
committer | Gabriel Ganne <gabriel.ganne@qosmos.com> | 2016-10-25 13:16:05 +0200 |
commit | 55fd743fd66df0005f6f506c59a44d2ecd3aabdf (patch) | |
tree | d54f3f57f9af430cf483ad7590859fc4319ffea5 /flowtable | |
parent | fbaa56c33664c57083e6e96c7086e8afd95077b9 (diff) |
flowtable - initial commit
Adds vpp patches for:
- the flowtable plugin
- a port-mirroring plugin to work with the flowtable
- test scripts
Change-Id: I61d988342921b994cf1a6c0b784fa7e75ca07276
Signed-off-by: Gabriel Ganne <gabriel.ganne@qosmos.com>
Diffstat (limited to 'flowtable')
-rw-r--r-- | flowtable/0001-Add-flowtable-feature.patch | 1544 | ||||
-rw-r--r-- | flowtable/0002-Add-Port-Mirroring-feature.patch | 896 | ||||
-rw-r--r-- | flowtable/0003-test-scripts.patch | 185 | ||||
-rw-r--r-- | flowtable/README.md | 74 |
4 files changed, 2699 insertions, 0 deletions
diff --git a/flowtable/0001-Add-flowtable-feature.patch b/flowtable/0001-Add-flowtable-feature.patch new file mode 100644 index 0000000..bf4db62 --- /dev/null +++ b/flowtable/0001-Add-flowtable-feature.patch @@ -0,0 +1,1544 @@ +From aad927d0274e060e8b3b95fcfd543bad61140987 Mon Sep 17 00:00:00 2001 +From: Gabriel Ganne <gabriel.ganne@qosmos.com> +Date: Sat, 17 Sep 2016 10:05:42 +0200 +Subject: [PATCH 1/3] Add flowtable feature + +flowtable is under /plugins + +Default behavior is to connect transparently to given interface. +Can reroute packets to given node +Can receive additional informations, and, if set offload session + +cli : + * flowable <intf-name> [next-node <name>] [disable] + +Add API : + * to configure flowtable + * to update flow information with any external data + +As advised in the thread below : +https://lists.fd.io/pipermail/vpp-dev/2016-October/002787.html +hashtable is configured to alloc (NUM_BUCKETS * CLIB_CACHE_LINE_BYTES) Bytes +with (flow_count / (BIHASH_KVP_PER_PAGE / 2)) Buckets + +Default flow_count set to 1M + +Author: Christophe Fontaine <christophe.fontaine@qosmos.com> +Author: Gabriel Ganne <gabriel.ganne@qosmos.com> + +Signed-off-by: Gabriel Ganne <gabriel.ganne@qosmos.com> +Change-Id: Ibb04917fbfdff84bfc0ffd4b64434e574ef4ae3d +--- + plugins/Makefile.am | 4 + + plugins/configure.ac | 1 + + plugins/flowtable-plugin/LICENSE | 201 ++++++++ + plugins/flowtable-plugin/Makefile.am | 55 +++ + plugins/flowtable-plugin/configure.ac | 9 + + plugins/flowtable-plugin/flowtable/api.c | 169 +++++++ + plugins/flowtable-plugin/flowtable/flowdata.h | 35 ++ + plugins/flowtable-plugin/flowtable/flowtable.api | 59 +++ + plugins/flowtable-plugin/flowtable/flowtable.c | 86 ++++ + plugins/flowtable-plugin/flowtable/flowtable.h | 173 +++++++ + .../flowtable-plugin/flowtable/flowtable_node.c | 535 +++++++++++++++++++++ + .../flowtable/nodes_registration.c | 80 +++ + 12 files changed, 1407 insertions(+) + create mode 100644 plugins/flowtable-plugin/LICENSE + create mode 100644 plugins/flowtable-plugin/Makefile.am + create mode 100644 plugins/flowtable-plugin/configure.ac + create mode 100644 plugins/flowtable-plugin/flowtable/api.c + create mode 100644 plugins/flowtable-plugin/flowtable/flowdata.h + create mode 100644 plugins/flowtable-plugin/flowtable/flowtable.api + create mode 100644 plugins/flowtable-plugin/flowtable/flowtable.c + create mode 100644 plugins/flowtable-plugin/flowtable/flowtable.h + create mode 100644 plugins/flowtable-plugin/flowtable/flowtable_node.c + create mode 100644 plugins/flowtable-plugin/flowtable/nodes_registration.c + +diff --git a/plugins/Makefile.am b/plugins/Makefile.am +index 5293e6e..d0a2e3c 100644 +--- a/plugins/Makefile.am ++++ b/plugins/Makefile.am +@@ -51,3 +51,7 @@ endif + if ENABLE_lb_PLUGIN + SUBDIRS += lb-plugin + endif ++ ++if ENABLE_flowtable_PLUGIN ++SUBDIRS += flowtable-plugin ++endif +diff --git a/plugins/configure.ac b/plugins/configure.ac +index 6ee064e..cbb180f 100644 +--- a/plugins/configure.ac ++++ b/plugins/configure.ac +@@ -58,6 +58,7 @@ PLUGIN_ENABLED(ioam) + PLUGIN_ENABLED(snat) + PLUGIN_ENABLED(ila) + PLUGIN_ENABLED(lb) ++PLUGIN_ENABLED(flowtable) + + # Disabled plugins, require --enable-XXX-plugin + PLUGIN_DISABLED(vcgn) +diff --git a/plugins/flowtable-plugin/LICENSE b/plugins/flowtable-plugin/LICENSE +new file mode 100644 +index 0000000..8dada3e +--- /dev/null ++++ b/plugins/flowtable-plugin/LICENSE +@@ -0,0 +1,201 @@ ++ Apache License ++ Version 2.0, January 2004 ++ http://www.apache.org/licenses/ ++ ++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION ++ ++ 1. Definitions. ++ ++ "License" shall mean the terms and conditions for use, reproduction, ++ and distribution as defined by Sections 1 through 9 of this document. ++ ++ "Licensor" shall mean the copyright owner or entity authorized by ++ the copyright owner that is granting the License. ++ ++ "Legal Entity" shall mean the union of the acting entity and all ++ other entities that control, are controlled by, or are under common ++ control with that entity. For the purposes of this definition, ++ "control" means (i) the power, direct or indirect, to cause the ++ direction or management of such entity, whether by contract or ++ otherwise, or (ii) ownership of fifty percent (50%) or more of the ++ outstanding shares, or (iii) beneficial ownership of such entity. ++ ++ "You" (or "Your") shall mean an individual or Legal Entity ++ exercising permissions granted by this License. ++ ++ "Source" form shall mean the preferred form for making modifications, ++ including but not limited to software source code, documentation ++ source, and configuration files. ++ ++ "Object" form shall mean any form resulting from mechanical ++ transformation or translation of a Source form, including but ++ not limited to compiled object code, generated documentation, ++ and conversions to other media types. ++ ++ "Work" shall mean the work of authorship, whether in Source or ++ Object form, made available under the License, as indicated by a ++ copyright notice that is included in or attached to the work ++ (an example is provided in the Appendix below). ++ ++ "Derivative Works" shall mean any work, whether in Source or Object ++ form, that is based on (or derived from) the Work and for which the ++ editorial revisions, annotations, elaborations, or other modifications ++ represent, as a whole, an original work of authorship. For the purposes ++ of this License, Derivative Works shall not include works that remain ++ separable from, or merely link (or bind by name) to the interfaces of, ++ the Work and Derivative Works thereof. ++ ++ "Contribution" shall mean any work of authorship, including ++ the original version of the Work and any modifications or additions ++ to that Work or Derivative Works thereof, that is intentionally ++ submitted to Licensor for inclusion in the Work by the copyright owner ++ or by an individual or Legal Entity authorized to submit on behalf of ++ the copyright owner. For the purposes of this definition, "submitted" ++ means any form of electronic, verbal, or written communication sent ++ to the Licensor or its representatives, including but not limited to ++ communication on electronic mailing lists, source code control systems, ++ and issue tracking systems that are managed by, or on behalf of, the ++ Licensor for the purpose of discussing and improving the Work, but ++ excluding communication that is conspicuously marked or otherwise ++ designated in writing by the copyright owner as "Not a Contribution." ++ ++ "Contributor" shall mean Licensor and any individual or Legal Entity ++ on behalf of whom a Contribution has been received by Licensor and ++ subsequently incorporated within the Work. ++ ++ 2. Grant of Copyright License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ copyright license to reproduce, prepare Derivative Works of, ++ publicly display, publicly perform, sublicense, and distribute the ++ Work and such Derivative Works in Source or Object form. ++ ++ 3. Grant of Patent License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ (except as stated in this section) patent license to make, have made, ++ use, offer to sell, sell, import, and otherwise transfer the Work, ++ where such license applies only to those patent claims licensable ++ by such Contributor that are necessarily infringed by their ++ Contribution(s) alone or by combination of their Contribution(s) ++ with the Work to which such Contribution(s) was submitted. If You ++ institute patent litigation against any entity (including a ++ cross-claim or counterclaim in a lawsuit) alleging that the Work ++ or a Contribution incorporated within the Work constitutes direct ++ or contributory patent infringement, then any patent licenses ++ granted to You under this License for that Work shall terminate ++ as of the date such litigation is filed. ++ ++ 4. Redistribution. You may reproduce and distribute copies of the ++ Work or Derivative Works thereof in any medium, with or without ++ modifications, and in Source or Object form, provided that You ++ meet the following conditions: ++ ++ (a) You must give any other recipients of the Work or ++ Derivative Works a copy of this License; and ++ ++ (b) You must cause any modified files to carry prominent notices ++ stating that You changed the files; and ++ ++ (c) You must retain, in the Source form of any Derivative Works ++ that You distribute, all copyright, patent, trademark, and ++ attribution notices from the Source form of the Work, ++ excluding those notices that do not pertain to any part of ++ the Derivative Works; and ++ ++ (d) If the Work includes a "NOTICE" text file as part of its ++ distribution, then any Derivative Works that You distribute must ++ include a readable copy of the attribution notices contained ++ within such NOTICE file, excluding those notices that do not ++ pertain to any part of the Derivative Works, in at least one ++ of the following places: within a NOTICE text file distributed ++ as part of the Derivative Works; within the Source form or ++ documentation, if provided along with the Derivative Works; or, ++ within a display generated by the Derivative Works, if and ++ wherever such third-party notices normally appear. The contents ++ of the NOTICE file are for informational purposes only and ++ do not modify the License. You may add Your own attribution ++ notices within Derivative Works that You distribute, alongside ++ or as an addendum to the NOTICE text from the Work, provided ++ that such additional attribution notices cannot be construed ++ as modifying the License. ++ ++ You may add Your own copyright statement to Your modifications and ++ may provide additional or different license terms and conditions ++ for use, reproduction, or distribution of Your modifications, or ++ for any such Derivative Works as a whole, provided Your use, ++ reproduction, and distribution of the Work otherwise complies with ++ the conditions stated in this License. ++ ++ 5. Submission of Contributions. Unless You explicitly state otherwise, ++ any Contribution intentionally submitted for inclusion in the Work ++ by You to the Licensor shall be under the terms and conditions of ++ this License, without any additional terms or conditions. ++ Notwithstanding the above, nothing herein shall supersede or modify ++ the terms of any separate license agreement you may have executed ++ with Licensor regarding such Contributions. ++ ++ 6. Trademarks. This License does not grant permission to use the trade ++ names, trademarks, service marks, or product names of the Licensor, ++ except as required for reasonable and customary use in describing the ++ origin of the Work and reproducing the content of the NOTICE file. ++ ++ 7. Disclaimer of Warranty. Unless required by applicable law or ++ agreed to in writing, Licensor provides the Work (and each ++ Contributor provides its Contributions) on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or ++ implied, including, without limitation, any warranties or conditions ++ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A ++ PARTICULAR PURPOSE. You are solely responsible for determining the ++ appropriateness of using or redistributing the Work and assume any ++ risks associated with Your exercise of permissions under this License. ++ ++ 8. Limitation of Liability. In no event and under no legal theory, ++ whether in tort (including negligence), contract, or otherwise, ++ unless required by applicable law (such as deliberate and grossly ++ negligent acts) or agreed to in writing, shall any Contributor be ++ liable to You for damages, including any direct, indirect, special, ++ incidental, or consequential damages of any character arising as a ++ result of this License or out of the use or inability to use the ++ Work (including but not limited to damages for loss of goodwill, ++ work stoppage, computer failure or malfunction, or any and all ++ other commercial damages or losses), even if such Contributor ++ has been advised of the possibility of such damages. ++ ++ 9. Accepting Warranty or Additional Liability. While redistributing ++ the Work or Derivative Works thereof, You may choose to offer, ++ and charge a fee for, acceptance of support, warranty, indemnity, ++ or other liability obligations and/or rights consistent with this ++ License. However, in accepting such obligations, You may act only ++ on Your own behalf and on Your sole responsibility, not on behalf ++ of any other Contributor, and only if You agree to indemnify, ++ defend, and hold each Contributor harmless for any liability ++ incurred by, or claims asserted against, such Contributor by reason ++ of your accepting any such warranty or additional liability. ++ ++ END OF TERMS AND CONDITIONS ++ ++ APPENDIX: How to apply the Apache License to your work. ++ ++ To apply the Apache License to your work, attach the following ++ boilerplate notice, with the fields enclosed by brackets "{}" ++ replaced with your own identifying information. (Don't include ++ the brackets!) The text should be enclosed in the appropriate ++ comment syntax for the file format. We also recommend that a ++ file or class name and description of purpose be included on the ++ same "printed page" as the copyright notice for easier ++ identification within third-party archives. ++ ++ Copyright {yyyy} {name of copyright owner} ++ ++ 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. +diff --git a/plugins/flowtable-plugin/Makefile.am b/plugins/flowtable-plugin/Makefile.am +new file mode 100644 +index 0000000..fda9b81 +--- /dev/null ++++ b/plugins/flowtable-plugin/Makefile.am +@@ -0,0 +1,55 @@ ++# 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. ++ ++AUTOMAKE_OPTIONS = foreign subdir-objects ++ ++AM_CFLAGS = -Wall ++AM_LDFLAGS = -module -shared -avoid-version ++ ++#vppapitestpluginsdir = ${libdir}/vpp_api_test_plugins ++vpppluginsdir = ${libdir}/vpp_plugins ++ ++vppplugins_LTLIBRARIES = flowtable_plugin.la ++ ++flowtable_plugin_la_SOURCES = \ ++ flowtable/api.c \ ++ flowtable/flowtable.c \ ++ flowtable/flowtable_node.c \ ++ flowtable/nodes_registration.c ++ ++nobase_include_HEADERS = \ ++ flowtable/flowtable.h \ ++ flowtable/flowdata.h \ ++ flowtable/flowtable.api.h ++ ++BUILT_SOURCES = flowtable/flowtable.api.h flowtable/flowtable.py ++ ++SUFFIXES = .api.h .api ++ ++%.api.h: %.api ++ mkdir -p `dirname $@` ; \ ++ $(CC) $(CPPFLAGS) -E -P -C -x c $^ \ ++ | vppapigen --input - --output $@ --show-name $@ ++ ++%.py: %.api ++ $(info Creating Python binding for $@) ++ $(CC) $(CPPFLAGS) -E -P -C -x c $< \ ++ | vppapigen --input - --python - \ ++ | pyvppapigen.py --input - > $@ ++ ++ ++pyapidir = ${prefix}/vpp_papi_plugins ++pyapi_DATA = flowtable/flowtable.py ++ ++install-data-hook: ++ @(cd $(vpppluginsdir) && $(RM) $(vppplugins_LTLIBRARIES)) +diff --git a/plugins/flowtable-plugin/configure.ac b/plugins/flowtable-plugin/configure.ac +new file mode 100644 +index 0000000..684569c +--- /dev/null ++++ b/plugins/flowtable-plugin/configure.ac +@@ -0,0 +1,9 @@ ++AC_INIT(flowtable_plugin, 1.0) ++LT_INIT ++AM_INIT_AUTOMAKE ++AM_SILENT_RULES([yes]) ++AC_PREFIX_DEFAULT([/usr]) ++ ++AC_PROG_CC ++ ++AC_OUTPUT([Makefile]) +diff --git a/plugins/flowtable-plugin/flowtable/api.c b/plugins/flowtable-plugin/flowtable/api.c +new file mode 100644 +index 0000000..cad2a8b +--- /dev/null ++++ b/plugins/flowtable-plugin/flowtable/api.c +@@ -0,0 +1,169 @@ ++/* ++ * 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 <flowtable/flowtable.h> ++ ++#include <vppinfra/byte_order.h> ++#include <vlibapi/api.h> ++#include <vlibmemory/api.h> ++#include <vlibsocket/api.h> ++ ++#define vl_msg_id(n,h) n, ++typedef enum { ++#include <flowtable/flowtable.api.h> ++ /* We'll want to know how many messages IDs we need... */ ++ VL_MSG_FIRST_AVAILABLE, ++} vl_msg_id_t; ++#undef vl_msg_id ++ ++ ++/* define message structures */ ++#define vl_typedefs ++#include <flowtable/flowtable.api.h> ++#undef vl_typedefs ++ ++/* define generated endian-swappers */ ++#define vl_endianfun ++#include <flowtable/flowtable.api.h> ++#undef vl_endianfun ++ ++#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) ++ ++/* Get the API version number */ ++#define vl_api_version(n,v) static u32 api_version=(v); ++#include <flowtable/flowtable.api.h> ++#undef vl_api_version ++ ++/* Macro to finish up custom dump fns */ ++#define FINISH \ ++ vec_add1 (s, 0); \ ++ vl_print (handle, (char *)s); \ ++ vec_free (s); \ ++ return handle; ++ ++/* ++ * 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)+ftm->msg_id_base); \ ++ rmp->context = mp->context; \ ++ rmp->retval = ntohl(rv); \ ++ \ ++ vl_msg_api_send_shmem (q, (u8 *)&rmp); \ ++} while(0); ++ ++static void ++vl_api_flowtable_conf_t_handler (vl_api_flowtable_conf_t * mp) ++{ ++ flowtable_main_t *ftm = &flowtable_main; ++ vl_api_flowtable_conf_reply_t * rmp; ++ int rv; ++ ++ if (mp->next_node_index == 0xff) ++ ftm->next_node_index = FT_NEXT_ETHERNET_INPUT; ++ else ++ ftm->next_node_index = mp->next_node_index; ++ ++ rv = flowtable_enable_disable(ftm, mp->sw_if_index, ! mp->enable_disable); ++ ++ REPLY_MACRO (VL_API_FLOWTABLE_CONF_REPLY); ++} ++ ++static void * ++vl_api_flowtable_conf_t_print (vl_api_flowtable_conf_t *mp, void * handle) ++{ ++ u8 * s; ++ s = format (0, "SCRIPT: flowtable_conf "); ++ ++ s = format (s, "%u ", mp->sw_if_index); ++ s = format (s, "%u ", mp->enable_disable); ++ ++ FINISH; ++} ++ ++static void ++vl_api_flowtable_update_t_handler (vl_api_flowtable_update_t * mp) ++{ ++ int rv; ++ flowtable_main_t *ftm = &flowtable_main; ++ vl_api_flowtable_update_reply_t * rmp; ++ ++ rv = flowtable_update(mp->is_ip4, mp->ip_src, mp->ip_dst, ++ mp->ip_upper_proto, mp->port_src, mp->port_dst, ++ mp->lifetime, mp->offloaded, mp->infos); ++ ++ REPLY_MACRO (VL_API_FLOWTABLE_UPDATE_REPLY); ++} ++ ++static void * ++vl_api_flowtable_update_t_print (vl_api_flowtable_update_t *mp, void * handle) ++{ ++ u8 * s; ++ s = format (0, "SCRIPT: flowtable_update "); ++ ++ s = format (s, "%u ", mp->is_ip4); ++ s = format (s, "%s ", mp->ip_src); ++ s = format (s, "%s ", mp->ip_dst); ++ s = format (s, "%u ", mp->ip_upper_proto); ++ s = format (s, "%u ", mp->port_src); ++ s = format (s, "%u ", mp->port_dst); ++ s = format (s, "%u ", mp->lifetime); ++ s = format (s, "%u ", mp->offloaded); ++ s = format (s, "%s ", mp->infos); ++ ++ FINISH; ++} ++ ++/* List of message types that this plugin understands */ ++#define foreach_flowtable_plugin_api_msg \ ++ _(FLOWTABLE_CONF, flowtable_conf) \ ++ _(FLOWTABLE_UPDATE, flowtable_update) \ ++ ++ ++static clib_error_t * flowtable_api_init (vlib_main_t * vm) ++{ ++ flowtable_main_t *ftm = &flowtable_main; ++ u8 *name = format (0, "flowtable_%08x%c", api_version, 0); ++ ftm->msg_id_base = vl_msg_api_get_msg_ids ++ ((char *) name, VL_MSG_FIRST_AVAILABLE); ++ ++#define _(N,n) \ ++ vl_msg_api_set_handlers((VL_API_##N + ftm->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_flowtable_plugin_api_msg; ++#undef _ ++ ++ return 0; ++} ++ ++VLIB_INIT_FUNCTION (flowtable_api_init); ++ +diff --git a/plugins/flowtable-plugin/flowtable/flowdata.h b/plugins/flowtable-plugin/flowtable/flowdata.h +new file mode 100644 +index 0000000..9624039 +--- /dev/null ++++ b/plugins/flowtable-plugin/flowtable/flowdata.h +@@ -0,0 +1,35 @@ ++/*--------------------------------------------------------------------------- ++ * Copyright (c) 2016 Qosmos 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 __flowdata_h__ ++#define __flowdata_h__ ++ ++#include <vnet/vnet.h> ++ ++/* the following union will be copied to vlib->opaque ++ * it MUST be less or equal CLIB_CACHE_LINE_BYTES */ ++typedef union { ++ struct { ++ u32 sw_if_index_current; ++ u8 offloaded; ++ ++ u8 opaque[27]; ++ } data; ++ ++ u32 flow_data[8]; /* 32 Bytes == sizeof vlib_buffer_t's opaque field */ ++} flow_data_t; ++ ++#endif /* __flowdata_h__ */ +diff --git a/plugins/flowtable-plugin/flowtable/flowtable.api b/plugins/flowtable-plugin/flowtable/flowtable.api +new file mode 100644 +index 0000000..d0797de +--- /dev/null ++++ b/plugins/flowtable-plugin/flowtable/flowtable.api +@@ -0,0 +1,59 @@ ++/** \brief hook flowtable configuration ++ @param client_index - opaque cookie to identify the sender ++ @param context - sender context, to match reply w/ request ++ @param sw_if_index - interface index ++ @param next_node_index - (optional) redirect packets to given node, or 0xff for default ++ @param enable_disable - 0/1 set to disable ++*/ ++define flowtable_conf ++{ ++ u32 client_index; ++ u32 context; ++ u8 sw_if_index; ++ u8 next_node_index; ++ u8 enable_disable; ++}; ++ ++define flowtable_conf_reply ++{ ++ u32 context; ++ i32 retval; ++}; ++ ++/** \brief send additional informations to the flowtable ++ @param client_index - opaque cookie to identify the sender ++ @param context - sender context, to match reply w/ request ++ @param is_ip4 - 0/1 ip version ++ @param ip_src - source ip address ++ @param ip_dst - destination ip address ++ @param ip_upper_proto - tcp or udp ++ @param port_src - source port ++ @param port_dst - destination port ++ @param lifetime - time to live (~0 for left untouched) ++ @param offloaded - offloading status ++ @param infos - external infos (opaque field) ++*/ ++define flowtable_update ++{ ++ u32 client_index; ++ u32 context; ++ ++ /* used to compute flow key */ ++ u8 is_ip4; ++ u8 ip_src[16]; ++ u8 ip_dst[16]; ++ u8 ip_upper_proto; ++ u16 port_src; ++ u16 port_dst; ++ ++ /* additional flow informations */ ++ u16 lifetime; ++ u8 offloaded; ++ u8 infos[27]; ++}; ++ ++define flowtable_update_reply ++{ ++ u32 context; ++ i32 retval; ++}; +diff --git a/plugins/flowtable-plugin/flowtable/flowtable.c b/plugins/flowtable-plugin/flowtable/flowtable.c +new file mode 100644 +index 0000000..ce769a3 +--- /dev/null ++++ b/plugins/flowtable-plugin/flowtable/flowtable.c +@@ -0,0 +1,86 @@ ++/* ++ * Copyright (c) 2016 Qosmos 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 "flowtable.h" ++#include <vnet/plugin/plugin.h> ++ ++flowtable_main_t flowtable_main; ++ ++int ++flowtable_enable_disable(flowtable_main_t * fm, ++ u32 sw_if_index, int enable_disable) ++{ ++ u32 node_index = enable_disable ? flowtable_node.index : ~0; ++ ++ return vnet_hw_interface_rx_redirect_to_node(fm->vnet_main, ++ sw_if_index, node_index); ++} ++ ++static clib_error_t * ++flowtable_enable_disable_command_fn(vlib_main_t * vm, ++ unformat_input_t * input, vlib_cli_command_t * cmd) ++{ ++ flowtable_main_t * fm = &flowtable_main; ++ u32 sw_if_index = ~0; ++ int enable_disable = 1; ++ u32 next_node_index = ~0; ++ ++ int rv; ++ ++ while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) ++ { ++ if (unformat(input, "disable")) ++ enable_disable = 0; ++ else if (unformat(input, "next-node %U", unformat_vlib_node, ++ fm->vnet_main, &next_node_index)) ++ ; ++ else if (unformat(input, "%U", unformat_vnet_sw_interface, ++ fm->vnet_main, &sw_if_index)) ++ ; ++ else ++ break; ++ } ++ ++ if (sw_if_index == ~0) ++ return clib_error_return(0, "No Interface specified"); ++ ++ /* by default, leave the packet follow its course */ ++ if (next_node_index != ~0) ++ fm->next_node_index = next_node_index; ++ else ++ fm->next_node_index = FT_NEXT_ETHERNET_INPUT; ++ ++ rv = flowtable_enable_disable(fm, sw_if_index, enable_disable); ++ switch (rv) { ++ case 0: ++ break; ++ case VNET_API_ERROR_INVALID_SW_IF_INDEX: ++ return clib_error_return(0, "Invalid interface"); ++ case VNET_API_ERROR_UNIMPLEMENTED: ++ return clib_error_return(0, ++ "Device driver doesn't support redirection"); ++ default: ++ return clib_error_return(0, "flowtable_enable_disable returned %d", ++ rv); ++ } ++ ++ return 0; ++} ++ ++VLIB_CLI_COMMAND(flowtable_interface_enable_disable_command) = { ++ .path = "flowtable", ++ .short_help = "flowtable <interface> [next-node <name>] [disable]", ++ .function = flowtable_enable_disable_command_fn, ++}; +diff --git a/plugins/flowtable-plugin/flowtable/flowtable.h b/plugins/flowtable-plugin/flowtable/flowtable.h +new file mode 100644 +index 0000000..faa7410 +--- /dev/null ++++ b/plugins/flowtable-plugin/flowtable/flowtable.h +@@ -0,0 +1,173 @@ ++/*--------------------------------------------------------------------------- ++ * Copyright (c) 2016 Qosmos 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 __flowtable_h__ ++#define __flowtable_h__ ++ ++#include <stdbool.h> ++#include <vppinfra/error.h> ++#include <vnet/vnet.h> ++#include <vnet/ip/ip.h> ++#include <vppinfra/bihash_8_8.h> ++#include <vppinfra/dlist.h> ++#include <vppinfra/pool.h> ++#include <vppinfra/vec.h> ++ ++#include "flowdata.h" ++ ++#define foreach_flowtable_error \ ++ _(HIT, "packets with an existing flow") \ ++ _(THRU, "packets gone through") \ ++ _(CREATED, "packets which created a new flow") \ ++ _(OFFLOADED, "packets which have been offloaded") \ ++ _(ALLOC_ERROR, "failed to allocate flow") \ ++ _(TIMER_EXPIRE, "flows that have expired") \ ++ _(COLLISION, "hashtable collisions") ++ ++typedef enum { ++#define _(sym, str) FLOWTABLE_ERROR_##sym, ++ foreach_flowtable_error ++#undef _ ++ FLOWTABLE_N_ERROR ++} flowtable_error_t; ++ ++ ++typedef enum { ++ FT_NEXT_DROP, ++ FT_NEXT_ETHERNET_INPUT, ++ FT_NEXT_N_NEXT ++} flowtable_next_t; ++ ++/* signatures */ ++struct ip6_sig { ++ ip6_address_t src, dst; ++ u8 proto; ++ u16 port_src, port_dst; ++} __attribute__ ((packed)); ++struct ip4_sig { ++ ip4_address_t src, dst; ++ u8 proto; ++ u16 port_src, port_dst; ++} __attribute__ ((packed)); ++ ++typedef union { ++ struct ip6_sig ip6; ++ struct ip4_sig ip4; ++ u8 data[0]; /* gcc will take the max */ ++} signature; ++ ++/* dlist helpers */ ++#define dlist_is_head(node) ((node)->value == (u32) ~0) ++#define dlist_is_empty(pool, head_index) \ ++({ \ ++ dlist_elt_t *head = pool_elt_at_index ((pool), (head_index)); \ ++ (head->next == (u32) ~0 || head->next == (head_index)); \ ++}) ++ ++/* flow helpers */ ++#define flow_is_offloaded(f) ((f)->infos.data.offloaded) ++ ++typedef struct { ++ u32 straight; ++ u32 reverse; ++} flow_stats_t; ++ ++typedef struct flow_entry ++{ ++ /* flow signature */ ++ u32 sig_len; ++ signature sig; ++ u64 sig_hash; /* used to delete hashtable entries */ ++ ++ /* hashtable */ ++ u32 ht_line_index; /* index of the list head of the line in the hashtable */ ++ u32 ht_index; /* index in the hashtable line pool */ ++ ++ /* stats */ ++ flow_stats_t stats; ++ ++ /* timers */ ++ u32 expire; /* in seconds */ ++ u32 lifetime; /* in seconds */ ++ u32 timer_index; /* index in the timer pool */ ++ ++ /* the following union will be copied to vlib->opaque ++ * it MUST be less or equal CLIB_CACHE_LINE_BYTES */ ++ flow_data_t infos; ++} flow_entry_t; ++ ++/* TODO improve timer duration (tcp sm state) */ ++ ++/* Timers (in seconds) */ ++#define TIMER_DEFAULT_LIFETIME (60) ++#define TIMER_MAX_LIFETIME (300) ++ ++/* Default max number of flows to expire during one run. ++ * 256 is the max number of packets in a vector, so this is a minimum ++ * if all packets create a flow. */ ++#define TIMER_MAX_EXPIRE (256) ++ ++typedef struct { ++ /* flow entry pool */ ++ flow_entry_t * flows; ++ ++ /* hashtable */ ++ BVT(clib_bihash) flows_ht; ++ dlist_elt_t * ht_lines; ++ u64 flows_cpt; ++ ++ /* flowtable node index */ ++ u32 flowtable_index; ++ ++ /* timers */ ++ dlist_elt_t * timers; ++ u32 * timer_wheel; ++ u32 time_index; ++ ++ /* convenience */ ++ vlib_main_t * vlib_main; ++ vnet_main_t * vnet_main; ++ ++ /* next-node of flowtable node, NOT pm node id */ ++ u32 next_node_index; ++ ++ /* API dynamically registered base ID. */ ++ u16 msg_id_base; ++} flowtable_main_t; ++ ++extern flowtable_main_t flowtable_main; ++ ++/* ++ * As advised in the thread below : ++ * https://lists.fd.io/pipermail/vpp-dev/2016-October/002787.html ++ * hashtable is configured to alloc (NUM_BUCKETS * CLIB_CACHE_LINE_BYTES) Bytes ++ * with (flow_count / (BIHASH_KVP_PER_PAGE / 2)) Buckets ++ */ ++#define FM_POOL_COUNT_LOG2 20 ++#define FM_POOL_COUNT (1 << FM_POOL_COUNT_LOG2) ++#define FM_NUM_BUCKETS (1 << (FM_POOL_COUNT_LOG2 - (BIHASH_KVP_PER_PAGE / 2))) ++#define FM_MEMORY_SIZE (FM_NUM_BUCKETS * CLIB_CACHE_LINE_BYTES) ++ ++ ++extern vlib_node_registration_t flowtable_node; ++ ++/* API functions */ ++int flowtable_enable_disable(flowtable_main_t * fm, u32 sw_if_index, int enable_disable); ++ ++int flowtable_update(u8 is_ip4, u8 ip_src[16], u8 ip_dst[16], u8 ip_upper_proto, ++ u16 port_src, u16 port_dst, u16 lifetime, u8 offloaded, u8 infos[27]); ++ ++#endif /* __flowtable_h__ */ +diff --git a/plugins/flowtable-plugin/flowtable/flowtable_node.c b/plugins/flowtable-plugin/flowtable/flowtable_node.c +new file mode 100644 +index 0000000..3a652df +--- /dev/null ++++ b/plugins/flowtable-plugin/flowtable/flowtable_node.c +@@ -0,0 +1,535 @@ ++#include <vppinfra/dlist.h> ++#include <vppinfra/types.h> ++#include <vppinfra/vec.h> ++#include <vnet/ip/ip4_packet.h> ++ ++#include "flowtable.h" ++ ++vlib_node_registration_t flowtable_node; ++ ++ ++typedef struct { ++ u32 sw_if_index; ++ u32 next_index; ++ u32 offloaded; ++} flow_trace_t; ++ ++static u8 * ++format_get_flowinfo(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 *); ++ flow_trace_t * t = va_arg(*args, flow_trace_t *); ++ ++ s = format(s, "FlowInfo - sw_if_index %d, next_index = %d, offload = %d", ++ t->sw_if_index, t->next_index, t->offloaded); ++ return s; ++} ++ ++/* TODO find a better hash function */ ++static inline u64 ++hash_signature(u8 is_ip4, signature const * sig) ++{ ++ if (is_ip4) { ++ return sig->ip4.src.as_u32 ^ sig->ip4.dst.as_u32 ^ sig->ip4.proto ++ ^ sig->ip4.port_src ^ sig->ip4.port_dst; ++ } else { ++ return sig->ip6.dst.as_u64[0] ^ sig->ip6.dst.as_u64[1] ++ ^ sig->ip6.src.as_u64[0] ^ sig->ip6.src.as_u64[1] ++ ^ sig->ip4.port_src ^ sig->ip4.port_dst; ++ } ++} ++ ++static inline u64 ++parse_ip4_packet(ip4_header_t * ip0, uword * is_reverse, struct ip4_sig *sig) ++{ ++ sig->proto = ip0->protocol; ++ ++ if (ip4_address_compare(&ip0->src_address, &ip0->dst_address) < 0) { ++ sig->src = ip0->src_address; ++ sig->dst = ip0->dst_address; ++ *is_reverse = 1; ++ } else { ++ sig->src = ip0->dst_address; ++ sig->dst = ip0->src_address; ++ } ++ ++ if (sig->proto == IP_PROTOCOL_UDP || sig->proto == IP_PROTOCOL_TCP) { ++ /* tcp and udp ports have the same offset */ ++ udp_header_t * udp0 = (udp_header_t *) ip4_next_header(ip0); ++ if (is_reverse == 0) { ++ sig->port_src = udp0->src_port; ++ sig->port_dst = udp0->dst_port; ++ } else { ++ sig->port_src = udp0->dst_port; ++ sig->port_dst = udp0->src_port; ++ } ++ } else { ++ sig->port_src = 0; ++ sig->port_dst = 0; ++ } ++ ++ return hash_signature(1 /* is_ip4 */, (signature *) sig); ++} ++ ++static inline u64 ++parse_ip6_packet(ip6_header_t * ip60, uword * is_reverse, struct ip6_sig * sig) ++{ ++ sig->proto = ip60->protocol; ++ ++ if (ip6_address_compare(&ip60->src_address, &ip60->dst_address) < 0) { ++ sig->src = ip60->src_address; ++ sig->dst = ip60->dst_address; ++ *is_reverse = 1; ++ } else { ++ sig->src = ip60->dst_address; ++ sig->dst = ip60->src_address; ++ } ++ ++ if (sig->proto == IP_PROTOCOL_UDP || sig->proto == IP_PROTOCOL_TCP) { ++ /* tcp and udp ports have the same offset */ ++ udp_header_t *udp0 = (udp_header_t *) ip6_next_header(ip60); ++ if (is_reverse == 0) { ++ sig->port_src = udp0->src_port; ++ sig->port_dst = udp0->dst_port; ++ } else { ++ sig->port_src = udp0->dst_port; ++ sig->port_dst = udp0->src_port; ++ } ++ } else { ++ sig->port_src = 0; ++ sig->port_dst = 0; ++ } ++ ++ return hash_signature(0 /* is_ip4 */, (signature *) sig); ++} ++ ++int ++flowtable_update(u8 is_ip4, u8 ip_src[16], u8 ip_dst[16], u8 ip_upper_proto, ++ u16 port_src, u16 port_dst, u16 lifetime, u8 offloaded, u8 infos[27]) ++{ ++ flowtable_main_t * fm = &flowtable_main; ++ u32 sig_len; ++ signature sig; ++ flow_entry_t *flow; ++ BVT(clib_bihash_kv) kv; ++ ++ if (is_ip4) { ++ sig_len = sizeof(sig.ip4); ++ clib_memcpy (&sig.ip4.src, ip_src, 4); ++ clib_memcpy (&sig.ip4.dst, ip_dst, 4); ++ sig.ip4.proto = ip_upper_proto; ++ sig.ip4.port_src = port_src; ++ sig.ip4.port_dst = port_dst; ++ ++ } else { ++ sig_len = sizeof(sig.ip6); ++ clib_memcpy (&sig.ip6.src, ip_src, 16); ++ clib_memcpy (&sig.ip6.dst, ip_dst, 16); ++ sig.ip6.proto = ip_upper_proto; ++ sig.ip6.port_src = port_src; ++ sig.ip6.port_dst = port_dst; ++ } ++ ++ flow = NULL; ++ kv.key = hash_signature(is_ip4, &sig); ++ if (PREDICT_FALSE(BV(clib_bihash_search) (&fm->flows_ht, &kv, &kv))) { ++ return -1; /* flow not found */ ++ } else { ++ dlist_elt_t * ht_line; ++ dlist_elt_t * e; ++ u32 ht_line_head_index; ++ ++ flow = NULL; ++ ht_line_head_index = (u32) kv.value; ++ if (dlist_is_empty(fm->ht_lines, ht_line_head_index)) ++ return -1; /* flow not found */ ++ ++ ht_line = pool_elt_at_index(fm->ht_lines, ht_line_head_index); ++ e = pool_elt_at_index(fm->ht_lines, ht_line->next); ++ while (!dlist_is_head(e)) { ++ flow = pool_elt_at_index(fm->flows, e->value); ++ if (PREDICT_TRUE(memcmp(&flow->sig, &sig, sig_len) == 0)) { ++ break; ++ } ++ e = pool_elt_at_index(fm->ht_lines, e->next); ++ } ++ } ++ ++ if (PREDICT_FALSE(flow == NULL)) ++ return -1; /* flow not found */ ++ ++ if (lifetime != (u16) ~0) { ++ ASSERT(lifetime < TIMER_MAX_LIFETIME); ++ flow->lifetime = lifetime; ++ } ++ flow->infos.data.offloaded = offloaded; ++ clib_memcpy(flow->infos.data.opaque, infos, sizeof(flow->infos.data.opaque)); ++ ++ return 0; ++} ++ ++static inline void ++flowtable_entry_remove(flowtable_main_t *fm, flow_entry_t * f) ++{ ++ /* remove node from hashtable */ ++ clib_dlist_remove(fm->ht_lines, f->ht_index); ++ pool_put_index(fm->ht_lines, f->ht_index); ++ ++ /* if list is empty, free it and delete hashtable entry */ ++ if (dlist_is_empty(fm->ht_lines, f->ht_line_index)) { ++ pool_put_index(fm->ht_lines, f->ht_line_index); ++ ++ BVT(clib_bihash_kv) kv = {.key = f->sig_hash}; ++ BV(clib_bihash_add_del) (&fm->flows_ht, &kv, 0 /* is_add */); ++ } ++ ++ /* release flow to pool */ ++ pool_put(fm->flows, f); ++ ASSERT(fm->flows_cpt > 1); ++ fm->flows_cpt--; ++} ++ ++static u64 ++flowtable_timer_expire(flowtable_main_t *fm, u32 now) ++{ ++ u64 expire_cpt; ++ flow_entry_t * f; ++ u32 * time_slot_curr_index; ++ dlist_elt_t * time_slot_curr; ++ dlist_elt_t * e; ++ ++ time_slot_curr_index = vec_elt_at_index(fm->timer_wheel, fm->time_index); ++ ++ if (PREDICT_FALSE(dlist_is_empty(fm->timers, *time_slot_curr_index))) ++ return 0; ++ ++ expire_cpt = 0; ++ time_slot_curr = pool_elt_at_index(fm->timers, *time_slot_curr_index); ++ ++ e = pool_elt_at_index(fm->timers, time_slot_curr->next); ++ while (!dlist_is_head(e) && expire_cpt < TIMER_MAX_EXPIRE) { ++ u32 next_index; ++ f = pool_elt_at_index(fm->flows, e->value); ++ ++ ASSERT(f->timer_index == (e - fm->timers)); ++ ASSERT(f->expire >= now); ++ flowtable_entry_remove(fm, f); ++ ++ next_index = e->next; ++ clib_dlist_remove(fm->timers, e - fm->timers); ++ pool_put(fm->timers, e); ++ ++ expire_cpt++; ++ e = pool_elt_at_index(fm->timers, next_index); ++ } ++ ++ return expire_cpt; ++} ++ ++static inline void ++timer_wheel_insert_flow(flowtable_main_t *fm, flow_entry_t *f) ++{ ++ u32 timer_slot_head_index; ++ ++ timer_slot_head_index = (fm->time_index + f->lifetime) % TIMER_MAX_LIFETIME; ++ clib_dlist_addtail(fm->timers, timer_slot_head_index, f->timer_index); ++} ++ ++static void ++timer_wheel_resched_flow(flowtable_main_t *fm, flow_entry_t *f, u64 now) ++{ ++ clib_dlist_remove(fm->timers, f->timer_index); ++ f->expire = now + f->lifetime; ++ timer_wheel_insert_flow(fm, f); ++ ++ return; ++} ++ ++/* TODO: replace with a more appropriate hashtable */ ++static inline flow_entry_t * ++flowtable_entry_lookup_create(flowtable_main_t *fm, BVT(clib_bihash_kv) *kv, ++ signature const *sig, u32 const sig_len, u32 const now, int *created) ++{ ++ flow_entry_t * f; ++ dlist_elt_t * ht_line; ++ dlist_elt_t * timer_entry; ++ dlist_elt_t * flow_entry; ++ u32 ht_line_head_index; ++ ++ ht_line = NULL; ++ ++ /* get hashtable line */ ++ if (PREDICT_TRUE(BV(clib_bihash_search) (&fm->flows_ht, kv, kv) == 0)) { ++ dlist_elt_t * e; ++ ht_line_head_index = (u32) kv->value; ++ ht_line = pool_elt_at_index(fm->ht_lines, ht_line_head_index); ++ ++ /* The list CANNOT be a singleton */ ++ e = pool_elt_at_index(fm->ht_lines, ht_line->next); ++ while (!dlist_is_head(e)) { ++ f = pool_elt_at_index(fm->flows, e->value); ++ if (PREDICT_TRUE(memcmp(&f->sig, &sig, sig_len) == 0)) { ++ return f; ++ } ++ e = pool_elt_at_index(fm->ht_lines, e->next); ++ } ++ ++ vlib_node_increment_counter(fm->vlib_main, flowtable_node.index, ++ FLOWTABLE_ERROR_COLLISION , 1); ++ } else { ++ /* create a new line */ ++ pool_get(fm->ht_lines, ht_line); ++ ++ ht_line_head_index = ht_line - fm->ht_lines; ++ clib_dlist_init (fm->ht_lines, ht_line_head_index); ++ kv->value = ht_line_head_index; ++ BV(clib_bihash_add_del) (&fm->flows_ht, kv, 1 /* is_add */); ++ } ++ ++ /* assume the flowtable has been configured correctly */ ++ ASSERT(fm->flows_cpt <= FM_POOL_COUNT); ++ if (PREDICT_FALSE(fm->flows_cpt > FM_POOL_COUNT)) { ++ return NULL; ++ } ++ ++ /* create new flow */ ++ *created = 1; ++ pool_get_aligned(fm->flows, f, CLIB_CACHE_LINE_BYTES); ++ fm->flows_cpt++; ++ ++ memset(f, 0, sizeof(*f)); ++ f->sig_len = sig_len; ++ clib_memcpy(&f->sig, &sig, sig_len); ++ f->sig_hash = kv->key; ++ f->lifetime = TIMER_DEFAULT_LIFETIME; ++ f->expire = now + TIMER_DEFAULT_LIFETIME; ++ ++ /* insert in timer list */ ++ pool_get(fm->timers, timer_entry); ++ timer_entry->value = f - fm->flows; /* index within the flow pool */ ++ f->timer_index = timer_entry - fm->timers; /* index within the timer pool */ ++ timer_wheel_insert_flow(fm, f); ++ ++ /* insert in ht line */ ++ pool_get(fm->ht_lines, flow_entry); ++ f->ht_index = flow_entry - fm->ht_lines; /* index within the ht line pool */ ++ flow_entry->value = f - fm->flows; /* index within the flow pool */ ++ f->ht_line_index = ht_line_head_index; ++ clib_dlist_addhead(fm->ht_lines, ht_line_head_index, f->ht_index); ++ ++ return f; ++} ++ ++static inline void ++timer_wheel_index_update(flowtable_main_t * fm, u32 now) ++{ ++ u32 new_index = now % TIMER_MAX_LIFETIME; ++ ++ if (PREDICT_FALSE(fm->time_index == ~0)) { ++ fm->time_index = new_index; ++ return; ++ } ++ ++ if (new_index != fm->time_index) { ++ /* reschedule all remaining flows on current time index ++ * at the begining of the next one */ ++ ++ u32 * curr_slot_index = vec_elt_at_index(fm->timer_wheel, fm->time_index); ++ dlist_elt_t * curr_head = pool_elt_at_index(fm->timers, *curr_slot_index); ++ ++ u32 * next_slot_index = vec_elt_at_index(fm->timer_wheel, new_index); ++ dlist_elt_t * next_head = pool_elt_at_index(fm->timers, *next_slot_index); ++ ++ if (PREDICT_FALSE(dlist_is_empty(fm->timers, *curr_slot_index))) { ++ fm->time_index = new_index; ++ return; ++ } ++ ++ dlist_elt_t * curr_prev = pool_elt_at_index (fm->timers, curr_head->prev); ++ dlist_elt_t * curr_next = pool_elt_at_index (fm->timers, curr_head->next); ++ ++ /* insert timer list of current time slot at the begining of the next slot */ ++ if (PREDICT_FALSE(dlist_is_empty(fm->timers, *next_slot_index))) { ++ next_head->next = curr_head->next; ++ next_head->prev = curr_head->prev; ++ curr_prev->next = *next_slot_index; ++ curr_next->prev = *next_slot_index; ++ } else { ++ dlist_elt_t * next_next = pool_elt_at_index (fm->timers, next_head->next); ++ curr_prev->next = next_head->next; ++ next_head->next = curr_head->next; ++ next_next->prev = curr_head->prev; ++ curr_next->prev = *next_slot_index; ++ } ++ ++ /* reset current time slot as an empty list */ ++ memset (curr_head, 0xff, sizeof (*curr_head)); ++ ++ fm->time_index = new_index; ++ } ++} ++ ++static uword ++flowtable_process(vlib_main_t * vm, ++ vlib_node_runtime_t * node, vlib_frame_t * frame) ++{ ++ u32 n_left_from, * from, next_index, * to_next; ++ flowtable_main_t * fm = &flowtable_main; ++ ++#define _(sym, str) u32 CPT_##sym = 0; ++ foreach_flowtable_error ++#undef _ ++ ++ from = vlib_frame_vector_args(frame); ++ n_left_from = frame->n_vectors; ++ next_index = node->cached_next_index; ++ ++ u32 current_time = (u32) ((u64) fm->vlib_main->cpu_time_last_node_dispatch / fm->vlib_main->clib_time.clocks_per_second); ++ timer_wheel_index_update(fm, current_time); ++ ++ while (n_left_from > 0) ++ { ++ u32 pi0; ++ u32 next0; ++ u32 n_left_to_next; ++ ++ vlib_buffer_t * b0; ++ 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) ++ { ++ int created = 0; ++ flow_entry_t * flow = NULL; ++ uword is_reverse = 0; ++ u64 sig_hash; ++ BVT(clib_bihash_kv) kv; ++ ++ u16 type; ++ pi0 = to_next[0] = from[0]; ++ b0 = vlib_get_buffer(vm, pi0); ++ u32 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; ++ ++ /* Get Flow & copy metadatas into opaque1 or opaque2 */ ++ ethernet_header_t * eth0 = (void *) (b0->data + b0->current_data); ++ type = clib_net_to_host_u16(eth0->type); ++ if (PREDICT_TRUE ++ (type == ETHERNET_TYPE_IP6 || type == ETHERNET_TYPE_IP4)) ++ { ++ u32 sig_len; ++ signature sig; ++ vlib_buffer_advance(b0, sizeof(ethernet_header_t)); ++ ++ /* compute 5 tuple key so that 2 half connections ++ * get into the same flow */ ++ if (type == ETHERNET_TYPE_IP4) ++ { ++ sig_len = sizeof(struct ip4_sig); ++ sig_hash = parse_ip4_packet(vlib_buffer_get_current(b0), ++ &is_reverse, (struct ip4_sig *) &sig); ++ } else { ++ sig_len = sizeof(struct ip6_sig); ++ sig_hash = parse_ip6_packet(vlib_buffer_get_current(b0), ++ &is_reverse, (struct ip6_sig *) &sig); ++ } ++ ++ /* lookup flow */ ++ kv.key = sig_hash; ++ flow = flowtable_entry_lookup_create(fm, &kv, &sig, sig_len, ++ current_time, &created); ++ ++ if (PREDICT_FALSE(flow == NULL)) { ++ CPT_ALLOC_ERROR++; ++ next0 = FT_NEXT_ETHERNET_INPUT; ++ goto get_flowinfo_error; ++ } ++ ++ if (created) { ++ CPT_CREATED++; ++ } else { ++ timer_wheel_resched_flow(fm, flow, current_time); ++ CPT_HIT++; ++ } ++ ++ if (is_reverse) ++ flow->stats.reverse++; ++ else ++ flow->stats.straight++; ++ ++ ++ if (flow_is_offloaded(flow)) { ++ next0 = FT_NEXT_ETHERNET_INPUT; ++ clib_memcpy(b0->opaque, &flow->infos, sizeof(flow->infos)); ++ vnet_buffer (b0)->sw_if_index[VLIB_RX] = flow->infos.data.sw_if_index_current; ++ ++ CPT_OFFLOADED++; ++ } else { ++ flow->infos.data.sw_if_index_current = sw_if_index0; ++ clib_memcpy(b0->opaque, &flow->infos, sizeof(flow->infos)); ++ next0 = fm->next_node_index; ++ } ++ } else { ++ next0 = FT_NEXT_ETHERNET_INPUT; ++ } ++ vlib_buffer_reset(b0); ++ ++ /* stats */ ++ CPT_THRU++; ++ ++ /* frame mgmt */ ++ from++; ++ to_next++; ++ n_left_from--; ++ n_left_to_next--; ++ ++ if (b0->flags & VLIB_BUFFER_IS_TRACED) ++ { ++ flow_trace_t * t = vlib_add_trace(vm, node, b0, sizeof(*t)); ++ t->sw_if_index = sw_if_index0; ++ t->next_index = next0; ++ if (flow) ++ t->offloaded = flow->infos.data.offloaded; ++ else ++ t->offloaded = 0; ++ } ++ ++get_flowinfo_error: ++ 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); ++ } ++ ++ /* handle expirations */ ++ CPT_TIMER_EXPIRE += flowtable_timer_expire(fm, current_time); ++ ++#define _(sym, str) \ ++ vlib_node_increment_counter(vm, flowtable_node.index, \ ++ FLOWTABLE_ERROR_##sym , CPT_##sym); ++ foreach_flowtable_error ++#undef _ ++ ++ return frame->n_vectors; ++} ++ ++static char * flowtable_error_strings[] = { ++#define _(sym, string) string, ++ foreach_flowtable_error ++#undef _ ++}; ++ ++VLIB_REGISTER_NODE(flowtable_node) = { ++ .function = flowtable_process, ++ .name = "flowtable-process", ++ .vector_size = sizeof(u32), ++ .format_trace = format_get_flowinfo, ++ .type = VLIB_NODE_TYPE_INTERNAL, ++ .n_errors = FLOWTABLE_N_ERROR, ++ .error_strings = flowtable_error_strings, ++ .n_next_nodes = FT_NEXT_N_NEXT, ++ .next_nodes = { ++ [FT_NEXT_DROP] = "error-drop", ++ [FT_NEXT_ETHERNET_INPUT] = "ethernet-input" ++ } ++}; +diff --git a/plugins/flowtable-plugin/flowtable/nodes_registration.c b/plugins/flowtable-plugin/flowtable/nodes_registration.c +new file mode 100644 +index 0000000..ba36afb +--- /dev/null ++++ b/plugins/flowtable-plugin/flowtable/nodes_registration.c +@@ -0,0 +1,80 @@ ++/* ++ * Copyright (c) 2016 Qosmos and/or its affiliates. ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at: ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#include <vnet/plugin/plugin.h> ++#include <vppinfra/bihash_8_8.h> ++#include <vppinfra/dlist.h> ++#include <vppinfra/pool.h> ++#include <vppinfra/types.h> ++#include <vppinfra/vec.h> ++ ++#include "flowtable.h" ++ ++/* ++ * 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; ++ flowtable_main_t * fm = &flowtable_main; ++ ++ fm->vnet_main = vnet_get_main(); ++ fm->vlib_main = vm; ++ ++ return error; ++} ++ ++static clib_error_t * ++flowtable_init(vlib_main_t * vm) ++{ ++ int i; ++ clib_error_t * error = 0; ++ flowtable_main_t * fm = &flowtable_main; ++ ++ fm->flowtable_index = flowtable_node.index; ++ ++ /* ensures flow_info structure fits into vlib_buffer_t's opaque 1 field */ ++ ASSERT(sizeof(flow_data_t) <= sizeof(u32) * 8); ++ ++ /* init flow pool ++ * TODO get flow count from configuration */ ++ pool_alloc_aligned(fm->flows, FM_POOL_COUNT, CLIB_CACHE_LINE_BYTES); ++ ++ /* init hashtable */ ++ fm->flows_cpt = 0; ++ pool_alloc(fm->ht_lines, 2 * FM_POOL_COUNT); ++ BV(clib_bihash_init) (&fm->flows_ht, "flow hash table", ++ FM_NUM_BUCKETS, FM_MEMORY_SIZE); ++ ++ /* init timer wheel */ ++ fm->time_index = ~0; ++ for (i = 0; i < TIMER_MAX_LIFETIME ; i++) { ++ dlist_elt_t * timer_slot; ++ pool_get(fm->timers, timer_slot); ++ ++ u32 timer_slot_head_index = timer_slot - fm->timers; ++ clib_dlist_init (fm->timers, timer_slot_head_index); ++ vec_add1(fm->timer_wheel, timer_slot_head_index); ++ } ++ ++ return error; ++} ++ ++VLIB_INIT_FUNCTION(flowtable_init); +-- +2.10.0.rc1.15.g5cb0d5a + diff --git a/flowtable/0002-Add-Port-Mirroring-feature.patch b/flowtable/0002-Add-Port-Mirroring-feature.patch new file mode 100644 index 0000000..270a487 --- /dev/null +++ b/flowtable/0002-Add-Port-Mirroring-feature.patch @@ -0,0 +1,896 @@ +From ea8b126e31ab0f4e2c980be43cb423d50b7d7244 Mon Sep 17 00:00:00 2001 +From: Gabriel Ganne <gabriel.ganne@qosmos.com> +Date: Sat, 17 Sep 2016 10:13:52 +0200 +Subject: [PATCH 2/3] Add Port Mirroring feature + +port-mirroring is under /plugins +connect l2-input-classify node to the port-mirroring node +connect l2-output-classify node to the port-mirroring node + +next node depends on previous node. + +Author: Christophe Fontaine <christophe.fontaine@qosmos.com> +Author: Gabriel Ganne <gabriel.ganne@qosmos.com> + +Signed-off-by: Gabriel Ganne <gabriel.ganne@qosmos.com> +Change-Id: Ia8b83615880ca274ee6b292e4fea0eb02d4d7aaa +--- + plugins/Makefile.am | 4 + + plugins/configure.ac | 1 + + plugins/portmirroring-plugin/Makefile.am | 59 +++++++ + plugins/portmirroring-plugin/configure.ac | 16 ++ + plugins/portmirroring-plugin/portmirroring/api.c | 131 +++++++++++++++ + .../portmirroring-plugin/portmirroring/flowdata.h | 1 + + .../portmirroring/port_mirroring.c | 134 ++++++++++++++++ + .../portmirroring/port_mirroring.h | 67 ++++++++ + .../portmirroring/port_mirroring_in_node.c | 178 +++++++++++++++++++++ + .../portmirroring/port_mirroring_out_node.c | 168 +++++++++++++++++++ + .../portmirroring/portmirroring.api | 21 +++ + 11 files changed, 780 insertions(+) + create mode 100644 plugins/portmirroring-plugin/Makefile.am + create mode 100644 plugins/portmirroring-plugin/configure.ac + create mode 100644 plugins/portmirroring-plugin/portmirroring/api.c + create mode 120000 plugins/portmirroring-plugin/portmirroring/flowdata.h + create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring.c + create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring.h + create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c + create mode 100644 plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c + create mode 100644 plugins/portmirroring-plugin/portmirroring/portmirroring.api + +diff --git a/plugins/Makefile.am b/plugins/Makefile.am +index d0a2e3c..7bd93a9 100644 +--- a/plugins/Makefile.am ++++ b/plugins/Makefile.am +@@ -55,3 +55,7 @@ endif + if ENABLE_flowtable_PLUGIN + SUBDIRS += flowtable-plugin + endif ++ ++if ENABLE_portmirroring_PLUGIN ++SUBDIRS += portmirroring-plugin ++endif +diff --git a/plugins/configure.ac b/plugins/configure.ac +index cbb180f..5e30011 100644 +--- a/plugins/configure.ac ++++ b/plugins/configure.ac +@@ -59,6 +59,7 @@ PLUGIN_ENABLED(snat) + PLUGIN_ENABLED(ila) + PLUGIN_ENABLED(lb) + PLUGIN_ENABLED(flowtable) ++PLUGIN_ENABLED(portmirroring) + + # Disabled plugins, require --enable-XXX-plugin + PLUGIN_DISABLED(vcgn) +diff --git a/plugins/portmirroring-plugin/Makefile.am b/plugins/portmirroring-plugin/Makefile.am +new file mode 100644 +index 0000000..77da992 +--- /dev/null ++++ b/plugins/portmirroring-plugin/Makefile.am +@@ -0,0 +1,59 @@ ++# 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. ++ ++AUTOMAKE_OPTIONS = foreign subdir-objects ++ ++AM_CFLAGS = -Wall ++AM_LDFLAGS = -module -shared -avoid-version ++ ++vppapitestpluginsdir = ${libdir}/vpp_api_test_plugins ++vpppluginsdir = ${libdir}/vpp_plugins ++ ++# vppapitestplugins_LTLIBRARIES = portmirroring_test_plugin.la ++vppplugins_LTLIBRARIES = portmirroring_plugin.la ++ ++portmirroring_plugin_la_SOURCES = \ ++ portmirroring/api.c \ ++ portmirroring/port_mirroring.c \ ++ portmirroring/port_mirroring_in_node.c \ ++ portmirroring/port_mirroring_out_node.c ++ ++BUILT_SOURCES = portmirroring/portmirroring.api.h portmirroring/portmirroring.py ++ ++SUFFIXES = .api.h .api ++ ++%.api.h: %.api ++ mkdir -p `dirname $@` ; \ ++ $(CC) $(CPPFLAGS) -E -P -C -x c $^ \ ++ | vppapigen --input - --output $@ --show-name $@ ++ ++%.py: %.api ++ $(info Creating Python binding for $@) ++ $(CC) $(CPPFLAGS) -E -P -C -x c $< \ ++ | vppapigen --input - --python - \ ++ | pyvppapigen.py --input - > $@ ++ ++ ++pyapidir = ${prefix}/vpp_papi_plugins ++pyapi_DATA = portmirroring/portmirroring.py ++ ++noinst_HEADERS = portmirroring/port_mirroring.h portmirroring/portmirroring.api.h ++ ++#portmirroring_test_plugin_la_SOURCES = \ ++# portmirroring/portmirroring_test.c portmirroring/portmirroring_plugin.api.h ++ ++# Remove *.la files ++install-data-hook: ++ @(cd $(vpppluginsdir) && $(RM) $(vppplugins_LTLIBRARIES)) ++ ++# @(cd $(vppapitestpluginsdir) && $(RM) $(vppapitestplugins_LTLIBRARIES)) +diff --git a/plugins/portmirroring-plugin/configure.ac b/plugins/portmirroring-plugin/configure.ac +new file mode 100644 +index 0000000..3af74c0 +--- /dev/null ++++ b/plugins/portmirroring-plugin/configure.ac +@@ -0,0 +1,16 @@ ++AC_INIT(pm_plugin, 1.0) ++AM_INIT_AUTOMAKE ++AM_SILENT_RULES([yes]) ++AC_PREFIX_DEFAULT([/usr]) ++ ++AC_PROG_LIBTOOL ++AC_PROG_CC ++ ++AC_ARG_WITH(dpdk, ++ AC_HELP_STRING([--with-dpdk],[Use the Intel dpdk]), ++ [with_dpdk=1], ++ [with_dpdk=0]) ++ ++AM_CONDITIONAL(WITH_DPDK, test "$with_dpdk" = "1") ++AM_COND_IF(WITH_DPDK, CFLAGS+="-DDPDK=1") ++AC_OUTPUT([Makefile]) +diff --git a/plugins/portmirroring-plugin/portmirroring/api.c b/plugins/portmirroring-plugin/portmirroring/api.c +new file mode 100644 +index 0000000..696557b +--- /dev/null ++++ b/plugins/portmirroring-plugin/portmirroring/api.c +@@ -0,0 +1,131 @@ ++/* ++ * 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 <portmirroring/port_mirroring.h> ++ ++#include <vppinfra/byte_order.h> ++#include <vlibapi/api.h> ++#include <vlibmemory/api.h> ++#include <vlibsocket/api.h> ++ ++#define vl_msg_id(n,h) n, ++typedef enum { ++#include <portmirroring/portmirroring.api.h> ++ /* We'll want to know how many messages IDs we need... */ ++ VL_MSG_FIRST_AVAILABLE, ++} vl_msg_id_t; ++#undef vl_msg_id ++ ++ ++/* define message structures */ ++#define vl_typedefs ++#include <portmirroring/portmirroring.api.h> ++#undef vl_typedefs ++ ++/* define generated endian-swappers */ ++#define vl_endianfun ++#include <portmirroring/portmirroring.api.h> ++#undef vl_endianfun ++ ++#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) ++ ++/* Get the API version number */ ++#define vl_api_version(n,v) static u32 api_version=(v); ++#include <portmirroring/portmirroring.api.h> ++#undef vl_api_version ++ ++/* Macro to finish up custom dump fns */ ++#define FINISH \ ++ vec_add1 (s, 0); \ ++ vl_print (handle, (char *)s); \ ++ vec_free (s); \ ++ return handle; ++ ++/* ++ * 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)+pmm->msg_id_base); \ ++ rmp->context = mp->context; \ ++ rmp->retval = ntohl(rv); \ ++ \ ++ vl_msg_api_send_shmem (q, (u8 *)&rmp); \ ++} while(0); ++ ++static void ++vl_api_pm_conf_t_handler ++(vl_api_pm_conf_t * mp) ++{ ++ pm_main_t *pmm = &pm_main; ++ vl_api_pm_conf_reply_t * rmp; ++ int rv = 0; ++ ++ rv = pm_conf(mp->dst_interface, mp->from_node, mp->is_del); ++ ++ REPLY_MACRO (VL_API_PM_CONF_REPLY); ++} ++ ++static void *vl_api_pm_conf_t_print ++(vl_api_pm_conf_t *mp, void * handle) ++{ ++ u8 * s; ++ s = format (0, "SCRIPT: pm_conf "); ++ s = format (s, "%u ", mp->dst_interface); ++ s = format (s, "%u ", mp->from_node); ++ s = format (s, "%s ", (mp->is_del? "DELETE" : "ADD" )); ++ FINISH; ++} ++ ++ ++/* List of message types that this plugin understands */ ++#define foreach_pm_plugin_api_msg \ ++_(PM_CONF, pm_conf) \ ++ ++ ++static clib_error_t * pm_api_init (vlib_main_t * vm) ++{ ++ pm_main_t *pmm = &pm_main; ++ u8 *name = format (0, "portmirroring_%08x%c", api_version, 0); ++ pmm->msg_id_base = vl_msg_api_get_msg_ids ++ ((char *) name, VL_MSG_FIRST_AVAILABLE); ++ ++#define _(N,n) \ ++ vl_msg_api_set_handlers((VL_API_##N + pmm->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_pm_plugin_api_msg; ++#undef _ ++ ++ return 0; ++} ++ ++VLIB_INIT_FUNCTION (pm_api_init); ++ +diff --git a/plugins/portmirroring-plugin/portmirroring/flowdata.h b/plugins/portmirroring-plugin/portmirroring/flowdata.h +new file mode 120000 +index 0000000..67f4f12 +--- /dev/null ++++ b/plugins/portmirroring-plugin/portmirroring/flowdata.h +@@ -0,0 +1 @@ ++../../flowtable-plugin/flowtable/flowdata.h +\ No newline at end of file +diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring.c b/plugins/portmirroring-plugin/portmirroring/port_mirroring.c +new file mode 100644 +index 0000000..46b6e0f +--- /dev/null ++++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring.c +@@ -0,0 +1,134 @@ ++/* ++ * 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. ++ */ ++ ++#if DPDK != 1 ++#error "DPDK needed" ++#endif ++ ++#include <vnet/plugin/plugin.h> ++#include <vnet/api_errno.h> ++#include <portmirroring/port_mirroring.h> ++ ++int pm_conf(u8 dst_interface, u8 from_node, u8 is_del) ++{ ++ pm_main_t *pm = &pm_main; ++ ++ if(is_del == 0) { ++ pm->sw_if_index = dst_interface; ++ pm->from_node = from_node; ++ if (from_node == PM_FROM_FLOWTABLE) { ++ pm->next_node_index = PM_IN_HIT_NEXT_ETHERNET_INPUT; ++ } else if (from_node == PM_FROM_CLASSSIFIER) { ++ pm->next_node_index = PM_IN_HIT_NEXT_L2_LEARN; ++ } else { ++ pm->next_node_index = PM_IN_HIT_NEXT_ERROR; ++ return -1; ++ } ++ } else { ++ pm->sw_if_index = ~0; ++ pm->from_node = ~0; ++ pm->next_node_index = ~0; ++ } ++ ++ return 0; ++} ++ ++static clib_error_t * ++set_pm_command_fn (vlib_main_t * vm, ++ unformat_input_t * input, vlib_cli_command_t * cmd) ++{ ++ pm_main_t *pm = &pm_main; ++ int enable_disable = 1; ++ int sw_if_index = ~0; ++ int from_node = ~0; ++ ++ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) ++ { ++ if (unformat (input, "from %U", unformat_vlib_node, ++ pm->vnet_main, &from_node)); ++ if (unformat (input, "to %U", unformat_vnet_sw_interface, ++ pm->vnet_main, &sw_if_index)); ++ else if (unformat (input, "del")) ++ enable_disable = 0; ++ else if (unformat (input, "disable")) ++ enable_disable = 0; ++ else ++ break; ++ } ++ ++ if (sw_if_index == ~0) ++ return clib_error_return (0, "interface required"); ++ if (from_node == ~0) ++ return clib_error_return (0, "previous node required"); ++ ++ ++ if (enable_disable) ++ { ++ pm->sw_if_index = sw_if_index; ++ pm->from_node = from_node; ++ return 0; ++ } ++ else ++ { ++ pm->sw_if_index = ~0; ++ pm->from_node = ~0; ++ } ++ ++ ++ return 0; ++} ++ ++VLIB_CLI_COMMAND (set_pm_command, static) = ++{ ++ .path = "set pm", ++ .short_help = "set pm from <0|1> to <intfc> [del]", ++ .function = set_pm_command_fn, ++}; ++ ++ ++static clib_error_t * ++pm_init (vlib_main_t * vm) ++{ ++ pm_main_t *pm = &pm_main; ++ ++ pm->sw_if_index = ~0; ++ pm->from_node = ~0; ++ ++ pm->pm_in_hit_node_index = pm_in_hit_node.index; ++ pm->pm_out_hit_node_index = pm_out_hit_node.index; ++ ++ /* pm-out previous node */ ++ u32 l2out_classify_idx = vlib_get_node_by_name(vm, (u8*) "l2-output-classify")->index; ++ vlib_node_add_next(vm, l2out_classify_idx, pm->pm_out_hit_node_index); ++ ++ /* pm-in & pm-out next node */ ++ pm->interface_output_node_index = vlib_get_node_by_name(vm, (u8*) "interface-output")->index; ++ ++ return 0; ++} ++ ++VLIB_INIT_FUNCTION (pm_init); ++ ++clib_error_t * ++vlib_plugin_register (vlib_main_t * vm, ++ vnet_plugin_handoff_t * h, ++ int from_early_init) ++{ ++ clib_error_t *error = 0; ++ pm_main_t *pm = &pm_main; ++ pm->vnet_main = vnet_get_main(); ++ pm->vlib_main = vm; ++ return error; ++} +diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring.h b/plugins/portmirroring-plugin/portmirroring/port_mirroring.h +new file mode 100644 +index 0000000..8e9826f +--- /dev/null ++++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring.h +@@ -0,0 +1,67 @@ ++/* ++ * 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. ++ */ ++ ++#ifndef __port_mirroring_h__ ++#define __port_mirroring_h__ ++ ++#include <vnet/vnet.h> ++#include <vnet/ip/ip.h> ++ ++enum { ++ PM_FROM_CLASSSIFIER = 0, ++ PM_FROM_FLOWTABLE = 1, ++ PM_FROM_MAX ++}; ++ ++typedef enum { ++ PM_IN_HIT_NEXT_ERROR, ++ PM_IN_HIT_NEXT_ETHERNET_INPUT, ++ PM_IN_HIT_NEXT_L2_LEARN, ++ PM_IN_HIT_N_NEXT, ++} pm_in_hit_next_t; ++ ++typedef struct ++{ ++ /* mirror interface index */ ++ u32 sw_if_index; ++ u32 from_node; ++ ++ /* Hit node index */ ++ u32 pm_in_hit_node_index; ++ u32 pm_out_hit_node_index; ++ ++ u32 interface_output_node_index; ++ ++ /* depends on the previous node */ ++ u32 next_node_index; ++ ++ /* convenience */ ++ vlib_main_t *vlib_main; ++ vnet_main_t *vnet_main; ++ ++ /** ++ * API dynamically registered base ID. ++ */ ++ u16 msg_id_base; ++} pm_main_t; ++ ++pm_main_t pm_main; ++ ++int pm_conf(u8 dst_interface, u8 from_node, u8 is_del); ++ ++extern vlib_node_registration_t pm_in_hit_node; ++extern vlib_node_registration_t pm_out_hit_node; ++ ++#endif /* __port_mirroring_h__ */ +diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c b/plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c +new file mode 100644 +index 0000000..04b05df +--- /dev/null ++++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring_in_node.c +@@ -0,0 +1,178 @@ ++/* ++ * 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 <vlib/vlib.h> ++#include <vnet/vnet.h> ++#include <vppinfra/error.h> ++ ++#if DPDK != 1 ++#error "DPDK needed" ++#endif ++ ++#include <portmirroring/port_mirroring.h> ++#include <vnet/dpdk_replication.h> ++ ++#include <vppinfra/error.h> ++#include <vppinfra/elog.h> ++ ++#include "flowdata.h" ++#include "port_mirroring.h" ++ ++vlib_node_registration_t pm_in_hit_node; ++ ++typedef struct { ++ u32 next_index; ++} pm_in_hit_trace_t; ++ ++/* packet trace format function */ ++static u8 * ++format_pm_in_hit_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 *); ++ pm_in_hit_trace_t * t = va_arg(*args, pm_in_hit_trace_t *); ++ ++ s = format(s, "PM_IN_HIT: next index %d", t->next_index); ++ ++ return s; ++} ++ ++vlib_node_registration_t pm_in_hit_node; ++ ++#define foreach_pm_in_hit_error \ ++ _(HITS, "PM in packets processed") \ ++ _(NO_INTF_OUT, "No out interface configured") ++ ++typedef enum { ++#define _(sym, str) PM_IN_HIT_ERROR_ ## sym, ++ foreach_pm_in_hit_error ++#undef _ ++ PM_IN_HIT_N_ERROR, ++} pm_in_hit_error_t; ++ ++static char * pm_in_hit_error_strings[] = { ++#define _(sym, string) string, ++ foreach_pm_in_hit_error ++#undef _ ++}; ++ ++static uword ++pm_in_hit_node_fn(vlib_main_t * vm, ++ vlib_node_runtime_t * node, vlib_frame_t * frame) ++{ ++ u32 n_left_from, * from, * to_next; ++ u32 next_index; ++ vlib_frame_t * dup_frame = 0; ++ u32 * to_int_next = 0; ++ pm_main_t * pm = &pm_main; ++ ++ from = vlib_frame_vector_args(frame); ++ n_left_from = frame->n_vectors; ++ next_index = node->cached_next_index; ++ ++ if (pm->sw_if_index == ~0) { ++ vlib_node_increment_counter (vm, pm_in_hit_node.index, ++ PM_IN_HIT_ERROR_NO_INTF_OUT, ++ n_left_from); ++ } else { ++ /* The intercept frame... */ ++ dup_frame = vlib_get_frame_to_node(vm, pm->interface_output_node_index); ++ to_int_next = vlib_frame_vector_args(dup_frame); ++ } ++ ++ while (n_left_from > 0) ++ { ++ u32 n_left_to_next; ++ ++ vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next); ++ ++ while (n_left_from > 0 && n_left_to_next > 0) ++ { ++ u32 bi0; ++ vlib_buffer_t * b0; ++ vlib_buffer_t * c0; ++ u32 next0 = pm->next_node_index; ++ ++ /* 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); ++ if (PREDICT_TRUE(to_int_next != 0)) ++ { ++ /* Make a copy */ ++ c0 = vlib_dpdk_clone_buffer(vm, b0); ++ ++ vnet_buffer (c0)->sw_if_index[VLIB_TX] = pm->sw_if_index; ++ ++ to_int_next[0] = vlib_get_buffer_index(vm, c0); ++ to_int_next++; ++ ++ } ++ ++ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) ++ && (b0->flags & VLIB_BUFFER_IS_TRACED))) ++ { ++ pm_in_hit_trace_t * t = vlib_add_trace(vm, node, b0, sizeof(*t)); ++ t->next_index = next0; ++ } ++ ++ /* restore opaque value which was used by the flowtable plugin */ ++ if (pm->from_node == PM_FROM_FLOWTABLE) { ++ flow_data_t flow_data; ++ clib_memcpy(&flow_data, b0->opaque, sizeof(flow_data)); ++ vnet_buffer (b0)->sw_if_index[VLIB_RX] = flow_data.data.sw_if_index_current; ++ } ++ ++ /* 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); ++ } ++ ++ if (dup_frame) ++ { ++ dup_frame->n_vectors = frame->n_vectors; ++ vlib_put_frame_to_node(vm, pm->interface_output_node_index, dup_frame); ++ } ++ ++ vlib_node_increment_counter(vm, pm_in_hit_node.index, ++ PM_IN_HIT_ERROR_HITS, frame->n_vectors); ++ return frame->n_vectors; ++} ++ ++VLIB_REGISTER_NODE(pm_in_hit_node) = { ++ .function = pm_in_hit_node_fn, ++ .name = "pm-in-hit", ++ .vector_size = sizeof(u32), ++ .format_trace = format_pm_in_hit_trace, ++ .type = VLIB_NODE_TYPE_INTERNAL, ++ ++ .n_errors = ARRAY_LEN(pm_in_hit_error_strings), ++ .error_strings = pm_in_hit_error_strings, ++ ++ .n_next_nodes = PM_IN_HIT_N_NEXT, ++ .next_nodes = { ++ [PM_IN_HIT_NEXT_ERROR] = "error-drop", ++ [PM_IN_HIT_NEXT_ETHERNET_INPUT] = "ethernet-input", ++ [PM_IN_HIT_NEXT_L2_LEARN] = "l2-learn", ++ } ++}; +diff --git a/plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c b/plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c +new file mode 100644 +index 0000000..9eebb88 +--- /dev/null ++++ b/plugins/portmirroring-plugin/portmirroring/port_mirroring_out_node.c +@@ -0,0 +1,168 @@ ++/* ++ * 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 <vlib/vlib.h> ++#include <vnet/vnet.h> ++#include <vppinfra/error.h> ++ ++#if DPDK != 1 ++#error "DPDK needed" ++#endif ++ ++#include <portmirroring/port_mirroring.h> ++#include <vnet/dpdk_replication.h> ++ ++#include <vppinfra/error.h> ++#include <vppinfra/elog.h> ++ ++vlib_node_registration_t pm_out_hit_node; ++ ++typedef struct { ++ u32 next_index; ++} pm_out_hit_trace_t; ++ ++/* packet trace format function */ ++static u8 * ++format_pm_out_hit_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 *); ++ pm_out_hit_trace_t * t = va_arg(*args, pm_out_hit_trace_t *); ++ ++ s = format(s, "PM_OUT_HIT: next index %d", t->next_index); ++ ++ return s; ++} ++ ++#define foreach_pm_out_hit_error \ ++ _(HITS, "PM out packets processed") \ ++ _(NO_COLLECTOR, "No collector configured") ++ ++typedef enum { ++#define _(sym, str) PM_OUT_HIT_ERROR_ ## sym, ++ foreach_pm_out_hit_error ++#undef _ ++ PM_OUT_HIT_N_ERROR, ++} pm_out_hit_error_t; ++ ++static char * pm_out_hit_error_strings[] = { ++#define _(sym, string) string, ++ foreach_pm_out_hit_error ++#undef _ ++}; ++ ++typedef enum { ++ PM_OUT_HIT_NEXT_IF_OUT, ++ PM_OUT_HIT_N_NEXT, ++} pm_out_hit_next_t; ++ ++static uword ++pm_out_hit_node_fn(vlib_main_t * vm, ++ vlib_node_runtime_t * node, vlib_frame_t * frame) ++{ ++ u32 n_left_from, * from, * to_next; ++ pm_out_hit_next_t next_index; ++ vlib_frame_t * dup_frame = 0; ++ u32 * to_int_next = 0; ++ pm_main_t * pm = &pm_main; ++ ++ from = vlib_frame_vector_args(frame); ++ n_left_from = frame->n_vectors; ++ next_index = node->cached_next_index; ++ ++ if (pm->sw_if_index == ~0) { ++ vlib_node_increment_counter (vm, pm_out_hit_node.index, ++ PM_OUT_HIT_ERROR_NO_COLLECTOR, ++ n_left_from); ++ } else { ++ /* The intercept frame... */ ++ dup_frame = vlib_get_frame_to_node(vm, pm->interface_output_node_index); ++ to_int_next = vlib_frame_vector_args(dup_frame); ++ } ++ ++ while (n_left_from > 0) ++ { ++ u32 n_left_to_next; ++ ++ vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next); ++ ++ while (n_left_from > 0 && n_left_to_next > 0) ++ { ++ u32 bi0; ++ vlib_buffer_t * b0; ++ vlib_buffer_t * c0; ++ u32 next0 = PM_OUT_HIT_NEXT_IF_OUT; ++ ++ /* 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); ++ if (PREDICT_TRUE(to_int_next != 0)) ++ { ++ /* Make an intercept copy */ ++ c0 = vlib_dpdk_clone_buffer(vm, b0); ++ ++ vnet_buffer (c0)->sw_if_index[VLIB_TX] = pm->sw_if_index; ++ ++ to_int_next[0] = vlib_get_buffer_index(vm, c0); ++ to_int_next++; ++ } ++ ++ if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) ++ && (b0->flags & VLIB_BUFFER_IS_TRACED))) ++ { ++ pm_out_hit_trace_t * t = vlib_add_trace(vm, node, b0, sizeof(*t)); ++ t->next_index = next0; ++ } ++ /* 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); ++ } ++ ++ if (dup_frame) ++ { ++ dup_frame->n_vectors = frame->n_vectors; ++ vlib_put_frame_to_node(vm, pm->interface_output_node_index, dup_frame); ++ } ++ ++ vlib_node_increment_counter(vm, pm_out_hit_node.index, ++ PM_OUT_HIT_ERROR_HITS, frame->n_vectors); ++ return frame->n_vectors; ++} ++ ++VLIB_REGISTER_NODE(pm_out_hit_node) = { ++ .function = pm_out_hit_node_fn, ++ .name = "pm-out-hit", ++ .vector_size = sizeof(u32), ++ .format_trace = format_pm_out_hit_trace, ++ .type = VLIB_NODE_TYPE_INTERNAL, ++ ++ .n_errors = ARRAY_LEN(pm_out_hit_error_strings), ++ .error_strings = pm_out_hit_error_strings, ++ ++ .n_next_nodes = PM_OUT_HIT_N_NEXT, ++ ++ .next_nodes = { ++ [PM_OUT_HIT_NEXT_IF_OUT] = "interface-output", ++ } ++}; +diff --git a/plugins/portmirroring-plugin/portmirroring/portmirroring.api b/plugins/portmirroring-plugin/portmirroring/portmirroring.api +new file mode 100644 +index 0000000..fe86bb2 +--- /dev/null ++++ b/plugins/portmirroring-plugin/portmirroring/portmirroring.api +@@ -0,0 +1,21 @@ ++/** \brief Configure Port-Mirroring global parameters ++ @param client_index - opaque cookie to identify the sender ++ @param context - sender context, to match reply w/ request ++ @param dst_interface - name or id of interface to copy packets to ++ @param from_node - 0|1 (classifier|flowtable) ++ @param is_del - Whether we have to delete any port mirrring information ++*/ ++define pm_conf ++{ ++ u32 client_index; ++ u32 context; ++ u8 dst_interface; ++ u8 from_node; ++ u8 is_del; ++}; ++ ++define pm_conf_reply { ++ u32 context; ++ i32 retval; ++}; ++ +-- +2.10.0.rc1.15.g5cb0d5a + diff --git a/flowtable/0003-test-scripts.patch b/flowtable/0003-test-scripts.patch new file mode 100644 index 0000000..e501561 --- /dev/null +++ b/flowtable/0003-test-scripts.patch @@ -0,0 +1,185 @@ +From 344cc48bef87c54a3d7d6c489b56fb1ac513897e Mon Sep 17 00:00:00 2001 +From: Gabriel Ganne <gabriel.ganne@qosmos.com> +Date: Sat, 17 Sep 2016 10:16:19 +0200 +Subject: [PATCH 3/3] test scripts + +Adds small scripts to test both flowtable and portmirroring APIs + +Signed-off-by: Gabriel Ganne <gabriel.ganne@qosmos.com> +Change-Id: I59c6c0860125f8d8f553312fe44a2ed42d0b3e7c +--- + ft-test/classifier-env-setup.sh | 24 +++++++++++++++++++++ + ft-test/classifier-pm.py | 46 +++++++++++++++++++++++++++++++++++++++++ + ft-test/flowtable-env-setup.sh | 27 ++++++++++++++++++++++++ + ft-test/flowtable-pm.py | 41 ++++++++++++++++++++++++++++++++++++ + 4 files changed, 138 insertions(+) + create mode 100755 ft-test/classifier-env-setup.sh + create mode 100755 ft-test/classifier-pm.py + create mode 100755 ft-test/flowtable-env-setup.sh + create mode 100755 ft-test/flowtable-pm.py + +diff --git a/ft-test/classifier-env-setup.sh b/ft-test/classifier-env-setup.sh +new file mode 100755 +index 0000000..c930ec5 +--- /dev/null ++++ b/ft-test/classifier-env-setup.sh +@@ -0,0 +1,24 @@ ++#!/bin/bash ++ ++for i in `seq 1 3` ++do ++ ip netns delete vpp$i ++ ip netns add vpp$i ++ vppctl tap connect vpp$i ++ vppctl set interface state tap-$(($i - 1)) up ++ ip link set dev vpp$i netns vpp$i ++ ip netns exec vpp$i ip link set vpp$i up ++done ++ ++vppctl set interface l2 bridge tap-0 23 ++vppctl set interface l2 bridge tap-1 23 ++ ++ip netns exec vpp1 ip addr add 192.168.0.1/24 dev vpp1 ++ip netns exec vpp2 ip addr add 192.168.0.2/24 dev vpp2 ++ ++vppctl set int l3 tap-2 ++vppctl set int ip addr tap-2 192.168.1.1/24 ++ ++# test ++ip netns exec vpp1 ping -c1 192.168.0.2 ++ip netns exec vpp2 ping -c1 192.168.0.1 +diff --git a/ft-test/classifier-pm.py b/ft-test/classifier-pm.py +new file mode 100755 +index 0000000..4110e24 +--- /dev/null ++++ b/ft-test/classifier-pm.py +@@ -0,0 +1,46 @@ ++#!/bin/env python ++from __future__ import print_function ++ ++import vpp_papi as vpp ++ ++from vpp_papi.portmirroring import * ++import vpp_papi.portmirroring as pm ++ ++if_1_name = 'tap-0' ++if_2_name = 'tap-1' ++if_3_name = 'tap-2' ++ ++r = vpp.connect('papi') ++ ++if_1_sw_if_index = vpp.sw_interface_dump(1, if_1_name)[0].sw_if_index ++if_2_sw_if_index = vpp.sw_interface_dump(1, if_2_name)[0].sw_if_index ++if_3_sw_if_index = vpp.sw_interface_dump(1, if_3_name)[0].sw_if_index ++ ++# add port-mirroring as available classifier next nodes ++r = vpp.add_node_next("l2-input-classify", "pm-in-hit") ++print(r) ++pm_in_hit_idx = r.node_index; ++ ++r = vpp.add_node_next("l2-output-classify", "pm-out-hit") ++print(r) ++pm_out_hit_idx = r.node_index; ++ ++# configure portmirroring ++# 0 -> classifier, 0 -> add ++r = pm.pm_conf(if_3_sw_if_index, 0, 0) ++print(r) ++ ++# add, table_index, nbuckets, memory_size, skip_n_vectors, match_n_vectors, next_table_index, miss_next_index, mask ++cl0 = vpp.classify_add_del_table(1, 0xffffffff, 64, 1024*1024, 0, 1, 0xffffffff, pm_in_hit_idx, '') ++print(cl0) ++cl1 = vpp.classify_add_del_table(1, 0xffffffff, 64, 1024*1024, 0, 1, 0xffffffff, pm_out_hit_idx, '') ++print(cl1) ++ ++# input -> 1, output -> 0 ++r = vpp.classify_set_interface_l2_tables(if_1_sw_if_index, cl0.new_table_index, 0xffffffff, 0xffffffff, 1) ++print(r) ++r = vpp.classify_set_interface_l2_tables(if_1_sw_if_index, cl1.new_table_index, 0xffffffff, 0xffffffff, 0) ++print(r) ++ ++r = vpp.disconnect() ++exit(r) +diff --git a/ft-test/flowtable-env-setup.sh b/ft-test/flowtable-env-setup.sh +new file mode 100755 +index 0000000..1584000 +--- /dev/null ++++ b/ft-test/flowtable-env-setup.sh +@@ -0,0 +1,27 @@ ++#!/bin/bash ++ ++for i in `seq 1 3` ++do ++ ip netns delete vpp$i ++ ip netns add vpp$i ++ vppctl tap connect vpp$i ++ vppctl set interface state tap-$(($i - 1)) up ++ ip link set dev vpp$i netns vpp$i ++ ip netns exec vpp$i ip link set vpp$i up ++done ++ ++vppctl set interface l2 bridge tap-0 23 ++vppctl set interface l2 bridge tap-1 23 ++ ++ip netns exec vpp1 ip addr add 192.168.0.1/24 dev vpp1 ++ip netns exec vpp2 ip addr add 192.168.0.2/24 dev vpp2 ++ip netns exec vpp3 ip addr add 192.168.1.1/24 dev vpp3 ++ ++for if in `seq 1 3` ++do ++ ip netns exec vpp$i ifconfig vpp$i mtu 10000 ++done ++ ++# test ++ip netns exec vpp1 ping -c1 192.168.0.2 ++ip netns exec vpp2 ping -c1 192.168.0.1 +diff --git a/ft-test/flowtable-pm.py b/ft-test/flowtable-pm.py +new file mode 100755 +index 0000000..6907b8f +--- /dev/null ++++ b/ft-test/flowtable-pm.py +@@ -0,0 +1,41 @@ ++#!/bin/env python ++from __future__ import print_function ++ ++import sys ++import vpp_papi as vpp ++ ++from portmirroring import * ++portmirroring = sys.modules['portmirroring'] ++ ++ ++from flowtable import * ++flowtable = sys.modules['flowtable'] ++ ++if_1_name = 'tap-0' ++if_2_name = 'tap-1' ++if_3_name = 'tap-2' ++ ++r = vpp.connect('papi') ++ ++if_1_sw_if_index = vpp.sw_interface_dump(1, if_1_name)[0].sw_if_index ++if_2_sw_if_index = vpp.sw_interface_dump(1, if_2_name)[0].sw_if_index ++if_3_sw_if_index = vpp.sw_interface_dump(1, if_3_name)[0].sw_if_index ++ ++# add port-mirroring as available flowtable next node ++r = vpp.add_node_next("flowtable-process", "pm-in-hit") ++print(r) ++portmirroring_index = r.node_index; ++ ++# configure portmirroring ++# 1 = flowtable, 0 = is_add ++r = portmirroring.pm_conf(if_3_sw_if_index, 1, 0) ++print(r) ++ ++# hook flowtable from both intfs 1 & 2: ++r = flowtable.flowtable_conf(if_1_sw_if_index, portmirroring_index, 0) ++print(r) ++r = flowtable.flowtable_conf(if_2_sw_if_index, portmirroring_index, 0) ++print(r) ++ ++r = vpp.disconnect() ++exit(r) +-- +2.10.0.rc1.15.g5cb0d5a + diff --git a/flowtable/README.md b/flowtable/README.md new file mode 100644 index 0000000..03c4a04 --- /dev/null +++ b/flowtable/README.md @@ -0,0 +1,74 @@ +# A flowtable plugin + +Provides a flowtable node to do flow classification, and associate a flow +context that can be enriched as needed by another node or an external +application. The objective is to be adaptable so as to be used for any +statefull use such as load-balancing, firewalling ... + +Compared to the classifier, it stores a flow context, which changes the following: +- a flow context (which can be updated with external informations) +- it can offload +- flows have a lifetime + +## Build/Install + +Patches summary: + - The flowtable plugin: 0001-Add-flowtable-feature.patch + - Example nodeusing the flowtable: 0002-Add-Port-Mirroring-feature.patch + - The est scripts I used: 0003-test-scripts.patch + + +The tests scripts are used are under ft-test. +They tests the functionality versus the classifier. + +Apply the patches to vpp: +``` +git am -3 0001-Add-flowtable-feature.patch +git am -3 0002-Add-Port-Mirroring-feature.patch +git am -3 0003-test-scripts.patch +``` + +After that, you shoud be able to build the plugins as part of the make *plugins* +target as usual: +``` +make bootstrap +make build +make build-vpp-api +make plugins +``` + +## Test + +In itself, the flowtable only provides a context and an API. +I added a simple port-mirroring as a test. + +I might add some other test examples if needed. + +## Administrative + +### Current status + +Currently : +- Default behavior is to connect transparently to given interface. +- Can reroute packets to given node +- Can receive additional information +- Can offload sessions +- Only support IP packets + +I'm still working on this, do not support multithreading, and haven't +gone through performance tests yet. + +Planned : +- improve flow statistics, and add a way to get them back at expiration time +- improve timer management (tcp sm state) + +### Objective + +The objective of this project is to provide a stateful node with flow-level +API, available for consecutive nodes or external applications. + +### Main contributors + +- Gabriel Ganne - gabriel.ganne@qosmos.com +- Christophe Fontaine - christophe.fontaine@qosmos.com + (https://github.com/christophefontaine/flowtable-plugin) |