summaryrefslogtreecommitdiffstats
path: root/src/conntrack/labels.c
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2012-11-06 17:06:39 +0100
committerFlorian Westphal <fw@strlen.de>2013-05-06 21:34:15 +0200
commit6510a98f4139f112a0c76c71ff889ef93eac41fb (patch)
tree01e2ee90772ff378629bd889d51a509a26d3098e /src/conntrack/labels.c
parent013a5284c901a6ce80320f499685b89d15eeed9e (diff)
api: add connlabel api and attribute
adds new labelmap api to create a name <-> bit mapping from a text file (default: /etc/xtables/connlabel.conf). nfct_labelmap_new(filename) is used to create the map, nfct_labelmap_destroy() releases the resources allocated for the map. Two functions are added to make map lookups: nfct_labelmap_get_name(map, bit) returns the name of a bit, nfct_labelmap_get_bit returns the bit associated with a name. The connlabel attribute is represented by a nfct_bitmask object, the nfct_bitmask api can be used to test/set/get individual bits ("labels"). The exisiting nfct_attr_get/set interfaces can be used to read or replace the existing labels associated with a conntrack with a new set. Signed-off-by: Florian Westphal <fw@strlen.de>
Diffstat (limited to 'src/conntrack/labels.c')
-rw-r--r--src/conntrack/labels.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/src/conntrack/labels.c b/src/conntrack/labels.c
new file mode 100644
index 0000000..f7a2742
--- /dev/null
+++ b/src/conntrack/labels.c
@@ -0,0 +1,243 @@
+#include <stdint.h>
+
+#include "internal/internal.h"
+
+#define MAX_BITS 1024
+
+#define CONNLABEL_CFG "/etc/xtables/connlabel.conf"
+#define HASH_SIZE 64
+
+struct labelmap_bucket {
+ char *name;
+ unsigned int bit;
+ struct labelmap_bucket *next;
+};
+
+struct nfct_labelmap {
+ struct labelmap_bucket *map_name[HASH_SIZE];
+ unsigned int namecount;
+ char **bit_to_name;
+};
+
+static struct labelmap_bucket* label_map_bucket_alloc(const char *n, unsigned int b)
+{
+ struct labelmap_bucket *bucket;
+ char *name = strdup(n);
+
+ if (!name)
+ return NULL;
+
+ bucket = malloc(sizeof(*bucket));
+ if (!bucket) {
+ free(name);
+ return NULL;
+ }
+ bucket->name = name;
+ bucket->bit = b;
+ return bucket;
+}
+
+static unsigned int hash_name(const char *name)
+{
+ unsigned int hash = 0;
+
+ while (*name) {
+ hash = (hash << 5) - hash + *name;
+ name++;
+ }
+ return hash & (HASH_SIZE - 1);
+}
+
+int __labelmap_get_bit(struct nfct_labelmap *m, const char *name)
+{
+ unsigned int i = hash_name(name);
+ struct labelmap_bucket *list = m->map_name[i];
+
+ while (list) {
+ if (strcmp(name, list->name) == 0)
+ return list->bit;
+ list = list->next;
+ }
+ return -1;
+}
+
+const char *__labelmap_get_name(struct nfct_labelmap *m, unsigned int bit)
+{
+ if (bit < m->namecount)
+ return m->bit_to_name[bit] ? m->bit_to_name[bit] : "";
+ return NULL;
+}
+
+static int map_insert(struct nfct_labelmap *m, const char *n, unsigned int b)
+{
+ unsigned int i = hash_name(n);
+ struct labelmap_bucket *list = m->map_name[i];
+
+ while (list) {
+ if (strcmp(list->name, n) == 0)
+ return -1;
+ list = list->next;
+ }
+
+ list = label_map_bucket_alloc(n, b);
+ if (!list)
+ return -1;
+
+ if (m->map_name[i])
+ list->next = m->map_name[i];
+ else
+ list->next = NULL;
+ m->map_name[i] = list;
+ return 0;
+}
+
+static int is_space_posix(int c)
+{
+ return c == ' ' || c == '\f' || c == '\r' || c == '\t' || c == '\v';
+}
+
+static char *trim_label(char *label)
+{
+ char *end;
+
+ while (is_space_posix(*label))
+ label++;
+ end = strchr(label, '\n');
+ if (end)
+ *end = 0;
+ else
+ end = strchr(label, '\0');
+ end--;
+
+ while (is_space_posix(*end) && end > label) {
+ *end = 0;
+ end--;
+ }
+
+ return *label ? label : NULL;
+}
+
+static int
+xtables_parse_connlabel_numerical(const char *s, char **end)
+{
+ unsigned long value;
+
+ value = strtoul(s, end, 0);
+ if (value == 0 && s == *end)
+ return -1;
+ if (value < 0 || value >= MAX_BITS)
+ return -1;
+ return value;
+}
+
+static void free_list(struct labelmap_bucket *b)
+{
+ struct labelmap_bucket *tmp;
+
+ while (b) {
+ free(b->name);
+
+ tmp = b;
+ b = b->next;
+
+ free(tmp);
+ }
+}
+
+void __labelmap_destroy(struct nfct_labelmap *map)
+{
+ unsigned int i;
+ struct labelmap_bucket *b;
+
+ for (i = 0; i < HASH_SIZE; i++) {
+ b = map->map_name[i];
+ free_list(b);
+ }
+
+ free(map->bit_to_name);
+ free(map);
+}
+
+static void make_name_table(struct nfct_labelmap *m)
+{
+ struct labelmap_bucket *b;
+ unsigned int i;
+
+ for (i = 0; i < HASH_SIZE; i++) {
+ b = m->map_name[i];
+ while (b) {
+ m->bit_to_name[b->bit] = b->name;
+ b = b->next;
+ }
+ }
+}
+
+static struct nfct_labelmap *map_alloc(void)
+{
+ struct nfct_labelmap *map = malloc(sizeof(*map));
+ if (map) {
+ unsigned int i;
+ for (i = 0; i < HASH_SIZE; i++)
+ map->map_name[i] = NULL;
+ }
+ map->bit_to_name = NULL;
+ return map;
+}
+
+struct nfct_labelmap *__labelmap_new(const char *name)
+{
+ struct nfct_labelmap *map;
+ char label[1024];
+ char *end;
+ FILE *fp;
+ int added = 0;
+ unsigned int maxbit = 0;
+ uint32_t bits_seen[MAX_BITS/32];
+
+ fp = fopen(name ? name : CONNLABEL_CFG, "re");
+ if (!fp)
+ return NULL;
+
+ memset(bits_seen, 0, sizeof(bits_seen));
+
+ map = map_alloc();
+ if (!map) {
+ fclose(fp);
+ return NULL;
+ }
+
+ while (fgets(label, sizeof(label), fp)) {
+ int bit;
+
+ if (label[0] == '#')
+ continue;
+
+ bit = xtables_parse_connlabel_numerical(label, &end);
+ if (bit < 0 || test_bit(bit, bits_seen))
+ continue;
+
+ end = trim_label(end);
+ if (!end)
+ continue;
+ if (map_insert(map, end, bit) == 0) {
+ added++;
+ if (maxbit < bit)
+ maxbit = bit;
+ set_bit(bit, bits_seen);
+ }
+ }
+
+ fclose(fp);
+
+ if (added) {
+ map->namecount = maxbit + 1;
+ map->bit_to_name = calloc(sizeof(char *), map->namecount);
+ if (!map->bit_to_name)
+ goto err;
+ make_name_table(map);
+ return map;
+ }
+ err:
+ __labelmap_destroy(map);
+ return NULL;
+}