net/ixgbe/base: update X550 SFP identification
[dpdk.git] / drivers / net / softnic / rte_eth_softnic_cli.c
index 59688ff..0c7448c 100644 (file)
@@ -185,1335 +185,1519 @@ cmd_swq(struct pmd_internals *softnic,
 }
 
 /**
- * tap <tap_name>
+ * tmgr shaper profile
+ *  id <profile_id>
+ *  rate <tb_rate> size <tb_size>
+ *  adj <packet_length_adjust>
  */
 static void
-cmd_tap(struct pmd_internals *softnic,
+cmd_tmgr_shaper_profile(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       char *name;
-       struct softnic_tap *tap;
+       struct rte_tm_shaper_params sp;
+       struct rte_tm_error error;
+       uint32_t shaper_profile_id;
+       uint16_t port_id;
+       int status;
 
-       if (n_tokens != 2) {
+       memset(&sp, 0, sizeof(struct rte_tm_shaper_params));
+
+       if (n_tokens != 11) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       name = tokens[1];
+       if (strcmp(tokens[1], "shaper") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
+               return;
+       }
 
-       tap = softnic_tap_create(softnic, name);
-       if (tap == NULL) {
+       if (strcmp(tokens[2], "profile") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
+               return;
+       }
+
+       if (strcmp(tokens[3], "id") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "id");
+               return;
+       }
+
+       if (softnic_parser_read_uint32(&shaper_profile_id, tokens[4]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "profile_id");
+               return;
+       }
+
+       if (strcmp(tokens[5], "rate") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rate");
+               return;
+       }
+
+       if (softnic_parser_read_uint64(&sp.peak.rate, tokens[6]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "tb_rate");
+               return;
+       }
+
+       if (strcmp(tokens[7], "size") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
+               return;
+       }
+
+       if (softnic_parser_read_uint64(&sp.peak.size, tokens[8]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "tb_size");
+               return;
+       }
+
+       if (strcmp(tokens[9], "adj") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "adj");
+               return;
+       }
+
+       if (softnic_parser_read_int32(&sp.pkt_length_adjust, tokens[10]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "packet_length_adjust");
+               return;
+       }
+
+       status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
+       if (status)
+               return;
+
+       status = rte_tm_shaper_profile_add(port_id, shaper_profile_id, &sp, &error);
+       if (status != 0) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
        }
 }
 
 /**
- * port in action profile <profile_name>
- *  [filter match | mismatch offset <key_offset> mask <key_mask> key <key_value> port <port_id>]
- *  [balance offset <key_offset> mask <key_mask> port <port_id0> ... <port_id15>]
+ * tmgr shared shaper
+ *  id <shared_shaper_id>
+ *  profile <shaper_profile_id>
  */
 static void
-cmd_port_in_action_profile(struct pmd_internals *softnic,
+cmd_tmgr_shared_shaper(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       struct softnic_port_in_action_profile_params p;
-       struct softnic_port_in_action_profile *ap;
-       char *name;
-       uint32_t t0;
-
-       memset(&p, 0, sizeof(p));
+       struct rte_tm_error error;
+       uint32_t shared_shaper_id, shaper_profile_id;
+       uint16_t port_id;
+       int status;
 
-       if (n_tokens < 5) {
+       if (n_tokens != 7) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       if (strcmp(tokens[1], "in") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+       if (strcmp(tokens[1], "shared") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shared");
                return;
        }
 
-       if (strcmp(tokens[2], "action") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
+       if (strcmp(tokens[2], "shaper") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
                return;
        }
 
-       if (strcmp(tokens[3], "profile") != 0) {
+       if (strcmp(tokens[3], "id") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "id");
+               return;
+       }
+
+       if (softnic_parser_read_uint32(&shared_shaper_id, tokens[4]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "shared_shaper_id");
+               return;
+       }
+
+       if (strcmp(tokens[5], "profile") != 0) {
                snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
                return;
        }
 
-       name = tokens[4];
+       if (softnic_parser_read_uint32(&shaper_profile_id, tokens[6]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "shaper_profile_id");
+               return;
+       }
 
-       t0 = 5;
+       status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
+       if (status)
+               return;
 
-       if (t0 < n_tokens &&
-               (strcmp(tokens[t0], "filter") == 0)) {
-               uint32_t size;
+       status = rte_tm_shared_shaper_add_update(port_id,
+               shared_shaper_id,
+               shaper_profile_id,
+               &error);
+       if (status != 0) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
 
-               if (n_tokens < t0 + 10) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH, "port in action profile filter");
-                       return;
-               }
+/**
+ * tmgr node
+ *   id <node_id>
+ *   parent <parent_node_id | none>
+ *   priority <priority>
+ *   weight <weight>
+ *   [shaper profile <shaper_profile_id>]
+ *   [shared shaper <shared_shaper_id>]
+ *   [nonleaf sp <n_sp_priorities>]
+ */
+static void
+cmd_tmgr_node(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct rte_tm_error error;
+       struct rte_tm_node_params np;
+       uint32_t node_id, parent_node_id, priority, weight, shared_shaper_id;
+       uint16_t port_id;
+       int status;
 
-               if (strcmp(tokens[t0 + 1], "match") == 0) {
-                       p.fltr.filter_on_match = 1;
-               } else if (strcmp(tokens[t0 + 1], "mismatch") == 0) {
-                       p.fltr.filter_on_match = 0;
-               } else {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "match or mismatch");
-                       return;
-               }
+       memset(&np, 0, sizeof(struct rte_tm_node_params));
+       np.shaper_profile_id = RTE_TM_SHAPER_PROFILE_ID_NONE;
+       np.nonleaf.n_sp_priorities = 1;
 
-               if (strcmp(tokens[t0 + 2], "offset") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
-                       return;
-               }
+       if (n_tokens < 10) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
 
-               if (softnic_parser_read_uint32(&p.fltr.key_offset,
-                       tokens[t0 + 3]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
-                       return;
-               }
+       if (strcmp(tokens[1], "node") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "node");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 4], "mask") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
-                       return;
-               }
+       if (strcmp(tokens[2], "id") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "id");
+               return;
+       }
 
-               size = RTE_PORT_IN_ACTION_FLTR_KEY_SIZE;
-               if ((softnic_parse_hex_string(tokens[t0 + 5],
-                       p.fltr.key_mask, &size) != 0) ||
-                       size != RTE_PORT_IN_ACTION_FLTR_KEY_SIZE) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
-                       return;
-               }
+       if (softnic_parser_read_uint32(&node_id, tokens[3]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "node_id");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 6], "key") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
-                       return;
-               }
+       if (strcmp(tokens[4], "parent") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "parent");
+               return;
+       }
 
-               size = RTE_PORT_IN_ACTION_FLTR_KEY_SIZE;
-               if ((softnic_parse_hex_string(tokens[t0 + 7],
-                       p.fltr.key, &size) != 0) ||
-                       size != RTE_PORT_IN_ACTION_FLTR_KEY_SIZE) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "key_value");
+       if (strcmp(tokens[5], "none") == 0)
+               parent_node_id = RTE_TM_NODE_ID_NULL;
+       else {
+               if (softnic_parser_read_uint32(&parent_node_id, tokens[5]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "parent_node_id");
                        return;
                }
+       }
 
-               if (strcmp(tokens[t0 + 8], "port") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
-                       return;
-               }
+       if (strcmp(tokens[6], "priority") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "priority");
+               return;
+       }
 
-               if (softnic_parser_read_uint32(&p.fltr.port_id,
-                       tokens[t0 + 9]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
-                       return;
-               }
+       if (softnic_parser_read_uint32(&priority, tokens[7]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "priority");
+               return;
+       }
 
-               p.action_mask |= 1LLU << RTE_PORT_IN_ACTION_FLTR;
-               t0 += 10;
-       } /* filter */
+       if (strcmp(tokens[8], "weight") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "weight");
+               return;
+       }
 
-       if (t0 < n_tokens &&
-               (strcmp(tokens[t0], "balance") == 0)) {
-               uint32_t i;
+       if (softnic_parser_read_uint32(&weight, tokens[9]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "weight");
+               return;
+       }
 
-               if (n_tokens < t0 + 22) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "port in action profile balance");
+       tokens += 10;
+       n_tokens -= 10;
+
+       if (n_tokens >= 2 &&
+               (strcmp(tokens[0], "shaper") == 0) &&
+               (strcmp(tokens[1], "profile") == 0)) {
+               if (n_tokens < 3) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, "tmgr node");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 1], "offset") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
-                       return;
+               if (strcmp(tokens[2], "none") == 0) {
+                       np.shaper_profile_id = RTE_TM_SHAPER_PROFILE_ID_NONE;
+               } else {
+                       if (softnic_parser_read_uint32(&np.shaper_profile_id, tokens[2]) != 0) {
+                               snprintf(out, out_size, MSG_ARG_INVALID, "shaper_profile_id");
+                               return;
+                       }
                }
 
-               if (softnic_parser_read_uint32(&p.lb.key_offset,
-                       tokens[t0 + 2]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
+               tokens += 3;
+               n_tokens -= 3;
+       } /* shaper profile */
+
+       if (n_tokens >= 2 &&
+               (strcmp(tokens[0], "shared") == 0) &&
+               (strcmp(tokens[1], "shaper") == 0)) {
+               if (n_tokens < 3) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, "tmgr node");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 3], "mask") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
+               if (softnic_parser_read_uint32(&shared_shaper_id, tokens[2]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "shared_shaper_id");
                        return;
                }
 
-               p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
-               if (softnic_parse_hex_string(tokens[t0 + 4],
-                       p.lb.key_mask, &p.lb.key_size) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
+               np.shared_shaper_id = &shared_shaper_id;
+               np.n_shared_shapers = 1;
+
+               tokens += 3;
+               n_tokens -= 3;
+       } /* shared shaper */
+
+       if (n_tokens >= 2 &&
+               (strcmp(tokens[0], "nonleaf") == 0) &&
+               (strcmp(tokens[1], "sp") == 0)) {
+               if (n_tokens < 3) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, "tmgr node");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 5], "port") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
+               if (softnic_parser_read_uint32(&np.nonleaf.n_sp_priorities, tokens[2]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "n_sp_priorities");
                        return;
                }
 
-               for (i = 0; i < 16; i++)
-                       if (softnic_parser_read_uint32(&p.lb.port_id[i],
-                       tokens[t0 + 6 + i]) != 0) {
-                               snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
-                               return;
-                       }
-
-               p.action_mask |= 1LLU << RTE_PORT_IN_ACTION_LB;
-               t0 += 22;
-       } /* balance */
+               tokens += 3;
+               n_tokens -= 3;
+       } /* nonleaf sp <n_sp_priorities> */
 
-       if (t0 < n_tokens) {
+       if (n_tokens) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       ap = softnic_port_in_action_profile_create(softnic, name, &p);
-       if (ap == NULL) {
+       status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
+       if (status != 0)
+               return;
+
+       status = rte_tm_node_add(port_id,
+               node_id,
+               parent_node_id,
+               priority,
+               weight,
+               RTE_TM_NODE_LEVEL_ID_ANY,
+               &np,
+               &error);
+       if (status != 0) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
        }
 }
 
-/**
- * table action profile <profile_name>
- *  ipv4 | ipv6
- *  offset <ip_offset>
- *  fwd
- *  [balance offset <key_offset> mask <key_mask> outoffset <out_offset>]
- *  [meter srtcm | trtcm
- *      tc <n_tc>
- *      stats none | pkts | bytes | both]
- *  [tm spp <n_subports_per_port> pps <n_pipes_per_subport>]
- *  [encap ether | vlan | qinq | mpls | pppoe]
- *  [nat src | dst
- *      proto udp | tcp]
- *  [ttl drop | fwd
- *      stats none | pkts]
- *  [stats pkts | bytes | both]
- *  [time]
- */
-static void
-cmd_table_action_profile(struct pmd_internals *softnic,
-       char **tokens,
-       uint32_t n_tokens,
-       char *out,
-       size_t out_size)
+static uint32_t
+root_node_id(uint32_t n_spp,
+       uint32_t n_pps)
 {
-       struct softnic_table_action_profile_params p;
-       struct softnic_table_action_profile *ap;
-       char *name;
-       uint32_t t0;
-
-       memset(&p, 0, sizeof(p));
+       uint32_t n_queues = n_spp * n_pps * RTE_SCHED_QUEUES_PER_PIPE;
+       uint32_t n_tc = n_spp * n_pps * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
+       uint32_t n_pipes = n_spp * n_pps;
 
-       if (n_tokens < 8) {
-               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-               return;
-       }
+       return n_queues + n_tc + n_pipes + n_spp;
+}
 
-       if (strcmp(tokens[1], "action") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
-               return;
-       }
+static uint32_t
+subport_node_id(uint32_t n_spp,
+       uint32_t n_pps,
+       uint32_t subport_id)
+{
+       uint32_t n_pipes = n_spp * n_pps;
+       uint32_t n_tc = n_pipes * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
+       uint32_t n_queues = n_pipes * RTE_SCHED_QUEUES_PER_PIPE;
 
-       if (strcmp(tokens[2], "profile") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
-               return;
-       }
+       return n_queues + n_tc + n_pipes + subport_id;
+}
 
