helpers: implement explicit CT helper assignment support
[project/firewall3.git] / options.c
1 /*
2 * firewall3 - 3rd OpenWrt UCI firewall implementation
3 *
4 * Copyright (C) 2013-2014 Jo-Philipp Wich <jo@mein.io>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "options.h"
20 #include "ubus.h"
21
22
23 static bool
24 put_value(void *ptr, void *val, int elem_size, bool is_list)
25 {
26 void *copy;
27
28 if (is_list)
29 {
30 copy = malloc(elem_size);
31
32 if (!copy)
33 return false;
34
35 memcpy(copy, val, elem_size);
36 list_add_tail((struct list_head *)copy, (struct list_head *)ptr);
37 return true;
38 }
39
40 memcpy(ptr, val, elem_size);
41 return false;
42 }
43
44 static bool
45 parse_enum(void *ptr, const char *val, const char **values, int min, int max)
46 {
47 int i, l = strlen(val);
48
49 if (l > 0)
50 {
51 for (i = 0; i <= (max - min); i++)
52 {
53 if (!strncasecmp(val, values[i], l))
54 {
55 *((int *)ptr) = min + i;
56 return true;
57 }
58 }
59 }
60
61 return false;
62 }
63
64
65 const char *fw3_flag_names[__FW3_FLAG_MAX] = {
66 "filter",
67 "nat",
68 "mangle",
69 "raw",
70
71 "IPv4",
72 "IPv6",
73
74 "ACCEPT",
75 "REJECT",
76 "DROP",
77 "NOTRACK",
78 "HELPER",
79 "MARK",
80 "DNAT",
81 "SNAT",
82 "MASQUERADE",
83
84 "ACCEPT",
85 "REJECT",
86 "DROP",
87 };
88
89 const char *fw3_limit_units[__FW3_LIMIT_UNIT_MAX] = {
90 "second",
91 "minute",
92 "hour",
93 "day",
94 };
95
96 const char *fw3_ipset_method_names[__FW3_IPSET_METHOD_MAX] = {
97 "(bug)",
98 "bitmap",
99 "hash",
100 "list",
101 };
102
103 const char *fw3_ipset_type_names[__FW3_IPSET_TYPE_MAX] = {
104 "(bug)",
105 "ip",
106 "port",
107 "mac",
108 "net",
109 "set",
110 };
111
112 static const char *weekdays[] = {
113 "monday",
114 "tuesday",
115 "wednesday",
116 "thursday",
117 "friday",
118 "saturday",
119 "sunday",
120 };
121
122 static const char *include_types[] = {
123 "script",
124 "restore",
125 };
126
127 static const char *reflection_sources[] = {
128 "internal",
129 "external",
130 };
131
132
133 bool
134 fw3_parse_bool(void *ptr, const char *val, bool is_list)
135 {
136 if (!strcmp(val, "true") || !strcmp(val, "yes") || !strcmp(val, "1"))
137 *((bool *)ptr) = true;
138 else
139 *((bool *)ptr) = false;
140
141 return true;
142 }
143
144 bool
145 fw3_parse_int(void *ptr, const char *val, bool is_list)
146 {
147 char *e;
148 int n = strtol(val, &e, 0);
149
150 if (e == val || *e)
151 return false;
152
153 *((int *)ptr) = n;
154
155 return true;
156 }
157
158 bool
159 fw3_parse_string(void *ptr, const char *val, bool is_list)
160 {
161 *((char **)ptr) = (char *)val;
162 return true;
163 }
164
165 bool
166 fw3_parse_target(void *ptr, const char *val, bool is_list)
167 {
168 return parse_enum(ptr, val, &fw3_flag_names[FW3_FLAG_ACCEPT],
169 FW3_FLAG_ACCEPT, FW3_FLAG_MASQUERADE);
170 }
171
172 bool
173 fw3_parse_limit(void *ptr, const char *val, bool is_list)
174 {
175 struct fw3_limit *limit = ptr;
176 enum fw3_limit_unit u = FW3_LIMIT_UNIT_SECOND;
177 char *e;
178 int n;
179
180 if (*val == '!')
181 {
182 limit->invert = true;
183 while (isspace(*++val));
184 }
185
186 n = strtol(val, &e, 10);
187
188 if (errno == ERANGE || errno == EINVAL)
189 return false;
190
191 if (*e && *e++ != '/')
192 return false;
193
194 if (!strlen(e))
195 return false;
196
197 if (!parse_enum(&u, e, fw3_limit_units, 0, FW3_LIMIT_UNIT_DAY))
198 return false;
199
200 limit->rate = n;
201 limit->unit = u;
202
203 return true;
204 }
205
206 bool
207 fw3_parse_device(void *ptr, const char *val, bool is_list)
208 {
209 char *p;
210 struct fw3_device dev = { };
211
212 if (*val == '*')
213 {
214 dev.set = true;
215 dev.any = true;
216 put_value(ptr, &dev, sizeof(dev), is_list);
217 return true;
218 }
219
220 if (*val == '!')
221 {
222 dev.invert = true;
223 while (isspace(*++val));
224 }
225
226 if ((p = strchr(val, '@')) != NULL)
227 {
228 *p++ = 0;
229 snprintf(dev.network, sizeof(dev.network), "%s", p);
230 }
231
232 if (*val)
233 snprintf(dev.name, sizeof(dev.name), "%s", val);
234 else
235 return false;
236
237 dev.set = true;
238 put_value(ptr, &dev, sizeof(dev), is_list);
239 return true;
240 }
241
242 bool
243 fw3_parse_address(void *ptr, const char *val, bool is_list)
244 {
245 struct fw3_address addr = { };
246 struct in_addr v4;
247 struct in6_addr v6;
248 char *p = NULL, *m = NULL, *s, *e;
249 int bits = -1;
250
251 if (*val == '!')
252 {
253 addr.invert = true;
254 while (isspace(*++val));
255 }
256
257 s = strdup(val);
258
259 if (!s)
260 return false;
261
262 if ((m = strchr(s, '/')) != NULL)
263 *m++ = 0;
264 else if ((p = strchr(s, '-')) != NULL)
265 *p++ = 0;
266
267 if (inet_pton(AF_INET6, s, &v6))
268 {
269 addr.family = FW3_FAMILY_V6;
270 addr.address.v6 = v6;
271
272 if (m)
273 {
274 if (!inet_pton(AF_INET6, m, &v6))
275 {
276 bits = strtol(m, &e, 10);
277
278 if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v6))
279 goto fail;
280 }
281
282 addr.mask.v6 = v6;
283 }
284 else if (p)
285 {
286 if (!inet_pton(AF_INET6, p, &addr.mask.v6))
287 goto fail;
288
289 addr.range = true;
290 }
291 else
292 {
293 memset(addr.mask.v6.s6_addr, 0xFF, 16);
294 }
295 }
296 else if (inet_pton(AF_INET, s, &v4))
297 {
298 addr.family = FW3_FAMILY_V4;
299 addr.address.v4 = v4;
300
301 if (m)
302 {
303 if (!inet_pton(AF_INET, m, &v4))
304 {
305 bits = strtol(m, &e, 10);
306
307 if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v4))
308 goto fail;
309 }
310
311 addr.mask.v4 = v4;
312 }
313 else if (p)
314 {
315 if (!inet_pton(AF_INET, p, &addr.mask.v4))
316 goto fail;
317
318 addr.range = true;
319 }
320 else
321 {
322 addr.mask.v4.s_addr = 0xFFFFFFFF;
323 }
324 }
325 else
326 {
327 goto fail;
328 }
329
330 free(s);
331 addr.set = true;
332 put_value(ptr, &addr, sizeof(addr), is_list);
333 return true;
334
335 fail:
336 free(s);
337 return false;
338 }
339
340 bool
341 fw3_parse_network(void *ptr, const char *val, bool is_list)
342 {
343 struct fw3_device dev = { };
344 struct fw3_address *addr, *tmp;
345 LIST_HEAD(addr_list);
346 int n_addrs;
347
348 if (!fw3_parse_address(ptr, val, is_list))
349 {
350 if (!fw3_parse_device(&dev, val, false))
351 return false;
352
353 n_addrs = fw3_ubus_address(&addr_list, dev.name);
354
355 list_for_each_entry(addr, &addr_list, list)
356 {
357 addr->invert = dev.invert;
358 addr->resolved = true;
359 }
360
361 /* add an empty address member with .set = false, .resolved = true
362 * to signal resolving failure to callers */
363 if (n_addrs == 0)
364 {
365 tmp = fw3_alloc(sizeof(*tmp));
366 tmp->resolved = true;
367
368 list_add_tail(&tmp->list, &addr_list);
369 }
370
371 if (is_list)
372 {
373 list_splice_tail(&addr_list, ptr);
374 }
375 else if (!list_empty(&addr_list))
376 {
377 memcpy(ptr, list_first_entry(&addr_list, typeof(*addr), list),
378 sizeof(*addr));
379
380 list_for_each_entry_safe(addr, tmp, &addr_list, list)
381 free(addr);
382 }
383 }
384
385 return true;
386 }
387
388 bool
389 fw3_parse_mac(void *ptr, const char *val, bool is_list)
390 {
391 struct fw3_mac addr = { };
392 struct ether_addr *mac;
393
394 if (*val == '!')
395 {
396 addr.invert = true;
397 while (isspace(*++val));
398 }
399
400 if ((mac = ether_aton(val)) != NULL)
401 {
402 addr.mac = *mac;
403 addr.set = true;
404
405 put_value(ptr, &addr, sizeof(addr), is_list);
406 return true;
407 }
408
409 return false;
410 }
411
412 bool
413 fw3_parse_port(void *ptr, const char *val, bool is_list)
414 {
415 struct fw3_port range = { };
416 uint16_t n;
417 uint16_t m;
418 char *p;
419
420 if (*val == '!')
421 {
422 range.invert = true;
423 while (isspace(*++val));
424 }
425
426 n = strtoul(val, &p, 10);
427
428 if (errno == ERANGE || errno == EINVAL)
429 return false;
430
431 if (*p && *p != '-' && *p != ':')
432 return false;
433
434 if (*p)
435 {
436 m = strtoul(++p, NULL, 10);
437
438 if (errno == ERANGE || errno == EINVAL || m < n)
439 return false;
440
441 range.port_min = n;
442 range.port_max = m;
443 }
444 else
445 {
446 range.port_min = n;
447 range.port_max = n;
448 }
449
450 range.set = true;
451 put_value(ptr, &range, sizeof(range), is_list);
452 return true;
453 }
454
455 bool
456 fw3_parse_family(void *ptr, const char *val, bool is_list)
457 {
458 if (!strcmp(val, "any") || !strcmp(val, "*"))
459 *((enum fw3_family *)ptr) = FW3_FAMILY_ANY;
460 else if (!strcmp(val, "inet") || strrchr(val, '4'))
461 *((enum fw3_family *)ptr) = FW3_FAMILY_V4;
462 else if (!strcmp(val, "inet6") || strrchr(val, '6'))
463 *((enum fw3_family *)ptr) = FW3_FAMILY_V6;
464 else
465 return false;
466
467 return true;
468 }
469
470 bool
471 fw3_parse_icmptype(void *ptr, const char *val, bool is_list)
472 {
473 struct fw3_icmptype icmp = { };
474 bool v4 = false;
475 bool v6 = false;
476 char *p;
477 int i;
478
479 for (i = 0; i < ARRAY_SIZE(fw3_icmptype_list_v4); i++)
480 {
481 if (!strcmp(val, fw3_icmptype_list_v4[i].name))
482 {
483 icmp.type = fw3_icmptype_list_v4[i].type;
484 icmp.code_min = fw3_icmptype_list_v4[i].code_min;
485 icmp.code_max = fw3_icmptype_list_v4[i].code_max;
486
487 v4 = true;
488 break;
489 }
490 }
491
492 for (i = 0; i < ARRAY_SIZE(fw3_icmptype_list_v6); i++)
493 {
494 if (!strcmp(val, fw3_icmptype_list_v6[i].name))
495 {
496 icmp.type6 = fw3_icmptype_list_v6[i].type;
497 icmp.code6_min = fw3_icmptype_list_v6[i].code_min;
498 icmp.code6_max = fw3_icmptype_list_v6[i].code_max;
499
500 v6 = true;
501 break;
502 }
503 }
504
505 if (!v4 && !v6)
506 {
507 i = strtoul(val, &p, 10);
508
509 if ((p == val) || (*p != '/' && *p != 0) || (i > 0xFF))
510 return false;
511
512 icmp.type = i;
513
514 if (*p == '/')
515 {
516 val = ++p;
517 i = strtoul(val, &p, 10);
518
519 if ((p == val) || (*p != 0) || (i > 0xFF))
520 return false;
521
522 icmp.code_min = i;
523 icmp.code_max = i;
524 }
525 else
526 {
527 icmp.code_min = 0;
528 icmp.code_max = 0xFF;
529 }
530
531 icmp.type6 = icmp.type;
532 icmp.code6_min = icmp.code_max;
533 icmp.code6_max = icmp.code_max;
534
535 v4 = true;
536 v6 = true;
537 }
538
539 icmp.family = (v4 && v6) ? FW3_FAMILY_ANY
540 : (v6 ? FW3_FAMILY_V6 : FW3_FAMILY_V4);
541
542 put_value(ptr, &icmp, sizeof(icmp), is_list);
543 return true;
544 }
545
546 bool
547 fw3_parse_protocol(void *ptr, const char *val, bool is_list)
548 {
549 struct fw3_protocol proto = { };
550 struct protoent *ent;
551 char *e;
552
553 if (*val == '!')
554 {
555 proto.invert = true;
556 while (isspace(*++val));
557 }
558
559 if (!strcmp(val, "all") || !strcmp(val, "any") || !strcmp(val, "*"))
560 {
561 proto.any = true;
562 put_value(ptr, &proto, sizeof(proto), is_list);
563 return true;
564 }
565 else if (!strcmp(val, "icmpv6"))
566 {
567 val = "ipv6-icmp";
568 }
569 else if (!strcmp(val, "tcpudp"))
570 {
571 proto.protocol = 6;
572 if (put_value(ptr, &proto, sizeof(proto), is_list))
573 {
574 proto.protocol = 17;
575 put_value(ptr, &proto, sizeof(proto), is_list);
576 }
577
578 return true;
579 }
580
581 ent = getprotobyname(val);
582
583 if (ent)
584 {
585 proto.protocol = ent->p_proto;
586 put_value(ptr, &proto, sizeof(proto), is_list);
587 return true;
588 }
589
590 proto.protocol = strtoul(val, &e, 10);
591
592 if ((e == val) || (*e != 0))
593 return false;
594
595 put_value(ptr, &proto, sizeof(proto), is_list);
596 return true;
597 }
598
599 bool
600 fw3_parse_ipset_method(void *ptr, const char *val, bool is_list)
601 {
602 return parse_enum(ptr, val, &fw3_ipset_method_names[FW3_IPSET_METHOD_BITMAP],
603 FW3_IPSET_METHOD_BITMAP, FW3_IPSET_METHOD_LIST);
604 }
605
606 bool
607 fw3_parse_ipset_datatype(void *ptr, const char *val, bool is_list)
608 {
609 struct fw3_ipset_datatype type = { };
610
611 type.dir = "src";
612
613 if (!strncmp(val, "dest_", 5))
614 {
615 val += 5;
616 type.dir = "dst";
617 }
618 else if (!strncmp(val, "dst_", 4))
619 {
620 val += 4;
621 type.dir = "dst";
622 }
623 else if (!strncmp(val, "src_", 4))
624 {
625 val += 4;
626 type.dir = "src";
627 }
628
629 if (parse_enum(&type.type, val, &fw3_ipset_type_names[FW3_IPSET_TYPE_IP],
630 FW3_IPSET_TYPE_IP, FW3_IPSET_TYPE_SET))
631 {
632 put_value(ptr, &type, sizeof(type), is_list);
633 return true;
634 }
635
636 return false;
637 }
638
639 bool
640 fw3_parse_date(void *ptr, const char *val, bool is_list)
641 {
642 unsigned int year = 1970, mon = 1, day = 1, hour = 0, min = 0, sec = 0;
643 struct tm tm = { 0 };
644 char *p;
645
646 year = strtoul(val, &p, 10);
647 if ((*p != '-' && *p) || year < 1970 || year > 2038)
648 goto fail;
649 else if (!*p)
650 goto ret;
651
652 mon = strtoul(++p, &p, 10);
653 if ((*p != '-' && *p) || mon > 12)
654 goto fail;
655 else if (!*p)
656 goto ret;
657
658 day = strtoul(++p, &p, 10);
659 if ((*p != 'T' && *p) || day > 31)
660 goto fail;
661 else if (!*p)
662 goto ret;
663
664 hour = strtoul(++p, &p, 10);
665 if ((*p != ':' && *p) || hour > 23)
666 goto fail;
667 else if (!*p)
668 goto ret;
669
670 min = strtoul(++p, &p, 10);
671 if ((*p != ':' && *p) || min > 59)
672 goto fail;
673 else if (!*p)
674 goto ret;
675
676 sec = strtoul(++p, &p, 10);
677 if (*p || sec > 59)
678 goto fail;
679
680 ret:
681 tm.tm_year = year - 1900;
682 tm.tm_mon = mon - 1;
683 tm.tm_mday = day;
684 tm.tm_hour = hour;
685 tm.tm_min = min;
686 tm.tm_sec = sec;
687
688 if (mktime(&tm) >= 0)
689 {
690 *((struct tm *)ptr) = tm;
691 return true;
692 }
693
694 fail:
695 return false;
696 }
697
698 bool
699 fw3_parse_time(void *ptr, const char *val, bool is_list)
700 {
701 unsigned int hour = 0, min = 0, sec = 0;
702 char *p;
703
704 hour = strtoul(val, &p, 10);
705 if (*p != ':' || hour > 23)
706 goto fail;
707
708 min = strtoul(++p, &p, 10);
709 if ((*p != ':' && *p) || min > 59)
710 goto fail;
711 else if (!*p)
712 goto ret;
713
714 sec = strtoul(++p, &p, 10);
715 if (*p || sec > 59)
716 goto fail;
717
718 ret:
719 *((int *)ptr) = 60 * 60 * hour + 60 * min + sec;
720 return true;
721
722 fail:
723 return false;
724 }
725
726 bool
727 fw3_parse_weekdays(void *ptr, const char *val, bool is_list)
728 {
729 unsigned int w = 0;
730 char *p, *s;
731
732 if (*val == '!')
733 {
734 fw3_setbit(*(uint8_t *)ptr, 0);
735 while (isspace(*++val));
736 }
737
738 if (!(s = strdup(val)))
739 return false;
740
741 for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
742 {
743 if (!parse_enum(&w, p, weekdays, 1, 7))
744 {
745 w = strtoul(p, &p, 10);
746
747 if (*p || w < 1 || w > 7)
748 {
749 free(s);
750 return false;
751 }
752 }
753
754 fw3_setbit(*(uint8_t *)ptr, w);
755 }
756
757 free(s);
758 return true;
759 }
760
761 bool
762 fw3_parse_monthdays(void *ptr, const char *val, bool is_list)
763 {
764 unsigned int d;
765 char *p, *s;
766
767 if (*val == '!')
768 {
769 fw3_setbit(*(uint32_t *)ptr, 0);
770 while (isspace(*++val));
771 }
772
773 if (!(s = strdup(val)))
774 return false;
775
776 for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
777 {
778 d = strtoul(p, &p, 10);
779
780 if (*p || d < 1 || d > 31)
781 {
782 free(s);
783 return false;
784 }
785
786 fw3_setbit(*(uint32_t *)ptr, d);
787 }
788
789 free(s);
790 return true;
791 }
792
793 bool
794 fw3_parse_include_type(void *ptr, const char *val, bool is_list)
795 {
796 return parse_enum(ptr, val, include_types,
797 FW3_INC_TYPE_SCRIPT, FW3_INC_TYPE_RESTORE);
798 }
799
800 bool
801 fw3_parse_reflection_source(void *ptr, const char *val, bool is_list)
802 {
803 return parse_enum(ptr, val, reflection_sources,
804 FW3_REFLECTION_INTERNAL, FW3_REFLECTION_EXTERNAL);
805 }
806
807 bool
808 fw3_parse_mark(void *ptr, const char *val, bool is_list)
809 {
810 uint32_t n;
811 char *s, *e;
812 struct fw3_mark *m = ptr;
813
814 if (*val == '!')
815 {
816 m->invert = true;
817 while (isspace(*++val));
818 }
819
820 if ((s = strchr(val, '/')) != NULL)
821 *s++ = 0;
822
823 n = strtoul(val, &e, 0);
824
825 if (e == val || *e)
826 return false;
827
828 m->mark = n;
829 m->mask = 0xFFFFFFFF;
830
831 if (s)
832 {
833 n = strtoul(s, &e, 0);
834
835 if (e == s || *e)
836 return false;
837
838 m->mask = n;
839 }
840
841 m->set = true;
842 return true;
843 }
844
845 bool
846 fw3_parse_setmatch(void *ptr, const char *val, bool is_list)
847 {
848 struct fw3_setmatch *m = ptr;
849 char *p, *s;
850 int i;
851
852 if (*val == '!')
853 {
854 m->invert = true;
855 while (isspace(*++val));
856 }
857
858 if (!(s = strdup(val)))
859 return false;
860
861 if (!(p = strtok(s, " \t")))
862 {
863 free(s);
864 return false;
865 }
866
867 strncpy(m->name, p, sizeof(m->name));
868
869 for (i = 0, p = strtok(NULL, " \t,");
870 i < 3 && p != NULL;
871 i++, p = strtok(NULL, " \t,"))
872 {
873 if (!strncmp(p, "dest", 4) || !strncmp(p, "dst", 3))
874 m->dir[i] = "dst";
875 else if (!strncmp(p, "src", 3))
876 m->dir[i] = "src";
877 }
878
879 free(s);
880
881 m->set = true;
882 return true;
883 }
884
885 bool
886 fw3_parse_direction(void *ptr, const char *val, bool is_list)
887 {
888 bool *is_out = ptr;
889 bool valid = true;
890
891 if (!strcmp(val, "in") || !strcmp(val, "ingress"))
892 *is_out = false;
893 else if (!strcmp(val, "out") || !strcmp(val, "egress"))
894 *is_out = true;
895 else
896 valid = false;
897
898 return valid;
899 }
900
901 bool
902 fw3_parse_cthelper(void *ptr, const char *val, bool is_list)
903 {
904 struct fw3_cthelpermatch m = { };
905
906 if (*val == '!')
907 {
908 m.invert = true;
909 while (isspace(*++val));
910 }
911
912 if (*val)
913 {
914 m.set = true;
915 strncpy(m.name, val, sizeof(m.name) - 1);
916 put_value(ptr, &m, sizeof(m), is_list);
917 return true;
918 }
919
920 return false;
921 }
922
923
924 bool
925 fw3_parse_options(void *s, const struct fw3_option *opts,
926 struct uci_section *section)
927 {
928 char *p, *v;
929 bool known, inv;
930 struct uci_element *e, *l;
931 struct uci_option *o;
932 const struct fw3_option *opt;
933 struct list_head *dest;
934 bool valid = true;
935
936 uci_foreach_element(&section->options, e)
937 {
938 o = uci_to_option(e);
939 known = false;
940
941 for (opt = opts; opt->name; opt++)
942 {
943 if (!opt->parse)
944 continue;
945
946 if (strcmp(opt->name, e->name))
947 continue;
948
949 if (o->type == UCI_TYPE_LIST)
950 {
951 if (!opt->elem_size)
952 {
953 warn_elem(e, "must not be a list");
954 valid = false;
955 }
956 else
957 {
958 dest = (struct list_head *)((char *)s + opt->offset);
959
960 uci_foreach_element(&o->v.list, l)
961 {
962 if (!l->name)
963 continue;
964
965 if (!opt->parse(dest, l->name, true))
966 {
967 warn_elem(e, "has invalid value '%s'", l->name);
968 valid = false;
969 continue;
970 }
971 }
972 }
973 }
974 else
975 {
976 v = o->v.string;
977
978 if (!v)
979 continue;
980
981 if (!opt->elem_size)
982 {
983 if (!opt->parse((char *)s + opt->offset, o->v.string, false))
984 {
985 warn_elem(e, "has invalid value '%s'", o->v.string);
986 valid = false;
987 }
988 }
989 else
990 {
991 inv = false;
992 dest = (struct list_head *)((char *)s + opt->offset);
993
994 for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
995 {
996 /* If we encounter a sole "!" token, assume that it
997 * is meant to be part of the next token, so silently
998 * skip it and remember the state... */
999 if (!strcmp(p, "!"))
1000 {
1001 inv = true;
1002 continue;
1003 }
1004
1005 /* The previous token was a sole "!", rewind pointer
1006 * back by one byte to precede the value with an
1007 * exclamation mark which effectively turns
1008 * ("!", "foo") into ("!foo") */
1009 if (inv)
1010 {
1011 *--p = '!';
1012 inv = false;
1013 }
1014
1015 if (!opt->parse(dest, p, true))
1016 {
1017 warn_elem(e, "has invalid value '%s'", p);
1018 valid = false;
1019 continue;
1020 }
1021 }
1022
1023 /* The last token was a sole "!" without any subsequent
1024 * text, so pass it to the option parser as-is. */
1025 if (inv && !opt->parse(dest, "!", true))
1026 {
1027 warn_elem(e, "has invalid value '%s'", p);
1028 valid = false;
1029 }
1030 }
1031 }
1032
1033 known = true;
1034 break;
1035 }
1036
1037 if (!known)
1038 warn_elem(e, "is unknown");
1039 }
1040
1041 return valid;
1042 }
1043
1044
1045 bool
1046 fw3_parse_blob_options(void *s, const struct fw3_option *opts,
1047 struct blob_attr *a, const char *name)
1048 {
1049 char *p, *v, buf[16];
1050 bool known;
1051 unsigned rem, erem;
1052 struct blob_attr *o, *e;
1053 const struct fw3_option *opt;
1054 struct list_head *dest;
1055 bool valid = true;
1056
1057 blobmsg_for_each_attr(o, a, rem)
1058 {
1059 known = false;
1060
1061 for (opt = opts; opt->name; opt++)
1062 {
1063 if (!opt->parse)
1064 continue;
1065
1066 if (strcmp(opt->name, blobmsg_name(o)))
1067 continue;
1068
1069 if (blobmsg_type(o) == BLOBMSG_TYPE_ARRAY)
1070 {
1071 if (!opt->elem_size)
1072 {
1073 fprintf(stderr, "%s: '%s' must not be a list\n",
1074 name, opt->name);
1075
1076 valid = false;
1077 }
1078 else
1079 {
1080 dest = (struct list_head *)((char *)s + opt->offset);
1081
1082 blobmsg_for_each_attr(e, o, erem)
1083 {
1084 if (blobmsg_type(e) == BLOBMSG_TYPE_INT32) {
1085 snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(e));
1086 v = buf;
1087 } else {
1088 v = blobmsg_get_string(e);
1089 }
1090
1091 if (!opt->parse(dest, v, true))
1092 {
1093 fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1094 name, opt->name, v);
1095 valid = false;
1096 continue;
1097 }
1098 }
1099 }
1100 }
1101 else
1102 {
1103 if (blobmsg_type(o) == BLOBMSG_TYPE_INT32) {
1104 snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(o));
1105 v = buf;
1106 } else {
1107 v = blobmsg_get_string(o);
1108 }
1109
1110 if (!v)
1111 continue;
1112
1113 if (!opt->elem_size)
1114 {
1115 if (!opt->parse((char *)s + opt->offset, v, false))
1116 {
1117 fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1118 name, opt->name, v);
1119 valid = false;
1120 }
1121 }
1122 else
1123 {
1124 dest = (struct list_head *)((char *)s + opt->offset);
1125
1126 for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
1127 {
1128 if (!opt->parse(dest, p, true))
1129 {
1130 fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1131 name, opt->name, p);
1132 valid = false;
1133 continue;
1134 }
1135 }
1136 }
1137 }
1138
1139 known = true;
1140 break;
1141 }
1142
1143 if (!known && strcmp(blobmsg_name(o), "type"))
1144 fprintf(stderr, "%s: '%s' is unknown\n", name, blobmsg_name(o));
1145 }
1146
1147 return valid;
1148 }
1149
1150
1151 const char *
1152 fw3_address_to_string(struct fw3_address *address, bool allow_invert, bool as_cidr)
1153 {
1154 char *p, ip[INET6_ADDRSTRLEN];
1155 static char buf[INET6_ADDRSTRLEN * 2 + 2];
1156
1157 p = buf;
1158
1159 if (address->invert && allow_invert)
1160 p += sprintf(p, "!");
1161
1162 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1163 &address->address.v4, ip, sizeof(ip));
1164
1165 p += sprintf(p, "%s", ip);
1166
1167 if (address->range)
1168 {
1169 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1170 &address->mask.v4, ip, sizeof(ip));
1171
1172 p += sprintf(p, "-%s", ip);
1173 }
1174 else if (!as_cidr)
1175 {
1176 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1177 &address->mask.v4, ip, sizeof(ip));
1178
1179 p += sprintf(p, "/%s", ip);
1180 }
1181 else
1182 {
1183 p += sprintf(p, "/%u", fw3_netmask2bitlen(address->family,
1184 &address->mask.v6));
1185 }
1186
1187 return buf;
1188 }