Add pipeline object implementation to the application.

Signed-off-by: Cristian Dumitrescu <cristian.dumitre...@intel.com>
Signed-off-by: Jasvinder Singh <jasvinder.si...@intel.com>
---
 examples/ip_pipeline/Makefile    |   1 +
 examples/ip_pipeline/cli.c       | 786 +++++++++++++++++++++++++++++++++
 examples/ip_pipeline/cli.h       |   2 -
 examples/ip_pipeline/main.c      |   8 +
 examples/ip_pipeline/meson.build |   1 +
 examples/ip_pipeline/pipeline.c  | 930 +++++++++++++++++++++++++++++++++++++++
 examples/ip_pipeline/pipeline.h  | 265 +++++++++++
 7 files changed, 1991 insertions(+), 2 deletions(-)
 create mode 100644 examples/ip_pipeline/pipeline.c
 create mode 100644 examples/ip_pipeline/pipeline.h

diff --git a/examples/ip_pipeline/Makefile b/examples/ip_pipeline/Makefile
index d9c8d86..033319d 100644
--- a/examples/ip_pipeline/Makefile
+++ b/examples/ip_pipeline/Makefile
@@ -13,6 +13,7 @@ SRCS-y += link.c
 SRCS-y += main.c
 SRCS-y += mempool.c
 SRCS-y += parser.c
+SRCS-y += pipeline.c
 SRCS-y += swq.c
 SRCS-y += tap.c
 SRCS-y += tmgr.c
diff --git a/examples/ip_pipeline/cli.c b/examples/ip_pipeline/cli.c
index ff3a996..acf4fe0 100644
--- a/examples/ip_pipeline/cli.c
+++ b/examples/ip_pipeline/cli.c
@@ -14,6 +14,7 @@
 #include "link.h"
 #include "mempool.h"
 #include "parser.h"
+#include "pipeline.h"
 #include "swq.h"
 #include "tap.h"
 #include "tmgr.h"
@@ -994,6 +995,751 @@ cmd_table_action_profile(char **tokens,
        }
 }
 
