~ubuntu-branches/ubuntu/trusty/libnl3/trusty

« back to all changes in this revision

Viewing changes to lib/route/classid.c

  • Committer: Bazaar Package Importer
  • Author(s): Heiko Stuebner
  • Date: 2011-05-21 19:25:13 UTC
  • Revision ID: james.westby@ubuntu.com-20110521192513-1ieyu9w9kym4bt16
Tags: upstream-3.0
ImportĀ upstreamĀ versionĀ 3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * lib/route/classid.c       ClassID Management
 
3
 *
 
4
 *      This library is free software; you can redistribute it and/or
 
5
 *      modify it under the terms of the GNU Lesser General Public
 
6
 *      License as published by the Free Software Foundation version 2.1
 
7
 *      of the License.
 
8
 *
 
9
 * Copyright (c) 2010 Thomas Graf <tgraf@suug.ch>
 
10
 */
 
11
 
 
12
/**
 
13
 * @ingroup tc
 
14
 * @defgroup classid ClassID Management
 
15
 * @{
 
16
 */
 
17
 
 
18
#include <netlink-local.h>
 
19
#include <netlink-tc.h>
 
20
#include <netlink/netlink.h>
 
21
#include <netlink/utils.h>
 
22
#include <netlink/route/tc.h>
 
23
 
 
24
struct classid_map
 
25
{
 
26
        uint32_t                classid;
 
27
        char *                  name;
 
28
        struct nl_list_head     name_list;
 
29
};
 
30
 
 
31
#define CLASSID_NAME_HT_SIZ 256
 
32
 
 
33
static struct nl_list_head tbl_name[CLASSID_NAME_HT_SIZ];
 
34
 
 
35
static void *id_root = NULL;
 
36
 
 
37
static int compare_id(const void *pa, const void *pb)
 
38
{
 
39
        const struct classid_map *ma = pa;
 
40
        const struct classid_map *mb = pb;
 
41
 
 
42
        if (ma->classid < mb->classid)
 
43
                return -1;
 
44
        
 
45
        if (ma->classid > mb->classid)
 
46
                return 1;
 
47
 
 
48
        return 0;
 
49
}
 
50
 
 
51
/* djb2 */
 
52
static unsigned int classid_tbl_hash(const char *str)
 
53
{
 
54
        unsigned long hash = 5381;
 
55
        int c;
 
56
 
 
57
        while ((c = *str++))
 
58
                hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
 
59
 
 
60
        return hash % CLASSID_NAME_HT_SIZ;
 
61
}
 
62
 
 
63
static int classid_lookup(const char *name, uint32_t *result)
 
64
{
 
65
        struct classid_map *map;
 
66
        int n = classid_tbl_hash(name);
 
67
 
 
68
        nl_list_for_each_entry(map, &tbl_name[n], name_list) {
 
69
                if (!strcasecmp(map->name, name)) {
 
70
                        *result = map->classid;
 
71
                        return 0;
 
72
                }
 
73
        }
 
74
 
 
75
        return -NLE_OBJ_NOTFOUND;
 
76
}
 
77
 
 
78
static char *name_lookup(const uint32_t classid)
 
79
{
 
80
        void *res;
 
81
        struct classid_map cm = {
 
82
                .classid = classid,
 
83
                .name = "search entry",
 
84
        };
 
85
 
 
86
        if ((res = tfind(&cm, &id_root, &compare_id)))
 
87
                return (*(struct classid_map **) res)->name;
 
88
 
 
89
        return NULL;
 
90
}
 
91
 
 
92
/**
 
93
 * @name Traffic Control Handle Translations
 
94
 * @{
 
95
 */
 
96
 
 
97
/**
 
98
 * Convert a traffic control handle to a character string (Reentrant).
 
99
 * @arg handle          traffic control handle
 
100
 * @arg buf             destination buffer
 
101
 * @arg len             buffer length
 
102
 *
 
103
 * Converts a tarffic control handle to a character string in the
 
104
 * form of \c MAJ:MIN and stores it in the specified destination buffer.
 
105
 *
 
106
 * @return The destination buffer or the type encoded in hexidecimal
 
107
 *         form if no match was found.
 
108
 */
 
