net: add macro to extract MAC address bytes
[dpdk.git] / examples / pipeline / cli.c
index a8a2517..1ba9b8c 100644 (file)
@@ -376,7 +376,7 @@ print_link_info(struct link *link, char *out, size_t out_size)
        snprintf(out, out_size,
                "\n"
                "%s: flags=<%s> mtu %u\n"
-               "\tether %02X:%02X:%02X:%02X:%02X:%02X rxqueues %u txqueues %u\n"
+               "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
                "\tport# %u  speed %s\n"
                "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
                "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
@@ -385,9 +385,7 @@ print_link_info(struct link *link, char *out, size_t out_size)
                link->name,
                eth_link.link_status == 0 ? "DOWN" : "UP",
                mtu,
-               mac_addr.addr_bytes[0], mac_addr.addr_bytes[1],
-               mac_addr.addr_bytes[2], mac_addr.addr_bytes[3],
-               mac_addr.addr_bytes[4], mac_addr.addr_bytes[5],
+               RTE_ETHER_ADDR_BYTES(&mac_addr),
                link->n_rxq,
                link->n_txq,
                link->port_id,
@@ -1038,25 +1036,319 @@ table_entry_free(struct rte_swx_table_entry *entry)
        free(entry);
 }
 
-static const char cmd_pipeline_table_update_help[] =
-"pipeline <pipeline_name> table <table_name> update <file_name_add> "
-"<file_name_delete> <file_name_default>";
+#ifndef MAX_LINE_SIZE
+#define MAX_LINE_SIZE 2048
+#endif
+
+static int
+pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
+                          const char *table_name,
+                          FILE *file,
+                          uint32_t *file_line_number)
+{
+       char *line = NULL;
+       uint32_t line_id = 0;
+       int status = 0;
+
+       /* Buffer allocation. */
+       line = malloc(MAX_LINE_SIZE);
+       if (!line)
+               return -ENOMEM;
+
+       /* File read. */
+       for (line_id = 1; ; line_id++) {
+               struct rte_swx_table_entry *entry;
+               int is_blank_or_comment;
+
+               if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+                       break;
+
+               entry = rte_swx_ctl_pipeline_table_entry_read(p,
+                                                             table_name,
+                                                             line,
+                                                             &is_blank_or_comment);
+               if (!entry) {
+                       if (is_blank_or_comment)
+                               continue;
+
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               status = rte_swx_ctl_pipeline_table_entry_add(p,
+                                                             table_name,
+                                                             entry);
+               table_entry_free(entry);
+               if (status)
+                       goto error;
+       }
+
+error:
+       free(line);
+       *file_line_number = line_id;
+       return status;
+}
+
+static const char cmd_pipeline_table_add_help[] =
+"pipeline <pipeline_name> table <table_name> add <file_name>\n";
+
+static void
+cmd_pipeline_table_add(char **tokens,
+                      uint32_t n_tokens,
+                      char *out,
+                      size_t out_size,
+                      void *obj)
+{
+       struct pipeline *p;
+       char *pipeline_name, *table_name, *file_name;
+       FILE *file = NULL;
+       uint32_t file_line_number = 0;
+       int status;
+
+       if (n_tokens != 6) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+       p = pipeline_find(obj, pipeline_name);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       table_name = tokens[3];
+
+       file_name = tokens[5];
+       file = fopen(file_name, "r");
+       if (!file) {
+               snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+               return;
+       }
+
+       status = pipeline_table_entries_add(p->ctl,
+                                           table_name,
+                                           file,
+                                           &file_line_number);
+       if (status)
+               snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
+                        file_name,
+                        file_line_number);
+
+       fclose(file);
+}
+
+static int
+pipeline_table_entries_delete(struct rte_swx_ctl_pipeline *p,
+                             const char *table_name,
+                             FILE *file,
+                             uint32_t *file_line_number)
+{
+       char *line = NULL;
+       uint32_t line_id = 0;
+       int status = 0;
+
+       /* Buffer allocation. */
+       line = malloc(MAX_LINE_SIZE);
+       if (!line)
+               return -ENOMEM;
+
+       /* File read. */
+       for (line_id = 1; ; line_id++) {
+               struct rte_swx_table_entry *entry;
+               int is_blank_or_comment;
+
+               if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+                       break;
+
+               entry = rte_swx_ctl_pipeline_table_entry_read(p,
+                                                             table_name,
+                                                             line,
+                                                             &is_blank_or_comment);
+               if (!entry) {
+                       if (is_blank_or_comment)
+                               continue;
+
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               status = rte_swx_ctl_pipeline_table_entry_delete(p,
+                                                                table_name,
+                                                                entry);
+               table_entry_free(entry);
+               if (status)
+                       goto error;
+       }
+
+error:
+       *file_line_number = line_id;
+       free(line);
+       return status;
+}
+
+static const char cmd_pipeline_table_delete_help[] =
+"pipeline <pipeline_name> table <table_name> delete <file_name>\n";
+
+static void
+cmd_pipeline_table_delete(char **tokens,
+                         uint32_t n_tokens,
+                         char *out,
+                         size_t out_size,
+                         void *obj)
+{
+       struct pipeline *p;
+       char *pipeline_name, *table_name, *file_name;
+       FILE *file = NULL;
+       uint32_t file_line_number = 0;
+       int status;
+
+       if (n_tokens != 6) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+       p = pipeline_find(obj, pipeline_name);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       table_name = tokens[3];
+
+       file_name = tokens[5];
+       file = fopen(file_name, "r");
+       if (!file) {
+               snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+               return;
+       }
+
+       status = pipeline_table_entries_delete(p->ctl,
+                                              table_name,
+                                              file,
+                                              &file_line_number);
+       if (status)
+               snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
+                        file_name,
+                        file_line_number);
+
+       fclose(file);
+}
+
+static int
+pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *p,
+                                const char *table_name,
+                                FILE *file,
+                                uint32_t *file_line_number)
+{
+       char *line = NULL;
+       uint32_t line_id = 0;
+       int status = 0;
+
+       /* Buffer allocation. */
+       line = malloc(MAX_LINE_SIZE);
+       if (!line)
+               return -ENOMEM;
+
+       /* File read. */
+       for (line_id = 1; ; line_id++) {
+               struct rte_swx_table_entry *entry;
+               int is_blank_or_comment;
+
+               if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+                       break;
+
+               entry = rte_swx_ctl_pipeline_table_entry_read(p,
+                                                             table_name,
+                                                             line,
+                                                             &is_blank_or_comment);
+               if (!entry) {
+                       if (is_blank_or_comment)
+                               continue;
+
+                       status = -EINVAL;
+                       goto error;
+               }
+
+               status = rte_swx_ctl_pipeline_table_default_entry_add(p,
+                                                                     table_name,
+                                                                     entry);
+               table_entry_free(entry);
+               if (status)
+                       goto error;
+       }
+
+error:
+       *file_line_number = line_id;
+       free(line);
+       return status;
+}
+
+static const char cmd_pipeline_table_default_help[] =
+"pipeline <pipeline_name> table <table_name> default <file_name>\n";
+
+static void
+cmd_pipeline_table_default(char **tokens,
+                          uint32_t n_tokens,
+                          char *out,
+                          size_t out_size,
+                          void *obj)
+{
+       struct pipeline *p;
+       char *pipeline_name, *table_name, *file_name;
+       FILE *file = NULL;
+       uint32_t file_line_number = 0;
+       int status;
+
+       if (n_tokens != 6) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+       p = pipeline_find(obj, pipeline_name);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       table_name = tokens[3];
+
+       file_name = tokens[5];
+       file = fopen(file_name, "r");
+       if (!file) {
+               snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+               return;
+       }
+
+       status = pipeline_table_default_entry_add(p->ctl,
+                                                 table_name,
+                                                 file,
+                                                 &file_line_number);
+       if (status)
+               snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
+                        file_name,
+                        file_line_number);
+
+       fclose(file);
+}
+
+static const char cmd_pipeline_table_show_help[] =
+"pipeline <pipeline_name> table <table_name> show\n";
 
 static void
