2 * firewall3 - 3rd OpenWrt UCI firewall implementation
4 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
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.
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.
21 const char *fw3_flag_names[FW3_DEFAULT_DROP_INVALID + 1] = {
39 fw3_parse_bool(void *ptr, const char *val)
41 if (!strcmp(val, "true") || !strcmp(val, "yes") || !strcmp(val, "1"))
42 *((bool *)ptr) = true;
44 *((bool *)ptr) = false;
50 fw3_parse_int(void *ptr, const char *val)
52 int n = strtol(val, NULL, 10);
54 if (errno == ERANGE || errno == EINVAL)
63 fw3_parse_string(void *ptr, const char *val)
65 *((char **)ptr) = (char *)val;
70 fw3_parse_target(void *ptr, const char *val)
72 if (!strcmp(val, "ACCEPT"))
74 *((enum fw3_target *)ptr) = FW3_TARGET_ACCEPT;
77 else if (!strcmp(val, "REJECT"))
79 *((enum fw3_target *)ptr) = FW3_TARGET_REJECT;
82 else if (!strcmp(val, "DROP"))
84 *((enum fw3_target *)ptr) = FW3_TARGET_DROP;
87 else if (!strcmp(val, "NOTRACK"))
89 *((enum fw3_target *)ptr) = FW3_TARGET_NOTRACK;
92 else if (!strcmp(val, "DNAT"))
94 *((enum fw3_target *)ptr) = FW3_TARGET_DNAT;
97 else if (!strcmp(val, "SNAT"))
99 *((enum fw3_target *)ptr) = FW3_TARGET_SNAT;
107 fw3_parse_limit(void *ptr, const char *val)
109 struct fw3_limit *limit = ptr;
110 enum fw3_limit_unit u = FW3_LIMIT_UNIT_SECOND;
116 limit->invert = true;
117 while (isspace(*++val));
120 n = strtol(val, &e, 10);
122 if (errno == ERANGE || errno == EINVAL)
125 if (*e && *e++ != '/')
131 if (!strncmp(e, "second", strlen(e)))
132 u = FW3_LIMIT_UNIT_SECOND;
133 else if (!strncmp(e, "minute", strlen(e)))
134 u = FW3_LIMIT_UNIT_MINUTE;
135 else if (!strncmp(e, "hour", strlen(e)))
136 u = FW3_LIMIT_UNIT_HOUR;
137 else if (!strncmp(e, "day", strlen(e)))
138 u = FW3_LIMIT_UNIT_DAY;
149 fw3_parse_device(void *ptr, const char *val)
151 struct fw3_device *dev = ptr;
163 while (isspace(*++val));
167 snprintf(dev->name, sizeof(dev->name), "%s", val);
176 fw3_parse_address(void *ptr, const char *val)
178 struct fw3_address *addr = ptr;
187 while (isspace(*++val));
195 if ((p = strchr(s, '/')) != NULL)
198 m = strtoul(p, &e, 10);
200 if ((e == p) || (*e != 0))
202 if (strchr(s, ':') || !inet_pton(AF_INET, p, &v4))
208 for (i = 0, m = 32; !(v4.s_addr & 1) && (i < 32); i++)
215 else if ((p = strchr(s, '-')) != NULL)
219 if (inet_pton(AF_INET6, p, &v6))
221 addr->family = FW3_FAMILY_V6;
222 addr->address2.v6 = v6;
225 else if (inet_pton(AF_INET, p, &v4))
227 addr->family = FW3_FAMILY_V4;
228 addr->address2.v4 = v4;
238 if (inet_pton(AF_INET6, s, &v6))
240 addr->family = FW3_FAMILY_V6;
241 addr->address.v6 = v6;
242 addr->mask = (m >= 0) ? m : 128;
244 else if (inet_pton(AF_INET, s, &v4))
246 addr->family = FW3_FAMILY_V4;
247 addr->address.v4 = v4;
248 addr->mask = (m >= 0) ? m : 32;
262 fw3_parse_mac(void *ptr, const char *val)
264 struct fw3_mac *addr = ptr;
265 struct ether_addr *mac;
270 while (isspace(*++val));
273 if ((mac = ether_aton(val)) != NULL)
284 fw3_parse_port(void *ptr, const char *val)
286 struct fw3_port *range = ptr;
293 range->invert = true;
294 while (isspace(*++val));
297 n = strtoul(val, &p, 10);
299 if (errno == ERANGE || errno == EINVAL)
302 if (*p && *p != '-' && *p != ':')
307 m = strtoul(++p, NULL, 10);
309 if (errno == ERANGE || errno == EINVAL || m < n)
326 fw3_parse_family(void *ptr, const char *val)
328 if (!strcmp(val, "any"))
329 *((enum fw3_family *)ptr) = FW3_FAMILY_ANY;
330 else if (!strcmp(val, "inet") || strrchr(val, '4'))
331 *((enum fw3_family *)ptr) = FW3_FAMILY_V4;
332 else if (!strcmp(val, "inet6") || strrchr(val, '6'))
333 *((enum fw3_family *)ptr) = FW3_FAMILY_V6;
341 fw3_parse_icmptype(void *ptr, const char *val)
343 struct fw3_icmptype *icmp = ptr;
349 for (i = 0; i < ARRAY_SIZE(fw3_icmptype_list_v4); i++)
351 if (!strcmp(val, fw3_icmptype_list_v4[i].name))
353 icmp->type = fw3_icmptype_list_v4[i].type;
354 icmp->code_min = fw3_icmptype_list_v4[i].code_min;
355 icmp->code_max = fw3_icmptype_list_v4[i].code_max;
362 for (i = 0; i < ARRAY_SIZE(fw3_icmptype_list_v6); i++)
364 if (!strcmp(val, fw3_icmptype_list_v6[i].name))
366 icmp->type6 = fw3_icmptype_list_v6[i].type;
367 icmp->code6_min = fw3_icmptype_list_v6[i].code_min;
368 icmp->code6_max = fw3_icmptype_list_v6[i].code_max;
377 i = strtoul(val, &p, 10);
379 if ((p == val) || (*p != '/' && *p != 0) || (i > 0xFF))
387 i = strtoul(val, &p, 10);
389 if ((p == val) || (*p != 0) || (i > 0xFF))
398 icmp->code_max = 0xFF;
401 icmp->type6 = icmp->type;
402 icmp->code6_min = icmp->code_max;
403 icmp->code6_max = icmp->code_max;
409 icmp->family = (v4 && v6) ? FW3_FAMILY_ANY
410 : (v6 ? FW3_FAMILY_V6 : FW3_FAMILY_V4);
416 fw3_parse_protocol(void *ptr, const char *val)
418 struct fw3_protocol *proto = ptr;
419 struct protoent *ent;
423 proto->invert = true;
424 while (isspace(*++val));
427 if (!strcmp(val, "all"))
432 else if (!strcmp(val, "icmpv6"))
437 ent = getprotobyname(val);
441 proto->protocol = ent->p_proto;
445 proto->protocol = strtoul(val, NULL, 10);
446 return (errno != ERANGE && errno != EINVAL);
450 fw3_parse_ipset_method(void *ptr, const char *val)
452 if (!strncmp(val, "bitmap", strlen(val)))
454 *((enum fw3_ipset_method *)ptr) = FW3_IPSET_METHOD_BITMAP;
457 else if (!strncmp(val, "hash", strlen(val)))
459 *((enum fw3_ipset_method *)ptr) = FW3_IPSET_METHOD_HASH;
462 else if (!strncmp(val, "list", strlen(val)))
464 *((enum fw3_ipset_method *)ptr) = FW3_IPSET_METHOD_LIST;
472 fw3_parse_ipset_datatype(void *ptr, const char *val)
474 struct fw3_ipset_datatype *type = ptr;
476 if (!strncmp(val, "dest_", 5))
481 else if (!strncmp(val, "dst_", 4))
486 else if (!strncmp(val, "src_", 4))
492 if (!strncmp(val, "ip", strlen(val)))
494 type->type = FW3_IPSET_TYPE_IP;
497 else if (!strncmp(val, "port", strlen(val)))
499 type->type = FW3_IPSET_TYPE_PORT;
502 else if (!strncmp(val, "mac", strlen(val)))
504 type->type = FW3_IPSET_TYPE_MAC;
507 else if (!strncmp(val, "net", strlen(val)))
509 type->type = FW3_IPSET_TYPE_NET;
512 else if (!strncmp(val, "set", strlen(val)))
514 type->type = FW3_IPSET_TYPE_SET;
522 fw3_parse_date(void *ptr, const char *val)
524 unsigned int year = 1970, mon = 1, day = 1, hour = 0, min = 0, sec = 0;
525 struct tm tm = { 0 };
528 year = strtoul(val, &p, 10);
529 if ((*p != '-' && *p) || year < 1970 || year > 2038)
534 mon = strtoul(++p, &p, 10);
535 if ((*p != '-' && *p) || mon > 12)
540 day = strtoul(++p, &p, 10);
541 if ((*p != 'T' && *p) || day > 31)
546 hour = strtoul(++p, &p, 10);
547 if ((*p != ':' && *p) || hour > 23)
552 min = strtoul(++p, &p, 10);
553 if ((*p != ':' && *p) || min > 59)
558 sec = strtoul(++p, &p, 10);
563 tm.tm_year = year - 1900;
570 if (mktime(&tm) >= 0)
572 *((struct tm *)ptr) = tm;
581 fw3_parse_time(void *ptr, const char *val)
583 unsigned int hour = 0, min = 0, sec = 0;
586 hour = strtoul(val, &p, 10);
587 if (*p != ':' || hour > 23)
590 min = strtoul(++p, &p, 10);
591 if ((*p != ':' && *p) || min > 59)
596 sec = strtoul(++p, &p, 10);
601 *((int *)ptr) = 60 * 60 * hour + 60 * min + sec;
609 fw3_parse_weekdays(void *ptr, const char *val)
616 setbit(*(uint8_t *)ptr, 0);
617 while (isspace(*++val));
620 for (p = strtok((char *)val, " \t"); p; p = strtok(NULL, " \t"))
622 if (!strncasecmp(p, "monday", strlen(p)))
624 else if (!strncasecmp(p, "tuesday", strlen(p)))
626 else if (!strncasecmp(p, "wednesday", strlen(p)))
628 else if (!strncasecmp(p, "thursday", strlen(p)))
630 else if (!strncasecmp(p, "friday", strlen(p)))
632 else if (!strncasecmp(p, "saturday", strlen(p)))
634 else if (!strncasecmp(p, "sunday", strlen(p)))
638 w = strtoul(p, &p, 10);
640 if (*p || w < 1 || w > 7)
644 setbit(*(uint8_t *)ptr, w);
651 fw3_parse_monthdays(void *ptr, const char *val)
658 setbit(*(uint32_t *)ptr, 0);
659 while (isspace(*++val));
662 for (p = strtok((char *)val, " \t"); p; p = strtok(NULL, " \t"))
664 d = strtoul(p, &p, 10);
666 if (*p || d < 1 || d > 31)
669 setbit(*(uint32_t *)ptr, d);
677 fw3_parse_options(void *s, const struct fw3_option *opts,
678 struct uci_section *section)
682 struct uci_element *e, *l;
683 struct uci_option *o;
684 const struct fw3_option *opt;
685 struct list_head *item;
686 struct list_head *dest;
688 uci_foreach_element(§ion->options, e)
690 o = uci_to_option(e);
693 for (opt = opts; opt->name; opt++)
698 if (strcmp(opt->name, e->name))
701 if (o->type == UCI_TYPE_LIST)
705 warn_elem(e, "must not be a list");
709 uci_foreach_element(&o->v.list, l)
714 item = malloc(opt->elem_size);
719 memset(item, 0, opt->elem_size);
721 if (!opt->parse(item, l->name))
723 warn_elem(e, "has invalid value '%s'", l->name);
728 dest = (struct list_head *)((char *)s + opt->offset);
729 list_add_tail(item, dest);
740 if (!opt->parse((char *)s + opt->offset, o->v.string))
741 warn_elem(e, "has invalid value '%s'", o->v.string);
745 for (p = strtok(o->v.string, " \t");
747 p = strtok(NULL, " \t"))
749 item = malloc(opt->elem_size);
754 memset(item, 0, opt->elem_size);
756 if (!opt->parse(item, p))
758 warn_elem(e, "has invalid value '%s'", p);
763 dest = (struct list_head *)((char *)s + opt->offset);
764 list_add_tail(item, dest);
774 warn_elem(e, "is unknown");
780 fw3_format_in_out(struct fw3_device *in, struct fw3_device *out)
783 fw3_pr(" %s-i %s", in->invert ? "! " : "", in->name);
785 if (out && !out->any)
786 fw3_pr(" %s-o %s", out->invert ? "! " : "", out->name);
790 fw3_format_src_dest(struct fw3_address *src, struct fw3_address *dest)
792 char s[INET6_ADDRSTRLEN];
794 if ((src && src->range) || (dest && dest->range))
795 fw3_pr(" -m iprange");
801 inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
802 &src->address.v4, s, sizeof(s));
804 fw3_pr(" %s--src-range %s", src->invert ? "! " : "", s);
806 inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
807 &src->address2.v4, s, sizeof(s));
813 inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
814 &src->address.v4, s, sizeof(s));
816 fw3_pr(" %s-s %s/%u", src->invert ? "! " : "", s, src->mask);
820 if (dest && dest->set)
824 inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
825 &dest->address.v4, s, sizeof(s));
827 fw3_pr(" %s--dst-range %s", dest->invert ? "! " : "", s);
829 inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
830 &dest->address2.v4, s, sizeof(s));
836 inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
837 &dest->address.v4, s, sizeof(s));
839 fw3_pr(" %s-d %s/%u", dest->invert ? "! " : "", s, dest->mask);
845 fw3_format_sport_dport(struct fw3_port *sp, struct fw3_port *dp)
849 if (sp->port_min == sp->port_max)
850 fw3_pr(" %s--sport %u", sp->invert ? "! " : "", sp->port_min);
852 fw3_pr(" %s--sport %u:%u",
853 sp->invert ? "! " : "", sp->port_min, sp->port_max);
858 if (dp->port_min == dp->port_max)
859 fw3_pr(" %s--dport %u", dp->invert ? "! " : "", dp->port_min);
861 fw3_pr(" %s--dport %u:%u",
862 dp->invert ? "! " : "", dp->port_min, dp->port_max);
867 fw3_format_mac(struct fw3_mac *mac)
872 fw3_pr(" -m mac %s--mac-source %s",
873 mac->invert ? "! " : "", ether_ntoa(&mac->mac));
877 fw3_format_protocol(struct fw3_protocol *proto, enum fw3_family family)
884 pr = proto->protocol;
886 if (pr == 1 && family == FW3_FAMILY_V6)
892 fw3_pr(" %s-p %u", proto->invert ? "! " : "", pr);
896 fw3_format_icmptype(struct fw3_icmptype *icmp, enum fw3_family family)
901 if (family != FW3_FAMILY_V6)
903 if (icmp->code_min == 0 && icmp->code_max == 0xFF)
904 fw3_pr(" %s--icmp-type %u", icmp->invert ? "! " : "", icmp->type);
906 fw3_pr(" %s--icmp-type %u/%u",
907 icmp->invert ? "! " : "", icmp->type, icmp->code_min);
911 if (icmp->code6_min == 0 && icmp->code6_max == 0xFF)
912 fw3_pr(" %s--icmpv6-type %u", icmp->invert ? "! " : "", icmp->type6);
914 fw3_pr(" %s--icmpv6-type %u/%u",
915 icmp->invert ? "! " : "", icmp->type6, icmp->code6_min);
920 fw3_format_limit(struct fw3_limit *limit)
925 const char *units[] = {
926 [FW3_LIMIT_UNIT_SECOND] = "second",
927 [FW3_LIMIT_UNIT_MINUTE] = "minute",
928 [FW3_LIMIT_UNIT_HOUR] = "hour",
929 [FW3_LIMIT_UNIT_DAY] = "day",
934 fw3_pr(" -m limit %s--limit %u/%s",
935 limit->invert ? "! " : "", limit->rate, units[limit->unit]);
937 if (limit->burst > 0)
938 fw3_pr(" --limit-burst %u", limit->burst);
943 fw3_format_ipset(struct fw3_ipset *ipset, bool invert)
946 const char *name = NULL;
947 struct fw3_ipset_datatype *type;
952 if (ipset->external && *ipset->external)
953 name = ipset->external;
957 fw3_pr(" -m set %s--match-set %s", invert ? "! " : "", name);
959 list_for_each_entry(type, &ipset->datatypes, list)
961 fw3_pr("%c%s", first ? ' ' : ',', type->dest ? "dst" : "src");
967 fw3_format_time(struct fw3_time *time)
970 struct tm empty = { 0 };
971 char buf[sizeof("9999-99-99T23:59:59\0")];
972 bool d1 = memcmp(&time->datestart, &empty, sizeof(empty));
973 bool d2 = memcmp(&time->datestop, &empty, sizeof(empty));
976 if (!d1 && !d2 && !time->timestart && !time->timestop &&
977 !(time->monthdays & 0xFFFFFFFE) && !(time->weekdays & 0xFE))
989 strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestart);
990 fw3_pr(" --datestart %s", buf);
995 strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestop);
996 fw3_pr(" --datestop %s", buf);
1001 fw3_pr(" --timestart %02d:%02d:%02d",
1002 time->timestart / 3600,
1003 time->timestart % 3600 / 60,
1004 time->timestart % 60);
1009 fw3_pr(" --timestop %02d:%02d:%02d",
1010 time->timestop / 3600,
1011 time->timestop % 3600 / 60,
1012 time->timestop % 60);
1015 if (time->monthdays & 0xFFFFFFFE)
1017 fw3_pr(" %s--monthdays", hasbit(time->monthdays, 0) ? "! " : "");
1019 for (i = 1, first = true; i < 32; i++)
1021 if (hasbit(time->monthdays, i))
1023 fw3_pr("%c%u", first ? ' ' : ',', i);
1029 if (time->weekdays & 0xFE)
1031 fw3_pr(" %s--weekdays", hasbit(time->weekdays, 0) ? "! " : "");
1033 for (i = 1, first = true; i < 8; i++)
1035 if (hasbit(time->weekdays, i))
1037 fw3_pr("%c%u", first ? ' ' : ',', i);
1045 __fw3_format_comment(const char *comment, ...)
1051 if (!comment || !*comment)
1054 fw3_pr(" -m comment --comment \"");
1058 va_start(ap, comment);
1084 c = va_arg(ap, const char *);
1094 fw3_format_extra(const char *extra)
1096 if (!extra || !*extra)
1099 fw3_pr(" %s", extra);