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