-cmd_pipeline_table_update(char **tokens,
+cmd_pipeline_table_show(char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size,
        void *obj)
 {
        struct pipeline *p;
-       char *pipeline_name, *table_name, *line = NULL;
-       char *file_name_add, *file_name_delete, *file_name_default;
-       FILE *file_add = NULL, *file_delete = NULL, *file_default = NULL;
-       uint32_t line_id;
+       char *pipeline_name, *table_name;
        int status;
 
-       if (n_tokens != 8) {
+       if (n_tokens != 5) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
@@ -1068,211 +1360,857 @@ cmd_pipeline_table_update(char **tokens,
                return;
        }
 
-       if (strcmp(tokens[2], "table") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
+       table_name = tokens[3];
+       status = rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name);
+       if (status)
+               snprintf(out, out_size, MSG_ARG_INVALID, "table_name");
+}
+
+static const char cmd_pipeline_selector_group_add_help[] =
+"pipeline <pipeline_name> selector <selector_name> group add\n";
+
+static void
+cmd_pipeline_selector_group_add(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       char *pipeline_name, *selector_name;
+       uint32_t group_id;
+       int status;
+
+       if (n_tokens != 6) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       table_name = tokens[3];
+       pipeline_name = tokens[1];
+       p = pipeline_find(obj, pipeline_name);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
 
-       if (strcmp(tokens[4], "update") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "update");
+       if (strcmp(tokens[2], "selector") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
                return;
        }
 
-       file_name_add = tokens[5];
-       file_name_delete = tokens[6];
-       file_name_default = tokens[7];
+       selector_name = tokens[3];
 
-       /* File open. */
-       if (strcmp(file_name_add, "none")) {
-               file_add = fopen(file_name_add, "r");
-               if (!file_add) {
-                       snprintf(out, out_size, "Cannot open file %s",
-                               file_name_add);
-                       goto error;
-               }
+       if (strcmp(tokens[4], "group") ||
+               strcmp(tokens[5], "add")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group add");
+               return;
        }
 
-       if (strcmp(file_name_delete, "none")) {
-               file_delete = fopen(file_name_delete, "r");
-               if (!file_delete) {
-                       snprintf(out, out_size, "Cannot open file %s",
-                               file_name_delete);
-                       goto error;
-               }
+       status = rte_swx_ctl_pipeline_selector_group_add(p->ctl,
+               selector_name,
+               &group_id);
+       if (status)
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+       else
+               snprintf(out, out_size, "Group ID: %u\n", group_id);
+}
+
+static const char cmd_pipeline_selector_group_delete_help[] =
+"pipeline <pipeline_name> selector <selector_name> group delete <group_id>\n";
+
+static void
+cmd_pipeline_selector_group_delete(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       char *pipeline_name, *selector_name;
+       uint32_t group_id;
+       int status;
+
+       if (n_tokens != 7) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
        }
 
-       if (strcmp(file_name_default, "none")) {
-               file_default = fopen(file_name_default, "r");
-               if (!file_default) {
-                       snprintf(out, out_size, "Cannot open file %s",
-                               file_name_default);
-                       goto error;
-               }
+       pipeline_name = tokens[1];
+       p = pipeline_find(obj, pipeline_name);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
        }
 
-       if (!file_add && !file_delete && !file_default) {
-               snprintf(out, out_size, "Nothing to be done.");
+       if (strcmp(tokens[2], "selector") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
                return;
        }
 
-       /* Buffer allocation. */
-       line = malloc(2048);
-       if (!line) {
-               snprintf(out, out_size, MSG_OUT_OF_MEMORY);
+       selector_name = tokens[3];
+
+       if (strcmp(tokens[4], "group") ||
+               strcmp(tokens[5], "delete")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group delete");
+               return;
+       }
+
+       if (parser_read_uint32(&group_id, tokens[6]) != 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "group_id");
+               return;
+       }
+
+       status = rte_swx_ctl_pipeline_selector_group_delete(p->ctl,
+               selector_name,
+               group_id);
+       if (status)
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+}
+
+#define GROUP_MEMBER_INFO_TOKENS_MAX 6
+
+static int
+token_is_comment(const char *token)
+{
+       if ((token[0] == '#') ||
+           (token[0] == ';') ||
+           ((token[0] == '/') && (token[1] == '/')))
+               return 1; /* TRUE. */
+
+       return 0; /* FALSE. */
+}
+
+static int
+pipeline_selector_group_member_read(const char *string,
+                                     uint32_t *group_id,
+                                     uint32_t *member_id,
+                                     uint32_t *weight,
+                                     int *is_blank_or_comment)
+{
+       char *token_array[GROUP_MEMBER_INFO_TOKENS_MAX], **tokens;
+       char *s0 = NULL, *s;
+       uint32_t n_tokens = 0, group_id_val = 0, member_id_val = 0, weight_val = 0;
+       int blank_or_comment = 0;
+
+       /* Check input arguments. */
+       if (!string || !string[0])
+               goto error;
+
+       /* Memory allocation. */
+       s0 = strdup(string);
+       if (!s0)
                goto error;
+
+       /* Parse the string into tokens. */
+       for (s = s0; ; ) {
+               char *token;
+
+               token = strtok_r(s, " \f\n\r\t\v", &s);
+               if (!token || token_is_comment(token))
+                       break;
+
+               if (n_tokens >= GROUP_MEMBER_INFO_TOKENS_MAX)
+                       goto error;
+
+               token_array[n_tokens] = token;
+               n_tokens++;
        }
 
-       /* Add. */
-       if (file_add)
-               for (line_id = 1; ; line_id++) {
-                       struct rte_swx_table_entry *entry;
-                       int is_blank_or_comment;
+       if (!n_tokens) {
+               blank_or_comment = 1;
+               goto error;
+       }
 
-                       if (fgets(line, 2048, file_add) == NULL)
-                               break;
+       tokens = token_array;
 
-                       entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
-                               table_name,
-                               line,
-                               &is_blank_or_comment);
-                       if (!entry) {
-                               if (is_blank_or_comment)
-                                       continue;
+       if (n_tokens < 4 ||
+               strcmp(tokens[0], "group") ||
+               strcmp(tokens[2], "member"))
+               goto error;
 
-                               snprintf(out, out_size, MSG_FILE_ERR,
-                                       file_name_add, line_id);
-                               goto error;
-                       }
+       /*
+        * Group ID.
+        */
+       if (parser_read_uint32(&group_id_val, tokens[1]) != 0)
+               goto error;
+       *group_id = group_id_val;
 
-                       status = rte_swx_ctl_pipeline_table_entry_add(p->ctl,
-                               table_name,
-                               entry);
-                       table_entry_free(entry);
-                       if (status) {
-                               snprintf(out, out_size,
-                                       "Invalid entry in file %s at line %u",
-                                       file_name_add, line_id);
-                               goto error;
-                       }
-               }
+       /*
+        * Member ID.
+        */
+       if (parser_read_uint32(&member_id_val, tokens[3]) != 0)
+               goto error;
+       *member_id = member_id_val;
 
+       tokens += 4;
+       n_tokens -= 4;
 
-       /* Delete. */
-       if (file_delete)
-               for (line_id = 1; ; line_id++) {
-                       struct rte_swx_table_entry *entry;
-                       int is_blank_or_comment;
+       /*
+        * Weight.
+        */
+       if (n_tokens && !strcmp(tokens[0], "weight")) {
+               if (n_tokens < 2)
+                       goto error;
 
-                       if (fgets(line, 2048, file_delete) == NULL)
-                               break;
+               if (parser_read_uint32(&weight_val, tokens[1]) != 0)
+                       goto error;
+               *weight = weight_val;
 
-                       entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
-                               table_name,
-                               line,
-                               &is_blank_or_comment);
-                       if (!entry) {
-                               if (is_blank_or_comment)
-                                       continue;
+               tokens += 2;
+               n_tokens -= 2;
+       }
 
-                               snprintf(out, out_size, MSG_FILE_ERR,
-                                       file_name_delete, line_id);
-                               goto error;
-                       }
+       if (n_tokens)
+               goto error;
 
-                       status = rte_swx_ctl_pipeline_table_entry_delete(p->ctl,
-                               table_name,
-                               entry);
-                       table_entry_free(entry);
-                       if (status)  {
-                               snprintf(out, out_size,
-                                       "Invalid entry in file %s at line %u",
-                                       file_name_delete, line_id);
-                               goto error;
-                       }
+       free(s0);
+       return 0;
+
+error:
+       free(s0);
+       if (is_blank_or_comment)
+               *is_blank_or_comment = blank_or_comment;
+       return -EINVAL;
+}
+
+static int
+pipeline_selector_group_members_add(struct rte_swx_ctl_pipeline *p,
+                          const char *selector_name,
+                          FILE *file,
+                          uint32_t *file_line_number)
+{
+       char *line = NULL;
+       uint32_t line_id = 0;
+       int status = 0;
+
+       /* Buffer allocation. */
+       line = malloc(MAX_LINE_SIZE);
+       if (!line)
+               return -ENOMEM;
+
+       /* File read. */
+       for (line_id = 1; ; line_id++) {
+               uint32_t group_id, member_id, weight;
+               int is_blank_or_comment;
+
+               if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+                       break;
+
+               status = pipeline_selector_group_member_read(line,
+                                                             &group_id,
+                                                             &member_id,
+                                                             &weight,
+                                                             &is_blank_or_comment);
+               if (status) {
+                       if (is_blank_or_comment)
+                               continue;
+
+                       goto error;
                }
 
-       /* Default. */
-       if (file_default)
-               for (line_id = 1; ; line_id++) {
-                       struct rte_swx_table_entry *entry;
-                       int is_blank_or_comment;
+               status = rte_swx_ctl_pipeline_selector_group_member_add(p,
+                       selector_name,
+                       group_id,
+                       member_id,
+                       weight);
+               if (status)
+                       goto error;
+       }
+
+error:
+       free(line);
+       *file_line_number = line_id;
+       return status;
+}
+
+static const char cmd_pipeline_selector_group_member_add_help[] =
+"pipeline <pipeline_name> selector <selector_name> group member add <file_name>";
+
+static void
+cmd_pipeline_selector_group_member_add(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       char *pipeline_name, *selector_name, *file_name;
+       FILE *file = NULL;
+       uint32_t file_line_number = 0;
+       int status;
+
+       if (n_tokens != 8) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+       p = pipeline_find(obj, pipeline_name);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       if (strcmp(tokens[2], "selector") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
+               return;
+       }
+
+       selector_name = tokens[3];
+
+       if (strcmp(tokens[4], "group") ||
+               strcmp(tokens[5], "member") ||
+               strcmp(tokens[6], "add")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member add");
+               return;
+       }
+
+       file_name = tokens[7];
+       file = fopen(file_name, "r");
+       if (!file) {
+               snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+               return;
+       }
+
+       status = pipeline_selector_group_members_add(p->ctl,
+                                           selector_name,
+                                           file,
+                                           &file_line_number);
+       if (status)
+               snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
+                        file_name,
+                        file_line_number);
+
+       fclose(file);
+}
+
+static int
+pipeline_selector_group_members_delete(struct rte_swx_ctl_pipeline *p,
+                          const char *selector_name,
+                          FILE *file,
+                          uint32_t *file_line_number)
+{
+       char *line = NULL;
+       uint32_t line_id = 0;
+       int status = 0;
+
+       /* Buffer allocation. */
+       line = malloc(MAX_LINE_SIZE);
+       if (!line)
+               return -ENOMEM;
+
+       /* File read. */
+       for (line_id = 1; ; line_id++) {
+               uint32_t group_id, member_id, weight;
+               int is_blank_or_comment;
+
+               if (fgets(line, MAX_LINE_SIZE, file) == NULL)
+                       break;
+
+               status = pipeline_selector_group_member_read(line,
+                                                             &group_id,
+                                                             &member_id,
+                                                             &weight,
+                                                             &is_blank_or_comment);
+               if (status) {
+                       if (is_blank_or_comment)
+                               continue;
+
+                       goto error;
+               }
+
+               status = rte_swx_ctl_pipeline_selector_group_member_delete(p,
+                       selector_name,
+                       group_id,
+                       member_id);
+               if (status)
+                       goto error;
+       }
+
+error:
+       free(line);
+       *file_line_number = line_id;
+       return status;
+}
+
+static const char cmd_pipeline_selector_group_member_delete_help[] =
+"pipeline <pipeline_name> selector <selector_name> group member delete <file_name>";
+
+static void
+cmd_pipeline_selector_group_member_delete(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       char *pipeline_name, *selector_name, *file_name;
+       FILE *file = NULL;
+       uint32_t file_line_number = 0;
+       int status;
+
+       if (n_tokens != 8) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+       p = pipeline_find(obj, pipeline_name);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       if (strcmp(tokens[2], "selector") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
+               return;
+       }
+
+       selector_name = tokens[3];
+
+       if (strcmp(tokens[4], "group") ||
+               strcmp(tokens[5], "member") ||
+               strcmp(tokens[6], "delete")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member delete");
+               return;
+       }
+
+       file_name = tokens[7];
+       file = fopen(file_name, "r");
+       if (!file) {
+               snprintf(out, out_size, "Cannot open file %s.\n", file_name);
+               return;
+       }
+
+       status = pipeline_selector_group_members_delete(p->ctl,
+                                           selector_name,
+                                           file,
+                                           &file_line_number);
+       if (status)
+               snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
+                        file_name,
+                        file_line_number);
+
+       fclose(file);
+}
+
+static const char cmd_pipeline_selector_show_help[] =
+"pipeline <pipeline_name> selector <selector_name> show\n";
+
+static void
+cmd_pipeline_selector_show(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       char *pipeline_name, *selector_name;
+       int status;
+
+       if (n_tokens != 5) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+       p = pipeline_find(obj, pipeline_name);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       selector_name = tokens[3];
+       status = rte_swx_ctl_pipeline_selector_fprintf(stdout,
+               p->ctl, selector_name);
+       if (status)
+               snprintf(out, out_size, MSG_ARG_INVALID, "selector_name");
+}
+
+static const char cmd_pipeline_commit_help[] =
+"pipeline <pipeline_name> commit\n";
+
+static void
+cmd_pipeline_commit(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       char *pipeline_name;
+       int status;
+
+       if (n_tokens != 3) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+       p = pipeline_find(obj, pipeline_name);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
+       if (status)
+               snprintf(out, out_size, "Commit failed. "
+                       "Use \"commit\" to retry or \"abort\" to discard the pending work.\n");
+}
+
+static const char cmd_pipeline_abort_help[] =
+"pipeline <pipeline_name> abort\n";
+
+static void
+cmd_pipeline_abort(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       char *pipeline_name;
+
+       if (n_tokens != 3) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       pipeline_name = tokens[1];
+       p = pipeline_find(obj, pipeline_name);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       rte_swx_ctl_pipeline_abort(p->ctl);
+}
+
+static const char cmd_pipeline_regrd_help[] =
+"pipeline <pipeline_name> regrd <register_array_name> <index>\n";
+
+static void
+cmd_pipeline_regrd(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       const char *name;
+       uint64_t value;
+       uint32_t idx;
+       int status;
+
+       if (n_tokens != 5) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       p = pipeline_find(obj, tokens[1]);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       if (strcmp(tokens[2], "regrd")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regrd");
+               return;
+       }
+
+       name = tokens[3];
+
+       if (parser_read_uint32(&idx, tokens[4])) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "index");
+               return;
+       }
+
+       status = rte_swx_ctl_pipeline_regarray_read(p->p, name, idx, &value);
+       if (status) {
+               snprintf(out, out_size, "Command failed.\n");
+               return;
+       }
+
+       snprintf(out, out_size, "0x%" PRIx64 "\n", value);
+}
+
+static const char cmd_pipeline_regwr_help[] =
+"pipeline <pipeline_name> regwr <register_array_name> <index> <value>\n";
+
+static void
+cmd_pipeline_regwr(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       const char *name;
+       uint64_t value;
+       uint32_t idx;
+       int status;
+
+       if (n_tokens != 6) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       p = pipeline_find(obj, tokens[1]);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       if (strcmp(tokens[2], "regwr")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regwr");
+               return;
+       }
+
+       name = tokens[3];
+
+       if (parser_read_uint32(&idx, tokens[4])) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "index");
+               return;
+       }
+
+       if (parser_read_uint64(&value, tokens[5])) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "value");
+               return;
+       }
+
+       status = rte_swx_ctl_pipeline_regarray_write(p->p, name, idx, value);
+       if (status) {
+               snprintf(out, out_size, "Command failed.\n");
+               return;
+       }
+}
+
+static const char cmd_pipeline_meter_profile_add_help[] =
+"pipeline <pipeline_name> meter profile <profile_name> add "
+       "cir <cir> pir <pir> cbs <cbs> pbs <pbs>\n";
+
+static void
+cmd_pipeline_meter_profile_add(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct rte_meter_trtcm_params params;
+       struct pipeline *p;
+       const char *profile_name;
+       int status;
+
+       if (n_tokens != 14) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       p = pipeline_find(obj, tokens[1]);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       if (strcmp(tokens[2], "meter")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
+               return;
+       }
+
+       if (strcmp(tokens[3], "profile")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
+               return;
+       }
+
+       profile_name = tokens[4];
+
+       if (strcmp(tokens[5], "add")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
+               return;
+       }
+
+       if (strcmp(tokens[6], "cir")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
+               return;
+       }
+
+       if (parser_read_uint64(&params.cir, tokens[7])) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "cir");
+               return;
+       }
+
+       if (strcmp(tokens[8], "pir")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
+               return;
+       }
+
+       if (parser_read_uint64(&params.pir, tokens[9])) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pir");
+               return;
+       }
+
+       if (strcmp(tokens[10], "cbs")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
+               return;
+       }
+
+       if (parser_read_uint64(&params.cbs, tokens[11])) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
+               return;
+       }
+
+       if (strcmp(tokens[12], "pbs")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
+               return;
+       }
+
+       if (parser_read_uint64(&params.pbs, tokens[13])) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
+               return;
+       }
+
+       status = rte_swx_ctl_meter_profile_add(p->p, profile_name, &params);
+       if (status) {
+               snprintf(out, out_size, "Command failed.\n");
+               return;
+       }
+}
+
+static const char cmd_pipeline_meter_profile_delete_help[] =
+"pipeline <pipeline_name> meter profile <profile_name> delete\n";
+
+static void
+cmd_pipeline_meter_profile_delete(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       const char *profile_name;
+       int status;
+
+       if (n_tokens != 6) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       p = pipeline_find(obj, tokens[1]);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
+
+       if (strcmp(tokens[2], "meter")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
+               return;
+       }
+
+       if (strcmp(tokens[3], "profile")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
+               return;
+       }
+
+       profile_name = tokens[4];
+
+       if (strcmp(tokens[5], "delete")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
+               return;
+       }
+
+       status = rte_swx_ctl_meter_profile_delete(p->p, profile_name);
+       if (status) {
+               snprintf(out, out_size, "Command failed.\n");
+               return;
+       }
+}
+
+static const char cmd_pipeline_meter_reset_help[] =
+"pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
+       "reset\n";
+
+static void
+cmd_pipeline_meter_reset(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size,
+       void *obj)
+{
+       struct pipeline *p;
+       const char *name;
+       uint32_t idx0 = 0, idx1 = 0;
+
+       if (n_tokens != 9) {
+               snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+               return;
+       }
+
+       p = pipeline_find(obj, tokens[1]);
+       if (!p || !p->ctl) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+               return;
+       }
 