-       name = tokens[3];
+static uint32_t
+pipe_node_id(uint32_t n_spp,
+       uint32_t n_pps,
+       uint32_t subport_id,
+       uint32_t pipe_id)
+{
+       uint32_t n_pipes = n_spp * n_pps;
+       uint32_t n_tc = n_pipes * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
+       uint32_t n_queues = n_pipes * RTE_SCHED_QUEUES_PER_PIPE;
+
+       return n_queues +
+               n_tc +
+               pipe_id +
+               subport_id * n_pps;
+}
 
-       if (strcmp(tokens[4], "ipv4") == 0) {
-               p.common.ip_version = 1;
-       } else if (strcmp(tokens[4], "ipv6") == 0) {
-               p.common.ip_version = 0;
-       } else {
-               snprintf(out, out_size, MSG_ARG_INVALID, "ipv4 or ipv6");
+static uint32_t
+tc_node_id(uint32_t n_spp,
+       uint32_t n_pps,
+       uint32_t subport_id,
+       uint32_t pipe_id,
+       uint32_t tc_id)
+{
+       uint32_t n_pipes = n_spp * n_pps;
+       uint32_t n_queues = n_pipes * RTE_SCHED_QUEUES_PER_PIPE;
+
+       return n_queues +
+               tc_id +
+               (pipe_id + subport_id * n_pps) * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
+}
+
+static uint32_t
+queue_node_id(uint32_t n_spp __rte_unused,
+       uint32_t n_pps,
+       uint32_t subport_id,
+       uint32_t pipe_id,
+       uint32_t tc_id,
+       uint32_t queue_id)
+{
+       return queue_id +
+               tc_id * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE +
+               (pipe_id + subport_id * n_pps) * RTE_SCHED_QUEUES_PER_PIPE;
+}
+
+struct tmgr_hierarchy_default_params {
+       uint32_t n_spp; /**< Number of subports per port. */
+       uint32_t n_pps; /**< Number of pipes per subport. */
+
+       struct {
+               uint32_t port;
+               uint32_t subport;
+               uint32_t pipe;
+               uint32_t tc[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE];
+       } shaper_profile_id;
+
+       struct {
+               uint32_t tc[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE];
+               uint32_t tc_valid[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE];
+       } shared_shaper_id;
+
+       struct {
+               uint32_t queue[RTE_SCHED_QUEUES_PER_PIPE];
+       } weight;
+};
+
+static int
+tmgr_hierarchy_default(struct pmd_internals *softnic,
+       struct tmgr_hierarchy_default_params *params)
+{
+       struct rte_tm_node_params root_node_params = {
+               .shaper_profile_id = params->shaper_profile_id.port,
+               .nonleaf = {
+                       .n_sp_priorities = 1,
+               },
+       };
+
+       struct rte_tm_node_params subport_node_params = {
+               .shaper_profile_id = params->shaper_profile_id.subport,
+               .nonleaf = {
+                       .n_sp_priorities = 1,
+               },
+       };
+
+       struct rte_tm_node_params pipe_node_params = {
+               .shaper_profile_id = params->shaper_profile_id.pipe,
+               .nonleaf = {
+                       .n_sp_priorities = RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE,
+               },
+       };
+
+       struct rte_tm_node_params tc_node_params[] = {
+               [0] = {
+                       .shaper_profile_id = params->shaper_profile_id.tc[0],
+                       .shared_shaper_id = &params->shared_shaper_id.tc[0],
+                       .n_shared_shapers =
+                               (&params->shared_shaper_id.tc_valid[0]) ? 1 : 0,
+                       .nonleaf = {
+                               .n_sp_priorities = 1,
+                       },
+               },
+
+               [1] = {
+                       .shaper_profile_id = params->shaper_profile_id.tc[1],
+                       .shared_shaper_id = &params->shared_shaper_id.tc[1],
+                       .n_shared_shapers =
+                               (&params->shared_shaper_id.tc_valid[1]) ? 1 : 0,
+                       .nonleaf = {
+                               .n_sp_priorities = 1,
+                       },
+               },
+
+               [2] = {
+                       .shaper_profile_id = params->shaper_profile_id.tc[2],
+                       .shared_shaper_id = &params->shared_shaper_id.tc[2],
+                       .n_shared_shapers =
+                               (&params->shared_shaper_id.tc_valid[2]) ? 1 : 0,
+                       .nonleaf = {
+                               .n_sp_priorities = 1,
+                       },
+               },
+
+               [3] = {
+                       .shaper_profile_id = params->shaper_profile_id.tc[3],
+                       .shared_shaper_id = &params->shared_shaper_id.tc[3],
+                       .n_shared_shapers =
+                               (&params->shared_shaper_id.tc_valid[3]) ? 1 : 0,
+                       .nonleaf = {
+                               .n_sp_priorities = 1,
+                       },
+               },
+       };
+
+       struct rte_tm_node_params queue_node_params = {
+               .shaper_profile_id = RTE_TM_SHAPER_PROFILE_ID_NONE,
+       };
+
+       struct rte_tm_error error;
+       uint32_t n_spp = params->n_spp, n_pps = params->n_pps, s;
+       int status;
+       uint16_t port_id;
+
+       status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
+       if (status)
+               return -1;
+
+       /* Hierarchy level 0: Root node */
+       status = rte_tm_node_add(port_id,
+               root_node_id(n_spp, n_pps),
+               RTE_TM_NODE_ID_NULL,
+               0,
+               1,
+               RTE_TM_NODE_LEVEL_ID_ANY,
+               &root_node_params,
+               &error);
+       if (status)
+               return -1;
+
+       /* Hierarchy level 1: Subport nodes */
+       for (s = 0; s < params->n_spp; s++) {
+               uint32_t p;
+
+               status = rte_tm_node_add(port_id,
+                       subport_node_id(n_spp, n_pps, s),
+                       root_node_id(n_spp, n_pps),
+                       0,
+                       1,
+                       RTE_TM_NODE_LEVEL_ID_ANY,
+                       &subport_node_params,
+                       &error);
+               if (status)
+                       return -1;
+
+               /* Hierarchy level 2: Pipe nodes */
+               for (p = 0; p < params->n_pps; p++) {
+                       uint32_t t;
+
+                       status = rte_tm_node_add(port_id,
+                               pipe_node_id(n_spp, n_pps, s, p),
+                               subport_node_id(n_spp, n_pps, s),
+                               0,
+                               1,
+                               RTE_TM_NODE_LEVEL_ID_ANY,
+                               &pipe_node_params,
+                               &error);
+                       if (status)
+                               return -1;
+
+                       /* Hierarchy level 3: Traffic class nodes */
+                       for (t = 0; t < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; t++) {
+                               uint32_t q;
+
+                               status = rte_tm_node_add(port_id,
+                                       tc_node_id(n_spp, n_pps, s, p, t),
+                                       pipe_node_id(n_spp, n_pps, s, p),
+                                       t,
+                                       1,
+                                       RTE_TM_NODE_LEVEL_ID_ANY,
+                                       &tc_node_params[t],
+                                       &error);
+                               if (status)
+                                       return -1;
+
+                               /* Hierarchy level 4: Queue nodes */
+                               for (q = 0; q < RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS; q++) {
+                                       status = rte_tm_node_add(port_id,
+                                               queue_node_id(n_spp, n_pps, s, p, t, q),
+                                               tc_node_id(n_spp, n_pps, s, p, t),
+                                               0,
+                                               params->weight.queue[q],
+                                               RTE_TM_NODE_LEVEL_ID_ANY,
+                                               &queue_node_params,
+                                               &error);
+                                       if (status)
+                                               return -1;
+                               } /* Queue */
+                       } /* TC */
+               } /* Pipe */
+       } /* Subport */
+
+       return 0;
+}
+
+
+/**
+ * tmgr hierarchy-default
+ *  spp <n_subports_per_port>
+ *  pps <n_pipes_per_subport>
+ *  shaper profile
+ *   port <profile_id>
+ *   subport <profile_id>
+ *   pipe <profile_id>
+ *   tc0 <profile_id>
+ *   tc1 <profile_id>
+ *   tc2 <profile_id>
+ *   tc3 <profile_id>
+ *  shared shaper
+ *   tc0 <id | none>
+ *   tc1 <id | none>
+ *   tc2 <id | none>
+ *   tc3 <id | none>
+ *  weight
+ *   queue  <q0> ... <q15>
+ */
+static void
+cmd_tmgr_hierarchy_default(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct tmgr_hierarchy_default_params p;
+       int i, status;
+
+       memset(&p, 0, sizeof(p));
+
+       if (n_tokens != 50) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       if (strcmp(tokens[5], "offset") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
+       if (strcmp(tokens[1], "hierarchy-default") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "hierarchy-default");
                return;
        }
 
-       if (softnic_parser_read_uint32(&p.common.ip_offset,
-               tokens[6]) != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "ip_offset");
+       if (strcmp(tokens[2], "spp") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
                return;
        }
 
-       if (strcmp(tokens[7], "fwd") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "fwd");
+       if (softnic_parser_read_uint32(&p.n_spp, tokens[3]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "n_subports_per_port");
                return;
        }
 
-       p.action_mask |= 1LLU << RTE_TABLE_ACTION_FWD;
+       if (strcmp(tokens[4], "pps") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
+               return;
+       }
 
-       t0 = 8;
-       if (t0 < n_tokens &&
-               (strcmp(tokens[t0], "balance") == 0)) {
-               if (n_tokens < t0 + 7) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH, "table action profile balance");
-                       return;
-               }
+       if (softnic_parser_read_uint32(&p.n_pps, tokens[5]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "n_pipes_per_subport");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 1], "offset") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
-                       return;
-               }
+       /* Shaper profile */
 
-               if (softnic_parser_read_uint32(&p.lb.key_offset,
-                       tokens[t0 + 2]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
-                       return;
-               }
+       if (strcmp(tokens[6], "shaper") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 3], "mask") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
-                       return;
-               }
+       if (strcmp(tokens[7], "profile") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
+               return;
+       }
 
-               p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
-               if (softnic_parse_hex_string(tokens[t0 + 4],
-                       p.lb.key_mask, &p.lb.key_size) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
-                       return;
-               }
+       if (strcmp(tokens[8], "port") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 5], "outoffset") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "outoffset");
-                       return;
-               }
+       if (softnic_parser_read_uint32(&p.shaper_profile_id.port, tokens[9]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "port profile id");
+               return;
+       }
 
-               if (softnic_parser_read_uint32(&p.lb.out_offset,
-                       tokens[t0 + 6]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "out_offset");
-                       return;
-               }
+       if (strcmp(tokens[10], "subport") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "subport");
+               return;
+       }
 
-               p.action_mask |= 1LLU << RTE_TABLE_ACTION_LB;
-               t0 += 7;
-       } /* balance */
+       if (softnic_parser_read_uint32(&p.shaper_profile_id.subport, tokens[11]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "subport profile id");
+               return;
+       }
 
-       if (t0 < n_tokens &&
-               (strcmp(tokens[t0], "meter") == 0)) {
-               if (n_tokens < t0 + 6) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "table action profile meter");
-                       return;
-               }
+       if (strcmp(tokens[12], "pipe") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipe");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 1], "srtcm") == 0) {
-                       p.mtr.alg = RTE_TABLE_ACTION_METER_SRTCM;
-               } else if (strcmp(tokens[t0 + 1], "trtcm") == 0) {
-                       p.mtr.alg = RTE_TABLE_ACTION_METER_TRTCM;
-               } else {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "srtcm or trtcm");
-                       return;
-               }
+       if (softnic_parser_read_uint32(&p.shaper_profile_id.pipe, tokens[13]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipe_profile_id");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 2], "tc") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc");
-                       return;
-               }
+       if (strcmp(tokens[14], "tc0") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc0");
+               return;
+       }
 
-               if (softnic_parser_read_uint32(&p.mtr.n_tc,
-                       tokens[t0 + 3]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "n_tc");
-                       return;
-               }
+       if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[0], tokens[15]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "tc0 profile id");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 4], "stats") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
-                       return;
-               }
+       if (strcmp(tokens[16], "tc1") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc1");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 5], "none") == 0) {
-                       p.mtr.n_packets_enabled = 0;
-                       p.mtr.n_bytes_enabled = 0;
-               } else if (strcmp(tokens[t0 + 5], "pkts") == 0) {
-                       p.mtr.n_packets_enabled = 1;
-                       p.mtr.n_bytes_enabled = 0;
-               } else if (strcmp(tokens[t0 + 5], "bytes") == 0) {
-                       p.mtr.n_packets_enabled = 0;
-                       p.mtr.n_bytes_enabled = 1;
-               } else if (strcmp(tokens[t0 + 5], "both") == 0) {
-                       p.mtr.n_packets_enabled = 1;
-                       p.mtr.n_bytes_enabled = 1;
-               } else {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "none or pkts or bytes or both");
-                       return;
-               }
+       if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[1], tokens[17]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "tc1 profile id");
+               return;
+       }
 
-               p.action_mask |= 1LLU << RTE_TABLE_ACTION_MTR;
-               t0 += 6;
-       } /* meter */
+       if (strcmp(tokens[18], "tc2") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc2");
+               return;
+       }
 
