summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-10-24 21:42:48 +0200
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-10-24 21:42:48 +0200
commit0d32c5c070f817229110f92d7b31df9a3e4eeec5 (patch)
tree5ee04a9e03de32d2029ad0d708811e382ca922d1 /lib
parent62a3d29539aa109fed1c8a20d63ef95948b13842 (diff)
Fixes, cleanups, commentsv5.0-pre8
- More comments added to the code - ICMP and ICMPv6 support added to the hash:ip,port, hash:ip,port,ip and hash:ip,port,net types - hash:net and hash:ip,port,net types are reworked - hash:net,port type added - Wrong direction parameters fixed in hash:ip,port - Helps and manpage are updated - More tests added - Ugly macros are rewritten to functions in parse.c (Holger Eitzenberger) - resize related bug in hash types fixed (Holger Eitzenberger) - autoreconf patches by Jan Engelhardt applied - netlink patch minimalized: dumping can be initialized by a second parsing of the message (thanks to David and Patrick for the suggestion) - IPv4/IPv6 address attributes are introduced in order to fix the context (suggested by David)
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am6
-rw-r--r--lib/data.c15
-rw-r--r--lib/icmp.c79
-rw-r--r--lib/icmpv6.c66
-rw-r--r--lib/mnl.c17
-rw-r--r--lib/parse.c413
-rw-r--r--lib/print.c95
-rw-r--r--lib/session.c161
-rw-r--r--lib/types.c16
9 files changed, 664 insertions, 204 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index bf4e133..7913877 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,13 +1,15 @@
include $(top_srcdir)/Make_global.am
-AM_CFLAGS += -fPIC
-LIBS =
+AM_CFLAGS += ${libmnl_CFLAGS}
lib_LTLIBRARIES = libipset.la
libipset_la_LDFLAGS = -version-info $(LIBVERSION)
+libipset_la_LIBADD = ${libmnl_LIBS}
libipset_la_SOURCES = \
data.c \
+ icmp.c \
+ icmpv6.c \
mnl.c \
parse.c \
print.c \
diff --git a/lib/data.c b/lib/data.c
index 65ba209..2872c0d 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -31,6 +31,7 @@ struct ipset_data {
uint64_t ignored;
/* Setname */
char setname[IPSET_MAXNAMELEN];
+ /* Set type */
const struct ipset_type *type;
/* Common CADT options */
uint8_t cidr;
@@ -161,7 +162,7 @@ do { \
/**
* ipset_data_ignored - test and set ignored bits in the data blob
* @data: data blob
- * @flags: the option flags which is ignored
+ * @flags: the option flag to be ignored
*
* Returns true if the option was not already ignored.
*/
@@ -205,6 +206,7 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
break;
case IPSET_OPT_FAMILY:
data->family = *(const uint8_t *) value;
+ D("family set to %u", data->family);
break;
/* CADT options */
case IPSET_OPT_IP:
@@ -263,7 +265,8 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
break;
/* Create-specific options, type */
case IPSET_OPT_TYPENAME:
- ipset_strncpy(data->u.create.typename, value, IPSET_MAXNAMELEN);
+ ipset_strncpy(data->u.create.typename, value,
+ IPSET_MAXNAMELEN);
break;
case IPSET_OPT_REVISION:
data->u.create.revision = *(const uint8_t *) value;
@@ -332,7 +335,7 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
assert(data);
assert(opt != IPSET_OPT_NONE);
- if (opt != IPSET_OPT_TYPENAME && !ipset_data_test(data, opt))
+ if (!(opt == IPSET_OPT_TYPENAME || ipset_data_test(data, opt)))
return NULL;
switch (opt) {
@@ -418,11 +421,11 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
}
/**
- * ipset_data_sizeof - calculates the size for the type of data
+ * ipset_data_sizeof - calculates the size of the data type
* @opt: option kind of the data
* @family: INET family
*
- * Returns the size required to store the given option kind.
+ * Returns the size required to store the given data type.
*/
size_t
ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
@@ -502,7 +505,7 @@ ipset_data_family(const struct ipset_data *data)
* @data: data blob
*
* Return the value of IPSET_OPT_CIDR stored in the data blob.
- * If it is not set, the the returned value corresponds to
+ * If it is not set, then the returned value corresponds to
* the default one according to the family type or zero.
*/
uint8_t
diff --git a/lib/icmp.c b/lib/icmp.c
new file mode 100644
index 0000000..93276e2
--- /dev/null
+++ b/lib/icmp.c
@@ -0,0 +1,79 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <libipset/utils.h> /* STRNEQ */
+#include <libipset/icmp.h> /* prototypes */
+
+struct icmp_names {
+ const char *name;
+ uint8_t type, code;
+};
+
+static const struct icmp_names icmp_typecodes[] = {
+ { "echo-reply", 0, 0 },
+ { "pong", 0, 0 },
+ { "network-unreachable", 3, 0 },
+ { "host-unreachable", 3, 1 },
+ { "protocol-unreachable", 3, 2 },
+ { "port-unreachable", 3, 3 },
+ { "fragmentation-needed", 3, 4 },
+ { "source-route-failed", 3, 5 },
+ { "network-unknown", 3, 6 },
+ { "host-unknown", 3, 7 },
+ { "network-prohibited", 3, 9 },
+ { "host-prohibited", 3, 10 },
+ { "TOS-network-unreachable", 3, 11 },
+ { "TOS-host-unreachable", 3, 12 },
+ { "communication-prohibited", 3, 13 },
+ { "host-precedence-violation", 3, 14 },
+ { "precedence-cutoff", 3, 15 },
+ { "source-quench", 4, 0 },
+ { "network-redirect", 5, 0 },
+ { "host-redirect", 5, 1 },
+ { "TOS-network-redirect", 5, 2 },
+ { "TOS-host-redirect", 5, 3 },
+ { "echo-request", 8, 0 },
+ { "ping", 8, 0 },
+ { "router-advertisement", 9, 0 },
+ { "router-solicitation", 10, 0 },
+ { "ttl-zero-during-transit", 11, 0 },
+ { "ttl-zero-during-reassembly", 11, 1 },
+ { "ip-header-bad", 12, 0 },
+ { "required-option-missing", 12, 1 },
+ { "timestamp-request", 13, 0 },
+ { "timestamp-reply", 14, 0 },
+ { "address-mask-request", 17, 0 },
+ { "address-mask-reply", 18, 0 },
+};
+
+const char * id_to_icmp(uint8_t id)
+{
+ return id < ARRAY_SIZE(icmp_typecodes) ? icmp_typecodes[id].name : NULL;
+}
+
+const char * icmp_to_name(uint8_t type, uint8_t code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(icmp_typecodes); i++)
+ if (icmp_typecodes[i].type == type && icmp_typecodes[i].code == code)
+ return icmp_typecodes[i].name;
+
+ return NULL;
+}
+
+int name_to_icmp(const char *str, uint16_t *typecode)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(icmp_typecodes); i++)
+ if (STRNEQ(icmp_typecodes[i].name, str, strlen(str))) {
+ *typecode = (icmp_typecodes[i].type << 8) | icmp_typecodes[i].code;
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/lib/icmpv6.c b/lib/icmpv6.c
new file mode 100644
index 0000000..c32a6a4
--- /dev/null
+++ b/lib/icmpv6.c
@@ -0,0 +1,66 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <libipset/utils.h> /* STRNEQ */
+#include <libipset/icmpv6.h> /* prototypes */
+
+struct icmpv6_names {
+ const char *name;
+ uint8_t type, code;
+};
+
+static const struct icmpv6_names icmpv6_typecodes[] = {
+ { "no-route", 1, 0 },
+ { "communication-prohibited", 1, 1 },
+ { "address-unreachable", 1, 3 },
+ { "port-unreachable", 1, 4 },
+ { "packet-too-big", 2, 0 },
+ { "ttl-zero-during-transit", 3, 0 },
+ { "ttl-zero-during-reassembly", 3, 1 },
+ { "bad-header", 4, 0 },
+ { "unknown-header-type", 4, 1 },
+ { "unknown-option", 4, 2 },
+ { "echo-request", 128, 0 },
+ { "ping", 128, 0 },
+ { "echo-reply", 129, 0 },
+ { "pong", 129, 0 },
+ { "router-solicitation", 133, 0 },
+ { "router-advertisement", 134, 0 },
+ { "neighbour-solicitation", 135, 0 },
+ { "neigbour-solicitation", 135, 0 },
+ { "neighbour-advertisement", 136, 0 },
+ { "neigbour-advertisement", 136, 0 },
+ { "redirect", 137, 0 },
+};
+
+const char * id_to_icmpv6(uint8_t id)
+{
+ return id < ARRAY_SIZE(icmpv6_typecodes) ? icmpv6_typecodes[id].name : NULL;
+}
+
+const char * icmpv6_to_name(uint8_t type, uint8_t code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); i++)
+ if (icmpv6_typecodes[i].type == type && icmpv6_typecodes[i].code == code)
+ return icmpv6_typecodes[i].name;
+
+ return NULL;
+}
+
+int name_to_icmpv6(const char *str, uint16_t *typecode)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); i++)
+ if (STRNEQ(icmpv6_typecodes[i].name, str, strlen(str))) {
+ *typecode = (icmpv6_typecodes[i].type << 8) | icmpv6_typecodes[i].code;
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/lib/mnl.c b/lib/mnl.c
index 8056427..aa98a7c 100644
--- a/lib/mnl.c
+++ b/lib/mnl.c
@@ -21,12 +21,13 @@
#define NFNL_SUBSYS_IPSET 6
#endif
+/* Internal data structure for the kernel-userspace communication parameters */
struct ipset_handle {
- struct mnl_socket *h;
- unsigned int seq;
- unsigned int portid;
- mnl_cb_t *cb_ctl;
- void *data;
+ struct mnl_socket *h; /* the mnl socket */
+ unsigned int seq; /* netlink message sequence number */
+ unsigned int portid; /* the socket port identifier */
+ mnl_cb_t *cb_ctl; /* control block callbacks */
+ void *data; /* data pointer */
};
/* Netlink flags of the commands */
@@ -46,6 +47,12 @@ static uint16_t cmdflags[] = {
[IPSET_CMD_PROTOCOL-1] = NLM_F_REQUEST,
};
+/**
+ * ipset_get_nlmsg_type - get ipset netlink message type
+ * @nlh: pointer to the netlink message header
+ *
+ * Returns the ipset netlink message type, i.e. the ipset command.
+ */
int
ipset_get_nlmsg_type(const struct nlmsghdr *nlh)
{
diff --git a/lib/parse.c b/lib/parse.c
index 84b6a3f..28192d8 100644
--- a/lib/parse.c
+++ b/lib/parse.c
@@ -12,9 +12,12 @@
#include <sys/types.h> /* getaddrinfo */
#include <sys/socket.h> /* getaddrinfo, AF_ */
#include <net/ethernet.h> /* ETH_ALEN */
+#include <netinet/in.h> /* IPPROTO_ */
#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* IPSET_OPT_* */
+#include <libipset/icmp.h> /* name_to_icmp */
+#include <libipset/icmpv6.h> /* name_to_icmpv6 */
#include <libipset/pfxlen.h> /* prefixlen_netmask_map */
#include <libipset/session.h> /* ipset_err */
#include <libipset/types.h> /* ipset_type_get */
@@ -192,32 +195,35 @@ error:
* Parse TCP service names or port numbers
*/
static int
-parse_portname(struct ipset_session *session, const char *str, uint16_t *port)
+parse_portname(struct ipset_session *session, const char *str,
+ uint16_t *port, const char *proto)
{
struct servent *service;
- if ((service = getservbyname(str, "tcp")) != NULL) {
+ if ((service = getservbyname(str, proto)) != NULL) {
*port = ntohs((uint16_t) service->s_port);
return 0;
}
- return syntax_err("cannot parse '%s' as a (TCP) port", str);
+ return syntax_err("cannot parse '%s' as a %s port", str, proto);
}
/**
- * ipset_parse_single_port - parse a single (TCP) port number or name
+ * ipset_parse_single_port - parse a single port number or name
* @session: session structure
* @opt: option kind of the data
* @str: string to parse
+ * @proto: protocol
*
- * Parse string as a single (TCP) port number or name. The parsed port
+ * Parse string as a single port number or name. The parsed port
* number is stored in the data blob of the session.
*
* Returns 0 on success or a negative error code.
*/
int
-ipset_parse_single_port(struct ipset_session *session,
- enum ipset_opt opt, const char *str)
+ipset_parse_port(struct ipset_session *session,
+ enum ipset_opt opt, const char *str,
+ const char *proto)
{
uint16_t port;
int err;
@@ -227,31 +233,31 @@ ipset_parse_single_port(struct ipset_session *session,
assert(str);
if ((err = string_to_u16(session, str, &port)) == 0
- || (err = parse_portname(session, str, &port)) == 0)
+ || (err = parse_portname(session, str, &port, proto)) == 0)
err = ipset_session_data_set(session, opt, &port);
if (!err)
- /* No error, so reset session messages! */
+ /* No error, so reset false error messages! */
ipset_session_report_reset(session);
return err;
}
/**
- * ipset_parse_port - parse (TCP) port name, number, or range of them
+ * ipset_parse_tcp_port - parse TCP port name, number, or range of them
* @session: session structure
* @opt: option kind of the data
* @str: string to parse
*
- * Parse string as a TCP port name or number or range of them.
+ * Parse string as a TCP port name or number or range of them
* separated by a dash. The parsed port numbers are stored
* in the data blob of the session.
*
* Returns 0 on success or a negative error code.
*/
int
-ipset_parse_port(struct ipset_session *session,
- enum ipset_opt opt, const char *str)
+ipset_parse_tcp_port(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
{
char *a, *saved, *tmp;
int err = 0;
@@ -270,11 +276,11 @@ ipset_parse_port(struct ipset_session *session,
if (a != NULL) {
/* port-port */
*a++ = '\0';
- err = ipset_parse_single_port(session, IPSET_OPT_PORT_TO, a);
+ err = ipset_parse_port(session, IPSET_OPT_PORT_TO, a, "TCP");
if (err)
goto error;
}
- err = ipset_parse_single_port(session, opt, tmp);
+ err = ipset_parse_port(session, opt, tmp, "TCP");
error:
free(saved);
@@ -282,14 +288,36 @@ error:
}
/**
+ * ipset_parse_single_tcp_port - parse TCP port name or number
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a single TCP port name or number.
+ * The parsed port number is stored
+ * in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_single_tcp_port(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ assert(session);
+ assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO);
+ assert(str);
+
+ return ipset_parse_port(session, opt, str, "TCP");
+}
+
+/**
* 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.
+ * Parse string as a protocol name.
+ * The parsed protocol is stored in the data blob of the session.
*
* Returns 0 on success or a negative error code.
*/
@@ -297,26 +325,114 @@ int
ipset_parse_proto(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
+ struct protoent *protoent;
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);
+ protoent = getprotobyname(strcasecmp(str, "icmpv6") == 0
+ ? "ipv6-icmp" : str);
+ if (protoent == NULL)
+ return syntax_err("cannot parse '%s' "
+ "as a protocol name", str);
+ proto = protoent->p_proto;
+ if (!proto)
+ return syntax_err("Unsupported protocol '%s'", str);
return ipset_session_data_set(session, opt, &proto);
}
+/* Parse ICMP and ICMPv6 type/code */
+static int
+parse_icmp_typecode(struct ipset_session *session,
+ enum ipset_opt opt, const char *str,
+ const char *family)
+{
+ uint16_t typecode;
+ uint8_t type, code;
+ char *a, *saved, *tmp;
+ int err;
+
+ saved = tmp = strdup(str);
+ if (tmp == NULL)
+ return ipset_err(session,
+ "Cannot allocate memory to duplicate %s.",
+ str);
+ a = cidr_separator(tmp);
+ if (a == NULL) {
+ free(saved);
+ return ipset_err(session,
+ "Cannot parse %s as an %s type/code.", str, family);
+ }
+ *a++ = '\0';
+ if ((err = string_to_u8(session, a, &type)) != 0
+ || (err = string_to_u8(session, tmp, &code)) != 0)
+ goto error;
+
+ typecode = (type << 8) | code;
+ err = ipset_session_data_set(session, opt, &typecode);
+
+error:
+ free(saved);
+ return err;
+}
+
+/**
+ * ipset_parse_icmp - parse an ICMP name or type/code
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an ICMP name or type/code numbers.
+ * The parsed ICMP type/code is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_icmp(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ uint16_t typecode;
+
+ assert(session);
+ assert(opt == IPSET_OPT_PORT);
+ assert(str);
+
+ if (name_to_icmp(str, &typecode) < 0)
+ return parse_icmp_typecode(session, opt, str, "ICMP");
+
+ return ipset_session_data_set(session, opt, &typecode);
+}
+
+/**
+ * ipset_parse_icmpv6 - parse an ICMPv6 name or type/code
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as an ICMPv6 name or type/code numbers.
+ * The parsed ICMPv6 type/code is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_icmpv6(struct ipset_session *session,
+ enum ipset_opt opt, const char *str)
+{
+ uint16_t typecode;
+
+ assert(session);
+ assert(opt == IPSET_OPT_PORT);
+ assert(str);
+
+ if (name_to_icmpv6(str, &typecode) < 0)
+ return parse_icmp_typecode(session, opt, str, "ICMPv6");
+
+ return ipset_session_data_set(session, opt, &typecode);
+}
+
/**
* ipset_parse_proto_port - parse (optional) protocol and a single port
* @session: session structure
@@ -334,13 +450,17 @@ int
ipset_parse_proto_port(struct ipset_session *session,
enum ipset_opt opt, const char *str)
{
+ struct ipset_data *data;
char *a, *saved, *tmp;
+ const char *proto;
+ uint8_t p = IPPROTO_TCP;
int err = 0;
assert(session);
assert(opt == IPSET_OPT_PORT);
assert(str);
+ data = ipset_session_data(session);
saved = tmp = strdup(str);
if (tmp == NULL)
return ipset_err(session,
@@ -349,14 +469,54 @@ ipset_parse_proto_port(struct ipset_session *session,
a = proto_separator(tmp);
if (a != NULL) {
+ uint8_t family = ipset_data_family(data);
+
/* proto:port */
*a++ = '\0';
err = ipset_parse_proto(session, IPSET_OPT_PROTO, tmp);
if (err)
goto error;
- tmp = a;
+
+ p = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO);
+ switch (p) {
+ case IPPROTO_TCP:
+ proto = tmp;
+ tmp = a;
+ goto parse_port;
+ case IPPROTO_UDP:
+ proto = tmp;
+ tmp = a;
+ goto parse_port;
+ case IPPROTO_ICMP:
+ if (family != AF_INET) {
+ syntax_err("Protocol ICMP can be used with family INET only");
+ goto error;
+ }
+ err = ipset_parse_icmp(session, opt, a);
+ break;
+ case IPPROTO_ICMPV6:
+ if (family != AF_INET6) {
+ syntax_err("Protocol ICMPv6 can be used with family INET6 only");
+ goto error;
+ }
+ err = ipset_parse_icmpv6(session, opt, a);
+ break;
+ default:
+ if (!STREQ(a, "0")) {
+ syntax_err("Protocol %s can be used with pseudo port value 0 only.");
+ goto error;
+ }
+ ipset_data_flags_set(data, IPSET_FLAG(opt));
+ }
+ goto error;
+ } else {
+ proto = "TCP";
+ err = ipset_data_set(data, IPSET_OPT_PROTO, &p);
+ if (err)
+ goto error;
}
- err = ipset_parse_single_port(session, opt, tmp);
+parse_port:
+ err = ipset_parse_port(session, opt, tmp, proto);
error:
free(saved);
@@ -387,7 +547,8 @@ ipset_parse_family(struct ipset_session *session,
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");
+ syntax_err("protocol family may not be specified "
+ "multiple times");
if (STREQ(str, "inet") || STREQ(str, "ipv4") || STREQ(str, "-4"))
family = AF_INET;
@@ -402,12 +563,13 @@ ipset_parse_family(struct ipset_session *session,
}
/*
- * Parse IPv4/IPv6 addresses, networks and ranges
+ * Parse IPv4/IPv6 addresses, networks and ranges.
* We resolve hostnames but just the first IP address is used.
*/
static struct addrinfo *
-get_addrinfo(struct ipset_session *session, const char *str, uint8_t family)
+call_getaddrinfo(struct ipset_session *session, const char *str,
+ uint8_t family)
{
struct addrinfo hints;
struct addrinfo *res;
@@ -429,101 +591,96 @@ get_addrinfo(struct ipset_session *session, const char *str, uint8_t family)
return res;
}
-#define GET_ADDRINFO(family, IP, f, n) \
-static int \
-get_addrinfo##f(struct ipset_session *session, \
- const char *str, \
- struct addrinfo **info, \
- struct in##n##_addr **inaddr) \
-{ \
- struct addrinfo *i; \
- struct sockaddr_in##n saddr; \
- int found; \
- \
- if ((*info = get_addrinfo(session, str, family)) == NULL) { \
- syntax_err("cannot parse %s: " IP " resolving failed", \
- str); \
- return EINVAL; \
- } \
- \
- for (i = *info, found = 0; i != NULL; i = i->ai_next) { \
- if (i->ai_family != family \
- || i->ai_addrlen != sizeof(saddr)) \
- continue; \
- if (found == 0) { \
- /* Workaround: can't cast on Sparc */ \
- memcpy(&saddr, i->ai_addr, sizeof(saddr)); \
- *inaddr = &saddr.sin##n##_addr; \
- } else if (found == 1) { \
- ipset_warn(session, \
- "%s resolves to multiple addresses: " \
- "using only the first one returned by the resolver", \
- str); \
- } \
- found++; \
- } \
- if (found == 0) \
- return syntax_err("cannot parse %s: " \
- IP "address could not be resolved", \
- str); \
- return 0; \
+static int
+get_addrinfo(struct ipset_session *session,
+ enum ipset_opt opt,
+ const char *str,
+ struct addrinfo **info,
+ uint8_t family)
+{
+ struct addrinfo *i;
+ size_t addrlen = family == AF_INET ? sizeof(struct sockaddr_in)
+ : sizeof(struct sockaddr_in6);
+ int found, err = 0;
+
+ if ((*info = call_getaddrinfo(session, str, family)) == NULL) {
+ syntax_err("cannot parse %s: resolving to %s address failed",
+ str, family == AF_INET ? "IPv4" : "IPv6");
+ return EINVAL;
+ }
+
+ for (i = *info, found = 0; i != NULL; i = i->ai_next) {
+ if (i->ai_family != family || i->ai_addrlen != addrlen)
+ continue;
+ if (found == 0) {
+ if (family == AF_INET) {
+ err = ipset_session_data_set(session, opt,
+ &((const struct sockaddr_in *)i->ai_addr)->sin_addr);
+ } else {
+ err = ipset_session_data_set(session, opt,
+ &((const struct sockaddr_in6 *)i->ai_addr)->sin6_addr);
+ }
+ } else if (found == 1) {
+ ipset_warn(session,
+ "%s resolves to multiple addresses: "
+ "using only the first one returned "
+ "by the resolver",
+ str);
+ }
+ found++;
+ }
+ if (found == 0)
+ return syntax_err("cannot parse %s: "
+ "%s address could not be resolved",
+ str, family == AF_INET ? "IPv4" : "IPv6");
+ return err;
}
-#define PARSE_IP(mask, f, n) \
-static int \
-parse_ipv##f(struct ipset_session *session, \
- enum ipset_opt opt, const char *str) \
-{ \
- uint8_t m = mask; \
- int aerr = EINVAL, err = 0, range = 0; \
- char *saved = strdup(str); \
- char *a, *tmp = saved; \
- struct addrinfo *info; \
- struct in##n##_addr *inaddr; \
- struct ipset_data *data = ipset_session_data(session); \
- enum ipset_opt copt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR \
- : IPSET_OPT_CIDR2; \
- \
- if (tmp == NULL) \
- return ipset_err(session, \
- "Cannot allocate memory to duplicate %s.",\
- str); \
- if ((a = cidr_separator(tmp)) != NULL) { \
- /* IP/mask */ \
- *a++ = '\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 = range_separator(tmp)) != NULL) { \
- /* IP-IP */ \
- *a++ = '\0'; \
- D("range %s", a); \
- range++; \
- } \
- if ((aerr = get_addrinfo##f(session, tmp, &info, &inaddr)) != 0 \
- || (err = ipset_data_set(data, opt, inaddr)) != 0 \
- || !range) \
- goto out; \
- freeaddrinfo(info); \
- if ((aerr = get_addrinfo##f(session, a, &info, &inaddr)) == 0) \
- err = ipset_data_set(data, IPSET_OPT_IP_TO, inaddr); \
- \
-out: \
- if (aerr != EINVAL) \
- /* getaddrinfo not failed */ \
- freeaddrinfo(info); \
- else if (aerr) \
- err = -1; \
- free(saved); \
- return err; \
-}
+static int
+parse_ipaddr(struct ipset_session *session,
+ enum ipset_opt opt, const char *str,
+ uint8_t family)
+{
+ uint8_t m = family == AF_INET ? 32 : 128;
+ int aerr = EINVAL, err = 0, range = 0;
+ char *saved = strdup(str);
+ char *a, *tmp = saved;
+ struct addrinfo *info;
+ enum ipset_opt copt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR
+ : IPSET_OPT_CIDR2;
-GET_ADDRINFO(AF_INET, "IPv4", 4, )
-PARSE_IP(32, 4, )
+ if (tmp == NULL)
+ return ipset_err(session,
+ "Cannot allocate memory to duplicate %s.",
+ str);
+ if ((a = cidr_separator(tmp)) != NULL) {
+ /* IP/mask */
+ *a++ = '\0';
-GET_ADDRINFO(AF_INET6, "IPv6", 6, 6)
-PARSE_IP(128, 6, 6)
+ if ((err = string_to_cidr(session, a, 0, m, &m)) != 0
+ || (err = ipset_session_data_set(session, copt, &m)) != 0)
+ goto out;
+ } else if ((a = range_separator(tmp)) != NULL) {
+ /* IP-IP */
+ *a++ = '\0';
+ D("range %s", a);
+ range++;
+ }
+ if ((aerr = get_addrinfo(session, opt, tmp, &info, family)) != 0
+ || !range)
+ goto out;
+ freeaddrinfo(info);
+ aerr = get_addrinfo(session, IPSET_OPT_IP_TO, a, &info, family);
+
+out:
+ if (aerr != EINVAL)
+ /* getaddrinfo not failed */
+ freeaddrinfo(info);
+ else if (aerr)
+ err = -1;
+ free(saved);
+ return err;
+}
enum ipaddr_type {
IPADDR_ANY,
@@ -536,7 +693,6 @@ static int
parse_ip(struct ipset_session *session,
enum ipset_opt opt, const char *str, enum ipaddr_type addrtype)
{
- int err = 0;
struct ipset_data *data = ipset_session_data(session);
uint8_t family = ipset_data_family(data);
@@ -566,12 +722,7 @@ parse_ip(struct ipset_session *session,
break;
}
- if (family == AF_INET)
- err = parse_ipv4(session, opt, str);
- else
- err = parse_ipv6(session, opt, str);
-
- return err;
+ return parse_ipaddr(session, opt, str, family);
}
/**
diff --git a/lib/print.c b/lib/print.c
index 77c283a..87a9f2b 100644
--- a/lib/print.c
+++ b/lib/print.c
@@ -15,6 +15,8 @@
#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* ipset_data_* */
+#include <libipset/icmp.h> /* icmp_to_name */
+#include <libipset/icmpv6.h> /* icmpv6_to_name */
#include <libipset/parse.h> /* IPSET_*_SEPARATOR */
#include <libipset/types.h> /* ipset set types */
#include <libipset/session.h> /* IPSET_FLAG_ */
@@ -463,8 +465,6 @@ ipset_print_proto(char *buf, unsigned int len,
proto = *(const 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);
@@ -474,6 +474,72 @@ ipset_print_proto(char *buf, unsigned int len,
}
/**
+ * ipset_print_icmp - print ICMP code name or type/code
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print ICMP code name or type/code name to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_icmp(char *buf, unsigned int len,
+ const struct ipset_data *data, enum ipset_opt opt,
+ uint8_t env UNUSED)
+{
+ const char *name;
+ uint16_t typecode;
+
+ assert(buf);
+ assert(len > 0);
+ assert(data);
+ assert(opt == IPSET_OPT_PORT);
+
+ typecode = *(const uint16_t *) ipset_data_get(data, IPSET_OPT_PORT);
+ name = icmp_to_name(typecode >> 8, typecode & 0xFF);
+ if (name != NULL)
+ return snprintf(buf, len, "%s", name);
+ else
+ return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF);
+}
+
+/**
+ * ipset_print_icmpv6 - print ICMPv6 code name or type/code
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * Print ICMPv6 code name or type/code name to output buffer.
+ *
+ * Return lenght of printed string or error size.
+ */
+int
+ipset_print_icmpv6(char *buf, unsigned int len,
+ const struct ipset_data *data, enum ipset_opt opt,
+ uint8_t env UNUSED)
+{
+ const char *name;
+ uint16_t typecode;
+
+ assert(buf);
+ assert(len > 0);
+ assert(data);
+ assert(opt == IPSET_OPT_PORT);
+
+ typecode = *(const uint16_t *) ipset_data_get(data, IPSET_OPT_PORT);
+ name = icmpv6_to_name(typecode >> 8, typecode & 0xFF);
+ if (name != NULL)
+ return snprintf(buf, len, "%s", name);
+ else
+ return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF);
+}
+
+/**
* ipset_print_proto_port - print proto:port
* @buf: printing buffer
* @len: length of available buffer space
@@ -498,14 +564,33 @@ ipset_print_proto_port(char *buf, unsigned int len,
assert(opt == IPSET_OPT_PORT);
if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_PROTO))) {
+ uint8_t proto = *(const uint8_t *) ipset_data_get(data,
+ 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);
+ size = snprintf(buf + offset, len, IPSET_PROTO_SEPARATOR);
+ SNPRINTF_FAILURE(size, len, offset);
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ break;
+ case IPPROTO_ICMP:
+ return ipset_print_icmp(buf + offset, len, data,
+ IPSET_OPT_PORT, env);
+ case IPPROTO_ICMPV6:
+ return ipset_print_icmpv6(buf + offset, len, data,
+ IPSET_OPT_PORT, env);
+ default:
+ break;
+ }
}
- return ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env);
+ size = ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env);
+ SNPRINTF_FAILURE(size, len, offset);
+
+ return offset;
}
#define print_second(data) \
diff --git a/lib/session.c b/lib/session.c
index 8a0493a..ba4e458 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -319,11 +319,11 @@ const struct ipset_attr_policy cmd_attrs[] = {
const struct ipset_attr_policy create_attrs[] = {
[IPSET_ATTR_IP] = {
- .type = MNL_TYPE_BINARY,
+ .type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP,
},
[IPSET_ATTR_IP_TO] = {
- .type = MNL_TYPE_BINARY,
+ .type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP_TO,
},
[IPSET_ATTR_CIDR] = {
@@ -394,11 +394,11 @@ const struct ipset_attr_policy create_attrs[] = {
const struct ipset_attr_policy adt_attrs[] = {
[IPSET_ATTR_IP] = {
- .type = MNL_TYPE_BINARY,
+ .type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP,
},
[IPSET_ATTR_IP_TO] = {
- .type = MNL_TYPE_BINARY,
+ .type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP_TO,
},
[IPSET_ATTR_CIDR] = {
@@ -445,7 +445,7 @@ const struct ipset_attr_policy adt_attrs[] = {
.len = IPSET_MAXNAMELEN,
},
[IPSET_ATTR_IP2] = {
- .type = MNL_TYPE_BINARY,
+ .type = MNL_TYPE_NESTED,
.opt = IPSET_OPT_IP2,
},
[IPSET_ATTR_CIDR2] = {
@@ -454,6 +454,61 @@ const struct ipset_attr_policy adt_attrs[] = {
},
};
+const struct ipset_attr_policy ipaddr_attrs[] = {
+ [IPSET_ATTR_IPADDR_IPV4] = {
+ .type = MNL_TYPE_U32,
+ },
+ [IPSET_ATTR_IPADDR_IPV6] = {
+ .type = MNL_TYPE_BINARY,
+ .len = sizeof(union nf_inet_addr),
+ },
+};
+
+static int
+generic_data_attr_cb(const struct nlattr *attr, void *data,
+ int attr_max, const struct ipset_attr_policy *policy)
+{
+ const struct nlattr **tb = (const struct nlattr **)data;
+ int type = mnl_attr_get_type(attr);
+
+ D("attr type: %u, len %u", type, attr->nla_len);
+ if (mnl_attr_type_valid(attr, attr_max) < 0) {
+ D("attr type: %u INVALID", type);
+ return MNL_CB_ERROR;
+ }
+ if (mnl_attr_validate(attr, policy[type].type) < 0) {
+ D("attr type: %u POLICY, attrlen %u", type,
+ mnl_attr_get_payload_len(attr));
+ return MNL_CB_ERROR;
+ }
+ if (policy[type].type == MNL_TYPE_NUL_STRING
+ && mnl_attr_get_payload_len(attr) > IPSET_MAXNAMELEN)
+ return MNL_CB_ERROR;
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+create_attr_cb(const struct nlattr *attr, void *data)
+{
+ return generic_data_attr_cb(attr, data,
+ IPSET_ATTR_CREATE_MAX, create_attrs);
+}
+
+static int
+adt_attr_cb(const struct nlattr *attr, void *data)
+{
+ return generic_data_attr_cb(attr, data,
+ IPSET_ATTR_ADT_MAX, adt_attrs);
+}
+
+static int
+ipaddr_attr_cb(const struct nlattr *attr, void *data)
+{
+ return generic_data_attr_cb(attr, data,
+ IPSET_ATTR_IPADDR_MAX, ipaddr_attrs);
+}
+
#define FAILURE(format, args...) \
{ ipset_err(session, format , ## args); return MNL_CB_ERROR; }
@@ -469,26 +524,45 @@ attr2data(struct ipset_session *session, struct nlattr *nla[],
attr = &attrs[type];
d = mnl_attr_get_payload(nla[type]);
- if (attr->type == MNL_TYPE_BINARY && !attr->len) {
+ if (attr->type == MNL_TYPE_NESTED && attr->opt) {
+ /* IP addresses */
+ struct nlattr *ipattr[IPSET_ATTR_IPADDR_MAX+1] = {};
uint8_t family = ipset_data_family(data);
+ int atype;
+ D("attr type %u", type);
+ if (mnl_attr_parse_nested(nla[type],
+ ipaddr_attr_cb, ipattr) < 0)
+ FAILURE("Broken kernel message, cannot validate "
+ "IP address attribute!");
/* Validate by hand */
switch (family) {
case AF_INET:
- if (nla[type]->nla_len < sizeof(uint32_t))
+ atype = IPSET_ATTR_IPADDR_IPV4;
+ if (!ipattr[atype])
+ FAILURE("Broken kernel message: IPv4 address "
+ "expected but not received!");
+ if (ipattr[atype]->nla_len < sizeof(uint32_t))
FAILURE("Broken kernel message: "
- "cannot validate IPv4 address attribute!");
+ "cannot validate IPv4 "
+ "address attribute!");
break;
case AF_INET6:
- if (nla[type]->nla_len < sizeof(struct in6_addr))
+ atype = IPSET_ATTR_IPADDR_IPV6;
+ if (!ipattr[atype])
+ FAILURE("Broken kernel message: IPv6 address "
+ "expected but not received!");
+ if (ipattr[atype]->nla_len < sizeof(struct in6_addr))
FAILURE("Broken kernel message: "
- "cannot validate IPv6 address attribute!");
+ "cannot validate IPv6 "
+ "address attribute!");
break;
default:
FAILURE("Broken kernel message: "
"IP address attribute but "
"family is unspecified!");
}
+ d = mnl_attr_get_payload(ipattr[atype]);
} else if (nla[type]->nla_type & NLA_F_NET_BYTEORDER) {
switch (attr->type) {
case MNL_TYPE_U32: {
@@ -744,7 +818,8 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
for (arg = type->args[IPSET_CREATE]; arg != NULL && arg->opt; arg++) {
if (!arg->print
|| !ipset_data_test(data, arg->opt)
- || (arg->opt == IPSET_OPT_FAMILY && family == type->family))
+ || (arg->opt == IPSET_OPT_FAMILY
+ && family == type->family))
continue;
switch (session->mode) {
case IPSET_LIST_SAVE:
@@ -820,44 +895,6 @@ print_set_done(struct ipset_session *session)
}
static int
-generic_data_attr_cb(const struct nlattr *attr, void *data,
- int attr_max, const struct ipset_attr_policy *policy)
-{
- const struct nlattr **tb = (const struct nlattr **)data;
- int type = mnl_attr_get_type(attr);
-
- D("attr type: %u, len %u", type, attr->nla_len);
- if (mnl_attr_type_valid(attr, attr_max) < 0) {
- D("attr type: %u INVALID", type);
- return MNL_CB_ERROR;
- }
- if (mnl_attr_validate(attr, policy[type].type) < 0) {
- D("attr type: %u POLICY, attrlen %u", type,
- mnl_attr_get_payload_len(attr));
- return MNL_CB_ERROR;
- }
- if (policy[type].type == MNL_TYPE_NUL_STRING
- && mnl_attr_get_payload_len(attr) > IPSET_MAXNAMELEN)
- return MNL_CB_ERROR;
- tb[type] = attr;
- return MNL_CB_OK;
-}
-
-static int
-create_attr_cb(const struct nlattr *attr, void *data)
-{
- return generic_data_attr_cb(attr, data,
- IPSET_ATTR_CREATE_MAX, create_attrs);
-}
-
-static int
-adt_attr_cb(const struct nlattr *attr, void *data)
-{
- return generic_data_attr_cb(attr, data,
- IPSET_ATTR_ADT_MAX, adt_attrs);
-}
-
-static int
callback_list(struct ipset_session *session, struct nlattr *nla[],
enum ipset_cmd cmd)
{
@@ -1291,7 +1328,7 @@ static size_t
attr_len(const struct ipset_attr_policy *attr, uint8_t family, uint16_t *flags)
{
switch (attr->type) {
- case MNL_TYPE_BINARY:
+ case MNL_TYPE_NESTED:
if (attr->len)
return attr->len;
@@ -1325,6 +1362,19 @@ rawdata2attr(struct nlmsghdr *nlh,
alen = attr_len(attr, family, &flags);
switch (attr->type) {
+ case MNL_TYPE_NESTED: {
+ /* IP addresses */
+ struct nlattr *nested = mnl_attr_nest_start(nlh, type);
+ int atype = family == AF_INET ? IPSET_ATTR_IPADDR_IPV4
+ : IPSET_ATTR_IPADDR_IPV6;
+
+ D("family: %s", family == AF_INET ? "INET" :
+ family == AF_INET6 ? "INET6" : "UNSPEC");
+ mnl_attr_put(nlh, atype | flags, alen, d);
+ mnl_attr_nest_end(nlh, nested);
+
+ return 0;
+ }
case MNL_TYPE_U32: {
uint32_t value = htonl(*(const uint32_t *)d);
@@ -1510,7 +1560,8 @@ build_msg(struct ipset_session *session, bool aggregate)
ADDATTR(nlh, data, IPSET_ATTR_TYPENAME, AF_INET, cmd_attrs);
ADDATTR_RAW(nlh, &type->revision,
IPSET_ATTR_REVISION, cmd_attrs);
- D("family: %u", ipset_data_family(data));
+ D("family: %u, type family %u",
+ ipset_data_family(data), type->family);
ADDATTR(nlh, data, IPSET_ATTR_FAMILY, AF_INET, cmd_attrs);
/* Type-specific create attributes */
@@ -1568,8 +1619,10 @@ build_msg(struct ipset_session *session, bool aggregate)
}
}
type = ipset_data_get(data, IPSET_OPT_TYPE);
+ D("family: %u, type family %u",
+ ipset_data_family(data), type->family);
open_nested(session, nlh, IPSET_ATTR_DATA);
- addattr_adt(nlh, data, type->family);
+ addattr_adt(nlh, data, ipset_data_family(data));
ADDATTR_RAW(nlh, &session->lineno,
IPSET_ATTR_LINENO, cmd_attrs);
close_nested(session, nlh);
@@ -1590,9 +1643,11 @@ build_msg(struct ipset_session *session, bool aggregate)
"Invalid test command: missing settype");
type = ipset_data_get(data, IPSET_OPT_TYPE);
+ D("family: %u, type family %u",
+ ipset_data_family(data), type->family);
ADDATTR_SETNAME(nlh, data);
open_nested(session, nlh, IPSET_ATTR_DATA);
- addattr_adt(nlh, data, type->family);
+ addattr_adt(nlh, data, ipset_data_family(data));
close_nested(session, nlh);
break;
}
diff --git a/lib/types.c b/lib/types.c
index e3cad35..3d9b034 100644
--- a/lib/types.c
+++ b/lib/types.c
@@ -11,6 +11,7 @@
#include <sys/socket.h> /* AF_ */
#include <stdlib.h> /* malloc, free */
#include <stdio.h> /* FIXME: debug */
+#include <libmnl/libmnl.h> /* MNL_ALIGN */
#include <libipset/debug.h> /* D() */
#include <libipset/data.h> /* ipset_data_* */
@@ -438,14 +439,25 @@ type_max_size(struct ipset_type *type, uint8_t family)
continue;
if (!(IPSET_FLAG(opt) & type->full[IPSET_ADD]))
continue;
- max += ipset_data_sizeof(opt, family);
+ max += MNL_ALIGN(ipset_data_sizeof(opt, family))
+ + MNL_ATTR_HDRLEN;
+ switch (opt) {
+ case IPSET_OPT_IP:
+ case IPSET_OPT_IP_TO:
+ case IPSET_OPT_IP2:
+ /* Nested attributes */
+ max += MNL_ATTR_HDRLEN;
+ break;
+ default:
+ break;
+ }
}
type->maxsize[sizeid] = max;
}
/**
* ipset_type_add - add (register) a userspace set type
- * @type: set type structure
+ * @type: pointer to the set type structure
*
* Add the given set type to the type list. The types
* are added sorted, in descending revision number.