libnl  3.2.3
/build/buildd/libnl3-3.2.3/lib/route/classid.c
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 /** @} */