ipsets: add support for specifying entries
[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 bool
924 fw3_parse_setentry(void *ptr, const char *val, bool is_list)
925 {
926 struct fw3_setentry e = { };
927
928 e.value = val;
929 put_value(ptr, &e, sizeof(e), is_list);
930
931 return true;
932 }
933
934
935 bool
936 fw3_parse_options(void *s, const struct fw3_option *opts,
937 struct uci_section *section)
938 {
939 char *p, *v;
940 bool known, inv;
941 struct uci_element *e, *l;
942 struct uci_option *o;
943 const struct fw3_option *opt;
944 struct list_head *dest;
945 bool valid = true;
946
947 uci_foreach_element(&section->options, e)
948 {
949 o = uci_to_option(e);
950 known = false;
951
952 for (opt = opts; opt->name; opt++)
953 {
954 if (!opt->parse)
955 continue;
956
957 if (strcmp(opt->name, e->name))
958 continue;
959
960 if (o->type == UCI_TYPE_LIST)
961 {
962 if (!opt->elem_size)
963 {
964 warn_elem(e, "must not be a list");
965 valid = false;
966 }
967 else
968 {
969 dest = (struct list_head *)((char *)s + opt->offset);
970
971 uci_foreach_element(&o->v.list, l)
972 {
973 if (!l->name)
974 continue;
975
976 if (!opt->parse(dest, l->name, true))
977 {
978 warn_elem(e, "has invalid value '%s'", l->name);
979 valid = false;
980 continue;
981 }
982 }
983 }
984 }
985 else
986 {
987 v = o->v.string;
988
989 if (!v)
990 continue;
991
992 if (!opt->elem_size)
993 {
994 if (!opt->parse((char *)s + opt->offset, o->v.string, false))
995 {
996 warn_elem(e, "has invalid value '%s'", o->v.string);
997 valid = false;
998 }
999 }
1000 else
1001 {
1002 inv = false;
1003 dest = (struct list_head *)((char *)s + opt->offset);
1004
1005 for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
1006 {
1007 /* If we encounter a sole "!" token, assume that it
1008 * is meant to be part of the next token, so silently
1009 * skip it and remember the state... */
1010 if (!strcmp(p, "!"))
1011 {
1012 inv = true;
1013 continue;
1014 }
1015
1016 /* The previous token was a sole "!", rewind pointer
1017 * back by one byte to precede the value with an
1018 * exclamation mark which effectively turns
1019 * ("!", "foo") into ("!foo") */
1020 if (inv)
1021 {
1022 *--p = '!';
1023 inv = false;
1024 }
1025
1026 if (!opt->parse(dest, p, true))
1027 {
1028 warn_elem(e, "has invalid value '%s'", p);
1029 valid = false;
1030 continue;
1031 }
1032 }
1033
1034 /* The last token was a sole "!" without any subsequent
1035 * text, so pass it to the option parser as-is. */
1036 if (inv && !opt->parse(dest, "!", true))
1037 {
1038 warn_elem(e, "has invalid value '%s'", p);
1039 valid = false;
1040 }
1041 }
1042 }
1043
1044 known = true;
1045 break;
1046 }
1047
1048 if (!known)
1049 warn_elem(e, "is unknown");
1050 }
1051
1052 return valid;
1053 }
1054
1055
1056 bool
1057 fw3_parse_blob_options(void *s, const struct fw3_option *opts,
1058 struct blob_attr *a, const char *name)
1059 {
1060 char *p, *v, buf[16];
1061 bool known;
1062 unsigned rem, erem;
1063 struct blob_attr *o, *e;
1064 const struct fw3_option *opt;
1065 struct list_head *dest;
1066 bool valid = true;
1067
1068 blobmsg_for_each_attr(o, a, rem)
1069 {
1070 known = false;
1071
1072 for (opt = opts; opt->name; opt++)
1073 {
1074 if (!opt->parse)
1075 continue;
1076
1077 if (strcmp(opt->name, blobmsg_name(o)))
1078 continue;
1079
1080 if (blobmsg_type(o) == BLOBMSG_TYPE_ARRAY)
1081 {
1082 if (!opt->elem_size)
1083 {
1084 fprintf(stderr, "%s: '%s' must not be a list\n",
1085 name, opt->name);
1086
1087 valid = false;
1088 }
1089 else
1090 {
1091 dest = (struct list_head *)((char *)s + opt->offset);
1092
1093 blobmsg_for_each_attr(e, o, erem)
1094 {
1095 if (blobmsg_type(e) == BLOBMSG_TYPE_INT32) {
1096 snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(e));
1097 v = buf;
1098 } else {
1099 v = blobmsg_get_string(e);
1100 }
1101
1102 if (!opt->parse(dest, v, true))
1103 {
1104 fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1105 name, opt->name, v);
1106 valid = false;
1107 continue;
1108 }
1109 }
1110 }
1111 }
1112 else
1113 {
1114 if (blobmsg_type(o) == BLOBMSG_TYPE_INT32) {
1115 snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(o));
1116 v = buf;
1117 } else {
1118 v = blobmsg_get_string(o);
1119 }
1120
1121 if (!v)
1122 continue;
1123
1124 if (!opt->elem_size)
1125 {
1126 if (!opt->parse((char *)s + opt->offset, v, false))
1127 {
1128 fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1129 name, opt->name, v);
1130 valid = false;
1131 }
1132 }
1133 else
1134 {
1135 dest = (struct list_head *)((char *)s + opt->offset);
1136
1137 for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
1138 {
1139 if (!opt->parse(dest, p, true))
1140 {
1141 fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1142 name, opt->name, p);
1143 valid = false;
1144 continue;
1145 }
1146 }
1147 }
1148 }
1149
1150 known = true;
1151 break;
1152 }
1153
1154 if (!known && strcmp(blobmsg_name(o), "type"))
1155 fprintf(stderr, "%s: '%s' is unknown\n", name, blobmsg_name(o));
1156 }
1157
1158 return valid;
1159 }
1160
1161
1162 const char *
1163 fw3_address_to_string(struct fw3_address *address, bool allow_invert, bool as_cidr)
1164 {
1165 char *p, ip[INET6_ADDRSTRLEN];
1166 static char buf[INET6_ADDRSTRLEN * 2 + 2];
1167
1168 p = buf;
1169
1170 if (address->invert && allow_invert)
1171 p += sprintf(p, "!");
1172
1173 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1174 &address->address.v4, ip, sizeof(ip));
1175
1176 p += sprintf(p, "%s", ip);
1177
1178 if (address->range)
1179 {
1180 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1181 &address->mask.v4, ip, sizeof(ip));
1182
1183 p += sprintf(p, "-%s", ip);
1184 }
1185 else if (!as_cidr)
1186 {
1187 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1188 &address->mask.v4, ip, sizeof(ip));
1189
1190 p += sprintf(p, "/%s", ip);
1191 }
1192 else
1193 {
1194 p += sprintf(p, "/%u", fw3_netmask2bitlen(address->family,
1195 &address->mask.v6));
1196 }
1197
1198 return buf;
1199 }