net: add macro for MAC address print
[dpdk.git] / examples / ip_pipeline / cli.c
index 5011cad..f4b208b 100644 (file)
@@ -245,22 +245,35 @@ static void
 print_link_info(struct link *link, char *out, size_t out_size)
 {
        struct rte_eth_stats stats;
-       struct ether_addr mac_addr;
+       struct rte_ether_addr mac_addr;
        struct rte_eth_link eth_link;
        uint16_t mtu;
+       int ret;
 
        memset(&stats, 0, sizeof(stats));
        rte_eth_stats_get(link->port_id, &stats);
 
-       rte_eth_macaddr_get(link->port_id, &mac_addr);
-       rte_eth_link_get(link->port_id, &eth_link);
+       ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
+       if (ret != 0) {
+               snprintf(out, out_size, "\n%s: MAC address get failed: %s",
+                        link->name, rte_strerror(-ret));
+               return;
+       }
+
+       ret = rte_eth_link_get(link->port_id, &eth_link);
+       if (ret < 0) {
+               snprintf(out, out_size, "\n%s: link get failed: %s",
+                        link->name, rte_strerror(-ret));
+               return;
+       }
+
        rte_eth_dev_get_mtu(link->port_id, &mtu);
 
        snprintf(out, out_size,
                "\n"
                "%s: flags=<%s> mtu %u\n"
-               "\tether %02X:%02X:%02X:%02X:%02X:%02X rxqueues %u txqueues %u\n"
-               "\tport# %u  speed %u Mbps\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"
                "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
@@ -274,7 +287,7 @@ print_link_info(struct link *link, char *out, size_t out_size)
                link->n_rxq,
                link->n_txq,
                link->port_id,
-               eth_link.link_speed,
+               rte_eth_link_speed_to_str(eth_link.link_speed),
                stats.ipackets,
                stats.ibytes,
                stats.ierrors,
@@ -377,7 +390,9 @@ cmd_swq(char **tokens,
 static const char cmd_tmgr_subport_profile_help[] =
 "tmgr subport profile\n"
 "   <tb_rate> <tb_size>\n"
-"   <tc0_rate> <tc1_rate> <tc2_rate> <tc3_rate>\n"
+"   <tc0_rate> <tc1_rate> <tc2_rate> <tc3_rate> <tc4_rate>"
+"        <tc5_rate> <tc6_rate> <tc7_rate> <tc8_rate>"
+"        <tc9_rate> <tc10_rate> <tc11_rate> <tc12_rate>\n"
 "   <tc_period>\n";
 
 static void
@@ -386,36 +401,37 @@ cmd_tmgr_subport_profile(char **tokens,
        char *out,
        size_t out_size)
 {
-       struct rte_sched_subport_params p;
+       struct rte_sched_subport_profile_params subport_profile;
        int status, i;
 
-       if (n_tokens != 10) {
+       if (n_tokens != 19) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       if (parser_read_uint32(&p.tb_rate, tokens[3]) != 0) {
+       if (parser_read_uint64(&subport_profile.tb_rate, tokens[3]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "tb_rate");
                return;
        }
 
-       if (parser_read_uint32(&p.tb_size, tokens[4]) != 0) {
+       if (parser_read_uint64(&subport_profile.tb_size, tokens[4]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "tb_size");
                return;
        }
 
        for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
-               if (parser_read_uint32(&p.tc_rate[i], tokens[5 + i]) != 0) {
+               if (parser_read_uint64(&subport_profile.tc_rate[i],
+                               tokens[5 + i]) != 0) {
                        snprintf(out, out_size, MSG_ARG_INVALID, "tc_rate");
                        return;
                }
 
-       if (parser_read_uint32(&p.tc_period, tokens[9]) != 0) {
+       if (parser_read_uint64(&subport_profile.tc_period, tokens[18]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "tc_period");
                return;
        }
 
-       status = tmgr_subport_profile_add(&p);
+       status = tmgr_subport_profile_add(&subport_profile);
        if (status != 0) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
@@ -425,10 +441,12 @@ cmd_tmgr_subport_profile(char **tokens,
 static const char cmd_tmgr_pipe_profile_help[] =
 "tmgr pipe profile\n"
 "   <tb_rate> <tb_size>\n"
-"   <tc0_rate> <tc1_rate> <tc2_rate> <tc3_rate>\n"
+"   <tc0_rate> <tc1_rate> <tc2_rate> <tc3_rate> <tc4_rate>"
+"     <tc5_rate> <tc6_rate> <tc7_rate> <tc8_rate>"
+"     <tc9_rate> <tc10_rate> <tc11_rate> <tc12_rate>\n"
 "   <tc_period>\n"
 "   <tc_ov_weight>\n"
-"   <wrr_weight0..15>\n";
+"   <wrr_weight0..3>\n";
 
 static void
 cmd_tmgr_pipe_profile(char **tokens,
@@ -439,41 +457,39 @@ cmd_tmgr_pipe_profile(char **tokens,
        struct rte_sched_pipe_params p;
        int status, i;
 
-       if (n_tokens != 27) {
+       if (n_tokens != 24) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
 
-       if (parser_read_uint32(&p.tb_rate, tokens[3]) != 0) {
+       if (parser_read_uint64(&p.tb_rate, tokens[3]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "tb_rate");
                return;
        }
 
-       if (parser_read_uint32(&p.tb_size, tokens[4]) != 0) {
+       if (parser_read_uint64(&p.tb_size, tokens[4]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "tb_size");
                return;
        }
 
        for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
-               if (parser_read_uint32(&p.tc_rate[i], tokens[5 + i]) != 0) {
+               if (parser_read_uint64(&p.tc_rate[i], tokens[5 + i]) != 0) {
                        snprintf(out, out_size, MSG_ARG_INVALID, "tc_rate");
                        return;
                }
 
-       if (parser_read_uint32(&p.tc_period, tokens[9]) != 0) {
+       if (parser_read_uint64(&p.tc_period, tokens[18]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "tc_period");
                return;
        }
 
-#ifdef RTE_SCHED_SUBPORT_TC_OV
-       if (parser_read_uint8(&p.tc_ov_weight, tokens[10]) != 0) {
+       if (parser_read_uint8(&p.tc_ov_weight, tokens[19]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "tc_ov_weight");
                return;
        }
-#endif
 
-       for (i = 0; i < RTE_SCHED_QUEUES_PER_PIPE; i++)
-               if (parser_read_uint8(&p.wrr_weights[i], tokens[11 + i]) != 0) {
+       for (i = 0; i < RTE_SCHED_BE_QUEUES_PER_PIPE; i++)
+               if (parser_read_uint8(&p.wrr_weights[i], tokens[20 + i]) != 0) {
                        snprintf(out, out_size, MSG_ARG_INVALID, "wrr_weights");
                        return;
                }
@@ -490,7 +506,6 @@ static const char cmd_tmgr_help[] =
 "   rate <rate>\n"
 "   spp <n_subports_per_port>\n"
 "   pps <n_pipes_per_subport>\n"
-"   qsize <qsize_tc0> <qsize_tc1> <qsize_tc2> <qsize_tc3>\n"
 "   fo <frame_overhead>\n"
 "   mtu <mtu>\n"
 "   cpu <cpu_id>\n";
@@ -504,9 +519,8 @@ cmd_tmgr(char **tokens,
        struct tmgr_port_params p;
        char *name;
        struct tmgr_port *tmgr_port;
-       int i;
 
-       if (n_tokens != 19) {
+       if (n_tokens != 14) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
@@ -518,7 +532,7 @@ cmd_tmgr(char **tokens,
                return;
        }
 
-       if (parser_read_uint32(&p.rate, tokens[3]) != 0) {
+       if (parser_read_uint64(&p.rate, tokens[3]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "rate");
                return;
        }
@@ -534,7 +548,7 @@ cmd_tmgr(char **tokens,
        }
 
        if (strcmp(tokens[6], "pps") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
                return;
        }
 
@@ -543,43 +557,32 @@ cmd_tmgr(char **tokens,
                return;
        }
 
-       if (strcmp(tokens[8], "qsize") != 0) {
-               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
-               return;
-       }
-
-       for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
-               if (parser_read_uint16(&p.qsize[i], tokens[9 + i]) != 0) {
-                       snprintf(out, out_size, MSG_ARG_INVALID, "qsize");
-                       return;
-               }
-
-       if (strcmp(tokens[13], "fo") != 0) {
+       if (strcmp(tokens[8], "fo") != 0) {
                snprintf(out, out_size, MSG_ARG_NOT_FOUND, "fo");
                return;
        }
 
-       if (parser_read_uint32(&p.frame_overhead, tokens[14]) != 0) {
+       if (parser_read_uint32(&p.frame_overhead, tokens[9]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "frame_overhead");
                return;
        }
 
-       if (strcmp(tokens[15], "mtu") != 0) {
+       if (strcmp(tokens[10], "mtu") != 0) {
                snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mtu");
                return;
        }
 
-       if (parser_read_uint32(&p.mtu, tokens[16]) != 0) {
+       if (parser_read_uint32(&p.mtu, tokens[11]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
                return;
        }
 
-       if (strcmp(tokens[17], "cpu") != 0) {
+       if (strcmp(tokens[12], "cpu") != 0) {
                snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
                return;
        }
 
-       if (parser_read_uint32(&p.cpu_id, tokens[18]) != 0) {
+       if (parser_read_uint32(&p.cpu_id, tokens[13]) != 0) {
                snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
                return;
        }
@@ -790,7 +793,8 @@ cmd_kni(char **tokens,
 static const char cmd_cryptodev_help[] =
 "cryptodev <cryptodev_name>\n"
 "   dev <device_name> | dev_id <device_id>\n"
-"   queue <n_queues> <queue_size>\n";
+"   queue <n_queues> <queue_size>\n"
+"   max_sessions <n_sessions>";
 
 static void
 cmd_cryptodev(char **tokens,
@@ -802,7 +806,7 @@ cmd_cryptodev(char **tokens,
        char *name;
 
        memset(&params, 0, sizeof(params));
-       if (n_tokens != 7) {
+       if (n_tokens != 9) {
                snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
                return;
        }
@@ -825,7 +829,7 @@ cmd_cryptodev(char **tokens,
 
        if (strcmp(tokens[4], "queue")) {
                snprintf(out, out_size, MSG_ARG_NOT_FOUND,
-                       "4");
+                       "queue");
                return;
        }
 
@@ -841,6 +845,18 @@ cmd_cryptodev(char **tokens,
                return;
        }
 
+       if (strcmp(tokens[7], "max_sessions")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND,
+                       "max_sessions");
+               return;
+       }
+
+       if (parser_read_uint32(&params.session_pool_size, tokens[8]) < 0) {
+               snprintf(out, out_size, MSG_ARG_INVALID,
+                       "queue_size");
+               return;
+       }
+
        if (cryptodev_create(name, &params) == NULL) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
@@ -1022,7 +1038,7 @@ static const char cmd_table_action_profile_help[] =
 "       tc <n_tc>\n"
 "       stats none | pkts | bytes | both]\n"
 "   [tm spp <n_subports_per_port> pps <n_pipes_per_subport>]\n"
-"   [encap ether | vlan | qinq | mpls | pppoe |\n"
+"   [encap ether | vlan | qinq | mpls | pppoe | qinq_pppoe \n"
 "       vxlan offset <ether_offset> ipv4 | ipv6 vlan on | off]\n"
 "   [nat src | dst\n"
 "       proto udp | tcp]\n"
@@ -1030,9 +1046,7 @@ static const char cmd_table_action_profile_help[] =
 "       stats none | pkts]\n"
 "   [stats pkts | bytes | both]\n"
 "   [time]\n"
-"   [sym_crypto dev <CRYPTODEV_NAME> offset <op_offset> "
-"       mempool_create <mempool_name>\n"
-"       mempool_init <mempool_name>]\n"
+"   [sym_crypto dev <CRYPTODEV_NAME> offset <op_offset>]\n"
 "   [tag]\n"
 "   [decap]\n";
 
@@ -1290,7 +1304,10 @@ cmd_table_action_profile(char **tokens,
 
                        p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_VXLAN;
                        n_extra_tokens = 5;
-               } else {
+               } else if (strcmp(tokens[t0 + 1], "qinq_pppoe") == 0)
+                       p.encap.encap_mask =
+                               1LLU << RTE_TABLE_ACTION_ENCAP_QINQ_PPPOE;
+               else {
                        snprintf(out, out_size, MSG_ARG_MISMATCH, "encap");
                        return;
                }
@@ -1404,13 +1421,10 @@ cmd_table_action_profile(char **tokens,
 
        if ((t0 < n_tokens) && (strcmp(tokens[t0], "sym_crypto") == 0)) {
                struct cryptodev *cryptodev;
-               struct mempool *mempool;
 
-               if (n_tokens < t0 + 9 ||
+               if (n_tokens < t0 + 5 ||
                                strcmp(tokens[t0 + 1], "dev") ||
-                               strcmp(tokens[t0 + 3], "offset") ||
-                               strcmp(tokens[t0 + 5], "mempool_create") ||
-                               strcmp(tokens[t0 + 7], "mempool_init")) {
+                               strcmp(tokens[t0 + 3], "offset")) {
                        snprintf(out, out_size, MSG_ARG_MISMATCH,
                                "table action profile sym_crypto");
                        return;
@@ -1432,25 +1446,12 @@ cmd_table_action_profile(char **tokens,
                        return;
                }
 
-               mempool = mempool_find(tokens[t0 + 6]);
-               if (mempool == NULL) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "table action profile sym_crypto");
-                       return;
-               }
-               p.sym_crypto.mp_create = mempool->m;
-
-               mempool = mempool_find(tokens[t0 + 8]);
-               if (mempool == NULL) {
-                       snprintf(out, out_size, MSG_ARG_INVALID,
-                               "table action profile sym_crypto");
-                       return;
-               }
-               p.sym_crypto.mp_init = mempool->m;
+               p.sym_crypto.mp_create = cryptodev->mp_create;
+               p.sym_crypto.mp_init = cryptodev->mp_init;
 
                p.action_mask |= 1LLU << RTE_TABLE_ACTION_SYM_CRYPTO;
 
-               t0 += 9;
+               t0 += 5;
        } /* sym_crypto */
 
        if ((t0 < n_tokens) && (strcmp(tokens[t0], "tag") == 0)) {
@@ -2656,7 +2657,7 @@ struct pkt_key_qinq {
        uint16_t svlan;
        uint16_t ethertype_cvlan;
        uint16_t cvlan;
-} __attribute__((__packed__));
+} __rte_packed;
 
 struct pkt_key_ipv4_5tuple {
        uint8_t time_to_live;
@@ -2666,7 +2667,7 @@ struct pkt_key_ipv4_5tuple {
        uint32_t da;
        uint16_t sp;
        uint16_t dp;
-} __attribute__((__packed__));
+} __rte_packed;
 
 struct pkt_key_ipv6_5tuple {
        uint16_t payload_length;
@@ -2676,15 +2677,15 @@ struct pkt_key_ipv6_5tuple {
        uint8_t da[16];
        uint16_t sp;
        uint16_t dp;
-} __attribute__((__packed__));
+} __rte_packed;
 
 struct pkt_key_ipv4_addr {
        uint32_t addr;
-} __attribute__((__packed__));
+} __rte_packed;
 
 struct pkt_key_ipv6_addr {
        uint8_t addr[16];
-} __attribute__((__packed__));
+} __rte_packed;
 
 static uint32_t
 parse_match(char **tokens,
@@ -3090,6 +3091,7 @@ parse_match(char **tokens,
  *       ether <da> <sa>
  *       | vlan <da> <sa> <pcp> <dei> <vid>
  *       | qinq <da> <sa> <pcp> <dei> <vid> <pcp> <dei> <vid>
+ *       | qinq_pppoe <da> <sa> <pcp> <dei> <vid> <pcp> <dei> <vid> <session_id>
  *       | mpls unicast | multicast
  *          <da> <sa>
  *          label0 <label> <tc> <ttl>
@@ -3238,11 +3240,11 @@ parse_table_action_meter_tc(char **tokens,
                parser_read_uint32(&mtr->meter_profile_id, tokens[1]) ||
                strcmp(tokens[2], "policer") ||
                strcmp(tokens[3], "g") ||
-               parse_policer_action(tokens[4], &mtr->policer[e_RTE_METER_GREEN]) ||
+               parse_policer_action(tokens[4], &mtr->policer[RTE_COLOR_GREEN]) ||
                strcmp(tokens[5], "y") ||
-               parse_policer_action(tokens[6], &mtr->policer[e_RTE_METER_YELLOW]) ||
+               parse_policer_action(tokens[6], &mtr->policer[RTE_COLOR_YELLOW]) ||
                strcmp(tokens[7], "r") ||
-               parse_policer_action(tokens[8], &mtr->policer[e_RTE_METER_RED]))
+               parse_policer_action(tokens[8], &mtr->policer[RTE_COLOR_RED]))
                return 0;
 
        return 9;
@@ -3391,6 +3393,44 @@ parse_table_action_encap(char **tokens,
                return 1 + 9;
        }
 
+       /* qinq_pppoe */
+       if (n_tokens && (strcmp(tokens[0], "qinq_pppoe") == 0)) {
+               uint32_t svlan_pcp, svlan_dei, svlan_vid;
+               uint32_t cvlan_pcp, cvlan_dei, cvlan_vid;
+
+               if ((n_tokens < 10) ||
+                       parse_mac_addr(tokens[1],
+                               &a->encap.qinq_pppoe.ether.da) ||
+                       parse_mac_addr(tokens[2],
+                               &a->encap.qinq_pppoe.ether.sa) ||
+                       parser_read_uint32(&svlan_pcp, tokens[3]) ||
+                       (svlan_pcp > 0x7) ||
+                       parser_read_uint32(&svlan_dei, tokens[4]) ||
+                       (svlan_dei > 0x1) ||
+                       parser_read_uint32(&svlan_vid, tokens[5]) ||
+                       (svlan_vid > 0xFFF) ||
+                       parser_read_uint32(&cvlan_pcp, tokens[6]) ||
+                       (cvlan_pcp > 0x7) ||
+                       parser_read_uint32(&cvlan_dei, tokens[7]) ||
+                       (cvlan_dei > 0x1) ||
+                       parser_read_uint32(&cvlan_vid, tokens[8]) ||
+                       (cvlan_vid > 0xFFF) ||
+                       parser_read_uint16(&a->encap.qinq_pppoe.pppoe.session_id,
+                               tokens[9]))
+                       return 0;
+
+               a->encap.qinq_pppoe.svlan.pcp = svlan_pcp & 0x7;
+               a->encap.qinq_pppoe.svlan.dei = svlan_dei & 0x1;
+               a->encap.qinq_pppoe.svlan.vid = svlan_vid & 0xFFF;
+               a->encap.qinq_pppoe.cvlan.pcp = cvlan_pcp & 0x7;
+               a->encap.qinq_pppoe.cvlan.dei = cvlan_dei & 0x1;
+               a->encap.qinq_pppoe.cvlan.vid = cvlan_vid & 0xFFF;
+               a->encap.type = RTE_TABLE_ACTION_ENCAP_QINQ_PPPOE;
+               a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
+               return 1 + 10;
+
+       }
+
        /* mpls */
        if (n_tokens && (strcmp(tokens[0], "mpls") == 0)) {
                uint32_t label, tc, ttl;
@@ -3735,24 +3775,18 @@ parse_free_sym_crypto_param_data(struct rte_table_action_sym_crypto_params *p)
 
                switch (xform[i]->type) {
                case RTE_CRYPTO_SYM_XFORM_CIPHER:
-                       if (xform[i]->cipher.key.data)
-                               free(xform[i]->cipher.key.data);
                        if (p->cipher_auth.cipher_iv.val)
                                free(p->cipher_auth.cipher_iv.val);
                        if (p->cipher_auth.cipher_iv_update.val)
                                free(p->cipher_auth.cipher_iv_update.val);
                        break;
                case RTE_CRYPTO_SYM_XFORM_AUTH:
-                       if (xform[i]->auth.key.data)
-                               free(xform[i]->cipher.key.data);
                        if (p->cipher_auth.auth_iv.val)
                                free(p->cipher_auth.cipher_iv.val);
                        if (p->cipher_auth.auth_iv_update.val)
                                free(p->cipher_auth.cipher_iv_update.val);
                        break;
                case RTE_CRYPTO_SYM_XFORM_AEAD:
-                       if (xform[i]->aead.key.data)
-                               free(xform[i]->cipher.key.data);
                        if (p->aead.iv.val)
                                free(p->aead.iv.val);
                        if (p->aead.aad.val)
@@ -3767,8 +3801,8 @@ parse_free_sym_crypto_param_data(struct rte_table_action_sym_crypto_params *p)
 
 static struct rte_crypto_sym_xform *
 parse_table_action_cipher(struct rte_table_action_sym_crypto_params *p,
-               char **tokens, uint32_t n_tokens, uint32_t encrypt,
-               uint32_t *used_n_tokens)
+               uint8_t *key, uint32_t max_key_len, char **tokens,
+               uint32_t n_tokens, uint32_t encrypt, uint32_t *used_n_tokens)
 {
        struct rte_crypto_sym_xform *xform_cipher;
        int status;
@@ -3795,16 +3829,16 @@ parse_table_action_cipher(struct rte_table_action_sym_crypto_params *p,
 
        /* cipher_key */
        len = strlen(tokens[4]);
-       xform_cipher->cipher.key.data = calloc(1, len / 2 + 1);
-       if (xform_cipher->cipher.key.data == NULL)
+       if (len / 2 > max_key_len) {
+               status = -ENOMEM;
                goto error_exit;
+       }
 
-       status = parse_hex_string(tokens[4],
-                       xform_cipher->cipher.key.data,
-                       (uint32_t *)&len);
+       status = parse_hex_string(tokens[4], key, (uint32_t *)&len);
        if (status < 0)
                goto error_exit;
 
+       xform_cipher->cipher.key.data = key;
        xform_cipher->cipher.key.length = (uint16_t)len;
 
        /* cipher_iv */
@@ -3828,9 +3862,6 @@ parse_table_action_cipher(struct rte_table_action_sym_crypto_params *p,
        return xform_cipher;
 
 error_exit:
-       if (xform_cipher->cipher.key.data)
-               free(xform_cipher->cipher.key.data);
-
        if (p->cipher_auth.cipher_iv.val) {
                free(p->cipher_auth.cipher_iv.val);
                p->cipher_auth.cipher_iv.val = NULL;
@@ -3843,8 +3874,8 @@ error_exit:
 
 static struct rte_crypto_sym_xform *
 parse_table_action_cipher_auth(struct rte_table_action_sym_crypto_params *p,
-               char **tokens, uint32_t n_tokens, uint32_t encrypt,
-               uint32_t *used_n_tokens)
+               uint8_t *key, uint32_t max_key_len, char **tokens,
+               uint32_t n_tokens, uint32_t encrypt, uint32_t *used_n_tokens)
 {
        struct rte_crypto_sym_xform *xform_cipher;
        struct rte_crypto_sym_xform *xform_auth;
@@ -3873,17 +3904,21 @@ parse_table_action_cipher_auth(struct rte_table_action_sym_crypto_params *p,
 
        /* auth_key */
        len = strlen(tokens[10]);
-       xform_auth->auth.key.data = calloc(1, len / 2 + 1);
-       if (xform_auth->auth.key.data == NULL)
+       if (len / 2 > max_key_len) {
+               status = -ENOMEM;
                goto error_exit;
+       }
 
-       status = parse_hex_string(tokens[10],
-                       xform_auth->auth.key.data, (uint32_t *)&len);
+       status = parse_hex_string(tokens[10], key, (uint32_t *)&len);
        if (status < 0)
                goto error_exit;
 
+       xform_auth->auth.key.data = key;
        xform_auth->auth.key.length = (uint16_t)len;
 
+       key += xform_auth->auth.key.length;
+       max_key_len -= xform_auth->auth.key.length;
+
        if (strcmp(tokens[11], "digest_size"))
                goto error_exit;
 
@@ -3892,8 +3927,8 @@ parse_table_action_cipher_auth(struct rte_table_action_sym_crypto_params *p,
        if (status < 0)
                goto error_exit;
 
-       xform_cipher = parse_table_action_cipher(p, tokens, 7, encrypt,
-                       used_n_tokens);
+       xform_cipher = parse_table_action_cipher(p, key, max_key_len, tokens,
+                       7, encrypt, used_n_tokens);
        if (xform_cipher == NULL)
                goto error_exit;
 
@@ -3908,8 +3943,6 @@ parse_table_action_cipher_auth(struct rte_table_action_sym_crypto_params *p,
        }
 
 error_exit:
-       if (xform_auth->auth.key.data)
-               free(xform_auth->auth.key.data);
        if (p->cipher_auth.auth_iv.val) {
                free(p->cipher_auth.auth_iv.val);
                p->cipher_auth.auth_iv.val = 0;
@@ -3922,8 +3955,8 @@ error_exit:
 
 static struct rte_crypto_sym_xform *
 parse_table_action_aead(struct rte_table_action_sym_crypto_params *p,
-               char **tokens, uint32_t n_tokens, uint32_t encrypt,
-               uint32_t *used_n_tokens)
+               uint8_t *key, uint32_t max_key_len, char **tokens,
+               uint32_t n_tokens, uint32_t encrypt, uint32_t *used_n_tokens)
 {
        struct rte_crypto_sym_xform *xform_aead;
        int status;
@@ -3952,15 +3985,16 @@ parse_table_action_aead(struct rte_table_action_sym_crypto_params *p,
 
        /* aead_key */
        len = strlen(tokens[4]);
-       xform_aead->aead.key.data = calloc(1, len / 2 + 1);
-       if (xform_aead->aead.key.data == NULL)
+       if (len / 2 > max_key_len) {
+               status = -ENOMEM;
                goto error_exit;
+       }
 
-       status = parse_hex_string(tokens[4], xform_aead->aead.key.data,
-                       (uint32_t *)&len);
+       status = parse_hex_string(tokens[4], key, (uint32_t *)&len);
        if (status < 0)
                goto error_exit;
 
+       xform_aead->aead.key.data = key;
        xform_aead->aead.key.length = (uint16_t)len;
 
        /* aead_iv */
@@ -4002,8 +4036,6 @@ parse_table_action_aead(struct rte_table_action_sym_crypto_params *p,
        return xform_aead;
 
 error_exit:
-       if (xform_aead->aead.key.data)
-               free(xform_aead->aead.key.data);
        if (p->aead.iv.val) {
                free(p->aead.iv.val);
                p->aead.iv.val = NULL;
@@ -4026,6 +4058,8 @@ parse_table_action_sym_crypto(char **tokens,
 {
        struct rte_table_action_sym_crypto_params *p = &a->sym_crypto;
        struct rte_crypto_sym_xform *xform = NULL;
+       uint8_t *key = a->sym_crypto_key;
+       uint32_t max_key_len = SYM_CRYPTO_MAX_KEY_SIZE;
        uint32_t used_n_tokens;
        uint32_t encrypt;
        int status;
@@ -4050,20 +4084,20 @@ parse_table_action_sym_crypto(char **tokens,
                tokens += 3;
                n_tokens -= 3;
 
-               xform = parse_table_action_cipher(p, tokens, n_tokens, encrypt,
-                               &used_n_tokens);
+               xform = parse_table_action_cipher(p, key, max_key_len, tokens,
+                               n_tokens, encrypt, &used_n_tokens);
        } else if (strcmp(tokens[3], "cipher_auth") == 0) {
                tokens += 3;
                n_tokens -= 3;
 
-               xform = parse_table_action_cipher_auth(p, tokens, n_tokens,
-                               encrypt, &used_n_tokens);
+               xform = parse_table_action_cipher_auth(p, key, max_key_len,
+                               tokens, n_tokens, encrypt, &used_n_tokens);
        } else if (strcmp(tokens[3], "aead") == 0) {
                tokens += 3;
                n_tokens -= 3;
 
-               xform = parse_table_action_aead(p, tokens, n_tokens, encrypt,
-                               &used_n_tokens);
+               xform = parse_table_action_aead(p, key, max_key_len, tokens,
+                               n_tokens, encrypt, &used_n_tokens);
        }
 
        if (xform == NULL)
@@ -4407,7 +4441,6 @@ cmd_pipeline_table_rule_add_default(char **tokens,
        size_t out_size)
 {
        struct table_rule_action action;
-       void *data;
        char *pipeline_name;
        uint32_t table_id;
        int status;
@@ -4513,8 +4546,7 @@ cmd_pipeline_table_rule_add_default(char **tokens,
 
        status = pipeline_table_rule_add_default(pipeline_name,
                table_id,
-               &action,
-               &data);
+               &action);
        if (status) {
                snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
                return;
@@ -4741,19 +4773,530 @@ cmd_pipeline_table_rule_delete_default(char **tokens,
        }
 }
 
+static void
+ether_addr_show(FILE *f, struct rte_ether_addr *addr)
+{
+       fprintf(f, RTE_ETHER_ADDR_PRT_FMT,
+               (uint32_t)addr->addr_bytes[0], (uint32_t)addr->addr_bytes[1],
+               (uint32_t)addr->addr_bytes[2], (uint32_t)addr->addr_bytes[3],
+               (uint32_t)addr->addr_bytes[4], (uint32_t)addr->addr_bytes[5]);
+}
+
+static void
+ipv4_addr_show(FILE *f, uint32_t addr)
+{
+       fprintf(f, "%u.%u.%u.%u",
+               addr >> 24,
+               (addr >> 16) & 0xFF,
+               (addr >> 8) & 0xFF,
+               addr & 0xFF);
+}
+
+static void
+ipv6_addr_show(FILE *f, uint8_t *addr)
+{
+       fprintf(f, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
+               "%02x%02x:%02x%02x:%02x%02x:%02x%02x:",
+               (uint32_t)addr[0], (uint32_t)addr[1],
+               (uint32_t)addr[2], (uint32_t)addr[3],
+               (uint32_t)addr[4], (uint32_t)addr[5],
+               (uint32_t)addr[6], (uint32_t)addr[7],
+               (uint32_t)addr[8], (uint32_t)addr[9],
+               (uint32_t)addr[10], (uint32_t)addr[11],
+               (uint32_t)addr[12], (uint32_t)addr[13],
+               (uint32_t)addr[14], (uint32_t)addr[15]);
+}
+
+static const char *
+policer_action_string(enum rte_table_action_policer action) {
+       switch (action) {
+               case RTE_TABLE_ACTION_POLICER_COLOR_GREEN: return "G";
+               case RTE_TABLE_ACTION_POLICER_COLOR_YELLOW: return "Y";
+               case RTE_TABLE_ACTION_POLICER_COLOR_RED: return "R";
+               case RTE_TABLE_ACTION_POLICER_DROP: return "D";
+               default: return "?";
+       }
+}
+
+static int
+table_rule_show(const char *pipeline_name,
+       uint32_t table_id,
+       const char *file_name)
+{
+       struct pipeline *p;
+       struct table *table;
+       struct table_rule *rule;
+       FILE *f = NULL;
+       uint32_t i;
+
+       /* Check input params. */
+       if ((pipeline_name == NULL) ||
+               (file_name == NULL))
+               return -1;
+
+       p = pipeline_find(pipeline_name);
+       if ((p == NULL) ||
+               (table_id >= p->n_tables))
+               return -1;
+
+       table = &p->table[table_id];
+
+       /* Open file. */
+       f = fopen(file_name, "w");
+       if (f == NULL)
+               return -1;
+
+       /* Write table rules to file. */
+       TAILQ_FOREACH(rule, &table->rules, node) {
+               struct table_rule_match *m = &rule->match;
+               struct table_rule_action *a = &rule->action;
+
+               fprintf(f, "match ");
+               switch (m->match_type) {
+               case TABLE_ACL:
+                       fprintf(f, "acl priority %u ",
+                               m->match.acl.priority);
+
+                       fprintf(f, m->match.acl.ip_version ? "ipv4 " : "ipv6 ");
+
+                       if (m->match.acl.ip_version)
+                               ipv4_addr_show(f, m->match.acl.ipv4.sa);
+                       else
+                               ipv6_addr_show(f, m->match.acl.ipv6.sa);
+
+                       fprintf(f, "%u",        m->match.acl.sa_depth);
+
+                       if (m->match.acl.ip_version)
+                               ipv4_addr_show(f, m->match.acl.ipv4.da);
+                       else
+                               ipv6_addr_show(f, m->match.acl.ipv6.da);
+
+                       fprintf(f, "%u",        m->match.acl.da_depth);
+
+                       fprintf(f, "%u %u %u %u %u ",
+                               (uint32_t)m->match.acl.sp0,
+                               (uint32_t)m->match.acl.sp1,
+                               (uint32_t)m->match.acl.dp0,
+                               (uint32_t)m->match.acl.dp1,
+                               (uint32_t)m->match.acl.proto);
+                       break;
+
+               case TABLE_ARRAY:
+                       fprintf(f, "array %u ",
+                               m->match.array.pos);
+                       break;
+
+               case TABLE_HASH:
+                       fprintf(f, "hash raw ");
+                       for (i = 0; i < table->params.match.hash.key_size; i++)
+                               fprintf(f, "%02x", m->match.hash.key[i]);
+                       fprintf(f, " ");
+                       break;
+
+               case TABLE_LPM:
+                       fprintf(f, "lpm ");
+
+                       fprintf(f, m->match.lpm.ip_version ? "ipv4 " : "ipv6 ");
+
+                       if (m->match.acl.ip_version)
+                               ipv4_addr_show(f, m->match.lpm.ipv4);
+                       else
+                               ipv6_addr_show(f, m->match.lpm.ipv6);
+
+                       fprintf(f, "%u ",
+                               (uint32_t)m->match.lpm.depth);
+                       break;
+
+               default:
+                       fprintf(f, "unknown ");
+               }
+
+               fprintf(f, "action ");
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_FWD)) {
+                       fprintf(f, "fwd ");
+                       switch (a->fwd.action) {
+                       case RTE_PIPELINE_ACTION_DROP:
+                               fprintf(f, "drop ");
+                               break;
+
+                       case RTE_PIPELINE_ACTION_PORT:
+                               fprintf(f, "port %u ", a->fwd.id);
+                               break;
+
+                       case RTE_PIPELINE_ACTION_PORT_META:
+                               fprintf(f, "meta ");
+                               break;
+
+                       case RTE_PIPELINE_ACTION_TABLE:
+                       default:
+                               fprintf(f, "table %u ", a->fwd.id);
+                       }
+               }
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_LB)) {
+                       fprintf(f, "balance ");
+                       for (i = 0; i < RTE_DIM(a->lb.out); i++)
+                               fprintf(f, "%u ", a->lb.out[i]);
+               }
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) {
+                       fprintf(f, "mtr ");
+                       for (i = 0; i < RTE_TABLE_ACTION_TC_MAX; i++)
+                               if (a->mtr.tc_mask & (1 << i)) {
+                                       struct rte_table_action_mtr_tc_params *p =
+                                               &a->mtr.mtr[i];
+                                       enum rte_table_action_policer ga =
+                                               p->policer[RTE_COLOR_GREEN];
+                                       enum rte_table_action_policer ya =
+                                               p->policer[RTE_COLOR_YELLOW];
+                                       enum rte_table_action_policer ra =
+                                               p->policer[RTE_COLOR_RED];
+
+                                       fprintf(f, "tc%u meter %u policer g %s y %s r %s ",
+                                               i,
+                                               a->mtr.mtr[i].meter_profile_id,
+                                               policer_action_string(ga),
+                                               policer_action_string(ya),
+                                               policer_action_string(ra));
+                               }
+               }
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_TM))
+                       fprintf(f, "tm subport %u pipe %u ",
+                               a->tm.subport_id,
+                               a->tm.pipe_id);
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_ENCAP)) {
+                       fprintf(f, "encap ");
+                       switch (a->encap.type) {
+                       case RTE_TABLE_ACTION_ENCAP_ETHER:
+                               fprintf(f, "ether ");
+                               ether_addr_show(f, &a->encap.ether.ether.da);
+                               fprintf(f, " ");
+                               ether_addr_show(f, &a->encap.ether.ether.sa);
+                               fprintf(f, " ");
+                               break;
+
+                       case RTE_TABLE_ACTION_ENCAP_VLAN:
+                               fprintf(f, "vlan ");
+                               ether_addr_show(f, &a->encap.vlan.ether.da);
+                               fprintf(f, " ");
+                               ether_addr_show(f, &a->encap.vlan.ether.sa);
+                               fprintf(f, " pcp %u dei %u vid %u ",
+                                       a->encap.vlan.vlan.pcp,
+                                       a->encap.vlan.vlan.dei,
+                                       a->encap.vlan.vlan.vid);
+                               break;
+
+                       case RTE_TABLE_ACTION_ENCAP_QINQ:
+                               fprintf(f, "qinq ");
+                               ether_addr_show(f, &a->encap.qinq.ether.da);
+                               fprintf(f, " ");
+                               ether_addr_show(f, &a->encap.qinq.ether.sa);
+                               fprintf(f, " pcp %u dei %u vid %u pcp %u dei %u vid %u ",
+                                       a->encap.qinq.svlan.pcp,
+                                       a->encap.qinq.svlan.dei,
+                                       a->encap.qinq.svlan.vid,
+                                       a->encap.qinq.cvlan.pcp,
+                                       a->encap.qinq.cvlan.dei,
+                                       a->encap.qinq.cvlan.vid);
+                               break;
+
+                       case RTE_TABLE_ACTION_ENCAP_MPLS:
+                               fprintf(f, "mpls %s ", (a->encap.mpls.unicast) ?
+                                       "unicast " : "multicast ");
+                               ether_addr_show(f, &a->encap.mpls.ether.da);
+                               fprintf(f, " ");
+                               ether_addr_show(f, &a->encap.mpls.ether.sa);
+                               fprintf(f, " ");
+                               for (i = 0; i < a->encap.mpls.mpls_count; i++) {
+                                       struct rte_table_action_mpls_hdr *l =
+                                               &a->encap.mpls.mpls[i];
+
+                                       fprintf(f, "label%u %u %u %u ",
+                                               i,
+                                               l->label,
+                                               l->tc,
+                                               l->ttl);
+                               }
+                               break;
+
+                       case RTE_TABLE_ACTION_ENCAP_PPPOE:
+                               fprintf(f, "pppoe ");
+                               ether_addr_show(f, &a->encap.pppoe.ether.da);
+                               fprintf(f, " ");
+                               ether_addr_show(f, &a->encap.pppoe.ether.sa);
+                               fprintf(f, " %u ", a->encap.pppoe.pppoe.session_id);
+                               break;
+
+                       case RTE_TABLE_ACTION_ENCAP_VXLAN:
+                               fprintf(f, "vxlan ether ");
+                               ether_addr_show(f, &a->encap.vxlan.ether.da);
+                               fprintf(f, " ");
+                               ether_addr_show(f, &a->encap.vxlan.ether.sa);
+                               if (table->ap->params.encap.vxlan.vlan)
+                                       fprintf(f, " vlan pcp %u dei %u vid %u ",
+                                               a->encap.vxlan.vlan.pcp,
+                                               a->encap.vxlan.vlan.dei,
+                                               a->encap.vxlan.vlan.vid);
+                               if (table->ap->params.encap.vxlan.ip_version) {
+                                       fprintf(f, " ipv4 ");
+                                       ipv4_addr_show(f, a->encap.vxlan.ipv4.sa);
+                                       fprintf(f, " ");
+                                       ipv4_addr_show(f, a->encap.vxlan.ipv4.da);
+                                       fprintf(f, " %u %u ",
+                                               (uint32_t)a->encap.vxlan.ipv4.dscp,
+                                               (uint32_t)a->encap.vxlan.ipv4.ttl);
+                               } else {
+                                       fprintf(f, " ipv6 ");
+                                       ipv6_addr_show(f, a->encap.vxlan.ipv6.sa);
+                                       fprintf(f, " ");
+                                       ipv6_addr_show(f, a->encap.vxlan.ipv6.da);
+                                       fprintf(f, " %u %u %u ",
+                                               a->encap.vxlan.ipv6.flow_label,
+                                               (uint32_t)a->encap.vxlan.ipv6.dscp,
+                                               (uint32_t)a->encap.vxlan.ipv6.hop_limit);
+                                       fprintf(f, " udp %u %u vxlan %u ",
+                                               a->encap.vxlan.udp.sp,
+                                               a->encap.vxlan.udp.dp,
+                                               a->encap.vxlan.vxlan.vni);
+                               }
+                               break;
+
+                       default:
+                               fprintf(f, "unknown ");
+                       }
+               }
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_NAT)) {
+                       fprintf(f, "nat %s ", (a->nat.ip_version) ? "ipv4 " : "ipv6 ");
+                       if (a->nat.ip_version)
+                               ipv4_addr_show(f, a->nat.addr.ipv4);
+                       else
+                               ipv6_addr_show(f, a->nat.addr.ipv6);
+                       fprintf(f, " %u ", (uint32_t)(a->nat.port));
+               }
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_TTL))
+                       fprintf(f, "ttl %s ", (a->ttl.decrement) ? "dec" : "keep");
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_STATS))
+                       fprintf(f, "stats ");
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_TIME))
+                       fprintf(f, "time ");
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_SYM_CRYPTO))
+                       fprintf(f, "sym_crypto ");
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_TAG))
+                       fprintf(f, "tag %u ", a->tag.tag);
+
+               if (a->action_mask & (1LLU << RTE_TABLE_ACTION_DECAP))
+                       fprintf(f, "decap %u ", a->decap.n);
+
+               /* end */
+               fprintf(f, "\n");
+       }
+
+       /* Write table default rule to file. */
+       if (table->rule_default) {
+               struct table_rule_action *a = &table->rule_default->action;
+
+               fprintf(f, "# match default action fwd ");
+
+               switch (a->fwd.action) {
+               case RTE_PIPELINE_ACTION_DROP:
+                       fprintf(f, "drop ");
+                       break;
+
+               case RTE_PIPELINE_ACTION_PORT:
+                       fprintf(f, "port %u ", a->fwd.id);
+                       break;
+
+               case RTE_PIPELINE_ACTION_PORT_META:
+                       fprintf(f, "meta ");
+                       break;
+
+               case RTE_PIPELINE_ACTION_TABLE:
+               default:
+                       fprintf(f, "table %u ", a->fwd.id);
+               }
+       } else
+               fprintf(f, "# match default action fwd drop ");
+
+       fprintf(f, "\n");
+
+       /* Close file. */
+       fclose(f);
+
+       return 0;
+}
+
+static const char cmd_pipeline_table_rule_show_help[] =
+"pipeline <pipeline_name> table <table_id> rule show\n"
+"     file <file_name>\n";
+
+static void
+cmd_pipeline_table_rule_show(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       char *file_name = NULL, *pipeline_name;
+       uint32_t table_id;
+       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 (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], "show") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "show");
+               return;
+       }
+
+       if (strcmp(tokens[6], "file") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "file");
+               return;
+       }
+
+       file_name = tokens[7];
+
+       status = table_rule_show(pipeline_name, table_id, file_name);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+}
 
 static const char cmd_pipeline_table_rule_stats_read_help[] =
-"pipeline <pipeline_name> table <table_id> rule read stats [clear]\n";
+"pipeline <pipeline_name> table <table_id> rule read stats [clear]\n"
+"     match <match>\n";
 
 static void
 cmd_pipeline_table_rule_stats_read(char **tokens,
-       uint32_t n_tokens __rte_unused,
+       uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
-}
+       struct table_rule_match m;
+       struct rte_table_action_stats_counters stats;
+       char *pipeline_name;
+       uint32_t table_id, n_tokens_parsed;
+       int clear = 0, status;
+
+       if (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, "table");
+               return;
+       }
+
+       if (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], "read") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
+               return;
+       }
+
+       if (strcmp(tokens[6], "stats") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
+               return;
+       }
+
+       n_tokens -= 7;
+       tokens += 7;
+
+       /* clear */
+       if (n_tokens && (strcmp(tokens[0], "clear") == 0)) {
+               clear = 1;
+
+               n_tokens--;
+               tokens++;
+       }
+
+       /* match */
+       if ((n_tokens == 0) || strcmp(tokens[0], "match")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
+               return;
+       }
+
+       n_tokens_parsed = parse_match(tokens,
+               n_tokens,
+               out,
+               out_size,
+               &m);
+       if (n_tokens_parsed == 0)
+               return;
+       n_tokens -= n_tokens_parsed;
+       tokens += n_tokens_parsed;
+
+       /* end */
+       if (n_tokens) {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       /* Read table rule stats. */
+       status = pipeline_table_rule_stats_read(pipeline_name,
+               table_id,
+               &m,
+               &stats,
+               clear);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+
+       /* Print stats. */
+       if (stats.n_packets_valid && stats.n_bytes_valid)
+               snprintf(out, out_size, "Packets: %" PRIu64 "; Bytes: %" PRIu64 "\n",
+                       stats.n_packets,
+                       stats.n_bytes);
+
+       if (stats.n_packets_valid && !stats.n_bytes_valid)
+               snprintf(out, out_size, "Packets: %" PRIu64 "; Bytes: N/A\n",
+                       stats.n_packets);
+
+       if (!stats.n_packets_valid && stats.n_bytes_valid)
+               snprintf(out, out_size, "Packets: N/A; Bytes: %" PRIu64 "\n",
+                       stats.n_bytes);
 
+       if (!stats.n_packets_valid && !stats.n_bytes_valid)
+               snprintf(out, out_size, "Packets: N/A ; Bytes: N/A\n");
+}
 
 static const char cmd_pipeline_table_meter_profile_add_help[] =
 "pipeline <pipeline_name> table <table_id> meter profile <meter_profile_id>\n"
@@ -4970,15 +5513,98 @@ cmd_pipeline_table_meter_profile_delete(char **tokens,
 
 
 static const char cmd_pipeline_table_rule_meter_read_help[] =
-"pipeline <pipeline_name> table <table_id> rule read meter [clear]\n";
+"pipeline <pipeline_name> table <table_id> rule read meter [clear]\n"
+"     match <match>\n";
 
 static void
 cmd_pipeline_table_rule_meter_read(char **tokens,
-       uint32_t n_tokens __rte_unused,
+       uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
+       struct table_rule_match m;
+       struct rte_table_action_mtr_counters stats;
+       char *pipeline_name;
+       uint32_t table_id, n_tokens_parsed;
+       int clear = 0, status;
+
+       if (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, "table");
+               return;
+       }
+
+       if (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], "read") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
+               return;
+       }
+
+       if (strcmp(tokens[6], "meter") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
+               return;
+       }
+
+       n_tokens -= 7;
+       tokens += 7;
+
+       /* clear */
+       if (n_tokens && (strcmp(tokens[0], "clear") == 0)) {
+               clear = 1;
+
+               n_tokens--;
+               tokens++;
+       }
+
+       /* match */
+       if ((n_tokens == 0) || strcmp(tokens[0], "match")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
+               return;
+       }
+
+       n_tokens_parsed = parse_match(tokens,
+               n_tokens,
+               out,
+               out_size,
+               &m);
+       if (n_tokens_parsed == 0)
+               return;
+       n_tokens -= n_tokens_parsed;
+       tokens += n_tokens_parsed;
+
+       /* end */
+       if (n_tokens) {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       /* Read table rule meter stats. */
+       status = pipeline_table_rule_mtr_read(pipeline_name,
+               table_id,
+               &m,
+               &stats,
+               clear);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+
+       /* Print stats. */
 }
 
 
@@ -5017,7 +5643,7 @@ load_dscp_table(struct rte_table_action_dscp_table *dscp_table,
        for (dscp = 0, l = 1; ; l++) {
                char line[64];
                char *tokens[3];
-               enum rte_meter_color color;
+               enum rte_color color;
                uint32_t tc_id, tc_queue_id, n_tokens = RTE_DIM(tokens);
 
                if (fgets(line, sizeof(line), f) == NULL)
@@ -5050,17 +5676,17 @@ load_dscp_table(struct rte_table_action_dscp_table *dscp_table,
                switch (tokens[2][0]) {
                case 'g':
                case 'G':
-                       color = e_RTE_METER_GREEN;
+                       color = RTE_COLOR_GREEN;
                        break;
 
                case 'y':
                case 'Y':
-                       color = e_RTE_METER_YELLOW;
+                       color = RTE_COLOR_YELLOW;
                        break;
 
                case 'r':
                case 'R':
-                       color = e_RTE_METER_RED;
+                       color = RTE_COLOR_RED;
                        break;
 
                default:
@@ -5133,17 +5759,188 @@ cmd_pipeline_table_dscp(char **tokens,
 
 
 static const char cmd_pipeline_table_rule_ttl_read_help[] =
-"pipeline <pipeline_name> table <table_id> rule read ttl [clear]\n";
+"pipeline <pipeline_name> table <table_id> rule read ttl [clear]\n"
+"     match <match>\n";
 
 static void
 cmd_pipeline_table_rule_ttl_read(char **tokens,
-       uint32_t n_tokens __rte_unused,
+       uint32_t n_tokens,
        char *out,
        size_t out_size)
 {
-       snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
+       struct table_rule_match m;
+       struct rte_table_action_ttl_counters stats;
+       char *pipeline_name;
+       uint32_t table_id, n_tokens_parsed;
+       int clear = 0, status;
+
+       if (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, "table");
+               return;
+       }
+
+       if (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], "read") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
+               return;
+       }
+
+       if (strcmp(tokens[6], "ttl") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ttl");
+               return;
+       }
+
+       n_tokens -= 7;
+       tokens += 7;
+
+       /* clear */
+       if (n_tokens && (strcmp(tokens[0], "clear") == 0)) {
+               clear = 1;
+
+               n_tokens--;
+               tokens++;
+       }
+
+       /* match */
+       if ((n_tokens == 0) || strcmp(tokens[0], "match")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
+               return;
+       }
+
+       n_tokens_parsed = parse_match(tokens,
+               n_tokens,
+               out,
+               out_size,
+               &m);
+       if (n_tokens_parsed == 0)
+               return;
+       n_tokens -= n_tokens_parsed;
+       tokens += n_tokens_parsed;
+
+       /* end */
+       if (n_tokens) {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       /* Read table rule TTL stats. */
+       status = pipeline_table_rule_ttl_read(pipeline_name,
+               table_id,
+               &m,
+               &stats,
+               clear);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+
+       /* Print stats. */
+       snprintf(out, out_size, "Packets: %" PRIu64 "\n",
+               stats.n_packets);
 }
 
+static const char cmd_pipeline_table_rule_time_read_help[] =
+"pipeline <pipeline_name> table <table_id> rule read time\n"
+"     match <match>\n";
+
+static void
+cmd_pipeline_table_rule_time_read(char **tokens,
+       uint32_t n_tokens,
+       char *out,
+       size_t out_size)
+{
+       struct table_rule_match m;
+       char *pipeline_name;
+       uint64_t timestamp;
+       uint32_t table_id, n_tokens_parsed;
+       int status;
+
+       if (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, "table");
+               return;
+       }
+
+       if (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], "read") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
+               return;
+       }
+
+       if (strcmp(tokens[6], "time") != 0) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "time");
+               return;
+       }
+
+       n_tokens -= 7;
+       tokens += 7;
+
+       /* match */
+       if ((n_tokens == 0) || strcmp(tokens[0], "match")) {
+               snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
+               return;
+       }
+
+       n_tokens_parsed = parse_match(tokens,
+               n_tokens,
+               out,
+               out_size,
+               &m);
+       if (n_tokens_parsed == 0)
+               return;
+       n_tokens -= n_tokens_parsed;
+       tokens += n_tokens_parsed;
+
+       /* end */
+       if (n_tokens) {
+               snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
+               return;
+       }
+
+       /* Read table rule timestamp. */
+       status = pipeline_table_rule_time_read(pipeline_name,
+               table_id,
+               &m,
+               &timestamp);
+       if (status) {
+               snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
+               return;
+       }
+
+       /* Print stats. */
+       snprintf(out, out_size, "Packets: %" PRIu64 "\n", timestamp);
+}
 
 static const char cmd_thread_pipeline_enable_help[] =
 "thread <thread_id> pipeline <pipeline_name> enable\n";
@@ -5268,12 +6065,14 @@ cmd_help(char **tokens, uint32_t n_tokens, char *out, size_t out_size)
                        "\tpipeline table rule add bulk\n"
                        "\tpipeline table rule delete\n"
                        "\tpipeline table rule delete default\n"
+                       "\tpipeline table rule show\n"
                        "\tpipeline table rule stats read\n"
                        "\tpipeline table meter profile add\n"
                        "\tpipeline table meter profile delete\n"
                        "\tpipeline table rule meter read\n"
                        "\tpipeline table dscp\n"
                        "\tpipeline table rule ttl read\n"
+                       "\tpipeline table rule time read\n"
                        "\tthread pipeline enable\n"
                        "\tthread pipeline disable\n\n");
                return;
@@ -5481,6 +6280,14 @@ cmd_help(char **tokens, uint32_t n_tokens, char *out, size_t out_size)
                        return;
                }
 
+               if ((n_tokens == 4) &&
+                       (strcmp(tokens[2], "rule") == 0) &&
+                       (strcmp(tokens[3], "show") == 0)) {
+                       snprintf(out, out_size, "\n%s\n",
+                               cmd_pipeline_table_rule_show_help);
+                       return;
+               }
+
                if ((n_tokens == 5) &&
                        (strcmp(tokens[2], "rule") == 0) &&
                        (strcmp(tokens[3], "stats") == 0) &&
@@ -5525,6 +6332,15 @@ cmd_help(char **tokens, uint32_t n_tokens, char *out, size_t out_size)
                                cmd_pipeline_table_rule_ttl_read_help);
                        return;
                }
+
+               if ((n_tokens == 5) &&
+                       (strcmp(tokens[2], "rule") == 0) &&
+                       (strcmp(tokens[3], "time") == 0) &&
+                       (strcmp(tokens[4], "read") == 0)) {
+                       snprintf(out, out_size, "\n%s\n",
+                               cmd_pipeline_table_rule_time_read_help);
+                       return;
+               }
        }
 
        if ((n_tokens == 3) &&
@@ -5776,6 +6592,15 @@ cli_process(char *in, char *out, size_t out_size)
                        return;
                }
 
+               if ((n_tokens >= 6) &&
+                       (strcmp(tokens[2], "table") == 0) &&
+                       (strcmp(tokens[4], "rule") == 0) &&
+                       (strcmp(tokens[5], "show") == 0)) {
+                       cmd_pipeline_table_rule_show(tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
+
                if ((n_tokens >= 7) &&
                        (strcmp(tokens[2], "table") == 0) &&
                        (strcmp(tokens[4], "rule") == 0) &&
@@ -5833,6 +6658,16 @@ cli_process(char *in, char *out, size_t out_size)
                                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], "time") == 0)) {
+                       cmd_pipeline_table_rule_time_read(tokens, n_tokens,
+                               out, out_size);
+                       return;
+               }
        }
 
        if (strcmp(tokens[0], "thread") == 0) {
@@ -6040,20 +6875,26 @@ cli_rule_file_process(const char *file_name,
        return 0;
 
 cli_rule_file_process_free:
-       *rule_list = NULL;
-       *n_rules = rule_id;
-       *line_number = line_id;
+       if (rule_list != NULL)
+               *rule_list = NULL;
 
-       for ( ; ; ) {
-               struct table_rule *rule;
+       if (n_rules != NULL)
+               *n_rules = rule_id;
 
-               rule = TAILQ_FIRST(list);
-               if (rule == NULL)
-                       break;
+       if (line_number != NULL)
+               *line_number = line_id;
 
-               TAILQ_REMOVE(list, rule, node);
-               free(rule);
-       }
+       if (list != NULL)
+               for ( ; ; ) {
+                       struct table_rule *rule;
+
+                       rule = TAILQ_FIRST(list);
+                       if (rule == NULL)
+                               break;
+
+                       TAILQ_REMOVE(list, rule, node);
+                       free(rule);
+               }
 
        if (f)
                fclose(f);