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