2
* lib/route/classid.c ClassID Management
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
9
* Copyright (c) 2010 Thomas Graf <tgraf@suug.ch>
14
* @defgroup classid ClassID Management
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>
28
struct nl_list_head name_list;
31
#define CLASSID_NAME_HT_SIZ 256
33
static struct nl_list_head tbl_name[CLASSID_NAME_HT_SIZ];
35
static void *id_root = NULL;
37
static int compare_id(const void *pa, const void *pb)
39
const struct classid_map *ma = pa;
40
const struct classid_map *mb = pb;
42
if (ma->classid < mb->classid)
45
if (ma->classid > mb->classid)
52
static unsigned int classid_tbl_hash(const char *str)
54
unsigned long hash = 5381;
58
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
60
return hash % CLASSID_NAME_HT_SIZ;
63
static int classid_lookup(const char *name, uint32_t *result)
65
struct classid_map *map;
66
int n = classid_tbl_hash(name);
68
nl_list_for_each_entry(map, &tbl_name[n], name_list) {
69
if (!strcasecmp(map->name, name)) {
70
*result = map->classid;
75
return -NLE_OBJ_NOTFOUND;
78
static char *name_lookup(const uint32_t classid)
81
struct classid_map cm = {
83
.name = "search entry",
86
if ((res = tfind(&cm, &id_root, &compare_id)))
87
return (*(struct classid_map **) res)->name;
93
* @name Traffic Control Handle Translations
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
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.
106
* @return The destination buffer or the type encoded in hexidecimal
107
* form if no match was found.
109
char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len)
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");
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);
127
snprintf(buf, len, "%02x:%02x",
128
TC_H_MAJ(handle) >> 16, TC_H_MIN(handle));
135
* Convert a charactering strint to a traffic control handle
136
* @arg str traffic control handle as character string
137
* @arg res destination buffer
139
* Converts the provided character string specifying a traffic
140
* control handle to the corresponding numeric value.
142
* The handle must be provided in one of the following formats:
152
* @return 0 on success or a negative error code
154
int rtnl_tc_str2handle(const char *str, uint32_t *res)
159
if (!strcasecmp(str, "root")) {
164
if (!strcasecmp(str, "none")) {
169
h = strtoul(str, &colon, 16);
171
/* MAJ is not a number */
179
char name[64] = { 0 };
181
if (!(colon = strpbrk(str, ":"))) {
183
return classid_lookup(str, res);
187
if (len >= sizeof(name))
190
memcpy(name, str, len);
192
if ((err = classid_lookup(name, &h)) < 0)
195
/* Name must point to a qdisc alias */
199
/* NAME: is not allowed */
200
if (colon[1] == '\0')
209
/* check if we would lose bits */
214
if ('\0' == colon[1]) {
222
l = strtoul(colon+1, &end, 16);
224
/* check if we overlap with major part */
233
} else if ('\0' == *colon) {
242
static void free_nothing(void *arg)
246
static void classid_map_free(struct classid_map *map)
255
static void clear_hashtable(void)
259
for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) {
260
struct classid_map *map, *n;
262
nl_list_for_each_entry_safe(map, n, &tbl_name[i], name_list)
263
classid_map_free(map);
265
nl_init_list_head(&tbl_name[i]);
270
tdestroy(&id_root, &free_nothing);
275
static int classid_map_add(uint32_t classid, const char *name)
277
struct classid_map *map;
280
if (!(map = calloc(1, sizeof(*map))))
283
map->classid = classid;
284
map->name = strdup(name);
286
n = classid_tbl_hash(map->name);
287
nl_list_add_tail(&map->name_list, &tbl_name[n]);
289
if (!tsearch((void *) map, &id_root, &compare_id)) {
290
classid_map_free(map);
298
* (Re-)read classid file
300
* Rereads the contents of the classid file (typically found at the location
301
* /etc/libnl/classid) and refreshes the classid maps.
303
* @return 0 on success or a negative error code.
305
int rtnl_tc_read_classid_file(void)
307
static time_t last_read;
308
struct stat st = {0};
309
char buf[256], *path;
313
asprintf(&path, "%s/classid", SYSCONFDIR);
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) {
324
if (!(fd = fopen(path, "r"))) {
325
err = -nl_syserr2nlerr(errno);
331
while (fgets(buf, sizeof(buf), fd)) {
335
/* ignore comments and empty lines */
336
if (*buf == '#' || *buf == '\n' || *buf == '\r')
340
if (!(tok = strtok_r(buf, " \t", &ptr))) {
345
if ((err = rtnl_tc_str2handle(tok, &classid)) < 0)
348
if (!(tok = strtok_r(NULL, " \t\n\r#", &ptr))) {
353
if ((err = classid_map_add(classid, tok)) < 0)
358
last_read = st.st_mtime;
369
int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent)
371
static uint32_t base = 0x4000 << 16;
377
if (parent == TC_H_ROOT || parent == TC_H_INGRESS) {
380
if (base == TC_H_MAJ(TC_H_ROOT))
382
} while (name_lookup(base));
386
classid = TC_H_MAJ(parent);
388
if (TC_H_MIN(++classid) == TC_H_MIN(TC_H_ROOT))
390
} while (name_lookup(classid));
393
NL_DBG(2, "Generated new classid %#x\n", classid);
395
if (asprintf(&path, "%s/classid", SYSCONFDIR) < 0)
398
if (!(fd = fopen(path, "a"))) {
399
err = -nl_syserr2nlerr(errno);
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);
410
if ((err = classid_map_add(classid, name)) < 0) {
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.
416
rtnl_tc_read_classid_file();
429
static void __init classid_init(void)
433
for (i = 0; i < CLASSID_NAME_HT_SIZ; i++)
434
nl_init_list_head(&tbl_name[i]);
436
if ((err = rtnl_tc_read_classid_file()) < 0)
437
fprintf(stderr, "Failed to read classid file: %s\n", nl_geterror(err));