nl-qdisc-add tool

Adds a cli based tool to add/update/replace qdiscs. This tool requires
each qdisc to be supported via a dynamic loadable module in
pkglibdir/cli/qdisc/$name.so.

So far HTB and blackhole have been implemented.

Syntax:
nl-qdisc-add --dev eth2 --parent root --id 1: htb --r2q=5
nl-qdisc-add --update-only --dev eth2 --id 1: htb --r2q=10
This commit is contained in:
Thomas Graf 2010-10-19 13:06:42 +02:00
parent 3229b32e39
commit c0cd587dfc
9 changed files with 400 additions and 3 deletions

View file

@ -13,11 +13,24 @@
#define __NETLINK_CLI_QDISC_H_ #define __NETLINK_CLI_QDISC_H_
#include <netlink/route/qdisc.h> #include <netlink/route/qdisc.h>
#include <netlink/route/qdisc-modules.h>
#define nl_cli_qdisc_alloc_cache(sk) \ #define nl_cli_qdisc_alloc_cache(sk) \
nl_cli_alloc_cache((sk), "queueing disciplines", \ nl_cli_alloc_cache((sk), "queueing disciplines", \
rtnl_qdisc_alloc_cache) rtnl_qdisc_alloc_cache)
struct nl_cli_qdisc_module
{
const char * qm_name;
struct rtnl_qdisc_ops * qm_ops;
void (*qm_parse_argv)(struct rtnl_qdisc *, int, char **);
struct nl_list_head qm_list;
};
extern struct nl_cli_qdisc_module *nl_cli_qdisc_lookup(struct rtnl_qdisc_ops *);
extern void nl_cli_qdisc_register(struct nl_cli_qdisc_module *);
extern void nl_cli_qdisc_unregister(struct nl_cli_qdisc_module *);
extern struct rtnl_qdisc *nl_cli_qdisc_alloc(void); extern struct rtnl_qdisc *nl_cli_qdisc_alloc(void);
extern void nl_cli_qdisc_parse_dev(struct rtnl_qdisc *, struct nl_cache *, char *); extern void nl_cli_qdisc_parse_dev(struct rtnl_qdisc *, struct nl_cache *, char *);

View file

