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