From 3fd6b24ace319b139ec3c4e3031a5f05d21e304e Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 15 Jun 2010 13:30:55 +0200 Subject: ipset 5 in an almost ready state - milestone Reworked protocol and internal interfaces, missing set types added, backward compatibility verified, lots of tests added (and thanks to the tests, bugs fixed), even the manpage is rewritten ;-). Countless changes everywhere... The missing bits before announcing ipset 5: - net namespace support - new iptables/ip6tables extension library - iptables/ip6tables match and target tests (backward/forward compatibility) - tests on catching syntax errors --- lib/parse.c | 449 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 355 insertions(+), 94 deletions(-) (limited to 'lib/parse.c') diff --git a/lib/parse.c b/lib/parse.c index 0e0e7f1..e347c69 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -13,6 +13,7 @@ #include /* getaddrinfo, AF_ */ #include /* ETH_ALEN */ +#include /* D() */ #include /* IPSET_OPT_* */ #include /* prefixlen_netmask_map */ #include /* ipset_err */ @@ -22,14 +23,31 @@ /* Parse input data */ -#define ipset_cidr_separator(str) ipset_strchr(str, IPSET_CIDR_SEPARATOR) -#define ipset_range_separator(str) ipset_strchr(str, IPSET_RANGE_SEPARATOR) -#define ipset_elem_separator(str) ipset_strchr(str, IPSET_ELEM_SEPARATOR) -#define ipset_name_separator(str) ipset_strchr(str, IPSET_NAME_SEPARATOR) +#define cidr_separator(str) ipset_strchr(str, IPSET_CIDR_SEPARATOR) +#define range_separator(str) ipset_strchr(str, IPSET_RANGE_SEPARATOR) +#define elem_separator(str) ipset_strchr(str, IPSET_ELEM_SEPARATOR) +#define name_separator(str) ipset_strchr(str, IPSET_NAME_SEPARATOR) #define syntax_err(fmt, args...) \ ipset_err(session, "Syntax error: " fmt , ## args) +static char * +ipset_strchr(const char *str, const char *sep) +{ + char *match; + + assert(str); + assert(sep); + + for (; *sep != '\0'; sep++) + if ((match = strchr(str, (int)sep[0])) != NULL + && str[0] != sep[0] + && str[strlen(str)-1] != sep[0]) + return match; + + return NULL; +} + /* * Parser functions, shamelessly taken from iptables.c, ip6tables.c * and parser.c from libnetfilter_conntrack. @@ -70,33 +88,53 @@ string_to_number_ll(struct ipset_session *session, } static int -string_to_number_l(struct ipset_session *session, - const char *str, - unsigned long min, - unsigned long max, - unsigned long *ret) +string_to_u8(struct ipset_session *session, + const char *str, uint8_t *ret) { int err; - unsigned long long number = 0; + unsigned long long num = 0; - err = string_to_number_ll(session, str, min, max, &number); - *ret = (unsigned long) number; + err = string_to_number_ll(session, str, 0, 255, &num); + *ret = (uint8_t) num; return err; } static int -string_to_number(struct ipset_session *session, - const char *str, - unsigned int min, - unsigned int max, - unsigned int *ret) +string_to_cidr(struct ipset_session *session, + const char *str, uint8_t min, uint8_t max, uint8_t *ret) +{ + int err = string_to_u8(session, str, ret); + + if (!err && (*ret < min || *ret > max)) + return syntax_err("'%s' is out of range %u-%u", + str, min, max); + + return err; +} + +static int +string_to_u16(struct ipset_session *session, + const char *str, uint16_t *ret) +{ + int err; + unsigned long long num = 0; + + err = string_to_number_ll(session, str, 0, USHRT_MAX, &num); + *ret = (uint16_t) num; + + return err; +} + +static int +string_to_u32(struct ipset_session *session, + const char *str, uint32_t *ret) { int err; - unsigned long number = 0; + unsigned long long num = 0; - err = string_to_number_l(session, str, min, max, &number); - *ret = (unsigned int) number; + err = string_to_number_ll(session, str, 0, UINT_MAX, &num); + *ret = (uint32_t) num; return err; } @@ -161,12 +199,6 @@ parse_portname(struct ipset_session *session, const char *str, uint16_t *port) return syntax_err("cannot parse '%s' as a (TCP) port", str); } -static int -parse_portnum(struct ipset_session *session, const char *str, uint16_t *port) -{ - return string_to_number(session, str, 0, 65535, (unsigned int *)port); -} - /** * ipset_parse_single_port - parse a single (TCP) port number or name * @session: session structure @@ -189,7 +221,7 @@ ipset_parse_single_port(struct ipset_session *session, assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO); assert(str); - if ((err = parse_portnum(session, str, &port)) == 0 + if ((err = string_to_u16(session, str, &port)) == 0 || (err = parse_portname(session, str, &port)) == 0) err = ipset_session_data_set(session, opt, &port); @@ -229,7 +261,7 @@ ipset_parse_port(struct ipset_session *session, "Cannot allocate memory to duplicate %s.", str); - a = ipset_range_separator(tmp); + a = range_separator(tmp); if (a != NULL) { /* port-port */ *a++ = '\0'; @@ -256,14 +288,20 @@ error: * Returns 0 on success or a negative error code. */ int -ipset_parse_family(struct ipset_session *session, int opt, const char *str) +ipset_parse_family(struct ipset_session *session, + enum ipset_opt opt, const char *str) { + struct ipset_data *data; uint8_t family; assert(session); assert(opt == IPSET_OPT_FAMILY); assert(str); + data = ipset_session_data(session); + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_FAMILY))) + syntax_err("protocol family may not be specified multiple times"); + if (STREQ(str, "inet") || STREQ(str, "ipv4") || STREQ(str, "-4")) family = AF_INET; else if (STREQ(str, "inet6") || STREQ(str, "ipv6") || STREQ(str, "-6")) @@ -273,7 +311,7 @@ ipset_parse_family(struct ipset_session *session, int opt, const char *str) else return syntax_err("unknown INET family %s", str); - return ipset_session_data_set(session, opt, &family); + return ipset_data_set(data, opt, &family); } /* @@ -316,8 +354,8 @@ get_addrinfo##f(struct ipset_session *session, \ int found; \ \ if ((*info = get_addrinfo(session, str, family)) == NULL) { \ - syntax_err("cannot parse %s: resolving " \ - IP " failed", str); \ + syntax_err("cannot parse %s: " IP " resolving failed", \ + str); \ return EINVAL; \ } \ \ @@ -347,7 +385,7 @@ static int \ parse_ipv##f(struct ipset_session *session, \ enum ipset_opt opt, const char *str) \ { \ - unsigned int m = mask; \ + uint8_t m = mask; \ int aerr = EINVAL, err = 0, range = 0; \ char *saved = strdup(str); \ char *a, *tmp = saved; \ @@ -361,14 +399,14 @@ parse_ipv##f(struct ipset_session *session, \ return ipset_err(session, \ "Cannot allocate memory to duplicate %s.",\ str); \ - if ((a = ipset_cidr_separator(tmp)) != NULL) { \ + if ((a = cidr_separator(tmp)) != NULL) { \ /* IP/mask */ \ *a++ = '\0'; \ \ - if ((err = string_to_number(session, a, 0, m, &m)) != 0 \ + if ((err = string_to_cidr(session, a, 0, m, &m)) != 0 \ || (err = ipset_data_set(data, copt, &m)) != 0) \ goto out; \ - } else if ((a = ipset_range_separator(tmp)) != NULL) { \ + } else if ((a = range_separator(tmp)) != NULL) { \ /* IP-IP */ \ *a++ = '\0'; \ D("range %s", a); \ @@ -420,17 +458,17 @@ parse_ip(struct ipset_session *session, switch (addrtype) { case IPADDR_PLAIN: - if (ipset_range_separator(str) || ipset_cidr_separator(str)) + if (range_separator(str) || cidr_separator(str)) return syntax_err("plain IP address must be supplied: %s", str); break; case IPADDR_NET: - if (!ipset_cidr_separator(str) || ipset_range_separator(str)) + if (!cidr_separator(str) || range_separator(str)) return syntax_err("IP/netblock must be supplied: %s", str); break; case IPADDR_RANGE: - if (!ipset_range_separator(str) || ipset_cidr_separator(str)) + if (!range_separator(str) || cidr_separator(str)) return syntax_err("IP-IP range must supplied: %s", str); break; @@ -539,7 +577,7 @@ ipset_parse_range(struct ipset_session *session, enum ipset_opt opt, const char *str) { assert(session); - assert(opt == IPSET_OPT_IP); + assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); assert(str); return parse_ip(session, IPSET_OPT_IP, str, IPADDR_RANGE); @@ -563,15 +601,118 @@ ipset_parse_netrange(struct ipset_session *session, enum ipset_opt opt, const char *str) { assert(session); - assert(opt == IPSET_OPT_IP); + assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); assert(str); - if (!(ipset_range_separator(str) || ipset_cidr_separator(str))) - return syntax_err("IP/net or IP-IP range must be specified: %s", + if (!(range_separator(str) || cidr_separator(str))) + return syntax_err("IP/cidr or IP-IP range must be specified: %s", str); return parse_ip(session, opt, str, IPADDR_ANY); } +/** + * ipset_parse_iprange - parse IPv4|IPv6 address or range + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as an IPv4|IPv6 address pattern or a range + * of addresses separated by a dash. If family is not set yet in + * the data blob, INET is assumed. + * The value is stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_iprange(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + assert(session); + assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); + assert(str); + + if (cidr_separator(str)) + return syntax_err("IP address or IP-IP range must be specified: %s", + str); + return parse_ip(session, opt, str, IPADDR_ANY); +} + +/** + * ipset_parse_ipnet - parse IPv4|IPv6 address or address/cidr pattern + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as an IPv4|IPv6 address or address/cidr pattern. + * If family is not set yet in the data blob, INET is assumed. + * The value is stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_ipnet(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + assert(session); + assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); + assert(str); + + if (range_separator(str)) + return syntax_err("IP address or IP/cidr must be specified: %s", + str); + return parse_ip(session, opt, str, IPADDR_ANY); +} + +/** + * ipset_parse_iptimeout - parse IPv4|IPv6 address and timeout + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as an IPv4|IPv6 address and timeout parameter. + * If family is not set yet in the data blob, INET is assumed. + * The value is stored in the data blob of the session. + * + * Compatibility parser. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_iptimeout(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + char *tmp, *saved, *a; + int err; + + assert(session); + assert(opt == IPSET_OPT_IP); + assert(str); + + /* IP,timeout */ + if (ipset_data_flags_test(ipset_session_data(session), + IPSET_FLAG(IPSET_OPT_TIMEOUT))) + return syntax_err("mixed syntax, timeout already specified"); + + tmp = saved = strdup(str); + if (saved == NULL) + return ipset_err(session, + "Cannot allocate memory to duplicate %s.", + str); + + a = elem_separator(tmp); + if (a == NULL) { + free(saved); + return syntax_err("Missing separator from %s", str); + } + *a++ = '\0'; + err = parse_ip(session, opt, tmp, IPADDR_ANY); + if (!err) + err = ipset_parse_uint32(session, IPSET_OPT_TIMEOUT, a); + + free(saved); + return err; +} + #define check_setname(str, saved) \ do { \ if (strlen(str) > IPSET_MAXNAMELEN - 1) { \ @@ -584,7 +725,7 @@ do { \ /** - * ipset_parse_name - parse setname as element + * ipset_parse_name_compat - parse setname as element * @session: session structure * @opt: option kind of the data * @str: string to parse @@ -597,8 +738,8 @@ do { \ * Returns 0 on success or a negative error code. */ int -ipset_parse_name(struct ipset_session *session, - enum ipset_opt opt, const char *str) +ipset_parse_name_compat(struct ipset_session *session, + enum ipset_opt opt, const char *str) { char *saved; char *a = NULL, *b = NULL, *tmp; @@ -607,25 +748,22 @@ ipset_parse_name(struct ipset_session *session, struct ipset_data *data; assert(session); - assert(opt == IPSET_OPT_NAME || opt == IPSET_OPT_SETNAME2); + assert(opt == IPSET_OPT_NAME); assert(str); data = ipset_session_data(session); - if (opt == IPSET_OPT_SETNAME2) { - check_setname(str, NULL); - - return ipset_data_set(data, opt, str); - } + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF))) + syntax_err("mixed syntax, before|after option already used"); tmp = saved = strdup(str); if (saved == NULL) return ipset_err(session, "Cannot allocate memory to duplicate %s.", str); - if ((a = ipset_elem_separator(tmp)) != NULL) { + if ((a = elem_separator(tmp)) != NULL) { /* setname,[before|after,setname */ *a++ = '\0'; - if ((b = ipset_elem_separator(a)) != NULL) + if ((b = elem_separator(a)) != NULL) *b++ = '\0'; if (b == NULL || !(STREQ(a, "before") || STREQ(a, "after"))) { @@ -644,7 +782,7 @@ ipset_parse_name(struct ipset_session *session, if ((err = ipset_data_set(data, IPSET_OPT_NAMEREF, b)) != 0) goto out; - + if (before) err = ipset_data_set(data, IPSET_OPT_BEFORE, &before); @@ -654,12 +792,12 @@ out: } /** - * ipset_parse_setname - parse name as the name of the (current) set + * ipset_parse_setname - parse string as a setname * @session: session structure * @opt: option kind of the data * @str: string to parse * - * Parse string as the name of the (current) set. + * Parse string as a setname. * The value is stored in the data blob of the session. * * Returns 0 on success or a negative error code. @@ -669,7 +807,9 @@ ipset_parse_setname(struct ipset_session *session, enum ipset_opt opt, const char *str) { assert(session); - assert(opt == IPSET_SETNAME); + assert(opt == IPSET_SETNAME + || opt == IPSET_OPT_NAME + || opt == IPSET_OPT_SETNAME2); assert(str); check_setname(str, NULL); @@ -677,6 +817,67 @@ ipset_parse_setname(struct ipset_session *session, return ipset_session_data_set(session, opt, str); } +/** + * ipset_parse_before - parse string as "before" reference setname + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a "before" reference setname for list:set + * type of sets. The value is stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_before(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + struct ipset_data *data; + + assert(session); + assert(opt == IPSET_OPT_NAMEREF); + assert(str); + + data = ipset_session_data(session); + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF))) + syntax_err("mixed syntax, before|after option already used"); + + check_setname(str, NULL); + ipset_data_set(data, IPSET_OPT_BEFORE, str); + + return ipset_data_set(data, opt, str); +} + +/** + * ipset_parse_after - parse string as "after" reference setname + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a "after" reference setname for list:set + * type of sets. The value is stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_after(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + struct ipset_data *data; + + assert(session); + assert(opt == IPSET_OPT_NAMEREF); + assert(str); + + data = ipset_session_data(session); + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF))) + syntax_err("mixed syntax, before|after option already used"); + + check_setname(str, NULL); + + return ipset_data_set(data, opt, str); +} + /** * ipset_parse_uint32 - parse string as an unsigned integer * @session: session structure @@ -698,7 +899,7 @@ ipset_parse_uint32(struct ipset_session *session, assert(session); assert(str); - if ((err = string_to_number(session, str, 0, 0, &value)) == 0) + if ((err = string_to_u32(session, str, &value)) == 0) return ipset_session_data_set(session, opt, &value); return err; @@ -719,13 +920,13 @@ int ipset_parse_uint8(struct ipset_session *session, enum ipset_opt opt, const char *str) { - unsigned int value; + uint8_t value; int err; assert(session); assert(str); - if ((err = string_to_number(session, str, 0, 255, &value)) == 0) + if ((err = string_to_u8(session, str, &value)) == 0) return ipset_session_data_set(session, opt, &value); return err; @@ -747,7 +948,7 @@ int ipset_parse_netmask(struct ipset_session *session, enum ipset_opt opt, const char *str) { - unsigned int family, cidr; + uint8_t family, cidr; struct ipset_data *data; int err = 0; @@ -762,10 +963,10 @@ ipset_parse_netmask(struct ipset_session *session, ipset_data_set(data, IPSET_OPT_FAMILY, &family); } - err = string_to_number(session, str, - family == AF_INET ? 1 : 4, - family == AF_INET ? 31 : 124, - &cidr); + err = string_to_cidr(session, str, + family == AF_INET ? 1 : 4, + family == AF_INET ? 31 : 124, + &cidr); if (err) return syntax_err("netmask is out of the inclusive range " @@ -864,15 +1065,72 @@ ipset_parse_output(struct ipset_session *session, return syntax_err("unkown output mode '%s'", str); } +/** + * ipset_parse_ignored - "parse" ignored option + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Ignore deprecated options. A single warning is generated + * for every ignored opton. + * + * Returns 0. + */ +int +ipset_parse_ignored(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + assert(session); + assert(str); + + if (!ipset_data_ignored(ipset_session_data(session), opt)) + ipset_warn(session, + "Option %s is ignored. Please upgrade your syntax.", str); + + return 0; +} + +/** + * ipset_call_parser - call a parser function + * @session: session structure + * @parsefn: parser function + * @optstr: option name + * @opt: option kind of the data + * @str: string to parse + * + * Wrapper to call the parser functions so that ignored options + * are handled properly. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_call_parser(struct ipset_session *session, + ipset_parsefn parse, const char *optstr, + enum ipset_opt opt, const char *str) +{ + if (ipset_data_flags_test(ipset_session_data(session), + IPSET_FLAG(opt))) + syntax_err("%s already specified", optstr); + + return parse(session, opt, parse == ipset_parse_ignored + ? optstr : str); +} + #define parse_elem(s, t, d, str) \ do { \ - if (!t->elem[d].parse) \ + if (!(t)->elem[d].parse) \ goto internal; \ - err = t->elem[d].parse(s, t->elem[d].opt, str); \ - if (err) \ + ret = (t)->elem[d].parse(s, (t)->elem[d].opt, str); \ + if (ret) \ goto out; \ } while (0) +#define elem_syntax_err(fmt, args...) \ +do { \ + free(saved); \ + return syntax_err(fmt , ## args);\ +} while (0) + /** * ipset_parse_elem - parse ADT elem, depending on settype * @session: session structure @@ -890,7 +1148,7 @@ ipset_parse_elem(struct ipset_session *session, { const struct ipset_type *type; char *a = NULL, *b = NULL, *tmp, *saved; - int err; + int ret; assert(session); assert(str); @@ -906,40 +1164,43 @@ ipset_parse_elem(struct ipset_session *session, "Cannot allocate memory to duplicate %s.", str); - a = ipset_elem_separator(tmp); + a = elem_separator(tmp); if (type->dimension > IPSET_DIM_ONE) { if (a != NULL) { /* elem,elem */ *a++ = '\0'; - } else if (type->dimension > IPSET_DIM_TWO && !optional) { - free(tmp); - return syntax_err("Second element is missing from %s.", - str); + } else if (!optional) + elem_syntax_err("Second element is missing from %s.", + str); + } else if (a != NULL) { + if (type->compat_parse_elem) { + ret = type->compat_parse_elem(session, + type->elem[IPSET_DIM_ONE].opt, + saved); + goto out; } - } else if (a != NULL) - return syntax_err("Elem separator in %s, " - "but settype %s supports none.", - str, type->name); + elem_syntax_err("Elem separator in %s, " + "but settype %s supports none.", + str, type->name); + } if (a) - b = ipset_elem_separator(a); + b = elem_separator(a); if (type->dimension > IPSET_DIM_TWO) { if (b != NULL) { /* elem,elem,elem */ *b++ = '\0'; - } else if (!optional) { - free(tmp); - return syntax_err("Third element is missing from %s.", - str); - } + } else if (!optional) + elem_syntax_err("Third element is missing from %s.", + str); } else if (b != NULL) - return syntax_err("Two elem separators in %s, " - "but settype %s supports one.", - str, type->name); - if (b != NULL && ipset_elem_separator(b)) - return syntax_err("Three elem separators in %s, " - "but settype %s supports two.", - str, type->name); + elem_syntax_err("Two elem separators in %s, " + "but settype %s supports one.", + str, type->name); + if (b != NULL && elem_separator(b)) + elem_syntax_err("Three elem separators in %s, " + "but settype %s supports two.", + str, type->name); D("parse elem part one: %s", tmp); parse_elem(session, type, IPSET_DIM_ONE, tmp); @@ -954,10 +1215,10 @@ ipset_parse_elem(struct ipset_session *session, goto out; internal: - err = ipset_err(session, + ret = ipset_err(session, "Internal error: missing parser function for %s", type->name); out: free(saved); - return err; + return ret; } -- cgit v1.2.3