-                       if (fgets(line, 2048, file_default) == NULL)
-                               break;
+       if (strcmp(tokens[2], "meter")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
+               return;
+       }
 
-                       entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
-                               table_name,
-                               line,
-                               &is_blank_or_comment);
-                       if (!entry) {
-                               if (is_blank_or_comment)
-                                       continue;
+       name = tokens[3];
 
-                               snprintf(out, out_size, MSG_FILE_ERR,
-                                       file_name_default, line_id);
-                               goto error;
-                       }
+       if (strcmp(tokens[4], "from")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
+               return;
+       }
 
-                       status = rte_swx_ctl_pipeline_table_default_entry_add(p->ctl,
-                               table_name,
-                               entry);
-                       table_entry_free(entry);
-                       if (status) {
-                               snprintf(out, out_size,
-                                       "Invalid entry in file %s at line %u",
-                                       file_name_default, line_id);
-                               goto error;
-                       }
-               }
+       if (parser_read_uint32(&idx0, tokens[5])) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "index0");
+               return;
+       }
 
-       status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
-       if (status) {
-               snprintf(out, out_size, "Commit failed.");
-               goto error;
+       if (strcmp(tokens[6], "to")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
+               return;
        }
 
+       if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "index1");
+               return;
+       }
 
-       rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name);
+       if (strcmp(tokens[8], "reset")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "reset");
+               return;
+       }
 
