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.
23 parse_enum(void *ptr, const char *val, const char **values, int min, int max)
25 int i, l = strlen(val);
29 for (i = 0; i <= (max - min); i++)
31 if (!strncasecmp(val, values[i], l))
33 *((int *)ptr) = min + i;
43 const char *fw3_flag_names[__FW3_FLAG_MAX] = {
64 static const char *limit_units[] = {
71 static const char *ipset_methods[] = {
77 static const char *ipset_types[] = {
85 static const char *weekdays[] = {
95 static const char *include_types[] = {
100 static const char *reflection_sources[] = {
107 fw3_parse_bool(void *ptr, const char *val)
109 if (!strcmp(val, "true") || !strcmp(val, "yes") || !strcmp(val, "1"))
110 *((bool *)ptr) = true;
112 *((bool *)ptr) = false;
118 fw3_parse_int(void *ptr, const char *val)
120 int n = strtol(val, NULL, 10);
122 if (errno == ERANGE || errno == EINVAL)
131 fw3_parse_string(void *ptr, const char *val)
133 *((char **)ptr) = (char *)val;
138 fw3_parse_target(void *ptr, const char *val)
140 return parse_enum(ptr, val, &fw3_flag_names[FW3_FLAG_ACCEPT],
141 FW3_FLAG_ACCEPT, FW3_FLAG_SNAT);
145 fw3_parse_limit(void *ptr, const char *val)
147 struct fw3_limit *limit = ptr;
148 enum fw3_limit_unit u = FW3_LIMIT_UNIT_SECOND;
154 limit->invert = true;
155 while (isspace(*++val));
158 n = strtol(val, &e, 10);
160 if (errno == ERANGE || errno == EINVAL)
163 if (*e && *e++ != '/')
169 if (!parse_enum(&u, e, limit_units, 0, FW3_LIMIT_UNIT_DAY))
179 fw3_parse_device(void *ptr, const char *val)
181 struct fw3_device *dev = ptr;
193 while (isspace(*++val));
197 snprintf(dev->name, sizeof(dev->name), "%s", val);
206 fw3_parse_address(void *ptr, const char *val)
208 struct fw3_address *addr = ptr;
217 while (isspace(*++val));
225 if ((p = strchr(s, '/')) != NULL)
228 m = strtoul(p, &e, 10);
230 if ((e == p) || (*e != 0))
232 if (strchr(s, ':') || !inet_pton(AF_INET, p, &v4))
238 for (i = 0, m = 32; !(v4.s_addr & 1) && (i < 32); i++)
245 else if ((p = strchr(s, '-')) != NULL)
249 if (inet_pton(AF_INET6, p, &v6))
251 addr->family = FW3_FAMILY_V6;
252 addr->address2.v6 = v6;
255 else if (inet_pton(AF_INET, p, &v4))
257 addr->family = FW3_FAMILY_V4;
258 addr->address2.v4 = v4;
268 if (inet_pton(AF_INET6, s, &v6))
270 addr->family = FW3_FAMILY_V6;
271 addr->address.v6 = v6;
272 addr->mask = (m >= 0) ? m : 128;
274 else if (inet_pton(AF_INET, s, &v4))
276 addr->family = FW3_FAMILY_V4;
277 addr->address.v4 = v4;
278 addr->mask = (m >= 0) ? m : 32;
292 fw3_parse_mac(void *ptr, const char *val)
294 struct fw3_mac *addr = ptr;
295 struct ether_addr *mac;
300 while (isspace(*++val));
303 if ((mac = ether_aton(val)) != NULL)
314 fw3_parse_port(void *ptr, const char *val)
316 struct fw3_port *range = ptr;
323 range->invert = true;
324 while (isspace(*++val));
327 n = strtoul(val, &p, 10);
329 if (errno == ERANGE || errno == EINVAL)
332 if (*p && *p != '-' && *p != ':')
337 m = strtoul(++p, NULL, 10);
339 if (errno == ERANGE || errno == EINVAL || m < n)
356 fw3_parse_family(void *ptr, const char *val)
358 if (!strcmp(val, "any"))
359 *((enum fw3_family *)ptr) = FW3_FAMILY_ANY;
360 else if (!strcmp(val, "inet") || strrchr(val, '4'))
361 *((enum fw3_family *)ptr) = FW3_FAMILY_V4;
362 else if (!strcmp(val, "inet6") || strrchr(val, '6'))
363 *((enum fw3_family *)ptr) = FW3_FAMILY_V6;
371 fw3_parse_icmptype(void *ptr, const char *val)
373 struct fw3_icmptype *icmp = ptr;
379 for (i = 0; i < ARRAY_SIZE(fw3_icmptype_list_v4); i++)
381 if (!strcmp(val, fw3_icmptype_list_v4[i].name))
383 icmp->type = fw3_icmptype_list_v4[i].type;
384 icmp->code_min = fw3_icmptype_list_v4[i].code_min;
385 icmp->code_max = fw3_icmptype_list_v4[i].code_max;
392 for (i = 0; i < ARRAY_SIZE(fw3_icmptype_list_v6); i++)
394 if (!strcmp(val, fw3_icmptype_list_v6[i].name))
396 icmp->type6 = fw3_icmptype_list_v6[i].type;
397 icmp->code6_min = fw3_icmptype_list_v6[i].code_min;
398 icmp->code6_max = fw3_icmptype_list_v6[i].code_max;
407 i = strtoul(val, &p, 10);
409 if ((p == val) || (*p != '/' && *p != 0) || (i > 0xFF))
417 i = strtoul(val, &p, 10);
419 if ((p == val) || (*p != 0) || (i > 0xFF))
428 icmp->code_max = 0xFF;
431 icmp->type6 = icmp->type;
432 icmp->code6_min = icmp->code_max;
433 icmp->code6_max = icmp->code_max;
439 icmp->family = (v4 && v6) ? FW3_FAMILY_ANY
440 : (v6 ? FW3_FAMILY_V6 : FW3_FAMILY_V4);
446 fw3_parse_protocol(void *ptr, const char *val)
448 struct fw3_protocol *proto = ptr;
449 struct protoent *ent;
453 proto->invert = true;
454 while (isspace(*++val));
457 if (!strcmp(val, "all"))
462 else if (!strcmp(val, "icmpv6"))
467 ent = getprotobyname(val);
471 proto->protocol = ent->p_proto;
475 proto->protocol = strtoul(val, NULL, 10);
476 return (errno != ERANGE && errno != EINVAL);
480 fw3_parse_ipset_method(void *ptr, const char *val)
482 return parse_enum(ptr, val, ipset_methods,
483 FW3_IPSET_METHOD_BITMAP, FW3_IPSET_METHOD_LIST);
487 fw3_parse_ipset_datatype(void *ptr, const char *val)
489 struct fw3_ipset_datatype *type = ptr;
491 if (!strncmp(val, "dest_", 5))
496 else if (!strncmp(val, "dst_", 4))
501 else if (!strncmp(val, "src_", 4))
507 return parse_enum(&type->type, val, ipset_types,
508 FW3_IPSET_TYPE_IP, FW3_IPSET_TYPE_SET);
512 fw3_parse_date(void *ptr, const char *val)
514 unsigned int year = 1970, mon = 1, day = 1, hour = 0, min = 0, sec = 0;
515 struct tm tm = { 0 };
518 year = strtoul(val, &p, 10);
519 if ((*p != '-' && *p) || year < 1970 || year > 2038)
524 mon = strtoul(++p, &p, 10);
525 if ((*p != '-' && *p) || mon > 12)
530 day = strtoul(++p, &p, 10);
531 if ((*p != 'T' && *p) || day > 31)
536 hour = strtoul(++p, &p, 10);
537 if ((*p != ':' && *p) || hour > 23)
542 min = strtoul(++p, &p, 10);
543 if ((*p != ':' && *p) || min > 59)
548 sec = strtoul(++p, &p, 10);
553 tm.tm_year = year - 1900;
560 if (mktime(&tm) >= 0)
562 *((struct tm *)ptr) = tm;
571 fw3_parse_time(void *ptr, const char *val)
573 unsigned int hour = 0, min = 0, sec = 0;
576 hour = strtoul(val, &p, 10);
577 if (*p != ':' || hour > 23)
580 min = strtoul(++p, &p, 10);
581 if ((*p != ':' && *p) || min > 59)
586 sec = strtoul(++p, &p, 10);
591 *((int *)ptr) = 60 * 60 * hour + 60 * min + sec;
599 fw3_parse_weekdays(void *ptr, const char *val)
606 setbit(*(uint8_t *)ptr, 0);
607 while (isspace(*++val));
610 if (!(s = strdup(val)))
613 for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
615 if (!parse_enum(&w, p, weekdays, 1, 7))
617 w = strtoul(p, &p, 10);
619 if (*p || w < 1 || w > 7)
626 setbit(*(uint8_t *)ptr, w);
634 fw3_parse_monthdays(void *ptr, const char *val)
641 setbit(*(uint32_t *)ptr, 0);
642 while (isspace(*++val));
645 if (!(s = strdup(val)))
648 for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
650 d = strtoul(p, &p, 10);
652 if (*p || d < 1 || d > 31)
658 setbit(*(uint32_t *)ptr, d);
666 fw3_parse_include_type(void *ptr, const char *val)
668 return parse_enum(ptr, val, include_types,
669 FW3_INC_TYPE_SCRIPT, FW3_INC_TYPE_RESTORE);
673 fw3_parse_reflection_source(void *ptr, const char *val)
675 return parse_enum(ptr, val, reflection_sources,
676 FW3_REFLECTION_INTERNAL, FW3_REFLECTION_EXTERNAL);
681 fw3_parse_options(void *s, const struct fw3_option *opts,
682 struct uci_section *section)
686 struct uci_element *e, *l;
687 struct uci_option *o;
688 const struct fw3_option *opt;
689 struct list_head *item;
690 struct list_head *dest;
692 uci_foreach_element(§ion->options, e)
694 o = uci_to_option(e);
697 for (opt = opts; opt->name; opt++)
702 if (strcmp(opt->name, e->name))
705 if (o->type == UCI_TYPE_LIST)
709 warn_elem(e, "must not be a list");
713 uci_foreach_element(&o->v.list, l)
718 item = malloc(opt->elem_size);
723 memset(item, 0, opt->elem_size);
725 if (!opt->parse(item, l->name))
727 warn_elem(e, "has invalid value '%s'", l->name);
732 dest = (struct list_head *)((char *)s + opt->offset);
733 list_add_tail(item, dest);
744 /* protocol "tcpudp" compatibility hack */
745 if (opt->parse == fw3_parse_protocol && !strcmp(v, "tcpudp"))
746 v = strdup("tcp udp");
750 if (!opt->parse((char *)s + opt->offset, o->v.string))
751 warn_elem(e, "has invalid value '%s'", o->v.string);
755 for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
757 item = malloc(opt->elem_size);
762 memset(item, 0, opt->elem_size);
764 if (!opt->parse(item, p))
766 warn_elem(e, "has invalid value '%s'", p);
771 dest = (struct list_head *)((char *)s + opt->offset);
772 list_add_tail(item, dest);
776 if (v != o->v.string)
785 warn_elem(e, "is unknown");
791 fw3_format_in_out(struct fw3_device *in, struct fw3_device *out)
794 fw3_pr(" %s-i %s", in->invert ? "! " : "", in->name);
796 if (out && !out->any)
797 fw3_pr(" %s-o %s", out->invert ? "! " : "", out->name);
801 fw3_format_src_dest(struct fw3_address *src, struct fw3_address *dest)
803 char s[INET6_ADDRSTRLEN];
805 if ((src && src->range) || (dest && dest->range))
806 fw3_pr(" -m iprange");
812 inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
813 &src->address.v4, s, sizeof(s));
815 fw3_pr(" %s--src-range %s", src->invert ? "! " : "", s);
817 inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
818 &src->address2.v4, s, sizeof(s));
824 inet_ntop(src->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
825 &src->address.v4, s, sizeof(s));
827 fw3_pr(" %s-s %s/%u", src->invert ? "! " : "", s, src->mask);
831 if (dest && dest->set)
835 inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
836 &dest->address.v4, s, sizeof(s));
838 fw3_pr(" %s--dst-range %s", dest->invert ? "! " : "", s);
840 inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
841 &dest->address2.v4, s, sizeof(s));
847 inet_ntop(dest->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
848 &dest->address.v4, s, sizeof(s));
850 fw3_pr(" %s-d %s/%u", dest->invert ? "! " : "", s, dest->mask);
856 fw3_format_sport_dport(struct fw3_port *sp, struct fw3_port *dp)
860 if (sp->port_min == sp->port_max)
861 fw3_pr(" %s--sport %u", sp->invert ? "! " : "", sp->port_min);
863 fw3_pr(" %s--sport %u:%u",
864 sp->invert ? "! " : "", sp->port_min, sp->port_max);
869 if (dp->port_min == dp->port_max)
870 fw3_pr(" %s--dport %u", dp->invert ? "! " : "", dp->port_min);
872 fw3_pr(" %s--dport %u:%u",
873 dp->invert ? "! " : "", dp->port_min, dp->port_max);
878 fw3_format_mac(struct fw3_mac *mac)
883 fw3_pr(" -m mac %s--mac-source %s",
884 mac->invert ? "! " : "", ether_ntoa(&mac->mac));
888 fw3_format_protocol(struct fw3_protocol *proto, enum fw3_family family)
895 pr = proto->protocol;
897 if (pr == 1 && family == FW3_FAMILY_V6)
903 fw3_pr(" %s-p %u", proto->invert ? "! " : "", pr);
907 fw3_format_icmptype(struct fw3_icmptype *icmp, enum fw3_family family)
912 if (family != FW3_FAMILY_V6)
914 if (icmp->code_min == 0 && icmp->code_max == 0xFF)
915 fw3_pr(" %s--icmp-type %u", icmp->invert ? "! " : "", icmp->type);
917 fw3_pr(" %s--icmp-type %u/%u",
918 icmp->invert ? "! " : "", icmp->type, icmp->code_min);
922 if (icmp->code6_min == 0 && icmp->code6_max == 0xFF)
923 fw3_pr(" %s--icmpv6-type %u", icmp->invert ? "! " : "", icmp->type6);
925 fw3_pr(" %s--icmpv6-type %u/%u",
926 icmp->invert ? "! " : "", icmp->type6, icmp->code6_min);
931 fw3_format_limit(struct fw3_limit *limit)
938 fw3_pr(" -m limit %s--limit %u/%s",
939 limit->invert ? "! " : "",
940 limit->rate, limit_units[limit->unit]);
942 if (limit->burst > 0)
943 fw3_pr(" --limit-burst %u", limit->burst);
948 fw3_format_ipset(struct fw3_ipset *ipset, bool invert)
951 const char *name = NULL;
952 struct fw3_ipset_datatype *type;
957 if (ipset->external && *ipset->external)
958 name = ipset->external;
962 fw3_pr(" -m set %s--match-set %s", invert ? "! " : "", name);
964 list_for_each_entry(type, &ipset->datatypes, list)
966 fw3_pr("%c%s", first ? ' ' : ',', type->dest ? "dst" : "src");
972 fw3_format_time(struct fw3_time *time)
975 struct tm empty = { 0 };
976 char buf[sizeof("9999-99-99T23:59:59\0")];
977 bool d1 = memcmp(&time->datestart, &empty, sizeof(empty));
978 bool d2 = memcmp(&time->datestop, &empty, sizeof(empty));
981 if (!d1 && !d2 && !time->timestart && !time->timestop &&
982 !(time->monthdays & 0xFFFFFFFE) && !(time->weekdays & 0xFE))
994 strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestart);
995 fw3_pr(" --datestart %s", buf);
1000 strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestop);
1001 fw3_pr(" --datestop %s", buf);
1004 if (time->timestart)
1006 fw3_pr(" --timestart %02d:%02d:%02d",
1007 time->timestart / 3600,
1008 time->timestart % 3600 / 60,
1009 time->timestart % 60);
1014 fw3_pr(" --timestop %02d:%02d:%02d",
1015 time->timestop / 3600,
1016 time->timestop % 3600 / 60,
1017 time->timestop % 60);
1020 if (time->monthdays & 0xFFFFFFFE)
1022 fw3_pr(" %s--monthdays", hasbit(time->monthdays, 0) ? "! " : "");
1024 for (i = 1, first = true; i < 32; i++)
1026 if (hasbit(time->monthdays, i))
1028 fw3_pr("%c%u", first ? ' ' : ',', i);
1034 if (time->weekdays & 0xFE)
1036 fw3_pr(" %s--weekdays", hasbit(time->weekdays, 0) ? "! " : "");
1038 for (i = 1, first = true; i < 8; i++)
1040 if (hasbit(time->weekdays, i))
1042 fw3_pr("%c%u", first ? ' ' : ',', i);
1050 __fw3_format_comment(const char *comment, ...)
1056 if (!comment || !*comment)
1059 fw3_pr(" -m comment --comment \"");
1063 va_start(ap, comment);
1089 c = va_arg(ap, const char *);
1099 fw3_format_extra(const char *extra)
1101 if (!extra || !*extra)
1104 fw3_pr(" %s", extra);