classid.c 8.6 KB

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