109
char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len)
 
110
{
 
111
        if (TC_H_ROOT == handle)
 
112
                snprintf(buf, len, "root");
 
113
        else if (TC_H_UNSPEC == handle)
 
114
                snprintf(buf, len, "none");
 
115
        else if (TC_H_INGRESS == handle)
 
116
                snprintf(buf, len, "ingress");
 
117
        else {
 
118
                char *name;
 
119
 
 
120
                if ((name = name_lookup(handle)))
 
121
                        snprintf(buf, len, "%s", name);
 
122
                else if (0 == TC_H_MAJ(handle))
 
123
                        snprintf(buf, len, ":%02x", TC_H_MIN(handle));
 
124
                else if (0 == TC_H_MIN(handle))
 
125
                        snprintf(buf, len, "%02x:", TC_H_MAJ(handle) >> 16);
 
126
                else
 
127
                        snprintf(buf, len, "%02x:%02x",
 
128
                                TC_H_MAJ(handle) >> 16, TC_H_MIN(handle));
 
129
        }
 
130
 
 
131
        return buf;
 
132
}
 
133
 
 
134
/**
 
135
 * Convert a charactering strint to a traffic control handle
 
136
 * @arg str             traffic control handle as character string
 
137
 * @arg res             destination buffer
 
138
 *
 
139
 * Converts the provided character string specifying a traffic
 
140
 * control handle to the corresponding numeric value.
 
141
 *
 
142
 * The handle must be provided in one of the following formats:
 
143
 *  - NAME
 
144
 *  - root
 
145
 *  - none
 
146
 *  - MAJ:
 
147
 *  - :MIN
 
148
 *  - NAME:MIN
 
149
 *  - MAJ:MIN
 
150
 *  - MAJMIN
 
151
 *
 
152
 * @return 0 on success or a negative error code
 
153
 */
 
154
int rtnl_tc_str2handle(const char *str, uint32_t *res)
 
155
{
 
156
        char *colon, *end;
 
157
        uint32_t h, err;
 
158
 
 
159
        if (!strcasecmp(str, "root")) {
 
160
                *res = TC_H_ROOT;
 
161
                return 0;
 
162
        }
 
163
 
 
164
        if (!strcasecmp(str, "none")) {
 
165
                *res = TC_H_UNSPEC;
 
166
                return 0;
 
167
        }
 
168
 
 
169
        h = strtoul(str, &colon, 16);
 
170
 
 
171
        /* MAJ is not a number */
 
172
        if (colon == str) {
 
173
not_a_number:
 
174
                if (*colon == ':') {
 
175
                        /* :YYYY */
 
176
                        h = 0;
 
177
                } else {
 
178
                        size_t len;
 
179
                        char name[64] = { 0 };
 
180
 
 
181
                        if (!(colon = strpbrk(str, ":"))) {
 
182
                                /* NAME */
 
183
                                return classid_lookup(str, res);
 
184
                        } else {
 
185
                                /* NAME:YYYY */
 
186
                                len = colon - str;
 
187
                                if (len >= sizeof(name))
 
188
                                        return -NLE_INVAL;
 
189
 
 
190
                                memcpy(name, str, len);
 
191
 
 
192
                                if ((err = classid_lookup(name, &h)) < 0)
 
193
                                        return err;
 
194
 
 
195
                                /* Name must point to a qdisc alias */
 
196
                                if (TC_H_MIN(h))
 
197
                                        return -NLE_INVAL;
 
198
 
 
199
                                /* NAME: is not allowed */
 
200
                                if (colon[1] == '\0')
 
201
                                        return -NLE_INVAL;
 
202
 
 
203
                                goto update;
 
204
                        }
 
205
                }
 
206
        }
 
207
 
 
208
        if (':' == *colon) {
 
209
                /* check if we would lose bits */
 
210
                if (TC_H_MAJ(h))
 
211
                        return -NLE_RANGE;
 
212
                h <<= 16;
 
213
 
 
214
                if ('\0' == colon[1]) {
 
215
                        /* XXXX: */
 
216
                        *res = h;
 
217
                } else {
 
218
                        /* XXXX:YYYY */
 
219
                        uint32_t l;
 
220
                        
 
221
update:
 
222
                        l = strtoul(colon+1, &end, 16);
 
223
 
 
224
                        /* check if we overlap with major part */
 
225
                        if (TC_H_MAJ(l))
 
226
                                return -NLE_RANGE;
 
227
 
 
228
                        if ('\0' != *end)
 
229
                                return -NLE_INVAL;
 
230
 
 
231
                        *res = (h | l);
 
232
                }
 
233
        } else if ('\0' == *colon) {
 
234
                /* XXXXYYYY */
 
235
                *res = h;
 
236
        } else
 
237
                goto not_a_number;
 
238
 
 
239
        return 0;
 
240
}
 