-       if (t0 < n_tokens &&
-               (strcmp(tokens[t0], "tm") == 0)) {
-               if (n_tokens < t0 + 5) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "table action profile tm");
-                       return;
-               }
+       if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[2], tokens[19]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "tc2 profile id");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 1], "spp") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
-                       return;
-               }
+       if (strcmp(tokens[20], "tc3") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc3");
+               return;
+       }
 
-               if (softnic_parser_read_uint32(&p.tm.n_subports_per_port,
-                       tokens[t0 + 2]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "n_subports_per_port");
-                       return;
-               }
+       if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[3], tokens[21]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "tc3 profile id");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 3], "pps") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
-                       return;
-               }
+       /* Shared shaper */
 
-               if (softnic_parser_read_uint32(&p.tm.n_pipes_per_subport,
-                       tokens[t0 + 4]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "n_pipes_per_subport");
+       if (strcmp(tokens[22], "shared") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shared");
+               return;
+       }
+
+       if (strcmp(tokens[23], "shaper") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
+               return;
+       }
+
+       if (strcmp(tokens[24], "tc0") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc0");
+               return;
+       }
+
+       if (strcmp(tokens[25], "none") == 0)
+               p.shared_shaper_id.tc_valid[0] = 0;
+       else {
+               if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[0], tokens[25]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc0");
                        return;
                }
 
-               p.action_mask |= 1LLU << RTE_TABLE_ACTION_TM;
-               t0 += 5;
-       } /* tm */
+               p.shared_shaper_id.tc_valid[0] = 1;
+       }
 
-       if (t0 < n_tokens &&
-               (strcmp(tokens[t0], "encap") == 0)) {
-               if (n_tokens < t0 + 2) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "action profile encap");
+       if (strcmp(tokens[26], "tc1") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc1");
+               return;
+       }
+
+       if (strcmp(tokens[27], "none") == 0)
+               p.shared_shaper_id.tc_valid[1] = 0;
+       else {
+               if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[1], tokens[27]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc1");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 1], "ether") == 0) {
-                       p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_ETHER;
-               } else if (strcmp(tokens[t0 + 1], "vlan") == 0) {
-                       p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_VLAN;
-               } else if (strcmp(tokens[t0 + 1], "qinq") == 0) {
-                       p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_QINQ;
-               } else if (strcmp(tokens[t0 + 1], "mpls") == 0) {
-                       p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_MPLS;
-               } else if (strcmp(tokens[t0 + 1], "pppoe") == 0) {
-                       p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_PPPOE;
-               } else {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH, "encap");
+               p.shared_shaper_id.tc_valid[1] = 1;
+       }
+
+       if (strcmp(tokens[28], "tc2") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc2");
+               return;
+       }
+
+       if (strcmp(tokens[29], "none") == 0)
+               p.shared_shaper_id.tc_valid[2] = 0;
+       else {
+               if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[2], tokens[29]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc2");
                        return;
                }
 
-               p.action_mask |= 1LLU << RTE_TABLE_ACTION_ENCAP;
-               t0 += 2;
-       } /* encap */
+               p.shared_shaper_id.tc_valid[2] = 1;
+       }
 
-       if (t0 < n_tokens &&
-               (strcmp(tokens[t0], "nat") == 0)) {
-               if (n_tokens < t0 + 4) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "table action profile nat");
+       if (strcmp(tokens[30], "tc3") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc3");
+               return;
+       }
+
+       if (strcmp(tokens[31], "none") == 0)
+               p.shared_shaper_id.tc_valid[3] = 0;
+       else {
+               if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[3], tokens[31]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc3");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 1], "src") == 0) {
-                       p.nat.source_nat = 1;
-               } else if (strcmp(tokens[t0 + 1], "dst") == 0) {
-                       p.nat.source_nat = 0;
-               } else {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "src or dst");
-                       return;
-               }
-
-               if (strcmp(tokens[t0 + 2], "proto") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "proto");
-                       return;
-               }
-
-               if (strcmp(tokens[t0 + 3], "tcp") == 0) {
-                       p.nat.proto = 0x06;
-               } else if (strcmp(tokens[t0 + 3], "udp") == 0) {
-                       p.nat.proto = 0x11;
-               } else {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "tcp or udp");
-                       return;
-               }
-
-               p.action_mask |= 1LLU << RTE_TABLE_ACTION_NAT;
-               t0 += 4;
-       } /* nat */
+               p.shared_shaper_id.tc_valid[3] = 1;
+       }
 
-       if (t0 < n_tokens &&
-               (strcmp(tokens[t0], "ttl") == 0)) {
-               if (n_tokens < t0 + 4) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "table action profile ttl");
-                       return;
-               }
+       /* Weight */
 
-               if (strcmp(tokens[t0 + 1], "drop") == 0) {
-                       p.ttl.drop = 1;
-               } else if (strcmp(tokens[t0 + 1], "fwd") == 0) {
-                       p.ttl.drop = 0;
-               } else {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "drop or fwd");
-                       return;
-               }
+       if (strcmp(tokens[32], "weight") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "weight");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 2], "stats") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
-                       return;
-               }
+       if (strcmp(tokens[33], "queue") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queue");
+               return;
+       }
 
-               if (strcmp(tokens[t0 + 3], "none") == 0) {
-                       p.ttl.n_packets_enabled = 0;
-               } else if (strcmp(tokens[t0 + 3], "pkts") == 0) {
-                       p.ttl.n_packets_enabled = 1;
-               } else {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "none or pkts");
+       for (i = 0; i < 16; i++) {
+               if (softnic_parser_read_uint32(&p.weight.queue[i], tokens[34 + i]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "weight queue");
                        return;
                }
+       }
 
-               p.action_mask |= 1LLU << RTE_TABLE_ACTION_TTL;
-               t0 += 4;
-       } /* ttl */
-
-       if (t0 < n_tokens &&
-               (strcmp(tokens[t0], "stats") == 0)) {
-               if (n_tokens < t0 + 2) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "table action profile stats");
-                       return;
-               }
+       status = tmgr_hierarchy_default(softnic, &p);
+       if (status != 0) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
 
-               if (strcmp(tokens[t0 + 1], "pkts") == 0) {
-                       p.stats.n_packets_enabled = 1;
-                       p.stats.n_bytes_enabled = 0;
-               } else if (strcmp(tokens[t0 + 1], "bytes") == 0) {
-                       p.stats.n_packets_enabled = 0;
-                       p.stats.n_bytes_enabled = 1;
-               } else if (strcmp(tokens[t0 + 1], "both") == 0) {
-                       p.stats.n_packets_enabled = 1;
-                       p.stats.n_bytes_enabled = 1;
-               } else {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "pkts or bytes or both");
-                       return;
-               }
+/**
+ * tmgr hierarchy commit
+ */
+static void
+cmd_tmgr_hierarchy_commit(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct rte_tm_error error;
+       uint16_t port_id;
+       int status;
 
-               p.action_mask |= 1LLU << RTE_TABLE_ACTION_STATS;
-               t0 += 2;
-       } /* stats */
+       if (n_tokens != 3) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
 
-       if (t0 < n_tokens &&
-               (strcmp(tokens[t0], "time") == 0)) {
-               p.action_mask |= 1LLU << RTE_TABLE_ACTION_TIME;
-               t0 += 1;
-       } /* time */
+       if (strcmp(tokens[1], "hierarchy") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "hierarchy");
+               return;
+       }
 
-       if (t0 < n_tokens) {
-               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+       if (strcmp(tokens[2], "commit") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "commit");
                return;
        }
 
-       ap = softnic_table_action_profile_create(softnic, name, &p);
-       if (ap == NULL) {
+       status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
+       if (status != 0)
+               return;
+
+       status = rte_tm_hierarchy_commit(port_id, 1, &error);
+       if (status) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
        }
 }
 
 /**
- * pipeline <pipeline_name>
- *  period <timer_period_ms>
- *  offset_port_id <offset_port_id>
+ * tmgr <tmgr_name>
  */
 static void
