From 757592ec1b1d3c70a325ae997adb94009589c5a6 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 19 Oct 2010 16:31:23 +0200 Subject: [PATCH] classid database A database to resolve qdisc/class names to classid values and vice versa. The function rtnl_tc_handle2str() and rtnl_tc_str2handle() will resolve names automatically. A CLI based tool nl-classid-lookup is provided to integrate the database into existing iproute2 scripts. --- Makefile.am | 4 +- configure.in | 2 +- etc/classid | 36 ++++ include/netlink-local.h | 1 + include/netlink/route/tc.h | 1 + lib/Makefile.am | 2 +- lib/route/classid.c | 357 +++++++++++++++++++++++++++++++++++++ lib/route/tc.c | 108 ----------- man/Makefile.am | 3 + man/nl-classid-lookup.8 | 48 +++++ src/.gitignore | 1 + src/Makefile.am | 6 +- src/nl-classid-lookup.c | 79 ++++++++ 13 files changed, 535 insertions(+), 113 deletions(-) create mode 100644 etc/classid create mode 100644 lib/route/classid.c create mode 100644 man/Makefile.am create mode 100644 man/nl-classid-lookup.8 create mode 100644 src/nl-classid-lookup.c diff --git a/Makefile.am b/Makefile.am index 28545fd..420f065 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,12 +8,12 @@ if ENABLE_CLI OPT_DIRS += src endif -SUBDIRS = include lib doc $(OPT_DIRS) +SUBDIRS = include lib doc man $(OPT_DIRS) pkgconfig_DATA = libnl-2.1.pc sysconfdir = @sysconfdir@/libnl -sysconf_DATA = etc/pktloc +sysconf_DATA = etc/pktloc etc/classid .PHONY: cscope cscope: diff --git a/configure.in b/configure.in index 2d0b2dd..c58a316 100644 --- a/configure.in +++ b/configure.in @@ -37,6 +37,6 @@ AM_CONDITIONAL([ENABLE_CLI], [test "$enable_cli" = "yes"]) AC_CHECK_LIB([m], [pow], [], AC_MSG_ERROR([libm is required])) AC_CONFIG_FILES([Makefile doc/Doxyfile doc/Makefile lib/Makefile - include/Makefile src/Makefile src/lib/Makefile \ + include/Makefile src/Makefile src/lib/Makefile man/Makefile libnl-2.1.pc include/netlink/version.h]) AC_OUTPUT diff --git a/etc/classid b/etc/classid new file mode 100644 index 0000000..76a11f2 --- /dev/null +++ b/etc/classid @@ -0,0 +1,36 @@ +############################################################################### +# +# ClassID <-> Name Translation Table +# +# This file can be used to assign names to classids for easier reference +# in all libnl tools. +# +# Format: +# # qdisc definition +# # class deifnition +# # class definition referencing an +# existing qdisc definition. +# +# Example: +# 1: top # top -> 1:0 +# top:1 interactive # interactive -> 1:1 +# top:2 www # www -> 1:2 +# top:3 bulk # bulk -> 1:3 +# 2:1 test_class # test_class -> 2:1 +# +# Illegal Example: +# 30:1 classD +# classD:2 invalidClass # classD refers to a class, not a qdisc +# +############################################################################### + +# + +# Reserved default classids +0:0 none +ffff:ffff root +ffff:fff1 ingress + +# +# List your classid definitions below: +# diff --git a/include/netlink-local.h b/include/netlink-local.h index e0c79db..1e5fc0e 100644 --- a/include/netlink-local.h +++ b/include/netlink-local.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/include/netlink/route/tc.h b/include/netlink/route/tc.h index 3cb876f..84c766f 100644 --- a/include/netlink/route/tc.h +++ b/include/netlink/route/tc.h @@ -53,6 +53,7 @@ extern int rtnl_tc_build_rate_table(uint32_t *, uint8_t, uint8_t, int, int); /* TC Handle Translations */ +extern int rtnl_tc_read_classid_file(void); extern char * rtnl_tc_handle2str(uint32_t, char *, size_t); extern int rtnl_tc_str2handle(const char *, uint32_t *); diff --git a/lib/Makefile.am b/lib/Makefile.am index afb08ea..0769011 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -39,7 +39,7 @@ libnl_route_la_SOURCES = \ route/cls.c route/cls_api.c route/cls_obj.c route/link.c \ route/neigh.c route/neightbl.c route/nexthop.c route/qdisc.c \ route/qdisc_api.c route/qdisc_obj.c route/route.c route/route_obj.c \ - route/route_utils.c route/rtnl.c route/rule.c route/tc.c \ + route/route_utils.c route/rtnl.c route/rule.c route/tc.c route/classid.c \ \ route/cls/fw.c route/cls/police.c route/cls/u32.c \ \ diff --git a/lib/route/classid.c b/lib/route/classid.c new file mode 100644 index 0000000..5650a20 --- /dev/null +++ b/lib/route/classid.c @@ -0,0 +1,357 @@ +/* + * lib/route/classid.c ClassID Management + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2010 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup classid ClassID Management + * @{ + */ + +#include +#include +#include +#include +#include + +struct classid_map +{ + uint32_t classid; + char * name; + struct nl_list_head name_list; +}; + +#define CLASSID_NAME_HT_SIZ 256 + +static struct nl_list_head tbl_name[CLASSID_NAME_HT_SIZ]; + +static void *id_root = NULL; + +static int compare_id(const void *pa, const void *pb) +{ + const struct classid_map *ma = pa; + const struct classid_map *mb = pb; + + if (ma->classid < mb->classid) + return -1; + + if (ma->classid > mb->classid) + return 1; + + return 0; +} + +/* djb2 */ +static unsigned int classid_tbl_hash(const char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash % CLASSID_NAME_HT_SIZ; +} + +static int classid_lookup(const char *name, uint32_t *result) +{ + struct classid_map *map; + int n = classid_tbl_hash(name); + + nl_list_for_each_entry(map, &tbl_name[n], name_list) { + if (!strcasecmp(map->name, name)) { + *result = map->classid; + return 0; + } + } + + return -NLE_OBJ_NOTFOUND; +} + +/** + * @name Traffic Control Handle Translations + * @{ + */ + +/** + * Convert a traffic control handle to a character string (Reentrant). + * @arg handle traffic control handle + * @arg buf destination buffer + * @arg len buffer length + * + * Converts a tarffic control handle to a character string in the + * form of \c MAJ:MIN and stores it in the specified destination buffer. + * + * @return The destination buffer or the type encoded in hexidecimal + * form if no match was found. + */ +char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) +{ + if (TC_H_ROOT == handle) + snprintf(buf, len, "root"); + else if (TC_H_UNSPEC == handle) + snprintf(buf, len, "none"); + else if (TC_H_INGRESS == handle) + snprintf(buf, len, "ingress"); + else { + void *res; + struct classid_map cm = { + .classid = handle, + .name = "search entry", + }; + + if ((res = tfind(&cm, &id_root, &compare_id))) + snprintf(buf, len, "%s", (*(struct classid_map **) res)->name); + else if (0 == TC_H_MAJ(handle)) + snprintf(buf, len, ":%02x", TC_H_MIN(handle)); + else if (0 == TC_H_MIN(handle)) + snprintf(buf, len, "%02x:", TC_H_MAJ(handle) >> 16); + else + snprintf(buf, len, "%02x:%02x", + TC_H_MAJ(handle) >> 16, TC_H_MIN(handle)); + } + + return buf; +} + +/** + * Convert a charactering strint to a traffic control handle + * @arg str traffic control handle as character string + * @arg res destination buffer + * + * Converts the provided character string specifying a traffic + * control handle to the corresponding numeric value. + * + * The handle must be provided in one of the following formats: + * - NAME + * - root + * - none + * - MAJ: + * - :MIN + * - NAME:MIN + * - MAJ:MIN + * - MAJMIN + * + * @return 0 on success or a negative error code + */ +int rtnl_tc_str2handle(const char *str, uint32_t *res) +{ + char *colon, *end; + uint32_t h, err; + + if (!strcasecmp(str, "root")) { + *res = TC_H_ROOT; + return 0; + } + + if (!strcasecmp(str, "none")) { + *res = TC_H_UNSPEC; + return 0; + } + + h = strtoul(str, &colon, 16); + + /* MAJ is not a number */ + if (colon == str) { + if (*colon == ':') { + /* :YYYY */ + h = 0; + } else { + size_t len; + char name[64] = { 0 }; + + if (!(colon = strpbrk(str, ":"))) { + /* NAME */ + return classid_lookup(str, res); + } else { + /* NAME:YYYY */ + len = colon - str; + if (len >= sizeof(name)) + return -NLE_INVAL; + + memcpy(name, str, len); + + if ((err = classid_lookup(name, &h)) < 0) + return err; + + /* Name must point to a qdisc alias */ + if (TC_H_MIN(h)) + return -NLE_INVAL; + + /* NAME: is not allowed */ + if (colon[1] == '\0') + return -NLE_INVAL; + + goto update; + } + } + } + + if (':' == *colon) { + /* check if we would lose bits */ + if (TC_H_MAJ(h)) + return -NLE_RANGE; + h <<= 16; + + if ('\0' == colon[1]) { + /* XXXX: */ + *res = h; + } else { + /* XXXX:YYYY */ + uint32_t l; + +update: + l = strtoul(colon+1, &end, 16); + + /* check if we overlap with major part */ + if (TC_H_MAJ(l)) + return -NLE_RANGE; + + if ('\0' != *end) + return -NLE_INVAL; + + *res = (h | l); + } + } else if ('\0' == *colon) { + /* XXXXYYYY */ + *res = h; + } else + return -NLE_INVAL; + + return 0; +} + +static void free_nothing(void *arg) +{ +} + +static void clear_hashtable(void) +{ + int i; + + for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) { + struct classid_map *map, *n; + + nl_list_for_each_entry_safe(map, n, &tbl_name[i], name_list) { + free(map->name); + free(map); + } + + nl_init_list_head(&tbl_name[i]); + + } + + if (id_root) { + tdestroy(&id_root, &free_nothing); + id_root = NULL; + } +} + +/** + * (Re-)read classid file + * + * Rereads the contents of the classid file (typically found at the location + * /etc/libnl/classid) and refreshes the classid maps. + * + * @return 0 on success or a negative error code. + */ +int rtnl_tc_read_classid_file(void) +{ + static time_t last_read; + struct stat st = {0}; + char buf[256], *path; + FILE *fd; + int err; + + asprintf(&path, "%s/classid", SYSCONFDIR); + + /* if stat fails, just (re-)read the file */ + if (stat(path, &st) == 0) { + /* Don't re-read file if file is unchanged */ + if (last_read == st.st_mtime) { + err = 0; + goto errout; + } + } + + if (!(fd = fopen(path, "r"))) { + err = -nl_syserr2nlerr(errno); + goto errout; + } + + clear_hashtable(); + + while (fgets(buf, sizeof(buf), fd)) { + struct classid_map *map; + uint32_t classid; + char *ptr, *tok; + int n; + + /* ignore comments and empty lines */ + if (*buf == '#' || *buf == '\n' || *buf == '\r') + continue; + + /* token 1 */ + if (!(tok = strtok_r(buf, " \t", &ptr))) { + err = -NLE_INVAL; + goto errout_close; + } + + if ((err = rtnl_tc_str2handle(tok, &classid)) < 0) + goto errout_close; + + if (!(tok = strtok_r(NULL, " \t\n\r#", &ptr))) { + err = -NLE_INVAL; + goto errout_close; + } + + if (!(map = calloc(1, sizeof(*map)))) { + err = -NLE_NOMEM; + goto errout_close; + } + + map->classid = classid; + map->name = strdup(tok); + + n = classid_tbl_hash(map->name); + nl_list_add_tail(&map->name_list, &tbl_name[n]); + + if (!tsearch((void *) map, &id_root, &compare_id)) { + err = -NLE_NOMEM; + goto errout_close; + } + } + + err = 0; + last_read = st.st_mtime; + +errout_close: + fclose(fd); +errout: + free(path); + + return err; + +} + +/** @} */ + +static void __init classid_init(void) +{ + int err, i; + + for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) + nl_init_list_head(&tbl_name[i]); + + if ((err = rtnl_tc_read_classid_file()) < 0) + fprintf(stderr, "Failed to read classid file: %s\n", nl_geterror(err)); +} + +/** @} */ diff --git a/lib/route/tc.c b/lib/route/tc.c index 97faef4..9d85801 100644 --- a/lib/route/tc.c +++ b/lib/route/tc.c @@ -465,113 +465,5 @@ int rtnl_tc_build_rate_table(uint32_t *dst, uint8_t mpu, uint8_t overhead, /** @} */ -/** - * @name Traffic Control Handle Translations - * @{ - */ - -/** - * Convert a traffic control handle to a character string (Reentrant). - * @arg handle traffic control handle - * @arg buf destination buffer - * @arg len buffer length - * - * Converts a tarffic control handle to a character string in the - * form of \c MAJ:MIN and stores it in the specified destination buffer. - * - * @return The destination buffer or the type encoded in hexidecimal - * form if no match was found. - */ -char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) -{ - if (TC_H_ROOT == handle) - snprintf(buf, len, "root"); - else if (TC_H_UNSPEC == handle) - snprintf(buf, len, "none"); - else if (0 == TC_H_MAJ(handle)) - snprintf(buf, len, ":%02x", TC_H_MIN(handle)); - else if (0 == TC_H_MIN(handle)) - snprintf(buf, len, "%02x:", TC_H_MAJ(handle) >> 16); - else - snprintf(buf, len, "%02x:%02x", - TC_H_MAJ(handle) >> 16, TC_H_MIN(handle)); - - return buf; -} - -/** - * Convert a charactering strint to a traffic control handle - * @arg name traffic control handle as character string - * @arg res destination buffer - * - * Converts the provided character string specifying a traffic - * control handle to the corresponding numeric value. - * - * The handle must be provided in one of the following formats: - * - root - * - none - * - XXXX: - * - :YYYY - * - XXXX:YYYY - * - XXXXYYYY - * - * @return 0 on success or a negative error code - */ -int rtnl_tc_str2handle(const char *name, uint32_t *res) -{ - char *colon, *end; - uint32_t h; - - if (!strcasecmp(name, "root")) { - *res = TC_H_ROOT; - return 0; - } - - if (!strcasecmp(name, "none")) { - *res = TC_H_UNSPEC; - return 0; - } - - h = strtoul(name, &colon, 16); - - if (colon == name) { - /* :YYYY */ - h = 0; - if (':' != *colon) - return -NLE_INVAL; - } - - if (':' == *colon) { - /* check if we would lose bits */ - if (TC_H_MAJ(h)) - return -NLE_RANGE; - h <<= 16; - - if ('\0' == colon[1]) { - /* XXXX: */ - *res = h; - } else { - /* XXXX:YYYY */ - uint32_t l = strtoul(colon+1, &end, 16); - - /* check if we overlap with major part */ - if (TC_H_MAJ(l)) - return -NLE_RANGE; - - if ('\0' != *end) - return -NLE_INVAL; - - *res = (h | l); - } - } else if ('\0' == *colon) { - /* XXXXYYYY */ - *res = h; - } else - return -NLE_INVAL; - - return 0; -} - -/** @} */ /** @} */ diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 0000000..0d7ad7a --- /dev/null +++ b/man/Makefile.am @@ -0,0 +1,3 @@ +# -*- Makefile -*- + +dist_man8_MANS = nl-classid-lookup.8 diff --git a/man/nl-classid-lookup.8 b/man/nl-classid-lookup.8 new file mode 100644 index 0000000..f447461 --- /dev/null +++ b/man/nl-classid-lookup.8 @@ -0,0 +1,48 @@ +.TH nl\-classid\-lookup 8 "19 October 2010" "libnl" +.LO 1 +.SH NAME +nl\-classid\-lookup - Lookup classid definitions +.SH SYNOPSIS +.B nl\-classid\-lookup +.RB [ \-hv ] +.RB [ \-r ] +.I name + +.SH DESCRIPTION +.PP +nl\-classid\-lookup searches the classid database for a matching entry. It is used +to resolve qdisc/class names to classid values and vice versa. + +.SH OPTIONS +.TP +.BR \-\^h " or " \-\-help +Print help text to console and exit. +.TP +.BR \-\^v " or " \-\-version +Print versioning information to console and exit. +.TP +.BR \-\^r " or " \-\-reverse +Do a reverse lookup. Lookup a classid and print its name. + +.SH USAGE +.PP +Resolve the qdisc/class name "interactive": +.PP +.RS +# nl\-classid\-lookup interactive +.RE +.PP +Lookup the name of classid 1:2: +.PP +.RS +# nl\-classid\-lookup -r 1:2 +.RE + +.SH FILES +.PP +/etc/libnl/classid + +.SH AUTHOR +.PP +Thomas Graf is the original author and current maintainer of libnl and +libnl tools. Many people have contributed to it since. diff --git a/src/.gitignore b/src/.gitignore index 60233c8..12859cb 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -29,3 +29,4 @@ nl-rule-list nl-tctree-list nl-util-addr nf-queue +nl-classid-lookup diff --git a/src/Makefile.am b/src/Makefile.am index 5144bb4..4a7f98f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,7 +6,8 @@ AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SO AM_LDFLAGS = -L${top_builddir}/lib -L${top_builddir}/src/lib -lnl-cli sbin_PROGRAMS = \ - nl-qdisc-add + nl-qdisc-add \ + nl-classid-lookup noinst_PROGRAMS = \ genl-ctrl-list \ @@ -104,3 +105,6 @@ nl_util_addr_LDADD = -lnl-route nl_pktloc_lookup_SOURCES = nl-pktloc-lookup.c nl_pktloc_lookup_LDADD = -lnl-route + +nl_classid_lookup_SOURCES = nl-classid-lookup.c +nl_classid_lookup_LDADD = -lnl-route diff --git a/src/nl-classid-lookup.c b/src/nl-classid-lookup.c new file mode 100644 index 0000000..faa65dd --- /dev/null +++ b/src/nl-classid-lookup.c @@ -0,0 +1,79 @@ +/* + * src/nl-classid-lookup.c Lookup classid + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2010 Thomas Graf + */ + +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-classid-lookup [OPTIONS]... NAME\n" +"\n" +"OPTIONS\n" +" -h, --help Show this help text.\n" +" -v, --version Show versioning information.\n" +" -r, --reverse Do a reverse lookup, i.e. classid to name.\n" +"\n" +"EXAMPLE\n" +" $ nl-classid-lookup low_latency\n" +" $ nl-classid-lookup -r 1:12\n" +"\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) +{ + uint32_t classid; + char *name; + int err, reverse = 0; + + for (;;) { + int c, optidx = 0; + static struct option long_opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "reverse", 0, 0, 'r' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hvr", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'h': print_usage(); break; + case 'v': nl_cli_print_version(); break; + case 'r': reverse = 1; break; + } + } + + if (optind >= argc) + print_usage(); + + name = argv[optind++]; + + /* + * We use rtnl_tc_str2handle() even while doing a reverse lookup. This + * allows for name -> name lookups. This is intentional, it does not + * do any harm and avoids duplicating a lot of code. + */ + if ((err = rtnl_tc_str2handle(name, &classid)) < 0) + nl_cli_fatal(err, "Unable to lookup classid \"%s\": %s", + name, nl_geterror(err)); + + if (reverse) { + char buf[64]; + printf("%s\n", rtnl_tc_handle2str(classid, buf, sizeof(buf))); + } else + printf("%x:%x\n", TC_H_MAJ(classid) >> 16, TC_H_MIN(classid)); + + return 0; +}