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