-       free(line);
-       if (file_add)
-               fclose(file_add);
-       if (file_delete)
-               fclose(file_delete);
-       if (file_default)
-               fclose(file_default);
-       return;
+       for ( ; idx0 <= idx1; idx0++) {
+               int status;
 
-error:
-       rte_swx_ctl_pipeline_abort(p->ctl);
-       free(line);
-       if (file_add)
-               fclose(file_add);
-       if (file_delete)
-               fclose(file_delete);
-       if (file_default)
-               fclose(file_default);
+               status = rte_swx_ctl_meter_reset(p->p, name, idx0);
+               if (status) {
+                       snprintf(out, out_size, "Command failed for index %u.\n", idx0);
+                       return;
+               }
+       }
 }
 
-static const char cmd_pipeline_regrd_help[] =
-"pipeline <pipeline_name> regrd <register_array_name> <index>\n";
+static const char cmd_pipeline_meter_set_help[] =
+"pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
+       "set profile <profile_name>\n";
 
 static void
-cmd_pipeline_regrd(char **tokens,
+cmd_pipeline_meter_set(char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size,
        void *obj)
 {
        struct pipeline *p;
-       const char *name;
-       uint64_t value;
-       uint32_t idx;
-       int status;
+       const char *name, *profile_name;
+       uint32_t idx0 = 0, idx1 = 0;
 
-       if (n_tokens != 5) {
+       if (n_tokens != 11) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
@@ -1283,44 +2221,73 @@ cmd_pipeline_regrd(char **tokens,
                return;
        }
 
-       if (strcmp(tokens[2], "regrd")) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regrd");
+       if (strcmp(tokens[2], "meter")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
                return;
        }
 
        name = tokens[3];
 
-       if (parser_read_uint32(&idx, tokens[4])) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "index");
+       if (strcmp(tokens[4], "from")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
                return;
        }
 
-       status = rte_swx_ctl_pipeline_regarray_read(p->p, name, idx, &value);
-       if (status) {
-               snprintf(out, out_size, "Command failed.\n");
+       if (parser_read_uint32(&idx0, tokens[5])) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "index0");
                return;
        }
 
-       snprintf(out, out_size, "0x%" PRIx64 "\n", value);
+       if (strcmp(tokens[6], "to")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
+               return;
+       }
+
+       if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "index1");
+               return;
+       }
+
+       if (strcmp(tokens[8], "set")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "set");
+               return;
+       }
+
+       if (strcmp(tokens[9], "profile")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
+               return;
+       }
+
+       profile_name = tokens[10];
+
+       for ( ; idx0 <= idx1; idx0++) {
+               int status;
+
+               status = rte_swx_ctl_meter_set(p->p, name, idx0, profile_name);
+               if (status) {
+                       snprintf(out, out_size, "Command failed for index %u.\n", idx0);
+                       return;
+               }
+       }
 }
 
