X-Git-Url: http://git.openwrt.org/?p=project%2Ffirewall3.git;a=blobdiff_plain;f=options.c;h=f686cf084fa65e59098d5979ef6b0ef3f7224a50;hp=80e5542a420385a3e393164b2ac1f120982cc31c;hb=HEAD;hpb=e678dcbf0336c3ca10f9fe2fae8b19347b6c1d4d diff --git a/options.c b/options.c index 80e5542..2f419a3 100644 --- a/options.c +++ b/options.c @@ -1,7 +1,7 @@ /* * firewall3 - 3rd OpenWrt UCI firewall implementation * - * Copyright (C) 2013-2014 Jo-Philipp Wich + * Copyright (C) 2013-2014 Jo-Philipp Wich * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -75,7 +75,9 @@ const char *fw3_flag_names[__FW3_FLAG_MAX] = { "REJECT", "DROP", "NOTRACK", + "HELPER", "MARK", + "DSCP", "DNAT", "SNAT", "MASQUERADE", @@ -85,6 +87,12 @@ const char *fw3_flag_names[__FW3_FLAG_MAX] = { "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", @@ -128,6 +136,32 @@ static const char *reflection_sources[] = { "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 }, + { "LE", 0x01 }, + { "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) @@ -168,6 +202,13 @@ fw3_parse_target(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) { @@ -244,8 +285,8 @@ fw3_parse_address(void *ptr, const char *val, bool is_list) struct fw3_address addr = { }; struct in_addr v4; struct in6_addr v6; - char *p, *s, *e; - int i, m = -1; + char *p = NULL, *m = NULL, *s, *e; + int bits = -1; if (*val == '!') { @@ -258,99 +299,126 @@ fw3_parse_address(void *ptr, const char *val, bool is_list) if (!s) return false; - if ((p = strchr(s, '/')) != NULL) - { + if ((m = strchr(s, '/')) != NULL) + *m++ = 0; + else if ((p = strchr(s, '-')) != NULL) *p++ = 0; - m = strtoul(p, &e, 10); - if ((e == p) || (*e != 0)) + if (inet_pton(AF_INET6, s, &v6)) + { + addr.family = FW3_FAMILY_V6; + addr.address.v6 = v6; + + if (m) { - if (strchr(s, ':') || !inet_pton(AF_INET, p, &v4)) + if (!inet_pton(AF_INET6, m, &v6)) { - free(s); - return false; - } + bits = strtol(m, &e, 10); - for (i = 0, m = 32; !(v4.s_addr & 1) && (i < 32); i++) - { - m--; - v4.s_addr >>= 1; + if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v6)) + goto fail; } - } - } - else if ((p = strchr(s, '-')) != NULL) - { - *p++ = 0; - if (inet_pton(AF_INET6, p, &v6)) - { - addr.family = FW3_FAMILY_V6; - addr.address2.v6 = v6; - addr.range = true; + addr.mask.v6 = v6; } - else if (inet_pton(AF_INET, p, &v4)) + else if (p) { - addr.family = FW3_FAMILY_V4; - addr.address2.v4 = v4; + if (!inet_pton(AF_INET6, p, &addr.mask.v6)) + goto fail; + addr.range = true; } else { - free(s); - return false; + memset(addr.mask.v6.s6_addr, 0xFF, 16); } } - - if (inet_pton(AF_INET6, s, &v6)) - { - addr.family = FW3_FAMILY_V6; - addr.address.v6 = v6; - addr.mask = (m >= 0) ? m : 128; - } else if (inet_pton(AF_INET, s, &v4)) { addr.family = FW3_FAMILY_V4; addr.address.v4 = v4; - addr.mask = (m >= 0) ? m : 32; + + if (m) + { + if (!inet_pton(AF_INET, m, &v4)) + { + bits = strtol(m, &e, 10); + + if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v4)) + goto fail; + } + + addr.mask.v4 = v4; + } + else if (p) + { + if (!inet_pton(AF_INET, p, &addr.mask.v4)) + goto fail; + + addr.range = true; + } + else + { + addr.mask.v4.s_addr = 0xFFFFFFFF; + } } else { - free(s); - return false; + goto fail; } free(s); addr.set = true; put_value(ptr, &addr, sizeof(addr), is_list); return true; + +fail: + free(s); + return false; } bool fw3_parse_network(void *ptr, const char *val, bool is_list) { struct fw3_device dev = { }; - struct fw3_address *addr; - struct list_head *addr_list; + 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; - addr_list = fw3_ubus_address(dev.name); + n_addrs = fw3_ubus_address(&addr_list, dev.name); - if (addr_list) + list_for_each_entry(addr, &addr_list, list) { - list_for_each_entry(addr, addr_list, list) - { - addr->invert = dev.invert; - addr->resolved = true; + addr->invert = dev.invert; + addr->resolved = true; + } - if (!put_value(ptr, addr, sizeof(*addr), is_list)) - break; - } + /* 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; - fw3_free_list(addr_list); + list_add_tail(&tmp->list, &addr_list); + } + + if (is_list) + { + list_splice_tail(&addr_list, ptr); + } + else if (!list_empty(&addr_list)) + { + memcpy(ptr, list_first_entry(&addr_list, typeof(*addr), list), + sizeof(*addr)); + + list_for_each_entry_safe(addr, tmp, &addr_list, list) + free(addr); } } @@ -427,7 +495,7 @@ fw3_parse_port(void *ptr, const char *val, bool is_list) 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; @@ -501,7 +569,7 @@ fw3_parse_icmptype(void *ptr, const char *val, bool is_list) } icmp.type6 = icmp.type; - icmp.code6_min = icmp.code_max; + icmp.code6_min = icmp.code_min; icmp.code6_max = icmp.code_max; v4 = true; @@ -528,7 +596,7 @@ fw3_parse_protocol(void *ptr, const char *val, bool is_list) 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); @@ -613,6 +681,7 @@ fw3_parse_date(void *ptr, const char *val, bool 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); @@ -657,9 +726,11 @@ ret: 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; } @@ -703,7 +774,7 @@ fw3_parse_weekdays(void *ptr, const char *val, bool is_list) if (*val == '!') { - setbit(*(uint8_t *)ptr, 0); + fw3_setbit(*(uint8_t *)ptr, 0); while (isspace(*++val)); } @@ -723,7 +794,7 @@ fw3_parse_weekdays(void *ptr, const char *val, bool is_list) } } - setbit(*(uint8_t *)ptr, w); + fw3_setbit(*(uint8_t *)ptr, w); } free(s); @@ -738,7 +809,7 @@ fw3_parse_monthdays(void *ptr, const char *val, bool is_list) if (*val == '!') { - setbit(*(uint32_t *)ptr, 0); + fw3_setbit(*(uint32_t *)ptr, 0); while (isspace(*++val)); } @@ -755,7 +826,7 @@ fw3_parse_monthdays(void *ptr, const char *val, bool is_list) return false; } - setbit(*(uint32_t *)ptr, d); + fw3_setbit(*(uint32_t *)ptr, d); } free(s); @@ -814,6 +885,39 @@ fw3_parse_mark(void *ptr, const char *val, bool is_list) 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) { @@ -836,7 +940,7 @@ 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; @@ -870,13 +974,46 @@ fw3_parse_direction(void *ptr, const char *val, bool is_list) 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; @@ -938,10 +1075,30 @@ fw3_parse_options(void *s, const struct fw3_option *opts, } 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); @@ -949,6 +1106,14 @@ fw3_parse_options(void *s, const struct fw3_option *opts, 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; + } } } @@ -966,7 +1131,7 @@ fw3_parse_options(void *s, const struct fw3_option *opts, 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; @@ -992,7 +1157,9 @@ fw3_parse_blob_options(void *s, const struct fw3_option *opts, { 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 @@ -1004,13 +1171,17 @@ fw3_parse_blob_options(void *s, const struct fw3_option *opts, 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; } @@ -1022,6 +1193,9 @@ fw3_parse_blob_options(void *s, const struct fw3_option *opts, 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); } @@ -1033,7 +1207,8 @@ fw3_parse_blob_options(void *s, const struct fw3_option *opts, { 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; } } @@ -1045,7 +1220,8 @@ fw3_parse_blob_options(void *s, const struct fw3_option *opts, { 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; } @@ -1057,8 +1233,8 @@ fw3_parse_blob_options(void *s, const struct fw3_option *opts, 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; @@ -1066,31 +1242,50 @@ fw3_parse_blob_options(void *s, const struct fw3_option *opts, const char * -fw3_address_to_string(struct fw3_address *address, bool allow_invert) +fw3_address_to_string(struct fw3_address *address, bool allow_invert, bool as_cidr) { 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->address2.v4, ip, sizeof(ip)); + &address->mask.v4, ip, sizeof(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", address->mask); + snprintf(p, rem, "/%u", + fw3_netmask2bitlen(address->family, &address->mask.v6)); } return buf;