summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/data.c7
-rw-r--r--lib/parse.c83
-rw-r--r--lib/print.c73
-rw-r--r--lib/session.c25
4 files changed, 188 insertions, 0 deletions
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)
@@ -281,6 +282,88 @@ error:
}
/**
+ * 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
* @opt: option kind of the data
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)) {