241
 
 
242
static void free_nothing(void *arg)
 
243
{
 
244
}
 
245
 
 
246
static void classid_map_free(struct classid_map *map)
 
247
{
 
248
        if (!map)
 
249
                return;
 
250
 
 
251
        free(map->name);
 
252
        free(map);
 
253
}
 
254
 
 
255
static void clear_hashtable(void)
 
256
{
 
257
        int i;
 
258
 
 
259
        for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) {
 
260
                struct classid_map *map, *n;
 
261
 
 
262
                nl_list_for_each_entry_safe(map, n, &tbl_name[i], name_list)
 
263
                        classid_map_free(map);
 
264
 
 
265
                nl_init_list_head(&tbl_name[i]);
 
266
 
 
267
        }
 
268
 
 
269
        if (id_root) {
 
270
                tdestroy(&id_root, &free_nothing);
 
271
                id_root = NULL;
 
272
        }
 
273
}
 
274
 
 
275
static int classid_map_add(uint32_t classid, const char *name)
 
276
{
 
277
        struct classid_map *map;
 
278
        int n;
 
279
 
 
280
        if (!(map = calloc(1, sizeof(*map))))
 
281
                return -NLE_NOMEM;
 
282
 
 
283
        map->classid = classid;
 
284
        map->name = strdup(name);
 
285
 
 
286
        n = classid_tbl_hash(map->name);
 
287
        nl_list_add_tail(&map->name_list, &tbl_name[n]);
 
288
 
 
289
        if (!tsearch((void *) map, &id_root, &compare_id)) {
 
290
                classid_map_free(map);
 
291
                return -NLE_NOMEM;
 
292
        }
 
293
 
 
294
        return 0;
 
295
}
 
296
 
 
297
/**
 
298
 * (Re-)read classid file
 
299
 * 
 
300
 * Rereads the contents of the classid file (typically found at the location
 
301
 * /etc/libnl/classid) and refreshes the classid maps.
 
302
 *
 
303
 * @return 0 on success or a negative error code.
 
304
 */
 
305
int rtnl_tc_read_classid_file(void)
 
