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