-static const char cmd_pipeline_regwr_help[] =
-"pipeline <pipeline_name> regwr <register_array_name> <index> <value>\n";
+static const char cmd_pipeline_meter_stats_help[] =
+"pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
+       "stats\n";
 
 static void
-cmd_pipeline_regwr(char **tokens,
+cmd_pipeline_meter_stats(char **tokens,
        uint32_t n_tokens,
        char *out,
        size_t out_size,
        void *obj)
 {
+       struct rte_swx_ctl_meter_stats stats;
        struct pipeline *p;
        const char *name;
-       uint64_t value;
-       uint32_t idx;
-       int status;
+       uint32_t idx0 = 0, idx1 = 0;
 
-       if (n_tokens != 6) {
+       if (n_tokens != 9) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
@@ -1331,28 +2298,84 @@ cmd_pipeline_regwr(char **tokens,
                return;
        }
 
-       if (strcmp(tokens[2], "regwr")) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regwr");
+       if (strcmp(tokens[2], "meter")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
                return;
        }
 
        name = tokens[3];
 
-       if (parser_read_uint32(&idx, tokens[4])) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "index");
+       if (strcmp(tokens[4], "from")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
                return;
        }
 
-       if (parser_read_uint64(&value, tokens[5])) {
-               snprintf(out, out_size, MSG_ARG_INVALID, "value");
+       if (parser_read_uint32(&idx0, tokens[5])) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "index0");
                return;
        }
 
-       status = rte_swx_ctl_pipeline_regarray_write(p->p, name, idx, value);
-       if (status) {
-               snprintf(out, out_size, "Command failed.\n");
+       if (strcmp(tokens[6], "to")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
+               return;
+       }
+
+       if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
+               snprintf(out, out_size, MSG_ARG_INVALID, "index1");
+               return;
+       }
+
+       if (strcmp(tokens[8], "stats")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
                return;
        }
+
+       /* Table header. */
+       snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
+                "-------",
+                "----------------", "----------------", "----------------",
+                "----------------", "----------------", "----------------");
+       out_size -= strlen(out);
+       out += strlen(out);
+
+       snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
+                "METER #",
+                "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
+                "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
+       out_size -= strlen(out);
+       out += strlen(out);
+
+       snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
+                "-------",
+                "----------------", "----------------", "----------------",
+                "----------------", "----------------", "----------------");
+       out_size -= strlen(out);
+       out += strlen(out);
+
+       /* Table rows. */
+       for ( ; idx0 <= idx1; idx0++) {
+               int status;
+
+               status = rte_swx_ctl_meter_stats_read(p->p, name, idx0, &stats);
+               if (status) {
+                       snprintf(out, out_size, "Pipeline meter stats error at index %u.\n", idx0);
+                       out_size -= strlen(out);
+                       out += strlen(out);
+                       return;
+               }
+
+               snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
+                        " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
+                        idx0,
+                        stats.n_pkts[RTE_COLOR_GREEN],
+                        stats.n_pkts[RTE_COLOR_YELLOW],
+                        stats.n_pkts[RTE_COLOR_RED],
+                        stats.n_bytes[RTE_COLOR_GREEN],
+                        stats.n_bytes[RTE_COLOR_YELLOW],
+                        stats.n_bytes[RTE_COLOR_RED]);
+               out_size -= strlen(out);
+               out += strlen(out);
+       }
 }
 
 static const char cmd_pipeline_stats_help[] =
