options: treat time strings as UTC times
[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 time_t ts;
645 char *p;
646
647 year = strtoul(val, &p, 10);
648 if ((*p != '-' && *p) || year < 1970 || year > 2038)
649 goto fail;
650 else if (!*p)
651 goto ret;
652
653 mon = strtoul(++p, &p, 10);
654 if ((*p != '-' && *p) || mon > 12)
655 goto fail;
656 else if (!*p)
657 goto ret;
658
659 day = strtoul(++p, &p, 10);
660 if ((*p != 'T' && *p) || day > 31)
661 goto fail;
662 else if (!*p)
663 goto ret;
664
665 hour = strtoul(++p, &p, 10);
666 if ((*p != ':' && *p) || hour > 23)
667 goto fail;
668 else if (!*p)
669 goto ret;
670
671 min = strtoul(++p, &p, 10);
672 if ((*p != ':' && *p) || min > 59)
673 goto fail;
674 else if (!*p)
675 goto ret;
676
677 sec = strtoul(++p, &p, 10);
678 if (*p || sec > 59)
679 goto fail;
680
681 ret:
682 tm.tm_year = year - 1900;
683 tm.tm_mon = mon - 1;
684 tm.tm_mday = day;
685 tm.tm_hour = hour;
686 tm.tm_min = min;
687 tm.tm_sec = sec;
688
689 ts = mktime(&tm) - timezone;
690
691 if (ts >= 0)
692 {
693 gmtime_r(&ts, (struct tm *)ptr);
694 return true;
695 }
696
697 fail:
698 return false;
699 }
700
701 bool
702 fw3_parse_time(void *ptr, const char *val, bool is_list)
703 {
704 unsigned int hour = 0, min = 0, sec = 0;
705 char *p;
706
707 hour = strtoul(val, &p, 10);
708 if (*p != ':' || hour > 23)
709 goto fail;
710
711 min = strtoul(++p, &p, 10);
712 if ((*p != ':' && *p) || min > 59)
713 goto fail;
714 else if (!*p)
715 goto ret;
716
717 sec = strtoul(++p, &p, 10);
718 if (*p || sec > 59)
719 goto fail;
720
721 ret:
722 *((int *)ptr) = 60 * 60 * hour + 60 * min + sec;
723 return true;
724
725 fail:
726 return false;
727 }
728
729 bool
730 fw3_parse_weekdays(void *ptr, const char *val, bool is_list)
731 {
732 unsigned int w = 0;
733 char *p, *s;
734
735 if (*val == '!')
736 {
737 fw3_setbit(*(uint8_t *)ptr, 0);
738 while (isspace(*++val));
739 }
740
741 if (!(s = strdup(val)))
742 return false;
743
744 for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
745 {
746 if (!parse_enum(&w, p, weekdays, 1, 7))
747 {
748 w = strtoul(p, &p, 10);
749
750 if (*p || w < 1 || w > 7)
751 {
752 free(s);
753 return false;
754 }
755 }
756
757 fw3_setbit(*(uint8_t *)ptr, w);
758 }
759
760 free(s);
761 return true;
762 }
763
764 bool
765 fw3_parse_monthdays(void *ptr, const char *val, bool is_list)
766 {
767 unsigned int d;
768 char *p, *s;
769
770 if (*val == '!')
771 {
772 fw3_setbit(*(uint32_t *)ptr, 0);
773 while (isspace(*++val));
774 }
775
776 if (!(s = strdup(val)))
777 return false;
778
779 for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
780 {
781 d = strtoul(p, &p, 10);
782
783 if (*p || d < 1 || d > 31)
784 {
785 free(s);
786 return false;
787 }
788
789 fw3_setbit(*(uint32_t *)ptr, d);
790 }
791
792 free(s);
793 return true;
794 }
795
796 bool
797 fw3_parse_include_type(void *ptr, const char *val, bool is_list)
798 {
799 return parse_enum(ptr, val, include_types,
800 FW3_INC_TYPE_SCRIPT, FW3_INC_TYPE_RESTORE);
801 }
802
803 bool
804 fw3_parse_reflection_source(void *ptr, const char *val, bool is_list)
805 {
806 return parse_enum(ptr, val, reflection_sources,
807 FW3_REFLECTION_INTERNAL, FW3_REFLECTION_EXTERNAL);
808 }
809
810 bool
811 fw3_parse_mark(void *ptr, const char *val, bool is_list)
812 {
813 uint32_t n;
814 char *s, *e;
815 struct fw3_mark *m = ptr;
816
817 if (*val == '!')
818 {
819 m->invert = true;
820 while (isspace(*++val));
821 }
822
823 if ((s = strchr(val, '/')) != NULL)
824 *s++ = 0;
825
826 n = strtoul(val, &e, 0);
827
828 if (e == val || *e)
829 return false;
830
831 m->mark = n;
832 m->mask = 0xFFFFFFFF;
833
834 if (s)
835 {
836 n = strtoul(s, &e, 0);
837
838 if (e == s || *e)
839 return false;
840
841 m->mask = n;
842 }
843
844 m->set = true;
845 return true;
846 }
847
848 bool
849 fw3_parse_setmatch(void *ptr, const char *val, bool is_list)
850 {
851 struct fw3_setmatch *m = ptr;
852 char *p, *s;
853 int i;
854
855 if (*val == '!')
856 {
857 m->invert = true;
858 while (isspace(*++val));
859 }
860
861 if (!(s = strdup(val)))
862 return false;
863
864 if (!(p = strtok(s, " \t")))
865 {
866 free(s);
867 return false;
868 }
869
870 strncpy(m->name, p, sizeof(m->name));
871
872 for (i = 0, p = strtok(NULL, " \t,");
873 i < 3 && p != NULL;
874 i++, p = strtok(NULL, " \t,"))
875 {
876 if (!strncmp(p, "dest", 4) || !strncmp(p, "dst", 3))
877 m->dir[i] = "dst";
878 else if (!strncmp(p, "src", 3))
879 m->dir[i] = "src";
880 }
881
882 free(s);
883
884 m->set = true;
885 return true;
886 }
887
888 bool
889 fw3_parse_direction(void *ptr, const char *val, bool is_list)
890 {
891 bool *is_out = ptr;
892 bool valid = true;
893
894 if (!strcmp(val, "in") || !strcmp(val, "ingress"))
895 *is_out = false;
896 else if (!strcmp(val, "out") || !strcmp(val, "egress"))
897 *is_out = true;
898 else
899 valid = false;
900
901 return valid;
902 }
903
904 bool
905 fw3_parse_cthelper(void *ptr, const char *val, bool is_list)
906 {
907 struct fw3_cthelpermatch m = { };
908
909 if (*val == '!')
910 {
911 m.invert = true;
912 while (isspace(*++val));
913 }
914
915 if (*val)
916 {
917 m.set = true;
918 strncpy(m.name, val, sizeof(m.name) - 1);
919 put_value(ptr, &m, sizeof(m), is_list);
920 return true;
921 }
922
923 return false;
924 }
925
926 bool
927 fw3_parse_setentry(void *ptr, const char *val, bool is_list)
928 {
929 struct fw3_setentry e = { };
930
931 e.value = val;
932 put_value(ptr, &e, sizeof(e), is_list);
933
934 return true;
935 }
936
937
938 bool
939 fw3_parse_options(void *s, const struct fw3_option *opts,
940 struct uci_section *section)
941 {
942 char *p, *v;
943 bool known, inv;
944 struct uci_element *e, *l;
945 struct uci_option *o;
946 const struct fw3_option *opt;
947 struct list_head *dest;
948 bool valid = true;
949
950 uci_foreach_element(&section->options, e)
951 {
952 o = uci_to_option(e);
953 known = false;
954
955 for (opt = opts; opt->name; opt++)
956 {
957 if (!opt->parse)
958 continue;
959
960 if (strcmp(opt->name, e->name))
961 continue;
962
963 if (o->type == UCI_TYPE_LIST)
964 {
965 if (!opt->elem_size)
966 {
967 warn_elem(e, "must not be a list");
968 valid = false;
969 }
970 else
971 {
972 dest = (struct list_head *)((char *)s + opt->offset);
973
974 uci_foreach_element(&o->v.list, l)
975 {
976 if (!l->name)
977 continue;
978
979 if (!opt->parse(dest, l->name, true))
980 {
981 warn_elem(e, "has invalid value '%s'", l->name);
982 valid = false;
983 continue;
984 }
985 }
986 }
987 }
988 else
989 {
990 v = o->v.string;
991
992 if (!v)
993 continue;
994
995 if (!opt->elem_size)
996 {
997 if (!opt->parse((char *)s + opt->offset, o->v.string, false))
998 {
999 warn_elem(e, "has invalid value '%s'", o->v.string);
1000 valid = false;
1001 }
1002 }
1003 else
1004 {
1005 inv = false;
1006 dest = (struct list_head *)((char *)s + opt->offset);
1007
1008 for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
1009 {
1010 /* If we encounter a sole "!" token, assume that it
1011 * is meant to be part of the next token, so silently
1012 * skip it and remember the state... */
1013 if (!strcmp(p, "!"))
1014 {
1015 inv = true;
1016 continue;
1017 }
1018
1019 /* The previous token was a sole "!", rewind pointer
1020 * back by one byte to precede the value with an
1021 * exclamation mark which effectively turns
1022 * ("!", "foo") into ("!foo") */
1023 if (inv)
1024 {
1025 *--p = '!';
1026 inv = false;
1027 }
1028
1029 if (!opt->parse(dest, p, true))
1030 {
1031 warn_elem(e, "has invalid value '%s'", p);
1032 valid = false;
1033 continue;
1034 }
1035 }
1036
1037 /* The last token was a sole "!" without any subsequent
1038 * text, so pass it to the option parser as-is. */
1039 if (inv && !opt->parse(dest, "!", true))
1040 {
1041 warn_elem(e, "has invalid value '%s'", p);
1042 valid = false;
1043 }
1044 }
1045 }
1046
1047 known = true;
1048 break;
1049 }
1050
1051 if (!known)
1052 warn_elem(e, "is unknown");
1053 }
1054
1055 return valid;
1056 }
1057
1058
1059 bool
1060 fw3_parse_blob_options(void *s, const struct fw3_option *opts,
1061 struct blob_attr *a, const char *name)
1062 {
1063 char *p, *v, buf[16];
1064 bool known;
1065 unsigned rem, erem;
1066 struct blob_attr *o, *e;
1067 const struct fw3_option *opt;
1068 struct list_head *dest;
1069 bool valid = true;
1070
1071 blobmsg_for_each_attr(o, a, rem)
1072 {
1073 known = false;
1074
1075 for (opt = opts; opt->name; opt++)
1076 {
1077 if (!opt->parse)
1078 continue;
1079
1080 if (strcmp(opt->name, blobmsg_name(o)))
1081 continue;
1082
1083 if (blobmsg_type(o) == BLOBMSG_TYPE_ARRAY)
1084 {
1085 if (!opt->elem_size)
1086 {
1087 fprintf(stderr, "%s: '%s' must not be a list\n",
1088 name, opt->name);
1089
1090 valid = false;
1091 }
1092 else
1093 {
1094 dest = (struct list_head *)((char *)s + opt->offset);
1095
1096 blobmsg_for_each_attr(e, o, erem)
1097 {
1098 if (blobmsg_type(e) == BLOBMSG_TYPE_INT32) {
1099 snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(e));
1100 v = buf;
1101 } else {
1102 v = blobmsg_get_string(e);
1103 }
1104
1105 if (!opt->parse(dest, v, true))
1106 {
1107 fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1108 name, opt->name, v);
1109 valid = false;
1110 continue;
1111 }
1112 }
1113 }
1114 }
1115 else
1116 {
1117 if (blobmsg_type(o) == BLOBMSG_TYPE_INT32) {
1118 snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(o));
1119 v = buf;
1120 } else {
1121 v = blobmsg_get_string(o);
1122 }
1123
1124 if (!v)
1125 continue;
1126
1127 if (!opt->elem_size)
1128 {
1129 if (!opt->parse((char *)s + opt->offset, v, false))
1130 {
1131 fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1132 name, opt->name, v);
1133 valid = false;
1134 }
1135 }
1136 else
1137 {
1138 dest = (struct list_head *)((char *)s + opt->offset);
1139
1140 for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
1141 {
1142 if (!opt->parse(dest, p, true))
1143 {
1144 fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1145 name, opt->name, p);
1146 valid = false;
1147 continue;
1148 }
1149 }
1150 }
1151 }
1152
1153 known = true;
1154 break;
1155 }
1156
1157 if (!known && strcmp(blobmsg_name(o), "type"))
1158 fprintf(stderr, "%s: '%s' is unknown\n", name, blobmsg_name(o));
1159 }
1160
1161 return valid;
1162 }
1163
1164
1165 const char *
1166 fw3_address_to_string(struct fw3_address *address, bool allow_invert, bool as_cidr)
1167 {
1168 char *p, ip[INET6_ADDRSTRLEN];
1169 static char buf[INET6_ADDRSTRLEN * 2 + 2];
1170
1171 p = buf;
1172
1173 if (address->invert && allow_invert)
1174 p += sprintf(p, "!");
1175
1176 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1177 &address->address.v4, ip, sizeof(ip));
1178
1179 p += sprintf(p, "%s", ip);
1180
1181 if (address->range)
1182 {
1183 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1184 &address->mask.v4, ip, sizeof(ip));
1185
1186 p += sprintf(p, "-%s", ip);
1187 }
1188 else if (!as_cidr)
1189 {
1190 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1191 &address->mask.v4, ip, sizeof(ip));
1192
1193 p += sprintf(p, "/%s", ip);
1194 }
1195 else
1196 {
1197 p += sprintf(p, "/%u", fw3_netmask2bitlen(address->family,
1198 &address->mask.v6));
1199 }
1200
1201 return buf;
1202 }