/*
* firewall3 - 3rd OpenWrt UCI firewall implementation
*
- * Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
+ * Copyright (C) 2013-2014 Jo-Philipp Wich <jo@mein.io>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
"REJECT",
"DROP",
"NOTRACK",
+ "HELPER",
"MARK",
+ "DSCP",
"DNAT",
"SNAT",
"MASQUERADE",
"DROP",
};
+const char *fw3_reject_code_names[__FW3_REJECT_CODE_MAX] = {
+ "tcp-reset",
+ "port-unreach",
+ "adm-prohibited",
+};
+
const char *fw3_limit_units[__FW3_LIMIT_UNIT_MAX] = {
"second",
"minute",
"external",
};
+static const struct { const char *name; uint8_t dscp; } dscp_classes[] = {
+ { "CS0", 0x00 },
+ { "CS1", 0x08 },
+ { "CS2", 0x10 },
+ { "CS3", 0x18 },
+ { "CS4", 0x20 },
+ { "CS5", 0x28 },
+ { "CS6", 0x30 },
+ { "CS7", 0x38 },
+ { "BE", 0x00 },
+ { "AF11", 0x0a },
+ { "AF12", 0x0c },
+ { "AF13", 0x0e },
+ { "AF21", 0x12 },
+ { "AF22", 0x14 },
+ { "AF23", 0x16 },
+ { "AF31", 0x1a },
+ { "AF32", 0x1c },
+ { "AF33", 0x1e },
+ { "AF41", 0x22 },
+ { "AF42", 0x24 },
+ { "AF43", 0x26 },
+ { "EF", 0x2e }
+};
+
bool
fw3_parse_bool(void *ptr, const char *val, bool is_list)
FW3_FLAG_ACCEPT, FW3_FLAG_MASQUERADE);
}
+bool
+fw3_parse_reject_code(void *ptr, const char *val, bool is_list)
+{
+ return parse_enum(ptr, val, &fw3_reject_code_names[FW3_REJECT_CODE_TCP_RESET],
+ FW3_REJECT_CODE_TCP_RESET, FW3_REJECT_CODE_ADM_PROHIBITED);
+}
+
bool
fw3_parse_limit(void *ptr, const char *val, bool is_list)
{
addr.family = FW3_FAMILY_V6;
addr.address.v6 = v6;
- if (m && !inet_pton(AF_INET6, m, &addr.mask.v6))
+ if (m)
{
- bits = strtol(m, &e, 10);
+ if (!inet_pton(AF_INET6, m, &v6))
+ {
+ bits = strtol(m, &e, 10);
- if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v6))
- goto fail;
+ if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v6))
+ goto fail;
+ }
addr.mask.v6 = v6;
}
addr.family = FW3_FAMILY_V4;
addr.address.v4 = v4;
- if (m && !inet_pton(AF_INET, m, &addr.mask.v4))
+ if (m)
{
- bits = strtol(m, &e, 10);
+ if (!inet_pton(AF_INET, m, &v4))
+ {
+ bits = strtol(m, &e, 10);
- if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v4))
- goto fail;
+ if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v4))
+ goto fail;
+ }
addr.mask.v4 = v4;
}
struct fw3_device dev = { };
struct fw3_address *addr, *tmp;
LIST_HEAD(addr_list);
+ int n_addrs;
if (!fw3_parse_address(ptr, val, is_list))
{
if (!fw3_parse_device(&dev, val, false))
return false;
- fw3_ubus_address(&addr_list, dev.name);
+ n_addrs = fw3_ubus_address(&addr_list, dev.name);
+
list_for_each_entry(addr, &addr_list, list)
{
addr->invert = dev.invert;
addr->resolved = true;
}
+ /* add an empty address member with .set = false, .resolved = true
+ * to signal resolving failure to callers */
+ if (n_addrs == 0)
+ {
+ tmp = fw3_alloc(sizeof(*tmp));
+ tmp->resolved = true;
+
+ list_add_tail(&tmp->list, &addr_list);
+ }
+
if (is_list)
{
list_splice_tail(&addr_list, ptr);
bool
fw3_parse_family(void *ptr, const char *val, bool is_list)
{
- if (!strcmp(val, "any"))
+ if (!strcmp(val, "any") || !strcmp(val, "*"))
*((enum fw3_family *)ptr) = FW3_FAMILY_ANY;
else if (!strcmp(val, "inet") || strrchr(val, '4'))
*((enum fw3_family *)ptr) = FW3_FAMILY_V4;
}
icmp.type6 = icmp.type;
- icmp.code6_min = icmp.code_max;
+ icmp.code6_min = icmp.code_min;
icmp.code6_max = icmp.code_max;
v4 = true;
while (isspace(*++val));
}
- if (!strcmp(val, "all"))
+ if (!strcmp(val, "all") || !strcmp(val, "any") || !strcmp(val, "*"))
{
proto.any = true;
put_value(ptr, &proto, sizeof(proto), is_list);
{
unsigned int year = 1970, mon = 1, day = 1, hour = 0, min = 0, sec = 0;
struct tm tm = { 0 };
+ time_t ts;
char *p;
year = strtoul(val, &p, 10);
tm.tm_min = min;
tm.tm_sec = sec;
- if (mktime(&tm) >= 0)
+ ts = mktime(&tm) - timezone;
+
+ if (ts >= 0)
{
- *((struct tm *)ptr) = tm;
+ gmtime_r(&ts, (struct tm *)ptr);
return true;
}
if (*val == '!')
{
- setbit(*(uint8_t *)ptr, 0);
+ fw3_setbit(*(uint8_t *)ptr, 0);
while (isspace(*++val));
}
}
}
- setbit(*(uint8_t *)ptr, w);
+ fw3_setbit(*(uint8_t *)ptr, w);
}
free(s);
if (*val == '!')
{
- setbit(*(uint32_t *)ptr, 0);
+ fw3_setbit(*(uint32_t *)ptr, 0);
while (isspace(*++val));
}
return false;
}
- setbit(*(uint32_t *)ptr, d);
+ fw3_setbit(*(uint32_t *)ptr, d);
}
free(s);
return true;
}
+bool
+fw3_parse_dscp(void *ptr, const char *val, bool is_list)
+{
+ uint32_t n;
+ char *e;
+ struct fw3_dscp *d = ptr;
+
+ if (*val == '!')
+ {
+ d->invert = true;
+ while (isspace(*++val));
+ }
+
+ for (n = 0; n < sizeof(dscp_classes) / sizeof(dscp_classes[0]); n++)
+ {
+ if (strcmp(dscp_classes[n].name, val))
+ continue;
+
+ d->set = true;
+ d->dscp = dscp_classes[n].dscp;
+ return true;
+ }
+
+ n = strtoul(val, &e, 0);
+
+ if (e == val || *e || n > 0x3F)
+ return false;
+
+ d->set = true;
+ d->dscp = n;
+ return true;
+}
+
bool
fw3_parse_setmatch(void *ptr, const char *val, bool is_list)
{
return false;
}
- strncpy(m->name, p, sizeof(m->name));
+ snprintf(m->name, sizeof(m->name), "%s", p);
for (i = 0, p = strtok(NULL, " \t,");
i < 3 && p != NULL;
return valid;
}
+bool
+fw3_parse_cthelper(void *ptr, const char *val, bool is_list)
+{
+ struct fw3_cthelpermatch m = { };
+
+ if (*val == '!')
+ {
+ m.invert = true;
+ while (isspace(*++val));
+ }
+
+ if (*val)
+ {
+ m.set = true;
+ snprintf(m.name, sizeof(m.name), "%s", val);
+ put_value(ptr, &m, sizeof(m), is_list);
+ return true;
+ }
+
+ return false;
+}
+
+bool
+fw3_parse_setentry(void *ptr, const char *val, bool is_list)
+{
+ struct fw3_setentry e = { };
+
+ e.value = val;
+ put_value(ptr, &e, sizeof(e), is_list);
+
+ return true;
+}
+
bool
fw3_parse_options(void *s, const struct fw3_option *opts,
struct uci_section *section)
{
char *p, *v;
- bool known;
+ bool known, inv;
struct uci_element *e, *l;
struct uci_option *o;
const struct fw3_option *opt;
}
else
{
+ inv = false;
dest = (struct list_head *)((char *)s + opt->offset);
for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
{
+ /* If we encounter a sole "!" token, assume that it
+ * is meant to be part of the next token, so silently
+ * skip it and remember the state... */
+ if (!strcmp(p, "!"))
+ {
+ inv = true;
+ continue;
+ }
+
+ /* The previous token was a sole "!", rewind pointer
+ * back by one byte to precede the value with an
+ * exclamation mark which effectively turns
+ * ("!", "foo") into ("!foo") */
+ if (inv)
+ {
+ *--p = '!';
+ inv = false;
+ }
+
if (!opt->parse(dest, p, true))
{
warn_elem(e, "has invalid value '%s'", p);
continue;
}
}
+
+ /* The last token was a sole "!" without any subsequent
+ * text, so pass it to the option parser as-is. */
+ if (inv && !opt->parse(dest, "!", true))
+ {
+ warn_elem(e, "has invalid value '%s'", p);
+ valid = false;
+ }
}
}
bool
fw3_parse_blob_options(void *s, const struct fw3_option *opts,
- struct blob_attr *a)
+ struct blob_attr *a, const char *name)
{
char *p, *v, buf[16];
bool known;
{
if (!opt->elem_size)
{
- fprintf(stderr, "%s must not be a list\n", opt->name);
+ fprintf(stderr, "%s: '%s' must not be a list\n",
+ name, opt->name);
+
valid = false;
}
else
if (blobmsg_type(e) == BLOBMSG_TYPE_INT32) {
snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(e));
v = buf;
+ } else if (blobmsg_type(o) == BLOBMSG_TYPE_BOOL) {
+ snprintf(buf, sizeof(buf), "%d", blobmsg_get_bool(o));
+ v = buf;
} else {
v = blobmsg_get_string(e);
}
if (!opt->parse(dest, v, true))
{
- fprintf(stderr, "%s has invalid value '%s'\n", opt->name, v);
+ fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
+ name, opt->name, v);
valid = false;
continue;
}
if (blobmsg_type(o) == BLOBMSG_TYPE_INT32) {
snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(o));
v = buf;
+ } else if (blobmsg_type(o) == BLOBMSG_TYPE_BOOL) {
+ snprintf(buf, sizeof(buf), "%d", blobmsg_get_bool(o));
+ v = buf;
} else {
v = blobmsg_get_string(o);
}
{
if (!opt->parse((char *)s + opt->offset, v, false))
{
- fprintf(stderr, "%s has invalid value '%s'\n", opt->name, v);
+ fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
+ name, opt->name, v);
valid = false;
}
}
{
if (!opt->parse(dest, p, true))
{
- fprintf(stderr, "%s has invalid value '%s'\n", opt->name, p);
+ fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
+ name, opt->name, p);
valid = false;
continue;
}
break;
}
- if (!known)
- fprintf(stderr, "%s is unknown\n", blobmsg_name(o));
+ if (!known && strcmp(blobmsg_name(o), "type"))
+ fprintf(stderr, "%s: '%s' is unknown\n", name, blobmsg_name(o));
}
return valid;
{
char *p, ip[INET6_ADDRSTRLEN];
static char buf[INET6_ADDRSTRLEN * 2 + 2];
+ size_t rem = sizeof(buf);
+ int len;
p = buf;
- if (address->invert && allow_invert)
- p += sprintf(p, "!");
+ if (address->invert && allow_invert) {
+ *p++ = '!';
+ *p = 0;
+ rem--;
+ }
inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
&address->address.v4, ip, sizeof(ip));
- p += sprintf(p, "%s", ip);
+ len = snprintf(p, rem, "%s", ip);
+
+ if (len < 0 || len >= rem)
+ return buf;
+
+ rem -= len;
+ p += len;
if (address->range)
{
inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
&address->mask.v4, ip, sizeof(ip));
- p += sprintf(p, "-%s", ip);
+ snprintf(p, rem, "-%s", ip);
}
else if (!as_cidr)
{
inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
&address->mask.v4, ip, sizeof(ip));
- p += sprintf(p, "/%s", ip);
+ snprintf(p, rem, "/%s", ip);
}
else
{
- p += sprintf(p, "/%u", fw3_netmask2bitlen(address->family,
- &address->mask.v6));
+ snprintf(p, rem, "/%u",
+ fw3_netmask2bitlen(address->family, &address->mask.v6));
}
return buf;