@@ -1410,7 +2433,7 @@ cmd_pipeline_stats(char **tokens,
                out += strlen(out);
        }
 
-       snprintf(out, out_size, "Output ports:\n");
+       snprintf(out, out_size, "\nOutput ports:\n");
        out_size -= strlen(out);
        out += strlen(out);
 
@@ -1426,6 +2449,58 @@ cmd_pipeline_stats(char **tokens,
                out_size -= strlen(out);
                out += strlen(out);
        }
+
+       snprintf(out, out_size, "\nTables:\n");
+       out_size -= strlen(out);
+       out += strlen(out);
+
+       for (i = 0; i < info.n_tables; i++) {
+               struct rte_swx_ctl_table_info table_info;
+               uint64_t n_pkts_action[info.n_actions];
+               struct rte_swx_table_stats stats = {
+                       .n_pkts_hit = 0,
+                       .n_pkts_miss = 0,
+                       .n_pkts_action = n_pkts_action,
+               };
+               uint32_t j;
+
+               status = rte_swx_ctl_table_info_get(p->p, i, &table_info);
+               if (status) {
+                       snprintf(out, out_size, "Table info get error.");
+                       return;
+               }
+
+               status = rte_swx_ctl_pipeline_table_stats_read(p->p, table_info.name, &stats);
+               if (status) {
+                       snprintf(out, out_size, "Table stats read error.");
+                       return;
+               }
+
+               snprintf(out, out_size, "\tTable %s:\n"
+                       "\t\tHit (packets): %" PRIu64 "\n"
+                       "\t\tMiss (packets): %" PRIu64 "\n",
+                       table_info.name,
+                       stats.n_pkts_hit,
+                       stats.n_pkts_miss);
+               out_size -= strlen(out);
+               out += strlen(out);
+
+               for (j = 0; j < info.n_actions; j++) {
+                       struct rte_swx_ctl_action_info action_info;
+
+                       status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
+                       if (status) {
+                               snprintf(out, out_size, "Action info get error.");
+                               return;
+                       }
+
+                       snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
+                               action_info.name,
+                               stats.n_pkts_action[j]);
+                       out_size -= strlen(out);
+                       out += strlen(out);
+               }
+       }
 }
 
 static const char cmd_thread_pipeline_enable_help[] =
