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