From 020936c8c3375e1efe44a3087c891a4b2cbfe044 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 22 Jun 2010 10:49:41 +0200 Subject: ipset 5: last new feature added - the hash types can now store protocol together port, not only port - lots of fixes everywhere: parser, error reporting, manpage The last bits on the todo list before announcing ipset 5: - recheck all the error messages - add possibly more tests - polish manpage --- lib/data.c | 7 +++++ lib/parse.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/print.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/session.c | 25 ++++++++++++++++++ 4 files changed, 188 insertions(+) (limited to 'lib') diff --git a/lib/data.c b/lib/data.c index f8ff4a9..65ba209 100644 --- a/lib/data.c +++ b/lib/data.c @@ -66,6 +66,7 @@ struct ipset_data { struct { union nf_inet_addr ip2; uint8_t cidr2; + uint8_t proto; char ether[ETH_ALEN]; char name[IPSET_MAXNAMELEN]; char nameref[IPSET_MAXNAMELEN]; @@ -288,6 +289,9 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value) case IPSET_OPT_CIDR2: data->u.adt.cidr2 = *(const uint8_t *) value; break; + case IPSET_OPT_PROTO: + data->u.adt.proto = *(const uint8_t *) value; + break; /* Swap/rename */ case IPSET_OPT_SETNAME2: ipset_strncpy(data->u.setname2, value, IPSET_MAXNAMELEN); @@ -396,6 +400,8 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt) return &data->u.adt.ip2; case IPSET_OPT_CIDR2: return &data->u.adt.cidr2; + case IPSET_OPT_PROTO: + return &data->u.adt.proto; /* Swap/rename */ case IPSET_OPT_SETNAME2: return data->u.setname2; @@ -450,6 +456,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family) case IPSET_OPT_NETMASK: case IPSET_OPT_PROBES: case IPSET_OPT_RESIZE: + case IPSET_OPT_PROTO: return sizeof(uint8_t); case IPSET_OPT_ETHER: return ETH_ALEN; diff --git a/lib/parse.c b/lib/parse.c index a07168d..b1fecc7 100644 --- a/lib/parse.c +++ b/lib/parse.c @@ -31,6 +31,7 @@ #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 proto_separator(str) ipset_strchr(str, IPSET_PROTO_SEPARATOR) #define syntax_err(fmt, args...) \ ipset_err(session, "Syntax error: " fmt , ## args) @@ -280,6 +281,88 @@ error: return err; } +/** + * ipset_parse_proto - parse protocol name + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a protocol name. "any" is supported + * as a special protocol name for ipset itself. + * The parsed protocol are stored in the data blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_proto(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + uint8_t proto = 0; + + assert(session); + assert(opt == IPSET_OPT_PROTO); + assert(str); + + if (STREQ(str, "any")) + proto = IPSET_IPPROTO_ANY; + else { + struct protoent *protoent = getprotobyname(str); + if (protoent == NULL) + return syntax_err("cannot parse '%s' as a protocol name", str); + proto = protoent->p_proto; + } + if (!proto || proto == IPSET_IPPROTO_TCPUDP) + return syntax_err("invalid protocol '%s'", str); + + return ipset_session_data_set(session, opt, &proto); +} + +/** + * ipset_parse_proto_port - parse (optional) protocol and a single port + * @session: session structure + * @opt: option kind of the data + * @str: string to parse + * + * Parse string as a protocol and port, separated by a colon. + * The protocol part is optional. + * The parsed protocol and port numbers are stored in the data + * blob of the session. + * + * Returns 0 on success or a negative error code. + */ +int +ipset_parse_proto_port(struct ipset_session *session, + enum ipset_opt opt, const char *str) +{ + char *a, *saved, *tmp; + int err = 0; + + assert(session); + assert(opt == IPSET_OPT_PORT); + assert(str); + + saved = tmp = strdup(str); + if (tmp == NULL) + return ipset_err(session, + "Cannot allocate memory to duplicate %s.", + str); + + a = proto_separator(tmp); + if (a != NULL) { + /* proto:port */ + *a++ = '\0'; + err = ipset_parse_proto(session, IPSET_OPT_PROTO, tmp); + if (err) + goto error; + tmp = a; + } + err = ipset_parse_single_port(session, opt, tmp); + +error: + free(saved); + return err; +} + /** * ipset_parse_family - parse INET|INET6 family names * @session: session structure diff --git a/lib/print.c b/lib/print.c index d96e643..68f658a 100644 --- a/lib/print.c +++ b/lib/print.c @@ -435,6 +435,79 @@ ipset_print_port(char *buf, unsigned int len, return offset; } +/** + * ipset_print_proto - print protocol name + * @buf: printing buffer + * @len: length of available buffer space + * @data: data blob + * @opt: the option kind + * @env: environment flags + * + * Print protocol name to output buffer. + * + * Return lenght of printed string or error size. + */ +int +ipset_print_proto(char *buf, unsigned int len, + const struct ipset_data *data, enum ipset_opt opt, + uint8_t env UNUSED) +{ + struct protoent *protoent; + uint8_t proto; + + assert(buf); + assert(len > 0); + assert(data); + assert(opt == IPSET_OPT_PROTO); + + proto = *(uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO); + assert(proto); + + if (proto == IPSET_IPPROTO_ANY) + return snprintf(buf, len, "any"); + protoent = getprotobynumber(proto); + if (protoent) + return snprintf(buf, len, "%s", protoent->p_name); + + /* Should not happen */ + return snprintf(buf, len, "%u", proto); +} + +/** + * ipset_print_proto_port - print proto:port + * @buf: printing buffer + * @len: length of available buffer space + * @data: data blob + * @opt: the option kind + * @env: environment flags + * + * Print protocol and port to output buffer. + * + * Return lenght of printed string or error size. + */ +int +ipset_print_proto_port(char *buf, unsigned int len, + const struct ipset_data *data, enum ipset_opt opt, + uint8_t env UNUSED) +{ + int size, offset = 0; + + assert(buf); + assert(len > 0); + assert(data); + assert(opt == IPSET_OPT_PORT); + + if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_PROTO))) { + size = ipset_print_proto(buf, len, data, IPSET_OPT_PROTO, env); + SNPRINTF_FAILURE(size, len, offset); + if (len < 2) + return -ENOSPC; + strcat(buf, ":"); + SNPRINTF_FAILURE(1, len, offset); + } + return ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env); +} + #define print_second(data) \ ipset_data_flags_test(data, \ IPSET_FLAG(IPSET_OPT_PORT)|IPSET_FLAG(IPSET_OPT_ETHER)) diff --git a/lib/session.c b/lib/session.c index 36716f0..fe1e178 100644 --- a/lib/session.c +++ b/lib/session.c @@ -35,6 +35,7 @@ struct ipset_session { enum ipset_cmd cmd; /* Current command */ uint32_t lineno; /* Current lineno in restore mode */ char saved_setname[IPSET_MAXNAMELEN]; /* Saved setname */ + const struct ipset_type *saved_type; /* Saved type */ struct nlattr *nested[IPSET_NEST_MAX]; /* Pointer to nest levels */ uint8_t nestid; /* Current nest level */ bool version_checked; /* Version checked */ @@ -82,6 +83,20 @@ ipset_session_handle(const struct ipset_session *session) return session->handle; } +/** + * ipset_saved_type - return pointer to the saved type + * @session: session structure + * + * Returns the pointer to the saved type from the last ipset_cmd + * It is required to decode type-specific error codes in restore mode. + */ +const struct ipset_type * +ipset_saved_type(const struct ipset_session *session) +{ + assert(session); + return session->saved_type; +} + /* * Environment options */ @@ -327,6 +342,10 @@ const struct ipset_attr_policy create_attrs[] = { .type = MNL_TYPE_U32, .opt = IPSET_OPT_TIMEOUT, }, + [IPSET_ATTR_PROTO] = { + .type = MNL_TYPE_U8, + .opt = IPSET_OPT_PROTO, + }, [IPSET_ATTR_CADT_FLAGS] = { .type = MNL_TYPE_U32, .opt = IPSET_OPT_CADT_FLAGS, @@ -394,6 +413,10 @@ const struct ipset_attr_policy adt_attrs[] = { .type = MNL_TYPE_U16, .opt = IPSET_OPT_PORT_TO, }, + [IPSET_ATTR_PROTO] = { + .type = MNL_TYPE_U8, + .opt = IPSET_OPT_PROTO, + }, [IPSET_ATTR_TIMEOUT] = { .type = MNL_TYPE_U32, .opt = IPSET_OPT_TIMEOUT, @@ -1714,6 +1737,8 @@ ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno) goto cleanup; D("past: build_msg"); + /* We have to save the type for error handling */ + session->saved_type = ipset_data_get(data, IPSET_OPT_TYPE); /* Save setname for the next possible aggregated restore line */ if (session->lineno != 0 && (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)) { -- cgit v1.2.3