-cmd_pipeline(struct pmd_internals *softnic,
+cmd_tmgr(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       struct pipeline_params p;
        char *name;
-       struct pipeline *pipeline;
+       struct softnic_tmgr_port *tmgr_port;
 
-       if (n_tokens != 6) {
+       if (n_tokens != 2) {
                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");
+       tmgr_port = softnic_tmgr_port_create(softnic, name);
+       if (tmgr_port == NULL) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
        }
+}
 
-       if (softnic_parser_read_uint32(&p.timer_period_ms,
-               tokens[3]) != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
-               return;
-       }
+/**
+ * tap <tap_name>
+ */
+static void
+cmd_tap(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       char *name;
+       struct softnic_tap *tap;
 
-       if (strcmp(tokens[4], "offset_port_id") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset_port_id");
+       if (n_tokens != 2) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       if (softnic_parser_read_uint32(&p.offset_port_id,
-               tokens[5]) != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "offset_port_id");
-               return;
-       }
+       name = tokens[1];
 
-       pipeline = softnic_pipeline_create(softnic, name, &p);
-       if (pipeline == NULL) {
+       tap = softnic_tap_create(softnic, name);
+       if (tap == 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>
- *  | source mempool <mempool_name> file <file_name> bpp <n_bytes_per_pkt>
- *  [action <port_in_action_profile_name>]
- *  [disabled]
+ * port in action profile <profile_name>
+ *  [filter match | mismatch offset <key_offset> mask <key_mask> key <key_value> port <port_id>]
+ *  [balance offset <key_offset> mask <key_mask> port <port_id0> ... <port_id15>]
  */
 static void
-cmd_pipeline_port_in(struct pmd_internals *softnic,
+cmd_port_in_action_profile(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       struct softnic_port_in_params p;
-       char *pipeline_name;
+       struct softnic_port_in_action_profile_params p;
+       struct softnic_port_in_action_profile *ap;
+       char *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];
+       memset(&p, 0, sizeof(p));
 
-       if (strcmp(tokens[2], "port") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
+       if (n_tokens < 5) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       if (strcmp(tokens[3], "in") != 0) {
+       if (strcmp(tokens[1], "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");
+       if (strcmp(tokens[2], "action") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
                return;
        }
 
-       if (softnic_parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
+       if (strcmp(tokens[3], "profile") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
                return;
        }
 
-       t0 = 6;
+       name = tokens[4];
 
-       if (strcmp(tokens[t0], "link") == 0) {
-               if (n_tokens < t0 + 4) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "pipeline port in link");
+       t0 = 5;
+
+       if (t0 < n_tokens &&
+               (strcmp(tokens[t0], "filter") == 0)) {
+               uint32_t size;
+
+               if (n_tokens < t0 + 10) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, "port in action profile filter");
                        return;
                }
 
-               p.type = PORT_IN_RXQ;
-
-               p.dev_name = tokens[t0 + 1];
+               if (strcmp(tokens[t0 + 1], "match") == 0) {
+                       p.fltr.filter_on_match = 1;
+               } else if (strcmp(tokens[t0 + 1], "mismatch") == 0) {
+                       p.fltr.filter_on_match = 0;
+               } else {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "match or mismatch");
+                       return;
+               }
 
-               if (strcmp(tokens[t0 + 2], "rxq") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
+               if (strcmp(tokens[t0 + 2], "offset") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
                        return;
                }
 
-               if (softnic_parser_read_uint16(&p.rxq.queue_id,
+               if (softnic_parser_read_uint32(&p.fltr.key_offset,
                        tokens[t0 + 3]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "queue_id");
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
                        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");
+
+               if (strcmp(tokens[t0 + 4], "mask") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
                        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");
+               size = RTE_PORT_IN_ACTION_FLTR_KEY_SIZE;
+               if ((softnic_parse_hex_string(tokens[t0 + 5],
+                       p.fltr.key_mask, &size) != 0) ||
+                       size != RTE_PORT_IN_ACTION_FLTR_KEY_SIZE) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
                        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");
+               if (strcmp(tokens[t0 + 6], "key") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
                        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");
+               size = RTE_PORT_IN_ACTION_FLTR_KEY_SIZE;
+               if ((softnic_parse_hex_string(tokens[t0 + 7],
+                       p.fltr.key, &size) != 0) ||
+                       size != RTE_PORT_IN_ACTION_FLTR_KEY_SIZE) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_value");
                        return;
                }
 
-               p.tap.mempool_name = tokens[t0 + 3];
-
-               if (strcmp(tokens[t0 + 4], "mtu") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "mtu");
+               if (strcmp(tokens[t0 + 8], "port") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
                        return;
                }
 
-               if (softnic_parser_read_uint32(&p.tap.mtu,
-                       tokens[t0 + 5]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
+               if (softnic_parser_read_uint32(&p.fltr.port_id,
+                       tokens[t0 + 9]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
                        return;
                }
 
-               t0 += 6;
-       } else if (strcmp(tokens[t0], "source") == 0) {
-               if (n_tokens < t0 + 6) {
+               p.action_mask |= 1LLU << RTE_PORT_IN_ACTION_FLTR;
+               t0 += 10;
+       } /* filter */
+
+       if (t0 < n_tokens &&
+               (strcmp(tokens[t0], "balance") == 0)) {
+               uint32_t i;
+
+               if (n_tokens < t0 + 22) {
                        snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "pipeline port in source");
+                               "port in action profile balance");
                        return;
                }
 
-               p.type = PORT_IN_SOURCE;
-
-               p.dev_name = NULL;
-
-               if (strcmp(tokens[t0 + 1], "mempool") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "mempool");
+               if (strcmp(tokens[t0 + 1], "offset") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
                        return;
                }
 
-               p.source.mempool_name = tokens[t0 + 2];
-
-               if (strcmp(tokens[t0 + 3], "file") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "file");
+               if (softnic_parser_read_uint32(&p.lb.key_offset,
+                       tokens[t0 + 2]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
                        return;
                }
 
-               p.source.file_name = tokens[t0 + 4];
-
-               if (strcmp(tokens[t0 + 5], "bpp") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "bpp");
+               if (strcmp(tokens[t0 + 3], "mask") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
                        return;
                }
 
-               if (softnic_parser_read_uint32(&p.source.n_bytes_per_pkt,
-                       tokens[t0 + 6]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "n_bytes_per_pkt");
+               p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
+               if (softnic_parse_hex_string(tokens[t0 + 4],
+                       p.lb.key_mask, &p.lb.key_size) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
                        return;
                }
 
-               t0 += 7;
-       } else {
-               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-               return;
-       }
-
-       p.action_profile_name = NULL;
-       if (n_tokens > t0 &&
-               (strcmp(tokens[t0], "action") == 0)) {
-               if (n_tokens < t0 + 2) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
+               if (strcmp(tokens[t0 + 5], "port") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
                        return;
                }
 
-               p.action_profile_name = tokens[t0 + 1];
-
-               t0 += 2;
-       }
-
-       enabled = 1;
-       if (n_tokens > t0 &&
-               (strcmp(tokens[t0], "disabled") == 0)) {
-               enabled = 0;
+               for (i = 0; i < 16; i++)
+                       if (softnic_parser_read_uint32(&p.lb.port_id[i],
+                       tokens[t0 + 6 + i]) != 0) {
+                               snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
+                               return;
+                       }
 
-               t0 += 1;
-       }
+               p.action_mask |= 1LLU << RTE_PORT_IN_ACTION_LB;
+               t0 += 22;
+       } /* balance */
 
-       if (n_tokens != t0) {
+       if (t0 < n_tokens) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       status = softnic_pipeline_port_in_create(softnic,
-               pipeline_name,
-               &p,
-               enabled);
-       if (status) {
+       ap = softnic_port_in_action_profile_create(softnic, name, &p);
+       if (ap == NULL) {
                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>
- *  | sink [file <file_name> pkts <max_n_pkts>]
+ * table action profile <profile_name>
+ *  ipv4 | ipv6
+ *  offset <ip_offset>
+ *  fwd
+ *  [balance offset <key_offset> mask <key_mask> outoffset <out_offset>]
+ *  [meter srtcm | trtcm
+ *      tc <n_tc>
+ *      stats none | pkts | bytes | both]
+ *  [tm spp <n_subports_per_port> pps <n_pipes_per_subport>]
+ *  [encap ether | vlan | qinq | mpls | pppoe]
+ *  [nat src | dst
+ *      proto udp | tcp]
+ *  [ttl drop | fwd
+ *      stats none | pkts]
+ *  [stats pkts | bytes | both]
+ *  [time]
  */
 static void
-cmd_pipeline_port_out(struct pmd_internals *softnic,
+cmd_table_action_profile(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       struct softnic_port_out_params p;
-       char *pipeline_name;
-       int status;
+       struct softnic_table_action_profile_params p;
+       struct softnic_table_action_profile *ap;
+       char *name;
+       uint32_t t0;
 
        memset(&p, 0, sizeof(p));
 
-       if (n_tokens < 7) {
+       if (n_tokens < 8) {
                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");
+       if (strcmp(tokens[1], "action") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
                return;
        }
 
-       if (strcmp(tokens[3], "out") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
+       if (strcmp(tokens[2], "profile") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
                return;
        }
 
-       if (strcmp(tokens[4], "bsz") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
+       name = tokens[3];
+
+       if (strcmp(tokens[4], "ipv4") == 0) {
+               p.common.ip_version = 1;
+       } else if (strcmp(tokens[4], "ipv6") == 0) {
+               p.common.ip_version = 0;
+       } else {
+               snprintf(out, out_size, MSG_ARG_INVALID, "ipv4 or ipv6");
                return;
        }
 
-       if (softnic_parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
+       if (strcmp(tokens[5], "offset") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
                return;
        }
 
-       if (strcmp(tokens[6], "link") == 0) {
-               if (n_tokens != 10) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "pipeline port out link");
-                       return;
-               }
+       if (softnic_parser_read_uint32(&p.common.ip_offset,
+               tokens[6]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "ip_offset");
+               return;
+       }
 
-               p.type = PORT_OUT_TXQ;
+       if (strcmp(tokens[7], "fwd") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "fwd");
+               return;
+       }
 
-               p.dev_name = tokens[7];
+       p.action_mask |= 1LLU << RTE_TABLE_ACTION_FWD;
 
-               if (strcmp(tokens[8], "txq") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
+       t0 = 8;
+       if (t0 < n_tokens &&
+               (strcmp(tokens[t0], "balance") == 0)) {
+               if (n_tokens < t0 + 7) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, "table action profile balance");
                        return;
                }
 
-               if (softnic_parser_read_uint16(&p.txq.queue_id,
-                       tokens[9]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "queue_id");
+               if (strcmp(tokens[t0 + 1], "offset") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
                        return;
                }
-       } else if (strcmp(tokens[6], "swq") == 0) {
-               if (n_tokens != 8) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "pipeline port out swq");
+
+               if (softnic_parser_read_uint32(&p.lb.key_offset,
+                       tokens[t0 + 2]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
                        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");
+               if (strcmp(tokens[t0 + 3], "mask") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
                        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");
+               p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
+               if (softnic_parse_hex_string(tokens[t0 + 4],
+                       p.lb.key_mask, &p.lb.key_size) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
                        return;
                }
 
-               p.type = PORT_OUT_TAP;
+               if (strcmp(tokens[t0 + 5], "outoffset") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "outoffset");
+                       return;
+               }
 
-               p.dev_name = tokens[7];
-       } else if (strcmp(tokens[6], "sink") == 0) {
-               if ((n_tokens != 7) && (n_tokens != 11)) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "pipeline port out sink");
+               if (softnic_parser_read_uint32(&p.lb.out_offset,
+                       tokens[t0 + 6]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "out_offset");
                        return;
                }
 
-               p.type = PORT_OUT_SINK;
+               p.action_mask |= 1LLU << RTE_TABLE_ACTION_LB;
+               t0 += 7;
+       } /* balance */
 
-               p.dev_name = NULL;
-
-               if (n_tokens == 7) {
-                       p.sink.file_name = NULL;
-                       p.sink.max_n_pkts = 0;
-               } else {
-                       if (strcmp(tokens[7], "file") != 0) {
-                               snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                                       "file");
-                               return;
-                       }
-
-                       p.sink.file_name = tokens[8];
-
-                       if (strcmp(tokens[9], "pkts") != 0) {
-                               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkts");
-                               return;
-                       }
-
-                       if (softnic_parser_read_uint32(&p.sink.max_n_pkts,
-                               tokens[10]) != 0) {
-                               snprintf(out, out_size, MSG_ARG_INVALID, "max_n_pkts");
-                               return;
-                       }
-               }
-       } else {
-               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-               return;
-       }
-
-       status = softnic_pipeline_port_out_create(softnic, 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 <table_action_profile_name>]
- */
-static void
-cmd_pipeline_table(struct pmd_internals *softnic,
-       char **tokens,
-       uint32_t n_tokens,
-       char *out,
-       size_t out_size)
-{
-       uint8_t key_mask[TABLE_RULE_MATCH_SIZE_MAX];
-       struct softnic_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 (t0 < n_tokens &&
+               (strcmp(tokens[t0], "meter") == 0)) {
                if (n_tokens < t0 + 6) {
                        snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "pipeline table acl");
+                               "table action profile meter");
                        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;
+               if (strcmp(tokens[t0 + 1], "srtcm") == 0) {
+                       p.mtr.alg = RTE_TABLE_ACTION_METER_SRTCM;
+               } else if (strcmp(tokens[t0 + 1], "trtcm") == 0) {
+                       p.mtr.alg = RTE_TABLE_ACTION_METER_TRTCM;
                } else {
                        snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "ipv4 or ipv6");
+                               "srtcm or trtcm");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 2], "offset") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
+               if (strcmp(tokens[t0 + 2], "tc") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc");
                        return;
                }
 
-               if (softnic_parser_read_uint32(&p.match.acl.ip_header_offset,
+               if (softnic_parser_read_uint32(&p.mtr.n_tc,
                        tokens[t0 + 3]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "ip_header_offset");
+                       snprintf(out, out_size, MSG_ARG_INVALID, "n_tc");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 4], "size") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
+               if (strcmp(tokens[t0 + 4], "stats") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
                        return;
                }
 
-               if (softnic_parser_read_uint32(&p.match.acl.n_rules,
-                       tokens[t0 + 5]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
+               if (strcmp(tokens[t0 + 5], "none") == 0) {
+                       p.mtr.n_packets_enabled = 0;
+                       p.mtr.n_bytes_enabled = 0;
+               } else if (strcmp(tokens[t0 + 5], "pkts") == 0) {
+                       p.mtr.n_packets_enabled = 1;
+                       p.mtr.n_bytes_enabled = 0;
+               } else if (strcmp(tokens[t0 + 5], "bytes") == 0) {
+                       p.mtr.n_packets_enabled = 0;
+                       p.mtr.n_bytes_enabled = 1;
+               } else if (strcmp(tokens[t0 + 5], "both") == 0) {
+                       p.mtr.n_packets_enabled = 1;
+                       p.mtr.n_bytes_enabled = 1;
+               } else {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "none or pkts or bytes or both");
                        return;
                }
 
+               p.action_mask |= 1LLU << RTE_TABLE_ACTION_MTR;
                t0 += 6;
-       } else if (strcmp(tokens[t0], "array") == 0) {
+       } /* meter */
+
+       if (t0 < n_tokens &&
+               (strcmp(tokens[t0], "tm") == 0)) {
                if (n_tokens < t0 + 5) {
                        snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "pipeline table array");
+                               "table action profile tm");
                        return;
                }
 
-               p.match_type = TABLE_ARRAY;
-
-               if (strcmp(tokens[t0 + 1], "offset") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
+               if (strcmp(tokens[t0 + 1], "spp") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
                        return;
                }
 
-               if (softnic_parser_read_uint32(&p.match.array.key_offset,
+               if (softnic_parser_read_uint32(&p.tm.n_subports_per_port,
                        tokens[t0 + 2]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "n_subports_per_port");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 3], "size") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
+               if (strcmp(tokens[t0 + 3], "pps") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
                        return;
                }
 
-               if (softnic_parser_read_uint32(&p.match.array.n_keys,
+               if (softnic_parser_read_uint32(&p.tm.n_pipes_per_subport,
                        tokens[t0 + 4]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "n_pipes_per_subport");
                        return;
                }
 
+               p.action_mask |= 1LLU << RTE_TABLE_ACTION_TM;
                t0 += 5;
-       } else if (strcmp(tokens[t0], "hash") == 0) {
-               uint32_t key_mask_size = TABLE_RULE_MATCH_SIZE_MAX;
+       } /* tm */
 
-               if (n_tokens < t0 + 12) {
+       if (t0 < n_tokens &&
+               (strcmp(tokens[t0], "encap") == 0)) {
+               if (n_tokens < t0 + 2) {
                        snprintf(out, out_size, MSG_ARG_MISMATCH,
-                               "pipeline table hash");
+                               "action profile encap");
                        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;
+               if (strcmp(tokens[t0 + 1], "ether") == 0) {
+                       p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_ETHER;
+               } else if (strcmp(tokens[t0 + 1], "vlan") == 0) {
+                       p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_VLAN;
+               } else if (strcmp(tokens[t0 + 1], "qinq") == 0) {
+                       p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_QINQ;
+               } else if (strcmp(tokens[t0 + 1], "mpls") == 0) {
+                       p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_MPLS;
+               } else if (strcmp(tokens[t0 + 1], "pppoe") == 0) {
+                       p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_PPPOE;
                } else {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                               "ext or lru");
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, "encap");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 2], "key") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
+               p.action_mask |= 1LLU << RTE_TABLE_ACTION_ENCAP;
+               t0 += 2;
+       } /* encap */
+
+       if (t0 < n_tokens &&
+               (strcmp(tokens[t0], "nat") == 0)) {
+               if (n_tokens < t0 + 4) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "table action profile nat");
                        return;
                }
 
-               if ((softnic_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");
+               if (strcmp(tokens[t0 + 1], "src") == 0) {
+                       p.nat.source_nat = 1;
+               } else if (strcmp(tokens[t0 + 1], "dst") == 0) {
+                       p.nat.source_nat = 0;
+               } else {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "src or dst");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 4], "mask") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
+               if (strcmp(tokens[t0 + 2], "proto") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "proto");
                        return;
                }
 
-               if ((softnic_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");
+               if (strcmp(tokens[t0 + 3], "tcp") == 0) {
+                       p.nat.proto = 0x06;
+               } else if (strcmp(tokens[t0 + 3], "udp") == 0) {
+                       p.nat.proto = 0x11;
+               } else {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "tcp or udp");
                        return;
                }
-               p.match.hash.key_mask = key_mask;
 
-               if (strcmp(tokens[t0 + 6], "offset") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
+               p.action_mask |= 1LLU << RTE_TABLE_ACTION_NAT;
+               t0 += 4;
+       } /* nat */
+
+       if (t0 < n_tokens &&
+               (strcmp(tokens[t0], "ttl") == 0)) {
+               if (n_tokens < t0 + 4) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "table action profile ttl");
                        return;
                }
 
-               if (softnic_parser_read_uint32(&p.match.hash.key_offset,
-                       tokens[t0 + 7]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
+               if (strcmp(tokens[t0 + 1], "drop") == 0) {
+                       p.ttl.drop = 1;
+               } else if (strcmp(tokens[t0 + 1], "fwd") == 0) {
+                       p.ttl.drop = 0;
+               } else {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "drop or fwd");
                        return;
                }
 
-               if (strcmp(tokens[t0 + 8], "buckets") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buckets");
-                       return;
-               }
-
-               if (softnic_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 (softnic_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");
+               if (strcmp(tokens[t0 + 2], "stats") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
                        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;
+               if (strcmp(tokens[t0 + 3], "none") == 0) {
+                       p.ttl.n_packets_enabled = 0;
+               } else if (strcmp(tokens[t0 + 3], "pkts") == 0) {
+                       p.ttl.n_packets_enabled = 1;
                } 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");
+                               "none or pkts");
                        return;
                }
 
-               if (softnic_parser_read_uint32(&p.match.lpm.key_offset,
-                       tokens[t0 + 3]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
-                       return;
-               }
+               p.action_mask |= 1LLU << RTE_TABLE_ACTION_TTL;
+               t0 += 4;
+       } /* ttl */
 
-               if (strcmp(tokens[t0 + 4], "size") != 0) {
-                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
+       if (t0 < n_tokens &&
+               (strcmp(tokens[t0], "stats") == 0)) {
+               if (n_tokens < t0 + 2) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "table action profile stats");
                        return;
                }
 
-               if (softnic_parser_read_uint32(&p.match.lpm.n_rules,
-                       tokens[t0 + 5]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
+               if (strcmp(tokens[t0 + 1], "pkts") == 0) {
+                       p.stats.n_packets_enabled = 1;
+                       p.stats.n_bytes_enabled = 0;
+               } else if (strcmp(tokens[t0 + 1], "bytes") == 0) {
+                       p.stats.n_packets_enabled = 0;
+                       p.stats.n_bytes_enabled = 1;
+               } else if (strcmp(tokens[t0 + 1], "both") == 0) {
+                       p.stats.n_packets_enabled = 1;
+                       p.stats.n_bytes_enabled = 1;
+               } else {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "pkts or bytes or both");
                        return;
                }
 
-               t0 += 6;
-       } else if (strcmp(tokens[t0], "stub") == 0) {
-               p.match_type = TABLE_STUB;
+               p.action_mask |= 1LLU << RTE_TABLE_ACTION_STATS;
+               t0 += 2;
+       } /* stats */
 
+       if (t0 < n_tokens &&
+               (strcmp(tokens[t0], "time") == 0)) {
+               p.action_mask |= 1LLU << RTE_TABLE_ACTION_TIME;
                t0 += 1;
-       } else {
-               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
-               return;
-       }
-
-       p.action_profile_name = NULL;
-       if (n_tokens > t0 &&
-               (strcmp(tokens[t0], "action") == 0)) {
-               if (n_tokens < t0 + 2) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
-                       return;
-               }
-
-               p.action_profile_name = tokens[t0 + 1];
-
-               t0 += 2;
-       }
+       } /* time */
 
-       if (n_tokens > t0) {
+       if (t0 < n_tokens) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       status = softnic_pipeline_table_create(softnic, pipeline_name, &p);
-       if (status) {
+       ap = softnic_table_action_profile_create(softnic, name, &p);
+       if (ap == NULL) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
        }
 }
 
 /**
- * pipeline <pipeline_name> port in <port_id> table <table_id>
+ * pipeline <pipeline_name>
+ *  period <timer_period_ms>
+ *  offset_port_id <offset_port_id>
  */
 static void
-cmd_pipeline_port_in_table(struct pmd_internals *softnic,
+cmd_pipeline(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       char *pipeline_name;
-       uint32_t port_id, table_id;
-       int status;
+       struct pipeline_params p;
+       char *name;
+       struct pipeline *pipeline;
 
-       if (n_tokens != 7) {
+       if (n_tokens != 6) {
                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;
-       }
+       name = tokens[1];
 
-       if (strcmp(tokens[3], "in") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
+       if (strcmp(tokens[2], "period") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
                return;
        }
 
-       if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
+       if (softnic_parser_read_uint32(&p.timer_period_ms,
+               tokens[3]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
                return;
        }
 
-       if (strcmp(tokens[5], "table") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
+       if (strcmp(tokens[4], "offset_port_id") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset_port_id");
                return;
        }
 
-       if (softnic_parser_read_uint32(&table_id, tokens[6]) != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
+       if (softnic_parser_read_uint32(&p.offset_port_id,
+               tokens[5]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "offset_port_id");
                return;
        }
 
-       status = softnic_pipeline_port_in_connect_to_table(softnic,
-               pipeline_name,
-               port_id,
-               table_id);
-       if (status) {
+       pipeline = softnic_pipeline_create(softnic, name, &p);
+       if (pipeline == NULL) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
        }
 }
 
 /**
- * pipeline <pipeline_name> port in <port_id> enable
+ * 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>
+ *  | source mempool <mempool_name> file <file_name> bpp <n_bytes_per_pkt>
+ *  [action <port_in_action_profile_name>]
+ *  [disabled]
  */
 static void
-cmd_softnic_pipeline_port_in_enable(struct pmd_internals *softnic,
+cmd_pipeline_port_in(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
+       struct softnic_port_in_params p;
        char *pipeline_name;
-       uint32_t port_id;
-       int status;
+       uint32_t t0;
+       int enabled, status;
 
-       if (n_tokens != 6) {
+       if (n_tokens < 7) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
@@ -1530,70 +1714,1002 @@ cmd_softnic_pipeline_port_in_enable(struct pmd_internals *softnic,
                return;
        }
 
-       if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
+       if (strcmp(tokens[4], "bsz") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
                return;
        }
 
-       if (strcmp(tokens[5], "enable") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
+       if (softnic_parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
                return;
        }
 
-       status = softnic_pipeline_port_in_enable(softnic, pipeline_name, port_id);
-       if (status) {
-               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
-               return;
-       }
-}
+       t0 = 6;
 
-/**
- * pipeline <pipeline_name> port in <port_id> disable
- */
-static void
-cmd_softnic_pipeline_port_in_disable(struct pmd_internals *softnic,
-       char **tokens,
-       uint32_t n_tokens,
-       char *out,
-       size_t out_size)
-{
-       char *pipeline_name;
-       uint32_t port_id;
-       int status;
+       if (strcmp(tokens[t0], "link") == 0) {
+               if (n_tokens < t0 + 4) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port in link");
+                       return;
+               }
 
-       if (n_tokens != 6) {
-               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-               return;
-       }
+               p.type = PORT_IN_RXQ;
 
-       pipeline_name = tokens[1];
+               p.dev_name = tokens[t0 + 1];
 
-       if (strcmp(tokens[2], "port") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
-               return;
-       }
+               if (strcmp(tokens[t0 + 2], "rxq") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
+                       return;
+               }
 
-       if (strcmp(tokens[3], "in") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
-               return;
-       }
+               if (softnic_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;
+               }
 
-       if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
-               return;
-       }
+               p.type = PORT_IN_SWQ;
 
-       if (strcmp(tokens[5], "disable") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
-               return;
-       }
+               p.dev_name = tokens[t0 + 1];
 
-       status = softnic_pipeline_port_in_disable(softnic, pipeline_name, port_id);
-       if (status) {
-               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
-               return;
-       }
-}
+               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 (softnic_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], "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 = NULL;
+
+               if (strcmp(tokens[t0 + 1], "mempool") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "mempool");
+                       return;
+               }
+
+               p.source.mempool_name = tokens[t0 + 2];
+
+               if (strcmp(tokens[t0 + 3], "file") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "file");
+                       return;
+               }
+
+               p.source.file_name = tokens[t0 + 4];
+
+               if (strcmp(tokens[t0 + 5], "bpp") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                               "bpp");
+                       return;
+               }
+
+               if (softnic_parser_read_uint32(&p.source.n_bytes_per_pkt,
+                       tokens[t0 + 6]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "n_bytes_per_pkt");
+                       return;
+               }
+
+               t0 += 7;
+       } else {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       p.action_profile_name = NULL;
+       if (n_tokens > t0 &&
+               (strcmp(tokens[t0], "action") == 0)) {
+               if (n_tokens < t0 + 2) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
+                       return;
+               }
+
+               p.action_profile_name = tokens[t0 + 1];
+
+               t0 += 2;
+       }
+
+       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 = softnic_pipeline_port_in_create(softnic,
+               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>
+ *  | sink [file <file_name> pkts <max_n_pkts>]
+ */
+static void
+cmd_pipeline_port_out(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct softnic_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 (softnic_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 (softnic_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], "sink") == 0) {
+               if ((n_tokens != 7) && (n_tokens != 11)) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               "pipeline port out sink");
+                       return;
+               }
+
+               p.type = PORT_OUT_SINK;
+
+               p.dev_name = NULL;
+
+               if (n_tokens == 7) {
+                       p.sink.file_name = NULL;
+                       p.sink.max_n_pkts = 0;
+               } else {
+                       if (strcmp(tokens[7], "file") != 0) {
+                               snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                                       "file");
+                               return;
+                       }
+
+                       p.sink.file_name = tokens[8];
+
+                       if (strcmp(tokens[9], "pkts") != 0) {
+                               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkts");
+                               return;
+                       }
+
+                       if (softnic_parser_read_uint32(&p.sink.max_n_pkts,
+                               tokens[10]) != 0) {
+                               snprintf(out, out_size, MSG_ARG_INVALID, "max_n_pkts");
+                               return;
+                       }
+               }
+       } else {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       status = softnic_pipeline_port_out_create(softnic, 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 <table_action_profile_name>]
+ */
+static void
+cmd_pipeline_table(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       uint8_t key_mask[TABLE_RULE_MATCH_SIZE_MAX];
+       struct softnic_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 (softnic_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 (softnic_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 (softnic_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 (softnic_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 ((softnic_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 ((softnic_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 (softnic_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 (softnic_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 (softnic_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 (softnic_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 (softnic_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) {
+               p.match_type = TABLE_STUB;
+
+               t0 += 1;
+       } else {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       p.action_profile_name = NULL;
+       if (n_tokens > t0 &&
+               (strcmp(tokens[t0], "action") == 0)) {
+               if (n_tokens < t0 + 2) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, "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 = softnic_pipeline_table_create(softnic, 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(struct pmd_internals *softnic,
+       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 (softnic_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 (softnic_parser_read_uint32(&table_id, tokens[6]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
+               return;
+       }
+
+       status = softnic_pipeline_port_in_connect_to_table(softnic,
+               pipeline_name,
+               port_id,
+               table_id);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
+
+/**
+ * pipeline <pipeline_name> port in <port_id> stats read [clear]
+ */
+
+#define MSG_PIPELINE_PORT_IN_STATS                         \
+       "Pkts in: %" PRIu64 "\n"                           \
+       "Pkts dropped by AH: %" PRIu64 "\n"                \
+       "Pkts dropped by other: %" PRIu64 "\n"
+
+static void
+cmd_pipeline_port_in_stats(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct rte_pipeline_port_in_stats stats;
+       char *pipeline_name;
+       uint32_t port_id;
+       int clear, status;
+
+       if (n_tokens != 7 &&
+               n_tokens != 8) {
+               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 (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
+               return;
+       }
+
+       if (strcmp(tokens[5], "stats") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
+               return;
+       }
+
+       if (strcmp(tokens[6], "read") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
+               return;
+       }
+
+       clear = 0;
+       if (n_tokens == 8) {
+               if (strcmp(tokens[7], "clear") != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "clear");
+                       return;
+               }
+
+               clear = 1;
+       }
+
+       status = softnic_pipeline_port_in_stats_read(softnic,
+               pipeline_name,
+               port_id,
+               &stats,
+               clear);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+
+       snprintf(out, out_size, MSG_PIPELINE_PORT_IN_STATS,
+               stats.stats.n_pkts_in,
+               stats.n_pkts_dropped_by_ah,
+               stats.stats.n_pkts_drop);
+}
+
+/**
+ * pipeline <pipeline_name> port in <port_id> enable
+ */
+static void
+cmd_softnic_pipeline_port_in_enable(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       char *pipeline_name;
+       uint32_t port_id;
+       int status;
+
+       if (n_tokens != 6) {
+               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 (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
+               return;
+       }
+
+       if (strcmp(tokens[5], "enable") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
+               return;
+       }
+
+       status = softnic_pipeline_port_in_enable(softnic, pipeline_name, port_id);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
+
+/**
+ * pipeline <pipeline_name> port in <port_id> disable
+ */
+static void
+cmd_softnic_pipeline_port_in_disable(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       char *pipeline_name;
+       uint32_t port_id;
+       int status;
+
+       if (n_tokens != 6) {
+               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 (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
+               return;
+       }
+
+       if (strcmp(tokens[5], "disable") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
+               return;
+       }
+
+       status = softnic_pipeline_port_in_disable(softnic, pipeline_name, port_id);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
+
+/**
+ * pipeline <pipeline_name> port out <port_id> stats read [clear]
+ */
+#define MSG_PIPELINE_PORT_OUT_STATS                        \
+       "Pkts in: %" PRIu64 "\n"                           \
+       "Pkts dropped by AH: %" PRIu64 "\n"                \
+       "Pkts dropped by other: %" PRIu64 "\n"
+
+static void
+cmd_pipeline_port_out_stats(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct rte_pipeline_port_out_stats stats;
+       char *pipeline_name;
+       uint32_t port_id;
+       int clear, status;
+
+       if (n_tokens != 7 &&
+               n_tokens != 8) {
+               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 (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
+               return;
+       }
+
+       if (strcmp(tokens[5], "stats") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
+               return;
+       }
+
+       if (strcmp(tokens[6], "read") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
+               return;
+       }
+
+       clear = 0;
+       if (n_tokens == 8) {
+               if (strcmp(tokens[7], "clear") != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "clear");
+                       return;
+               }
+
+               clear = 1;
+       }
+
+       status = softnic_pipeline_port_out_stats_read(softnic,
+               pipeline_name,
+               port_id,
+               &stats,
+               clear);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+
+       snprintf(out, out_size, MSG_PIPELINE_PORT_OUT_STATS,
+               stats.stats.n_pkts_in,
+               stats.n_pkts_dropped_by_ah,
+               stats.stats.n_pkts_drop);
+}
+
+/**
+ * pipeline <pipeline_name> table <table_id> stats read [clear]
+ */
+#define MSG_PIPELINE_TABLE_STATS                                     \
+       "Pkts in: %" PRIu64 "\n"                                     \
+       "Pkts in with lookup miss: %" PRIu64 "\n"                    \
+       "Pkts in with lookup hit dropped by AH: %" PRIu64 "\n"       \
+       "Pkts in with lookup hit dropped by others: %" PRIu64 "\n"   \
+       "Pkts in with lookup miss dropped by AH: %" PRIu64 "\n"      \
+       "Pkts in with lookup miss dropped by others: %" PRIu64 "\n"
+
+static void
+cmd_pipeline_table_stats(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct rte_pipeline_table_stats stats;
+       char *pipeline_name;
+       uint32_t table_id;
+       int clear, status;
+
+       if (n_tokens != 6 &&
+               n_tokens != 7) {
+               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, "port");
+               return;
+       }
+
+       if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
+               return;
+       }
+
+       if (strcmp(tokens[4], "stats") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
+               return;
+       }
+
+       if (strcmp(tokens[5], "read") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
+               return;
+       }
+
+       clear = 0;
+       if (n_tokens == 7) {
+               if (strcmp(tokens[6], "clear") != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "clear");
+                       return;
+               }
+
+               clear = 1;
+       }
+
+       status = softnic_pipeline_table_stats_read(softnic,
+               pipeline_name,
+               table_id,
+               &stats,
+               clear);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+
+       snprintf(out, out_size, MSG_PIPELINE_TABLE_STATS,
+               stats.stats.n_pkts_in,
+               stats.stats.n_pkts_lookup_miss,
+               stats.n_pkts_dropped_by_lkp_hit_ah,
+               stats.n_pkts_dropped_lkp_hit,
+               stats.n_pkts_dropped_by_lkp_miss_ah,
+               stats.n_pkts_dropped_lkp_miss);
+}
 
 /**
  * <match> ::=
@@ -2557,171 +3673,519 @@ static uint32_t
 parse_table_action(char **tokens,
        uint32_t n_tokens,
        char *out,
-       size_t out_size,
-       struct softnic_table_rule_action *a)
+       size_t out_size,
+       struct softnic_table_rule_action *a)
+{
+       uint32_t n_tokens0 = n_tokens;
+
+       memset(a, 0, sizeof(*a));
+
+       if (n_tokens < 2 ||
+               strcmp(tokens[0], "action"))
+               return 0;
+
+       tokens++;
+       n_tokens--;
+
+       if (n_tokens && (strcmp(tokens[0], "fwd") == 0)) {
+               uint32_t n;
+
+               n = parse_table_action_fwd(tokens, n_tokens, a);
+               if (n == 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "action fwd");
+                       return 0;
+               }
+
+               tokens += n;
+               n_tokens -= n;
+       }
+
+       if (n_tokens && (strcmp(tokens[0], "balance") == 0)) {
+               uint32_t n;
+
+               n = parse_table_action_balance(tokens, n_tokens, a);
+               if (n == 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "action balance");
+                       return 0;
+               }
+
+               tokens += n;
+               n_tokens -= n;
+       }
+
+       if (n_tokens && (strcmp(tokens[0], "meter") == 0)) {
+               uint32_t n;
+
+               n = parse_table_action_meter(tokens, n_tokens, a);
+               if (n == 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "action meter");
+                       return 0;
+               }
+
+               tokens += n;
+               n_tokens -= n;
+       }
+
+       if (n_tokens && (strcmp(tokens[0], "tm") == 0)) {
+               uint32_t n;
+
+               n = parse_table_action_tm(tokens, n_tokens, a);
+               if (n == 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "action tm");
+                       return 0;
+               }
+
+               tokens += n;
+               n_tokens -= n;
+       }
+
+       if (n_tokens && (strcmp(tokens[0], "encap") == 0)) {
+               uint32_t n;
+
+               n = parse_table_action_encap(tokens, n_tokens, a);
+               if (n == 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "action encap");
+                       return 0;
+               }
+
+               tokens += n;
+               n_tokens -= n;
+       }
+
+       if (n_tokens && (strcmp(tokens[0], "nat") == 0)) {
+               uint32_t n;
+
+               n = parse_table_action_nat(tokens, n_tokens, a);
+               if (n == 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "action nat");
+                       return 0;
+               }
+
+               tokens += n;
+               n_tokens -= n;
+       }
+
+       if (n_tokens && (strcmp(tokens[0], "ttl") == 0)) {
+               uint32_t n;
+
+               n = parse_table_action_ttl(tokens, n_tokens, a);
+               if (n == 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "action ttl");
+                       return 0;
+               }
+
+               tokens += n;
+               n_tokens -= n;
+       }
+
+       if (n_tokens && (strcmp(tokens[0], "stats") == 0)) {
+               uint32_t n;
+
+               n = parse_table_action_stats(tokens, n_tokens, a);
+               if (n == 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "action stats");
+                       return 0;
+               }
+
+               tokens += n;
+               n_tokens -= n;
+       }
+
+       if (n_tokens && (strcmp(tokens[0], "time") == 0)) {
+               uint32_t n;
+
+               n = parse_table_action_time(tokens, n_tokens, a);
+               if (n == 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID,
+                               "action time");
+                       return 0;
+               }
+
+               tokens += n;
+               n_tokens -= n;
+       }
+
+       if (n_tokens0 - n_tokens == 1) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "action");
+               return 0;
+       }
+
+       return n_tokens0 - n_tokens;
+}
+
+/**
+ * pipeline <pipeline_name> table <table_id> rule add
+ *    match <match>
+ *    action <table_action>
+ */
+static void
+cmd_softnic_pipeline_table_rule_add(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct softnic_table_rule_match m;
+       struct softnic_table_rule_action a;
+       char *pipeline_name;
+       void *data;
+       uint32_t table_id, t0, n_tokens_parsed;
+       int status;
+
+       if (n_tokens < 8) {
+               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 (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
+               return;
+       }
+
+       if (strcmp(tokens[4], "rule") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
+               return;
+       }
+
+       if (strcmp(tokens[5], "add") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+               return;
+       }
+
+       t0 = 6;
+
+       /* match */
+       n_tokens_parsed = parse_match(tokens + t0,
+               n_tokens - t0,
+               out,
+               out_size,
+               &m);
+       if (n_tokens_parsed == 0)
+               return;
+       t0 += n_tokens_parsed;
+
+       /* action */
+       n_tokens_parsed = parse_table_action(tokens + t0,
+               n_tokens - t0,
+               out,
+               out_size,
+               &a);
+       if (n_tokens_parsed == 0)
+               return;
+       t0 += n_tokens_parsed;
+
+       if (t0 != n_tokens) {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       status = softnic_pipeline_table_rule_add(softnic,
+               pipeline_name,
+               table_id,
+               &m,
+               &a,
+               &data);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
+
+/**
+ * pipeline <pipeline_name> table <table_id> rule add
+ *    match
+ *       default
+ *    action
+ *       fwd
+ *          drop
+ *          | port <port_id>
+ *          | meta
+ *          | table <table_id>
+ */
+static void
+cmd_softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
 {
-       uint32_t n_tokens0 = n_tokens;
+       struct softnic_table_rule_action action;
+       void *data;
+       char *pipeline_name;
+       uint32_t table_id;
+       int status;
 
-       memset(a, 0, sizeof(*a));
+       if (n_tokens != 11 &&
+               n_tokens != 12) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
 
-       if (n_tokens < 2 ||
-               strcmp(tokens[0], "action"))
-               return 0;
+       pipeline_name = tokens[1];
 
-       tokens++;
-       n_tokens--;
+       if (strcmp(tokens[2], "table") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
+               return;
+       }
 
-       if (n_tokens && (strcmp(tokens[0], "fwd") == 0)) {
-               uint32_t n;
+       if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
+               return;
+       }
 
-               n = parse_table_action_fwd(tokens, n_tokens, a);
-               if (n == 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "action fwd");
-                       return 0;
-               }
+       if (strcmp(tokens[4], "rule") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
+               return;
+       }
 
-               tokens += n;
-               n_tokens -= n;
+       if (strcmp(tokens[5], "add") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+               return;
        }
 
-       if (n_tokens && (strcmp(tokens[0], "balance") == 0)) {
-               uint32_t n;
+       if (strcmp(tokens[6], "match") != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "match");
+               return;
+       }
 
-               n = parse_table_action_balance(tokens, n_tokens, a);
-               if (n == 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "action balance");
-                       return 0;
-               }
+       if (strcmp(tokens[7], "default") != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "default");
+               return;
+       }
 
-               tokens += n;
-               n_tokens -= n;
+       if (strcmp(tokens[8], "action") != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "action");
+               return;
        }
 
-       if (n_tokens && (strcmp(tokens[0], "meter") == 0)) {
-               uint32_t n;
+       if (strcmp(tokens[9], "fwd") != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "fwd");
+               return;
+       }
 
-               n = parse_table_action_meter(tokens, n_tokens, a);
-               if (n == 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "action meter");
-                       return 0;
+       action.action_mask = 1 << RTE_TABLE_ACTION_FWD;
+
+       if (strcmp(tokens[10], "drop") == 0) {
+               if (n_tokens != 11) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+                       return;
                }
 
-               tokens += n;
-               n_tokens -= n;
-       }
+               action.fwd.action = RTE_PIPELINE_ACTION_DROP;
+       } else if (strcmp(tokens[10], "port") == 0) {
+               uint32_t id;
 
-       if (n_tokens && (strcmp(tokens[0], "tm") == 0)) {
-               uint32_t n;
+               if (n_tokens != 12) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+                       return;
+               }
 
-               n = parse_table_action_tm(tokens, n_tokens, a);
-               if (n == 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "action tm");
-                       return 0;
+               if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
+                       return;
                }
 
-               tokens += n;
-               n_tokens -= n;
-       }
+               action.fwd.action = RTE_PIPELINE_ACTION_PORT;
+               action.fwd.id = id;
+       } else if (strcmp(tokens[10], "meta") == 0) {
+               if (n_tokens != 11) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+                       return;
+               }
 
-       if (n_tokens && (strcmp(tokens[0], "encap") == 0)) {
-               uint32_t n;
+               action.fwd.action = RTE_PIPELINE_ACTION_PORT_META;
+       } else if (strcmp(tokens[10], "table") == 0) {
+               uint32_t id;
 
-               n = parse_table_action_encap(tokens, n_tokens, a);
-               if (n == 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "action encap");
-                       return 0;
+               if (n_tokens != 12) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+                       return;
                }
 
-               tokens += n;
-               n_tokens -= n;
+               if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
+                       return;
+               }
+
+               action.fwd.action = RTE_PIPELINE_ACTION_TABLE;
+               action.fwd.id = id;
+       } else {
+               snprintf(out, out_size, MSG_ARG_INVALID,
+                       "drop or port or meta or table");
+               return;
        }
 
-       if (n_tokens && (strcmp(tokens[0], "nat") == 0)) {
-               uint32_t n;
+       status = softnic_pipeline_table_rule_add_default(softnic,
+               pipeline_name,
+               table_id,
+               &action,
+               &data);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
 
-               n = parse_table_action_nat(tokens, n_tokens, a);
-               if (n == 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "action nat");
-                       return 0;
-               }
+/**
+ * pipeline <pipeline_name> table <table_id> rule add bulk <file_name> <n_rules>
+ *
+ * File <file_name>:
+ * - line format: match <match> action <action>
+ */
+static int
+cli_rule_file_process(const char *file_name,
+       size_t line_len_max,
+       struct softnic_table_rule_match *m,
+       struct softnic_table_rule_action *a,
+       uint32_t *n_rules,
+       uint32_t *line_number,
+       char *out,
+       size_t out_size);
 
-               tokens += n;
-               n_tokens -= n;
+static void
+cmd_softnic_pipeline_table_rule_add_bulk(struct pmd_internals *softnic,
+       char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct softnic_table_rule_match *match;
+       struct softnic_table_rule_action *action;
+       void **data;
+       char *pipeline_name, *file_name;
+       uint32_t table_id, n_rules, n_rules_parsed, line_number;
+       int status;
+
+       if (n_tokens != 9) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
        }
 
-       if (n_tokens && (strcmp(tokens[0], "ttl") == 0)) {
-               uint32_t n;
+       pipeline_name = tokens[1];
 
-               n = parse_table_action_ttl(tokens, n_tokens, a);
-               if (n == 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "action ttl");
-                       return 0;
-               }
+       if (strcmp(tokens[2], "table") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
+               return;
+       }
 
-               tokens += n;
-               n_tokens -= n;
+       if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
+               return;
        }
 
-       if (n_tokens && (strcmp(tokens[0], "stats") == 0)) {
-               uint32_t n;
+       if (strcmp(tokens[4], "rule") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
+               return;
+       }
 
-               n = parse_table_action_stats(tokens, n_tokens, a);
-               if (n == 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "action stats");
-                       return 0;
-               }
+       if (strcmp(tokens[5], "add") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+               return;
+       }
 
-               tokens += n;
-               n_tokens -= n;
+       if (strcmp(tokens[6], "bulk") != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "bulk");
+               return;
        }
 
-       if (n_tokens && (strcmp(tokens[0], "time") == 0)) {
-               uint32_t n;
+       file_name = tokens[7];
 
-               n = parse_table_action_time(tokens, n_tokens, a);
-               if (n == 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "action time");
-                       return 0;
-               }
+       if ((softnic_parser_read_uint32(&n_rules, tokens[8]) != 0) ||
+               n_rules == 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
+               return;
+       }
+
+       /* Memory allocation. */
+       match = calloc(n_rules, sizeof(struct softnic_table_rule_match));
+       action = calloc(n_rules, sizeof(struct softnic_table_rule_action));
+       data = calloc(n_rules, sizeof(void *));
+       if (match == NULL ||
+               action == NULL ||
+               data == NULL) {
+               snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+               free(data);
+               free(action);
+               free(match);
+               return;
+       }
 
-               tokens += n;
-               n_tokens -= n;
+       /* Load rule file */
+       n_rules_parsed = n_rules;
+       status = cli_rule_file_process(file_name,
+               1024,
+               match,
+               action,
+               &n_rules_parsed,
+               &line_number,
+               out,
+               out_size);
+       if (status) {
+               snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
+               free(data);
+               free(action);
+               free(match);
+               return;
+       }
+       if (n_rules_parsed != n_rules) {
+               snprintf(out, out_size, MSG_FILE_NOT_ENOUGH, file_name);
+               free(data);
+               free(action);
+               free(match);
+               return;
        }
 
-       if (n_tokens0 - n_tokens == 1) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "action");
-               return 0;
+       /* Rule bulk add */
+       status = softnic_pipeline_table_rule_add_bulk(softnic,
+               pipeline_name,
+               table_id,
+               match,
+               action,
+               data,
+               &n_rules);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               free(data);
+               free(action);
+               free(match);
+               return;
        }
 
-       return n_tokens0 - n_tokens;
+       /* Memory free */
+       free(data);
+       free(action);
+       free(match);
 }
 
 /**
- * pipeline <pipeline_name> table <table_id> rule add
+ * pipeline <pipeline_name> table <table_id> rule delete
  *    match <match>
- *    action <table_action>
  */
 static void
-cmd_softnic_pipeline_table_rule_add(struct pmd_internals *softnic,
+cmd_softnic_pipeline_table_rule_delete(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
        struct softnic_table_rule_match m;
-       struct softnic_table_rule_action a;
        char *pipeline_name;
-       void *data;
-       uint32_t table_id, t0, n_tokens_parsed;
+       uint32_t table_id, n_tokens_parsed, t0;
        int status;
 
        if (n_tokens < 8) {
@@ -2746,8 +4210,8 @@ cmd_softnic_pipeline_table_rule_add(struct pmd_internals *softnic,
                return;
        }
 
-       if (strcmp(tokens[5], "add") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+       if (strcmp(tokens[5], "delete") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
                return;
        }
 
@@ -2763,27 +4227,15 @@ cmd_softnic_pipeline_table_rule_add(struct pmd_internals *softnic,
                return;
        t0 += n_tokens_parsed;
 
-       /* action */
-       n_tokens_parsed = parse_table_action(tokens + t0,
-               n_tokens - t0,
-               out,
-               out_size,
-               &a);
-       if (n_tokens_parsed == 0)
-               return;
-       t0 += n_tokens_parsed;
-
-       if (t0 != n_tokens) {
-               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+       if (n_tokens != t0) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       status = softnic_pipeline_table_rule_add(softnic,
+       status = softnic_pipeline_table_rule_delete(softnic,
                pipeline_name,
                table_id,
-               &m,
-               &a,
-               &data);
+               &m);
        if (status) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
@@ -2791,31 +4243,22 @@ cmd_softnic_pipeline_table_rule_add(struct pmd_internals *softnic,
 }
 
 /**
- * pipeline <pipeline_name> table <table_id> rule add
+ * pipeline <pipeline_name> table <table_id> rule delete
  *    match
  *       default
- *    action
- *       fwd
- *          drop
- *          | port <port_id>
- *          | meta
- *          | table <table_id>
  */
 static void
-cmd_softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic,
+cmd_softnic_pipeline_table_rule_delete_default(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       struct softnic_table_rule_action action;
-       void *data;
        char *pipeline_name;
        uint32_t table_id;
        int status;
 
-       if (n_tokens != 11 &&
-               n_tokens != 12) {
+       if (n_tokens != 8) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
@@ -2837,8 +4280,8 @@ cmd_softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic,
                return;
        }
 
-       if (strcmp(tokens[5], "add") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+       if (strcmp(tokens[5], "delete") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
                return;
        }
 
@@ -2852,73 +4295,9 @@ cmd_softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic,
                return;
        }
 
-       if (strcmp(tokens[8], "action") != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "action");
-               return;
-       }
-
-       if (strcmp(tokens[9], "fwd") != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "fwd");
-               return;
-       }
-
-       action.action_mask = 1 << RTE_TABLE_ACTION_FWD;
-
-       if (strcmp(tokens[10], "drop") == 0) {
-               if (n_tokens != 11) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-                       return;
-               }
-
-               action.fwd.action = RTE_PIPELINE_ACTION_DROP;
-       } else if (strcmp(tokens[10], "port") == 0) {
-               uint32_t id;
-
-               if (n_tokens != 12) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-                       return;
-               }
-
-               if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
-                       return;
-               }
-
-               action.fwd.action = RTE_PIPELINE_ACTION_PORT;
-               action.fwd.id = id;
-       } else if (strcmp(tokens[10], "meta") == 0) {
-               if (n_tokens != 11) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-                       return;
-               }
-
-               action.fwd.action = RTE_PIPELINE_ACTION_PORT_META;
-       } else if (strcmp(tokens[10], "table") == 0) {
-               uint32_t id;
-
-               if (n_tokens != 12) {
-                       snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
-                       return;
-               }
-
-               if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
-                       return;
-               }
-
-               action.fwd.action = RTE_PIPELINE_ACTION_TABLE;
-               action.fwd.id = id;
-       } else {
-               snprintf(out, out_size, MSG_ARG_INVALID,
-                       "drop or port or meta or table");
-               return;
-       }
-
-       status = softnic_pipeline_table_rule_add_default(softnic,
+       status = softnic_pipeline_table_rule_delete_default(softnic,
                pipeline_name,
-               table_id,
-               &action,
-               &data);
+               table_id);
        if (status) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
@@ -2926,36 +4305,36 @@ cmd_softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic,
 }
 
 /**
- * pipeline <pipeline_name> table <table_id> rule add bulk <file_name> <n_rules>
- *
- * File <file_name>:
- * - line format: match <match> action <action>
+ * pipeline <pipeline_name> table <table_id> rule read stats [clear]
  */
-static int
-cli_rule_file_process(const char *file_name,
-       size_t line_len_max,
-       struct softnic_table_rule_match *m,
-       struct softnic_table_rule_action *a,
-       uint32_t *n_rules,
-       uint32_t *line_number,
+static void
+cmd_softnic_pipeline_table_rule_stats_read(struct pmd_internals *softnic __rte_unused,
+       char **tokens,
+       uint32_t n_tokens __rte_unused,
        char *out,
-       size_t out_size);
+       size_t out_size)
+{
+       snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
+}
 
+/**
+ * pipeline <pipeline_name> table <table_id> meter profile <meter_profile_id>
+ *  add srtcm cir <cir> cbs <cbs> ebs <ebs>
+ *  | trtcm cir <cir> pir <pir> cbs <cbs> pbs <pbs>
+ */
 static void
-cmd_softnic_pipeline_table_rule_add_bulk(struct pmd_internals *softnic,
+cmd_pipeline_table_meter_profile_add(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       struct softnic_table_rule_match *match;
-       struct softnic_table_rule_action *action;
-       void **data;
-       char *pipeline_name, *file_name;
-       uint32_t table_id, n_rules, n_rules_parsed, line_number;
+       struct rte_table_action_meter_profile p;
+       char *pipeline_name;
+       uint32_t table_id, meter_profile_id;
        int status;
 
-       if (n_tokens != 9) {
+       if (n_tokens < 9) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
@@ -2963,7 +4342,7 @@ cmd_softnic_pipeline_table_rule_add_bulk(struct pmd_internals *softnic,
        pipeline_name = tokens[1];
 
        if (strcmp(tokens[2], "table") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
                return;
        }
 
@@ -2972,107 +4351,142 @@ cmd_softnic_pipeline_table_rule_add_bulk(struct pmd_internals *softnic,
                return;
        }
 
-       if (strcmp(tokens[4], "rule") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
+       if (strcmp(tokens[4], "meter") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
                return;
        }
 
-       if (strcmp(tokens[5], "add") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+       if (strcmp(tokens[5], "profile") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
                return;
        }
 
-       if (strcmp(tokens[6], "bulk") != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "bulk");
+       if (softnic_parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
                return;
        }
 
-       file_name = tokens[7];
-
-       if ((softnic_parser_read_uint32(&n_rules, tokens[8]) != 0) ||
-               n_rules == 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
+       if (strcmp(tokens[7], "add") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
                return;
        }
 
-       /* Memory allocation. */
-       match = calloc(n_rules, sizeof(struct softnic_table_rule_match));
-       action = calloc(n_rules, sizeof(struct softnic_table_rule_action));
-       data = calloc(n_rules, sizeof(void *));
-       if (match == NULL ||
-               action == NULL ||
-               data == NULL) {
-               snprintf(out, out_size, MSG_OUT_OF_MEMORY);
-               free(data);
-               free(action);
-               free(match);
-               return;
-       }
+       if (strcmp(tokens[8], "srtcm") == 0) {
+               if (n_tokens != 15) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH,
+                               tokens[0]);
+                       return;
+               }
+
+               p.alg = RTE_TABLE_ACTION_METER_SRTCM;
+
+               if (strcmp(tokens[9], "cir") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
+                       return;
+               }
+
+               if (softnic_parser_read_uint64(&p.srtcm.cir, tokens[10]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "cir");
+                       return;
+               }
+
+               if (strcmp(tokens[11], "cbs") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
+                       return;
+               }
+
+               if (softnic_parser_read_uint64(&p.srtcm.cbs, tokens[12]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
+                       return;
+               }
+
+               if (strcmp(tokens[13], "ebs") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ebs");
+                       return;
+               }
+
+               if (softnic_parser_read_uint64(&p.srtcm.ebs, tokens[14]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "ebs");
+                       return;
+               }
+       } else if (strcmp(tokens[8], "trtcm") == 0) {
+               if (n_tokens != 17) {
+                       snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+                       return;
+               }
+
+               p.alg = RTE_TABLE_ACTION_METER_TRTCM;
+
+               if (strcmp(tokens[9], "cir") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
+                       return;
+               }
+
+               if (softnic_parser_read_uint64(&p.trtcm.cir, tokens[10]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "cir");
+                       return;
+               }
+
+               if (strcmp(tokens[11], "pir") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
+                       return;
+               }
+
+               if (softnic_parser_read_uint64(&p.trtcm.pir, tokens[12]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "pir");
+                       return;
+               }
+               if (strcmp(tokens[13], "cbs") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
+                       return;
+               }
+
+               if (softnic_parser_read_uint64(&p.trtcm.cbs, tokens[14]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
+                       return;
+               }
+
+               if (strcmp(tokens[15], "pbs") != 0) {
+                       snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
+                       return;
+               }
 
-       /* Load rule file */
-       n_rules_parsed = n_rules;
-       status = cli_rule_file_process(file_name,
-               1024,
-               match,
-               action,
-               &n_rules_parsed,
-               &line_number,
-               out,
-               out_size);
-       if (status) {
-               snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
-               free(data);
-               free(action);
-               free(match);
-               return;
-       }
-       if (n_rules_parsed != n_rules) {
-               snprintf(out, out_size, MSG_FILE_NOT_ENOUGH, file_name);
-               free(data);
-               free(action);
-               free(match);
+               if (softnic_parser_read_uint64(&p.trtcm.pbs, tokens[16]) != 0) {
+                       snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
+                       return;
+               }
+       } else {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       /* Rule bulk add */
-       status = softnic_pipeline_table_rule_add_bulk(softnic,
+       status = softnic_pipeline_table_mtr_profile_add(softnic,
                pipeline_name,
                table_id,
-               match,
-               action,
-               data,
-               &n_rules);
+               meter_profile_id,
+               &p);
        if (status) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
-               free(data);
-               free(action);
-               free(match);
                return;
        }
-
-       /* Memory free */
-       free(data);
-       free(action);
-       free(match);
 }
 
 /**
- * pipeline <pipeline_name> table <table_id> rule delete
- *    match <match>
+ * pipeline <pipeline_name> table <table_id>
+ *  meter profile <meter_profile_id> delete
  */
 static void
-cmd_softnic_pipeline_table_rule_delete(struct pmd_internals *softnic,
+cmd_pipeline_table_meter_profile_delete(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       struct softnic_table_rule_match m;
        char *pipeline_name;
-       uint32_t table_id, n_tokens_parsed, t0;
+       uint32_t table_id, meter_profile_id;
        int status;
 
-       if (n_tokens < 8) {
+       if (n_tokens != 8) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
@@ -3080,7 +4494,7 @@ cmd_softnic_pipeline_table_rule_delete(struct pmd_internals *softnic,
        pipeline_name = tokens[1];
 
        if (strcmp(tokens[2], "table") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
                return;
        }
 
@@ -3089,37 +4503,30 @@ cmd_softnic_pipeline_table_rule_delete(struct pmd_internals *softnic,
                return;
        }
 
-       if (strcmp(tokens[4], "rule") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
+       if (strcmp(tokens[4], "meter") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
                return;
        }
 
-       if (strcmp(tokens[5], "delete") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
+       if (strcmp(tokens[5], "profile") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
                return;
        }
 
-       t0 = 6;
-
-       /* match */
-       n_tokens_parsed = parse_match(tokens + t0,
-               n_tokens - t0,
-               out,
-               out_size,
-               &m);
-       if (n_tokens_parsed == 0)
+       if (softnic_parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
                return;
-       t0 += n_tokens_parsed;
+       }
 
-       if (n_tokens != t0) {
-               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+       if (strcmp(tokens[7], "delete") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
                return;
        }
 
-       status = softnic_pipeline_table_rule_delete(softnic,
+       status = softnic_pipeline_table_mtr_profile_delete(softnic,
                pipeline_name,
                table_id,
-               &m);
+               meter_profile_id);
        if (status) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
@@ -3127,22 +4534,129 @@ cmd_softnic_pipeline_table_rule_delete(struct pmd_internals *softnic,
 }
 
 /**
- * pipeline <pipeline_name> table <table_id> rule delete
- *    match
- *       default
+ * pipeline <pipeline_name> table <table_id> rule read meter [clear]
  */
 static void
-cmd_softnic_pipeline_table_rule_delete_default(struct pmd_internals *softnic,
+cmd_pipeline_table_rule_meter_read(struct pmd_internals *softnic __rte_unused,
+       char **tokens,
+       uint32_t n_tokens __rte_unused,
+       char *out,
+       size_t out_size)
+{
+       snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
+}
+
+/**
+ * pipeline <pipeline_name> table <table_id> dscp <file_name>
+ *
+ * File <file_name>:
+ *  - exactly 64 lines
+ *  - line format: <tc_id> <tc_queue_id> <color>, with <color> as: g | y | r
+ */
+static int
+load_dscp_table(struct rte_table_action_dscp_table *dscp_table,
+       const char *file_name,
+       uint32_t *line_number)
+{
+       FILE *f = NULL;
+       uint32_t dscp, l;
+
+       /* Check input arguments */
+       if (dscp_table == NULL ||
+               file_name == NULL ||
+               line_number == NULL) {
+               if (line_number)
+                       *line_number = 0;
+               return -EINVAL;
+       }
+
+       /* Open input file */
+       f = fopen(file_name, "r");
+       if (f == NULL) {
+               *line_number = 0;
+               return -EINVAL;
+       }
+
+       /* Read file */
+       for (dscp = 0, l = 1; ; l++) {
+               char line[64];
+               char *tokens[3];
+               enum rte_meter_color color;
+               uint32_t tc_id, tc_queue_id, n_tokens = RTE_DIM(tokens);
+
+               if (fgets(line, sizeof(line), f) == NULL)
+                       break;
+
+               if (is_comment(line))
+                       continue;
+
+               if (softnic_parse_tokenize_string(line, tokens, &n_tokens)) {
+                       *line_number = l;
+                       fclose(f);
+                       return -EINVAL;
+               }
+
+               if (n_tokens == 0)
+                       continue;
+
+               if (dscp >= RTE_DIM(dscp_table->entry) ||
+                       n_tokens != RTE_DIM(tokens) ||
+                       softnic_parser_read_uint32(&tc_id, tokens[0]) ||
+                       tc_id >= RTE_TABLE_ACTION_TC_MAX ||
+                       softnic_parser_read_uint32(&tc_queue_id, tokens[1]) ||
+                       tc_queue_id >= RTE_TABLE_ACTION_TC_QUEUE_MAX ||
+                       (strlen(tokens[2]) != 1)) {
+                       *line_number = l;
+                       fclose(f);
+                       return -EINVAL;
+               }
+
+               switch (tokens[2][0]) {
+               case 'g':
+               case 'G':
+                       color = e_RTE_METER_GREEN;
+                       break;
+
+               case 'y':
+               case 'Y':
+                       color = e_RTE_METER_YELLOW;
+                       break;
+
+               case 'r':
+               case 'R':
+                       color = e_RTE_METER_RED;
+                       break;
+
+               default:
+                       *line_number = l;
+                       fclose(f);
+                       return -EINVAL;
+               }
+
+               dscp_table->entry[dscp].tc_id = tc_id;
+               dscp_table->entry[dscp].tc_queue_id = tc_queue_id;
+               dscp_table->entry[dscp].color = color;
+               dscp++;
+       }
+
+       /* Close file */
+       fclose(f);
+       return 0;
+}
+
+static void
+cmd_pipeline_table_dscp(struct pmd_internals *softnic,
        char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       char *pipeline_name;
-       uint32_t table_id;
+       struct rte_table_action_dscp_table dscp_table;
+       char *pipeline_name, *file_name;
+       uint32_t table_id, line_number;
        int status;
 
-       if (n_tokens != 8) {
+       if (n_tokens != 6) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
@@ -3150,7 +4664,7 @@ cmd_softnic_pipeline_table_rule_delete_default(struct pmd_internals *softnic,
        pipeline_name = tokens[1];
 
        if (strcmp(tokens[2], "table") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
                return;
        }
 
@@ -3159,35 +4673,43 @@ cmd_softnic_pipeline_table_rule_delete_default(struct pmd_internals *softnic,
                return;
        }
 
-       if (strcmp(tokens[4], "rule") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
-               return;
-       }
-
-       if (strcmp(tokens[5], "delete") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
+       if (strcmp(tokens[4], "dscp") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dscp");
                return;
        }
 
-       if (strcmp(tokens[6], "match") != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "match");
-               return;
-       }
+       file_name = tokens[5];
 
-       if (strcmp(tokens[7], "default") != 0) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "default");
+       status = load_dscp_table(&dscp_table, file_name, &line_number);
+       if (status) {
+               snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
                return;
        }
 
-       status = softnic_pipeline_table_rule_delete_default(softnic,
+       status = softnic_pipeline_table_dscp_table_update(softnic,
                pipeline_name,
-               table_id);
+               table_id,
+               UINT64_MAX,
+               &dscp_table);
        if (status) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
        }
 }
 
+/**
+ * pipeline <pipeline_name> table <table_id> rule read ttl [clear]
+ */
+static void
+cmd_softnic_pipeline_table_rule_ttl_read(struct pmd_internals *softnic __rte_unused,
+       char **tokens,
+       uint32_t n_tokens __rte_unused,
+       char *out,
+       size_t out_size)
+{
+       snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
+}
+
 /**
  * thread <thread_id> pipeline <pipeline_name> enable
  */
@@ -3310,6 +4832,46 @@ softnic_cli_process(char *in, char *out, size_t out_size, void *arg)
                return;
        }
 
+       if (strcmp(tokens[0], "tmgr") == 0) {
+               if (n_tokens == 2) {
+                       cmd_tmgr(softnic, tokens, n_tokens, out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 3 &&
+                       (strcmp(tokens[1], "shaper") == 0) &&
+                       (strcmp(tokens[2], "profile") == 0)) {
+                       cmd_tmgr_shaper_profile(softnic, tokens, n_tokens, out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 3 &&
+                       (strcmp(tokens[1], "shared") == 0) &&
+                       (strcmp(tokens[2], "shaper") == 0)) {
+                       cmd_tmgr_shared_shaper(softnic, tokens, n_tokens, out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 2 &&
+                       (strcmp(tokens[1], "node") == 0)) {
+                       cmd_tmgr_node(softnic, tokens, n_tokens, out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 2 &&
+                       (strcmp(tokens[1], "hierarchy-default") == 0)) {
+                       cmd_tmgr_hierarchy_default(softnic, tokens, n_tokens, out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 3 &&
+                       (strcmp(tokens[1], "hierarchy") == 0) &&
+                       (strcmp(tokens[2], "commit") == 0)) {
+                       cmd_tmgr_hierarchy_commit(softnic, tokens, n_tokens, out, out_size);
+                       return;
+               }
+       }
+
        if (strcmp(tokens[0], "tap") == 0) {
                cmd_tap(softnic, tokens, n_tokens, out, out_size);
                return;
@@ -3364,6 +4926,15 @@ softnic_cli_process(char *in, char *out, size_t out_size, void *arg)
                        return;
                }
 
+               if (n_tokens >= 6 &&
+                       (strcmp(tokens[2], "port") == 0) &&
+                       (strcmp(tokens[3], "in") == 0) &&
+                       (strcmp(tokens[5], "stats") == 0)) {
+                       cmd_pipeline_port_in_stats(softnic, tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
+
                if (n_tokens >= 6 &&
                        (strcmp(tokens[2], "port") == 0) &&
                        (strcmp(tokens[3], "in") == 0) &&
@@ -3382,6 +4953,23 @@ softnic_cli_process(char *in, char *out, size_t out_size, void *arg)
                        return;
                }
 
+               if (n_tokens >= 6 &&
+                       (strcmp(tokens[2], "port") == 0) &&
+                       (strcmp(tokens[3], "out") == 0) &&
+                       (strcmp(tokens[5], "stats") == 0)) {
+                       cmd_pipeline_port_out_stats(softnic, tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 5 &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "stats") == 0)) {
+                       cmd_pipeline_table_stats(softnic, tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
+
                if (n_tokens >= 7 &&
                        (strcmp(tokens[2], "table") == 0) &&
                        (strcmp(tokens[4], "rule") == 0) &&
@@ -3425,6 +5013,64 @@ softnic_cli_process(char *in, char *out, size_t out_size, void *arg)
                                out, out_size);
                        return;
                }
+
+               if (n_tokens >= 7 &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "rule") == 0) &&
+                       (strcmp(tokens[5], "read") == 0) &&
+                       (strcmp(tokens[6], "stats") == 0)) {
+                       cmd_softnic_pipeline_table_rule_stats_read(softnic, tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 8 &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "meter") == 0) &&
+                       (strcmp(tokens[5], "profile") == 0) &&
+                       (strcmp(tokens[7], "add") == 0)) {
+                       cmd_pipeline_table_meter_profile_add(softnic, tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 8 &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "meter") == 0) &&
+                       (strcmp(tokens[5], "profile") == 0) &&
+                       (strcmp(tokens[7], "delete") == 0)) {
+                       cmd_pipeline_table_meter_profile_delete(softnic, tokens,
+                               n_tokens, out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 7 &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "rule") == 0) &&
+                       (strcmp(tokens[5], "read") == 0) &&
+                       (strcmp(tokens[6], "meter") == 0)) {
+                       cmd_pipeline_table_rule_meter_read(softnic, tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 5 &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "dscp") == 0)) {
+                       cmd_pipeline_table_dscp(softnic, tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
+
+               if (n_tokens >= 7 &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "rule") == 0) &&
+                       (strcmp(tokens[5], "read") == 0) &&
+                       (strcmp(tokens[6], "ttl") == 0)) {
+                       cmd_softnic_pipeline_table_rule_ttl_read(softnic, tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
        }
 
        if (strcmp(tokens[0], "thread") == 0) {