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