4 * Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 #include <sys/queue.h>
37 #include <netinet/in.h>
40 #include <rte_common.h>
41 #include <rte_hexdump.h>
42 #include <rte_malloc.h>
43 #include <cmdline_rdline.h>
44 #include <cmdline_parse.h>
45 #include <cmdline_parse_num.h>
46 #include <cmdline_parse_string.h>
49 #include "pipeline_common_fe.h"
50 #include "pipeline_flow_classification.h"
51 #include "hash_func.h"
59 uint16_t ethertype_svlan;
61 uint16_t ethertype_cvlan;
63 } __attribute__((__packed__));
65 struct pkt_key_ipv4_5tuple {
73 } __attribute__((__packed__));
75 struct pkt_key_ipv6_5tuple {
76 uint16_t payload_length;
83 } __attribute__((__packed__));
86 app_pipeline_fc_key_convert(struct pipeline_fc_key *key_in,
90 uint8_t buffer[PIPELINE_FC_FLOW_KEY_MAX_SIZE];
91 uint8_t m[PIPELINE_FC_FLOW_KEY_MAX_SIZE]; /* key mask */
92 void *key_buffer = (key_out) ? key_out : buffer;
94 memset(m, 0xFF, sizeof(m));
95 switch (key_in->type) {
98 struct pkt_key_qinq *qinq = key_buffer;
100 qinq->ethertype_svlan = 0;
101 qinq->svlan = rte_cpu_to_be_16(key_in->key.qinq.svlan);
102 qinq->ethertype_cvlan = 0;
103 qinq->cvlan = rte_cpu_to_be_16(key_in->key.qinq.cvlan);
106 *signature = (uint32_t) hash_default_key8(qinq, m, 8, 0);
110 case FLOW_KEY_IPV4_5TUPLE:
112 struct pkt_key_ipv4_5tuple *ipv4 = key_buffer;
115 ipv4->proto = key_in->key.ipv4_5tuple.proto;
117 ipv4->ip_src = rte_cpu_to_be_32(key_in->key.ipv4_5tuple.ip_src);
118 ipv4->ip_dst = rte_cpu_to_be_32(key_in->key.ipv4_5tuple.ip_dst);
119 ipv4->port_src = rte_cpu_to_be_16(key_in->key.ipv4_5tuple.port_src);
120 ipv4->port_dst = rte_cpu_to_be_16(key_in->key.ipv4_5tuple.port_dst);
123 *signature = (uint32_t) hash_default_key16(ipv4, m, 16, 0);
127 case FLOW_KEY_IPV6_5TUPLE:
129 struct pkt_key_ipv6_5tuple *ipv6 = key_buffer;
132 ipv6->payload_length = 0;
133 ipv6->proto = key_in->key.ipv6_5tuple.proto;
135 memcpy(&ipv6->ip_src, &key_in->key.ipv6_5tuple.ip_src, 16);
136 memcpy(&ipv6->ip_dst, &key_in->key.ipv6_5tuple.ip_dst, 16);
137 ipv6->port_src = rte_cpu_to_be_16(key_in->key.ipv6_5tuple.port_src);
138 ipv6->port_dst = rte_cpu_to_be_16(key_in->key.ipv6_5tuple.port_dst);
141 *signature = (uint32_t) hash_default_key64(ipv6, m, 64, 0);
151 * Flow classification pipeline
154 struct app_pipeline_fc_flow {
155 struct pipeline_fc_key key;
161 TAILQ_ENTRY(app_pipeline_fc_flow) node;
164 #define N_BUCKETS 65536
166 struct app_pipeline_fc {
169 uint32_t n_ports_out;
172 TAILQ_HEAD(, app_pipeline_fc_flow) flows[N_BUCKETS];
176 uint32_t default_flow_present;
177 uint32_t default_flow_port_id;
178 void *default_flow_entry_ptr;
181 static struct app_pipeline_fc_flow *
182 app_pipeline_fc_flow_find(struct app_pipeline_fc *p,
183 struct pipeline_fc_key *key)
185 struct app_pipeline_fc_flow *f;
186 uint32_t signature, bucket_id;
188 app_pipeline_fc_key_convert(key, NULL, &signature);
189 bucket_id = signature & (N_BUCKETS - 1);
191 TAILQ_FOREACH(f, &p->flows[bucket_id], node)
192 if ((signature == f->signature) &&
195 sizeof(struct pipeline_fc_key)) == 0))
202 app_pipeline_fc_init(struct pipeline_params *params,
203 __rte_unused void *arg)
205 struct app_pipeline_fc *p;
208 /* Check input arguments */
209 if ((params == NULL) ||
210 (params->n_ports_in == 0) ||
211 (params->n_ports_out == 0))
214 /* Memory allocation */
215 size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct app_pipeline_fc));
216 p = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
221 p->n_ports_in = params->n_ports_in;
222 p->n_ports_out = params->n_ports_out;
224 for (i = 0; i < N_BUCKETS; i++)
225 TAILQ_INIT(&p->flows[i]);
232 app_pipeline_fc_free(void *pipeline)
234 struct app_pipeline_fc *p = pipeline;
237 /* Check input arguments */
242 for (i = 0; i < N_BUCKETS; i++)
243 while (!TAILQ_EMPTY(&p->flows[i])) {
244 struct app_pipeline_fc_flow *flow;
246 flow = TAILQ_FIRST(&p->flows[i]);
247 TAILQ_REMOVE(&p->flows[i], flow, node);
256 app_pipeline_fc_key_check(struct pipeline_fc_key *key)
261 uint16_t svlan = key->key.qinq.svlan;
262 uint16_t cvlan = key->key.qinq.cvlan;
264 if ((svlan & 0xF000) ||
271 case FLOW_KEY_IPV4_5TUPLE:
274 case FLOW_KEY_IPV6_5TUPLE:
283 app_pipeline_fc_load_file_qinq(char *filename,
284 struct pipeline_fc_key *keys,
294 /* Check input arguments */
295 if ((filename == NULL) ||
297 (port_ids == NULL) ||
298 (flow_ids == NULL) ||
307 /* Open input file */
308 f = fopen(filename, "r");
315 for (i = 0, l = 1; i < *n_keys; l++) {
317 uint32_t n_tokens = RTE_DIM(tokens);
319 uint16_t svlan, cvlan;
320 uint32_t portid, flowid;
323 if (fgets(file_buf, sizeof(file_buf), f) == NULL)
326 status = parse_tokenize_string(file_buf, tokens, &n_tokens);
330 if ((n_tokens == 0) || (tokens[0][0] == '#'))
333 if ((n_tokens != 7) ||
334 strcmp(tokens[0], "qinq") ||
335 parser_read_uint16(&svlan, tokens[1]) ||
336 parser_read_uint16(&cvlan, tokens[2]) ||
337 strcmp(tokens[3], "port") ||
338 parser_read_uint32(&portid, tokens[4]) ||
339 strcmp(tokens[5], "id") ||
340 parser_read_uint32(&flowid, tokens[6]))
343 keys[i].type = FLOW_KEY_QINQ;
344 keys[i].key.qinq.svlan = svlan;
345 keys[i].key.qinq.cvlan = cvlan;
347 port_ids[i] = portid;
348 flow_ids[i] = flowid;
350 if (app_pipeline_fc_key_check(&keys[i]))
368 app_pipeline_fc_load_file_ipv4(char *filename,
369 struct pipeline_fc_key *keys,
379 /* Check input arguments */
380 if ((filename == NULL) ||
382 (port_ids == NULL) ||
383 (flow_ids == NULL) ||
392 /* Open input file */
393 f = fopen(filename, "r");
400 for (i = 0, l = 1; i < *n_keys; l++) {
402 uint32_t n_tokens = RTE_DIM(tokens);
404 struct in_addr sipaddr, dipaddr;
405 uint16_t sport, dport;
407 uint32_t portid, flowid;
410 if (fgets(file_buf, sizeof(file_buf), f) == NULL)
413 status = parse_tokenize_string(file_buf, tokens, &n_tokens);
417 if ((n_tokens == 0) || (tokens[0][0] == '#'))
420 if ((n_tokens != 10) ||
421 strcmp(tokens[0], "ipv4") ||
422 parse_ipv4_addr(tokens[1], &sipaddr) ||
423 parse_ipv4_addr(tokens[2], &dipaddr) ||
424 parser_read_uint16(&sport, tokens[3]) ||
425 parser_read_uint16(&dport, tokens[4]) ||
426 parser_read_uint8(&proto, tokens[5]) ||
427 strcmp(tokens[6], "port") ||
428 parser_read_uint32(&portid, tokens[7]) ||
429 strcmp(tokens[8], "id") ||
430 parser_read_uint32(&flowid, tokens[9]))
433 keys[i].type = FLOW_KEY_IPV4_5TUPLE;
434 keys[i].key.ipv4_5tuple.ip_src = rte_be_to_cpu_32(sipaddr.s_addr);
435 keys[i].key.ipv4_5tuple.ip_dst = rte_be_to_cpu_32(dipaddr.s_addr);
436 keys[i].key.ipv4_5tuple.port_src = sport;
437 keys[i].key.ipv4_5tuple.port_dst = dport;
438 keys[i].key.ipv4_5tuple.proto = proto;
440 port_ids[i] = portid;
441 flow_ids[i] = flowid;
443 if (app_pipeline_fc_key_check(&keys[i]))
461 app_pipeline_fc_load_file_ipv6(char *filename,
462 struct pipeline_fc_key *keys,
472 /* Check input arguments */
473 if ((filename == NULL) ||
475 (port_ids == NULL) ||
476 (flow_ids == NULL) ||
485 /* Open input file */
486 f = fopen(filename, "r");
493 for (i = 0, l = 1; i < *n_keys; l++) {
495 uint32_t n_tokens = RTE_DIM(tokens);
497 struct in6_addr sipaddr, dipaddr;
498 uint16_t sport, dport;
500 uint32_t portid, flowid;
503 if (fgets(file_buf, sizeof(file_buf), f) == NULL)
506 status = parse_tokenize_string(file_buf, tokens, &n_tokens);
510 if ((n_tokens == 0) || (tokens[0][0] == '#'))
513 if ((n_tokens != 10) ||
514 strcmp(tokens[0], "ipv6") ||
515 parse_ipv6_addr(tokens[1], &sipaddr) ||
516 parse_ipv6_addr(tokens[2], &dipaddr) ||
517 parser_read_uint16(&sport, tokens[3]) ||
518 parser_read_uint16(&dport, tokens[4]) ||
519 parser_read_uint8(&proto, tokens[5]) ||
520 strcmp(tokens[6], "port") ||
521 parser_read_uint32(&portid, tokens[7]) ||
522 strcmp(tokens[8], "id") ||
523 parser_read_uint32(&flowid, tokens[9]))
526 keys[i].type = FLOW_KEY_IPV6_5TUPLE;
527 memcpy(keys[i].key.ipv6_5tuple.ip_src,
529 sizeof(sipaddr.s6_addr));
530 memcpy(keys[i].key.ipv6_5tuple.ip_dst,
532 sizeof(dipaddr.s6_addr));
533 keys[i].key.ipv6_5tuple.port_src = sport;
534 keys[i].key.ipv6_5tuple.port_dst = dport;
535 keys[i].key.ipv6_5tuple.proto = proto;
537 port_ids[i] = portid;
538 flow_ids[i] = flowid;
540 if (app_pipeline_fc_key_check(&keys[i]))
560 app_pipeline_fc_add(struct app_params *app,
561 uint32_t pipeline_id,
562 struct pipeline_fc_key *key,
566 struct app_pipeline_fc *p;
567 struct app_pipeline_fc_flow *flow;
569 struct pipeline_fc_add_msg_req *req;
570 struct pipeline_fc_add_msg_rsp *rsp;
575 /* Check input arguments */
580 p = app_pipeline_data_fe(app, pipeline_id, &pipeline_flow_classification);
584 if (port_id >= p->n_ports_out)
587 if (app_pipeline_fc_key_check(key) != 0)
590 /* Find existing flow or allocate new flow */
591 flow = app_pipeline_fc_flow_find(p, key);
592 new_flow = (flow == NULL);
594 flow = rte_malloc(NULL, sizeof(*flow), RTE_CACHE_LINE_SIZE);
600 /* Allocate and write request */
601 req = app_msg_alloc(app);
605 req->type = PIPELINE_MSG_REQ_CUSTOM;
606 req->subtype = PIPELINE_FC_MSG_REQ_FLOW_ADD;
607 app_pipeline_fc_key_convert(key, req->key, &signature);
608 req->port_id = port_id;
609 req->flow_id = flow_id;
611 /* Send request and wait for response */
612 rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
619 /* Read response and write flow */
621 (rsp->entry_ptr == NULL) ||
622 ((new_flow == 0) && (rsp->key_found == 0)) ||
623 ((new_flow == 1) && (rsp->key_found == 1))) {
624 app_msg_free(app, rsp);
630 memset(&flow->key, 0, sizeof(flow->key));
631 memcpy(&flow->key, key, sizeof(flow->key));
632 flow->port_id = port_id;
633 flow->flow_id = flow_id;
634 flow->signature = signature;
635 flow->entry_ptr = rsp->entry_ptr;
639 uint32_t bucket_id = signature & (N_BUCKETS - 1);
641 TAILQ_INSERT_TAIL(&p->flows[bucket_id], flow, node);
646 app_msg_free(app, rsp);
652 app_pipeline_fc_add_bulk(struct app_params *app,
653 uint32_t pipeline_id,
654 struct pipeline_fc_key *key,
659 struct app_pipeline_fc *p;
660 struct pipeline_fc_add_bulk_msg_req *req;
661 struct pipeline_fc_add_bulk_msg_rsp *rsp;
663 struct app_pipeline_fc_flow **flow;
666 struct pipeline_fc_add_bulk_flow_req *flow_req;
667 struct pipeline_fc_add_bulk_flow_rsp *flow_rsp;
672 /* Check input arguments */
680 p = app_pipeline_data_fe(app, pipeline_id, &pipeline_flow_classification);
684 for (i = 0; i < n_keys; i++)
685 if (port_id[i] >= p->n_ports_out)
688 for (i = 0; i < n_keys; i++)
689 if (app_pipeline_fc_key_check(&key[i]) != 0)
692 /* Memory allocation */
693 flow = rte_malloc(NULL,
694 n_keys * sizeof(struct app_pipeline_fc_flow *),
695 RTE_CACHE_LINE_SIZE);
699 signature = rte_malloc(NULL,
700 n_keys * sizeof(uint32_t),
701 RTE_CACHE_LINE_SIZE);
702 if (signature == NULL) {
707 new_flow = rte_malloc(
709 n_keys * sizeof(int),
710 RTE_CACHE_LINE_SIZE);
711 if (new_flow == NULL) {
717 flow_req = rte_malloc(NULL,
718 n_keys * sizeof(struct pipeline_fc_add_bulk_flow_req),
719 RTE_CACHE_LINE_SIZE);
720 if (flow_req == NULL) {
727 flow_rsp = rte_malloc(NULL,
728 n_keys * sizeof(struct pipeline_fc_add_bulk_flow_rsp),
729 RTE_CACHE_LINE_SIZE);
730 if (flow_rsp == NULL) {
738 /* Find existing flow or allocate new flow */
739 for (i = 0; i < n_keys; i++) {
740 flow[i] = app_pipeline_fc_flow_find(p, &key[i]);
741 new_flow[i] = (flow[i] == NULL);
742 if (flow[i] == NULL) {
743 flow[i] = rte_zmalloc(NULL,
744 sizeof(struct app_pipeline_fc_flow),
745 RTE_CACHE_LINE_SIZE);
747 if (flow[i] == NULL) {
750 for (j = 0; j < i; j++)
764 /* Allocate and write request */
765 req = app_msg_alloc(app);
767 for (i = 0; i < n_keys; i++)
779 for (i = 0; i < n_keys; i++) {
780 app_pipeline_fc_key_convert(&key[i],
783 flow_req[i].port_id = port_id[i];
784 flow_req[i].flow_id = flow_id[i];
787 req->type = PIPELINE_MSG_REQ_CUSTOM;
788 req->subtype = PIPELINE_FC_MSG_REQ_FLOW_ADD_BULK;
791 req->n_keys = n_keys;
793 /* Send request and wait for response */
794 rsp = app_msg_send_recv(app, pipeline_id, req, 10000);
796 for (i = 0; i < n_keys; i++)
811 for (i = 0; i < rsp->n_keys; i++)
812 if ((flow_rsp[i].entry_ptr == NULL) ||
813 ((new_flow[i] == 0) && (flow_rsp[i].key_found == 0)) ||
814 ((new_flow[i] == 1) && (flow_rsp[i].key_found == 1)))
817 if (rsp->n_keys < n_keys)
821 for (i = 0; i < rsp->n_keys; i++) {
822 memcpy(&flow[i]->key, &key[i], sizeof(flow[i]->key));
823 flow[i]->port_id = port_id[i];
824 flow[i]->flow_id = flow_id[i];
825 flow[i]->signature = signature[i];
826 flow[i]->entry_ptr = flow_rsp[i].entry_ptr;
829 uint32_t bucket_id = signature[i] & (N_BUCKETS - 1);
831 TAILQ_INSERT_TAIL(&p->flows[bucket_id], flow[i], node);
838 for (i = rsp->n_keys; i < n_keys; i++)
842 app_msg_free(app, rsp);
853 app_pipeline_fc_del(struct app_params *app,
854 uint32_t pipeline_id,
855 struct pipeline_fc_key *key)
857 struct app_pipeline_fc *p;
858 struct app_pipeline_fc_flow *flow;
860 struct pipeline_fc_del_msg_req *req;
861 struct pipeline_fc_del_msg_rsp *rsp;
863 uint32_t signature, bucket_id;
865 /* Check input arguments */
870 p = app_pipeline_data_fe(app, pipeline_id, &pipeline_flow_classification);
874 if (app_pipeline_fc_key_check(key) != 0)
878 flow = app_pipeline_fc_flow_find(p, key);
882 /* Allocate and write request */
883 req = app_msg_alloc(app);
887 req->type = PIPELINE_MSG_REQ_CUSTOM;
888 req->subtype = PIPELINE_FC_MSG_REQ_FLOW_DEL;
889 app_pipeline_fc_key_convert(key, req->key, &signature);
891 /* Send request and wait for response */
892 rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
897 if (rsp->status || !rsp->key_found) {
898 app_msg_free(app, rsp);
903 bucket_id = signature & (N_BUCKETS - 1);
904 TAILQ_REMOVE(&p->flows[bucket_id], flow, node);
909 app_msg_free(app, rsp);
915 app_pipeline_fc_add_default(struct app_params *app,
916 uint32_t pipeline_id,
919 struct app_pipeline_fc *p;
921 struct pipeline_fc_add_default_msg_req *req;
922 struct pipeline_fc_add_default_msg_rsp *rsp;
924 /* Check input arguments */
928 p = app_pipeline_data_fe(app, pipeline_id, &pipeline_flow_classification);
932 if (port_id >= p->n_ports_out)
935 /* Allocate and write request */
936 req = app_msg_alloc(app);
940 req->type = PIPELINE_MSG_REQ_CUSTOM;
941 req->subtype = PIPELINE_FC_MSG_REQ_FLOW_ADD_DEFAULT;
942 req->port_id = port_id;
944 /* Send request and wait for response */
945 rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
949 /* Read response and write flow */
950 if (rsp->status || (rsp->entry_ptr == NULL)) {
951 app_msg_free(app, rsp);
955 p->default_flow_port_id = port_id;
956 p->default_flow_entry_ptr = rsp->entry_ptr;
959 p->default_flow_present = 1;
962 app_msg_free(app, rsp);
968 app_pipeline_fc_del_default(struct app_params *app,
969 uint32_t pipeline_id)
971 struct app_pipeline_fc *p;
973 struct pipeline_fc_del_default_msg_req *req;
974 struct pipeline_fc_del_default_msg_rsp *rsp;
976 /* Check input arguments */
980 p = app_pipeline_data_fe(app, pipeline_id, &pipeline_flow_classification);
984 /* Allocate and write request */
985 req = app_msg_alloc(app);
989 req->type = PIPELINE_MSG_REQ_CUSTOM;
990 req->subtype = PIPELINE_FC_MSG_REQ_FLOW_DEL_DEFAULT;
992 /* Send request and wait for response */
993 rsp = app_msg_send_recv(app, pipeline_id, req, MSG_TIMEOUT_DEFAULT);
999 app_msg_free(app, rsp);
1004 p->default_flow_present = 0;
1007 app_msg_free(app, rsp);
1017 print_fc_qinq_flow(struct app_pipeline_fc_flow *flow)
1019 printf("(SVLAN = %" PRIu32 ", "
1020 "CVLAN = %" PRIu32 ") => "
1021 "Port = %" PRIu32 ", "
1022 "Flow ID = %" PRIu32 ", "
1023 "(signature = 0x%08" PRIx32 ", "
1024 "entry_ptr = %p)\n",
1026 flow->key.key.qinq.svlan,
1027 flow->key.key.qinq.cvlan,
1035 print_fc_ipv4_5tuple_flow(struct app_pipeline_fc_flow *flow)
1037 printf("(SA = %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ", "
1038 "DA = %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ", "
1039 "SP = %" PRIu32 ", "
1040 "DP = %" PRIu32 ", "
1041 "Proto = %" PRIu32 ") => "
1042 "Port = %" PRIu32 ", "
1043 "Flow ID = %" PRIu32 " "
1044 "(signature = 0x%08" PRIx32 ", "
1045 "entry_ptr = %p)\n",
1047 (flow->key.key.ipv4_5tuple.ip_src >> 24) & 0xFF,
1048 (flow->key.key.ipv4_5tuple.ip_src >> 16) & 0xFF,
1049 (flow->key.key.ipv4_5tuple.ip_src >> 8) & 0xFF,
1050 flow->key.key.ipv4_5tuple.ip_src & 0xFF,
1052 (flow->key.key.ipv4_5tuple.ip_dst >> 24) & 0xFF,
1053 (flow->key.key.ipv4_5tuple.ip_dst >> 16) & 0xFF,
1054 (flow->key.key.ipv4_5tuple.ip_dst >> 8) & 0xFF,
1055 flow->key.key.ipv4_5tuple.ip_dst & 0xFF,
1057 flow->key.key.ipv4_5tuple.port_src,
1058 flow->key.key.ipv4_5tuple.port_dst,
1060 flow->key.key.ipv4_5tuple.proto,
1069 print_fc_ipv6_5tuple_flow(struct app_pipeline_fc_flow *flow) {
1070 printf("(SA = %02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
1071 ":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
1072 ":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
1073 ":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32 ", "
1074 "DA = %02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
1075 ":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
1076 ":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32
1077 ":%02" PRIx32 "%02" PRIx32 ":%02" PRIx32 "%02" PRIx32 ", "
1078 "SP = %" PRIu32 ", "
1080 "Proto = %" PRIu32 " "
1081 "=> Port = %" PRIu32 ", "
1082 "Flow ID = %" PRIu32 " "
1083 "(signature = 0x%08" PRIx32 ", "
1084 "entry_ptr = %p)\n",
1086 flow->key.key.ipv6_5tuple.ip_src[0],
1087 flow->key.key.ipv6_5tuple.ip_src[1],
1088 flow->key.key.ipv6_5tuple.ip_src[2],
1089 flow->key.key.ipv6_5tuple.ip_src[3],
1090 flow->key.key.ipv6_5tuple.ip_src[4],
1091 flow->key.key.ipv6_5tuple.ip_src[5],
1092 flow->key.key.ipv6_5tuple.ip_src[6],
1093 flow->key.key.ipv6_5tuple.ip_src[7],
1094 flow->key.key.ipv6_5tuple.ip_src[8],
1095 flow->key.key.ipv6_5tuple.ip_src[9],
1096 flow->key.key.ipv6_5tuple.ip_src[10],
1097 flow->key.key.ipv6_5tuple.ip_src[11],
1098 flow->key.key.ipv6_5tuple.ip_src[12],
1099 flow->key.key.ipv6_5tuple.ip_src[13],
1100 flow->key.key.ipv6_5tuple.ip_src[14],
1101 flow->key.key.ipv6_5tuple.ip_src[15],
1103 flow->key.key.ipv6_5tuple.ip_dst[0],
1104 flow->key.key.ipv6_5tuple.ip_dst[1],
1105 flow->key.key.ipv6_5tuple.ip_dst[2],
1106 flow->key.key.ipv6_5tuple.ip_dst[3],
1107 flow->key.key.ipv6_5tuple.ip_dst[4],
1108 flow->key.key.ipv6_5tuple.ip_dst[5],
1109 flow->key.key.ipv6_5tuple.ip_dst[6],
1110 flow->key.key.ipv6_5tuple.ip_dst[7],
1111 flow->key.key.ipv6_5tuple.ip_dst[8],
1112 flow->key.key.ipv6_5tuple.ip_dst[9],
1113 flow->key.key.ipv6_5tuple.ip_dst[10],
1114 flow->key.key.ipv6_5tuple.ip_dst[11],
1115 flow->key.key.ipv6_5tuple.ip_dst[12],
1116 flow->key.key.ipv6_5tuple.ip_dst[13],
1117 flow->key.key.ipv6_5tuple.ip_dst[14],
1118 flow->key.key.ipv6_5tuple.ip_dst[15],
1120 flow->key.key.ipv6_5tuple.port_src,
1121 flow->key.key.ipv6_5tuple.port_dst,
1123 flow->key.key.ipv6_5tuple.proto,
1132 print_fc_flow(struct app_pipeline_fc_flow *flow)
1134 switch (flow->key.type) {
1136 print_fc_qinq_flow(flow);
1139 case FLOW_KEY_IPV4_5TUPLE:
1140 print_fc_ipv4_5tuple_flow(flow);
1143 case FLOW_KEY_IPV6_5TUPLE:
1144 print_fc_ipv6_5tuple_flow(flow);
1150 app_pipeline_fc_ls(struct app_params *app,
1151 uint32_t pipeline_id)
1153 struct app_pipeline_fc *p;
1154 struct app_pipeline_fc_flow *flow;
1157 /* Check input arguments */
1161 p = app_pipeline_data_fe(app, pipeline_id, &pipeline_flow_classification);
1165 for (i = 0; i < N_BUCKETS; i++)
1166 TAILQ_FOREACH(flow, &p->flows[i], node)
1167 print_fc_flow(flow);
1169 if (p->default_flow_present)
1170 printf("Default flow: port %" PRIu32 " (entry ptr = %p)\n",
1171 p->default_flow_port_id,
1172 p->default_flow_entry_ptr);
1174 printf("Default: DROP\n");
1182 * p <pipelineid> flow add qinq <svlan> <cvlan> port <portid> id <flowid>
1183 * p <pipelineid> flow add qinq bulk <file>
1184 * p <pipelineid> flow add ipv4 <sipaddr> <dipaddr> <sport> <dport> <proto> port <port ID> id <flowid>
1185 * p <pipelineid> flow add ipv4 bulk <file>
1186 * p <pipelineid> flow add ipv6 <sipaddr> <dipaddr> <sport> <dport> <proto> port <port ID> id <flowid>
1187 * p <pipelineid> flow add ipv6 bulk <file>
1190 * p <pipelineid> flow add default <portid>
1193 * p <pipelineid> flow del qinq <svlan> <cvlan>
1194 * p <pipelineid> flow del ipv4 <sipaddr> <dipaddr> <sport> <dport> <proto>
1195 * p <pipelineid> flow del ipv6 <sipaddr> <dipaddr> <sport> <dport> <proto>
1198 * p <pipelineid> flow del default
1201 * p <pipelineid> flow ls
1204 struct cmd_flow_result {
1205 cmdline_fixed_string_t p_string;
1206 uint32_t pipeline_id;
1207 cmdline_fixed_string_t flow_string;
1208 cmdline_multi_string_t multi_string;
1212 cmd_flow_parsed(void *parsed_result,
1213 __attribute__((unused)) struct cmdline *cl,
1216 struct cmd_flow_result *results = parsed_result;
1217 struct app_params *app = data;
1220 uint32_t n_tokens = RTE_DIM(tokens);
1223 status = parse_tokenize_string(results->multi_string, tokens, &n_tokens);
1225 printf(CMD_MSG_TOO_MANY_ARGS, "flow");
1230 if ((n_tokens >= 3) &&
1231 (strcmp(tokens[0], "add") == 0) &&
1232 (strcmp(tokens[1], "qinq") == 0) &&
1233 strcmp(tokens[2], "bulk")) {
1234 struct pipeline_fc_key key;
1240 memset(&key, 0, sizeof(key));
1242 if (n_tokens != 8) {
1243 printf(CMD_MSG_MISMATCH_ARGS, "flow add qinq");
1247 if (parser_read_uint32(&svlan, tokens[2]) != 0) {
1248 printf(CMD_MSG_INVALID_ARG, "svlan");
1252 if (parser_read_uint32(&cvlan, tokens[3]) != 0) {
1253 printf(CMD_MSG_INVALID_ARG, "cvlan");
1257 if (strcmp(tokens[4], "port") != 0) {
1258 printf(CMD_MSG_ARG_NOT_FOUND, "port");
1262 if (parser_read_uint32(&port_id, tokens[5]) != 0) {
1263 printf(CMD_MSG_INVALID_ARG, "portid");
1267 if (strcmp(tokens[6], "id") != 0) {
1268 printf(CMD_MSG_ARG_NOT_FOUND, "id");
1272 if (parser_read_uint32(&flow_id, tokens[7]) != 0) {
1273 printf(CMD_MSG_INVALID_ARG, "flowid");
1277 key.type = FLOW_KEY_QINQ;
1278 key.key.qinq.svlan = svlan;
1279 key.key.qinq.cvlan = cvlan;
1281 status = app_pipeline_fc_add(app,
1282 results->pipeline_id,
1287 printf(CMD_MSG_FAIL, "flow add qinq");
1290 } /* flow add qinq */
1293 if ((n_tokens >= 3) &&
1294 (strcmp(tokens[0], "add") == 0) &&
1295 (strcmp(tokens[1], "ipv4") == 0) &&
1296 strcmp(tokens[2], "bulk")) {
1297 struct pipeline_fc_key key;
1298 struct in_addr sipaddr;
1299 struct in_addr dipaddr;
1306 memset(&key, 0, sizeof(key));
1308 if (n_tokens != 11) {
1309 printf(CMD_MSG_MISMATCH_ARGS, "flow add ipv4");
1313 if (parse_ipv4_addr(tokens[2], &sipaddr) != 0) {
1314 printf(CMD_MSG_INVALID_ARG, "sipv4addr");
1317 if (parse_ipv4_addr(tokens[3], &dipaddr) != 0) {
1318 printf(CMD_MSG_INVALID_ARG, "dipv4addr");
1322 if (parser_read_uint32(&sport, tokens[4]) != 0) {
1323 printf(CMD_MSG_INVALID_ARG, "sport");
1327 if (parser_read_uint32(&dport, tokens[5]) != 0) {
1328 printf(CMD_MSG_INVALID_ARG, "dport");
1332 if (parser_read_uint32(&proto, tokens[6]) != 0) {
1333 printf(CMD_MSG_INVALID_ARG, "proto");
1337 if (strcmp(tokens[7], "port") != 0) {
1338 printf(CMD_MSG_ARG_NOT_FOUND, "port");
1342 if (parser_read_uint32(&port_id, tokens[8]) != 0) {
1343 printf(CMD_MSG_INVALID_ARG, "portid");
1347 if (strcmp(tokens[9], "id") != 0) {
1348 printf(CMD_MSG_ARG_NOT_FOUND, "id");
1352 if (parser_read_uint32(&flow_id, tokens[10]) != 0) {
1353 printf(CMD_MSG_INVALID_ARG, "flowid");
1357 key.type = FLOW_KEY_IPV4_5TUPLE;
1358 key.key.ipv4_5tuple.ip_src = rte_be_to_cpu_32(sipaddr.s_addr);
1359 key.key.ipv4_5tuple.ip_dst = rte_be_to_cpu_32(dipaddr.s_addr);
1360 key.key.ipv4_5tuple.port_src = sport;
1361 key.key.ipv4_5tuple.port_dst = dport;
1362 key.key.ipv4_5tuple.proto = proto;
1364 status = app_pipeline_fc_add(app,
1365 results->pipeline_id,
1370 printf(CMD_MSG_FAIL, "flow add ipv4");
1373 } /* flow add ipv4 */
1376 if ((n_tokens >= 3) &&
1377 (strcmp(tokens[0], "add") == 0) &&
1378 (strcmp(tokens[1], "ipv6") == 0) &&
1379 strcmp(tokens[2], "bulk")) {
1380 struct pipeline_fc_key key;
1381 struct in6_addr sipaddr;
1382 struct in6_addr dipaddr;
1389 memset(&key, 0, sizeof(key));
1391 if (n_tokens != 11) {
1392 printf(CMD_MSG_MISMATCH_ARGS, "flow add ipv6");
1396 if (parse_ipv6_addr(tokens[2], &sipaddr) != 0) {
1397 printf(CMD_MSG_INVALID_ARG, "sipv6addr");
1400 if (parse_ipv6_addr(tokens[3], &dipaddr) != 0) {
1401 printf(CMD_MSG_INVALID_ARG, "dipv6addr");
1405 if (parser_read_uint32(&sport, tokens[4]) != 0) {
1406 printf(CMD_MSG_INVALID_ARG, "sport");
1410 if (parser_read_uint32(&dport, tokens[5]) != 0) {
1411 printf(CMD_MSG_INVALID_ARG, "dport");
1415 if (parser_read_uint32(&proto, tokens[6]) != 0) {
1416 printf(CMD_MSG_INVALID_ARG, "proto");
1420 if (strcmp(tokens[7], "port") != 0) {
1421 printf(CMD_MSG_ARG_NOT_FOUND, "port");
1425 if (parser_read_uint32(&port_id, tokens[8]) != 0) {
1426 printf(CMD_MSG_INVALID_ARG, "portid");
1430 if (strcmp(tokens[9], "id") != 0) {
1431 printf(CMD_MSG_ARG_NOT_FOUND, "id");
1435 if (parser_read_uint32(&flow_id, tokens[10]) != 0) {
1436 printf(CMD_MSG_INVALID_ARG, "flowid");
1440 key.type = FLOW_KEY_IPV6_5TUPLE;
1441 memcpy(key.key.ipv6_5tuple.ip_src, (void *)&sipaddr, 16);
1442 memcpy(key.key.ipv6_5tuple.ip_dst, (void *)&dipaddr, 16);
1443 key.key.ipv6_5tuple.port_src = sport;
1444 key.key.ipv6_5tuple.port_dst = dport;
1445 key.key.ipv6_5tuple.proto = proto;
1447 status = app_pipeline_fc_add(app,
1448 results->pipeline_id,
1453 printf(CMD_MSG_FAIL, "flow add ipv6");
1456 } /* flow add ipv6 */
1458 /* flow add qinq bulk */
1459 if ((n_tokens >= 3) &&
1460 (strcmp(tokens[0], "add") == 0) &&
1461 (strcmp(tokens[1], "qinq") == 0) &&
1462 (strcmp(tokens[2], "bulk") == 0)) {
1463 struct pipeline_fc_key *keys;
1464 uint32_t *port_ids, *flow_ids, n_keys, line;
1467 if (n_tokens != 4) {
1468 printf(CMD_MSG_MISMATCH_ARGS, "flow add qinq bulk");
1472 filename = tokens[3];
1474 n_keys = APP_PIPELINE_FC_MAX_FLOWS_IN_FILE;
1475 keys = malloc(n_keys * sizeof(struct pipeline_fc_key));
1478 memset(keys, 0, n_keys * sizeof(struct pipeline_fc_key));
1480 port_ids = malloc(n_keys * sizeof(uint32_t));
1481 if (port_ids == NULL) {
1486 flow_ids = malloc(n_keys * sizeof(uint32_t));
1487 if (flow_ids == NULL) {
1493 status = app_pipeline_fc_load_file_qinq(filename,
1500 printf(CMD_MSG_FILE_ERR, filename, line);
1507 status = app_pipeline_fc_add_bulk(app,
1508 results->pipeline_id,
1514 printf(CMD_MSG_FAIL, "flow add qinq bulk");
1520 } /* flow add qinq bulk */
1522 /* flow add ipv4 bulk */
1523 if ((n_tokens >= 3) &&
1524 (strcmp(tokens[0], "add") == 0) &&
1525 (strcmp(tokens[1], "ipv4") == 0) &&
1526 (strcmp(tokens[2], "bulk") == 0)) {
1527 struct pipeline_fc_key *keys;
1528 uint32_t *port_ids, *flow_ids, n_keys, line;
1531 if (n_tokens != 4) {
1532 printf(CMD_MSG_MISMATCH_ARGS, "flow add ipv4 bulk");
1536 filename = tokens[3];
1538 n_keys = APP_PIPELINE_FC_MAX_FLOWS_IN_FILE;
1539 keys = malloc(n_keys * sizeof(struct pipeline_fc_key));
1542 memset(keys, 0, n_keys * sizeof(struct pipeline_fc_key));
1544 port_ids = malloc(n_keys * sizeof(uint32_t));
1545 if (port_ids == NULL) {
1550 flow_ids = malloc(n_keys * sizeof(uint32_t));
1551 if (flow_ids == NULL) {
1557 status = app_pipeline_fc_load_file_ipv4(filename,
1564 printf(CMD_MSG_FILE_ERR, filename, line);
1571 status = app_pipeline_fc_add_bulk(app,
1572 results->pipeline_id,
1578 printf(CMD_MSG_FAIL, "flow add ipv4 bulk");
1584 } /* flow add ipv4 bulk */
1586 /* flow add ipv6 bulk */
1587 if ((n_tokens >= 3) &&
1588 (strcmp(tokens[0], "add") == 0) &&
1589 (strcmp(tokens[1], "ipv6") == 0) &&
1590 (strcmp(tokens[2], "bulk") == 0)) {
1591 struct pipeline_fc_key *keys;
1592 uint32_t *port_ids, *flow_ids, n_keys, line;
1595 if (n_tokens != 4) {
1596 printf(CMD_MSG_MISMATCH_ARGS, "flow add ipv6 bulk");
1600 filename = tokens[3];
1602 n_keys = APP_PIPELINE_FC_MAX_FLOWS_IN_FILE;
1603 keys = malloc(n_keys * sizeof(struct pipeline_fc_key));
1606 memset(keys, 0, n_keys * sizeof(struct pipeline_fc_key));
1608 port_ids = malloc(n_keys * sizeof(uint32_t));
1609 if (port_ids == NULL) {
1614 flow_ids = malloc(n_keys * sizeof(uint32_t));
1615 if (flow_ids == NULL) {
1621 status = app_pipeline_fc_load_file_ipv6(filename,
1628 printf(CMD_MSG_FILE_ERR, filename, line);
1635 status = app_pipeline_fc_add_bulk(app,
1636 results->pipeline_id,
1642 printf(CMD_MSG_FAIL, "flow add ipv6 bulk");
1648 } /* flow add ipv6 bulk */
1650 /* flow add default*/
1651 if ((n_tokens >= 2) &&
1652 (strcmp(tokens[0], "add") == 0) &&
1653 (strcmp(tokens[1], "default") == 0)) {
1656 if (n_tokens != 3) {
1657 printf(CMD_MSG_MISMATCH_ARGS, "flow add default");
1661 if (parser_read_uint32(&port_id, tokens[2]) != 0) {
1662 printf(CMD_MSG_INVALID_ARG, "portid");
1666 status = app_pipeline_fc_add_default(app,
1667 results->pipeline_id,
1670 printf(CMD_MSG_FAIL, "flow add default");
1673 } /* flow add default */
1676 if ((n_tokens >= 2) &&
1677 (strcmp(tokens[0], "del") == 0) &&
1678 (strcmp(tokens[1], "qinq") == 0)) {
1679 struct pipeline_fc_key key;
1683 memset(&key, 0, sizeof(key));
1685 if (n_tokens != 4) {
1686 printf(CMD_MSG_MISMATCH_ARGS, "flow del qinq");
1690 if (parser_read_uint32(&svlan, tokens[2]) != 0) {
1691 printf(CMD_MSG_INVALID_ARG, "svlan");
1695 if (parser_read_uint32(&cvlan, tokens[3]) != 0) {
1696 printf(CMD_MSG_INVALID_ARG, "cvlan");
1700 key.type = FLOW_KEY_QINQ;
1701 key.key.qinq.svlan = svlan;
1702 key.key.qinq.cvlan = cvlan;
1704 status = app_pipeline_fc_del(app,
1705 results->pipeline_id,
1708 printf(CMD_MSG_FAIL, "flow del qinq");
1711 } /* flow del qinq */
1714 if ((n_tokens >= 2) &&
1715 (strcmp(tokens[0], "del") == 0) &&
1716 (strcmp(tokens[1], "ipv4") == 0)) {
1717 struct pipeline_fc_key key;
1718 struct in_addr sipaddr;
1719 struct in_addr dipaddr;
1724 memset(&key, 0, sizeof(key));
1726 if (n_tokens != 7) {
1727 printf(CMD_MSG_MISMATCH_ARGS, "flow del ipv4");
1731 if (parse_ipv4_addr(tokens[2], &sipaddr) != 0) {
1732 printf(CMD_MSG_INVALID_ARG, "sipv4addr");
1735 if (parse_ipv4_addr(tokens[3], &dipaddr) != 0) {
1736 printf(CMD_MSG_INVALID_ARG, "dipv4addr");
1740 if (parser_read_uint32(&sport, tokens[4]) != 0) {
1741 printf(CMD_MSG_INVALID_ARG, "sport");
1745 if (parser_read_uint32(&dport, tokens[5]) != 0) {
1746 printf(CMD_MSG_INVALID_ARG, "dport");
1750 if (parser_read_uint32(&proto, tokens[6]) != 0) {
1751 printf(CMD_MSG_INVALID_ARG, "proto");
1755 key.type = FLOW_KEY_IPV4_5TUPLE;
1756 key.key.ipv4_5tuple.ip_src = rte_be_to_cpu_32(sipaddr.s_addr);
1757 key.key.ipv4_5tuple.ip_dst = rte_be_to_cpu_32(dipaddr.s_addr);
1758 key.key.ipv4_5tuple.port_src = sport;
1759 key.key.ipv4_5tuple.port_dst = dport;
1760 key.key.ipv4_5tuple.proto = proto;
1762 status = app_pipeline_fc_del(app,
1763 results->pipeline_id,
1766 printf(CMD_MSG_FAIL, "flow del ipv4");
1769 } /* flow del ipv4 */
1772 if ((n_tokens >= 2) &&
1773 (strcmp(tokens[0], "del") == 0) &&
1774 (strcmp(tokens[1], "ipv6") == 0)) {
1775 struct pipeline_fc_key key;
1776 struct in6_addr sipaddr;
1777 struct in6_addr dipaddr;
1782 memset(&key, 0, sizeof(key));
1784 if (n_tokens != 7) {
1785 printf(CMD_MSG_MISMATCH_ARGS, "flow del ipv6");
1789 if (parse_ipv6_addr(tokens[2], &sipaddr) != 0) {
1790 printf(CMD_MSG_INVALID_ARG, "sipv6addr");
1794 if (parse_ipv6_addr(tokens[3], &dipaddr) != 0) {
1795 printf(CMD_MSG_INVALID_ARG, "dipv6addr");
1799 if (parser_read_uint32(&sport, tokens[4]) != 0) {
1800 printf(CMD_MSG_INVALID_ARG, "sport");
1804 if (parser_read_uint32(&dport, tokens[5]) != 0) {
1805 printf(CMD_MSG_INVALID_ARG, "dport");
1809 if (parser_read_uint32(&proto, tokens[6]) != 0) {
1810 printf(CMD_MSG_INVALID_ARG, "proto");
1814 key.type = FLOW_KEY_IPV6_5TUPLE;
1815 memcpy(key.key.ipv6_5tuple.ip_src, &sipaddr, 16);
1816 memcpy(key.key.ipv6_5tuple.ip_dst, &dipaddr, 16);
1817 key.key.ipv6_5tuple.port_src = sport;
1818 key.key.ipv6_5tuple.port_dst = dport;
1819 key.key.ipv6_5tuple.proto = proto;
1821 status = app_pipeline_fc_del(app,
1822 results->pipeline_id,
1825 printf(CMD_MSG_FAIL, "flow del ipv6");
1828 } /* flow del ipv6 */
1830 /* flow del default*/
1831 if ((n_tokens >= 2) &&
1832 (strcmp(tokens[0], "del") == 0) &&
1833 (strcmp(tokens[1], "default") == 0)) {
1834 if (n_tokens != 2) {
1835 printf(CMD_MSG_MISMATCH_ARGS, "flow del default");
1839 status = app_pipeline_fc_del_default(app,
1840 results->pipeline_id);
1842 printf(CMD_MSG_FAIL, "flow del default");
1845 } /* flow del default */
1848 if ((n_tokens >= 1) && (strcmp(tokens[0], "ls") == 0)) {
1849 if (n_tokens != 1) {
1850 printf(CMD_MSG_MISMATCH_ARGS, "flow ls");
1854 status = app_pipeline_fc_ls(app, results->pipeline_id);
1856 printf(CMD_MSG_FAIL, "flow ls");
1861 printf(CMD_MSG_MISMATCH_ARGS, "flow");
1864 static cmdline_parse_token_string_t cmd_flow_p_string =
1865 TOKEN_STRING_INITIALIZER(struct cmd_flow_result, p_string, "p");
1867 static cmdline_parse_token_num_t cmd_flow_pipeline_id =
1868 TOKEN_NUM_INITIALIZER(struct cmd_flow_result, pipeline_id, UINT32);
1870 static cmdline_parse_token_string_t cmd_flow_flow_string =
1871 TOKEN_STRING_INITIALIZER(struct cmd_flow_result, flow_string, "flow");
1873 static cmdline_parse_token_string_t cmd_flow_multi_string =
1874 TOKEN_STRING_INITIALIZER(struct cmd_flow_result, multi_string,
1875 TOKEN_STRING_MULTI);
1877 static cmdline_parse_inst_t cmd_flow = {
1878 .f = cmd_flow_parsed,
1880 .help_str = "flow add / add bulk / add default / del / del default / ls",
1882 (void *) &cmd_flow_p_string,
1883 (void *) &cmd_flow_pipeline_id,
1884 (void *) &cmd_flow_flow_string,
1885 (void *) &cmd_flow_multi_string,
1890 static cmdline_parse_ctx_t pipeline_cmds[] = {
1891 (cmdline_parse_inst_t *) &cmd_flow,
1895 static struct pipeline_fe_ops pipeline_flow_classification_fe_ops = {
1896 .f_init = app_pipeline_fc_init,
1897 .f_post_init = NULL,
1898 .f_free = app_pipeline_fc_free,
1899 .f_track = app_pipeline_track_default,
1900 .cmds = pipeline_cmds,
1903 struct pipeline_type pipeline_flow_classification = {
1904 .name = "FLOW_CLASSIFICATION",
1905 .be_ops = &pipeline_flow_classification_be_ops,
1906 .fe_ops = &pipeline_flow_classification_fe_ops,