Home | History | Annotate | Download | only in conntrack
      1 #include <stdbool.h>
      2 #include <stdint.h>
      3 
      4 #include "internal/internal.h"
      5 
      6 #define MAX_BITS 1024
      7 
      8 #define CONNLABEL_CFG "/etc/xtables/connlabel.conf"
      9 #define HASH_SIZE 64
     10 
     11 struct labelmap_bucket {
     12 	char *name;
     13 	unsigned int bit;
     14 	struct labelmap_bucket *next;
     15 };
     16 
     17 struct nfct_labelmap {
     18 	struct labelmap_bucket *map_name[HASH_SIZE];
     19 	unsigned int namecount;
     20 	char **bit_to_name;
     21 };
     22 
     23 static struct labelmap_bucket* label_map_bucket_alloc(const char *n, unsigned int b)
     24 {
     25 	struct labelmap_bucket *bucket;
     26 	char *name = strdup(n);
     27 
     28 	if (!name)
     29 		return NULL;
     30 
     31 	bucket = malloc(sizeof(*bucket));
     32 	if (!bucket) {
     33 		free(name);
     34 		return NULL;
     35 	}
     36 	bucket->name = name;
     37 	bucket->bit = b;
     38 	return bucket;
     39 }
     40 
     41 static unsigned int hash_name(const char *name)
     42 {
     43 	unsigned int hash = 0;
     44 
     45 	while (*name) {
     46 		hash = (hash << 5) - hash + *name;
     47 		name++;
     48 	}
     49 	return hash & (HASH_SIZE - 1);
     50 }
     51 
     52 int __labelmap_get_bit(struct nfct_labelmap *m, const char *name)
     53 {
     54 	unsigned int i = hash_name(name);
     55 	struct labelmap_bucket *list = m->map_name[i];
     56 
     57 	while (list) {
     58 		if (strcmp(name, list->name) == 0)
     59 			return list->bit;
     60 		list = list->next;
     61 	}
     62 	return -1;
     63 }
     64 
     65 const char *__labelmap_get_name(struct nfct_labelmap *m, unsigned int bit)
     66 {
     67 	if (bit < m->namecount)
     68 		return m->bit_to_name[bit] ? m->bit_to_name[bit] : "";
     69 	return NULL;
     70 }
     71 
     72 static int map_insert(struct nfct_labelmap *m, const char *n, unsigned int b)
     73 {
     74 	unsigned int i = hash_name(n);
     75 	struct labelmap_bucket *list = m->map_name[i];
     76 
     77 	while (list) {
     78 		if (strcmp(list->name, n) == 0)
     79 			return -1;
     80 		list = list->next;
     81 	}
     82 
     83 	list = label_map_bucket_alloc(n, b);
     84 	if (!list)
     85 		return -1;
     86 
     87 	if (m->map_name[i])
     88 		list->next = m->map_name[i];
     89 	else
     90 		list->next = NULL;
     91 	m->map_name[i] = list;
     92 	return 0;
     93 }
     94 
     95 static int is_space_posix(int c)
     96 {
     97 	return c == ' ' || c == '\f' || c == '\r' || c == '\t' || c == '\v';
     98 }
     99 
    100 static char *trim_label(char *label)
    101 {
    102 	char *end;
    103 
    104 	while (is_space_posix(*label))
    105 		label++;
    106 	end = strchr(label, '\n');
    107 	if (end)
    108 		*end = 0;
    109 	else
    110 		end = strchr(label, '\0');
    111 	end--;
    112 
    113 	while (end > label && is_space_posix(*end)) {
    114 		*end = 0;
    115 		end--;
    116 	}
    117 
    118 	return *label ? label : NULL;
    119 }
    120 
    121 static int
    122 xtables_parse_connlabel_numerical(const char *s, char **end)
    123 {
    124 	unsigned long value;
    125 
    126 	value = strtoul(s, end, 0);
    127 	if (value == 0 && s == *end)
    128 		return -1;
    129 	if (value >= MAX_BITS)
    130 		return -1;
    131 	return value;
    132 }
    133 
    134 static void free_list(struct labelmap_bucket *b)
    135 {
    136 	struct labelmap_bucket *tmp;
    137 
    138 	while (b) {
    139 		free(b->name);
    140 
    141 		tmp = b;
    142 		b = b->next;
    143 
    144 		free(tmp);
    145 	}
    146 }
    147 
    148 void __labelmap_destroy(struct nfct_labelmap *map)
    149 {
    150 	unsigned int i;
    151 	struct labelmap_bucket *b;
    152 
    153 	for (i = 0; i < HASH_SIZE; i++) {
    154 		b = map->map_name[i];
    155 		free_list(b);
    156 	}
    157 
    158 	free(map->bit_to_name);
    159 	free(map);
    160 }
    161 
    162 static void make_name_table(struct nfct_labelmap *m)
    163 {
    164 	struct labelmap_bucket *b;
    165 	unsigned int i;
    166 
    167 	for (i = 0; i < HASH_SIZE; i++) {
    168 		b = m->map_name[i];
    169 		while (b) {
    170 			m->bit_to_name[b->bit] = b->name;
    171 			b = b->next;
    172 		}
    173 	}
    174 }
    175 
    176 static struct nfct_labelmap *map_alloc(void)
    177 {
    178 	struct nfct_labelmap *map = malloc(sizeof(*map));
    179 	if (map) {
    180 		unsigned int i;
    181 		for (i = 0; i < HASH_SIZE; i++)
    182 			map->map_name[i] = NULL;
    183 		map->bit_to_name = NULL;
    184 	}
    185 	return map;
    186 }
    187 
    188 /*
    189  * We will only accept alpha numerical labels; else
    190  * parses might choke on output when label named
    191  * "foo;<&bar" exists.  ASCII machines only.
    192  *
    193  * Avoids libc isalnum() etc. to avoid issues with locale
    194  * settings.
    195  */
    196 static bool label_is_sane(const char *label)
    197 {
    198 	for (;*label; label++) {
    199 		if (*label >= 'a' && *label <= 'z')
    200 			continue;
    201 		if (*label >= 'A' && *label <= 'Z')
    202 			continue;
    203 		if (*label >= '0' && *label <= '9')
    204 			continue;
    205 		if (*label == ' ' || *label == '-')
    206 			continue;
    207 		return false;
    208 	}
    209 	return true;
    210 }
    211 
    212 const char *__labels_get_path(void)
    213 {
    214 	return CONNLABEL_CFG;
    215 }
    216 
    217 struct nfct_labelmap *__labelmap_new(const char *name)
    218 {
    219 	struct nfct_labelmap *map;
    220 	char label[1024];
    221 	char *end;
    222 	FILE *fp;
    223 	int added = 0;
    224 	unsigned int maxbit = 0;
    225 	uint32_t bits_seen[MAX_BITS/32];
    226 
    227 	fp = fopen(name ? name : CONNLABEL_CFG, "re");
    228 	if (!fp)
    229 		return NULL;
    230 
    231 	memset(bits_seen, 0, sizeof(bits_seen));
    232 
    233 	map = map_alloc();
    234 	if (!map) {
    235 		fclose(fp);
    236 		return NULL;
    237 	}
    238 
    239 	while (fgets(label, sizeof(label), fp)) {
    240 		int bit;
    241 
    242 		if (label[0] == '#')
    243 			continue;
    244 
    245 		bit = xtables_parse_connlabel_numerical(label, &end);
    246 		if (bit < 0 || test_bit(bit, bits_seen))
    247 			continue;
    248 
    249 		end = trim_label(end);
    250 		if (!end)
    251 			continue;
    252 
    253 		if (label_is_sane(end) && map_insert(map, end, bit) == 0) {
    254 			added++;
    255 			if (maxbit < bit)
    256 				maxbit = bit;
    257 			set_bit(bit, bits_seen);
    258 		}
    259 	}
    260 
    261 	fclose(fp);
    262 
    263 	if (added) {
    264 		map->namecount = maxbit + 1;
    265 		map->bit_to_name = calloc(sizeof(char *), map->namecount);
    266 		if (!map->bit_to_name)
    267 			goto err;
    268 		make_name_table(map);
    269 		return map;
    270 	} else {
    271 		errno = 0;
    272 	}
    273  err:
    274 	__labelmap_destroy(map);
    275 	return NULL;
    276 }
    277