summaryrefslogtreecommitdiffstats
path: root/lib/data.c
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-04-22 16:52:29 +0200
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-04-22 16:52:29 +0200
commit8e0608d31d988333ff04f3faaa6e851c0ecdbc6e (patch)
treeb042fc732d7c784d298ed42496f88a2f164f413c /lib/data.c
parent1e6e8bd9a62aa7cd72e13db9355badc96df18ee8 (diff)
Fourth stage to ipset-5
Add new userspace files: include/, lib/ and plus new files in src/.
Diffstat (limited to 'lib/data.c')
-rw-r--r--lib/data.c505
1 files changed, 505 insertions, 0 deletions
diff --git a/lib/data.c b/lib/data.c
new file mode 100644
index 0000000..0de91a1
--- /dev/null
+++ b/lib/data.c
@@ -0,0 +1,505 @@
+/* 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 <assert.h> /* assert */
+#include <arpa/inet.h> /* ntoh* */
+#include <net/ethernet.h> /* ETH_ALEN */
+#include <sys/socket.h> /* AF_ */
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memset */
+
+#include <libipset/linux_ip_set.h> /* IPSET_MAXNAMELEN */
+#include <libipset/types.h> /* struct ipset_type */
+#include <libipset/utils.h> /* inXcpy */
+#include <libipset/data.h> /* prototypes */
+
+/* Internal data structure to hold
+ * a) input data entered by the user or
+ * b) data received from kernel
+ *
+ * We always store the data in host order, *except* IP addresses.
+ */
+
+struct ipset_data {
+ /* Option bits: which fields are set */
+ uint64_t bits;
+ /* Setname */
+ char setname[IPSET_MAXNAMELEN];
+ const struct ipset_type *type;
+ /* Common CADT options */
+ uint8_t cidr;
+ uint8_t family;
+ uint32_t flags;
+ uint32_t timeout;
+ union nf_inet_addr ip;
+ union nf_inet_addr ip_to;
+ uint16_t port;
+ uint16_t port_to;
+ union {
+ /* RENAME/SWAP */
+ char setname2[IPSET_MAXNAMELEN];
+ /* CREATE/LIST/SAVE */
+ struct {
+ uint8_t probes;
+ uint8_t resize;
+ uint8_t netmask;
+ uint32_t hashsize;
+ uint32_t maxelem;
+ uint32_t gc;
+ uint32_t size;
+ /* Filled out by kernel */
+ uint32_t references;
+ uint32_t elements;
+ uint32_t memsize;
+ char typename[IPSET_MAXNAMELEN];
+ uint8_t revision_min;
+ uint8_t revision;
+ } create;
+ /* ADT/LIST/SAVE */
+ struct {
+ union nf_inet_addr ip2;
+ uint8_t cidr2;
+ char ether[ETH_ALEN];
+ char name[IPSET_MAXNAMELEN];
+ char nameref[IPSET_MAXNAMELEN];
+ } adt;
+ } u;
+};
+
+static void
+copy_addr(uint8_t family, union nf_inet_addr *ip, const void *value)
+{
+ if (family == AF_INET)
+ in4cpy(&ip->in, (const struct in_addr *)value);
+ else
+ in6cpy(&ip->in6, (const struct in6_addr *)value);
+}
+
+/**
+ * ipset_data_flags_test - test option bits in the data blob
+ * @data: data blob
+ * @flags: the option flags to test
+ *
+ * Returns true if the options are already set in the data blob.
+ */
+bool
+ipset_data_flags_test(const struct ipset_data *data, uint64_t flags)
+{
+ assert(data);
+ return !!(data->bits & flags);
+}
+
+/**
+ * ipset_data_flags_set - set option bits in the data blob
+ * @data: data blob
+ * @flags: the option flags to set
+ *
+ * The function sets the flags in the data blob so that
+ * the corresponding fields are regarded as if filled with proper data.
+ */
+void
+ipset_data_flags_set(struct ipset_data *data, uint64_t flags)
+{
+ assert(data);
+ data->bits |= flags;
+}
+
+/**
+ * ipset_data_flags_unset - unset option bits in the data blob
+ * @data: data blob
+ * @flags: the option flags to unset
+ *
+ * The function unsets the flags in the data blob.
+ * This is the quick way to clear specific fields.
+ */
+void
+ipset_data_flags_unset(struct ipset_data *data, uint64_t flags)
+{
+ assert(data);
+ data->bits &= ~flags;
+}
+
+#define flag_type_attr(data, opt, flag) \
+do { \
+ data->flags |= (1 << flag); \
+ opt = IPSET_OPT_FLAGS; \
+} while (0)
+
+/**
+ * ipset_data_set - put data into the data blob
+ * @data: data blob
+ * @opt: the option kind of the data
+ * @value: the value of the data
+ *
+ * Put a given kind of data into the data blob and mark the
+ * option kind as already set in the blob.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
+{
+ assert(data);
+ assert(opt != IPSET_OPT_NONE);
+ assert(value);
+
+ switch (opt) {
+ /* Common ones */
+ case IPSET_SETNAME:
+ ipset_strncpy(data->setname, value, IPSET_MAXNAMELEN);
+ break;
+ case IPSET_OPT_TYPE:
+ data->type = value;
+ break;
+ case IPSET_OPT_FAMILY:
+ data->family = *(const uint8_t *) value;
+ break;
+ /* CADT options */
+ case IPSET_OPT_IP:
+ if (!(data->family == AF_INET || data->family == AF_INET6))
+ return -1;
+ copy_addr(data->family, &data->ip, value);
+ break;
+ case IPSET_OPT_IP_TO:
+ if (!(data->family == AF_INET || data->family == AF_INET6))
+ return -1;
+ copy_addr(data->family, &data->ip_to, value);
+ break;
+ case IPSET_OPT_CIDR:
+ data->cidr = *(const uint8_t *) value;
+ break;
+ case IPSET_OPT_PORT:
+ data->port = *(const uint16_t *) value;
+ break;
+ case IPSET_OPT_PORT_TO:
+ data->port_to = *(const uint16_t *) value;
+ break;
+ case IPSET_OPT_TIMEOUT:
+ data->timeout = *(const uint32_t *) value;
+ break;
+ /* Create-specific options */
+ case IPSET_OPT_GC:
+ data->u.create.gc = *(const uint32_t *) value;
+ break;
+ case IPSET_OPT_HASHSIZE:
+ data->u.create.hashsize = *(const uint32_t *) value;
+ break;
+ case IPSET_OPT_MAXELEM:
+ data->u.create.maxelem = *(const uint32_t *) value;
+ break;
+ case IPSET_OPT_NETMASK:
+ data->u.create.netmask = *(const uint8_t *) value;
+ break;
+ case IPSET_OPT_PROBES:
+ data->u.create.probes = *(const uint8_t *) value;
+ break;
+ case IPSET_OPT_RESIZE:
+ data->u.create.resize = *(const uint8_t *) value;
+ break;
+ case IPSET_OPT_SIZE:
+ data->u.create.size = *(const uint32_t *) value;
+ break;
+ /* Create-specific options, filled out by the kernel */
+ case IPSET_OPT_ELEMENTS:
+ data->u.create.elements = *(const uint32_t *) value;
+ break;
+ case IPSET_OPT_REFERENCES:
+ data->u.create.references = *(const uint32_t *) value;
+ break;
+ case IPSET_OPT_MEMSIZE:
+ data->u.create.memsize = *(const uint32_t *) value;
+ break;
+ /* Create-specific options, type */
+ case IPSET_OPT_TYPENAME:
+ ipset_strncpy(data->u.create.typename, value, IPSET_MAXNAMELEN);
+ break;
+ case IPSET_OPT_REVISION:
+ data->u.create.revision = *(const uint8_t *) value;
+ break;
+ case IPSET_OPT_REVISION_MIN:
+ data->u.create.revision_min = *(const uint8_t *) value;
+ break;
+ /* ADT-specific options */
+ case IPSET_OPT_ETHER:
+ memcpy(data->u.adt.ether, value, ETH_ALEN);
+ break;
+ case IPSET_OPT_NAME:
+ ipset_strncpy(data->u.adt.name, value, IPSET_MAXNAMELEN);
+ break;
+ case IPSET_OPT_NAMEREF:
+ ipset_strncpy(data->u.adt.nameref, value, IPSET_MAXNAMELEN);
+ break;
+ case IPSET_OPT_IP2:
+ if (!(data->family == AF_INET || data->family == AF_INET6))
+ return -1;
+ copy_addr(data->family, &data->u.adt.ip2, value);
+ break;
+ case IPSET_OPT_CIDR2:
+ data->u.adt.cidr2 = *(const uint8_t *) value;
+ break;
+ /* Swap/rename */
+ case IPSET_OPT_SETNAME2:
+ ipset_strncpy(data->u.setname2, value, IPSET_MAXNAMELEN);
+ break;
+ /* flags */
+ case IPSET_OPT_EXIST:
+ flag_type_attr(data, opt, IPSET_FLAG_EXIST);
+ break;
+ case IPSET_OPT_BEFORE:
+ flag_type_attr(data, opt, IPSET_FLAG_BEFORE);
+ break;
+ case IPSET_OPT_FLAGS:
+ data->flags = *(const uint32_t *)value;
+ break;
+ default:
+ return -1;
+ };
+
+ ipset_data_flags_set(data, IPSET_FLAG(opt));
+ return 0;
+}
+
+/**
+ * ipset_data_get - get data from the data blob
+ * @data: data blob
+ * @opt: option kind of the requested data
+ *
+ * Returns the pointer to the requested kind of data from the data blob
+ * if it is set. If the option kind is not set or is an unkown type,
+ * NULL is returned.
+ */
+const void *
+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))
+ return NULL;
+
+ switch (opt) {
+ /* Common ones */
+ case IPSET_SETNAME:
+ return data->setname;
+ case IPSET_OPT_TYPE:
+ return data->type;
+ case IPSET_OPT_TYPENAME:
+ if (ipset_data_test(data, IPSET_OPT_TYPE))
+ return data->type->name;
+ else if (ipset_data_test(data, IPSET_OPT_TYPENAME))
+ return data->u.create.typename;
+ return NULL;
+ case IPSET_OPT_FAMILY:
+ return &data->family;
+ /* CADT options */
+ case IPSET_OPT_IP:
+ return &data->ip;
+ case IPSET_OPT_IP_TO:
+ return &data->ip_to;
+ case IPSET_OPT_CIDR:
+ return &data->cidr;
+ case IPSET_OPT_PORT:
+ return &data->port;
+ case IPSET_OPT_PORT_TO:
+ return &data->port_to;
+ case IPSET_OPT_TIMEOUT:
+ return &data->timeout;
+ /* Create-specific options */
+ case IPSET_OPT_GC:
+ return &data->u.create.gc;
+ case IPSET_OPT_HASHSIZE:
+ return &data->u.create.hashsize;
+ case IPSET_OPT_MAXELEM:
+ return &data->u.create.maxelem;
+ case IPSET_OPT_NETMASK:
+ return &data->u.create.netmask;
+ case IPSET_OPT_PROBES:
+ return &data->u.create.probes;
+ case IPSET_OPT_RESIZE:
+ return &data->u.create.resize;
+ case IPSET_OPT_SIZE:
+ return &data->u.create.size;
+ /* Create-specific options, filled out by the kernel */
+ case IPSET_OPT_ELEMENTS:
+ return &data->u.create.elements;
+ case IPSET_OPT_REFERENCES:
+ return &data->u.create.references;
+ case IPSET_OPT_MEMSIZE:
+ return &data->u.create.memsize;
+ /* Create-specific options, TYPE */
+ case IPSET_OPT_REVISION:
+ return &data->u.create.revision;
+ case IPSET_OPT_REVISION_MIN:
+ return &data->u.create.revision_min;
+ /* ADT-specific options */
+ case IPSET_OPT_ETHER:
+ return data->u.adt.ether;
+ case IPSET_OPT_NAME:
+ return data->u.adt.name;
+ case IPSET_OPT_NAMEREF:
+ return data->u.adt.nameref;
+ case IPSET_OPT_IP2:
+ return &data->u.adt.ip2;
+ case IPSET_OPT_CIDR2:
+ return &data->u.adt.cidr2;
+ /* Swap/rename */
+ case IPSET_OPT_SETNAME2:
+ return data->u.setname2;
+ /* flags */
+ case IPSET_OPT_FLAGS:
+ case IPSET_OPT_EXIST:
+ case IPSET_OPT_BEFORE:
+ return &data->flags;
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * ipset_data_sizeof - calculates the size for the type of data
+ * @opt: option kind of the data
+ * @family: INET family
+ *
+ * Returns the size required to store the given option kind.
+ */
+size_t
+ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
+{
+ assert(opt != IPSET_OPT_NONE);
+
+ switch (opt) {
+ case IPSET_OPT_IP:
+ case IPSET_OPT_IP_TO:
+ case IPSET_OPT_IP2:
+ return family == AF_INET ? sizeof(uint32_t)
+ : sizeof(struct in6_addr);
+ case IPSET_OPT_PORT:
+ case IPSET_OPT_PORT_TO:
+ return sizeof(uint16_t);
+ case IPSET_SETNAME:
+ case IPSET_OPT_NAME:
+ case IPSET_OPT_NAMEREF:
+ return IPSET_MAXNAMELEN;
+ case IPSET_OPT_TIMEOUT:
+ case IPSET_OPT_GC:
+ case IPSET_OPT_HASHSIZE:
+ case IPSET_OPT_MAXELEM:
+ case IPSET_OPT_SIZE:
+ case IPSET_OPT_ELEMENTS:
+ case IPSET_OPT_REFERENCES:
+ case IPSET_OPT_MEMSIZE:
+ return sizeof(uint32_t);
+ case IPSET_OPT_CIDR:
+ case IPSET_OPT_CIDR2:
+ case IPSET_OPT_NETMASK:
+ case IPSET_OPT_PROBES:
+ case IPSET_OPT_RESIZE:
+ return sizeof(uint8_t);
+ case IPSET_OPT_ETHER:
+ return ETH_ALEN;
+ /* Flags counted once */
+ case IPSET_OPT_BEFORE:
+ return sizeof(uint32_t);
+ default:
+ return 0;
+ };
+}
+
+/**
+ * ipset_setname - return the name of the set from the data blob
+ * @data: data blob
+ *
+ * Return the name of the set from the data blob or NULL if the
+ * name not set yet.
+ */
+const char *
+ipset_data_setname(const struct ipset_data *data)
+{
+ assert(data);
+ return ipset_data_test(data, IPSET_SETNAME) ? data->setname : NULL;
+}
+
+/**
+ * ipset_family - return the INET family of the set from the data blob
+ * @data: data blob
+ *
+ * Return the INET family supported by the set from the data blob.
+ * If the family is not set yet, AF_UNSPEC is returned.
+ */
+uint8_t
+ipset_data_family(const struct ipset_data *data)
+{
+ assert(data);
+ return ipset_data_test(data, IPSET_OPT_FAMILY)
+ ? data->family : AF_UNSPEC;
+}
+
+/**
+ * ipset_data_cidr - return the value of IPSET_OPT_CIDR
+ * @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
+ * the default one according to the family type or zero.
+ */
+uint8_t
+ipset_data_cidr(const struct ipset_data *data)
+{
+ assert(data);
+ return ipset_data_test(data, IPSET_OPT_CIDR) ? data->cidr :
+ data->family == AF_INET ? 32 :
+ data->family == AF_INET6 ? 128 : 0;
+}
+
+/**
+ * ipset_flags - return which fields are set in the data blob
+ * @data: data blob
+ *
+ * Returns the value of the bit field which elements are set.
+ */
+uint64_t
+ipset_data_flags(const struct ipset_data *data)
+{
+ assert(data);
+ return data->bits;
+}
+
+/**
+ * ipset_data_reset - reset the data blob to unset
+ * @data: data blob
+ *
+ * Resets the data blob to the unset state for every field.
+ */
+void
+ipset_data_reset(struct ipset_data *data)
+{
+ assert(data);
+ memset(data, 0, sizeof(*data));
+}
+
+/**
+ * ipset_data_init - create a new data blob
+ *
+ * Return the new data blob initialized to empty. In case of
+ * an error, NULL is retured.
+ */
+struct ipset_data *
+ipset_data_init(void)
+{
+ return calloc(1, sizeof(struct ipset_data));
+}
+
+/**
+ * ipset_data_fini - release a data blob created by ipset_data_init
+ *
+ * Release the data blob created by ipset_data_init previously.
+ */
+void
+ipset_data_fini(struct ipset_data *data)
+{
+ assert(data);
+ free(data);
+}