summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Graf <tgraf@suug.ch>2010-10-19 14:31:23 (GMT)
committerThomas Graf <tgraf@suug.ch>2010-10-19 14:31:23 (GMT)
commit757592ec1b1d3c70a325ae997adb94009589c5a6 (patch)
tree40aabb22d05207a6e07a7a05f6d034dc06a0f038
parentfa89403149a59ed18015713f517a5cc9356caffd (diff)
downloadlibnl-757592ec1b1d3c70a325ae997adb94009589c5a6.zip
libnl-757592ec1b1d3c70a325ae997adb94009589c5a6.tar.gz
libnl-757592ec1b1d3c70a325ae997adb94009589c5a6.tar.bz2
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.
-rw-r--r--Makefile.am4
-rw-r--r--configure.in2
-rw-r--r--etc/classid36
-rw-r--r--include/netlink-local.h1
-rw-r--r--include/netlink/route/tc.h1
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/route/classid.c357
-rw-r--r--lib/route/tc.c108
-rw-r--r--man/Makefile.am3
-rw-r--r--man/nl-classid-lookup.848
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile.am6
-rw-r--r--src/nl-classid-lookup.c79
13 files changed, 535 insertions, 113 deletions
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:
+# <MAJ:> <NAME> # qdisc definition
+# <MAJ:MIN> <NAME> # class deifnition
+# <NAME:MIN> <NAME> # 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
+#
+###############################################################################
+
+# <CLASSID> <NAME>
+
+# 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 <inttypes.h>
#include <assert.h>
#include <limits.h>
+#include <search.h>
#include <arpa/inet.h>
#include <netdb.h>
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 <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup tc
+ * @defgroup classid ClassID Management
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink-tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/route/tc.h>
+
+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 <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+
+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;
+}