@@ -1548,9 +2623,24 @@ cmd_help(char **tokens,
                        "\tpipeline port in\n"
                        "\tpipeline port out\n"
                        "\tpipeline build\n"
-                       "\tpipeline table update\n"
+                       "\tpipeline table add\n"
+                       "\tpipeline table delete\n"
+                       "\tpipeline table default\n"
+                       "\tpipeline table show\n"
+                       "\tpipeline selector group add\n"
+                       "\tpipeline selector group delete\n"
+                       "\tpipeline selector group member add\n"
+                       "\tpipeline selector group member delete\n"
+                       "\tpipeline selector show\n"
+                       "\tpipeline commit\n"
+                       "\tpipeline abort\n"
                        "\tpipeline regrd\n"
                        "\tpipeline regwr\n"
+                       "\tpipeline meter profile add\n"
+                       "\tpipeline meter profile delete\n"
+                       "\tpipeline meter reset\n"
+                       "\tpipeline meter set\n"
+                       "\tpipeline meter stats\n"
                        "\tpipeline stats\n"
                        "\tthread pipeline enable\n"
                        "\tthread pipeline disable\n\n");
@@ -1607,9 +2697,103 @@ cmd_help(char **tokens,
        if ((strcmp(tokens[0], "pipeline") == 0) &&
                (n_tokens == 3) &&
                (strcmp(tokens[1], "table") == 0) &&
-               (strcmp(tokens[2], "update") == 0)) {
+               (strcmp(tokens[2], "add") == 0)) {
+               snprintf(out, out_size, "\n%s\n",
+                       cmd_pipeline_table_add_help);
+               return;
+       }
+
+       if ((strcmp(tokens[0], "pipeline") == 0) &&
+               (n_tokens == 3) &&
+               (strcmp(tokens[1], "table") == 0) &&
+               (strcmp(tokens[2], "delete") == 0)) {
+               snprintf(out, out_size, "\n%s\n",
+                       cmd_pipeline_table_delete_help);
+               return;
+       }
+
+       if ((strcmp(tokens[0], "pipeline") == 0) &&
+               (n_tokens == 3) &&
+               (strcmp(tokens[1], "table") == 0) &&
+               (strcmp(tokens[2], "default") == 0)) {
+               snprintf(out, out_size, "\n%s\n",
+                       cmd_pipeline_table_default_help);
+               return;
+       }
+
+       if ((strcmp(tokens[0], "pipeline") == 0) &&
+               (n_tokens == 3) &&
+               (strcmp(tokens[1], "table") == 0) &&
+               (strcmp(tokens[2], "show") == 0)) {
+               snprintf(out, out_size, "\n%s\n",
+                       cmd_pipeline_table_show_help);
+               return;
+       }
+
+       if ((strcmp(tokens[0], "pipeline") == 0) &&
+               (n_tokens == 4) &&
+               (strcmp(tokens[1], "selector") == 0) &&
+               (strcmp(tokens[2], "group") == 0) &&
+               (strcmp(tokens[3], "add") == 0)) {
+               snprintf(out, out_size, "\n%s\n",
+                       cmd_pipeline_selector_group_add_help);
+               return;
+       }
+
+       if ((strcmp(tokens[0], "pipeline") == 0) &&
+               (n_tokens == 4) &&
+               (strcmp(tokens[1], "selector") == 0) &&
+               (strcmp(tokens[2], "group") == 0) &&
+               (strcmp(tokens[3], "delete") == 0)) {
+               snprintf(out, out_size, "\n%s\n",
+                       cmd_pipeline_selector_group_delete_help);
+               return;
+       }
+
+       if ((strcmp(tokens[0], "pipeline") == 0) &&
+               (n_tokens == 5) &&
+               (strcmp(tokens[1], "selector") == 0) &&
+               (strcmp(tokens[2], "group") == 0) &&
+               (strcmp(tokens[3], "member") == 0) &&
+               (strcmp(tokens[4], "add") == 0)) {
+               snprintf(out, out_size, "\n%s\n",
+                       cmd_pipeline_selector_group_member_add_help);
+               return;
+       }
+
+       if ((strcmp(tokens[0], "pipeline") == 0) &&
+               (n_tokens == 5) &&
+               (strcmp(tokens[1], "selector") == 0) &&
+               (strcmp(tokens[2], "group") == 0) &&
+               (strcmp(tokens[3], "member") == 0) &&
+               (strcmp(tokens[4], "delete") == 0)) {
+               snprintf(out, out_size, "\n%s\n",
+                       cmd_pipeline_selector_group_member_delete_help);
+               return;
+       }
+
+       if ((strcmp(tokens[0], "pipeline") == 0) &&
+               (n_tokens == 3) &&
+               (strcmp(tokens[1], "selector") == 0) &&
+               (strcmp(tokens[2], "show") == 0)) {
+               snprintf(out, out_size, "\n%s\n",
+                       cmd_pipeline_selector_show_help);
+               return;
+       }
+
+       if ((strcmp(tokens[0], "pipeline") == 0) &&
+               (n_tokens == 2) &&
+               (strcmp(tokens[1], "commit") == 0)) {
+               snprintf(out, out_size, "\n%s\n",
+                       cmd_pipeline_commit_help);
+               return;
+       }
+
+       if ((strcmp(tokens[0], "pipeline") == 0) &&
+               (n_tokens == 2) &&
+               (strcmp(tokens[1], "abort") == 0)) {
                snprintf(out, out_size, "\n%s\n",
-                       cmd_pipeline_table_update_help);
+                       cmd_pipeline_abort_help);
                return;
        }
 
