123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- /*
- * lib/route/classid.c ClassID Management
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation version 2.1
- * of the License.
- *
- * Copyright (c) 2010-2013 Thomas Graf <tgraf@suug.ch>
- */
- /**
- * @ingroup tc
- * @defgroup classid ClassID Management
- * @{
- */
- #include <netlink-private/netlink.h>
- #include <netlink-private/tc.h>
- #include <netlink/netlink.h>
- #include <netlink/utils.h>
- #include <netlink/route/tc.h>
- struct classid_map
- {
- uint32_t classid;
- char * name;
- struct nl_list_head name_list;
- };
- #define CLASSID_NAME_HT_SIZ 256
- static struct nl_list_head tbl_name[CLASSID_NAME_HT_SIZ];
- static void *id_root = NULL;
- static int compare_id(const void *pa, const void *pb)
- {
- const struct classid_map *ma = pa;
- const struct classid_map *mb = pb;
- if (ma->classid < mb->classid)
- return -1;
- if (ma->classid > mb->classid)
- return 1;
- return 0;
- }
- /* djb2 */
- static unsigned int classid_tbl_hash(const char *str)
- {
- unsigned long hash = 5381;
- int c;
- while ((c = *str++))
- hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
- return hash % CLASSID_NAME_HT_SIZ;
- }
- static int classid_lookup(const char *name, uint32_t *result)
- {
- struct classid_map *map;
- int n = classid_tbl_hash(name);
- nl_list_for_each_entry(map, &tbl_name[n], name_list) {
- if (!strcasecmp(map->name, name)) {
- *result = map->classid;
- return 0;
- }
- }
- return -NLE_OBJ_NOTFOUND;
- }
- static char *name_lookup(const uint32_t classid)
- {
- void *res;
- struct classid_map cm = {
- .classid = classid,
- .name = "search entry",
- };
- if ((res = tfind(&cm, &id_root, &compare_id)))
- return (*(struct classid_map **) res)->name;
- return NULL;
- }
- /**
- * @name Traffic Control Handle Translations
- * @{
- */
- /**
- * Convert a traffic control handle to a character string (Reentrant).
- * @arg handle traffic control handle
- * @arg buf destination buffer
- * @arg len buffer length
- *
- * Converts a tarffic control handle to a character string in the
- * form of \c MAJ:MIN and stores it in the specified destination buffer.
- *
- * @return The destination buffer or the type encoded in hexidecimal
- * form if no match was found.
- */
- char *rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len)
- {
- if (TC_H_ROOT == handle)
- snprintf(buf, len, "root");
- else if (TC_H_UNSPEC == handle)
- snprintf(buf, len, "none");
- else if (TC_H_INGRESS == handle)
- snprintf(buf, len, "ingress");
- else {
- char *name;
- if ((name = name_lookup(handle)))
- snprintf(buf, len, "%s", name);
- else if (0 == TC_H_MAJ(handle))
- snprintf(buf, len, ":%x", TC_H_MIN(handle));
- else if (0 == TC_H_MIN(handle))
- snprintf(buf, len, "%x:", TC_H_MAJ(handle) >> 16);
- else
- snprintf(buf, len, "%x:%x",
- TC_H_MAJ(handle) >> 16, TC_H_MIN(handle));
- }
- return buf;
- }
- /**
- * Convert a charactering strint to a traffic control handle
- * @arg str traffic control handle as character string
- * @arg res destination buffer
- *
- * Converts the provided character string specifying a traffic
- * control handle to the corresponding numeric value.
- *
- * The handle must be provided in one of the following formats:
- * - NAME
- * - root
- * - none
- * - MAJ:
- * - :MIN
- * - NAME:MIN
- * - MAJ:MIN
- * - MAJMIN
- *
- * @return 0 on success or a negative error code
- */
- int rtnl_tc_str2handle(const char *str, uint32_t *res)
- {
- char *colon, *end;
- uint32_t h;
- int err;
- if (!strcasecmp(str, "root")) {
- *res = TC_H_ROOT;
- return 0;
- }
- if (!strcasecmp(str, "none")) {
- *res = TC_H_UNSPEC;
- return 0;
- }
- if (!strcasecmp(str, "ingress")) {
- *res = TC_H_INGRESS;
- return 0;
- }
- h = strtoul(str, &colon, 16);
- /* MAJ is not a number */
- if (colon == str) {
- not_a_number:
- if (*colon == ':') {
- /* :YYYY */
- h = 0;
- } else {
- size_t len;
- char name[64] = { 0 };
- if (!(colon = strpbrk(str, ":"))) {
- /* NAME */
- return classid_lookup(str, res);
- } else {
- /* NAME:YYYY */
- len = colon - str;
- if (len >= sizeof(name))
- return -NLE_INVAL;
- memcpy(name, str, len);
- if ((err = classid_lookup(name, &h)) < 0)
- return err;
- /* Name must point to a qdisc alias */
- if (TC_H_MIN(h))
- return -NLE_INVAL;
- /* NAME: is not allowed */
- if (colon[1] == '\0')
- return -NLE_INVAL;
- goto update;
- }
- }
- }
- if (':' == *colon) {
- /* check if we would lose bits */
- if (TC_H_MAJ(h))
- return -NLE_RANGE;
- h <<= 16;
- if ('\0' == colon[1]) {
- /* XXXX: */
- *res = h;
- } else {
- /* XXXX:YYYY */
- uint32_t l;
- update:
- l = strtoul(colon+1, &end, 16);
- /* check if we overlap with major part */
- if (TC_H_MAJ(l))
- return -NLE_RANGE;
- if ('\0' != *end)
- return -NLE_INVAL;
- *res = (h | l);
- }
- } else if ('\0' == *colon) {
- /* XXXXYYYY */
- *res = h;
- } else
- goto not_a_number;
- return 0;
- }
- static void free_nothing(void *arg)
- {
- }
- static void classid_map_free(struct classid_map *map)
- {
- if (!map)
- return;
- free(map->name);
- free(map);
- }
- static void clear_hashtable(void)
- {
- int i;
- for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) {
- struct classid_map *map, *n;
- nl_list_for_each_entry_safe(map, n, &tbl_name[i], name_list)
- classid_map_free(map);
- nl_init_list_head(&tbl_name[i]);
- }
- if (id_root) {
- tdestroy(&id_root, &free_nothing);
- id_root = NULL;
- }
- }
- static int classid_map_add(uint32_t classid, const char *name)
- {
- struct classid_map *map;
- int n;
- if (!(map = calloc(1, sizeof(*map))))
- return -NLE_NOMEM;
- map->classid = classid;
- map->name = strdup(name);
- n = classid_tbl_hash(map->name);
- nl_list_add_tail(&map->name_list, &tbl_name[n]);
- if (!tsearch((void *) map, &id_root, &compare_id)) {
- classid_map_free(map);
- return -NLE_NOMEM;
- }
- return 0;
- }
- /**
- * (Re-)read classid file
- *
- * Rereads the contents of the classid file (typically found at the location
- * /etc/libnl/classid) and refreshes the classid maps.
- *
- * @return 0 on success or a negative error code.
- */
- int rtnl_tc_read_classid_file(void)
- {
- static time_t last_read;
- struct stat st;
- char buf[256], *path;
- FILE *fd;
- int err;
- if (build_sysconf_path(&path, "classid") < 0)
- return -NLE_NOMEM;
- /* if stat fails, just (re-)read the file */
- if (stat(path, &st) == 0) {
- /* Don't re-read file if file is unchanged */
- if (last_read == st.st_mtime) {
- err = 0;
- goto errout;
- }
- }
- if (!(fd = fopen(path, "r"))) {
- err = -nl_syserr2nlerr(errno);
- goto errout;
- }
- clear_hashtable();
- while (fgets(buf, sizeof(buf), fd)) {
- uint32_t classid;
- char *ptr, *tok;
- /* ignore comments and empty lines */
- if (*buf == '#' || *buf == '\n' || *buf == '\r')
- continue;
- /* token 1 */
- if (!(tok = strtok_r(buf, " \t", &ptr))) {
- err = -NLE_INVAL;
- goto errout_close;
- }
- if ((err = rtnl_tc_str2handle(tok, &classid)) < 0)
- goto errout_close;
- if (!(tok = strtok_r(NULL, " \t\n\r#", &ptr))) {
- err = -NLE_INVAL;
- goto errout_close;
- }
- if ((err = classid_map_add(classid, tok)) < 0)
- goto errout_close;
- }
- err = 0;
- last_read = st.st_mtime;
- errout_close:
- fclose(fd);
- errout:
- free(path);
- return err;
- }
- int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent)
- {
- static uint32_t base = 0x4000 << 16;
- uint32_t classid;
- char *path;
- FILE *fd;
- int err = 0;
- if (parent == TC_H_ROOT || parent == TC_H_INGRESS) {
- do {
- base += (1 << 16);
- if (base == TC_H_MAJ(TC_H_ROOT))
- base = 0x4000 << 16;
- } while (name_lookup(base));
- classid = base;
- } else {
- classid = TC_H_MAJ(parent);
- do {
- if (TC_H_MIN(++classid) == TC_H_MIN(TC_H_ROOT))
- return -NLE_RANGE;
- } while (name_lookup(classid));
- }
- NL_DBG(2, "Generated new classid %#x\n", classid);
- if (build_sysconf_path(&path, "classid") < 0)
- return -NLE_NOMEM;
- if (!(fd = fopen(path, "a"))) {
- err = -nl_syserr2nlerr(errno);
- goto errout;
- }
- fprintf(fd, "%x:", TC_H_MAJ(classid) >> 16);
- if (TC_H_MIN(classid))
- fprintf(fd, "%x", TC_H_MIN(classid));
- fprintf(fd, "\t\t\t%s\n", name);
- fclose(fd);
- if ((err = classid_map_add(classid, name)) < 0) {
- /*
- * Error adding classid map, re-read classid file is best
- * option here. It is likely to fail as well but better
- * than nothing, entry was added to the file already anyway.
- */
- rtnl_tc_read_classid_file();
- }
- *result = classid;
- err = 0;
- errout:
- free(path);
- return err;
- }
- /** @} */
- static void __init classid_init(void)
- {
- int err, i;
- for (i = 0; i < CLASSID_NAME_HT_SIZ; i++)
- nl_init_list_head(&tbl_name[i]);
- if ((err = rtnl_tc_read_classid_file()) < 0)
- NL_DBG(1, "Failed to read classid file: %s\n", nl_geterror(err));
- }
- static void free_map(void *map) {
- free(((struct classid_map *)map)->name);
- free(map);
- };
- static void __exit classid_exit(void)
- {
- tdestroy(id_root, free_map);
- }
- /** @} */
|