libnl
3.2.3
|
00001 /* 00002 * lib/route/classid.c ClassID Management 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2010 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup tc 00014 * @defgroup classid ClassID Management 00015 * @{ 00016 */ 00017 00018 #include <netlink-local.h> 00019 #include <netlink-tc.h> 00020 #include <netlink/netlink.h> 00021 #include <netlink/utils.h> 00022 #include <netlink/route/tc.h> 00023 00024 struct classid_map 00025 { 00026 uint32_t classid; 00027 char * name; 00028 struct nl_list_head name_list; 00029 }; 00030 00031 #define CLASSID_NAME_HT_SIZ 256 00032 00033 static struct nl_list_head tbl_name[CLASSID_NAME_HT_SIZ]; 00034 00035 static void *id_root = NULL; 00036 00037 static int compare_id(const void *pa, const void *pb) 00038 { 00039 const struct classid_map *ma = pa; 00040 const struct classid_map *mb = pb; 00041 00042 if (ma->classid < mb->classid) 00043 return -1; 00044 00045 if (ma->classid > mb->classid) 00046 return 1; 00047 00048 return 0; 00049 } 00050 00051 /* djb2 */ 00052 static unsigned int classid_tbl_hash(const char *str) 00053 { 00054 unsigned long hash = 5381; 00055 int c; 00056 00057 while ((c = *str++)) 00058 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 00059 00060 return hash % CLASSID_NAME_HT_SIZ; 00061 } 00062 00063 static int classid_lookup(const char *name, uint32_t *result) 00064 { 00065 struct classid_map *map; 00066 int n = classid_tbl_hash(name); 00067 00068 nl_list_for_each_entry(map, &tbl_name[n], name_list) { 00069 if (!strcasecmp(map->name, name)) { 00070 *result = map->classid; 00071 return 0; 00072 } 00073 } 00074 00075 return -NLE_OBJ_NOTFOUND; 00076 } 00077 00078 static char *name_lookup(const uint32_t classid) 00079 { 00080 void *res; 00081 struct classid_map cm = { 00082 .classid = classid, 00083 .name = "search entry", 00084 }; 00085 00086 if ((res = tfind(&cm, &id_root, &compare_id))) 00087 return (*(struct classid_map **) res)->name; 00088 00089 return NULL; 00090 } 00091 00092 /** 00093 * @name Traffic Control Handle Translations 00094 * @{ 00095 */ 00096 00097 /** 00098 * Convert a traffic control handle to a character string (Reentrant). 00099 * @arg handle traffic control handle 00100 * @arg buf destination buffer 00101 * @arg len buffer length 00102 * 00103 * Converts a tarffic control handle to a character string in the 00104 * form of \c MAJ:MIN and stores it in the specified destination buffer. 00105 * 00106 * @return The destination buffer or the type encoded in hexidecimal 00107 * form if no match was found. 00108 */ 00109 char *rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) 00110 { 00111 if (TC_H_ROOT == handle) 00112 snprintf(buf, len, "root"); 00113 else if (TC_H_UNSPEC == handle) 00114 snprintf(buf, len, "none"); 00115 else if (TC_H_INGRESS == handle) 00116 snprintf(buf, len, "ingress"); 00117 else { 00118 char *name; 00119 00120 if ((name = name_lookup(handle))) 00121 snprintf(buf, len, "%s", name); 00122 else if (0 == TC_H_MAJ(handle)) 00123 snprintf(buf, len, ":%x", TC_H_MIN(handle)); 00124 else if (0 == TC_H_MIN(handle)) 00125 snprintf(buf, len, "%x:", TC_H_MAJ(handle) >> 16); 00126 else 00127 snprintf(buf, len, "%x:%x", 00128 TC_H_MAJ(handle) >> 16, TC_H_MIN(handle)); 00129 } 00130 00131 return buf; 00132 } 00133 00134 /** 00135 * Convert a charactering strint to a traffic control handle 00136 * @arg str traffic control handle as character string 00137 * @arg res destination buffer 00138 * 00139 * Converts the provided character string specifying a traffic 00140 * control handle to the corresponding numeric value. 00141 * 00142 * The handle must be provided in one of the following formats: 00143 * - NAME 00144 * - root 00145 * - none 00146 * - MAJ: 00147 * - :MIN 00148 * - NAME:MIN 00149 * - MAJ:MIN 00150 * - MAJMIN 00151 * 00152 * @return 0 on success or a negative error code 00153 */ 00154 int rtnl_tc_str2handle(const char *str, uint32_t *res) 00155 { 00156 char *colon, *end; 00157 uint32_t h, err; 00158 00159 if (!strcasecmp(str, "root")) { 00160 *res = TC_H_ROOT; 00161 return 0; 00162 } 00163 00164 if (!strcasecmp(str, "none")) { 00165 *res = TC_H_UNSPEC; 00166 return 0; 00167 } 00168 00169 h = strtoul(str, &colon, 16); 00170 00171 /* MAJ is not a number */ 00172 if (colon == str) { 00173 not_a_number: 00174 if (*colon == ':') { 00175 /* :YYYY */ 00176 h = 0; 00177 } else { 00178 size_t len; 00179 char name[64] = { 0 }; 00180 00181 if (!(colon = strpbrk(str, ":"))) { 00182 /* NAME */ 00183 return classid_lookup(str, res); 00184 } else { 00185 /* NAME:YYYY */ 00186 len = colon - str; 00187 if (len >= sizeof(name)) 00188 return -NLE_INVAL; 00189 00190 memcpy(name, str, len); 00191 00192 if ((err = classid_lookup(name, &h)) < 0) 00193 return err; 00194 00195 /* Name must point to a qdisc alias */ 00196 if (TC_H_MIN(h)) 00197 return -NLE_INVAL; 00198 00199 /* NAME: is not allowed */ 00200 if (colon[1] == '\0') 00201 return -NLE_INVAL; 00202 00203 goto update; 00204 } 00205 } 00206 } 00207 00208 if (':' == *colon) { 00209 /* check if we would lose bits */ 00210 if (TC_H_MAJ(h)) 00211 return -NLE_RANGE; 00212 h <<= 16; 00213 00214 if ('\0' == colon[1]) { 00215 /* XXXX: */ 00216 *res = h; 00217 } else { 00218 /* XXXX:YYYY */ 00219 uint32_t l; 00220 00221 update: 00222 l = strtoul(colon+1, &end, 16); 00223 00224 /* check if we overlap with major part */ 00225 if (TC_H_MAJ(l)) 00226 return -NLE_RANGE; 00227 00228 if ('\0' != *end) 00229 return -NLE_INVAL; 00230 00231 *res = (h | l); 00232 } 00233 } else if ('\0' == *colon) { 00234 /* XXXXYYYY */ 00235 *res = h; 00236 } else 00237 goto not_a_number; 00238 00239 return 0; 00240 } 00241 00242 static void free_nothing(void *arg) 00243 { 00244 } 00245 00246 static void classid_map_free(struct classid_map *map) 00247 { 00248 if (!map) 00249 return; 00250 00251 free(map->name); 00252 free(map); 00253 } 00254 00255 static void clear_hashtable(void) 00256 { 00257 int i; 00258 00259 for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) { 00260 struct classid_map *map, *n; 00261 00262 nl_list_for_each_entry_safe(map, n, &tbl_name[i], name_list) 00263 classid_map_free(map); 00264 00265 nl_init_list_head(&tbl_name[i]); 00266 00267 } 00268 00269 if (id_root) { 00270 tdestroy(&id_root, &free_nothing); 00271 id_root = NULL; 00272 } 00273 } 00274 00275 static int classid_map_add(uint32_t classid, const char *name) 00276 { 00277 struct classid_map *map; 00278 int n; 00279 00280 if (!(map = calloc(1, sizeof(*map)))) 00281 return -NLE_NOMEM; 00282 00283 map->classid = classid; 00284 map->name = strdup(name); 00285 00286 n = classid_tbl_hash(map->name); 00287 nl_list_add_tail(&map->name_list, &tbl_name[n]); 00288 00289 if (!tsearch((void *) map, &id_root, &compare_id)) { 00290 classid_map_free(map); 00291 return -NLE_NOMEM; 00292 } 00293 00294 return 0; 00295 } 00296 00297 /** 00298 * (Re-)read classid file 00299 * 00300 * Rereads the contents of the classid file (typically found at the location 00301 * /etc/libnl/classid) and refreshes the classid maps. 00302 * 00303 * @return 0 on success or a negative error code. 00304 */ 00305 int rtnl_tc_read_classid_file(void) 00306 { 00307 static time_t last_read; 00308 struct stat st = {0}; 00309 char buf[256], *path; 00310 FILE *fd; 00311 int err; 00312 00313 if (build_sysconf_path(&path, "classid") < 0) 00314 return -NLE_NOMEM; 00315 00316 /* if stat fails, just (re-)read the file */ 00317 if (stat(path, &st) == 0) { 00318 /* Don't re-read file if file is unchanged */ 00319 if (last_read == st.st_mtime) { 00320 err = 0; 00321 goto errout; 00322 } 00323 } 00324 00325 if (!(fd = fopen(path, "r"))) { 00326 err = -nl_syserr2nlerr(errno); 00327 goto errout; 00328 } 00329 00330 clear_hashtable(); 00331 00332 while (fgets(buf, sizeof(buf), fd)) { 00333 uint32_t classid; 00334 char *ptr, *tok; 00335 00336 /* ignore comments and empty lines */ 00337 if (*buf == '#' || *buf == '\n' || *buf == '\r') 00338 continue; 00339 00340 /* token 1 */ 00341 if (!(tok = strtok_r(buf, " \t", &ptr))) { 00342 err = -NLE_INVAL; 00343 goto errout_close; 00344 } 00345 00346 if ((err = rtnl_tc_str2handle(tok, &classid)) < 0) 00347 goto errout_close; 00348 00349 if (!(tok = strtok_r(NULL, " \t\n\r#", &ptr))) { 00350 err = -NLE_INVAL; 00351 goto errout_close; 00352 } 00353 00354 if ((err = classid_map_add(classid, tok)) < 0) 00355 goto errout_close; 00356 } 00357 00358 err = 0; 00359 last_read = st.st_mtime; 00360 00361 errout_close: 00362 fclose(fd); 00363 errout: 00364 free(path); 00365 00366 return err; 00367 00368 } 00369 00370 int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent) 00371 { 00372 static uint32_t base = 0x4000 << 16; 00373 uint32_t classid; 00374 char *path; 00375 FILE *fd; 00376 int err = 0; 00377 00378 if (parent == TC_H_ROOT || parent == TC_H_INGRESS) { 00379 do { 00380 base += (1 << 16); 00381 if (base == TC_H_MAJ(TC_H_ROOT)) 00382 base = 0x4000 << 16; 00383 } while (name_lookup(base)); 00384 00385 classid = base; 00386 } else { 00387 classid = TC_H_MAJ(parent); 00388 do { 00389 if (TC_H_MIN(++classid) == TC_H_MIN(TC_H_ROOT)) 00390 return -NLE_RANGE; 00391 } while (name_lookup(classid)); 00392 } 00393 00394 NL_DBG(2, "Generated new classid %#x\n", classid); 00395 00396 if (build_sysconf_path(&path, "classid") < 0) 00397 return -NLE_NOMEM; 00398 00399 if (!(fd = fopen(path, "a"))) { 00400 err = -nl_syserr2nlerr(errno); 00401 goto errout; 00402 } 00403 00404 fprintf(fd, "%x:", TC_H_MAJ(classid) >> 16); 00405 if (TC_H_MIN(classid)) 00406 fprintf(fd, "%x", TC_H_MIN(classid)); 00407 fprintf(fd, "\t\t\t%s\n", name); 00408 00409 fclose(fd); 00410 00411 if ((err = classid_map_add(classid, name)) < 0) { 00412 /* 00413 * Error adding classid map, re-read classid file is best 00414 * option here. It is likely to fail as well but better 00415 * than nothing, entry was added to the file already anyway. 00416 */ 00417 rtnl_tc_read_classid_file(); 00418 } 00419 00420 *result = classid; 00421 err = 0; 00422 errout: 00423 free(path); 00424 00425 return err; 00426 } 00427 00428 /** @} */ 00429 00430 static void __init classid_init(void) 00431 { 00432 int err, i; 00433 00434 for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) 00435 nl_init_list_head(&tbl_name[i]); 00436 00437 if ((err = rtnl_tc_read_classid_file()) < 0) 00438 fprintf(stderr, "Failed to read classid file: %s\n", nl_geterror(err)); 00439 } 00440 00441 /** @} */