1 /*
2 * parser.c - netlink command line parser
3 *
4 * Implementation of command line parser used by netlink code.
5 */
6
7 #include <string.h>
8 #include <stdlib.h>
9 #include <errno.h>
10 #include <ctype.h>
11
12 #include "../internal.h"
13 #include "../common.h"
14 #include "netlink.h"
15 #include "parser.h"
16
parser_err_unknown_param(struct nl_context * nlctx)17 static void parser_err_unknown_param(struct nl_context *nlctx)
18 {
19 fprintf(stderr, "ethtool (%s): unknown parameter '%s'\n", nlctx->cmd,
20 nlctx->param);
21 }
22
parser_err_dup_param(struct nl_context * nlctx)23 static void parser_err_dup_param(struct nl_context *nlctx)
24 {
25 fprintf(stderr, "ethtool (%s): duplicate parameter '%s'\n", nlctx->cmd,
26 nlctx->param);
27 }
28
parser_err_min_argc(struct nl_context * nlctx,unsigned int min_argc)29 static void parser_err_min_argc(struct nl_context *nlctx, unsigned int min_argc)
30 {
31 if (min_argc == 1)
32 fprintf(stderr, "ethtool (%s): no value for parameter '%s'\n",
33 nlctx->cmd, nlctx->param);
34 else
35 fprintf(stderr,
36 "ethtool (%s): parameter '%s' requires %u words\n",
37 nlctx->cmd, nlctx->param, min_argc);
38 }
39
parser_err_invalid_value(struct nl_context * nlctx,const char * val)40 static void parser_err_invalid_value(struct nl_context *nlctx, const char *val)
41 {
42 fprintf(stderr, "ethtool (%s): invalid value '%s' for parameter '%s'\n",
43 nlctx->cmd, val, nlctx->param);
44 }
45
parser_err_invalid_flag(struct nl_context * nlctx,const char * flag)46 static void parser_err_invalid_flag(struct nl_context *nlctx, const char *flag)
47 {
48 fprintf(stderr, "ethtool (%s): flag '%s' for parameter '%s' is not followed by 'on' or 'off'\n",
49 nlctx->cmd, flag, nlctx->param);
50 }
51
__prefix_0x(const char * p)52 static bool __prefix_0x(const char *p)
53 {
54 return p[0] == '0' && (p[1] == 'x' || p[1] == 'X');
55 }
56
parse_float(const char * arg,float * result,float min,float max)57 static float parse_float(const char *arg, float *result, float min,
58 float max)
59 {
60 char *endptr;
61 float val;
62
63 if (!arg || !arg[0])
64 return -EINVAL;
65 val = strtof(arg, &endptr);
66 if (*endptr || val < min || val > max)
67 return -EINVAL;
68
69 *result = val;
70 return 0;
71 }
72
__parse_u32(const char * arg,uint32_t * result,uint32_t min,uint32_t max,int base)73 static int __parse_u32(const char *arg, uint32_t *result, uint32_t min,
74 uint32_t max, int base)
75 {
76 unsigned long long val;
77 char *endptr;
78
79 if (!arg || !arg[0])
80 return -EINVAL;
81 val = strtoul(arg, &endptr, base);
82 if (*endptr || val < min || val > max)
83 return -EINVAL;
84
85 *result = (uint32_t)val;
86 return 0;
87 }
88
parse_u32d(const char * arg,uint32_t * result)89 static int parse_u32d(const char *arg, uint32_t *result)
90 {
91 return __parse_u32(arg, result, 0, 0xffffffff, 10);
92 }
93
parse_x32(const char * arg,uint32_t * result)94 static int parse_x32(const char *arg, uint32_t *result)
95 {
96 return __parse_u32(arg, result, 0, 0xffffffff, 16);
97 }
98
parse_u32(const char * arg,uint32_t * result)99 int parse_u32(const char *arg, uint32_t *result)
100 {
101 if (!arg)
102 return -EINVAL;
103 if (__prefix_0x(arg))
104 return parse_x32(arg + 2, result);
105 else
106 return parse_u32d(arg, result);
107 }
108
parse_u8(const char * arg,uint8_t * result)109 static int parse_u8(const char *arg, uint8_t *result)
110 {
111 uint32_t val;
112 int ret = parse_u32(arg, &val);
113
114 if (ret < 0)
115 return ret;
116 if (val > UINT8_MAX)
117 return -EINVAL;
118
119 *result = (uint8_t)val;
120 return 0;
121 }
122
lookup_u32(const char * arg,uint32_t * result,const struct lookup_entry_u32 * tbl)123 static int lookup_u32(const char *arg, uint32_t *result,
124 const struct lookup_entry_u32 *tbl)
125 {
126 if (!arg)
127 return -EINVAL;
128 while (tbl->arg) {
129 if (!strcmp(tbl->arg, arg)) {
130 *result = tbl->val;
131 return 0;
132 }
133 tbl++;
134 }
135
136 return -EINVAL;
137 }
138
lookup_u8(const char * arg,uint8_t * result,const struct lookup_entry_u8 * tbl)139 static int lookup_u8(const char *arg, uint8_t *result,
140 const struct lookup_entry_u8 *tbl)
141 {
142 if (!arg)
143 return -EINVAL;
144 while (tbl->arg) {
145 if (!strcmp(tbl->arg, arg)) {
146 *result = tbl->val;
147 return 0;
148 }
149 tbl++;
150 }
151
152 return -EINVAL;
153 }
154
155 /* Parser handler for a flag. Expects a name (with no additional argument),
156 * generates NLA_FLAG or sets a bool (if the name was present).
157 */
nl_parse_flag(struct nl_context * nlctx __maybe_unused,uint16_t type,const void * data __maybe_unused,struct nl_msg_buff * msgbuff,void * dest)158 int nl_parse_flag(struct nl_context *nlctx __maybe_unused, uint16_t type,
159 const void *data __maybe_unused, struct nl_msg_buff *msgbuff,
160 void *dest)
161 {
162 if (dest)
163 *(bool *)dest = true;
164 return (type && ethnla_put_flag(msgbuff, type, true)) ? -EMSGSIZE : 0;
165 }
166
167 /* Parser handler for null terminated string. Expects a string argument,
168 * generates NLA_NUL_STRING or fills const char *
169 */
nl_parse_string(struct nl_context * nlctx,uint16_t type,const void * data __maybe_unused,struct nl_msg_buff * msgbuff,void * dest)170 int nl_parse_string(struct nl_context *nlctx, uint16_t type,
171 const void *data __maybe_unused,
172 struct nl_msg_buff *msgbuff, void *dest)
173 {
174 const char *arg = *nlctx->argp;
175
176 nlctx->argp++;
177 nlctx->argc--;
178
179 if (dest)
180 *(const char **)dest = arg;
181 return (type && ethnla_put_strz(msgbuff, type, arg)) ? -EMSGSIZE : 0;
182 }
183
184 /* Parser handler for unsigned 32-bit integer. Expects a numeric argument
185 * (may use 0x prefix), generates NLA_U32 or fills an uint32_t.
186 */
nl_parse_direct_u32(struct nl_context * nlctx,uint16_t type,const void * data __maybe_unused,struct nl_msg_buff * msgbuff,void * dest)187 int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
188 const void *data __maybe_unused,
189 struct nl_msg_buff *msgbuff, void *dest)
190 {
191 const char *arg = *nlctx->argp;
192 uint32_t val;
193 int ret;
194
195 nlctx->argp++;
196 nlctx->argc--;
197 ret = parse_u32(arg, &val);
198 if (ret < 0) {
199 parser_err_invalid_value(nlctx, arg);
200 return ret;
201 }
202
203 if (dest)
204 *(uint32_t *)dest = val;
205 return (type && ethnla_put_u32(msgbuff, type, val)) ? -EMSGSIZE : 0;
206 }
207
208 /* Parser handler for unsigned 32-bit integer. Expects a numeric argument
209 * (may use 0x prefix), generates NLA_U32 or fills an uint32_t.
210 */
nl_parse_direct_u8(struct nl_context * nlctx,uint16_t type,const void * data __maybe_unused,struct nl_msg_buff * msgbuff,void * dest)211 int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
212 const void *data __maybe_unused,
213 struct nl_msg_buff *msgbuff, void *dest)
214 {
215 const char *arg = *nlctx->argp;
216 uint8_t val;
217 int ret;
218
219 nlctx->argp++;
220 nlctx->argc--;
221 ret = parse_u8(arg, &val);
222 if (ret < 0) {
223 parser_err_invalid_value(nlctx, arg);
224 return ret;
225 }
226
227 if (dest)
228 *(uint8_t *)dest = val;
229 return (type && ethnla_put_u8(msgbuff, type, val)) ? -EMSGSIZE : 0;
230 }
231
232 /* Parser handler for float meters and convert it to cm. Generates
233 * NLA_U32 or fills an uint32_t.
234 */
nl_parse_direct_m2cm(struct nl_context * nlctx,uint16_t type,const void * data __maybe_unused,struct nl_msg_buff * msgbuff,void * dest)235 int nl_parse_direct_m2cm(struct nl_context *nlctx, uint16_t type,
236 const void *data __maybe_unused,
237 struct nl_msg_buff *msgbuff, void *dest)
238 {
239 const char *arg = *nlctx->argp;
240 float meters = 0.0;
241 uint32_t cm;
242 int ret;
243
244 nlctx->argp++;
245 nlctx->argc--;
246 ret = parse_float(arg, &meters, 0, 150);
247 if (ret < 0) {
248 parser_err_invalid_value(nlctx, arg);
249 return ret;
250 }
251
252 cm = (uint32_t)(meters * 100 + 0.5);
253 if (dest)
254 *(uint32_t *)dest = cm;
255 return (type && ethnla_put_u32(msgbuff, type, cm)) ? -EMSGSIZE : 0;
256 }
257
258 /* Parser handler for (tri-state) bool. Expects "name on|off", generates
259 * NLA_U8 which is 1 for "on" and 0 for "off".
260 */
nl_parse_u8bool(struct nl_context * nlctx,uint16_t type,const void * data __maybe_unused,struct nl_msg_buff * msgbuff,void * dest)261 int nl_parse_u8bool(struct nl_context *nlctx, uint16_t type,
262 const void *data __maybe_unused,
263 struct nl_msg_buff *msgbuff, void *dest)
264 {
265 const char *arg = *nlctx->argp;
266 int ret;
267
268 nlctx->argp++;
269 nlctx->argc--;
270 if (!strcmp(arg, "on")) {
271 if (dest)
272 *(uint8_t *)dest = 1;
273 ret = type ? ethnla_put_u8(msgbuff, type, 1) : 0;
274 } else if (!strcmp(arg, "off")) {
275 if (dest)
276 *(uint8_t *)dest = 0;
277 ret = type ? ethnla_put_u8(msgbuff, type, 0) : 0;
278 } else {
279 parser_err_invalid_value(nlctx, arg);
280 return -EINVAL;
281 }
282
283 return ret ? -EMSGSIZE : 0;
284 }
285
286 /* Parser handler for 32-bit lookup value. Expects a string argument, looks it
287 * up in a table, generates NLA_U32 or fills uint32_t variable. The @data
288 * parameter is a null terminated array of struct lookup_entry_u32.
289 */
nl_parse_lookup_u32(struct nl_context * nlctx,uint16_t type,const void * data,struct nl_msg_buff * msgbuff,void * dest)290 int nl_parse_lookup_u32(struct nl_context *nlctx, uint16_t type,
291 const void *data, struct nl_msg_buff *msgbuff,
292 void *dest)
293 {
294 const char *arg = *nlctx->argp;
295 uint32_t val;
296 int ret;
297
298 nlctx->argp++;
299 nlctx->argc--;
300 ret = lookup_u32(arg, &val, data);
301 if (ret < 0) {
302 parser_err_invalid_value(nlctx, arg);
303 return ret;
304 }
305
306 if (dest)
307 *(uint32_t *)dest = val;
308 return (type && ethnla_put_u32(msgbuff, type, val)) ? -EMSGSIZE : 0;
309 }
310
311 /* Parser handler for 8-bit lookup value. Expects a string argument, looks it
312 * up in a table, generates NLA_U8 or fills uint8_t variable. The @data
313 * parameter is a null terminated array of struct lookup_entry_u8.
314 */
nl_parse_lookup_u8(struct nl_context * nlctx,uint16_t type,const void * data,struct nl_msg_buff * msgbuff,void * dest)315 int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type,
316 const void *data, struct nl_msg_buff *msgbuff,
317 void *dest)
318 {
319 const char *arg = *nlctx->argp;
320 uint8_t val;
321 int ret;
322
323 nlctx->argp++;
324 nlctx->argc--;
325 ret = lookup_u8(arg, &val, data);
326 if (ret < 0) {
327 parser_err_invalid_value(nlctx, arg);
328 return ret;
329 }
330
331 if (dest)
332 *(uint8_t *)dest = val;
333 return (type && ethnla_put_u8(msgbuff, type, val)) ? -EMSGSIZE : 0;
334 }
335
336 /* number of significant bits */
__nsb(uint32_t x)337 static unsigned int __nsb(uint32_t x)
338 {
339 unsigned int ret = 0;
340
341 if (x & 0xffff0000U) {
342 x >>= 16;
343 ret += 16;
344 }
345 if (x & 0xff00U) {
346 x >>= 8;
347 ret += 8;
348 }
349 if (x & 0xf0U) {
350 x >>= 4;
351 ret += 4;
352 }
353 if (x & 0xcU) {
354 x >>= 2;
355 ret += 2;
356 }
357 if (x & 0x2U) {
358 x >>= 1;
359 ret += 1;
360 }
361
362 return ret + x;
363 }
364
__is_hex(char c)365 static bool __is_hex(char c)
366 {
367 if (isdigit(c))
368 return true;
369 else
370 return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
371 }
372
__hex_val(char c)373 static unsigned int __hex_val(char c)
374 {
375 if (c >= '0' && c <= '9')
376 return c - '0';
377 if (c >= 'a' && c <= 'f')
378 return c - 'a' + 0xa;
379 if (c >= 'A' && c <= 'F')
380 return c - 'A' + 0xa;
381 return 0;
382 }
383
__bytestr_delim(const char * p,char delim)384 static bool __bytestr_delim(const char *p, char delim)
385 {
386 return !*p || (delim ? (*p == delim) : !__is_hex(*p));
387 }
388
389 /* Parser handler for generic byte string in MAC-like format. Expects string
390 * argument in the "[[:xdigit:]]{2}(:[[:xdigit:]]{2})*" format, generates
391 * NLA_BINARY or fills a struct byte_str_value (if @dest is not null and the
392 * handler succeeds, caller is responsible for freeing the value). The @data
393 * parameter points to struct byte_str_parser_data.
394 */
nl_parse_byte_str(struct nl_context * nlctx,uint16_t type,const void * data,struct nl_msg_buff * msgbuff,void * dest)395 int nl_parse_byte_str(struct nl_context *nlctx, uint16_t type, const void *data,
396 struct nl_msg_buff *msgbuff, void *dest)
397 {
398 const struct byte_str_parser_data *pdata = data;
399 struct byte_str_value *dest_value = dest;
400 const char *arg = *nlctx->argp;
401 uint8_t *val = NULL;
402 unsigned int len, i;
403 const char *p;
404 int ret;
405
406 nlctx->argp++;
407 nlctx->argc--;
408
409 len = 0;
410 p = arg;
411 if (!*p)
412 goto err;
413 while (true) {
414 len++;
415 if (!__bytestr_delim(p, pdata->delim))
416 p++;
417 if (!__bytestr_delim(p, pdata->delim))
418 p++;
419 if (!__bytestr_delim(p, pdata->delim))
420 goto err;
421 if (!*p)
422 break;
423 p++;
424 if (*p && __bytestr_delim(p, pdata->delim))
425 goto err;
426 }
427 if (len < pdata->min_len || (pdata->max_len && len > pdata->max_len))
428 goto err;
429 val = malloc(len);
430 if (!val)
431 return -ENOMEM;
432
433 p = arg;
434 for (i = 0; i < len; i++) {
435 uint8_t byte = 0;
436
437 if (!__is_hex(*p))
438 goto err;
439 while (__is_hex(*p))
440 byte = 16 * byte + __hex_val(*p++);
441 if (!__bytestr_delim(p, pdata->delim))
442 goto err;
443 val[i] = byte;
444 if (*p)
445 p++;
446 }
447 ret = type ? ethnla_put(msgbuff, type, len, val) : 0;
448 if (dest) {
449 dest_value->len = len;
450 dest_value->data = val;
451 } else {
452 free(val);
453 }
454 return ret;
455
456 err:
457 free(val);
458 fprintf(stderr, "ethtool (%s): invalid value '%s' of parameter '%s'\n",
459 nlctx->cmd, arg, nlctx->param);
460 return -EINVAL;
461 }
462
463 /* Parser handler for parameters recognized for backward compatibility but
464 * supposed to fail without passing to kernel. Does not generate any netlink
465 * attributes of fill any variable. The @data parameter points to struct
466 * error_parser_params (error message, return value and number of extra
467 * arguments to skip).
468 */
nl_parse_error(struct nl_context * nlctx,uint16_t type __maybe_unused,const void * data,struct nl_msg_buff * msgbuff __maybe_unused,void * dest __maybe_unused)469 int nl_parse_error(struct nl_context *nlctx, uint16_t type __maybe_unused,
470 const void *data, struct nl_msg_buff *msgbuff __maybe_unused,
471 void *dest __maybe_unused)
472 {
473 const struct error_parser_data *parser_data = data;
474 unsigned int skip = parser_data->extra_args;
475
476 fprintf(stderr, "ethtool (%s): ", nlctx->cmd);
477 fprintf(stderr, parser_data->err_msg, nlctx->param);
478 if (nlctx->argc < skip) {
479 fprintf(stderr, "ethtool (%s): too few arguments for parameter '%s' (expected %u)\n",
480 nlctx->cmd, nlctx->param, skip);
481 } else {
482 nlctx->argp += skip;
483 nlctx->argc -= skip;
484 }
485
486 return parser_data->ret_val;
487 }
488
489 /* bitset parser handlers */
490
491 /* Return true if a bitset argument should be parsed as numeric, i.e.
492 * (a) it starts with '0x'
493 * (b) it consists only of hex digits and at most one slash which can be
494 * optionally followed by "0x"; if no_mask is true, slash is not allowed
495 */
is_numeric_bitset(const char * arg,bool no_mask)496 static bool is_numeric_bitset(const char *arg, bool no_mask)
497 {
498 const char *p = arg;
499 bool has_slash = false;
500
501 if (!arg)
502 return false;
503 if (__prefix_0x(arg))
504 return true;
505 while (*p) {
506 if (*p == '/') {
507 if (has_slash || no_mask)
508 return false;
509 has_slash = true;
510 p++;
511 if (__prefix_0x(p))
512 p += 2;
513 continue;
514 }
515 if (!__is_hex(*p))
516 return false;
517 p++;
518 }
519 return true;
520 }
521
522 #define __MAX_U32_DIGITS 10
523
524 /* Parse hex string (without leading "0x") into a bitmap consisting of 32-bit
525 * words. Caller must make sure arg is at least len characters long and dst has
526 * place for at least (len + 7) / 8 32-bit words. If force_hex is false, allow
527 * also base 10 unsigned 32-bit value.
528 *
529 * Returns number of significant bits in the bitmap on success and negative
530 * value on error.
531 */
__parse_num_string(const char * arg,unsigned int len,uint32_t * dst,bool force_hex)532 static int __parse_num_string(const char *arg, unsigned int len, uint32_t *dst,
533 bool force_hex)
534 {
535 char buff[__MAX_U32_DIGITS + 1] = {};
536 unsigned int nbits = 0;
537 const char *p = arg;
538
539 if (!len)
540 return -EINVAL;
541 if (!force_hex && len <= __MAX_U32_DIGITS) {
542 strncpy(buff, arg, len);
543 if (!buff[__MAX_U32_DIGITS]) {
544 u32 val;
545 int ret;
546
547 ret = parse_u32d(buff, &val);
548 if (!ret) {
549 *dst = val;
550 return __nsb(val);
551 }
552 }
553 }
554
555 dst += (len - 1) / 8;
556 while (len > 0) {
557 unsigned int chunk = (len % 8) ?: 8;
558 unsigned long val;
559 char *endp;
560
561 memcpy(buff, p, chunk);
562 buff[chunk] = '\0';
563 val = strtoul(buff, &endp, 16);
564 if (*endp)
565 return -EINVAL;
566 *dst-- = (uint32_t)val;
567 if (nbits)
568 nbits += 4 * chunk;
569 else
570 nbits = __nsb(val);
571
572 p += chunk;
573 len -= chunk;
574 }
575 return nbits;
576 }
577
578 /* Parse bitset provided as a base 16 numeric value (@no_mask is true) or pair
579 * of base 16 numeric values separated by '/' (@no_mask is false). The "0x"
580 * prefix is optional. Generates bitset nested attribute in compact form.
581 */
parse_numeric_bitset(struct nl_context * nlctx,uint16_t type,bool no_mask,bool force_hex,struct nl_msg_buff * msgbuff)582 static int parse_numeric_bitset(struct nl_context *nlctx, uint16_t type,
583 bool no_mask, bool force_hex,
584 struct nl_msg_buff *msgbuff)
585 {
586 unsigned int nwords, len1, len2;
587 const char *arg = *nlctx->argp;
588 bool force_hex1 = force_hex;
589 bool force_hex2 = force_hex;
590 uint32_t *value = NULL;
591 uint32_t *mask = NULL;
592 struct nlattr *nest;
593 const char *maskptr;
594 int ret = 0;
595 int nbits;
596
597 if (__prefix_0x(arg)) {
598 force_hex1 = true;
599 arg += 2;
600 }
601
602 maskptr = strchr(arg, '/');
603 if (maskptr && no_mask) {
604 parser_err_invalid_value(nlctx, arg);
605 return -EINVAL;
606 }
607 len1 = maskptr ? (unsigned int)(maskptr - arg) : strlen(arg);
608 nwords = DIV_ROUND_UP(len1, 8);
609 nbits = 0;
610
611 if (maskptr) {
612 maskptr++;
613 if (__prefix_0x(maskptr)) {
614 maskptr += 2;
615 force_hex2 = true;
616 }
617 len2 = strlen(maskptr);
618 if (len2 > len1)
619 nwords = DIV_ROUND_UP(len2, 8);
620 mask = calloc(nwords, sizeof(uint32_t));
621 if (!mask)
622 return -ENOMEM;
623 ret = __parse_num_string(maskptr, strlen(maskptr), mask,
624 force_hex2);
625 if (ret < 0) {
626 parser_err_invalid_value(nlctx, arg);
627 goto out_free;
628 }
629 nbits = ret;
630 }
631
632 value = calloc(nwords, sizeof(uint32_t));
633 if (!value) {
634 free(mask);
635 return -ENOMEM;
636 }
637 ret = __parse_num_string(arg, len1, value, force_hex1);
638 if (ret < 0) {
639 parser_err_invalid_value(nlctx, arg);
640 goto out_free;
641 }
642 nbits = (nbits < ret) ? ret : nbits;
643 nwords = (nbits + 31) / 32;
644
645 ret = 0;
646 if (!type)
647 goto out_free;
648 ret = -EMSGSIZE;
649 nest = ethnla_nest_start(msgbuff, type);
650 if (!nest)
651 goto out_free;
652 if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, !mask) ||
653 ethnla_put_u32(msgbuff, ETHTOOL_A_BITSET_SIZE, nbits) ||
654 ethnla_put(msgbuff, ETHTOOL_A_BITSET_VALUE,
655 nwords * sizeof(uint32_t), value) ||
656 (mask &&
657 ethnla_put(msgbuff, ETHTOOL_A_BITSET_MASK,
658 nwords * sizeof(uint32_t), mask)))
659 goto out_free;
660 ethnla_nest_end(msgbuff, nest);
661 ret = 0;
662
663 out_free:
664 free(value);
665 free(mask);
666 nlctx->argp++;
667 nlctx->argc--;
668 return ret;
669 }
670
671 /* Parse bitset provided as series of "name on|off" pairs (@no_mask is false)
672 * or names (@no_mask is true). Generates bitset nested attribute in verbose
673 * form with names from command line.
674 */
parse_name_bitset(struct nl_context * nlctx,uint16_t type,bool no_mask,struct nl_msg_buff * msgbuff)675 static int parse_name_bitset(struct nl_context *nlctx, uint16_t type,
676 bool no_mask, struct nl_msg_buff *msgbuff)
677 {
678 struct nlattr *bitset_attr;
679 struct nlattr *bits_attr;
680 struct nlattr *bit_attr;
681 int ret;
682
683 bitset_attr = ethnla_nest_start(msgbuff, type);
684 if (!bitset_attr)
685 return -EMSGSIZE;
686 ret = -EMSGSIZE;
687 if (no_mask && ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true))
688 goto err;
689 bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS);
690 if (!bits_attr)
691 goto err;
692
693 while (nlctx->argc > 0) {
694 bool bit_val = true;
695
696 if (!strcmp(*nlctx->argp, "--")) {
697 nlctx->argp++;
698 nlctx->argc--;
699 break;
700 }
701 ret = -EINVAL;
702 if (!no_mask) {
703 if (nlctx->argc < 2 ||
704 (strcmp(nlctx->argp[1], "on") &&
705 strcmp(nlctx->argp[1], "off"))) {
706 parser_err_invalid_flag(nlctx, *nlctx->argp);
707 goto err;
708 }
709 bit_val = !strcmp(nlctx->argp[1], "on");
710 }
711
712 ret = -EMSGSIZE;
713 bit_attr = ethnla_nest_start(msgbuff,
714 ETHTOOL_A_BITSET_BITS_BIT);
715 if (!bit_attr)
716 goto err;
717 if (ethnla_put_strz(msgbuff, ETHTOOL_A_BITSET_BIT_NAME,
718 nlctx->argp[0]))
719 goto err;
720 if (!no_mask &&
721 ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_BIT_VALUE,
722 bit_val))
723 goto err;
724 ethnla_nest_end(msgbuff, bit_attr);
725
726 nlctx->argp += (no_mask ? 1 : 2);
727 nlctx->argc -= (no_mask ? 1 : 2);
728 }
729
730 ethnla_nest_end(msgbuff, bits_attr);
731 ethnla_nest_end(msgbuff, bitset_attr);
732 return 0;
733 err:
734 ethnla_nest_cancel(msgbuff, bitset_attr);
735 return ret;
736 }
737
is_char_bitset(const char * arg,const struct char_bitset_parser_data * data)738 static bool is_char_bitset(const char *arg,
739 const struct char_bitset_parser_data *data)
740 {
741 bool mask = (arg[0] == '+' || arg[0] == '-');
742 unsigned int i;
743 const char *p;
744
745 for (p = arg; *p; p++) {
746 if (*p == data->reset_char)
747 continue;
748 if (mask && (*p == '+' || *p == '-'))
749 continue;
750 for (i = 0; i < data->nbits; i++)
751 if (*p == data->bit_chars[i])
752 goto found;
753 return false;
754 found:
755 ;
756 }
757
758 return true;
759 }
760
761 /* Parse bitset provided as a string consisting of characters corresponding to
762 * bit indices. The "reset character" resets the no-mask bitset to empty. If
763 * the first character is '+' or '-', generated bitset has mask and '+' and
764 * '-' switch between enabling and disabling the following bits (i.e. their
765 * value being true/false). In such case, "reset character" is not allowed.
766 */
parse_char_bitset(struct nl_context * nlctx,uint16_t type,const struct char_bitset_parser_data * data,struct nl_msg_buff * msgbuff)767 static int parse_char_bitset(struct nl_context *nlctx, uint16_t type,
768 const struct char_bitset_parser_data *data,
769 struct nl_msg_buff *msgbuff)
770 {
771 const char *arg = *nlctx->argp;
772 struct nlattr *bitset_attr;
773 struct nlattr *saved_pos;
774 struct nlattr *bits_attr;
775 struct nlattr *bit_attr;
776 unsigned int idx;
777 bool val = true;
778 const char *p;
779 bool no_mask;
780 int ret;
781
782 no_mask = data->no_mask || !(arg[0] == '+' || arg[0] == '-');
783 bitset_attr = ethnla_nest_start(msgbuff, type);
784 if (!bitset_attr)
785 return -EMSGSIZE;
786 ret = -EMSGSIZE;
787 if (no_mask && ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true))
788 goto err;
789 bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS);
790 if (!bits_attr)
791 goto err;
792 saved_pos = mnl_nlmsg_get_payload_tail(msgbuff->nlhdr);
793
794 for (p = arg; *p; p++) {
795 if (*p == '+' || *p == '-') {
796 if (no_mask) {
797 parser_err_invalid_value(nlctx, arg);
798 ret = -EINVAL;
799 goto err;
800 }
801 val = (*p == '+');
802 continue;
803 }
804 if (*p == data->reset_char) {
805 if (no_mask) {
806 mnl_attr_nest_cancel(msgbuff->nlhdr, saved_pos);
807 continue;
808 }
809 fprintf(stderr, "ethtool (%s): invalid char '%c' in '%s' for parameter '%s'\n",
810 nlctx->cmd, *p, arg, nlctx->param);
811 ret = -EINVAL;
812 goto err;
813 }
814
815 for (idx = 0; idx < data->nbits; idx++) {
816 if (data->bit_chars[idx] == *p)
817 break;
818 }
819 if (idx >= data->nbits) {
820 fprintf(stderr, "ethtool (%s): invalid char '%c' in '%s' for parameter '%s'\n",
821 nlctx->cmd, *p, arg, nlctx->param);
822 ret = -EINVAL;
823 goto err;
824 }
825 bit_attr = ethnla_nest_start(msgbuff,
826 ETHTOOL_A_BITSET_BITS_BIT);
827 if (!bit_attr)
828 goto err;
829 if (ethnla_put_u32(msgbuff, ETHTOOL_A_BITSET_BIT_INDEX, idx))
830 goto err;
831 if (!no_mask &&
832 ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_BIT_VALUE, val))
833 goto err;
834 ethnla_nest_end(msgbuff, bit_attr);
835 }
836
837 ethnla_nest_end(msgbuff, bits_attr);
838 ethnla_nest_end(msgbuff, bitset_attr);
839 nlctx->argp++;
840 nlctx->argc--;
841 return 0;
842 err:
843 ethnla_nest_cancel(msgbuff, bitset_attr);
844 return ret;
845 }
846
847 /* Parser handler for bitset. Expects either a numeric value (base 16 or 10
848 * (unless force_hex is set)), optionally followed by '/' and another numeric
849 * value (mask, unless no_mask is set), or a series of "name on|off" pairs
850 * (no_mask not set) or names (no_mask set). In the latter case, names are
851 * passed on as they are and kernel performs their interpretation and
852 * validation. The @data parameter points to struct bitset_parser_data.
853 * Generates only a bitset nested attribute. Fails if @type is zero or @dest
854 * is not null.
855 */
nl_parse_bitset(struct nl_context * nlctx,uint16_t type,const void * data,struct nl_msg_buff * msgbuff,void * dest)856 int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data,
857 struct nl_msg_buff *msgbuff, void *dest)
858 {
859 const struct bitset_parser_data *parser_data = data;
860
861 if (!type || dest) {
862 fprintf(stderr, "ethtool (%s): internal error parsing '%s'\n",
863 nlctx->cmd, nlctx->param);
864 return -EFAULT;
865 }
866 if (is_numeric_bitset(*nlctx->argp, false))
867 return parse_numeric_bitset(nlctx, type, parser_data->no_mask,
868 parser_data->force_hex, msgbuff);
869 else
870 return parse_name_bitset(nlctx, type, parser_data->no_mask,
871 msgbuff);
872 }
873
874 /* Parser handler for bitset. Expects either a numeric value (base 10 or 16),
875 * optionally followed by '/' and another numeric value (mask, unless no_mask
876 * is set), or a string consisting of characters corresponding to bit indices.
877 * The @data parameter points to struct char_bitset_parser_data. Generates
878 * biset nested attribute. Fails if type is zero or if @dest is not null.
879 */
nl_parse_char_bitset(struct nl_context * nlctx,uint16_t type,const void * data,struct nl_msg_buff * msgbuff,void * dest)880 int nl_parse_char_bitset(struct nl_context *nlctx, uint16_t type,
881 const void *data, struct nl_msg_buff *msgbuff,
882 void *dest)
883 {
884 const struct char_bitset_parser_data *parser_data = data;
885
886 if (!type || dest) {
887 fprintf(stderr, "ethtool (%s): internal error parsing '%s'\n",
888 nlctx->cmd, nlctx->param);
889 return -EFAULT;
890 }
891 if (is_char_bitset(*nlctx->argp, data) ||
892 !is_numeric_bitset(*nlctx->argp, false))
893 return parse_char_bitset(nlctx, type, parser_data, msgbuff);
894 else
895 return parse_numeric_bitset(nlctx, type, parser_data->no_mask,
896 false, msgbuff);
897 }
898
899 /* parser implementation */
900
find_parser(const struct param_parser * params,const char * arg)901 static const struct param_parser *find_parser(const struct param_parser *params,
902 const char *arg)
903 {
904 const struct param_parser *parser;
905
906 for (parser = params; parser->arg; parser++)
907 if (!strcmp(arg, parser->arg))
908 return parser;
909 return NULL;
910 }
911
__parser_bit(const uint64_t * map,unsigned int idx)912 static bool __parser_bit(const uint64_t *map, unsigned int idx)
913 {
914 return map[idx / 64] & (1 << (idx % 64));
915 }
916
__parser_set(uint64_t * map,unsigned int idx)917 static void __parser_set(uint64_t *map, unsigned int idx)
918 {
919 map[idx / 64] |= (1 << (idx % 64));
920 }
921
__parser_set_group(const struct param_parser * params,uint64_t * map,unsigned int alt_group)922 static void __parser_set_group(const struct param_parser *params,
923 uint64_t *map, unsigned int alt_group)
924 {
925 const struct param_parser *parser;
926 unsigned int idx = 0;
927
928 for (parser = params; parser->arg; parser++, idx++)
929 if (parser->alt_group == alt_group)
930 __parser_set(map, idx);
931 }
932
933 struct tmp_buff {
934 struct nl_msg_buff *msgbuff;
935 unsigned int id;
936 unsigned int orig_len;
937 struct tmp_buff *next;
938 };
939
tmp_buff_find(struct tmp_buff * head,unsigned int id)940 static struct tmp_buff *tmp_buff_find(struct tmp_buff *head, unsigned int id)
941 {
942 struct tmp_buff *buff;
943
944 for (buff = head; buff; buff = buff->next)
945 if (buff->id == id)
946 break;
947
948 return buff;
949 }
950
tmp_buff_find_or_create(struct tmp_buff ** phead,unsigned int id)951 static struct tmp_buff *tmp_buff_find_or_create(struct tmp_buff **phead,
952 unsigned int id)
953 {
954 struct tmp_buff **pbuff;
955 struct tmp_buff *new_buff;
956
957 for (pbuff = phead; *pbuff; pbuff = &(*pbuff)->next)
958 if ((*pbuff)->id == id)
959 return *pbuff;
960
961 new_buff = malloc(sizeof(*new_buff));
962 if (!new_buff)
963 return NULL;
964 new_buff->id = id;
965 new_buff->msgbuff = malloc(sizeof(*new_buff->msgbuff));
966 if (!new_buff->msgbuff) {
967 free(new_buff);
968 return NULL;
969 }
970 msgbuff_init(new_buff->msgbuff);
971 new_buff->next = NULL;
972 *pbuff = new_buff;
973
974 return new_buff;
975 }
976
tmp_buff_destroy(struct tmp_buff * head)977 static void tmp_buff_destroy(struct tmp_buff *head)
978 {
979 struct tmp_buff *buff = head;
980 struct tmp_buff *next;
981
982 while (buff) {
983 next = buff->next;
984 if (buff->msgbuff) {
985 msgbuff_done(buff->msgbuff);
986 free(buff->msgbuff);
987 }
988 free(buff);
989 buff = next;
990 }
991 }
992
993 /* Main entry point of parser implementation.
994 * @nlctx: netlink context
995 * @params: array of struct param_parser describing expected arguments
996 * and their handlers; the array must be terminated by null
997 * element {}
998 * @dest: optional destination to copy parsed data to (at
999 * param_parser::offset)
1000 * @group_style: defines if identifiers in .group represent separate messages,
1001 * nested attributes or are not allowed
1002 * @msgbuffs: (only used for @group_style = PARSER_GROUP_MSG) array to store
1003 * pointers to composed messages; caller must make sure this
1004 * array is sufficient, i.e. that it has at least as many entries
1005 * as the number of different .group values in params array;
1006 * entries are filled from the start, remaining entries are not
1007 * modified; caller should zero initialize the array before
1008 * calling nl_parser()
1009 */
nl_parser(struct nl_context * nlctx,const struct param_parser * params,void * dest,enum parser_group_style group_style,struct nl_msg_buff ** msgbuffs)1010 int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
1011 void *dest, enum parser_group_style group_style,
1012 struct nl_msg_buff **msgbuffs)
1013 {
1014 struct nl_socket *nlsk = nlctx->ethnl_socket;
1015 const struct param_parser *parser;
1016 struct tmp_buff *buffs = NULL;
1017 unsigned int n_msgbuffs = 0;
1018 struct tmp_buff *buff;
1019 unsigned int n_params;
1020 uint64_t *params_seen;
1021 int ret;
1022
1023 n_params = 0;
1024 for (parser = params; parser->arg; parser++) {
1025 struct nl_msg_buff *msgbuff;
1026 struct nlattr *nest;
1027
1028 n_params++;
1029 if (group_style == PARSER_GROUP_NONE || !parser->group)
1030 continue;
1031 ret = -ENOMEM;
1032 buff = tmp_buff_find_or_create(&buffs, parser->group);
1033 if (!buff)
1034 goto out_free_buffs;
1035 msgbuff = buff->msgbuff;
1036 ret = msg_init(nlctx, msgbuff, parser->group,
1037 NLM_F_REQUEST | NLM_F_ACK);
1038 if (ret < 0)
1039 goto out_free_buffs;
1040
1041 switch (group_style) {
1042 case PARSER_GROUP_NEST:
1043 ret = -EMSGSIZE;
1044 nest = ethnla_nest_start(buff->msgbuff, parser->group);
1045 if (!nest)
1046 goto out_free_buffs;
1047 break;
1048 case PARSER_GROUP_MSG:
1049 if (ethnla_fill_header(msgbuff,
1050 ETHTOOL_A_LINKINFO_HEADER,
1051 nlctx->devname, 0))
1052 goto out_free_buffs;
1053 break;
1054 default:
1055 break;
1056 }
1057
1058 buff->orig_len = msgbuff_len(msgbuff);
1059 }
1060 ret = -ENOMEM;
1061 params_seen = calloc(DIV_ROUND_UP(n_params, 64), sizeof(uint64_t));
1062 if (!params_seen)
1063 goto out_free_buffs;
1064
1065 while (nlctx->argc > 0) {
1066 struct nl_msg_buff *msgbuff;
1067 void *param_dest;
1068
1069 nlctx->param = *nlctx->argp;
1070 ret = -EINVAL;
1071 parser = find_parser(params, nlctx->param);
1072 if (!parser) {
1073 parser_err_unknown_param(nlctx);
1074 goto out_free;
1075 }
1076
1077 /* check duplicates and minimum number of arguments */
1078 if (__parser_bit(params_seen, parser - params)) {
1079 parser_err_dup_param(nlctx);
1080 goto out_free;
1081 }
1082 nlctx->argc--;
1083 nlctx->argp++;
1084 if (nlctx->argc < parser->min_argc) {
1085 parser_err_min_argc(nlctx, parser->min_argc);
1086 goto out_free;
1087 }
1088 if (parser->alt_group)
1089 __parser_set_group(params, params_seen,
1090 parser->alt_group);
1091 else
1092 __parser_set(params_seen, parser - params);
1093
1094 buff = NULL;
1095 if (parser->group)
1096 buff = tmp_buff_find(buffs, parser->group);
1097 msgbuff = buff ? buff->msgbuff : &nlsk->msgbuff;
1098
1099 param_dest = dest ? ((char *)dest + parser->dest_offset) : NULL;
1100 ret = parser->handler(nlctx, parser->type, parser->handler_data,
1101 msgbuff, param_dest);
1102 if (ret < 0)
1103 goto out_free;
1104 }
1105
1106 if (group_style == PARSER_GROUP_MSG) {
1107 ret = -EOPNOTSUPP;
1108 for (buff = buffs; buff; buff = buff->next)
1109 if (msgbuff_len(buff->msgbuff) > buff->orig_len &&
1110 netlink_cmd_check(nlctx->ctx, buff->id, false))
1111 goto out_free;
1112 }
1113 for (buff = buffs; buff; buff = buff->next) {
1114 struct nl_msg_buff *msgbuff = buff->msgbuff;
1115
1116 if (group_style == PARSER_GROUP_NONE ||
1117 msgbuff_len(msgbuff) == buff->orig_len)
1118 continue;
1119 switch (group_style) {
1120 case PARSER_GROUP_NEST:
1121 ethnla_nest_end(msgbuff, msgbuff->payload);
1122 ret = msgbuff_append(&nlsk->msgbuff, msgbuff);
1123 if (ret < 0)
1124 goto out_free;
1125 break;
1126 case PARSER_GROUP_MSG:
1127 msgbuffs[n_msgbuffs++] = msgbuff;
1128 buff->msgbuff = NULL;
1129 break;
1130 default:
1131 break;
1132 }
1133 }
1134
1135 ret = 0;
1136 out_free:
1137 free(params_seen);
1138 out_free_buffs:
1139 tmp_buff_destroy(buffs);
1140 return ret;
1141 }
1142