Introduce new enum values for zone src policies and map src policy to dst_flags bitfi...
[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 }