0001-Add-flowtable-feature.patch revision 55fd743f
1From aad927d0274e060e8b3b95fcfd543bad61140987 Mon Sep 17 00:00:00 2001
2From: Gabriel Ganne <gabriel.ganne@qosmos.com>
3Date: Sat, 17 Sep 2016 10:05:42 +0200
4Subject: [PATCH 1/3] Add flowtable feature
5
6flowtable is under /plugins
7
8Default behavior is to connect transparently to given interface.
9Can reroute packets to given node
10Can receive additional informations, and, if set offload session
11
12cli :
13 *  flowable <intf-name> [next-node <name>] [disable]
14
15Add API :
16 * to configure flowtable
17 * to update flow information with any external data
18
19As advised in the thread below :
20https://lists.fd.io/pipermail/vpp-dev/2016-October/002787.html
21hashtable is configured to alloc (NUM_BUCKETS * CLIB_CACHE_LINE_BYTES) Bytes
22with (flow_count / (BIHASH_KVP_PER_PAGE / 2)) Buckets
23
24Default flow_count set to 1M
25
26Author: Christophe Fontaine <christophe.fontaine@qosmos.com>
27Author: Gabriel Ganne <gabriel.ganne@qosmos.com>
28
29Signed-off-by: Gabriel Ganne <gabriel.ganne@qosmos.com>
30Change-Id: Ibb04917fbfdff84bfc0ffd4b64434e574ef4ae3d
31---
32 plugins/Makefile.am                                |   4 +
33 plugins/configure.ac                               |   1 +
34 plugins/flowtable-plugin/LICENSE                   | 201 ++++++++
35 plugins/flowtable-plugin/Makefile.am               |  55 +++
36 plugins/flowtable-plugin/configure.ac              |   9 +
37 plugins/flowtable-plugin/flowtable/api.c           | 169 +++++++
38 plugins/flowtable-plugin/flowtable/flowdata.h      |  35 ++
39 plugins/flowtable-plugin/flowtable/flowtable.api   |  59 +++
40 plugins/flowtable-plugin/flowtable/flowtable.c     |  86 ++++
41 plugins/flowtable-plugin/flowtable/flowtable.h     | 173 +++++++
42 .../flowtable-plugin/flowtable/flowtable_node.c    | 535 +++++++++++++++++++++
43 .../flowtable/nodes_registration.c                 |  80 +++
44 12 files changed, 1407 insertions(+)
45 create mode 100644 plugins/flowtable-plugin/LICENSE
46 create mode 100644 plugins/flowtable-plugin/Makefile.am
47 create mode 100644 plugins/flowtable-plugin/configure.ac
48 create mode 100644 plugins/flowtable-plugin/flowtable/api.c
49 create mode 100644 plugins/flowtable-plugin/flowtable/flowdata.h
50 create mode 100644 plugins/flowtable-plugin/flowtable/flowtable.api
51 create mode 100644 plugins/flowtable-plugin/flowtable/flowtable.c
52 create mode 100644 plugins/flowtable-plugin/flowtable/flowtable.h
53 create mode 100644 plugins/flowtable-plugin/flowtable/flowtable_node.c
54 create mode 100644 plugins/flowtable-plugin/flowtable/nodes_registration.c
55
56diff --git a/plugins/Makefile.am b/plugins/Makefile.am
57index 5293e6e..d0a2e3c 100644
58--- a/plugins/Makefile.am
59+++ b/plugins/Makefile.am
60@@ -51,3 +51,7 @@ endif
61 if ENABLE_lb_PLUGIN
62 SUBDIRS += lb-plugin
63 endif
64+
65+if ENABLE_flowtable_PLUGIN
66+SUBDIRS += flowtable-plugin
67+endif
68diff --git a/plugins/configure.ac b/plugins/configure.ac
69index 6ee064e..cbb180f 100644
70--- a/plugins/configure.ac
71+++ b/plugins/configure.ac
72@@ -58,6 +58,7 @@ PLUGIN_ENABLED(ioam)
73 PLUGIN_ENABLED(snat)
74 PLUGIN_ENABLED(ila)
75 PLUGIN_ENABLED(lb)
76+PLUGIN_ENABLED(flowtable)
77 
78 # Disabled plugins, require --enable-XXX-plugin
79 PLUGIN_DISABLED(vcgn)
80diff --git a/plugins/flowtable-plugin/LICENSE b/plugins/flowtable-plugin/LICENSE
81new file mode 100644
82index 0000000..8dada3e
83--- /dev/null
84+++ b/plugins/flowtable-plugin/LICENSE
85@@ -0,0 +1,201 @@
86+                                 Apache License
87+                           Version 2.0, January 2004
88+                        http://www.apache.org/licenses/
89+
90+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
91+
92+   1. Definitions.
93+
94+      "License" shall mean the terms and conditions for use, reproduction,
95+      and distribution as defined by Sections 1 through 9 of this document.
96+
97+      "Licensor" shall mean the copyright owner or entity authorized by
98+      the copyright owner that is granting the License.
99+
100+      "Legal Entity" shall mean the union of the acting entity and all
101+      other entities that control, are controlled by, or are under common
102+      control with that entity. For the purposes of this definition,
103+      "control" means (i) the power, direct or indirect, to cause the
104+      direction or management of such entity, whether by contract or
105+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
106+      outstanding shares, or (iii) beneficial ownership of such entity.
107+
108+      "You" (or "Your") shall mean an individual or Legal Entity
109+      exercising permissions granted by this License.
110+
111+      "Source" form shall mean the preferred form for making modifications,
112+      including but not limited to software source code, documentation
113+      source, and configuration files.
114+
115+      "Object" form shall mean any form resulting from mechanical
116+      transformation or translation of a Source form, including but
117+      not limited to compiled object code, generated documentation,
118+      and conversions to other media types.
119+
120+      "Work" shall mean the work of authorship, whether in Source or
121+      Object form, made available under the License, as indicated by a
122+      copyright notice that is included in or attached to the work
123+      (an example is provided in the Appendix below).
124+
125+      "Derivative Works" shall mean any work, whether in Source or Object
126+      form, that is based on (or derived from) the Work and for which the
127+      editorial revisions, annotations, elaborations, or other modifications
128+      represent, as a whole, an original work of authorship. For the purposes
129+      of this License, Derivative Works shall not include works that remain
130+      separable from, or merely link (or bind by name) to the interfaces of,
131+      the Work and Derivative Works thereof.
132+
133+      "Contribution" shall mean any work of authorship, including
134+      the original version of the Work and any modifications or additions
135+      to that Work or Derivative Works thereof, that is intentionally
136+      submitted to Licensor for inclusion in the Work by the copyright owner
137+      or by an individual or Legal Entity authorized to submit on behalf of
138+      the copyright owner. For the purposes of this definition, "submitted"
139+      means any form of electronic, verbal, or written communication sent
140+      to the Licensor or its representatives, including but not limited to
141+      communication on electronic mailing lists, source code control systems,
142+      and issue tracking systems that are managed by, or on behalf of, the
143+      Licensor for the purpose of discussing and improving the Work, but
144+      excluding communication that is conspicuously marked or otherwise
145+      designated in writing by the copyright owner as "Not a Contribution."
146+
147+      "Contributor" shall mean Licensor and any individual or Legal Entity
148+      on behalf of whom a Contribution has been received by Licensor and
149+      subsequently incorporated within the Work.
150+
151+   2. Grant of Copyright License. Subject to the terms and conditions of
152+      this License, each Contributor hereby grants to You a perpetual,
153+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
154+      copyright license to reproduce, prepare Derivative Works of,
155+      publicly display, publicly perform, sublicense, and distribute the
156+      Work and such Derivative Works in Source or Object form.
157+
158+   3. Grant of Patent License. Subject to the terms and conditions of
159+      this License, each Contributor hereby grants to You a perpetual,
160+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
161+      (except as stated in this section) patent license to make, have made,
162+      use, offer to sell, sell, import, and otherwise transfer the Work,
163+      where such license applies only to those patent claims licensable
164+      by such Contributor that are necessarily infringed by their
165+      Contribution(s) alone or by combination of their Contribution(s)
166+      with the Work to which such Contribution(s) was submitted. If You
167+      institute patent litigation against any entity (including a
168+      cross-claim or counterclaim in a lawsuit) alleging that the Work
169+      or a Contribution incorporated within the Work constitutes direct
170+      or contributory patent infringement, then any patent licenses
171+      granted to You under this License for that Work shall terminate
172+      as of the date such litigation is filed.
173+
174+   4. Redistribution. You may reproduce and distribute copies of the
175+      Work or Derivative Works thereof in any medium, with or without
176+      modifications, and in Source or Object form, provided that You
177+      meet the following conditions:
178+
179+      (a) You must give any other recipients of the Work or
180+          Derivative Works a copy of this License; and
181+
182+      (b) You must cause any modified files to carry prominent notices
183+          stating that You changed the files; and
184+
185+      (c) You must retain, in the Source form of any Derivative Works
186+          that You distribute, all copyright, patent, trademark, and
187+          attribution notices from the Source form of the Work,
188+          excluding those notices that do not pertain to any part of
189+          the Derivative Works; and
190+
191+      (d) If the Work includes a "NOTICE" text file as part of its
192+          distribution, then any Derivative Works that You distribute must
193+          include a readable copy of the attribution notices contained
194+          within such NOTICE file, excluding those notices that do not
195+          pertain to any part of the Derivative Works, in at least one
196+          of the following places: within a NOTICE text file distributed
197+          as part of the Derivative Works; within the Source form or
198+          documentation, if provided along with the Derivative Works; or,
199+          within a display generated by the Derivative Works, if and
200+          wherever such third-party notices normally appear. The contents
201+          of the NOTICE file are for informational purposes only and
202+          do not modify the License. You may add Your own attribution
203+          notices within Derivative Works that You distribute, alongside
204+          or as an addendum to the NOTICE text from the Work, provided
205+          that such additional attribution notices cannot be construed
206+          as modifying the License.
207+
208+      You may add Your own copyright statement to Your modifications and
209+      may provide additional or different license terms and conditions
210+      for use, reproduction, or distribution of Your modifications, or
211+      for any such Derivative Works as a whole, provided Your use,
212+      reproduction, and distribution of the Work otherwise complies with
213+      the conditions stated in this License.
214+
215+   5. Submission of Contributions. Unless You explicitly state otherwise,
216+      any Contribution intentionally submitted for inclusion in the Work
217+      by You to the Licensor shall be under the terms and conditions of
218+      this License, without any additional terms or conditions.
219+      Notwithstanding the above, nothing herein shall supersede or modify
220+      the terms of any separate license agreement you may have executed
221+      with Licensor regarding such Contributions.
222+
223+   6. Trademarks. This License does not grant permission to use the trade
224+      names, trademarks, service marks, or product names of the Licensor,
225+      except as required for reasonable and customary use in describing the
226+      origin of the Work and reproducing the content of the NOTICE file.
227+
228+   7. Disclaimer of Warranty. Unless required by applicable law or
229+      agreed to in writing, Licensor provides the Work (and each
230+      Contributor provides its Contributions) on an "AS IS" BASIS,
231+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
232+      implied, including, without limitation, any warranties or conditions
233+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
234+      PARTICULAR PURPOSE. You are solely responsible for determining the
235+      appropriateness of using or redistributing the Work and assume any
236+      risks associated with Your exercise of permissions under this License.
237+
238+   8. Limitation of Liability. In no event and under no legal theory,
239+      whether in tort (including negligence), contract, or otherwise,
240+      unless required by applicable law (such as deliberate and grossly
241+      negligent acts) or agreed to in writing, shall any Contributor be
242+      liable to You for damages, including any direct, indirect, special,
243+      incidental, or consequential damages of any character arising as a
244+      result of this License or out of the use or inability to use the
245+      Work (including but not limited to damages for loss of goodwill,
246+      work stoppage, computer failure or malfunction, or any and all
247+      other commercial damages or losses), even if such Contributor
248+      has been advised of the possibility of such damages.
249+
250+   9. Accepting Warranty or Additional Liability. While redistributing
251+      the Work or Derivative Works thereof, You may choose to offer,
252+      and charge a fee for, acceptance of support, warranty, indemnity,
253+      or other liability obligations and/or rights consistent with this
254+      License. However, in accepting such obligations, You may act only
255+      on Your own behalf and on Your sole responsibility, not on behalf
256+      of any other Contributor, and only if You agree to indemnify,
257+      defend, and hold each Contributor harmless for any liability
258+      incurred by, or claims asserted against, such Contributor by reason
259+      of your accepting any such warranty or additional liability.
260+
261+   END OF TERMS AND CONDITIONS
262+
263+   APPENDIX: How to apply the Apache License to your work.
264+
265+      To apply the Apache License to your work, attach the following
266+      boilerplate notice, with the fields enclosed by brackets "{}"
267+      replaced with your own identifying information. (Don't include
268+      the brackets!)  The text should be enclosed in the appropriate
269+      comment syntax for the file format. We also recommend that a
270+      file or class name and description of purpose be included on the
271+      same "printed page" as the copyright notice for easier
272+      identification within third-party archives.
273+
274+   Copyright {yyyy} {name of copyright owner}
275+
276+   Licensed under the Apache License, Version 2.0 (the "License");
277+   you may not use this file except in compliance with the License.
278+   You may obtain a copy of the License at
279+
280+       http://www.apache.org/licenses/LICENSE-2.0
281+
282+   Unless required by applicable law or agreed to in writing, software
283+   distributed under the License is distributed on an "AS IS" BASIS,
284+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
285+   See the License for the specific language governing permissions and
286+   limitations under the License.
287diff --git a/plugins/flowtable-plugin/Makefile.am b/plugins/flowtable-plugin/Makefile.am
288new file mode 100644
289index 0000000..fda9b81
290--- /dev/null
291+++ b/plugins/flowtable-plugin/Makefile.am
292@@ -0,0 +1,55 @@
293+# Copyright (c) 2015 Cisco and/or its affiliates.
294+# Licensed under the Apache License, Version 2.0 (the "License");
295+# you may not use this file except in compliance with the License.
296+# You may obtain a copy of the License at:
297+#
298+#     http://www.apache.org/licenses/LICENSE-2.0
299+#
300+# Unless required by applicable law or agreed to in writing, software
301+# distributed under the License is distributed on an "AS IS" BASIS,
302+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
303+# See the License for the specific language governing permissions and
304+# limitations under the License.
305+
306+AUTOMAKE_OPTIONS = foreign subdir-objects
307+
308+AM_CFLAGS = -Wall
309+AM_LDFLAGS = -module -shared -avoid-version
310+
311+#vppapitestpluginsdir = ${libdir}/vpp_api_test_plugins
312+vpppluginsdir = ${libdir}/vpp_plugins
313+
314+vppplugins_LTLIBRARIES = flowtable_plugin.la
315+
316+flowtable_plugin_la_SOURCES = \
317+	flowtable/api.c \
318+	flowtable/flowtable.c \
319+	flowtable/flowtable_node.c \
320+	flowtable/nodes_registration.c
321+
322+nobase_include_HEADERS = \
323+	flowtable/flowtable.h \
324+	flowtable/flowdata.h \
325+	flowtable/flowtable.api.h 
326+
327+BUILT_SOURCES = flowtable/flowtable.api.h flowtable/flowtable.py
328+
329+SUFFIXES = .api.h .api
330+
331+%.api.h: %.api
332+	mkdir -p `dirname $@` ; \
333+	$(CC) $(CPPFLAGS) -E -P -C -x c $^ \
334+	| vppapigen --input - --output $@ --show-name $@
335+
336+%.py: %.api
337+	$(info Creating Python binding for $@)
338+	$(CC) $(CPPFLAGS) -E -P -C -x c $<              \
339+	| vppapigen --input - --python -                \
340+	| pyvppapigen.py --input - > $@
341+
342+
343+pyapidir = ${prefix}/vpp_papi_plugins
344+pyapi_DATA = flowtable/flowtable.py
345+
346+install-data-hook:
347+	@(cd $(vpppluginsdir) && $(RM) $(vppplugins_LTLIBRARIES))
348diff --git a/plugins/flowtable-plugin/configure.ac b/plugins/flowtable-plugin/configure.ac
349new file mode 100644
350index 0000000..684569c
351--- /dev/null
352+++ b/plugins/flowtable-plugin/configure.ac
353@@ -0,0 +1,9 @@
354+AC_INIT(flowtable_plugin, 1.0)
355+LT_INIT
356+AM_INIT_AUTOMAKE
357+AM_SILENT_RULES([yes])
358+AC_PREFIX_DEFAULT([/usr])
359+
360+AC_PROG_CC
361+
362+AC_OUTPUT([Makefile])
363diff --git a/plugins/flowtable-plugin/flowtable/api.c b/plugins/flowtable-plugin/flowtable/api.c
364new file mode 100644
365index 0000000..cad2a8b
366--- /dev/null
367+++ b/plugins/flowtable-plugin/flowtable/api.c
368@@ -0,0 +1,169 @@
369+/*
370+ * Copyright (c) 2016 Cisco and/or its affiliates.
371+ * Licensed under the Apache License, Version 2.0 (the "License");
372+ * you may not use this file except in compliance with the License.
373+ * You may obtain a copy of the License at:
374+ *
375+ *     http://www.apache.org/licenses/LICENSE-2.0
376+ *
377+ * Unless required by applicable law or agreed to in writing, software
378+ * distributed under the License is distributed on an "AS IS" BASIS,
379+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
380+ * See the License for the specific language governing permissions and
381+ * limitations under the License.
382+ */
383+
384+#include <flowtable/flowtable.h>
385+
386+#include <vppinfra/byte_order.h>
387+#include <vlibapi/api.h>
388+#include <vlibmemory/api.h>
389+#include <vlibsocket/api.h>
390+
391+#define vl_msg_id(n,h) n,
392+typedef enum {
393+#include <flowtable/flowtable.api.h>
394+    /* We'll want to know how many messages IDs we need... */
395+    VL_MSG_FIRST_AVAILABLE,
396+} vl_msg_id_t;
397+#undef vl_msg_id
398+
399+
400+/* define message structures */
401+#define vl_typedefs
402+#include <flowtable/flowtable.api.h>
403+#undef vl_typedefs
404+
405+/* define generated endian-swappers */
406+#define vl_endianfun
407+#include <flowtable/flowtable.api.h>
408+#undef vl_endianfun
409+
410+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
411+
412+/* Get the API version number */
413+#define vl_api_version(n,v) static u32 api_version=(v);
414+#include <flowtable/flowtable.api.h>
415+#undef vl_api_version
416+
417+/* Macro to finish up custom dump fns */
418+#define FINISH                                  \
419+    vec_add1 (s, 0);                            \
420+    vl_print (handle, (char *)s);               \
421+    vec_free (s);                               \
422+    return handle;
423+
424+/*
425+ * A handy macro to set up a message reply.
426+ * Assumes that the following variables are available:
427+ * mp - pointer to request message
428+ * rmp - pointer to reply message type
429+ * rv - return value
430+ */
431+
432+#define REPLY_MACRO(t)                                          \
433+do {                                                            \
434+    unix_shared_memory_queue_t * q =                            \
435+    vl_api_client_index_to_input_queue (mp->client_index);      \
436+    if (!q)                                                     \
437+        return;                                                 \
438+                                                                \
439+    rmp = vl_msg_api_alloc (sizeof (*rmp));                     \
440+    rmp->_vl_msg_id = ntohs((t)+ftm->msg_id_base);              \
441+    rmp->context = mp->context;                                 \
442+    rmp->retval = ntohl(rv);                                    \
443+                                                                \
444+    vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
445+} while(0);
446+
447+static void
448+vl_api_flowtable_conf_t_handler (vl_api_flowtable_conf_t * mp)
449+{
450+    flowtable_main_t *ftm = &flowtable_main;
451+    vl_api_flowtable_conf_reply_t * rmp;
452+    int rv;
453+
454+    if (mp->next_node_index == 0xff)
455+        ftm->next_node_index = FT_NEXT_ETHERNET_INPUT;
456+    else
457+        ftm->next_node_index = mp->next_node_index;
458+
459+    rv = flowtable_enable_disable(ftm, mp->sw_if_index, ! mp->enable_disable);
460+
461+    REPLY_MACRO (VL_API_FLOWTABLE_CONF_REPLY);
462+}
463+
464+static void *
465+vl_api_flowtable_conf_t_print (vl_api_flowtable_conf_t *mp, void * handle)
466+{
467+    u8 * s;
468+    s = format (0, "SCRIPT: flowtable_conf ");
469+
470+    s = format (s, "%u ", mp->sw_if_index);
471+    s = format (s, "%u ", mp->enable_disable);
472+
473+    FINISH;
474+}
475+
476+static void
477+vl_api_flowtable_update_t_handler (vl_api_flowtable_update_t * mp)
478+{
479+    int rv;
480+    flowtable_main_t *ftm = &flowtable_main;
481+    vl_api_flowtable_update_reply_t * rmp;
482+
483+    rv = flowtable_update(mp->is_ip4, mp->ip_src, mp->ip_dst,
484+            mp->ip_upper_proto, mp->port_src, mp->port_dst,
485+            mp->lifetime, mp->offloaded, mp->infos);
486+
487+    REPLY_MACRO (VL_API_FLOWTABLE_UPDATE_REPLY);
488+}
489+
490+static void *
491+vl_api_flowtable_update_t_print (vl_api_flowtable_update_t *mp, void * handle)
492+{
493+    u8 * s;
494+    s = format (0, "SCRIPT: flowtable_update ");
495+
496+    s = format (s, "%u ", mp->is_ip4);
497+    s = format (s, "%s ", mp->ip_src);
498+    s = format (s, "%s ", mp->ip_dst);
499+    s = format (s, "%u ", mp->ip_upper_proto);
500+    s = format (s, "%u ", mp->port_src);
501+    s = format (s, "%u ", mp->port_dst);
502+    s = format (s, "%u ", mp->lifetime);
503+    s = format (s, "%u ", mp->offloaded);
504+    s = format (s, "%s ", mp->infos);
505+
506+    FINISH;
507+}
508+
509+/* List of message types that this plugin understands */
510+#define foreach_flowtable_plugin_api_msg    \
511+    _(FLOWTABLE_CONF, flowtable_conf)       \
512+    _(FLOWTABLE_UPDATE, flowtable_update)   \
513+
514+
515+static clib_error_t * flowtable_api_init (vlib_main_t * vm)
516+{
517+  flowtable_main_t *ftm = &flowtable_main;
518+  u8 *name = format (0, "flowtable_%08x%c", api_version, 0);
519+  ftm->msg_id_base = vl_msg_api_get_msg_ids
520+      ((char *) name, VL_MSG_FIRST_AVAILABLE);
521+
522+#define _(N,n)                                                  \
523+    vl_msg_api_set_handlers((VL_API_##N + ftm->msg_id_base),    \
524+                           #n,                                  \
525+                           vl_api_##n##_t_handler,              \
526+                           vl_noop_handler,                     \
527+                           vl_api_##n##_t_endian,               \
528+                           vl_api_##n##_t_print,                \
529+                           sizeof(vl_api_##n##_t), 1);
530+  foreach_flowtable_plugin_api_msg;
531+#undef _
532+
533+  return 0;
534+}
535+
536+VLIB_INIT_FUNCTION (flowtable_api_init);
537+
538diff --git a/plugins/flowtable-plugin/flowtable/flowdata.h b/plugins/flowtable-plugin/flowtable/flowdata.h
539new file mode 100644
540index 0000000..9624039
541--- /dev/null
542+++ b/plugins/flowtable-plugin/flowtable/flowdata.h
543@@ -0,0 +1,35 @@
544+/*---------------------------------------------------------------------------
545+ * Copyright (c) 2016 Qosmos and/or its affiliates.
546+ * Licensed under the Apache License, Version 2.0 (the "License");
547+ * you may not use this file except in compliance with the License.
548+ * You may obtain a copy of the License at:
549+ *
550+ *     http://www.apache.org/licenses/LICENSE-2.0
551+ *
552+ * Unless required by applicable law or agreed to in writing, software
553+ * distributed under the License is distributed on an "AS IS" BASIS,
554+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
555+ * See the License for the specific language governing permissions and
556+ * limitations under the License.
557+ *---------------------------------------------------------------------------
558+ */
559+
560+#ifndef __flowdata_h__
561+#define __flowdata_h__
562+
563+#include <vnet/vnet.h>
564+
565+/* the following union will be copied to vlib->opaque
566+ * it MUST be less or equal CLIB_CACHE_LINE_BYTES */
567+typedef union {
568+    struct {
569+        u32 sw_if_index_current;
570+        u8 offloaded;
571+
572+        u8 opaque[27];
573+    } data;
574+
575+    u32 flow_data[8]; /* 32 Bytes == sizeof vlib_buffer_t's opaque field */
576+} flow_data_t;
577+
578+#endif /* __flowdata_h__ */
579diff --git a/plugins/flowtable-plugin/flowtable/flowtable.api b/plugins/flowtable-plugin/flowtable/flowtable.api
580new file mode 100644
581index 0000000..d0797de
582--- /dev/null
583+++ b/plugins/flowtable-plugin/flowtable/flowtable.api
584@@ -0,0 +1,59 @@
585+/** \brief hook flowtable configuration
586+    @param client_index - opaque cookie to identify the sender
587+    @param context - sender context, to match reply w/ request
588+    @param sw_if_index - interface index
589+    @param next_node_index - (optional) redirect packets to given node, or 0xff for default
590+    @param enable_disable - 0/1 set to disable
591+*/
592+define flowtable_conf
593+{
594+    u32 client_index;
595+    u32 context;
596+    u8 sw_if_index;
597+    u8 next_node_index;
598+    u8 enable_disable;
599+};
600+
601+define flowtable_conf_reply
602+{
603+    u32 context;
604+    i32 retval;
605+};
606+
607+/** \brief send additional informations to the flowtable
608+    @param client_index - opaque cookie to identify the sender
609+    @param context - sender context, to match reply w/ request
610+    @param is_ip4 - 0/1 ip version
611+    @param ip_src - source ip address
612+    @param ip_dst - destination ip address
613+    @param ip_upper_proto - tcp or udp
614+    @param port_src - source port
615+    @param port_dst - destination port
616+    @param lifetime - time to live (~0 for left untouched)
617+    @param offloaded - offloading status
618+    @param infos - external infos (opaque field)
619+*/
620+define flowtable_update
621+{
622+    u32 client_index;
623+    u32 context;
624+
625+    /* used to compute flow key */
626+    u8 is_ip4;
627+    u8 ip_src[16];
628+    u8 ip_dst[16];
629+    u8 ip_upper_proto;
630+    u16 port_src;
631+    u16 port_dst;
632+
633+    /* additional flow informations */
634+    u16 lifetime;
635+    u8 offloaded;
636+    u8 infos[27];
637+};
638+
639+define flowtable_update_reply
640+{
641+    u32 context;
642+    i32 retval;
643+};
644diff --git a/plugins/flowtable-plugin/flowtable/flowtable.c b/plugins/flowtable-plugin/flowtable/flowtable.c
645new file mode 100644
646index 0000000..ce769a3
647--- /dev/null
648+++ b/plugins/flowtable-plugin/flowtable/flowtable.c
649@@ -0,0 +1,86 @@
650+/*
651+ * Copyright (c) 2016 Qosmos and/or its affiliates.
652+ * Licensed under the Apache License, Version 2.0 (the "License");
653+ * you may not use this file except in compliance with the License.
654+ * You may obtain a copy of the License at:
655+ *
656+ *     http://www.apache.org/licenses/LICENSE-2.0
657+ *
658+ * Unless required by applicable law or agreed to in writing, software
659+ * distributed under the License is distributed on an "AS IS" BASIS,
660+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
661+ * See the License for the specific language governing permissions and
662+ * limitations under the License.
663+ */
664+
665+#include "flowtable.h"
666+#include <vnet/plugin/plugin.h>
667+
668+flowtable_main_t flowtable_main;
669+
670+int
671+flowtable_enable_disable(flowtable_main_t * fm,
672+    u32 sw_if_index, int enable_disable)
673+{
674+    u32 node_index = enable_disable ? flowtable_node.index : ~0;
675+
676+    return vnet_hw_interface_rx_redirect_to_node(fm->vnet_main,
677+            sw_if_index, node_index);
678+}
679+
680+static clib_error_t *
681+flowtable_enable_disable_command_fn(vlib_main_t * vm,
682+    unformat_input_t * input, vlib_cli_command_t * cmd)
683+{
684+    flowtable_main_t * fm = &flowtable_main;
685+    u32 sw_if_index = ~0;
686+    int enable_disable = 1;
687+    u32 next_node_index = ~0;
688+
689+    int rv;
690+
691+    while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT)
692+    {
693+        if (unformat(input, "disable"))
694+            enable_disable = 0;
695+        else if (unformat(input, "next-node %U", unformat_vlib_node,
696+                    fm->vnet_main, &next_node_index))
697+            ;
698+        else if (unformat(input, "%U", unformat_vnet_sw_interface,
699+            fm->vnet_main, &sw_if_index))
700+            ;
701+        else
702+            break;
703+    }
704+
705+    if (sw_if_index == ~0)
706+        return clib_error_return(0, "No Interface specified");
707+
708+    /* by default, leave the packet follow its course */
709+    if (next_node_index != ~0)
710+        fm->next_node_index = next_node_index;
711+    else
712+        fm->next_node_index = FT_NEXT_ETHERNET_INPUT;
713+
714+    rv = flowtable_enable_disable(fm, sw_if_index, enable_disable);
715+    switch (rv) {
716+      case 0:
717+          break;
718+      case VNET_API_ERROR_INVALID_SW_IF_INDEX:
719+          return clib_error_return(0, "Invalid interface");
720+      case VNET_API_ERROR_UNIMPLEMENTED:
721+          return clib_error_return(0,
722+                "Device driver doesn't support redirection");
723+      default:
724+          return clib_error_return(0, "flowtable_enable_disable returned %d",
725+                rv);
726+    }
727+
728+    return 0;
729+}
730+
731+VLIB_CLI_COMMAND(flowtable_interface_enable_disable_command) = {
732+    .path = "flowtable",
733+    .short_help = "flowtable <interface> [next-node <name>] [disable]",
734+    .function = flowtable_enable_disable_command_fn,
735+};
736diff --git a/plugins/flowtable-plugin/flowtable/flowtable.h b/plugins/flowtable-plugin/flowtable/flowtable.h
737new file mode 100644
738index 0000000..faa7410
739--- /dev/null
740+++ b/plugins/flowtable-plugin/flowtable/flowtable.h
741@@ -0,0 +1,173 @@
742+/*---------------------------------------------------------------------------
743+ * Copyright (c) 2016 Qosmos and/or its affiliates.
744+ * Licensed under the Apache License, Version 2.0 (the "License");
745+ * you may not use this file except in compliance with the License.
746+ * You may obtain a copy of the License at:
747+ *
748+ *     http://www.apache.org/licenses/LICENSE-2.0
749+ *
750+ * Unless required by applicable law or agreed to in writing, software
751+ * distributed under the License is distributed on an "AS IS" BASIS,
752+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
753+ * See the License for the specific language governing permissions and
754+ * limitations under the License.
755+ *---------------------------------------------------------------------------
756+ */
757+
758+#ifndef __flowtable_h__
759+#define __flowtable_h__
760+
761+#include <stdbool.h>
762+#include <vppinfra/error.h>
763+#include <vnet/vnet.h>
764+#include <vnet/ip/ip.h>
765+#include <vppinfra/bihash_8_8.h>
766+#include <vppinfra/dlist.h>
767+#include <vppinfra/pool.h>
768+#include <vppinfra/vec.h>
769+
770+#include "flowdata.h"
771+
772+#define foreach_flowtable_error                       \
773+    _(HIT, "packets with an existing flow")           \
774+    _(THRU, "packets gone through")                   \
775+    _(CREATED, "packets which created a new flow")    \
776+    _(OFFLOADED, "packets which have been offloaded") \
777+    _(ALLOC_ERROR, "failed to allocate flow")         \
778+    _(TIMER_EXPIRE, "flows that have expired")        \
779+    _(COLLISION, "hashtable collisions")
780+
781+typedef enum {
782+#define _(sym, str) FLOWTABLE_ERROR_##sym,
783+    foreach_flowtable_error
784+#undef _
785+    FLOWTABLE_N_ERROR
786+} flowtable_error_t;
787+
788+
789+typedef enum {
790+    FT_NEXT_DROP,
791+    FT_NEXT_ETHERNET_INPUT,
792+    FT_NEXT_N_NEXT
793+} flowtable_next_t;
794+
795+/* signatures */
796+struct ip6_sig {
797+    ip6_address_t src, dst;
798+    u8 proto;
799+    u16 port_src, port_dst;
800+} __attribute__ ((packed));
801+struct ip4_sig {
802+    ip4_address_t src, dst;
803+    u8 proto;
804+    u16 port_src, port_dst;
805+} __attribute__ ((packed));
806+
807+typedef union {
808+    struct ip6_sig ip6;
809+    struct ip4_sig ip4;
810+    u8 data[0]; /* gcc will take the max */
811+} signature;
812+
813+/* dlist helpers */
814+#define dlist_is_head(node) ((node)->value == (u32) ~0)
815+#define dlist_is_empty(pool, head_index)                          \
816+({                                                                \
817+    dlist_elt_t *head = pool_elt_at_index ((pool), (head_index)); \
818+    (head->next == (u32) ~0 || head->next == (head_index));       \
819+})
820+
821+/* flow helpers */
822+#define flow_is_offloaded(f) ((f)->infos.data.offloaded)
823+
824+typedef struct {
825+    u32 straight;
826+    u32 reverse;
827+} flow_stats_t;
828+
829+typedef struct flow_entry
830+{
831+    /* flow signature */
832+    u32 sig_len;
833+    signature sig;
834+    u64 sig_hash; /* used to delete hashtable entries */
835+
836+    /* hashtable */
837+    u32 ht_line_index; /* index of the list head of the line in the hashtable */
838+    u32 ht_index; /* index in the hashtable line pool */
839+
840+    /* stats */
841+    flow_stats_t stats;
842+
843+    /* timers */
844+    u32 expire; /* in seconds */
845+    u32 lifetime; /* in seconds */
846+    u32 timer_index; /* index in the timer pool */
847+
848+    /* the following union will be copied to vlib->opaque
849+     * it MUST be less or equal CLIB_CACHE_LINE_BYTES */
850+    flow_data_t infos;
851+} flow_entry_t;
852+
853+/* TODO improve timer duration (tcp sm state) */
854+
855+/* Timers (in seconds) */
856+#define TIMER_DEFAULT_LIFETIME (60)
857+#define TIMER_MAX_LIFETIME (300)
858+
859+/* Default max number of flows to expire during one run.
860+ * 256 is the max number of packets in a vector, so this is a minimum 
861+ * if all packets create a flow. */
862+#define TIMER_MAX_EXPIRE (256)
863+
864+typedef struct {
865+    /* flow entry pool */
866+    flow_entry_t * flows;
867+
868+    /* hashtable */
869+    BVT(clib_bihash) flows_ht;
870+    dlist_elt_t * ht_lines;
871+    u64 flows_cpt;
872+
873+    /* flowtable node index */
874+    u32 flowtable_index;
875+
876+    /* timers */
877+    dlist_elt_t * timers;
878+    u32 * timer_wheel;
879+    u32 time_index;
880+
881+    /* convenience */
882+    vlib_main_t * vlib_main;
883+    vnet_main_t * vnet_main;
884+
885+    /* next-node of flowtable node, NOT pm node id */
886+    u32 next_node_index;
887+
888+    /* API dynamically registered base ID. */
889+    u16 msg_id_base;
890+} flowtable_main_t;
891+
892+extern flowtable_main_t flowtable_main;
893+
894+/*
895+ * As advised in the thread below :
896+ * https://lists.fd.io/pipermail/vpp-dev/2016-October/002787.html
897+ * hashtable is configured to alloc (NUM_BUCKETS * CLIB_CACHE_LINE_BYTES) Bytes
898+ * with (flow_count / (BIHASH_KVP_PER_PAGE / 2)) Buckets
899+ */
900+#define FM_POOL_COUNT_LOG2 20
901+#define FM_POOL_COUNT (1 << FM_POOL_COUNT_LOG2)
902+#define FM_NUM_BUCKETS (1 << (FM_POOL_COUNT_LOG2 - (BIHASH_KVP_PER_PAGE / 2)))
903+#define FM_MEMORY_SIZE (FM_NUM_BUCKETS * CLIB_CACHE_LINE_BYTES)
904+
905+
906+extern vlib_node_registration_t flowtable_node;
907+
908+/* API functions */
909+int flowtable_enable_disable(flowtable_main_t * fm, u32 sw_if_index, int enable_disable);
910+
911+int flowtable_update(u8 is_ip4, u8 ip_src[16], u8 ip_dst[16], u8 ip_upper_proto,
912+        u16 port_src, u16 port_dst, u16 lifetime, u8 offloaded, u8 infos[27]);
913+
914+#endif  /* __flowtable_h__ */
915diff --git a/plugins/flowtable-plugin/flowtable/flowtable_node.c b/plugins/flowtable-plugin/flowtable/flowtable_node.c
916new file mode 100644
917index 0000000..3a652df
918--- /dev/null
919+++ b/plugins/flowtable-plugin/flowtable/flowtable_node.c
920@@ -0,0 +1,535 @@
921+#include <vppinfra/dlist.h>
922+#include <vppinfra/types.h>
923+#include <vppinfra/vec.h>
924+#include <vnet/ip/ip4_packet.h>
925+
926+#include "flowtable.h"
927+
928+vlib_node_registration_t flowtable_node;
929+
930+
931+typedef struct {
932+    u32 sw_if_index;
933+    u32 next_index;
934+    u32 offloaded;
935+} flow_trace_t;
936+
937+static u8 *
938+format_get_flowinfo(u8 * s, va_list * args)
939+{
940+    CLIB_UNUSED(vlib_main_t * vm) = va_arg(*args, vlib_main_t *);
941+    CLIB_UNUSED(vlib_node_t * node) = va_arg(*args, vlib_node_t *);
942+    flow_trace_t * t = va_arg(*args, flow_trace_t *);
943+
944+    s = format(s, "FlowInfo - sw_if_index %d, next_index = %d, offload = %d",
945+            t->sw_if_index, t->next_index, t->offloaded);
946+    return s;
947+}
948+
949+/* TODO find a better hash function */
950+static inline u64
951+hash_signature(u8 is_ip4, signature const * sig)
952+{
953+    if (is_ip4) {
954+        return sig->ip4.src.as_u32 ^ sig->ip4.dst.as_u32 ^ sig->ip4.proto
955+            ^ sig->ip4.port_src ^ sig->ip4.port_dst;
956+    } else {
957+        return sig->ip6.dst.as_u64[0] ^ sig->ip6.dst.as_u64[1]
958+            ^ sig->ip6.src.as_u64[0] ^ sig->ip6.src.as_u64[1]
959+            ^ sig->ip4.port_src ^ sig->ip4.port_dst;
960+    }
961+}
962+
963+static inline u64
964+parse_ip4_packet(ip4_header_t * ip0, uword * is_reverse, struct ip4_sig *sig)
965+{
966+    sig->proto = ip0->protocol;
967+
968+    if (ip4_address_compare(&ip0->src_address, &ip0->dst_address) < 0) {
969+        sig->src = ip0->src_address;
970+        sig->dst = ip0->dst_address;
971+        *is_reverse = 1;
972+    } else {
973+        sig->src = ip0->dst_address;
974+        sig->dst = ip0->src_address;
975+    }
976+
977+    if (sig->proto == IP_PROTOCOL_UDP || sig->proto == IP_PROTOCOL_TCP) {
978+        /* tcp and udp ports have the same offset */
979+        udp_header_t * udp0 = (udp_header_t *) ip4_next_header(ip0);
980+        if (is_reverse == 0) {
981+            sig->port_src = udp0->src_port;
982+            sig->port_dst = udp0->dst_port;
983+        } else {
984+            sig->port_src = udp0->dst_port;
985+            sig->port_dst = udp0->src_port;
986+        }
987+    } else {
988+        sig->port_src = 0;
989+        sig->port_dst = 0;
990+    }
991+
992+    return hash_signature(1 /* is_ip4 */, (signature *) sig);
993+}
994+
995+static inline u64
996+parse_ip6_packet(ip6_header_t * ip60, uword * is_reverse, struct ip6_sig * sig)
997+{
998+    sig->proto = ip60->protocol;
999+
1000+    if (ip6_address_compare(&ip60->src_address, &ip60->dst_address) < 0) {
1001+        sig->src = ip60->src_address;
1002+        sig->dst = ip60->dst_address;
1003+        *is_reverse = 1;
1004+    } else {
1005+        sig->src = ip60->dst_address;
1006+        sig->dst = ip60->src_address;
1007+    }
1008+
1009+    if (sig->proto == IP_PROTOCOL_UDP || sig->proto == IP_PROTOCOL_TCP) {
1010+        /* tcp and udp ports have the same offset */
1011+        udp_header_t *udp0 = (udp_header_t *) ip6_next_header(ip60);
1012+        if (is_reverse == 0) {
1013+            sig->port_src = udp0->src_port;
1014+            sig->port_dst = udp0->dst_port;
1015+        } else {
1016+            sig->port_src = udp0->dst_port;
1017+            sig->port_dst = udp0->src_port;
1018+        }
1019+    } else {
1020+        sig->port_src = 0;
1021+        sig->port_dst = 0;
1022+    }
1023+
1024+    return hash_signature(0 /* is_ip4 */, (signature *) sig);
1025+}
1026+
1027+int
1028+flowtable_update(u8 is_ip4, u8 ip_src[16], u8 ip_dst[16], u8 ip_upper_proto,
1029+        u16 port_src, u16 port_dst, u16 lifetime, u8 offloaded, u8 infos[27])
1030+{
1031+    flowtable_main_t * fm = &flowtable_main;
1032+    u32 sig_len;
1033+    signature sig;
1034+    flow_entry_t *flow;
1035+    BVT(clib_bihash_kv) kv;
1036+
1037+    if (is_ip4) {
1038+        sig_len = sizeof(sig.ip4);
1039+        clib_memcpy (&sig.ip4.src, ip_src, 4);
1040+        clib_memcpy (&sig.ip4.dst, ip_dst, 4);
1041+        sig.ip4.proto = ip_upper_proto;
1042+        sig.ip4.port_src = port_src;
1043+        sig.ip4.port_dst = port_dst;
1044+
1045+    } else {
1046+        sig_len = sizeof(sig.ip6);
1047+        clib_memcpy (&sig.ip6.src, ip_src, 16);
1048+        clib_memcpy (&sig.ip6.dst, ip_dst, 16);
1049+        sig.ip6.proto = ip_upper_proto;
1050+        sig.ip6.port_src = port_src;
1051+        sig.ip6.port_dst = port_dst;
1052+    }
1053+
1054+    flow = NULL;
1055+    kv.key = hash_signature(is_ip4, &sig);
1056+    if (PREDICT_FALSE(BV(clib_bihash_search) (&fm->flows_ht, &kv, &kv))) {
1057+        return -1; /* flow not found */
1058+    } else {
1059+        dlist_elt_t * ht_line;
1060+        dlist_elt_t * e;
1061+        u32 ht_line_head_index;
1062+
1063+        flow = NULL;
1064+        ht_line_head_index = (u32) kv.value;
1065+        if (dlist_is_empty(fm->ht_lines, ht_line_head_index))
1066+            return -1; /* flow not found */
1067+
1068+        ht_line = pool_elt_at_index(fm->ht_lines, ht_line_head_index);
1069+        e = pool_elt_at_index(fm->ht_lines, ht_line->next);
1070+        while (!dlist_is_head(e)) {
1071+            flow = pool_elt_at_index(fm->flows, e->value);
1072+            if (PREDICT_TRUE(memcmp(&flow->sig, &sig, sig_len) == 0)) {
1073+                break;
1074+            }
1075+            e = pool_elt_at_index(fm->ht_lines, e->next);
1076+        }
1077+    }
1078+
1079+    if (PREDICT_FALSE(flow == NULL))
1080+        return -1; /* flow not found */
1081+
1082+    if (lifetime != (u16) ~0) {
1083+        ASSERT(lifetime < TIMER_MAX_LIFETIME);
1084+        flow->lifetime = lifetime;
1085+    }
1086+    flow->infos.data.offloaded = offloaded;
1087+    clib_memcpy(flow->infos.data.opaque, infos, sizeof(flow->infos.data.opaque));
1088+
1089+    return 0;
1090+}
1091+
1092+static inline void
1093+flowtable_entry_remove(flowtable_main_t *fm, flow_entry_t * f)
1094+{
1095+    /* remove node from hashtable */
1096+    clib_dlist_remove(fm->ht_lines, f->ht_index);
1097+    pool_put_index(fm->ht_lines, f->ht_index);
1098+
1099+    /* if list is empty, free it and delete hashtable entry */
1100+    if (dlist_is_empty(fm->ht_lines, f->ht_line_index)) {
1101+        pool_put_index(fm->ht_lines, f->ht_line_index);
1102+
1103+        BVT(clib_bihash_kv) kv = {.key = f->sig_hash};
1104+        BV(clib_bihash_add_del) (&fm->flows_ht, &kv, 0 /* is_add */);
1105+    }
1106+
1107+    /* release flow to pool */
1108+    pool_put(fm->flows, f);
1109+    ASSERT(fm->flows_cpt > 1);
1110+    fm->flows_cpt--;
1111+}
1112+
1113+static u64
1114+flowtable_timer_expire(flowtable_main_t *fm, u32 now)
1115+{
1116+    u64 expire_cpt;
1117+    flow_entry_t * f;
1118+    u32 * time_slot_curr_index;
1119+    dlist_elt_t * time_slot_curr;
1120+    dlist_elt_t * e;
1121+
1122+    time_slot_curr_index = vec_elt_at_index(fm->timer_wheel, fm->time_index);
1123+
1124+    if (PREDICT_FALSE(dlist_is_empty(fm->timers, *time_slot_curr_index)))
1125+        return 0;
1126+
1127+    expire_cpt = 0;
1128+    time_slot_curr = pool_elt_at_index(fm->timers, *time_slot_curr_index);
1129+
1130+    e = pool_elt_at_index(fm->timers, time_slot_curr->next);
1131+    while (!dlist_is_head(e) && expire_cpt < TIMER_MAX_EXPIRE) {
1132+        u32 next_index;
1133+        f = pool_elt_at_index(fm->flows, e->value);
1134+
1135+        ASSERT(f->timer_index == (e - fm->timers));
1136+        ASSERT(f->expire >= now);
1137+        flowtable_entry_remove(fm, f);
1138+
1139+        next_index = e->next;
1140+        clib_dlist_remove(fm->timers, e - fm->timers);
1141+        pool_put(fm->timers, e);
1142+
1143+        expire_cpt++;
1144+        e = pool_elt_at_index(fm->timers, next_index);
1145+    }
1146+
1147+    return expire_cpt;
1148+}
1149+
1150+static inline void
1151+timer_wheel_insert_flow(flowtable_main_t *fm, flow_entry_t *f)
1152+{
1153+    u32 timer_slot_head_index;
1154+
1155+    timer_slot_head_index = (fm->time_index + f->lifetime) % TIMER_MAX_LIFETIME;
1156+    clib_dlist_addtail(fm->timers, timer_slot_head_index, f->timer_index);
1157+}
1158+
1159+static void
1160+timer_wheel_resched_flow(flowtable_main_t *fm, flow_entry_t *f, u64 now)
1161+{
1162+    clib_dlist_remove(fm->timers, f->timer_index);
1163+    f->expire = now + f->lifetime;
1164+    timer_wheel_insert_flow(fm, f);
1165+
1166+    return;
1167+}
1168+
1169+/* TODO: replace with a more appropriate hashtable */
1170+static inline flow_entry_t *
1171+flowtable_entry_lookup_create(flowtable_main_t *fm, BVT(clib_bihash_kv) *kv,
1172+        signature const *sig, u32 const sig_len, u32 const now, int *created)
1173+{
1174+    flow_entry_t * f;
1175+    dlist_elt_t * ht_line;
1176+    dlist_elt_t * timer_entry;
1177+    dlist_elt_t * flow_entry;
1178+    u32 ht_line_head_index;
1179+
1180+    ht_line = NULL;
1181+
1182+    /* get hashtable line */
1183+    if (PREDICT_TRUE(BV(clib_bihash_search) (&fm->flows_ht, kv, kv) == 0)) {
1184+        dlist_elt_t * e;
1185+        ht_line_head_index = (u32) kv->value;
1186+        ht_line = pool_elt_at_index(fm->ht_lines, ht_line_head_index);
1187+
1188+        /* The list CANNOT be a singleton */
1189+        e = pool_elt_at_index(fm->ht_lines, ht_line->next);
1190+        while (!dlist_is_head(e)) {
1191+            f = pool_elt_at_index(fm->flows, e->value);
1192+            if (PREDICT_TRUE(memcmp(&f->sig, &sig, sig_len) == 0)) {
1193+                return f;
1194+            }
1195+            e = pool_elt_at_index(fm->ht_lines, e->next);
1196+        }
1197+
1198+        vlib_node_increment_counter(fm->vlib_main, flowtable_node.index,
1199+                FLOWTABLE_ERROR_COLLISION , 1);
1200+    } else {
1201+        /* create a new line */
1202+        pool_get(fm->ht_lines, ht_line);
1203+
1204+        ht_line_head_index = ht_line - fm->ht_lines;
1205+        clib_dlist_init (fm->ht_lines, ht_line_head_index);
1206+        kv->value = ht_line_head_index;
1207+        BV(clib_bihash_add_del) (&fm->flows_ht, kv, 1 /* is_add */);
1208+    }
1209+
1210+    /* assume the flowtable has been configured correctly */
1211+    ASSERT(fm->flows_cpt <= FM_POOL_COUNT);
1212+    if (PREDICT_FALSE(fm->flows_cpt > FM_POOL_COUNT)) {
1213+        return NULL;
1214+    }
1215+
1216+    /* create new flow */
1217+    *created = 1;
1218+    pool_get_aligned(fm->flows, f, CLIB_CACHE_LINE_BYTES);
1219+    fm->flows_cpt++;
1220+
1221+    memset(f, 0, sizeof(*f));
1222+    f->sig_len = sig_len;
1223+    clib_memcpy(&f->sig, &sig, sig_len);
1224+     f->sig_hash = kv->key;
1225+    f->lifetime = TIMER_DEFAULT_LIFETIME;
1226+    f->expire = now + TIMER_DEFAULT_LIFETIME;
1227+
1228+    /* insert in timer list */
1229+    pool_get(fm->timers, timer_entry);
1230+    timer_entry->value = f - fm->flows; /* index within the flow pool */
1231+    f->timer_index = timer_entry - fm->timers; /* index within the timer pool */
1232+    timer_wheel_insert_flow(fm, f);
1233+
1234+    /* insert in ht line */
1235+    pool_get(fm->ht_lines, flow_entry);
1236+    f->ht_index = flow_entry - fm->ht_lines; /* index within the ht line pool */
1237+    flow_entry->value = f - fm->flows; /* index within the flow pool */
1238+    f->ht_line_index = ht_line_head_index;
1239+    clib_dlist_addhead(fm->ht_lines, ht_line_head_index, f->ht_index);
1240+
1241+    return f;
1242+}
1243+
1244+static inline void
1245+timer_wheel_index_update(flowtable_main_t * fm, u32 now)
1246+{
1247+    u32 new_index = now % TIMER_MAX_LIFETIME;
1248+
1249+    if (PREDICT_FALSE(fm->time_index == ~0)) {
1250+        fm->time_index = new_index;
1251+        return;
1252+    }
1253+
1254+    if (new_index != fm->time_index) {
1255+        /* reschedule all remaining flows on current time index
1256+         * at the begining of the next one */
1257+
1258+        u32 * curr_slot_index = vec_elt_at_index(fm->timer_wheel, fm->time_index);
1259+        dlist_elt_t * curr_head = pool_elt_at_index(fm->timers, *curr_slot_index);
1260+
1261+        u32 * next_slot_index = vec_elt_at_index(fm->timer_wheel, new_index);
1262+        dlist_elt_t * next_head = pool_elt_at_index(fm->timers, *next_slot_index);
1263+
1264+        if (PREDICT_FALSE(dlist_is_empty(fm->timers, *curr_slot_index))) {
1265+            fm->time_index = new_index;
1266+            return;
1267+        }
1268+
1269+        dlist_elt_t * curr_prev = pool_elt_at_index (fm->timers, curr_head->prev);
1270+        dlist_elt_t * curr_next = pool_elt_at_index (fm->timers, curr_head->next);
1271+
1272+        /* insert timer list of current time slot at the begining of the next slot */
1273+        if (PREDICT_FALSE(dlist_is_empty(fm->timers, *next_slot_index))) {
1274+            next_head->next = curr_head->next;
1275+            next_head->prev = curr_head->prev;
1276+            curr_prev->next = *next_slot_index;
1277+            curr_next->prev = *next_slot_index;
1278+        } else {
1279+            dlist_elt_t * next_next = pool_elt_at_index (fm->timers, next_head->next);
1280+            curr_prev->next = next_head->next;
1281+            next_head->next = curr_head->next;
1282+            next_next->prev = curr_head->prev;
1283+            curr_next->prev = *next_slot_index;
1284+        }
1285+
1286+        /* reset current time slot as an empty list */
1287+        memset (curr_head, 0xff, sizeof (*curr_head));
1288+
1289+        fm->time_index = new_index;
1290+    }
1291+}
1292+
1293+static uword
1294+flowtable_process(vlib_main_t * vm,
1295+    vlib_node_runtime_t * node, vlib_frame_t * frame)
1296+{
1297+    u32 n_left_from, * from, next_index, * to_next;
1298+    flowtable_main_t * fm = &flowtable_main;
1299+
1300+#define _(sym, str) u32 CPT_##sym = 0;
1301+    foreach_flowtable_error
1302+#undef _
1303+
1304+    from = vlib_frame_vector_args(frame);
1305+    n_left_from = frame->n_vectors;
1306+    next_index = node->cached_next_index;
1307+
1308+    u32 current_time = (u32) ((u64) fm->vlib_main->cpu_time_last_node_dispatch / fm->vlib_main->clib_time.clocks_per_second);
1309+    timer_wheel_index_update(fm, current_time);
1310+
1311+    while (n_left_from > 0)
1312+    {
1313+        u32 pi0;
1314+        u32 next0;
1315+        u32 n_left_to_next;
1316+
1317+        vlib_buffer_t * b0;
1318+        vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
1319+
1320+        /* Single loop */
1321+        while (n_left_from > 0 && n_left_to_next > 0)
1322+        {
1323+            int created = 0;
1324+            flow_entry_t * flow = NULL;
1325+            uword is_reverse = 0;
1326+            u64 sig_hash;
1327+            BVT(clib_bihash_kv) kv;
1328+
1329+            u16 type;
1330+            pi0 = to_next[0] = from[0];
1331+            b0 = vlib_get_buffer(vm, pi0);
1332+            u32 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1333+
1334+            /* Get Flow & copy metadatas into opaque1 or opaque2 */
1335+            ethernet_header_t * eth0 = (void *) (b0->data + b0->current_data);
1336+            type = clib_net_to_host_u16(eth0->type);
1337+            if (PREDICT_TRUE
1338+                    (type == ETHERNET_TYPE_IP6 || type == ETHERNET_TYPE_IP4))
1339+            {
1340+                u32 sig_len;
1341+                signature sig;
1342+                vlib_buffer_advance(b0, sizeof(ethernet_header_t));
1343+
1344+                /* compute 5 tuple key so that 2 half connections
1345+                 * get into the same flow */
1346+                if (type == ETHERNET_TYPE_IP4)
1347+                {
1348+                    sig_len = sizeof(struct ip4_sig);
1349+                    sig_hash = parse_ip4_packet(vlib_buffer_get_current(b0),
1350+                            &is_reverse, (struct ip4_sig *) &sig);
1351+                } else {
1352+                    sig_len = sizeof(struct ip6_sig);
1353+                    sig_hash = parse_ip6_packet(vlib_buffer_get_current(b0),
1354+                            &is_reverse, (struct ip6_sig *) &sig);
1355+                }
1356+
1357+                /* lookup flow */
1358+                kv.key = sig_hash;
1359+                flow = flowtable_entry_lookup_create(fm, &kv, &sig, sig_len,
1360+                        current_time, &created);
1361+
1362+                if (PREDICT_FALSE(flow == NULL)) {
1363+                    CPT_ALLOC_ERROR++;
1364+                    next0 = FT_NEXT_ETHERNET_INPUT;
1365+                    goto get_flowinfo_error;
1366+                }
1367+
1368+                if (created) {
1369+                    CPT_CREATED++;
1370+                } else {
1371+                    timer_wheel_resched_flow(fm, flow, current_time);
1372+                    CPT_HIT++;
1373+                }
1374+
1375+                if (is_reverse)
1376+                    flow->stats.reverse++;
1377+                else
1378+                    flow->stats.straight++;
1379+
1380+
1381+                if (flow_is_offloaded(flow)) {
1382+                    next0 = FT_NEXT_ETHERNET_INPUT;
1383+                    clib_memcpy(b0->opaque, &flow->infos, sizeof(flow->infos));
1384+                    vnet_buffer (b0)->sw_if_index[VLIB_RX] = flow->infos.data.sw_if_index_current;
1385+
1386+                    CPT_OFFLOADED++;
1387+                } else {
1388+                    flow->infos.data.sw_if_index_current = sw_if_index0;
1389+                    clib_memcpy(b0->opaque, &flow->infos, sizeof(flow->infos));
1390+                    next0 = fm->next_node_index;
1391+                }
1392+            } else {
1393+                next0 = FT_NEXT_ETHERNET_INPUT;
1394+            }
1395+            vlib_buffer_reset(b0);
1396+
1397+            /* stats */
1398+            CPT_THRU++;
1399+
1400+            /* frame mgmt */
1401+            from++;
1402+            to_next++;
1403+            n_left_from--;
1404+            n_left_to_next--;
1405+
1406+            if (b0->flags & VLIB_BUFFER_IS_TRACED)
1407+            {
1408+                flow_trace_t * t = vlib_add_trace(vm, node, b0, sizeof(*t));
1409+                t->sw_if_index = sw_if_index0;
1410+                t->next_index = next0;
1411+                if (flow)
1412+                    t->offloaded = flow->infos.data.offloaded;
1413+                else
1414+                    t->offloaded = 0;
1415+            }
1416+
1417+get_flowinfo_error:
1418+            vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
1419+                n_left_to_next, pi0, next0);
1420+        }
1421+        vlib_put_next_frame(vm, node, next_index, n_left_to_next);
1422+    }
1423+
1424+    /* handle expirations */
1425+    CPT_TIMER_EXPIRE += flowtable_timer_expire(fm, current_time);
1426+
1427+#define _(sym, str) \
1428+    vlib_node_increment_counter(vm, flowtable_node.index, \
1429+            FLOWTABLE_ERROR_##sym , CPT_##sym);
1430+    foreach_flowtable_error
1431+#undef _
1432+
1433+    return frame->n_vectors;
1434+}
1435+
1436+static char * flowtable_error_strings[] = {
1437+#define _(sym, string) string,
1438+    foreach_flowtable_error
1439+#undef _
1440+};
1441+
1442+VLIB_REGISTER_NODE(flowtable_node) = {
1443+    .function = flowtable_process,
1444+    .name = "flowtable-process",
1445+    .vector_size = sizeof(u32),
1446+    .format_trace = format_get_flowinfo,
1447+    .type = VLIB_NODE_TYPE_INTERNAL,
1448+    .n_errors = FLOWTABLE_N_ERROR,
1449+    .error_strings = flowtable_error_strings,
1450+    .n_next_nodes = FT_NEXT_N_NEXT,
1451+    .next_nodes = {
1452+        [FT_NEXT_DROP] = "error-drop",
1453+        [FT_NEXT_ETHERNET_INPUT] = "ethernet-input"
1454+    }
1455+};
1456diff --git a/plugins/flowtable-plugin/flowtable/nodes_registration.c b/plugins/flowtable-plugin/flowtable/nodes_registration.c
1457new file mode 100644
1458index 0000000..ba36afb
1459--- /dev/null
1460+++ b/plugins/flowtable-plugin/flowtable/nodes_registration.c
1461@@ -0,0 +1,80 @@
1462+/*
1463+ * Copyright (c) 2016 Qosmos and/or its affiliates.
1464+ * Licensed under the Apache License, Version 2.0 (the "License");
1465+ * you may not use this file except in compliance with the License.
1466+ * You may obtain a copy of the License at:
1467+ *
1468+ *     http://www.apache.org/licenses/LICENSE-2.0
1469+ *
1470+ * Unless required by applicable law or agreed to in writing, software
1471+ * distributed under the License is distributed on an "AS IS" BASIS,
1472+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1473+ * See the License for the specific language governing permissions and
1474+ * limitations under the License.
1475+ */
1476+
1477+#include <vnet/plugin/plugin.h>
1478+#include <vppinfra/bihash_8_8.h>
1479+#include <vppinfra/dlist.h>
1480+#include <vppinfra/pool.h>
1481+#include <vppinfra/types.h>
1482+#include <vppinfra/vec.h>
1483+
1484+#include "flowtable.h"
1485+
1486+/*
1487+ * This routine exists to convince the vlib plugin framework that
1488+ * we haven't accidentally copied a random .dll into the plugin directory.
1489+ *
1490+ * Also collects global variable pointers passed from the vpp engine
1491+ */
1492+clib_error_t *
1493+vlib_plugin_register(vlib_main_t * vm, vnet_plugin_handoff_t * h,
1494+    int from_early_init)
1495+{
1496+    clib_error_t * error = 0;
1497+    flowtable_main_t * fm = &flowtable_main;
1498+
1499+    fm->vnet_main = vnet_get_main();
1500+    fm->vlib_main = vm;
1501+
1502+    return error;
1503+}
1504+
1505+static clib_error_t *
1506+flowtable_init(vlib_main_t * vm)
1507+{
1508+    int i;
1509+    clib_error_t * error = 0;
1510+    flowtable_main_t * fm = &flowtable_main;
1511+
1512+    fm->flowtable_index = flowtable_node.index;
1513+
1514+    /* ensures flow_info structure fits into vlib_buffer_t's opaque 1 field */
1515+    ASSERT(sizeof(flow_data_t) <= sizeof(u32) * 8);
1516+
1517+    /* init flow pool
1518+     * TODO get flow count from configuration */
1519+    pool_alloc_aligned(fm->flows, FM_POOL_COUNT, CLIB_CACHE_LINE_BYTES);
1520+
1521+    /* init hashtable */
1522+    fm->flows_cpt = 0;
1523+    pool_alloc(fm->ht_lines, 2 * FM_POOL_COUNT);
1524+    BV(clib_bihash_init) (&fm->flows_ht, "flow hash table",
1525+            FM_NUM_BUCKETS, FM_MEMORY_SIZE);
1526+
1527+    /* init timer wheel */
1528+    fm->time_index = ~0;
1529+    for (i = 0; i < TIMER_MAX_LIFETIME ; i++) {
1530+        dlist_elt_t * timer_slot;
1531+        pool_get(fm->timers, timer_slot);
1532+
1533+        u32 timer_slot_head_index = timer_slot - fm->timers;
1534+        clib_dlist_init (fm->timers, timer_slot_head_index);
1535+        vec_add1(fm->timer_wheel, timer_slot_head_index);
1536+    }
1537+
1538+    return error;
1539+}
1540+
1541+VLIB_INIT_FUNCTION(flowtable_init);
1542-- 
15432.10.0.rc1.15.g5cb0d5a
1544
1545