@ -73,6 +73,8 @@ extern int nl_cli_confirm(struct nl_object *,
extern struct nl_cache *nl_cli_alloc_cache(struct nl_sock *, const char *, extern struct nl_cache *nl_cli_alloc_cache(struct nl_sock *, const char *,
int (*ac)(struct nl_sock *, struct nl_cache **)); int (*ac)(struct nl_sock *, struct nl_cache **));
extern void nl_cli_load_module(const char *, const char *);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -52,3 +52,13 @@ libnl_route_la_SOURCES = \
fib_lookup/lookup.c fib_lookup/request.c \ fib_lookup/lookup.c fib_lookup/request.c \
\ \
route/pktloc_syntax.c route/pktloc_grammar.c route/pktloc.c route/pktloc_syntax.c route/pktloc_grammar.c route/pktloc.c
if ENABLE_CLI
nobase_pkglib_LTLIBRARIES = \
cli/qdisc/htb.la \
cli/qdisc/blackhole.la
cli_qdisc_htb_la_LDFLAGS = -module -version-info 0:0:0
cli_qdisc_blackhole_la_LDFLAGS = -module -version-info 0:0:0
endif

63
lib/cli/qdisc/blackhole.c Normal file
View file

@ -0,0 +1,63 @@
/*
* src/lib/blackhole.c Blackhole module for CLI lib
*
* 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>
#include <netlink/cli/qdisc.h>
static void print_usage(void)
{
printf(
"Usage: nl-qdisc-add [...] blackhole [OPTIONS]...\n"
"\n"
"OPTIONS\n"
" --help Show this help text.\n"
"\n"
"EXAMPLE"
" # Drop all outgoing packets on eth1\n"
" nl-qdisc-add --dev=eth1 --parent=root blackhole\n");
}
static void blackhole_parse_argv(struct rtnl_qdisc *qdisc, int argc, char **argv)
{
for (;;) {
int c, optidx = 0;
static struct option long_opts[] = {
{ "help", 0, 0, 'h' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "h", long_opts, &optidx);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage();
return;
}
}
}
static struct nl_cli_qdisc_module blackhole_module =
{
.qm_name = "blackhole",
.qm_parse_argv = blackhole_parse_argv,
};
static void __init blackhole_init(void)
{
nl_cli_qdisc_register(&blackhole_module);
}
static void __exit blackhole_exit(void)
{
nl_cli_qdisc_unregister(&blackhole_module);
}

79
lib/cli/qdisc/htb.c Normal file
View file

@ -0,0 +1,79 @@
/*
* src/lib/htb.c HTB module for CLI lib
*
* 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>
#include <netlink/cli/qdisc.h>
static void print_usage(void)
{
printf(
"Usage: nl-qdisc-add [...] htb [OPTIONS]...\n"
"\n"
"OPTIONS\n"
" --help Show this help text.\n"
" --r2q=DIV Rate to quantum divisor (default: 10)\n"
" --default=ID Default class for unclassified traffic.\n"
"\n"
"EXAMPLE"
" # Create htb root qdisc 1: and direct unclassified traffic to class 1:10\n"
" nl-qdisc-add --dev=eth1 --parent=root --handle=1: htb --default=10\n");
}
static void htb_parse_argv(struct rtnl_qdisc *qdisc, int argc, char **argv)
{
for (;;) {
int c, optidx = 0;
enum {
ARG_R2Q = 257,
ARG_DEFAULT = 258,
};
static struct option long_opts[] = {
{ "help", 0, 0, 'h' },
{ "r2q", 1, 0, ARG_R2Q },
{ "default", 1, 0, ARG_DEFAULT },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "hv", long_opts, &optidx);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage();
return;
case ARG_R2Q:
rtnl_htb_set_rate2quantum(qdisc, nl_cli_parse_u32(optarg));
break;
case ARG_DEFAULT:
rtnl_htb_set_defcls(qdisc, nl_cli_parse_u32(optarg));
break;
}
}
}
static struct nl_cli_qdisc_module htb_module =
{
.qm_name = "htb",
.qm_parse_argv = htb_parse_argv,
};
static void __init htb_init(void)
{
nl_cli_qdisc_register(&htb_module);
}
static void __exit htb_exit(void)
{
nl_cli_qdisc_unregister(&htb_module);
}

View file

@ -5,6 +5,9 @@ SUBDIRS = lib
AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE
AM_LDFLAGS = -L${top_builddir}/lib -L${top_builddir}/src/lib -lnl-cli AM_LDFLAGS = -L${top_builddir}/lib -L${top_builddir}/src/lib -lnl-cli
sbin_PROGRAMS = \
nl-qdisc-add
noinst_PROGRAMS = \ noinst_PROGRAMS = \
genl-ctrl-list \ genl-ctrl-list \
nf-ct-list nf-log nf-queue nf-monitor \ nf-ct-list nf-log nf-queue nf-monitor \
@ -66,6 +69,8 @@ nl_neigh_list_LDADD = -lnl-route
nl_neightbl_list_SOURCES = nl-neightbl-list.c nl_neightbl_list_SOURCES = nl-neightbl-list.c
nl_neightbl_list_LDADD = -lnl-route nl_neightbl_list_LDADD = -lnl-route
nl_qdisc_add_SOURCES = nl-qdisc-add.c
nl_qdisc_add_LDADD = -lnl-route
nl_qdisc_delete_SOURCES = nl-qdisc-delete.c nl_qdisc_delete_SOURCES = nl-qdisc-delete.c
nl_qdisc_delete_LDADD = -lnl-route nl_qdisc_delete_LDADD = -lnl-route
nl_qdisc_list_SOURCES = nl-qdisc-list.c nl_qdisc_list_SOURCES = nl-qdisc-list.c

View file

@ -6,13 +6,12 @@
* License as published by the Free Software Foundation version 2.1 * License as published by the Free Software Foundation version 2.1
* of the License. * of the License.
* *
* Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch> * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
*/ */
/** /**
* @ingroup cli * @ingroup cli
* @defgroup cli_qdisc Queueing Disciplines * @defgroup cli_qdisc Queueing Disciplines
*
* @{ * @{
*/ */
@ -69,4 +68,58 @@ void nl_cli_qdisc_parse_kind(struct rtnl_qdisc *qdisc, char *arg)
rtnl_qdisc_set_kind(qdisc, arg); rtnl_qdisc_set_kind(qdisc, arg);
} }
static NL_LIST_HEAD(qdisc_modules);
struct nl_cli_qdisc_module *__nl_cli_qdisc_lookup(struct rtnl_qdisc_ops *ops)
{
struct nl_cli_qdisc_module *qm;
nl_list_for_each_entry(qm, &qdisc_modules, qm_list)
if (qm->qm_ops == ops)
return qm;
return NULL;
}
struct nl_cli_qdisc_module *nl_cli_qdisc_lookup(struct rtnl_qdisc_ops *ops)
{
struct nl_cli_qdisc_module *qm;
if ((qm = __nl_cli_qdisc_lookup(ops)))
return qm;
nl_cli_load_module("cli/qdisc", ops->qo_kind);
if (!(qm = __nl_cli_qdisc_lookup(ops))) {
nl_cli_fatal(EINVAL, "Application bug: The shared library for "
"the qdisc \"%s\" was successfully loaded but it "
"seems that module did not register itself");
}
return qm;
}
void nl_cli_qdisc_register(struct nl_cli_qdisc_module *qm)
{
struct rtnl_qdisc_ops *ops;
if (!(ops = __rtnl_qdisc_lookup_ops(qm->qm_name))) {
nl_cli_fatal(ENOENT, "Unable to register CLI qdisc module "
"\"%s\": No matching libnl qdisc module found.", qm->qm_name);
}
if (__nl_cli_qdisc_lookup(ops)) {
nl_cli_fatal(EEXIST, "Unable to register CLI qdisc module "
"\"%s\": Module already registered.", qm->qm_name);
}
qm->qm_ops = ops;
nl_list_add_tail(&qm->qm_list, &qdisc_modules);
}
void nl_cli_qdisc_unregister(struct nl_cli_qdisc_module *qm)
{
nl_list_del(&qm->qm_list);
}
/** @} */ /** @} */

