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