00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
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;
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
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
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, ":%02x", TC_H_MIN(handle));
00124 else if (0 == TC_H_MIN(handle))
00125 snprintf(buf, len, "%02x:", TC_H_MAJ(handle) >> 16);
00126 else
00127 snprintf(buf, len, "%02x:%02x",
00128 TC_H_MAJ(handle) >> 16, TC_H_MIN(handle));
00129 }
00130
00131 return buf;
00132 }
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
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
00172 if (colon == str) {
00173 not_a_number:
00174 if (*colon == ':') {
00175
00176 h = 0;
00177 } else {
00178 size_t len;
00179 char name[64] = { 0 };
00180
00181 if (!(colon = strpbrk(str, ":"))) {
00182
00183 return classid_lookup(str, res);
00184 } else {
00185
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
00196 if (TC_H_MIN(h))
00197 return -NLE_INVAL;
00198
00199
00200 if (colon[1] == '\0')
00201 return -NLE_INVAL;
00202
00203 goto update;
00204 }
00205 }
00206 }
00207
00208 if (':' == *colon) {
00209
00210 if (TC_H_MAJ(h))
00211 return -NLE_RANGE;
00212 h <<= 16;
00213
00214 if ('\0' == colon[1]) {
00215
00216 *res = h;
00217 } else {
00218
00219 uint32_t l;
00220
00221 update:
00222 l = strtoul(colon+1, &end, 16);
00223
00224
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
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
00299
00300
00301
00302
00303
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 asprintf(&path, "%s/classid", SYSCONFDIR);
00314
00315
00316 if (stat(path, &st) == 0) {
00317
00318 if (last_read == st.st_mtime) {
00319 err = 0;
00320 goto errout;
00321 }
00322 }
00323
00324 if (!(fd = fopen(path, "r"))) {
00325 err = -nl_syserr2nlerr(errno);
00326 goto errout;
00327 }
00328
00329 clear_hashtable();
00330
00331 while (fgets(buf, sizeof(buf), fd)) {
00332 uint32_t classid;
00333 char *ptr, *tok;
00334
00335
00336 if (*buf == '#' || *buf == '\n' || *buf == '\r')
00337 continue;
00338
00339
00340 if (!(tok = strtok_r(buf, " \t", &ptr))) {
00341 err = -NLE_INVAL;
00342 goto errout_close;
00343 }
00344
00345 if ((err = rtnl_tc_str2handle(tok, &classid)) < 0)
00346 goto errout_close;
00347
00348 if (!(tok = strtok_r(NULL, " \t\n\r#", &ptr))) {
00349 err = -NLE_INVAL;
00350 goto errout_close;
00351 }
00352
00353 if ((err = classid_map_add(classid, tok)) < 0)
00354 goto errout_close;
00355 }
00356
00357 err = 0;
00358 last_read = st.st_mtime;
00359
00360 errout_close:
00361 fclose(fd);
00362 errout:
00363 free(path);
00364
00365 return err;
00366
00367 }
00368
00369 int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent)
00370 {
00371 static uint32_t base = 0x4000 << 16;
00372 uint32_t classid;
00373 char *path;
00374 FILE *fd;
00375 int err = 0;
00376
00377 if (parent == TC_H_ROOT || parent == TC_H_INGRESS) {
00378 do {
00379 base += (1 << 16);
00380 if (base == TC_H_MAJ(TC_H_ROOT))
00381 base = 0x4000 << 16;
00382 } while (name_lookup(base));
00383
00384 classid = base;
00385 } else {
00386 classid = TC_H_MAJ(parent);
00387 do {
00388 if (TC_H_MIN(++classid) == TC_H_MIN(TC_H_ROOT))
00389 return -NLE_RANGE;
00390 } while (name_lookup(classid));
00391 }
00392
00393 NL_DBG(2, "Generated new classid %#x\n", classid);
00394
00395 if (asprintf(&path, "%s/classid", SYSCONFDIR) < 0)
00396 return -NLE_NOMEM;
00397
00398 if (!(fd = fopen(path, "a"))) {
00399 err = -nl_syserr2nlerr(errno);
00400 goto errout;
00401 }
00402
00403 fprintf(fd, "%x:", TC_H_MAJ(classid) >> 16);
00404 if (TC_H_MIN(classid))
00405 fprintf(fd, "%x", TC_H_MIN(classid));
00406 fprintf(fd, "\t\t\t%s\n", name);
00407
00408 fclose(fd);
00409
00410 if ((err = classid_map_add(classid, name)) < 0) {
00411
00412
00413
00414
00415
00416 rtnl_tc_read_classid_file();
00417 }
00418
00419 *result = classid;
00420 err = 0;
00421 errout:
00422 free(path);
00423
00424 return err;
00425 }
00426
00427
00428
00429 static void __init classid_init(void)
00430 {
00431 int err, i;
00432
00433 for (i = 0; i < CLASSID_NAME_HT_SIZ; i++)
00434 nl_init_list_head(&tbl_name[i]);
00435
00436 if ((err = rtnl_tc_read_classid_file()) < 0)
00437 fprintf(stderr, "Failed to read classid file: %s\n", nl_geterror(err));
00438 }
00439
00440