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, ":%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
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 if (build_sysconf_path(&path, "classid") < 0)
00314 return -NLE_NOMEM;
00315
00316
00317 if (stat(path, &st) == 0) {
00318
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
00337 if (*buf == '#' || *buf == '\n' || *buf == '\r')
00338 continue;
00339
00340
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
00414
00415
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