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 }