Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2021 NVIDIA Corporation & Affiliates
3 : : */
4 : :
5 : : #include <stddef.h>
6 : : #include <stdint.h>
7 : : #include <stdio.h>
8 : : #include <stdlib.h>
9 : : #include <errno.h>
10 : : #include <string.h>
11 : :
12 : : #include <rte_common.h>
13 : : #include <rte_ethdev.h>
14 : : #include <cmdline_parse.h>
15 : : #include <cmdline_parse_string.h>
16 : : #include <cmdline_parse_num.h>
17 : : #include <rte_flow.h>
18 : :
19 : : #include "testpmd.h"
20 : :
21 : : struct flex_item *flex_items[RTE_MAX_ETHPORTS][FLEX_MAX_PARSERS_NUM];
22 : : struct flex_pattern flex_patterns[FLEX_MAX_PATTERNS_NUM];
23 : :
24 : : #ifdef RTE_HAS_JANSSON
25 : :
26 : : static struct flex_item *
27 : 0 : flex_parser_fetch(uint16_t port_id, uint16_t flex_id)
28 : : {
29 : 0 : if (port_id >= RTE_MAX_ETHPORTS) {
30 : 0 : printf("Invalid port_id: %u\n", port_id);
31 : 0 : return FLEX_PARSER_ERR;
32 : : }
33 : 0 : if (flex_id >= FLEX_MAX_PARSERS_NUM) {
34 : 0 : printf("Invalid flex item flex_id: %u\n", flex_id);
35 : 0 : return FLEX_PARSER_ERR;
36 : : }
37 : 0 : return flex_items[port_id][flex_id];
38 : : }
39 : :
40 : : static __rte_always_inline bool
41 : : match_strkey(const char *key, const char *pattern)
42 : : {
43 : 0 : return strncmp(key, pattern, strlen(key)) == 0;
44 : : }
45 : :
46 : : static int
47 : 0 : flex_tunnel_parse(json_t *jtun, enum rte_flow_item_flex_tunnel_mode *tunnel)
48 : : {
49 : : int tun = -1;
50 : :
51 : 0 : if (json_is_integer(jtun))
52 : 0 : tun = (int)json_integer_value(jtun);
53 : 0 : else if (json_is_real(jtun))
54 : 0 : tun = (int)json_real_value(jtun);
55 : 0 : else if (json_is_string(jtun)) {
56 : 0 : const char *mode = json_string_value(jtun);
57 : :
58 : 0 : if (match_strkey(mode, "FLEX_TUNNEL_MODE_SINGLE"))
59 : : tun = FLEX_TUNNEL_MODE_SINGLE;
60 : 0 : else if (match_strkey(mode, "FLEX_TUNNEL_MODE_OUTER"))
61 : : tun = FLEX_TUNNEL_MODE_OUTER;
62 : 0 : else if (match_strkey(mode, "FLEX_TUNNEL_MODE_INNER"))
63 : : tun = FLEX_TUNNEL_MODE_INNER;
64 : 0 : else if (match_strkey(mode, "FLEX_TUNNEL_MODE_MULTI"))
65 : : tun = FLEX_TUNNEL_MODE_MULTI;
66 : 0 : else if (match_strkey(mode, "FLEX_TUNNEL_MODE_TUNNEL"))
67 : : tun = FLEX_TUNNEL_MODE_TUNNEL;
68 : : else
69 : : return -EINVAL;
70 : : } else
71 : : return -EINVAL;
72 : 0 : *tunnel = (enum rte_flow_item_flex_tunnel_mode)tun;
73 : 0 : return 0;
74 : : }
75 : :
76 : : static int
77 : 0 : flex_field_parse(json_t *jfld, struct rte_flow_item_flex_field *fld)
78 : : {
79 : : const char *key;
80 : : json_t *je;
81 : :
82 : : #define FLEX_FIELD_GET(fm, t) \
83 : : do { \
84 : : if (!strncmp(key, # fm, strlen(# fm))) { \
85 : : if (json_is_real(je)) \
86 : : fld->fm = (t) json_real_value(je); \
87 : : else if (json_is_integer(je)) \
88 : : fld->fm = (t) json_integer_value(je); \
89 : : else \
90 : : return -EINVAL; \
91 : : } \
92 : : } while (0)
93 : :
94 : 0 : json_object_foreach(jfld, key, je) {
95 : 0 : FLEX_FIELD_GET(field_size, uint32_t);
96 : 0 : FLEX_FIELD_GET(field_base, int32_t);
97 : 0 : FLEX_FIELD_GET(offset_base, uint32_t);
98 : 0 : FLEX_FIELD_GET(offset_mask, uint32_t);
99 : 0 : FLEX_FIELD_GET(offset_shift, int32_t);
100 : 0 : FLEX_FIELD_GET(field_id, uint16_t);
101 : 0 : if (match_strkey(key, "field_mode")) {
102 : : const char *mode;
103 : 0 : if (!json_is_string(je))
104 : : return -EINVAL;
105 : 0 : mode = json_string_value(je);
106 : 0 : if (match_strkey(mode, "FIELD_MODE_DUMMY"))
107 : 0 : fld->field_mode = FIELD_MODE_DUMMY;
108 : 0 : else if (match_strkey(mode, "FIELD_MODE_FIXED"))
109 : 0 : fld->field_mode = FIELD_MODE_FIXED;
110 : 0 : else if (match_strkey(mode, "FIELD_MODE_OFFSET"))
111 : 0 : fld->field_mode = FIELD_MODE_OFFSET;
112 : 0 : else if (match_strkey(mode, "FIELD_MODE_BITMASK"))
113 : 0 : fld->field_mode = FIELD_MODE_BITMASK;
114 : : else
115 : : return -EINVAL;
116 : : }
117 : : }
118 : : return 0;
119 : : }
120 : :
121 : : enum flex_link_type {
122 : : FLEX_LINK_IN = 0,
123 : : FLEX_LINK_OUT = 1
124 : : };
125 : :
126 : : static int
127 : 0 : flex_link_item_parse(const char *src, struct rte_flow_item *item)
128 : : {
129 : : #define FLEX_PARSE_DATA_SIZE 1024
130 : :
131 : : int ret;
132 : 0 : uint8_t *ptr, data[FLEX_PARSE_DATA_SIZE] = {0,};
133 : : char flow_rule[256];
134 : : struct rte_flow_attr *attr;
135 : : struct rte_flow_item *pattern;
136 : : struct rte_flow_action *actions;
137 : :
138 : : sprintf(flow_rule,
139 : : "flow create 0 pattern %s / end actions drop / end", src);
140 : : src = flow_rule;
141 : 0 : ret = flow_parse(src, (void *)data, sizeof(data),
142 : : &attr, &pattern, &actions);
143 : 0 : if (ret)
144 : : return ret;
145 : 0 : item->type = pattern->type;
146 : 0 : ret = rte_flow_conv(RTE_FLOW_CONV_OP_ITEM_MASK, NULL, 0, item, NULL);
147 : 0 : if ((ret > 0) && pattern->spec) {
148 : 0 : ptr = (void *)(uintptr_t)item->spec;
149 : 0 : memcpy(ptr, pattern->spec, ret);
150 : : } else {
151 : 0 : item->spec = NULL;
152 : : }
153 : 0 : if ((ret > 0) && pattern->mask) {
154 : 0 : ptr = (void *)(uintptr_t)item->mask;
155 : 0 : memcpy(ptr, pattern->mask, ret);
156 : : } else {
157 : 0 : item->mask = NULL;
158 : : }
159 : 0 : if ((ret > 0) && pattern->last) {
160 : 0 : ptr = (void *)(uintptr_t)item->last;
161 : 0 : memcpy(ptr, pattern->last, ret);
162 : : } else {
163 : 0 : item->last = NULL;
164 : : }
165 : : return 0;
166 : : }
167 : :
168 : : static int
169 : 0 : flex_link_parse(json_t *jobj, struct rte_flow_item_flex_link *link,
170 : : enum flex_link_type link_type)
171 : : {
172 : : const char *key;
173 : : json_t *je;
174 : : int ret;
175 : 0 : json_object_foreach(jobj, key, je) {
176 : 0 : if (match_strkey(key, "item")) {
177 : 0 : if (!json_is_string(je))
178 : : return -EINVAL;
179 : 0 : ret = flex_link_item_parse(json_string_value(je),
180 : : &link->item);
181 : 0 : if (ret)
182 : : return -EINVAL;
183 : 0 : if (link_type == FLEX_LINK_IN) {
184 : 0 : if (!link->item.spec || !link->item.mask)
185 : : return -EINVAL;
186 : 0 : if (link->item.last)
187 : : return -EINVAL;
188 : : }
189 : : }
190 : 0 : if (match_strkey(key, "next")) {
191 : 0 : if (json_is_integer(je))
192 : 0 : link->next = (typeof(link->next))
193 : 0 : json_integer_value(je);
194 : 0 : else if (json_is_real(je))
195 : 0 : link->next = (typeof(link->next))
196 : 0 : json_real_value(je);
197 : : else
198 : : return -EINVAL;
199 : : }
200 : : }
201 : : return 0;
202 : : }
203 : :
204 : 0 : static int flex_item_config(json_t *jroot,
205 : : struct rte_flow_item_flex_conf *flex_conf)
206 : : {
207 : : const char *key;
208 : : json_t *jobj = NULL;
209 : : int ret = 0;
210 : :
211 : 0 : json_object_foreach(jroot, key, jobj) {
212 : 0 : if (match_strkey(key, "tunnel")) {
213 : 0 : ret = flex_tunnel_parse(jobj, &flex_conf->tunnel);
214 : 0 : if (ret) {
215 : : printf("Can't parse tunnel value\n");
216 : 0 : goto out;
217 : : }
218 : 0 : } else if (match_strkey(key, "next_header")) {
219 : 0 : ret = flex_field_parse(jobj, &flex_conf->next_header);
220 : 0 : if (ret) {
221 : : printf("Can't parse next_header field\n");
222 : 0 : goto out;
223 : : }
224 : 0 : } else if (match_strkey(key, "next_protocol")) {
225 : 0 : ret = flex_field_parse(jobj,
226 : : &flex_conf->next_protocol);
227 : 0 : if (ret) {
228 : : printf("Can't parse next_protocol field\n");
229 : 0 : goto out;
230 : : }
231 : 0 : } else if (match_strkey(key, "sample_data")) {
232 : : json_t *ji;
233 : 0 : uint32_t i, size = json_array_size(jobj);
234 : 0 : if (size > FLEX_ITEM_MAX_SAMPLES_NUM) {
235 : : printf("Number of sample_data entries (%u) exceeds maximum %u\n",
236 : : size, FLEX_ITEM_MAX_SAMPLES_NUM);
237 : : ret = -EINVAL;
238 : 0 : goto out;
239 : : }
240 : 0 : for (i = 0; i < size; i++) {
241 : 0 : ji = json_array_get(jobj, i);
242 : 0 : ret = flex_field_parse
243 : 0 : (ji, flex_conf->sample_data + i);
244 : 0 : if (ret) {
245 : : printf("Can't parse sample_data field(s)\n");
246 : 0 : goto out;
247 : : }
248 : : }
249 : 0 : flex_conf->nb_samples = size;
250 : 0 : } else if (match_strkey(key, "input_link")) {
251 : : json_t *ji;
252 : 0 : uint32_t i, size = json_array_size(jobj);
253 : 0 : if (size > FLEX_ITEM_MAX_LINKS_NUM) {
254 : : printf("Number of input_link entries (%u) exceeds maximum %u\n",
255 : : size, FLEX_ITEM_MAX_LINKS_NUM);
256 : : ret = -EINVAL;
257 : 0 : goto out;
258 : : }
259 : 0 : for (i = 0; i < size; i++) {
260 : 0 : ji = json_array_get(jobj, i);
261 : 0 : ret = flex_link_parse(ji,
262 : 0 : flex_conf->input_link + i,
263 : : FLEX_LINK_IN);
264 : 0 : if (ret) {
265 : : printf("Can't parse input_link(s)\n");
266 : 0 : goto out;
267 : : }
268 : : }
269 : 0 : flex_conf->nb_inputs = size;
270 : 0 : } else if (match_strkey(key, "output_link")) {
271 : : json_t *ji;
272 : 0 : uint32_t i, size = json_array_size(jobj);
273 : 0 : if (size > FLEX_ITEM_MAX_LINKS_NUM) {
274 : : printf("Number of output_link entries (%u) exceeds maximum %u\n",
275 : : size, FLEX_ITEM_MAX_LINKS_NUM);
276 : : ret = -EINVAL;
277 : 0 : goto out;
278 : : }
279 : 0 : for (i = 0; i < size; i++) {
280 : 0 : ji = json_array_get(jobj, i);
281 : 0 : ret = flex_link_parse
282 : 0 : (ji, flex_conf->output_link + i,
283 : : FLEX_LINK_OUT);
284 : 0 : if (ret) {
285 : : printf("Can't parse output_link(s)\n");
286 : 0 : goto out;
287 : : }
288 : : }
289 : 0 : flex_conf->nb_outputs = size;
290 : : }
291 : : }
292 : 0 : out:
293 : 0 : return ret;
294 : : }
295 : :
296 : : static struct flex_item *
297 : 0 : flex_item_init(void)
298 : : {
299 : : size_t base_size, samples_size, links_size, spec_size;
300 : : struct rte_flow_item_flex_conf *conf;
301 : : struct flex_item *fp;
302 : : uint8_t (*pattern)[FLEX_MAX_FLOW_PATTERN_LENGTH];
303 : : int i;
304 : :
305 : : base_size = RTE_ALIGN(sizeof(*conf), sizeof(uintptr_t));
306 : : samples_size = RTE_ALIGN(FLEX_ITEM_MAX_SAMPLES_NUM *
307 : : sizeof(conf->sample_data[0]),
308 : : sizeof(uintptr_t));
309 : : links_size = RTE_ALIGN(FLEX_ITEM_MAX_LINKS_NUM *
310 : : sizeof(conf->input_link[0]),
311 : : sizeof(uintptr_t));
312 : : /* spec & mask for all input links */
313 : : spec_size = 2 * FLEX_MAX_FLOW_PATTERN_LENGTH * FLEX_ITEM_MAX_LINKS_NUM;
314 : 0 : fp = calloc(1, base_size + samples_size + 2 * links_size + spec_size);
315 : 0 : if (fp == NULL) {
316 : : printf("Can't allocate memory for flex item\n");
317 : 0 : return NULL;
318 : : }
319 : : conf = &fp->flex_conf;
320 : 0 : conf->sample_data = (typeof(conf->sample_data))
321 : : ((uint8_t *)fp + base_size);
322 : 0 : conf->input_link = (typeof(conf->input_link))
323 : : ((uint8_t *)conf->sample_data + samples_size);
324 : 0 : conf->output_link = (typeof(conf->output_link))
325 : : ((uint8_t *)conf->input_link + links_size);
326 : 0 : pattern = (typeof(pattern))((uint8_t *)conf->output_link + links_size);
327 : 0 : for (i = 0; i < FLEX_ITEM_MAX_LINKS_NUM; i++) {
328 : 0 : struct rte_flow_item_flex_link *in = conf->input_link + i;
329 : 0 : in->item.spec = pattern++;
330 : 0 : in->item.mask = pattern++;
331 : : }
332 : : return fp;
333 : : }
334 : :
335 : : static int
336 : 0 : flex_item_build_config(struct flex_item *fp, const char *filename)
337 : : {
338 : : int ret;
339 : : json_error_t json_error;
340 : 0 : json_t *jroot = json_load_file(filename, 0, &json_error);
341 : :
342 : 0 : if (!jroot) {
343 : : printf("Bad JSON file \"%s\": %s\n", filename, json_error.text);
344 : 0 : return -1;
345 : : }
346 : 0 : ret = flex_item_config(jroot, &fp->flex_conf);
347 : 0 : json_decref(jroot);
348 : 0 : return ret;
349 : : }
350 : :
351 : : void
352 : 0 : flex_item_create(portid_t port_id, uint16_t flex_id, const char *filename)
353 : : {
354 : : struct rte_flow_error flow_error;
355 : 0 : struct flex_item *fp = flex_parser_fetch(port_id, flex_id);
356 : : int ret;
357 : :
358 : 0 : if (fp == FLEX_PARSER_ERR) {
359 : : printf("Bad parameters: port_id=%u flex_id=%u\n",
360 : : port_id, flex_id);
361 : 0 : return;
362 : : }
363 : 0 : if (fp) {
364 : : printf("port-%u: flex item #%u is already in use\n",
365 : : port_id, flex_id);
366 : 0 : return;
367 : : }
368 : 0 : fp = flex_item_init();
369 : 0 : if (!fp) {
370 : : printf("Could not allocate flex item\n");
371 : 0 : goto out;
372 : : }
373 : 0 : ret = flex_item_build_config(fp, filename);
374 : 0 : if (ret)
375 : 0 : goto out;
376 : 0 : fp->flex_handle = rte_flow_flex_item_create(port_id,
377 : 0 : &fp->flex_conf,
378 : : &flow_error);
379 : 0 : if (fp->flex_handle) {
380 : 0 : flex_items[port_id][flex_id] = fp;
381 : : printf("port-%u: created flex item #%u\n", port_id, flex_id);
382 : : fp = NULL;
383 : : } else {
384 : 0 : printf("port-%u: flex item #%u creation failed: %s\n",
385 : : port_id, flex_id,
386 : 0 : flow_error.message ? flow_error.message : "");
387 : : }
388 : 0 : out:
389 : 0 : free(fp);
390 : : }
391 : :
392 : : void
393 : 0 : flex_item_destroy(portid_t port_id, uint16_t flex_id)
394 : : {
395 : : int ret;
396 : : struct rte_flow_error error;
397 : 0 : struct flex_item *fp = flex_parser_fetch(port_id, flex_id);
398 : 0 : if (fp == FLEX_PARSER_ERR) {
399 : : printf("Bad parameters: port_id=%u flex_id=%u\n",
400 : : port_id, flex_id);
401 : 0 : return;
402 : : }
403 : 0 : if (!fp)
404 : : return;
405 : 0 : ret = rte_flow_flex_item_release(port_id, fp->flex_handle, &error);
406 : 0 : if (!ret) {
407 : 0 : free(fp);
408 : 0 : flex_items[port_id][flex_id] = NULL;
409 : : printf("port-%u: released flex item #%u\n",
410 : : port_id, flex_id);
411 : :
412 : : } else {
413 : 0 : printf("port-%u: cannot release flex item #%u: %s\n",
414 : : port_id, flex_id, error.message);
415 : : }
416 : : }
417 : :
418 : : #else /* RTE_HAS_JANSSON */
419 : : void flex_item_create(__rte_unused portid_t port_id,
420 : : __rte_unused uint16_t flex_id,
421 : : __rte_unused const char *filename)
422 : : {
423 : : printf("cannot create flex item - no JSON library configured\n");
424 : : }
425 : :
426 : : void
427 : : flex_item_destroy(__rte_unused portid_t port_id, __rte_unused uint16_t flex_id)
428 : : {
429 : :
430 : : }
431 : :
432 : : #endif /* RTE_HAS_JANSSON */
433 : :
434 : : void
435 : 0 : port_flex_item_flush(portid_t port_id)
436 : : {
437 : : uint16_t i;
438 : :
439 : 0 : for (i = 0; i < FLEX_MAX_PARSERS_NUM; i++) {
440 : 0 : if (flex_items[port_id][i] != NULL) {
441 : 0 : flex_item_destroy(port_id, i);
442 : 0 : flex_items[port_id][i] = NULL;
443 : : }
444 : : }
445 : 0 : }
446 : :
447 : : struct flex_pattern_set {
448 : : cmdline_fixed_string_t set, flex_pattern;
449 : : cmdline_fixed_string_t is_spec, mask;
450 : : cmdline_fixed_string_t spec_data, mask_data;
451 : : uint16_t id;
452 : : };
453 : :
454 : : static cmdline_parse_token_string_t flex_pattern_set_token =
455 : : TOKEN_STRING_INITIALIZER(struct flex_pattern_set, set, "set");
456 : : static cmdline_parse_token_string_t flex_pattern_token =
457 : : TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
458 : : flex_pattern, "flex_pattern");
459 : : static cmdline_parse_token_string_t flex_pattern_is_token =
460 : : TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
461 : : is_spec, "is");
462 : : static cmdline_parse_token_string_t flex_pattern_spec_token =
463 : : TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
464 : : is_spec, "spec");
465 : : static cmdline_parse_token_string_t flex_pattern_mask_token =
466 : : TOKEN_STRING_INITIALIZER(struct flex_pattern_set, mask, "mask");
467 : : static cmdline_parse_token_string_t flex_pattern_spec_data_token =
468 : : TOKEN_STRING_INITIALIZER(struct flex_pattern_set, spec_data, NULL);
469 : : static cmdline_parse_token_string_t flex_pattern_mask_data_token =
470 : : TOKEN_STRING_INITIALIZER(struct flex_pattern_set, mask_data, NULL);
471 : : static cmdline_parse_token_num_t flex_pattern_id_token =
472 : : TOKEN_NUM_INITIALIZER(struct flex_pattern_set, id, RTE_UINT16);
473 : :
474 : : /*
475 : : * flex pattern data - spec or mask is a string representation of byte array
476 : : * in hexadecimal format. Each byte in data string must have 2 characters:
477 : : * 0x15 - "15"
478 : : * 0x1 - "01"
479 : : * Bytes in data array are in network order.
480 : : */
481 : : static uint32_t
482 : 0 : flex_pattern_data(const char *str, uint8_t *data)
483 : : {
484 : 0 : uint32_t i, len = strlen(str);
485 : : char b[3], *endptr;
486 : :
487 : 0 : if (len & 01)
488 : : return 0;
489 : 0 : len /= 2;
490 : 0 : if (len >= FLEX_MAX_FLOW_PATTERN_LENGTH)
491 : : return 0;
492 : 0 : for (i = 0, b[2] = '\0'; i < len; i++) {
493 : 0 : b[0] = str[2 * i];
494 : 0 : b[1] = str[2 * i + 1];
495 : 0 : data[i] = strtoul(b, &endptr, 16);
496 : 0 : if (endptr != &b[2])
497 : : return 0;
498 : : }
499 : : return len;
500 : : }
501 : :
502 : : static void
503 : 0 : flex_pattern_parsed_fn(void *parsed_result,
504 : : __rte_unused struct cmdline *cl,
505 : : __rte_unused void *data)
506 : : {
507 : : struct flex_pattern_set *res = parsed_result;
508 : : struct flex_pattern *fp;
509 : : bool full_spec;
510 : :
511 : 0 : if (res->id >= FLEX_MAX_PATTERNS_NUM) {
512 : : printf("Bad flex pattern id\n");
513 : 0 : return;
514 : : }
515 : 0 : fp = flex_patterns + res->id;
516 : 0 : memset(fp->spec_pattern, 0, sizeof(fp->spec_pattern));
517 : 0 : memset(fp->mask_pattern, 0, sizeof(fp->mask_pattern));
518 : 0 : fp->spec.length = flex_pattern_data(res->spec_data, fp->spec_pattern);
519 : 0 : if (!fp->spec.length) {
520 : : printf("Bad flex pattern spec\n");
521 : 0 : return;
522 : : }
523 : 0 : full_spec = strncmp(res->is_spec, "spec", strlen("spec")) == 0;
524 : 0 : if (full_spec) {
525 : 0 : fp->mask.length = flex_pattern_data(res->mask_data,
526 : : fp->mask_pattern);
527 : 0 : if (!fp->mask.length) {
528 : : printf("Bad flex pattern mask\n");
529 : 0 : return;
530 : : }
531 : : } else {
532 : 0 : memset(fp->mask_pattern, 0xFF, fp->spec.length);
533 : 0 : fp->mask.length = fp->spec.length;
534 : : }
535 : 0 : if (fp->mask.length != fp->spec.length) {
536 : : printf("Spec length do not match mask length\n");
537 : 0 : return;
538 : : }
539 : 0 : fp->spec.pattern = fp->spec_pattern;
540 : 0 : fp->mask.pattern = fp->mask_pattern;
541 : 0 : printf("created pattern #%u\n", res->id);
542 : : }
543 : :
544 : : cmdline_parse_inst_t cmd_set_flex_is_pattern = {
545 : : .f = flex_pattern_parsed_fn,
546 : : .data = NULL,
547 : : .help_str = "set flex_pattern <id> is <spec_data>",
548 : : .tokens = {
549 : : (void *)&flex_pattern_set_token,
550 : : (void *)&flex_pattern_token,
551 : : (void *)&flex_pattern_id_token,
552 : : (void *)&flex_pattern_is_token,
553 : : (void *)&flex_pattern_spec_data_token,
554 : : NULL,
555 : : }
556 : : };
557 : :
558 : : cmdline_parse_inst_t cmd_set_flex_spec_pattern = {
559 : : .f = flex_pattern_parsed_fn,
560 : : .data = NULL,
561 : : .help_str = "set flex_pattern <id> spec <spec_data> mask <mask_data>",
562 : : .tokens = {
563 : : (void *)&flex_pattern_set_token,
564 : : (void *)&flex_pattern_token,
565 : : (void *)&flex_pattern_id_token,
566 : : (void *)&flex_pattern_spec_token,
567 : : (void *)&flex_pattern_spec_data_token,
568 : : (void *)&flex_pattern_mask_token,
569 : : (void *)&flex_pattern_mask_data_token,
570 : : NULL,
571 : : }
572 : : };
|