306
{
 
307
        static time_t last_read;
 
308
        struct stat st = {0};
 
309
        char buf[256], *path;
 
310
        FILE *fd;
 
311
        int err;
 
312
 
 
313
        asprintf(&path, "%s/classid", SYSCONFDIR);
 
314
 
 
315
        /* if stat fails, just (re-)read the file */
 
316
        if (stat(path, &st) == 0) {
 
317
                /* Don't re-read file if file is unchanged */
 
318
                if (last_read == st.st_mtime) {
 
319
                        err = 0;
 
320
                        goto errout;
 
321
                }
 
322
        }
 
323
 
 
324
        if (!(fd = fopen(path, "r"))) {
 
325
                err = -nl_syserr2nlerr(errno);
 
326
                goto errout;
 
327
        }
 
328
 
 
329
        clear_hashtable();
 
330
 
 
331
        while (fgets(buf, sizeof(buf), fd)) {
 
332
                uint32_t classid;
 
333
                char *ptr, *tok;
 
334
 
 
335
                /* ignore comments and empty lines */
 
336
                if (*buf == '#' || *buf == '\n' || *buf == '\r')
 
337
                        continue;
 
338
 
 
339
                /* token 1 */
 
340
                if (!(tok = strtok_r(buf, " \t", &ptr))) {
 
341
                        err = -NLE_INVAL;
 
342
                        goto errout_close;
 
343
                }
 
344
 
 
345
                if ((err = rtnl_tc_str2handle(tok, &classid)) < 0)
 
346
                        goto errout_close;
 
347
 
 
348
                if (!(tok = strtok_r(NULL, " \t\n\r#", &ptr))) {
 
349
                        err = -NLE_INVAL;
 
350
                        goto errout_close;
 
351
                }
 
352
 
 
353
                if ((err = classid_map_add(classid, tok)) < 0)
 
354
                        goto errout_close;
 
355
        }
 
356
 
 
357
        err = 0;
 
358
        last_read = st.st_mtime;
 
359
 
 
360
errout_close:
 
361
        fclose(fd);
 
362
errout:
 
363
        free(path);
 
364
 
 
365
        return err;
 
366
 
 
367
}
 
368
 
 
369
int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent)
 
370
{
 
371
        static uint32_t base = 0x4000 << 16;
 
372
        uint32_t classid;
 
373
        char *path;
 
374
        FILE *fd;
 
375
        int err = 0;
 
376
 
 
377
        if (parent == TC_H_ROOT || parent == TC_H_INGRESS) {
 
378
                do {
 
379
                        base += (1 << 16);
 
380
                        if (base == TC_H_MAJ(TC_H_ROOT))
 
381
                                base = 0x4000 << 16;
 
382
                } while (name_lookup(base));
 
383
 
 
384
                classid = base;
 
385
        } else {
 
386
                classid = TC_H_MAJ(parent);
 
387
                do {
 
388
                        if (TC_H_MIN(++classid) == TC_H_MIN(TC_H_ROOT))
 
389
                                return -NLE_RANGE;
 
390
                } while (name_lookup(classid));
 
391
        }
 
392
 
 
393
        NL_DBG(2, "Generated new classid %#x\n", classid);
 
394
 
 
395
        if (asprintf(&path, "%s/classid", SYSCONFDIR) < 0)
 
396
                return -NLE_NOMEM;
 
397
 
 
398
        if (!(fd = fopen(path, "a"))) {
 
399
                err = -nl_syserr2nlerr(errno);
 
400
                goto errout;
 
401
        }
 
402
 
 
403
        fprintf(fd, "%x:", TC_H_MAJ(classid) >> 16);
 
404
        if (TC_H_MIN(classid))
 
405
                fprintf(fd, "%x", TC_H_MIN(classid));
 
406
        fprintf(fd, "\t\t\t%s\n", name);
 
407
 
 
408
        fclose(fd);
 
409
 
 
410
        if ((err = classid_map_add(classid, name)) < 0) {
 
411
                /* 
 
412
                 * Error adding classid map, re-read classid file is best
 
413
                 * option here. It is likely to fail as well but better
 
414
                 * than nothing, entry was added to the file already anyway.
 
415
                 */
 
416
                rtnl_tc_read_classid_file();
 
417
        }
 
418
 
 
419
        *result = classid;
 
420
        err = 0;
 
421
errout:
 
422
        free(path);
 
423
 
 
424
        return err;
 
425
}
 
426
 
 
427
/** @} */
 
428
 
 
429
static void __init classid_init(void)
 
430
{
 
431
        int err, i;
 
432
 
 
433
        for (i = 0; i < CLASSID_NAME_HT_SIZ; i++)
 
434
                nl_init_list_head(&tbl_name[i]);
 
435
 
 
436
        if ((err = rtnl_tc_read_classid_file()) < 0)
 
437
                fprintf(stderr, "Failed to read classid file: %s\n", nl_geterror(err));
 
438
}
 
439
 
 
440
/** @} */