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