zones: add interface/subnet bound LOG rules
[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 }