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.
24 parse_enum(void *ptr, const char *val, const char **values, int min, int max)
26 int i, l = strlen(val);
30 for (i = 0; i <= (max - min); i++)
32 if (!strncasecmp(val, values[i], l))
34 *((int *)ptr) = min + i;
44 const char *fw3_flag_names[__FW3_FLAG_MAX] = {
65 static const char *limit_units[] = {
72 static const char *ipset_methods[] = {
78 static const char *ipset_types[] = {
86 static const char *weekdays[] = {
96 static const char *include_types[] = {
101 static const char *reflection_sources[] = {
108 fw3_parse_bool(void *ptr, const char *val)
110 if (!strcmp(val, "true") || !strcmp(val, "yes") || !strcmp(val, "1"))
111 *((bool *)ptr) = true;
113 *((bool *)ptr) = false;
119 fw3_parse_int(void *ptr, const char *val)
121 int n = strtol(val, NULL, 10);
123 if (errno == ERANGE || errno == EINVAL)
132 fw3_parse_string(void *ptr, const char *val)
134 *((char **)ptr) = (char *)val;
139 fw3_parse_target(void *ptr, const char *val)
141 return parse_enum(ptr, val, &fw3_flag_names[FW3_FLAG_ACCEPT],
142 FW3_FLAG_ACCEPT, FW3_FLAG_SNAT);
146 fw3_parse_limit(void *ptr, const char *val)
148 struct fw3_limit *limit = ptr;
149 enum fw3_limit_unit u = FW3_LIMIT_UNIT_SECOND;
155 limit->invert = true;
156 while (isspace(*++val));
159 n = strtol(val, &e, 10);
161 if (errno == ERANGE || errno == EINVAL)
164 if (*e && *e++ != '/')
170 if (!parse_enum(&u, e, limit_units, 0, FW3_LIMIT_UNIT_DAY))
180 fw3_parse_device(void *ptr, const char *val)
182 struct fw3_device *dev = ptr;
194 while (isspace(*++val));
198 snprintf(dev->name, sizeof(dev->name), "%s", val);
207 fw3_parse_address(void *ptr, const char *val)
209 struct fw3_address *addr = ptr;
218 while (isspace(*++val));
226 if ((p = strchr(s, '/')) != NULL)
229 m = strtoul(p, &e, 10);
231 if ((e == p) || (*e != 0))
233 if (strchr(s, ':') || !inet_pton(AF_INET, p, &v4))
239 for (i = 0, m = 32; !(v4.s_addr & 1) && (i < 32); i++)
246 else if ((p = strchr(s, '-')) != NULL)
250 if (inet_pton(AF_INET6, p, &v6))
252 addr->family = FW3_FAMILY_V6;
253 addr->address2.v6 = v6;
256 else if (inet_pton(AF_INET, p, &v4))
258 addr->family = FW3_FAMILY_V4;
259 addr->address2.v4 = v4;
269 if (inet_pton(AF_INET6, s, &v6))
271 addr->family = FW3_FAMILY_V6;
272 addr->address.v6 = v6;
273 addr->mask = (m >= 0) ? m : 128;
275 else if (inet_pton(AF_INET, s, &v4))
277 addr->family = FW3_FAMILY_V4;
278 addr->address.v4 = v4;
279 addr->mask = (m >= 0) ? m : 32;
293 fw3_parse_network(void *ptr, const char *val)
295 struct fw3_device dev;
296 struct fw3_address *tmp, *addr = ptr;
297 struct list_head *list;
299 if (!fw3_parse_address(addr, val))
301 memset(&dev, 0, sizeof(dev));
303 if (!fw3_parse_device(&dev, val))
306 list = fw3_ubus_address(dev.name);
310 list_for_each_entry(tmp, list, list)
313 addr->invert = dev.invert;
317 fw3_ubus_address_free(list);
325 fw3_parse_mac(void *ptr, const char *val)
327 struct fw3_mac *addr = ptr;
328 struct ether_addr *mac;
333 while (isspace(*++val));
336 if ((mac = ether_aton(val)) != NULL)
347 fw3_parse_port(void *ptr, const char *val)
349 struct fw3_port *range = ptr;
356 range->invert = true;
357 while (isspace(*++val));
360 n = strtoul(val, &p, 10);
362 if (errno == ERANGE || errno == EINVAL)
365 if (*p && *p != '-' && *p != ':')
370 m = strtoul(++p, NULL, 10);
372 if (errno == ERANGE || errno == EINVAL || m < n)
389 fw3_parse_family(void *ptr, const char *val)
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;
404 fw3_parse_icmptype(void *ptr, const char *val)
406 struct fw3_icmptype *icmp = ptr;
412 for (i = 0; i < ARRAY_SIZE(fw3_icmptype_list_v4); i++)
414 if (!strcmp(val, fw3_icmptype_list_v4[i].name))
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;
425 for (i = 0; i < ARRAY_SIZE(fw3_icmptype_list_v6); i++)
427 if (!strcmp(val, fw3_icmptype_list_v6[i].name))
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;
440 i = strtoul(val, &p, 10);
442 if ((p == val) || (*p != '/' && *p != 0) || (i > 0xFF))
450 i = strtoul(val, &p, 10);
452 if ((p == val) || (*p != 0) || (i > 0xFF))
461 icmp->code_max = 0xFF;
464 icmp->type6 = icmp->type;
465 icmp->code6_min = icmp->code_max;
466 icmp->code6_max = icmp->code_max;
472 icmp->family = (v4 && v6) ? FW3_FAMILY_ANY
473 : (v6 ? FW3_FAMILY_V6 : FW3_FAMILY_V4);
479 fw3_parse_protocol(void *ptr, const char *val)
481 struct fw3_protocol *proto = ptr;
482 struct protoent *ent;
486 proto->invert = true;
487 while (isspace(*++val));
490 if (!strcmp(val, "all"))
495 else if (!strcmp(val, "icmpv6"))
500 ent = getprotobyname(val);
504 proto->protocol = ent->p_proto;
508 proto->protocol = strtoul(val, NULL, 10);
509 return (errno != ERANGE && errno != EINVAL);
513 fw3_parse_ipset_method(void *ptr, const char *val)
515 return parse_enum(ptr, val, ipset_methods,
516 FW3_IPSET_METHOD_BITMAP, FW3_IPSET_METHOD_LIST);
520 fw3_parse_ipset_datatype(void *ptr, const char *val)
522 struct fw3_ipset_datatype *type = ptr;
524 if (!strncmp(val, "dest_", 5))
529 else if (!strncmp(val, "dst_", 4))
534 else if (!strncmp(val, "src_", 4))
540 return parse_enum(&type->type, val, ipset_types,
541 FW3_IPSET_TYPE_IP, FW3_IPSET_TYPE_SET);
545 fw3_parse_date(void *ptr, const char *val)
547 unsigned int year = 1970, mon = 1, day = 1, hour = 0, min = 0, sec = 0;
548 struct tm tm = { 0 };
551 year = strtoul(val, &p, 10);
552 if ((*p != '-' && *p) || year < 1970 || year > 2038)
557 mon = strtoul(++p, &p, 10);
558 if ((*p != '-' && *p) || mon > 12)
563 day = strtoul(++p, &p, 10);
564 if ((*p != 'T' && *p) || day > 31)
569 hour = strtoul(++p, &p, 10);
570 if ((*p != ':' && *p) || hour > 23)
575 min = strtoul(++p, &p, 10);
576 if ((*p != ':' && *p) || min > 59)
581 sec = strtoul(++p, &p, 10);
586 tm.tm_year = year - 1900;
593 if (mktime(&tm) >= 0)
595 *((struct tm *)ptr) = tm;
604 fw3_parse_time(void *ptr, const char *val)
606 unsigned int hour = 0, min = 0, sec = 0;
609 hour = strtoul(val, &p, 10);
610 if (*p != ':' || hour > 23)
613 min = strtoul(++p, &p, 10);
614 if ((*p != ':' && *p) || min > 59)
619 sec = strtoul(++p, &p, 10);
624 *((int *)ptr) = 60 * 60 * hour + 60 * min + sec;
632 fw3_parse_weekdays(void *ptr, const char *val)
639 setbit(*(uint8_t *)ptr, 0);
640 while (isspace(*++val));
643 if (!(s = strdup(val)))
646 for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
648 if (!parse_enum(&w, p, weekdays, 1, 7))
650 w = strtoul(p, &p, 10);
652 if (*p || w < 1 || w > 7)
659 setbit(*(uint8_t *)ptr, w);
667 fw3_parse_monthdays(void *ptr, const char *val)
674 setbit(*(uint32_t *)ptr, 0);
675 while (isspace(*++val));
678 if (!(s = strdup(val)))
681 for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
683 d = strtoul(p, &p, 10);
685 if (*p || d < 1 || d > 31)
691 setbit(*(uint32_t *)ptr, d);
699 fw3_parse_include_type(void *ptr, const char *val)
701 return parse_enum(ptr, val, include_types,
702 FW3_INC_TYPE_SCRIPT, FW3_INC_TYPE_RESTORE);
706 fw3_parse_reflection_source(void *ptr, const char *val)
708 return parse_enum(ptr, val, reflection_sources,
709 FW3_REFLECTION_INTERNAL, FW3_REFLECTION_EXTERNAL);
714 fw3_parse_options(void *s, const struct fw3_option *opts,
715 struct uci_section *section)
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;
725 uci_foreach_element(§ion->options, e)
727 o = uci_to_option(e);
730 for (opt = opts; opt->name; opt++)
735 if (strcmp(opt->name, e->name))
738 if (o->type == UCI_TYPE_LIST)
742 warn_elem(e, "must not be a list");
746 uci_foreach_element(&o->v.list, l)
751 item = malloc(opt->elem_size);
756 memset(item, 0, opt->elem_size);
758 if (!opt->parse(item, l->name))
760 warn_elem(e, "has invalid value '%s'", l->name);
765 dest = (struct list_head *)((char *)s + opt->offset);
766 list_add_tail(item, dest);
777 /* protocol "tcpudp" compatibility hack */
778 if (opt->parse == fw3_parse_protocol && !strcmp(v, "tcpudp"))
779 v = strdup("tcp udp");
783 if (!opt->parse((char *)s + opt->offset, o->v.string))
784 warn_elem(e, "has invalid value '%s'", o->v.string);
788 for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
790 item = malloc(opt->elem_size);
795 memset(item, 0, opt->elem_size);
797 if (!opt->parse(item, p))
799 warn_elem(e, "has invalid value '%s'", p);
804 dest = (struct list_head *)((char *)s + opt->offset);
805 list_add_tail(item, dest);
809 if (v != o->v.string)
818 warn_elem(e, "is unknown");
824 fw3_format_in_out(struct fw3_device *in, struct fw3_device *out)
827 fw3_pr(" %s-i %s", in->invert ? "! " : "", in->name);
829 if (out && !out->any)
830 fw3_pr(" %s-o %s", out->invert ? "! " : "", out->name);
834 fw3_format_src_dest(struct fw3_address *src, struct fw3_address *dest)
836 char s[INET6_ADDRSTRLEN];
838 if ((src && src->range) || (dest && dest->range))
839 fw3_pr(" -m iprange");
845 inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
846 &src->address.v4, s, sizeof(s));
848 fw3_pr(" %s--src-range %s", src->invert ? "! " : "", s);
850 inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
851 &src->address2.v4, s, sizeof(s));
857 inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
858 &src->address.v4, s, sizeof(s));
860 fw3_pr(" %s-s %s/%u", src->invert ? "! " : "", s, src->mask);
864 if (dest && dest->set)
868 inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
869 &dest->address.v4, s, sizeof(s));
871 fw3_pr(" %s--dst-range %s", dest->invert ? "! " : "", s);
873 inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
874 &dest->address2.v4, s, sizeof(s));
880 inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
881 &dest->address.v4, s, sizeof(s));
883 fw3_pr(" %s-d %s/%u", dest->invert ? "! " : "", s, dest->mask);
889 fw3_format_sport_dport(struct fw3_port *sp, struct fw3_port *dp)
893 if (sp->port_min == sp->port_max)
894 fw3_pr(" %s--sport %u", sp->invert ? "! " : "", sp->port_min);
896 fw3_pr(" %s--sport %u:%u",
897 sp->invert ? "! " : "", sp->port_min, sp->port_max);
902 if (dp->port_min == dp->port_max)
903 fw3_pr(" %s--dport %u", dp->invert ? "! " : "", dp->port_min);
905 fw3_pr(" %s--dport %u:%u",
906 dp->invert ? "! " : "", dp->port_min, dp->port_max);
911 fw3_format_mac(struct fw3_mac *mac)
916 fw3_pr(" -m mac %s--mac-source %s",
917 mac->invert ? "! " : "", ether_ntoa(&mac->mac));
921 fw3_format_protocol(struct fw3_protocol *proto, enum fw3_family family)
928 pr = proto->protocol;
930 if (pr == 1 && family == FW3_FAMILY_V6)
936 fw3_pr(" %s-p %u", proto->invert ? "! " : "", pr);
940 fw3_format_icmptype(struct fw3_icmptype *icmp, enum fw3_family family)
945 if (family != FW3_FAMILY_V6)
947 if (icmp->code_min == 0 && icmp->code_max == 0xFF)
948 fw3_pr(" %s--icmp-type %u", icmp->invert ? "! " : "", icmp->type);
950 fw3_pr(" %s--icmp-type %u/%u",
951 icmp->invert ? "! " : "", icmp->type, icmp->code_min);
955 if (icmp->code6_min == 0 && icmp->code6_max == 0xFF)
956 fw3_pr(" %s--icmpv6-type %u", icmp->invert ? "! " : "", icmp->type6);
958 fw3_pr(" %s--icmpv6-type %u/%u",
959 icmp->invert ? "! " : "", icmp->type6, icmp->code6_min);
964 fw3_format_limit(struct fw3_limit *limit)
971 fw3_pr(" -m limit %s--limit %u/%s",
972 limit->invert ? "! " : "",
973 limit->rate, limit_units[limit->unit]);
975 if (limit->burst > 0)
976 fw3_pr(" --limit-burst %u", limit->burst);
981 fw3_format_ipset(struct fw3_ipset *ipset, bool invert)
984 const char *name = NULL;
985 struct fw3_ipset_datatype *type;
990 if (ipset->external && *ipset->external)
991 name = ipset->external;
995 fw3_pr(" -m set %s--match-set %s", invert ? "! " : "", name);
997 list_for_each_entry(type, &ipset->datatypes, list)
999 fw3_pr("%c%s", first ? ' ' : ',', type->dest ? "dst" : "src");
1005 fw3_format_time(struct fw3_time *time)
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));
1014 if (!d1 && !d2 && !time->timestart && !time->timestop &&
1015 !(time->monthdays & 0xFFFFFFFE) && !(time->weekdays & 0xFE))
1027 strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestart);
1028 fw3_pr(" --datestart %s", buf);
1033 strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestop);
1034 fw3_pr(" --datestop %s", buf);
1037 if (time->timestart)
1039 fw3_pr(" --timestart %02d:%02d:%02d",
1040 time->timestart / 3600,
1041 time->timestart % 3600 / 60,
1042 time->timestart % 60);
1047 fw3_pr(" --timestop %02d:%02d:%02d",
1048 time->timestop / 3600,
1049 time->timestop % 3600 / 60,
1050 time->timestop % 60);
1053 if (time->monthdays & 0xFFFFFFFE)
1055 fw3_pr(" %s--monthdays", hasbit(time->monthdays, 0) ? "! " : "");
1057 for (i = 1, first = true; i < 32; i++)
1059 if (hasbit(time->monthdays, i))
1061 fw3_pr("%c%u", first ? ' ' : ',', i);
1067 if (time->weekdays & 0xFE)
1069 fw3_pr(" %s--weekdays", hasbit(time->weekdays, 0) ? "! " : "");
1071 for (i = 1, first = true; i < 8; i++)
1073 if (hasbit(time->weekdays, i))
1075 fw3_pr("%c%u", first ? ' ' : ',', i);
1083 __fw3_format_comment(const char *comment, ...)
1089 if (!comment || !*comment)
1092 fw3_pr(" -m comment --comment \"");
1096 va_start(ap, comment);
1122 c = va_arg(ap, const char *);
1132 fw3_format_extra(const char *extra)
1134 if (!extra || !*extra)
1137 fw3_pr(" %s", extra);