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