+/**
+ * pipeline <pipeline_name>
+ *  period <timer_period_ms>
+ *  offset_port_id <offset_port_id>
+ *  cpu <cpu_id>
+ */
+static void
+cmd_pipeline(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct pipeline_params p;
+       char *name;
+       struct pipeline *pipeline;
+
+       if (n_tokens != 8) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       name = tokens[1];
+
+       if (strcmp(tokens[2], "period") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
+               return;
+       }
+
+       if (parser_read_uint32(&p.timer_period_ms, tokens[3]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
+               return;
+       }
+
+       if (strcmp(tokens[4], "offset_port_id") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset_port_id");
+               return;
+       }
+
+       if (parser_read_uint32(&p.offset_port_id, tokens[5]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "offset_port_id");
+               return;
+       }
+
+       if (strcmp(tokens[6], "cpu") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
+               return;
+       }
+
+       if (parser_read_uint32(&p.cpu_id, tokens[7]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
+               return;
+       }
+
+       pipeline = pipeline_create(name, &p);
+       if (pipeline == NULL) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
+
+/**
+ * pipeline <pipeline_name> port in
+ *  bsz <burst_size>
+ *  link <link_name> rxq <queue_id>
+ *  | swq <swq_name>
+ *  | tmgr <tmgr_name>
+ *  | tap <tap_name> mempool <mempool_name> mtu <mtu>
+ *  | kni <kni_name>
+ *  | source <source_name> mempool <mempool_name> file <file_name>
+ *      [disabled]
+ */
+static void
+cmd_pipeline_port_in(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct port_in_params p;
+       char *pipeline_name;
+       uint32_t t0;
+       int enabled, status;
+
+       if (n_tokens < 7) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+
+       if (strcmp(tokens[2], "port") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
+               return;
+       }
+
+       if (strcmp(tokens[3], "in") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+               return;
+       }
+
+       if (strcmp(tokens[4], "bsz") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
+               return;
+       }
+
+       if (parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
+               return;
+       }
+
+       t0 = 6;
+
+       if (strcmp(tokens[t0], "link") == 0) {
+               if (n_tokens < t0 + 4) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port in link");
+                       return;
+               }
+
+               p.type = PORT_IN_RXQ;
+
+               p.dev_name = tokens[t0 + 1];
+
+               if (strcmp(tokens[t0 + 2], "rxq") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
+                       return;
+               }
+
+               if (parser_read_uint16(&p.rxq.queue_id, tokens[t0 + 3]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "queue_id");
+                       return;
+               }
+               t0 += 4;
+       } else if (strcmp(tokens[t0], "swq") == 0) {
+               if (n_tokens < t0 + 2) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port in swq");
+                       return;
+               }
+
+               p.type = PORT_IN_SWQ;
+
+               p.dev_name = tokens[t0 + 1];
+
+               t0 += 2;
+       } else if (strcmp(tokens[t0], "tmgr") == 0) {
+               if (n_tokens < t0 + 2) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port in tmgr");
+                       return;
+               }
+
+               p.type = PORT_IN_TMGR;
+
+               p.dev_name = tokens[t0 + 1];
+
+               t0 += 2;
+       } else if (strcmp(tokens[t0], "tap") == 0) {
+               if (n_tokens < t0 + 6) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port in tap");
+                       return;
+               }
+
+               p.type = PORT_IN_TAP;
+
+               p.dev_name = tokens[t0 + 1];
+
+               if (strcmp(tokens[t0 + 2], "mempool") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "mempool");
+                       return;
+               }
+
+               p.tap.mempool_name = tokens[t0 + 3];
+
+               if (strcmp(tokens[t0 + 4], "mtu") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "mtu");
+                       return;
+               }
+
+               if (parser_read_uint32(&p.tap.mtu, tokens[t0 + 5]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
+                       return;
+               }
+
+               t0 += 6;
+       } else if (strcmp(tokens[t0], "kni") == 0) {
+               if (n_tokens < t0 + 2) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port in kni");
+                       return;
+               }
+
+               p.type = PORT_IN_KNI;
+
+               p.dev_name = tokens[t0 + 1];
+
+               t0 += 2;
+       } else if (strcmp(tokens[t0], "source") == 0) {
+               if (n_tokens < t0 + 6) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port in source");
+                       return;
+               }
+
+               p.type = PORT_IN_SOURCE;
+
+               p.dev_name = tokens[t0 + 1];
+
+               if (strcmp(tokens[t0 + 2], "mempool") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "mempool");
+                       return;
+               }
+
+               p.source.mempool_name = tokens[t0 + 3];
+
+               if (strcmp(tokens[t0 + 4], "file") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "file");
+                       return;
+               }
+
+               p.source.file_name = tokens[t0 + 5];
+
+               p.source.n_bytes_per_pkt = 0;
+
+               t0 += 6;
+       } else {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       enabled = 1;
+       if ((n_tokens > t0) &&
+               (strcmp(tokens[t0], "disabled") == 0)) {
+               enabled = 0;
+
+               t0 += 1;
+       }
+
+       if (n_tokens != t0) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       status = pipeline_port_in_create(pipeline_name,
+               &p, enabled);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
+
+/**
+ * pipeline <pipeline_name> port out
+ *  bsz <burst_size>
+ *  link <link_name> txq <txq_id>
+ *  | swq <swq_name>
+ *  | tmgr <tmgr_name>
+ *  | tap <tap_name>
+ *  | kni <kni_name>
+ *  | sink <sink_name> [file <file_name>]
+ */
+static void
+cmd_pipeline_port_out(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct port_out_params p;
+       char *pipeline_name;
+       int status;
+
+       memset(&p, 0, sizeof(p));
+
+       if (n_tokens < 7) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+
+       if (strcmp(tokens[2], "port") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
+               return;
+       }
+
+       if (strcmp(tokens[3], "out") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
+               return;
+       }
+
+       if (strcmp(tokens[4], "bsz") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
+               return;
+       }
+
+       if (parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
+               return;
+       }
+
+       if (strcmp(tokens[6], "link") == 0) {
+               if (n_tokens != 10) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port out link");
+                       return;
+               }
+
+               p.type = PORT_OUT_TXQ;
+
+               p.dev_name = tokens[7];
+
+               if (strcmp(tokens[8], "txq") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
+                       return;
+               }
+
+               if (parser_read_uint16(&p.txq.queue_id, tokens[9]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "queue_id");
+                       return;
+               }
+       } else if (strcmp(tokens[6], "swq") == 0) {
+               if (n_tokens != 8) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port out swq");
+                       return;
+               }
+
+               p.type = PORT_OUT_SWQ;
+
+               p.dev_name = tokens[7];
+       } else if (strcmp(tokens[6], "tmgr") == 0) {
+               if (n_tokens != 8) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port out tmgr");
+                       return;
+               }
+
+               p.type = PORT_OUT_TMGR;
+
+               p.dev_name = tokens[7];
+       } else if (strcmp(tokens[6], "tap") == 0) {
+               if (n_tokens != 8) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port out tap");
+                       return;
+               }
+
+               p.type = PORT_OUT_TAP;
+
+               p.dev_name = tokens[7];
+       } else if (strcmp(tokens[6], "kni") == 0) {
+               if (n_tokens != 8) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port out kni");
+                       return;
+               }
+
+               p.type = PORT_OUT_KNI;
+
+               p.dev_name = tokens[7];
+       } else if (strcmp(tokens[6], "sink") == 0) {
+               if ((n_tokens != 8) && (n_tokens != 10)) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port out sink");
+                       return;
+               }
+
+               p.type = PORT_OUT_SINK;
+
+               p.dev_name = tokens[7];
+
+               if (n_tokens == 8)
+                       p.sink.file_name = NULL;
+               else {
+                       if (strcmp(tokens[8], "file") != 0) {
+                               snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                                       "file");
+                               return;
+                       }
+
+                       p.sink.file_name = tokens[9];
+               }
+
+               p.sink.max_n_pkts = 0;
+       } else {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       status = pipeline_port_out_create(pipeline_name, &p);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
+
+/**
+ * pipeline <pipeline_name> table
+ *      match
+ *      acl
+ *          ipv4 | ipv6
+ *          offset <ip_header_offset>
+ *          size <n_rules>
+ *      | array
+ *          offset <key_offset>
+ *          size <n_keys>
+ *      | hash
+ *          ext | lru
+ *          key <key_size>
+ *          mask <key_mask>
+ *          offset <key_offset>
+ *          buckets <n_buckets>
+ *          size <n_keys>
+ *      | lpm
+ *          ipv4 | ipv6
+ *          offset <ip_header_offset>
+ *          size <n_rules>
+ *      | stub
+ *  action <action_profile_name>
+ */
+static void
+cmd_pipeline_table(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       uint8_t key_mask[TABLE_RULE_MATCH_SIZE_MAX];
+       struct table_params p;
+       char *pipeline_name;
+       uint32_t t0;
+       int status;
+
+       if (n_tokens < 5) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+
+       if (strcmp(tokens[2], "table") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
+               return;
+       }
+
+       if (strcmp(tokens[3], "match") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
+               return;
+       }
+
+       t0 = 4;
+       if (strcmp(tokens[t0], "acl") == 0) {
+               if (n_tokens < t0 + 6) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline table acl");
+                       return;
+               }
+
+               p.match_type = TABLE_ACL;
+
+               if (strcmp(tokens[t0 + 1], "ipv4") == 0)
+                       p.match.acl.ip_version = 1;
+               else if (strcmp(tokens[t0 + 1], "ipv6") == 0)
+                       p.match.acl.ip_version = 0;
+               else {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "ipv4 or ipv6");
+                       return;
+               }
+
+               if (strcmp(tokens[t0 + 2], "offset") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
+                       return;
+               }
+
+               if (parser_read_uint32(&p.match.acl.ip_header_offset,
+                       tokens[t0 + 3]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "ip_header_offset");
+                       return;
+               }
+
+               if (strcmp(tokens[t0 + 4], "size") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
+                       return;
+               }
+
+               if (parser_read_uint32(&p.match.acl.n_rules,
+                       tokens[t0 + 5]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
+                       return;
+               }
+
+               t0 += 6;
+       } else if (strcmp(tokens[t0], "array") == 0) {
+               if (n_tokens < t0 + 5) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline table array");
+                       return;
+               }
+
+               p.match_type = TABLE_ARRAY;
+
+               if (strcmp(tokens[t0 + 1], "offset") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
+                       return;
+               }
+
+               if (parser_read_uint32(&p.match.array.key_offset,
+                       tokens[t0 + 2]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
+                       return;
+               }
+
+               if (strcmp(tokens[t0 + 3], "size") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
+                       return;
+               }
+
+               if (parser_read_uint32(&p.match.array.n_keys,
+                       tokens[t0 + 4]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
+                       return;
+               }
+
+               t0 += 5;
+       } else if (strcmp(tokens[t0], "hash") == 0) {
+               uint32_t key_mask_size = TABLE_RULE_MATCH_SIZE_MAX;
+
+               if (n_tokens < t0 + 12) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline table hash");
+                       return;
+               }
+
+               p.match_type = TABLE_HASH;
+
+               if (strcmp(tokens[t0 + 1], "ext") == 0)
+                       p.match.hash.extendable_bucket = 1;
+               else if (strcmp(tokens[t0 + 1], "lru") == 0)
+                       p.match.hash.extendable_bucket = 0;
+               else {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "ext or lru");
+                       return;
+               }
+
+               if (strcmp(tokens[t0 + 2], "key") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
+                       return;
+               }
+
+               if ((parser_read_uint32(&p.match.hash.key_size,
+                       tokens[t0 + 3]) != 0) ||
+                       (p.match.hash.key_size == 0) ||
+                       (p.match.hash.key_size > TABLE_RULE_MATCH_SIZE_MAX)) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_size");
+                       return;
+               }
+
+               if (strcmp(tokens[t0 + 4], "mask") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
+                       return;
+               }
+
+               if ((parse_hex_string(tokens[t0 + 5],
+                       key_mask, &key_mask_size) != 0) ||
+                       (key_mask_size != p.match.hash.key_size)) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
+                       return;
+               }
+               p.match.hash.key_mask = key_mask;
+
+               if (strcmp(tokens[t0 + 6], "offset") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
+                       return;
+               }
+
+               if (parser_read_uint32(&p.match.hash.key_offset,
+                       tokens[t0 + 7]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
+                       return;
+               }
+
+               if (strcmp(tokens[t0 + 8], "buckets") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buckets");
+                       return;
+               }
+
+               if (parser_read_uint32(&p.match.hash.n_buckets,
+                       tokens[t0 + 9]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "n_buckets");
+                       return;
+               }
+
+               if (strcmp(tokens[t0 + 10], "size") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
+                       return;
+               }
+
+               if (parser_read_uint32(&p.match.hash.n_keys,
+                       tokens[t0 + 11]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
+                       return;
+               }
+
+               t0 += 12;
+       } else if (strcmp(tokens[t0], "lpm") == 0) {
+               if (n_tokens < t0 + 6) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline table lpm");
+                       return;
+               }
+
+               p.match_type = TABLE_LPM;
+
+               if (strcmp(tokens[t0 + 1], "ipv4") == 0)
+                       p.match.lpm.key_size = 4;
+               else if (strcmp(tokens[t0 + 1], "ipv6") == 0)
+                       p.match.lpm.key_size = 16;
+               else {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "ipv4 or ipv6");
+                       return;
+               }
+
+               if (strcmp(tokens[t0 + 2], "offset") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
+                       return;
+               }
+
+               if (parser_read_uint32(&p.match.lpm.key_offset,
+                       tokens[t0 + 3]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
+                       return;
+               }
+
+               if (strcmp(tokens[t0 + 4], "size") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
+                       return;
+               }
+
+               if (parser_read_uint32(&p.match.lpm.n_rules,
+                       tokens[t0 + 5]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
+                       return;
+               }
+
+               t0 += 6;
+       } else if (strcmp(tokens[t0], "stub") == 0) {
+               if (n_tokens < t0 + 1) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline table stub");
+                       return;
+               }
+
+               p.match_type = TABLE_STUB;
+
+               t0 += 1;
+       } else {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       if (n_tokens >= t0 + 2) {
+               if (strcmp(tokens[t0], "action") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
+                       return;
+               }
+
+               p.action_profile_name = tokens[t0 + 1];
+
+               t0 += 2;
+       }
+
+       if (n_tokens > t0) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       status = pipeline_table_create(pipeline_name, &p);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
+
+/**
+ * pipeline <pipeline_name> port in <port_id> table <table_id>
+ */
+static void
+cmd_pipeline_port_in_table(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       char *pipeline_name;
+       uint32_t port_id, table_id;
+       int status;
+
+       if (n_tokens != 7) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+
+       if (strcmp(tokens[2], "port") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
+               return;
+       }
+
+       if (strcmp(tokens[3], "in") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+               return;
+       }
+
+       if (parser_read_uint32(&port_id, tokens[4]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
+               return;
+       }
+
+       if (strcmp(tokens[5], "table") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
+               return;
+       }
+
+       if (parser_read_uint32(&table_id, tokens[6]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
+               return;
+       }
+
+       status = pipeline_port_in_connect_to_table(pipeline_name,
+               port_id,
+               table_id);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
+
 void
 cli_process(char *in, char *out, size_t out_size)
 {
@@ -1077,6 +1823,46 @@ cli_process(char *in, char *out, size_t out_size)
                return;
        }
 
+       if (strcmp(tokens[0], "pipeline") == 0) {
+               if ((n_tokens >= 3) &&
+                       (strcmp(tokens[2], "period") == 0)) {
+                       cmd_pipeline(tokens, n_tokens, out, out_size);
+                       return;
+               }
+
+               if ((n_tokens >= 5) &&
+                       (strcmp(tokens[2], "port") == 0) &&
+                       (strcmp(tokens[3], "in") == 0) &&
+                       (strcmp(tokens[4], "bsz") == 0)) {
+                       cmd_pipeline_port_in(tokens, n_tokens, out, out_size);
+                       return;
+               }
+
+               if ((n_tokens >= 5) &&
+                       (strcmp(tokens[2], "port") == 0) &&
+                       (strcmp(tokens[3], "out") == 0) &&
+                       (strcmp(tokens[4], "bsz") == 0)) {
+                       cmd_pipeline_port_out(tokens, n_tokens, out, out_size);
+                       return;
+               }
+
+               if ((n_tokens >= 4) &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[3], "match") == 0)) {
+                       cmd_pipeline_table(tokens, n_tokens, out, out_size);
+                       return;
+               }
+
+               if ((n_tokens >= 6) &&
+                       (strcmp(tokens[2], "port") == 0) &&
+                       (strcmp(tokens[3], "in") == 0) &&
+                       (strcmp(tokens[5], "table") == 0)) {
+                       cmd_pipeline_port_in_table(tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
+       }
+
        snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
 }
 
diff --git a/examples/ip_pipeline/cli.h b/examples/ip_pipeline/cli.h
index 02564a6..992e4c3 100644
--- a/examples/ip_pipeline/cli.h
+++ b/examples/ip_pipeline/cli.h
@@ -7,8 +7,6 @@
 
 #include <stddef.h>
 
-#include "action.h"
-
 void
 cli_process(char *in, char *out, size_t out_size);
 
diff --git a/examples/ip_pipeline/main.c b/examples/ip_pipeline/main.c
index 24b74cf..a7f4486 100644
--- a/examples/ip_pipeline/main.c
+++ b/examples/ip_pipeline/main.c
@@ -15,6 +15,7 @@
 #include "kni.h"
 #include "link.h"
 #include "mempool.h"
+#include "pipeline.h"
 #include "swq.h"
 #include "tap.h"
 #include "tmgr.h"
@@ -215,6 +216,13 @@ main(int argc, char **argv)
                return status;
        }
 
+       /* Pipeline */
+       status = pipeline_init();
+       if (status) {
+               printf("Error: Pipeline initialization failed (%d)\n", status);
+               return status;
+       }
+
        /* Script */
        if (app.script_name)
                cli_script_process(app.script_name,
diff --git a/examples/ip_pipeline/meson.build b/examples/ip_pipeline/meson.build
index fb1b61d..2366242 100644
--- a/examples/ip_pipeline/meson.build
+++ b/examples/ip_pipeline/meson.build
@@ -17,6 +17,7 @@ sources = files(
        'main.c',
        'mempool.c',
        'parser.c',
+       'pipeline.c',
        'swq.c',
        'tap.c',
        'tmgr.c'
diff --git a/examples/ip_pipeline/pipeline.c b/examples/ip_pipeline/pipeline.c
new file mode 100644
index 0000000..8efc70d
--- /dev/null
+++ b/examples/ip_pipeline/pipeline.c
@@ -0,0 +1,930 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2018 Intel Corporation
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_ip.h>
+#include <rte_tcp.h>
+
+#include <rte_port_ethdev.h>
+#include <rte_port_kni.h>
+#include <rte_port_ring.h>
+#include <rte_port_source_sink.h>
+#include <rte_port_fd.h>
+#include <rte_port_sched.h>
+
+#include <rte_table_acl.h>
+#include <rte_table_array.h>
+#include <rte_table_hash.h>
+#include <rte_table_lpm.h>
+#include <rte_table_lpm_ipv6.h>
+#include <rte_table_stub.h>
+
+#include "kni.h"
+#include "link.h"
+#include "mempool.h"
+#include "pipeline.h"
+#include "tap.h"
+#include "tmgr.h"
+#include "swq.h"
+
+#include "hash_func.h"
+
+#ifndef PIPELINE_MSGQ_SIZE
+#define PIPELINE_MSGQ_SIZE                                 64
+#endif
+
+#ifndef TABLE_LPM_NUMBER_TBL8
+#define TABLE_LPM_NUMBER_TBL8                              256
+#endif
+
+static struct pipeline_list pipeline_list;
+
+int
+pipeline_init(void)
+{
+       TAILQ_INIT(&pipeline_list);
+
+       return 0;
+}
+
+struct pipeline *
+pipeline_find(const char *name)
+{
+       struct pipeline *pipeline;
+
+       if (name == NULL)
+               return NULL;
+
+       TAILQ_FOREACH(pipeline, &pipeline_list, node)
+               if (strcmp(name, pipeline->name) == 0)
+                       return pipeline;
+
+       return NULL;
+}
+
+struct pipeline *
+pipeline_create(const char *name, struct pipeline_params *params)
+{
+       char msgq_name[NAME_MAX];
+       struct rte_pipeline_params pp;
+       struct pipeline *pipeline;
+       struct rte_pipeline *p;
+       struct rte_ring *msgq_req;
+       struct rte_ring *msgq_rsp;
+
+       /* Check input params */
+       if ((name == NULL) ||
+               pipeline_find(name) ||
+               (params == NULL) ||
+               (params->timer_period_ms == 0))
+               return NULL;
+
+       /* Resource create */
+       snprintf(msgq_name, sizeof(msgq_name), "%s-MSGQ-REQ", name);
+
+       msgq_req = rte_ring_create(msgq_name,
+               PIPELINE_MSGQ_SIZE,
+               params->cpu_id,
+               RING_F_SP_ENQ | RING_F_SC_DEQ);
+       if (msgq_req == NULL)
+               return NULL;
+
+       snprintf(msgq_name, sizeof(msgq_name), "%s-MSGQ-RSP", name);
+
+       msgq_rsp = rte_ring_create(msgq_name,
+               PIPELINE_MSGQ_SIZE,
+               params->cpu_id,
+               RING_F_SP_ENQ | RING_F_SC_DEQ);
+       if (msgq_rsp == NULL) {
+               rte_ring_free(msgq_req);
+               return NULL;
+       }
+
+       pp.name = name;
+       pp.socket_id = (int) params->cpu_id;
+       pp.offset_port_id = params->offset_port_id;
+
+       p = rte_pipeline_create(&pp);
+       if (p == NULL) {
+               rte_ring_free(msgq_rsp);
+               rte_ring_free(msgq_req);
+               return NULL;
+       }
+
+       /* Node allocation */
+       pipeline = calloc(1, sizeof(struct pipeline));
+       if (pipeline == NULL) {
+               rte_pipeline_free(p);
+               rte_ring_free(msgq_rsp);
+               rte_ring_free(msgq_req);
+               return NULL;
+       }
+
+       /* Node fill in */
+       strncpy(pipeline->name, name, sizeof(pipeline->name));
+       pipeline->p = p;
+       pipeline->n_ports_in = 0;
+       pipeline->n_ports_out = 0;
+       pipeline->n_tables = 0;
+       pipeline->msgq_req = msgq_req;
+       pipeline->msgq_rsp = msgq_rsp;
+       pipeline->timer_period_ms = params->timer_period_ms;
+       pipeline->enabled = 0;
+       pipeline->cpu_id = params->cpu_id;
+
+       /* Node add to list */
+       TAILQ_INSERT_TAIL(&pipeline_list, pipeline, node);
+
+       return pipeline;
+}
+
+int
+pipeline_port_in_create(const char *pipeline_name,
+       struct port_in_params *params,
+       int enabled)
+{
+       struct rte_pipeline_port_in_params p;
+
+       union {
+               struct rte_port_ethdev_reader_params ethdev;
+               struct rte_port_ring_reader_params ring;
+               struct rte_port_sched_reader_params sched;
+               struct rte_port_fd_reader_params fd;
+               struct rte_port_kni_reader_params kni;
+               struct rte_port_source_params source;
+       } pp;
+
+       struct pipeline *pipeline;
+       uint32_t port_id;
+       int status;
+
+       memset(&pp, 0, sizeof(pp));
+
+       /* Check input params */
+       if ((pipeline_name == NULL) ||
+               (params == NULL) ||
+               (params->burst_size == 0) ||
+               (params->burst_size > RTE_PORT_IN_BURST_SIZE_MAX))
+               return -1;
+
+       pipeline = pipeline_find(pipeline_name);
+       if (pipeline == NULL)
+               return -1;
+
+       switch (params->type) {
+       case PORT_IN_RXQ:
+       {
+               struct link *link;
+
+               link = link_find(params->dev_name);
+               if (link == NULL)
+                       return -1;
+
+               if (params->rxq.queue_id >= link->n_rxq)
+                       return -1;
+
+               pp.ethdev.port_id = link->port_id;
+               pp.ethdev.queue_id = params->rxq.queue_id;
+
+               p.ops = &rte_port_ethdev_reader_ops;
+               p.arg_create = &pp.ethdev;
+               break;
+       }
+
+       case PORT_IN_SWQ:
+       {
+               struct swq *swq;
+
+               swq = swq_find(params->dev_name);
+               if (swq == NULL)
+                       return -1;
+
+               pp.ring.ring = swq->r;
+
+               p.ops = &rte_port_ring_reader_ops;
+               p.arg_create = &pp.ring;
+               break;
+       }
+
+       case PORT_IN_TMGR:
+       {
+               struct tmgr_port *tmgr_port;
+
+               tmgr_port = tmgr_port_find(params->dev_name);
+               if (tmgr_port == NULL)
+                       return -1;
+
+               pp.sched.sched = tmgr_port->s;
+
+               p.ops = &rte_port_sched_reader_ops;
+               p.arg_create = &pp.sched;
+               break;
+       }
+
+       case PORT_IN_TAP:
+       {
+               struct tap *tap;
+               struct mempool *mempool;
+
+               tap = tap_find(params->dev_name);
+               mempool = mempool_find(params->tap.mempool_name);
+               if ((tap == NULL) || (mempool == NULL))
+                       return -1;
+
+               pp.fd.fd = tap->fd;
+               pp.fd.mempool = mempool->m;
+               pp.fd.mtu = params->tap.mtu;
+
+               p.ops = &rte_port_fd_reader_ops;
+               p.arg_create = &pp.fd;
+               break;
+       }
+
+       case PORT_IN_KNI:
+       {
+               struct kni *kni;
+
+               kni = kni_find(params->dev_name);
+               if (kni == NULL)
+                       return -1;
+
+               pp.kni.kni = kni->k;
+
+               p.ops = &rte_port_kni_reader_ops;
+               p.arg_create = &pp.kni;
+               break;
+       }
+
+       case PORT_IN_SOURCE:
+       {
+               struct mempool *mempool;
+
+               mempool = mempool_find(params->source.mempool_name);
+               if (mempool == NULL)
+                       return -1;
+
+               pp.source.mempool = mempool->m;
+               pp.source.file_name = params->source.file_name;
+               pp.source.n_bytes_per_pkt = params->source.n_bytes_per_pkt;
+
+               p.ops = &rte_port_source_ops;
+               p.arg_create = &pp.source;
+               break;
+       }
+
+       default:
+               return -1;
+       }
+
+       p.f_action = NULL;
+       p.arg_ah = NULL;
+       p.burst_size = params->burst_size;
+
+       /* Resource create */
+       status = rte_pipeline_port_in_create(pipeline->p,
+               &p,
+               &port_id);
+
+       if (status)
+               return -1;
+
+       if (enabled)
+               rte_pipeline_port_in_enable(pipeline->p, port_id);
+
+       /* Pipeline */
+       pipeline->n_ports_in++;
+
+       return 0;
+}
+
+int
+pipeline_port_in_connect_to_table(const char *pipeline_name,
+       uint32_t port_id,
+       uint32_t table_id)
+{
+       struct pipeline *pipeline;
+       int status;
+
+       /* Check input params */
+       if (pipeline_name == NULL)
+               return -1;
+
+       pipeline = pipeline_find(pipeline_name);
+       if ((pipeline == NULL) ||
+               (port_id >= pipeline->n_ports_in) ||
+               (table_id >= pipeline->n_tables))
+               return -1;
+
+       /* Resource */
+       status = rte_pipeline_port_in_connect_to_table(pipeline->p,
+               port_id,
+               table_id);
+
+       return status;
+
+}
+
+int
+pipeline_port_out_create(const char *pipeline_name,
+       struct port_out_params *params)
+{
+       struct rte_pipeline_port_out_params p;
+
+       union {
+               struct rte_port_ethdev_writer_params ethdev;
+               struct rte_port_ring_writer_params ring;
+               struct rte_port_sched_writer_params sched;
+               struct rte_port_fd_writer_params fd;
+               struct rte_port_kni_writer_params kni;
+               struct rte_port_sink_params sink;
+       } pp;
+
+       union {
+               struct rte_port_ethdev_writer_nodrop_params ethdev;
+               struct rte_port_ring_writer_nodrop_params ring;
+               struct rte_port_fd_writer_nodrop_params fd;
+               struct rte_port_kni_writer_nodrop_params kni;
+       } pp_nodrop;
+
+       struct pipeline *pipeline;
+       uint32_t port_id;
+       int status;
+
+       memset(&pp, 0, sizeof(pp));
+       memset(&pp_nodrop, 0, sizeof(pp_nodrop));
+
+       /* Check input params */
+       if ((pipeline_name == NULL) ||
+               (params == NULL) ||
+               (params->burst_size == 0) ||
+               (params->burst_size > RTE_PORT_IN_BURST_SIZE_MAX))
+               return -1;
+
+       pipeline = pipeline_find(pipeline_name);
+       if (pipeline == NULL)
+               return -1;
+
+       switch (params->type) {
+       case PORT_OUT_TXQ:
+       {
+               struct link *link;
+
+               link = link_find(params->dev_name);
+               if (link == NULL)
+                       return -1;
+
+               if (params->txq.queue_id >= link->n_txq)
+                       return -1;
+
+               pp.ethdev.port_id = link->port_id;
+               pp.ethdev.queue_id = params->txq.queue_id;
+               pp.ethdev.tx_burst_sz = params->burst_size;
+
+               pp_nodrop.ethdev.port_id = link->port_id;
+               pp_nodrop.ethdev.queue_id = params->txq.queue_id;
+               pp_nodrop.ethdev.tx_burst_sz = params->burst_size;
+               pp_nodrop.ethdev.n_retries = params->n_retries;
+
+               if (params->retry == 0) {
+                       p.ops = &rte_port_ethdev_writer_ops;
+                       p.arg_create = &pp.ethdev;
+               } else {
+                       p.ops = &rte_port_ethdev_writer_nodrop_ops;
+                       p.arg_create = &pp_nodrop.ethdev;
+               }
+               break;
+       }
+
+       case PORT_OUT_SWQ:
+       {
+               struct swq *swq;
+
+               swq = swq_find(params->dev_name);
+               if (swq == NULL)
+                       return -1;
+
+               pp.ring.ring = swq->r;
+               pp.ring.tx_burst_sz = params->burst_size;
+
+               pp_nodrop.ring.ring = swq->r;
+               pp_nodrop.ring.tx_burst_sz = params->burst_size;
+               pp_nodrop.ring.n_retries = params->n_retries;
+
+               if (params->retry == 0) {
+                       p.ops = &rte_port_ring_writer_ops;
+                       p.arg_create = &pp.ring;
+               } else {
+                       p.ops = &rte_port_ring_writer_nodrop_ops;
+                       p.arg_create = &pp_nodrop.ring;
+               }
+               break;
+       }
+
+       case PORT_OUT_TMGR:
+       {
+               struct tmgr_port *tmgr_port;
+
+               tmgr_port = tmgr_port_find(params->dev_name);
+               if (tmgr_port == NULL)
+                       return -1;
+
+               pp.sched.sched = tmgr_port->s;
+               pp.sched.tx_burst_sz = params->burst_size;
+
+               p.ops = &rte_port_sched_writer_ops;
+               p.arg_create = &pp.sched;
+               break;
+       }
+
+       case PORT_OUT_TAP:
+       {
+               struct tap *tap;
+
+               tap = tap_find(params->dev_name);
+               if (tap == NULL)
+                       return -1;
+
+               pp.fd.fd = tap->fd;
+               pp.fd.tx_burst_sz = params->burst_size;
+
+               pp_nodrop.fd.fd = tap->fd;
+               pp_nodrop.fd.tx_burst_sz = params->burst_size;
+               pp_nodrop.fd.n_retries = params->n_retries;
+
+               if (params->retry == 0) {
+                       p.ops = &rte_port_fd_writer_ops;
+                       p.arg_create = &pp.fd;
+               } else {
+                       p.ops = &rte_port_fd_writer_nodrop_ops;
+                       p.arg_create = &pp_nodrop.fd;
+               }
+               break;
+       }
+
+       case PORT_OUT_KNI:
+       {
+               struct kni *kni;
+
+               kni = kni_find(params->dev_name);
+               if (kni == NULL)
+                       return -1;
+
+               pp.kni.kni = kni->k;
+               pp.kni.tx_burst_sz = params->burst_size;
+
+               pp_nodrop.kni.kni = kni->k;
+               pp_nodrop.kni.tx_burst_sz = params->burst_size;
+               pp_nodrop.kni.n_retries = params->n_retries;
+
+               if (params->retry == 0) {
+                       p.ops = &rte_port_kni_writer_ops;
+                       p.arg_create = &pp.kni;
+               } else {
+                       p.ops = &rte_port_kni_writer_nodrop_ops;
+                       p.arg_create = &pp_nodrop.kni;
+               }
+               break;
+       }
+
+       case PORT_OUT_SINK:
+       {
+               pp.sink.file_name = params->sink.file_name;
+               pp.sink.max_n_pkts = params->sink.max_n_pkts;
+
+               p.ops = &rte_port_sink_ops;
+               p.arg_create = &pp.sink;
+               break;
+       }
+
+       default:
+               return -1;
+       }
+
+       p.f_action = NULL;
+       p.arg_ah = NULL;
+
+       /* Resource create */
+       status = rte_pipeline_port_out_create(pipeline->p,
+               &p,
+               &port_id);
+
+       if (status)
+               return -1;
+
+       /* Pipeline */
+       pipeline->n_ports_out++;
+
+       return 0;
+}
+
+static const struct rte_acl_field_def table_acl_field_format_ipv4[] = {
+       /* Protocol */
+       [0] = {
+               .type = RTE_ACL_FIELD_TYPE_BITMASK,
+               .size = sizeof(uint8_t),
+               .field_index = 0,
+               .input_index = 0,
+               .offset = offsetof(struct ipv4_hdr, next_proto_id),
+       },
+
+       /* Source IP address (IPv4) */
+       [1] = {
+               .type = RTE_ACL_FIELD_TYPE_MASK,
+               .size = sizeof(uint32_t),
+               .field_index = 1,
+               .input_index = 1,
+               .offset = offsetof(struct ipv4_hdr, src_addr),
+       },
+
+       /* Destination IP address (IPv4) */
+       [2] = {
+               .type = RTE_ACL_FIELD_TYPE_MASK,
+               .size = sizeof(uint32_t),
+               .field_index = 2,
+               .input_index = 2,
+               .offset = offsetof(struct ipv4_hdr, dst_addr),
+       },
+
+       /* Source Port */
+       [3] = {
+               .type = RTE_ACL_FIELD_TYPE_RANGE,
+               .size = sizeof(uint16_t),
+               .field_index = 3,
+               .input_index = 3,
+               .offset = sizeof(struct ipv4_hdr) +
+                       offsetof(struct tcp_hdr, src_port),
+       },
+
+       /* Destination Port */
+       [4] = {
+               .type = RTE_ACL_FIELD_TYPE_RANGE,
+               .size = sizeof(uint16_t),
+               .field_index = 4,
+               .input_index = 3,
+               .offset = sizeof(struct ipv4_hdr) +
+                       offsetof(struct tcp_hdr, dst_port),
+       },
+};
+
+static const struct rte_acl_field_def table_acl_field_format_ipv6[] = {
+       /* Protocol */
+       [0] = {
+               .type = RTE_ACL_FIELD_TYPE_BITMASK,
+               .size = sizeof(uint8_t),
+               .field_index = 0,
+               .input_index = 0,
+               .offset = offsetof(struct ipv6_hdr, proto),
+       },
+
+       /* Source IP address (IPv6) */
+       [1] = {
+               .type = RTE_ACL_FIELD_TYPE_MASK,
+               .size = sizeof(uint32_t),
+               .field_index = 1,
+               .input_index = 1,
+               .offset = offsetof(struct ipv6_hdr, src_addr[0]),
+       },
+
+       [2] = {
+               .type = RTE_ACL_FIELD_TYPE_MASK,
+               .size = sizeof(uint32_t),
+               .field_index = 2,
+               .input_index = 2,
+               .offset = offsetof(struct ipv6_hdr, src_addr[4]),
+       },
+
+       [3] = {
+               .type = RTE_ACL_FIELD_TYPE_MASK,
+               .size = sizeof(uint32_t),
+               .field_index = 3,
+               .input_index = 3,
+               .offset = offsetof(struct ipv6_hdr, src_addr[8]),
+       },
+
+       [4] = {
+               .type = RTE_ACL_FIELD_TYPE_MASK,
+               .size = sizeof(uint32_t),
+               .field_index = 4,
+               .input_index = 4,
+               .offset = offsetof(struct ipv6_hdr, src_addr[12]),
+       },
+
+       /* Destination IP address (IPv6) */
+       [5] = {
+               .type = RTE_ACL_FIELD_TYPE_MASK,
+               .size = sizeof(uint32_t),
+               .field_index = 5,
+               .input_index = 5,
+               .offset = offsetof(struct ipv6_hdr, dst_addr[0]),
+       },
+
+       [6] = {
+               .type = RTE_ACL_FIELD_TYPE_MASK,
+               .size = sizeof(uint32_t),
+               .field_index = 6,
+               .input_index = 6,
+               .offset = offsetof(struct ipv6_hdr, dst_addr[4]),
+       },
+
+       [7] = {
+               .type = RTE_ACL_FIELD_TYPE_MASK,
+               .size = sizeof(uint32_t),
+               .field_index = 7,
+               .input_index = 7,
+               .offset = offsetof(struct ipv6_hdr, dst_addr[8]),
+       },
+
+       [8] = {
+               .type = RTE_ACL_FIELD_TYPE_MASK,
+               .size = sizeof(uint32_t),
+               .field_index = 8,
+               .input_index = 8,
+               .offset = offsetof(struct ipv6_hdr, dst_addr[12]),
+       },
+
+       /* Source Port */
+       [9] = {
+               .type = RTE_ACL_FIELD_TYPE_RANGE,
+               .size = sizeof(uint16_t),
+               .field_index = 9,
+               .input_index = 9,
+               .offset = sizeof(struct ipv6_hdr) +
+                       offsetof(struct tcp_hdr, src_port),
+       },
+
+       /* Destination Port */
+       [10] = {
+               .type = RTE_ACL_FIELD_TYPE_RANGE,
+               .size = sizeof(uint16_t),
+               .field_index = 10,
+               .input_index = 9,
+               .offset = sizeof(struct ipv6_hdr) +
+                       offsetof(struct tcp_hdr, dst_port),
+       },
+};
+
+int
+pipeline_table_create(const char *pipeline_name,
+       struct table_params *params)
+{
+       char name[NAME_MAX];
+       struct rte_pipeline_table_params p;
+
+       union {
+               struct rte_table_acl_params acl;
+               struct rte_table_array_params array;
+               struct rte_table_hash_params hash;
+               struct rte_table_lpm_params lpm;
+               struct rte_table_lpm_ipv6_params lpm_ipv6;
+       } pp;
+
+       struct pipeline *pipeline;
+       struct table *table;
+       struct table_action_profile *ap;
+       struct rte_table_action *action;
+       uint32_t table_id;
+       int status;
+
+       memset(&p, 0, sizeof(p));
+       memset(&pp, 0, sizeof(pp));
+
+       /* Check input params */
+       if ((pipeline_name == NULL) ||
+               (params == NULL) ||
+               (params->action_profile_name == NULL))
+               return -1;
+
+       pipeline = pipeline_find(pipeline_name);
+       if ((pipeline == NULL) ||
+               (pipeline->n_tables >= PIPELINE_TABLE_MAX))
+               return -1;
+
+       ap = table_action_profile_find(params->action_profile_name);
+       if (ap == NULL)
+               return -1;
+
+       snprintf(name, NAME_MAX, "%s_table%u",
+               pipeline_name, pipeline->n_tables);
+
+       switch (params->match_type) {
+       case TABLE_ACL:
+       {
+               uint32_t ip_header_offset = params->match.acl.ip_header_offset -
+                       (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM);
+               uint32_t i;
+
+               if (params->match.acl.n_rules == 0)
+                       return -1;
+
+               pp.acl.name = name;
+               pp.acl.n_rules = params->match.acl.n_rules;
+               if (params->match.acl.ip_version) {
+                       memcpy(&pp.acl.field_format,
+                               &table_acl_field_format_ipv4,
+                               sizeof(table_acl_field_format_ipv4));
+                       pp.acl.n_rule_fields =
+                               RTE_DIM(table_acl_field_format_ipv4);
+               } else {
+                       memcpy(&pp.acl.field_format,
+                               &table_acl_field_format_ipv6,
+                               sizeof(table_acl_field_format_ipv6));
+                       pp.acl.n_rule_fields =
+                               RTE_DIM(table_acl_field_format_ipv6);
+               }
+
+               for (i = 0; i < pp.acl.n_rule_fields; i++)
+                       pp.acl.field_format[i].offset += ip_header_offset;
+
+               p.ops = &rte_table_acl_ops;
+               p.arg_create = &pp.acl;
+               break;
+       }
+
+       case TABLE_ARRAY:
+       {
+               if (params->match.array.n_keys == 0)
+                       return -1;
+
+               pp.array.n_entries = params->match.array.n_keys;
+               pp.array.offset = params->match.array.key_offset;
+
+               p.ops = &rte_table_array_ops;
+               p.arg_create = &pp.array;
+               break;
+       }
+
+       case TABLE_HASH:
+       {
+               struct rte_table_ops *ops;
+               rte_table_hash_op_hash f_hash;
+
+               if (params->match.hash.n_keys == 0)
+                       return -1;
+
+               switch (params->match.hash.key_size) {
+               case  8:
+                       f_hash = hash_default_key8;
+                       break;
+               case 16:
+                       f_hash = hash_default_key16;
+                       break;
+               case 24:
+                       f_hash = hash_default_key24;
+                       break;
+               case 32:
+                       f_hash = hash_default_key32;
+                       break;
+               case 40:
+                       f_hash = hash_default_key40;
+                       break;
+               case 48:
+                       f_hash = hash_default_key48;
+                       break;
+               case 56:
+                       f_hash = hash_default_key56;
+                       break;
+               case 64:
+                       f_hash = hash_default_key64;
+                       break;
+               default:
+                       return -1;
+               }
+
+               pp.hash.name = name;
+               pp.hash.key_size = params->match.hash.key_size;
+               pp.hash.key_offset = params->match.hash.key_offset;
+               pp.hash.key_mask = params->match.hash.key_mask;
+               pp.hash.n_keys = params->match.hash.n_keys;
+               pp.hash.n_buckets = params->match.hash.n_buckets;
+               pp.hash.f_hash = f_hash;
+               pp.hash.seed = 0;
+
+               if (params->match.hash.extendable_bucket)
+                       switch (params->match.hash.key_size) {
+                       case  8:
+                               ops = &rte_table_hash_key8_ext_ops;
+                               break;
+                       case 16:
+                               ops = &rte_table_hash_key16_ext_ops;
+                               break;
+                       default:
+                               ops = &rte_table_hash_ext_ops;
+                       }
+               else
+                       switch (params->match.hash.key_size) {
+                       case  8:
+                               ops = &rte_table_hash_key8_lru_ops;
+                               break;
+                       case 16:
+                               ops = &rte_table_hash_key16_lru_ops;
+                               break;
+                       default:
+                               ops = &rte_table_hash_lru_ops;
+                       }
+
+               p.ops = ops;
+               p.arg_create = &pp.hash;
+               break;
+       }
+
+       case TABLE_LPM:
+       {
+               if (params->match.lpm.n_rules == 0)
+                       return -1;
+
+               switch (params->match.lpm.key_size) {
+               case 4:
+               {
+                       pp.lpm.name = name;
+                       pp.lpm.n_rules = params->match.lpm.n_rules;
+                       pp.lpm.number_tbl8s = TABLE_LPM_NUMBER_TBL8;
+                       pp.lpm.flags = 0;
+                       pp.lpm.entry_unique_size = p.action_data_size +
+                               sizeof(struct rte_pipeline_table_entry);
+                       pp.lpm.offset = params->match.lpm.key_offset;
+
+                       p.ops = &rte_table_lpm_ops;
+                       p.arg_create = &pp.lpm;
+                       break;
+               }
+
+               case 16:
+               {
+                       pp.lpm_ipv6.name = name;
+                       pp.lpm_ipv6.n_rules = params->match.lpm.n_rules;
+                       pp.lpm_ipv6.number_tbl8s = TABLE_LPM_NUMBER_TBL8;
+                       pp.lpm_ipv6.entry_unique_size = p.action_data_size +
+                               sizeof(struct rte_pipeline_table_entry);
+                       pp.lpm_ipv6.offset = params->match.lpm.key_offset;
+
+                       p.ops = &rte_table_lpm_ipv6_ops;
+                       p.arg_create = &pp.lpm_ipv6;
+                       break;
+               }
+
+               default:
+                       return -1;
+               }
+
+               break;
+       }
+
+       case TABLE_STUB:
+       {
+               p.ops = &rte_table_stub_ops;
+               p.arg_create = NULL;
+               break;
+       }
+
+       default:
+               return -1;
+       }
+
+       /* Resource create */
+       action = rte_table_action_create(ap->ap,
+               pipeline->cpu_id);
+       if (action == NULL)
+               return -1;
+
+       status = rte_table_action_table_params_get(
+               action,
+               &p);
+       if (status ||
+               ((p.action_data_size +
+               sizeof(struct rte_pipeline_table_entry)) >
+               TABLE_RULE_ACTION_SIZE_MAX)) {
+               rte_table_action_free(action);
+               return -1;
+       }
+
+       if (params->match_type == TABLE_LPM) {
+               if (params->match.lpm.key_size == 4)
+                       pp.lpm.entry_unique_size = p.action_data_size +
+                               sizeof(struct rte_pipeline_table_entry);
+
+               if (params->match.lpm.key_size == 16)
+                       pp.lpm_ipv6.entry_unique_size = p.action_data_size +
+                               sizeof(struct rte_pipeline_table_entry);
+       }
+
+       status = rte_pipeline_table_create(pipeline->p,
+               &p,
+               &table_id);
+       if (status) {
+               rte_table_action_free(action);
+               return -1;
+       }
+
+       /* Pipeline */
+       table = &pipeline->table[pipeline->n_tables];
+       memcpy(&table->params, params, sizeof(*params));
+       table->ap = ap;
+       table->a = action;
+       pipeline->n_tables++;
+
+       return 0;
+}
diff --git a/examples/ip_pipeline/pipeline.h b/examples/ip_pipeline/pipeline.h
new file mode 100644
index 0000000..dbc6f77
--- /dev/null
+++ b/examples/ip_pipeline/pipeline.h
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2018 Intel Corporation
+ */
+
+#ifndef _INCLUDE_PIPELINE_H_
+#define _INCLUDE_PIPELINE_H_
+
+#include <stdint.h>
+#include <sys/queue.h>
+
+#include <rte_pipeline.h>
+#include <rte_table_action.h>
+
+#include "common.h"
+#include "action.h"
+
+struct pipeline_params {
+       uint32_t timer_period_ms;
+       uint32_t offset_port_id;
+       uint32_t cpu_id;
+};
+
+enum port_in_type {
+       PORT_IN_RXQ,
+       PORT_IN_SWQ,
+       PORT_IN_TMGR,
+       PORT_IN_TAP,
+       PORT_IN_KNI,
+       PORT_IN_SOURCE,
+};
+
+struct port_in_params {
+       enum port_in_type type;
+       const char *dev_name;
+       union {
+               struct {
+                       uint16_t queue_id;
+               } rxq;
+
+               struct {
+                       const char *mempool_name;
+                       uint32_t mtu;
+               } tap;
+
+               struct {
+                       const char *mempool_name;
+                       const char *file_name;
+                       uint32_t n_bytes_per_pkt;
+               } source;
+       };
+       uint32_t burst_size;
+};
+
+enum port_out_type {
+       PORT_OUT_TXQ,
+       PORT_OUT_SWQ,
+       PORT_OUT_TMGR,
+       PORT_OUT_TAP,
+       PORT_OUT_KNI,
+       PORT_OUT_SINK,
+};
+
+struct port_out_params {
+       enum port_out_type type;
+       const char *dev_name;
+       union {
+               struct {
+                       uint16_t queue_id;
+               } txq;
+
+               struct {
+                       const char *file_name;
+                       uint32_t max_n_pkts;
+               } sink;
+       };
+       uint32_t burst_size;
+       int retry;
+       uint32_t n_retries;
+};
+
+enum table_type {
+       TABLE_ACL,
+       TABLE_ARRAY,
+       TABLE_HASH,
+       TABLE_LPM,
+       TABLE_STUB,
+};
+
+struct table_acl_params {
+       uint32_t n_rules;
+       uint32_t ip_header_offset;
+       int ip_version;
+};
+
+struct table_array_params {
+       uint32_t n_keys;
+       uint32_t key_offset;
+};
+
+struct table_hash_params {
+       uint32_t n_keys;
+       uint32_t key_offset;
+       uint32_t key_size;
+       uint8_t *key_mask;
+       uint32_t n_buckets;
+       int extendable_bucket;
+};
+
+struct table_lpm_params {
+       uint32_t n_rules;
+       uint32_t key_offset;
+       uint32_t key_size;
+};
+
+struct table_params {
+       /* Match */
+       enum table_type match_type;
+       union {
+               struct table_acl_params acl;
+               struct table_array_params array;
+               struct table_hash_params hash;
+               struct table_lpm_params lpm;
+       } match;
+
+       /* Action */
+       const char *action_profile_name;
+};
+
+struct table {
+       struct table_params params;
+       struct table_action_profile *ap;
+       struct rte_table_action *a;
+};
+
+#ifndef PIPELINE_TABLE_MAX
+#define PIPELINE_TABLE_MAX                                 256
+#endif
+
+struct pipeline {
+       TAILQ_ENTRY(pipeline) node;
+       char name[NAME_SIZE];
+
+       struct rte_pipeline *p;
+       struct table table[PIPELINE_TABLE_MAX];
+       uint32_t n_ports_in;
+       uint32_t n_ports_out;
+       uint32_t n_tables;
+
+       struct rte_ring *msgq_req;
+       struct rte_ring *msgq_rsp;
+       uint32_t timer_period_ms;
+
+       int enabled;
+       uint32_t thread_id;
+       uint32_t cpu_id;
+};
+
+TAILQ_HEAD(pipeline_list, pipeline);
+
+int
+pipeline_init(void);
+
+struct pipeline *
+pipeline_find(const char *name);
+
+struct pipeline *
+pipeline_create(const char *name, struct pipeline_params *params);
+
+int
+pipeline_port_in_create(const char *pipeline_name,
+       struct port_in_params *params,
+       int enabled);
+
+int
+pipeline_port_in_connect_to_table(const char *pipeline_name,
+       uint32_t port_id,
+       uint32_t table_id);
+
+int
+pipeline_port_out_create(const char *pipeline_name,
+       struct port_out_params *params);
+
+int
+pipeline_table_create(const char *pipeline_name,
+       struct table_params *params);
+
+struct table_rule_match_acl {
+       int ip_version;
+
+       RTE_STD_C11
+       union {
+               struct {
+                       uint32_t sa;
+                       uint32_t da;
+               } ipv4;
+
+               struct {
+                       uint8_t sa[16];
+                       uint8_t da[16];
+               } ipv6;
+       };
+
+       uint32_t sa_depth;
+       uint32_t da_depth;
+       uint16_t sp0;
+       uint16_t sp1;
+       uint16_t dp0;
+       uint16_t dp1;
+       uint8_t proto;
+       uint8_t proto_mask;
+       uint32_t priority;
+};
+
+struct table_rule_match_array {
+       uint32_t pos;
+};
+
+#ifndef TABLE_RULE_MATCH_SIZE_MAX
+#define TABLE_RULE_MATCH_SIZE_MAX                          256
+#endif
+
+#ifndef TABLE_RULE_ACTION_SIZE_MAX
+#define TABLE_RULE_ACTION_SIZE_MAX                         2048
+#endif
+
+struct table_rule_match_hash {
+       uint8_t key[TABLE_RULE_MATCH_SIZE_MAX];
+};
+
+struct table_rule_match_lpm {
+       int ip_version;
+
+       RTE_STD_C11
+       union {
+               uint32_t ipv4;
+               uint8_t ipv6[16];
+       };
+
+       uint8_t depth;
+};
+
+struct table_rule_match {
+       enum table_type match_type;
+
+       union {
+               struct table_rule_match_acl acl;
+               struct table_rule_match_array array;
+               struct table_rule_match_hash hash;
+               struct table_rule_match_lpm lpm;
+       } match;
+};
+
+struct table_rule_action {
+       uint64_t action_mask;
+       struct rte_table_action_fwd_params fwd;
+       struct rte_table_action_mtr_params mtr;
+       struct rte_table_action_tm_params tm;
+       struct rte_table_action_encap_params encap;
+       struct rte_table_action_nat_params nat;
+       struct rte_table_action_ttl_params ttl;
+       struct rte_table_action_stats_params stats;
+       struct rte_table_action_time_params time;
+};
+
+#endif /* _INCLUDE_PIPELINE_H_ */
-- 
2.9.3

Reply via email to