libnl  3.6.0
classid.c
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2010-2013 Thomas Graf <tgraf@suug.ch>
4  */
5 
6 /**
7  * @ingroup tc
8  * @defgroup classid ClassID Management
9  * @{
10  */
11 
12 #include <netlink-private/netlink.h>
13 #include <netlink-private/tc.h>
14 #include <netlink/netlink.h>
15 #include <netlink/utils.h>
16 #include <netlink/route/tc.h>
17 
19 {
20  uint32_t classid;
21  char * name;
22  struct nl_list_head name_list;
23 };
24 
25 #define CLASSID_NAME_HT_SIZ 256
26 
27 static struct nl_list_head tbl_name[CLASSID_NAME_HT_SIZ];
28 
29 static void *id_root = NULL;
30 
31 static int compare_id(const void *pa, const void *pb)
32 {
33  const struct classid_map *ma = pa;
34  const struct classid_map *mb = pb;
35 
36  if (ma->classid < mb->classid)
37  return -1;
38 
39  if (ma->classid > mb->classid)
40  return 1;
41 
42  return 0;
43 }
44 
45 /* djb2 */
46 static unsigned int classid_tbl_hash(const char *str)
47 {
48  unsigned long hash = 5381;
49  int c;
50 
51  while ((c = *str++))
52  hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
53 
54  return hash % CLASSID_NAME_HT_SIZ;
55 }
56 
57 static int classid_lookup(const char *name, uint32_t *result)
58 {
59  struct classid_map *map;
60  int n = classid_tbl_hash(name);
61 
62  nl_list_for_each_entry(map, &tbl_name[n], name_list) {
63  if (!strcasecmp(map->name, name)) {
64  *result = map->classid;
65  return 0;
66  }
67  }
68 
69  return -NLE_OBJ_NOTFOUND;
70 }
71 
72 static char *name_lookup(const uint32_t classid)
73 {
74  void *res;
75  struct classid_map cm = {
76  .classid = classid,
77  .name = "search entry",
78  };
79 
80  if ((res = tfind(&cm, &id_root, &compare_id)))
81  return (*(struct classid_map **) res)->name;
82 
83  return NULL;
84 }
85 
86 /**
87  * @name Traffic Control Handle Translations
88  * @{
89  */
90 
91 /**
92  * Convert a traffic control handle to a character string (Reentrant).
93  * @arg handle traffic control handle
94  * @arg buf destination buffer
95  * @arg len buffer length
96  *
97  * Converts a tarffic control handle to a character string in the
98  * form of \c MAJ:MIN and stores it in the specified destination buffer.
99  *
100  * @return The destination buffer or the type encoded in hexidecimal
101  * form if no match was found.
102  */
103 char *rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len)
104 {
105  if (TC_H_ROOT == handle)
106  snprintf(buf, len, "root");
107  else if (TC_H_UNSPEC == handle)
108  snprintf(buf, len, "none");
109  else if (TC_H_INGRESS == handle)
110  snprintf(buf, len, "ingress");
111  else {
112  char *name;
113 
114  if ((name = name_lookup(handle)))
115  snprintf(buf, len, "%s", name);
116  else if (0 == TC_H_MAJ(handle))
117  snprintf(buf, len, ":%x", TC_H_MIN(handle));
118  else if (0 == TC_H_MIN(handle))
119  snprintf(buf, len, "%x:", TC_H_MAJ(handle) >> 16);
120  else
121  snprintf(buf, len, "%x:%x",
122  TC_H_MAJ(handle) >> 16, TC_H_MIN(handle));
123  }
124 
125  return buf;
126 }
127 
128 /**
129  * Convert a charactering strint to a traffic control handle
130  * @arg str traffic control handle as character string
131  * @arg res destination buffer
132  *
133  * Converts the provided character string specifying a traffic
134  * control handle to the corresponding numeric value.
135  *
136  * The handle must be provided in one of the following formats:
137  * - NAME
138  * - root
139  * - none
140  * - MAJ:
141  * - :MIN
142  * - NAME:MIN
143  * - MAJ:MIN
144  * - MAJMIN
145  *
146  * @return 0 on success or a negative error code
147  */
148 int rtnl_tc_str2handle(const char *str, uint32_t *res)
149 {
150  char *colon, *end;
151  uint32_t h;
152  int err;
153 
154  if (!strcasecmp(str, "root")) {
155  *res = TC_H_ROOT;
156  return 0;
157  }
158 
159  if (!strcasecmp(str, "none")) {
160  *res = TC_H_UNSPEC;
161  return 0;
162  }
163 
164  if (!strcasecmp(str, "ingress")) {
165  *res = TC_H_INGRESS;
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  */
306 {
307  static time_t last_read;
308  struct stat st;
309  char buf[256], *path;
310  FILE *fd;
311  int err;
312 
313  if (build_sysconf_path(&path, "classid") < 0)
314  return -NLE_NOMEM;
315 
316  /* if stat fails, just (re-)read the file */
317  if (stat(path, &st) == 0) {
318  /* Don't re-read file if file is unchanged */
319  if (last_read == st.st_mtime) {
320  err = 0;
321  goto errout;
322  }
323  }
324 
325  if (!(fd = fopen(path, "re"))) {
326  err = -nl_syserr2nlerr(errno);
327  goto errout;
328  }
329 
330  clear_hashtable();
331 
332  while (fgets(buf, sizeof(buf), fd)) {
333  uint32_t classid;
334  char *ptr, *tok;
335 
336  /* ignore comments and empty lines */
337  if (*buf == '#' || *buf == '\n' || *buf == '\r')
338  continue;
339 
340  /* token 1 */
341  if (!(tok = strtok_r(buf, " \t", &ptr))) {
342  err = -NLE_INVAL;
343  goto errout_close;
344  }
345 
346  if ((err = rtnl_tc_str2handle(tok, &classid)) < 0)
347  goto errout_close;
348 
349  if (!(tok = strtok_r(NULL, " \t\n\r#", &ptr))) {
350  err = -NLE_INVAL;
351  goto errout_close;
352  }
353 
354  if ((err = classid_map_add(classid, tok)) < 0)
355  goto errout_close;
356  }
357 
358  err = 0;
359  last_read = st.st_mtime;
360 
361 errout_close:
362  fclose(fd);
363 errout:
364  free(path);
365 
366  return err;
367 
368 }
369 
370 int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent)
371 {
372  static uint32_t base = 0x4000 << 16;
373  uint32_t classid;
374  char *path;
375  FILE *fd;
376  int err = 0;
377 
378  if (parent == TC_H_ROOT || parent == TC_H_INGRESS) {
379  do {
380  base += (1 << 16);
381  if (base == TC_H_MAJ(TC_H_ROOT))
382  base = 0x4000 << 16;
383  } while (name_lookup(base));
384 
385  classid = base;
386  } else {
387  classid = TC_H_MAJ(parent);
388  do {
389  if (TC_H_MIN(++classid) == TC_H_MIN(TC_H_ROOT))
390  return -NLE_RANGE;
391  } while (name_lookup(classid));
392  }
393 
394  NL_DBG(2, "Generated new classid %#x\n", classid);
395 
396  if (build_sysconf_path(&path, "classid") < 0)
397  return -NLE_NOMEM;
398 
399  if (!(fd = fopen(path, "ae"))) {
400  err = -nl_syserr2nlerr(errno);
401  goto errout;
402  }
403 
404  fprintf(fd, "%x:", TC_H_MAJ(classid) >> 16);
405  if (TC_H_MIN(classid))
406  fprintf(fd, "%x", TC_H_MIN(classid));
407  fprintf(fd, "\t\t\t%s\n", name);
408 
409  fclose(fd);
410 
411  if ((err = classid_map_add(classid, name)) < 0) {
412  /*
413  * Error adding classid map, re-read classid file is best
414  * option here. It is likely to fail as well but better
415  * than nothing, entry was added to the file already anyway.
416  */
418  }
419 
420  *result = classid;
421  err = 0;
422 errout:
423  free(path);
424 
425  return err;
426 }
427 
428 /** @} */
429 
430 static void __init classid_init(void)
431 {
432  int err, i;
433 
434  for (i = 0; i < CLASSID_NAME_HT_SIZ; i++)
435  nl_init_list_head(&tbl_name[i]);
436 
437  if ((err = rtnl_tc_read_classid_file()) < 0)
438  NL_DBG(1, "Failed to read classid file: %s\n", nl_geterror(err));
439 }
440 
441 static void free_map(void *map)
442 {
443  free(((struct classid_map *)map)->name);
444  free(map);
445 }
446 
447 static void __exit classid_exit(void)
448 {
449  tdestroy(id_root, free_map);
450 }
451 /** @} */
char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len)
Convert a traffic control handle to a character string (Reentrant).
Definition: classid.c:103
int rtnl_tc_str2handle(const char *str, uint32_t *res)
Convert a charactering strint to a traffic control handle.
Definition: classid.c:148
int rtnl_tc_read_classid_file(void)
(Re-)read classid file
Definition: classid.c:305