@@ -1625,6 +2809,43 @@ cmd_help(char **tokens,
                return;
        }
 
+       if (!strcmp(tokens[0], "pipeline") &&
+               (n_tokens == 4) && !strcmp(tokens[1], "meter")
+               && !strcmp(tokens[2], "profile")
+               && !strcmp(tokens[3], "add")) {
+               snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_add_help);
+               return;
+       }
+
+       if (!strcmp(tokens[0], "pipeline") &&
+               (n_tokens == 4) && !strcmp(tokens[1], "meter")
+               && !strcmp(tokens[2], "profile")
+               && !strcmp(tokens[3], "delete")) {
+               snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_delete_help);
+               return;
+       }
+
+       if (!strcmp(tokens[0], "pipeline") &&
+               (n_tokens == 3) && !strcmp(tokens[1], "meter")
+               && !strcmp(tokens[2], "reset")) {
+               snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_reset_help);
+               return;
+       }
+
+       if (!strcmp(tokens[0], "pipeline") &&
+               (n_tokens == 3) && !strcmp(tokens[1], "meter")
+               && !strcmp(tokens[2], "set")) {
+               snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_set_help);
+               return;
+       }
+
+       if (!strcmp(tokens[0], "pipeline") &&
+               (n_tokens == 3) && !strcmp(tokens[1], "meter")
+               && !strcmp(tokens[2], "stats")) {
+               snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_stats_help);
+               return;
+       }
+
        if ((strcmp(tokens[0], "pipeline") == 0) &&
                (n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
                snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
@@ -1730,9 +2951,94 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
                        return;
                }
 
+               if ((n_tokens >= 5) &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "add") == 0)) {
+                       cmd_pipeline_table_add(tokens, n_tokens, out,
+                               out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 5) &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "delete") == 0)) {
+                       cmd_pipeline_table_delete(tokens, n_tokens, out,
+                               out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 5) &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "default") == 0)) {
+                       cmd_pipeline_table_default(tokens, n_tokens, out,
+                               out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 5) &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "show") == 0)) {
+                       cmd_pipeline_table_show(tokens, n_tokens, out,
+                               out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 6) &&
+                       (strcmp(tokens[2], "selector") == 0) &&
+                       (strcmp(tokens[4], "group") == 0) &&
+                       (strcmp(tokens[5], "add") == 0)) {
+                       cmd_pipeline_selector_group_add(tokens, n_tokens, out,
+                               out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 6) &&
+                       (strcmp(tokens[2], "selector") == 0) &&
+                       (strcmp(tokens[4], "group") == 0) &&
+                       (strcmp(tokens[5], "delete") == 0)) {
+                       cmd_pipeline_selector_group_delete(tokens, n_tokens, out,
+                               out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 7) &&
+                       (strcmp(tokens[2], "selector") == 0) &&
+                       (strcmp(tokens[4], "group") == 0) &&
+                       (strcmp(tokens[5], "member") == 0) &&
+                       (strcmp(tokens[6], "add") == 0)) {
+                       cmd_pipeline_selector_group_member_add(tokens, n_tokens, out,
+                               out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 7) &&
+                       (strcmp(tokens[2], "selector") == 0) &&
+                       (strcmp(tokens[4], "group") == 0) &&
+                       (strcmp(tokens[5], "member") == 0) &&
+                       (strcmp(tokens[6], "delete") == 0)) {
+                       cmd_pipeline_selector_group_member_delete(tokens, n_tokens, out,
+                               out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 5) &&
+                       (strcmp(tokens[2], "selector") == 0) &&
+                       (strcmp(tokens[4], "show") == 0)) {
+                       cmd_pipeline_selector_show(tokens, n_tokens, out,
+                               out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 3) &&
+                       (strcmp(tokens[2], "commit") == 0)) {
+                       cmd_pipeline_commit(tokens, n_tokens, out,
+                               out_size, obj);
+                       return;
+               }
+
                if ((n_tokens >= 3) &&
-                       (strcmp(tokens[2], "table") == 0)) {
-                       cmd_pipeline_table_update(tokens, n_tokens, out,
+                       (strcmp(tokens[2], "abort") == 0)) {
+                       cmd_pipeline_abort(tokens, n_tokens, out,
                                out_size, obj);
                        return;
                }
@@ -1749,6 +3055,43 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
                        return;
                }
 
+               if ((n_tokens >= 6) &&
+                       (strcmp(tokens[2], "meter") == 0) &&
+                       (strcmp(tokens[3], "profile") == 0) &&
+                       (strcmp(tokens[5], "add") == 0)) {
+                       cmd_pipeline_meter_profile_add(tokens, n_tokens, out, out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 6) &&
+                       (strcmp(tokens[2], "meter") == 0) &&
+                       (strcmp(tokens[3], "profile") == 0) &&
+                       (strcmp(tokens[5], "delete") == 0)) {
+                       cmd_pipeline_meter_profile_delete(tokens, n_tokens, out, out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 9) &&
+                       (strcmp(tokens[2], "meter") == 0) &&
+                       (strcmp(tokens[8], "reset") == 0)) {
+                       cmd_pipeline_meter_reset(tokens, n_tokens, out, out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 9) &&
+                       (strcmp(tokens[2], "meter") == 0) &&
+                       (strcmp(tokens[8], "set") == 0)) {
+                       cmd_pipeline_meter_set(tokens, n_tokens, out, out_size, obj);
+                       return;
+               }
+
+               if ((n_tokens >= 9) &&
+                       (strcmp(tokens[2], "meter") == 0) &&
+                       (strcmp(tokens[8], "stats") == 0)) {
+                       cmd_pipeline_meter_stats(tokens, n_tokens, out, out_size, obj);
+                       return;
+               }
+
                if ((n_tokens >= 3) &&
                        (strcmp(tokens[2], "stats") == 0)) {
                        cmd_pipeline_stats(tokens, n_tokens, out, out_size,