View file

@ -13,10 +13,25 @@
* @defgroup cli Command Line Interface API * @defgroup cli Command Line Interface API
* *
* @{ * @{
*
* These modules provide an interface for text based applications. The
* functions provided are wrappers for their libnl equivalent with
* added error handling. The functions check for allocation failures,
* invalid input, and unknown types and will print error messages
* accordingly via nl_cli_fatal().
*/ */
#include <netlink/cli/utils.h> #include <netlink/cli/utils.h>
/**
* Parse a text based 32 bit unsigned integer argument
* @arg arg Integer in text form.
*
* Tries to convert the number provided in arg to a uint32_t. Will call
* nl_cli_fatal() if the conversion fails.
*
* @return 32bit unsigned integer.
*/
uint32_t nl_cli_parse_u32(const char *arg) uint32_t nl_cli_parse_u32(const char *arg)
{ {
unsigned long lval; unsigned long lval;
@ -34,7 +49,7 @@ void nl_cli_print_version(void)
{ {
printf("libnl tools version %s\n", LIBNL_VERSION); printf("libnl tools version %s\n", LIBNL_VERSION);
printf( printf(
"Copyright (C) 2003-2009 Thomas Graf <tgraf@redhat.com>\n" "Copyright (C) 2003-2010 Thomas Graf <tgraf@redhat.com>\n"
"\n" "\n"
"This program comes with ABSOLUTELY NO WARRANTY. This is free \n" "This program comes with ABSOLUTELY NO WARRANTY. This is free \n"
"software, and you are welcome to redistribute it under certain\n" "software, and you are welcome to redistribute it under certain\n"
@ -44,6 +59,14 @@ void nl_cli_print_version(void)
exit(0); exit(0);
} }
/**
* Print error message and quit application
* @arg err Error code.
* @arg fmt Error message.
*
* Prints the formatted error message to stderr and quits the application
* using the provided error code.
*/
void nl_cli_fatal(int err, const char *fmt, ...) void nl_cli_fatal(int err, const char *fmt, ...)
{ {
va_list ap; va_list ap;
@ -144,4 +167,17 @@ struct nl_cache *nl_cli_alloc_cache(struct nl_sock *sock, const char *name,
return cache; return cache;
} }
void nl_cli_load_module(const char *prefix, const char *name)
{
char path[FILENAME_MAX+1];
void *handle;
snprintf(path, sizeof(path), "%s/%s/%s.so",
PKGLIBDIR, prefix, name);
if (!(handle = dlopen(path, RTLD_NOW)))
nl_cli_fatal(ENOENT, "Unable to load module \"%s\": %s\n",
path, dlerror());
}
/** @} */ /** @} */

136
src/nl-qdisc-add.c Normal file
View file

@ -0,0 +1,136 @@
/*
* src/nl-qdisc-add.c Add Queueing Discipline
*
* 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>
#include <netlink/cli/qdisc.h>
#include <netlink/cli/link.h>
static int quiet = 0;
static void print_usage(void)
{
printf(
"Usage: nl-qdisc-add [OPTIONS]... QDISC [CONFIGURATION]...\n"
"\n"
"OPTIONS\n"
" -q, --quiet Do not print informal notifications.\n"
" -h, --help Show this help text.\n"
" -v, --version Show versioning information.\n"
" --update Update qdisc if it exists.\n"
" --replace Replace or update qdisc if it exists.\n"
" --update-only Only update qdisc, never create it.\n"
" --replace-only Only replace or update qdisc, never create it.\n"
" -d, --dev=DEV Network device the qdisc should be attached to.\n"
" -i, --id=ID ID of new qdisc (default: auto-generated)r\n"
" -p, --parent=ID ID of parent { root | ingress | QDISC-ID }\n"
"\n"
"CONFIGURATION\n"
" -h, --help Show help text of qdisc specific options.\n"
"\n"
"EXAMPLE\n"
" $ nl-qdisc-add --dev=eth1 --parent=root htb --rate=100mbit\n"
"\n"
);
exit(0);
}
int main(int argc, char *argv[])
{
struct nl_sock *sock;
struct rtnl_qdisc *qdisc;
struct nl_cache *link_cache;
struct nl_dump_params dp = {
.dp_type = NL_DUMP_DETAILS,
.dp_fd = stdout,
};
struct nl_cli_qdisc_module *qm;
struct rtnl_qdisc_ops *ops;
int err, flags = NLM_F_CREATE | NLM_F_EXCL;
char *kind;
sock = nl_cli_alloc_socket();
nl_cli_connect(sock, NETLINK_ROUTE);
link_cache = nl_cli_link_alloc_cache(sock);
qdisc = nl_cli_qdisc_alloc();
for (;;) {
int c, optidx = 0;
enum {
ARG_REPLACE = 257,
ARG_UPDATE = 258,
ARG_REPLACE_ONLY,
ARG_UPDATE_ONLY,
};
static struct option long_opts[] = {
{ "quiet", 0, 0, 'q' },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ "dev", 1, 0, 'd' },
{ "parent", 1, 0, 'p' },
{ "id", 1, 0, 'i' },
{ "replace", 0, 0, ARG_REPLACE },
{ "update", 0, 0, ARG_UPDATE },
{ "replace-only", 0, 0, ARG_REPLACE_ONLY },
{ "update-only", 0, 0, ARG_UPDATE_ONLY },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "+qhvd:p:i:",
long_opts, &optidx);
if (c == -1)
break;
switch (c) {
case 'q': quiet = 1; break;
case 'h': print_usage(); break;
case 'v': nl_cli_print_version(); break;
case 'd': nl_cli_qdisc_parse_dev(qdisc, link_cache, optarg); break;
case 'p': nl_cli_qdisc_parse_parent(qdisc, optarg); break;
case 'i': nl_cli_qdisc_parse_handle(qdisc, optarg); break;
case ARG_UPDATE: flags = NLM_F_CREATE; break;
case ARG_REPLACE: flags = NLM_F_CREATE | NLM_F_REPLACE; break;
case ARG_UPDATE_ONLY: flags = 0; break;
case ARG_REPLACE_ONLY: flags = NLM_F_REPLACE; break;
}
}
if (optind >= argc)
print_usage();
if (!rtnl_qdisc_get_ifindex(qdisc))
nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)");
if (!rtnl_qdisc_get_parent(qdisc))
nl_cli_fatal(EINVAL, "You must specify a parent");
kind = argv[optind++];
rtnl_qdisc_set_kind(qdisc, kind);
if (!(ops = rtnl_qdisc_lookup_ops(qdisc)))
nl_cli_fatal(ENOENT, "Unknown qdisc \"%s\"", kind);
if (!(qm = nl_cli_qdisc_lookup(ops)))
nl_cli_fatal(ENOTSUP, "Qdisc type \"%s\" not supported.", kind);
qm->qm_parse_argv(qdisc, argc, argv);
if (!quiet) {
printf("Adding ");
nl_object_dump(OBJ_CAST(qdisc), &dp);
}
if ((err = rtnl_qdisc_add(sock, qdisc, flags)) < 0)
nl_cli_fatal(EINVAL, "Unable to add qdisc: %s", nl_geterror(err));
return 0;
}