Exemple #1
0
static bool nl_new(struct dionaea *d)
{
	g_debug("%s", __PRETTY_FUNCTION__);
	nl_runtime.sock = nl_socket_alloc();
	struct nl_sock  *sock = nl_runtime.sock;
	nl_socket_disable_seq_check(sock);
	nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, nl_event_input, NULL);
	nl_join_groups(sock, RTMGRP_LINK);
	int err;
	if ( (err = nl_connect(sock, NETLINK_ROUTE)) < 0) 
	{
		g_error("Could not connect netlink (%s)", nl_geterror(err));
	}

	nl_socket_add_membership(sock, RTNLGRP_LINK);
	nl_socket_add_membership(sock, RTNLGRP_NEIGH);
	nl_socket_add_membership(sock, RTNLGRP_IPV4_IFADDR);
	nl_socket_add_membership(sock, RTNLGRP_IPV6_IFADDR);

	if( (err=rtnl_neigh_alloc_cache(sock, &nl_runtime.neigh_cache)) != 0 )
	{
		g_error("Could not allocate neigh cache! (%s)", nl_geterror(err));
	}
#if LIBNL_RTNL_LINK_ALLOC_CACHE_ARGC == 3
	if( (err=rtnl_link_alloc_cache(sock, AF_UNSPEC, &nl_runtime.link_cache)) != 0 )
#elif LIBNL_RTNL_LINK_ALLOC_CACHE_ARGC == 2
	if( (err=rtnl_link_alloc_cache(sock, &nl_runtime.link_cache)) != 0 )
#endif
	{
		g_error("Could not allocate link cache! (%s)", nl_geterror(err));
	}

	if( (err=rtnl_addr_alloc_cache(sock, &nl_runtime.addr_cache)) != 0 )
	{
		g_error("Could not allocate addr cache! (%s)", nl_geterror(err));
	}

	nl_cache_mngt_provide(nl_runtime.neigh_cache);
	nl_cache_mngt_provide(nl_runtime.link_cache);
	nl_cache_mngt_provide(nl_runtime.addr_cache);
	
	nl_runtime.ihandler = ihandler_new("dionaea.connection.*.accept", nl_ihandler_cb, NULL);

	ev_io_init(&nl_runtime.io_in, nl_io_in_cb, nl_socket_get_fd(sock), EV_READ);
	ev_io_start(g_dionaea->loop, &nl_runtime.io_in);
	nl_runtime.link_addr_cache = g_hash_table_new(g_int_hash, g_int_equal);
	nl_cache_foreach(nl_runtime.link_cache, nl_obj_input, NULL);
	nl_cache_foreach(nl_runtime.addr_cache, nl_obj_input, NULL);
    return true;
}
/**
 * nm_netlink_foreach_route:
 * @ifindex: the interface index to filter routes for
 * @family: the address family to filter routes for
 * @scope: route scope, eg RT_SCOPE_LINK
 * @ignore_inet6_ll_mc: if %TRUE ignore IPv6 link-local and multi-cast routes
 * @callback: function called when a route matches the filter
 * @user_data: data passed to @callback
 *
 * Filters each route in the routing table against the given @ifindex and
 * @family (if given) and calls @callback for each matching route.
 *
 * Returns: a route if @callback returned one; the caller must dispose of the
 * route using rtnl_route_put() when it is no longer required.
 **/
struct rtnl_route *
nm_netlink_foreach_route (int ifindex,
                          int family,
                          int scope,
                          gboolean ignore_inet6_ll_mc,
                          NlRouteForeachFunc callback,
                          gpointer user_data)
{
	struct nl_cache *cache;
	ForeachRouteInfo info;

	memset (&info, 0, sizeof (info));
	info.ifindex = ifindex;
	info.family = family;
	info.scope = scope;
	info.ignore_inet6_ll_mc = ignore_inet6_ll_mc;
	info.callback = callback;
	info.user_data = user_data;
	info.iface = nm_netlink_index_to_iface (ifindex);

	rtnl_route_alloc_cache (nm_netlink_get_default_handle (), family, 0, &cache);
	g_warn_if_fail (cache != NULL);
	if (cache) {
		nl_cache_foreach (cache, foreach_route_cb, &info);
		nl_cache_free (cache);
	}
	g_free (info.iface);
	return info.out_route;
}
Exemple #3
0
int main(int argc, char *argv[])
{
	struct nl_cache *link_cache;

	sock = nl_cli_alloc_socket();
	nl_cli_connect(sock, NETLINK_ROUTE);
	link_cache = nl_cli_link_alloc_cache(sock);
	qdisc_cache = nl_cli_qdisc_alloc_cache(sock);

	params.dp_fd = stdout;

	for (;;) {
		int c, optidx = 0;
		static struct option long_opts[] = {
			{ "format", 1, 0, 'f' },
			{ "help", 0, 0, 'h' },
			{ "version", 0, 0, 'v' },
			{ 0, 0, 0, 0 }
		};

		c = getopt_long(argc, argv, "f:hv", long_opts, &optidx);
		if (c == -1)
			break;

		switch (c) {
		case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
		case 'h': print_usage(); break;
		case 'v': nl_cli_print_version(); break;
		}
	}

	nl_cache_foreach(link_cache, &print_link, NULL);

	return 0;
}
Exemple #4
0
bool TNlCgFilter::Exists(const TNlLink &link) {
    int ret;
    struct nl_cache *clsCache;

    ret = rtnl_cls_alloc_cache(link.GetSock(), link.GetIndex(), Parent, &clsCache);
    if (ret < 0) {
        L_ERR() << "Can't allocate filter cache: " << nl_geterror(ret) << std::endl;
        return false;
    }

    link.LogCache(clsCache);

    struct CgFilterIter {
        uint32_t parent;
        uint32_t handle;
        bool exists;
    } data = { Parent, Handle, false };
    nl_cache_foreach(clsCache, [](struct nl_object *obj, void *data) {
                     CgFilterIter *p = (CgFilterIter *)data;
                     if (rtnl_tc_get_handle(TC_CAST(obj)) == p->handle &&
                         rtnl_tc_get_parent(TC_CAST(obj)) == p->parent)
                         p->exists = true;
                     }, &data);

    nl_cache_free(clsCache);
    return data.exists;
}
int main(int argc, char *argv[])
{
	struct nl_cache *link_cache;
	int dev = 0;

	params.dp_fd = stdout;
	sock = nlt_alloc_socket();
	nlt_connect(sock, NETLINK_ROUTE);
	link_cache = nlt_alloc_link_cache(sock);
	cls = nlt_alloc_cls();

	for (;;) {
		int c, optidx = 0;
		enum {
			ARG_PROTO = 257,
			ARG_PRIO = 258,
			ARG_ID,
		};
		static struct option long_opts[] = {
			{ "format", 1, 0, 'f' },
			{ "help", 0, 0, 'h' },
			{ "version", 0, 0, 'v' },
			{ "dev", 1, 0, 'd' },
			{ "parent", 1, 0, 'p' },
			{ "proto", 1, 0, ARG_PROTO },
			{ "prio", 1, 0, ARG_PRIO },
			{ "id", 1, 0, ARG_ID },
			{ 0, 0, 0, 0 }
		};
	
		c = getopt_long(argc, argv, "+f:qhva:d:", long_opts, &optidx);
		if (c == -1)
			break;

		switch (c) {
		case '?': exit(NLE_INVAL);
		case 'f': params.dp_type = nlt_parse_dumptype(optarg); break;
		case 'h': print_usage(); break;
		case 'v': nlt_print_version(); break;
		case 'd': dev = 1; parse_dev(cls, link_cache, optarg); break;
		case 'p': parse_parent(cls, optarg); break;
		case ARG_PRIO: parse_prio(cls, optarg); break;
		case ARG_ID: parse_handle(cls, optarg); break;
		case ARG_PROTO: parse_proto(cls, optarg); break;
		}
 	}

	if (!dev)
		nl_cache_foreach(link_cache, print_cls, NULL);
	else
		print_cls(NULL, NULL);

	return 0;
}
int main(int argc, char *argv[])
{
	struct nl_handle *nlh;
	struct nl_cache *link_cache;
	int err = 1;

	if (nltool_init(argc, argv) < 0)
		return -1;

	if (argc < 3 || !strcmp(argv[1], "-h"))
		print_usage();

	nlh = nltool_alloc_handle();
	if (!nlh)
		return -1;

	if (nltool_connect(nlh, NETLINK_ROUTE) < 0)
		goto errout;

	link_cache = nltool_alloc_link_cache(nlh);
	if (!link_cache)
		goto errout_close;

	gargv = &argv[2];
	gargc = argc - 2;

	if (!strcasecmp(argv[1], "all"))
		nl_cache_foreach(link_cache, dump_stats, NULL);
	else {
		int ifindex = strtoul(argv[1], NULL, 0);
		struct rtnl_link *link = rtnl_link_get(link_cache, ifindex);

		if (!link) {
			fprintf(stderr, "Could not find ifindex %d\n", ifindex);
			goto errout_link_cache;
		}

		dump_stats((struct nl_object *) link, NULL);
		rtnl_link_put(link);
	}

	err = 0;
errout_link_cache:
	nl_cache_free(link_cache);
errout_close:
	nl_close(nlh);
errout:
	nl_handle_destroy(nlh);
	return err;
}
void dabbad_interface_statistics_get(Dabba__DabbaService_Service * service,
                                     const Dabba__InterfaceIdList * id_list,
                                     Dabba__InterfaceStatisticsList_Closure
                                     closure, void *closure_data)
{
    Dabba__InterfaceStatisticsList statistics_list =
        DABBA__INTERFACE_STATISTICS_LIST__INIT;
    Dabba__InterfaceStatisticsList *statistics_listp = NULL;
    struct nl_sock *sock = NULL;
    struct nl_cache *cache;
    struct rtnl_link *link;
    size_t a;

    assert(service);
    assert(closure_data);

    cache = link_cache_alloc(&sock);
    link = rtnl_link_alloc();

    if (!link || !cache)
        goto out;

    if (id_list->n_list) {
        for (a = 0; a < id_list->n_list; a++) {
            rtnl_link_set_name(link, id_list->list[a]->name);
            nl_cache_foreach_filter(cache, OBJ_CAST(link),
                                    __interface_statistics_get,
                                    &statistics_list);
        }
    } else
        nl_cache_foreach(cache, __interface_statistics_get,
                         &statistics_list);

    statistics_listp = &statistics_list;

out:
    closure(statistics_listp, closure_data);
    for (a = 0; a < statistics_list.n_list; a++) {
        free(statistics_list.list[a]->status);
        free(statistics_list.list[a]->id);
        free(statistics_list.list[a]);
    }
    free(statistics_list.list);
    link_destroy(link);
    link_cache_destroy(sock, cache);
}
Exemple #8
0
int main(int argc, char *argv[])
{
	struct nl_cache *link_cache;

	if (nltool_init(argc, argv) < 0)
		return -1;

	dump_params.dp_fd = stdout;

	if (argc > 1) {
		if (!strcasecmp(argv[1], "brief"))
			dump_params.dp_type = NL_DUMP_BRIEF;
		else if (!strcasecmp(argv[1], "full"))
			dump_params.dp_type = NL_DUMP_FULL;
		else if (!strcasecmp(argv[1], "stats"))
			dump_params.dp_type = NL_DUMP_STATS;
	}

	nl_handle = nl_handle_alloc_nondefault(nltool_cbset);
	if (!nl_handle)
		return 1;

	if (nltool_connect(nl_handle, NETLINK_ROUTE) < 0)
		return 1;

	link_cache = nltool_alloc_link_cache(nl_handle);
	if (!link_cache)
		return 1;

	qdisc_cache = nltool_alloc_qdisc_cache(nl_handle);
	if (!qdisc_cache)
		return 1;

	nl_cache_foreach(link_cache, &print_link, NULL);

	nl_cache_free(qdisc_cache);
	nl_cache_free(link_cache);

	nl_close(nl_handle);
	nl_handle_destroy(nl_handle);
	return 0;
}
/**
 * nm_netlink_find_address:
 * @ifindex: interface index
 * @family: address family, either AF_INET or AF_INET6
 * @addr: binary address, either struct in_addr* or struct in6_addr*
 * @prefix: prefix length
 *
 * Searches for a matching address on the given interface.
 *
 * Returns: %TRUE if the given address was found on the interface, %FALSE if it
 * was not found or an error occurred.
 **/
gboolean
nm_netlink_find_address (int ifindex,
                         int family,
                         void *addr,  /* struct in_addr or struct in6_addr */
                         int prefix)
{
	struct nl_sock *nlh = NULL;
	struct nl_cache *cache = NULL;
	FindAddrInfo info;

	g_return_val_if_fail (ifindex > 0, FALSE);
	g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE);
	g_return_val_if_fail (addr != NULL, FALSE);
	g_return_val_if_fail (prefix >= 0, FALSE);

	memset (&info, 0, sizeof (info));
	info.ifindex = ifindex;
	info.family = family;
	info.prefix = prefix;
	info.addr = addr;
	if (family == AF_INET)
		info.addrlen = sizeof (struct in_addr);
	else if (family == AF_INET6)
		info.addrlen = sizeof (struct in6_addr);
	else
		g_assert_not_reached ();

	nlh = nm_netlink_get_default_handle ();
	if (nlh) {
		rtnl_addr_alloc_cache(nlh, &cache);
		if (cache) {
			nl_cache_mngt_provide (cache);
			nl_cache_foreach (cache, find_one_address, &info);
			nl_cache_free (cache);
		}
	}
	return info.found;
}
int main(int argc, char *argv[])
{
	struct rtnl_cls *cls;
	struct rtnl_tc *tc;
	struct nl_cache *link_cache;
	int ifindex;
 
	sock = nl_cli_alloc_socket();
	nl_cli_connect(sock, NETLINK_ROUTE);
	link_cache = nl_cli_link_alloc_cache(sock);
 	cls = nl_cli_cls_alloc();
	tc = (struct rtnl_tc *) cls;
 
	for (;;) {
		int c, optidx = 0;
		enum {
			ARG_YES = 257,
			ARG_INTERACTIVE = 258,
			ARG_PROTO,
			ARG_PRIO,
		};
		static struct option long_opts[] = {
			{ "interactive", 0, 0, ARG_INTERACTIVE },
			{ "yes", 0, 0, ARG_YES },
			{ "quiet", 0, 0, 'q' },
			{ "help", 0, 0, 'h' },
			{ "version", 0, 0, 'v' },
			{ "dev", 1, 0, 'd' },
			{ "parent", 1, 0, 'p' },
			{ "id", 1, 0, 'i' },
			{ "kind", 1, 0, 'k' },
			{ "proto", 1, 0, ARG_PROTO },
			{ "prio", 1, 0, ARG_PRIO },
			{ 0, 0, 0, 0 }
		};
	
		c = getopt_long(argc, argv, "qhvd:p:i:k:", long_opts, &optidx);
		if (c == -1)
			break;

		switch (c) {
		case '?': nl_cli_fatal(EINVAL, "Invalid options");
		case ARG_INTERACTIVE: interactive = 1; break;
		case ARG_YES: default_yes = 1; break;
		case 'q': quiet = 1; break;
		case 'h': print_usage(); break;
		case 'v': nl_cli_print_version(); break;
		case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break;
		case 'p': nl_cli_tc_parse_parent(tc, optarg); break;
		case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break;
		case 'k': nl_cli_tc_parse_kind(tc, optarg); break;
		case ARG_PROTO: nl_cli_cls_parse_proto(cls, optarg); break;
		case ARG_PRIO:
			rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg));
			break;
		}
 	}

	if ((ifindex = rtnl_tc_get_ifindex(tc)))
		__delete_link(ifindex, cls);
	 else
		nl_cache_foreach(link_cache, delete_link, cls);

	if (!quiet)
		printf("Deleted %d classs\n", deleted);

	return 0;
}
Exemple #11
0
static void handle_class(struct nl_object *obj, void *arg)
{
	struct rtnl_tc *tc = (struct rtnl_tc *) obj;
	struct element *e;
	struct rdata *rdata = arg;
	struct rdata ndata = {
		.level = rdata->level + 1,
	};

	if (!(e = handle_tc_obj(tc, "class", rdata)))
		return;

	ndata.parent = e;

	if (!strcmp(rtnl_tc_get_kind(tc), "htb"))
		element_set_txmax(e, rtnl_htb_get_rate((struct rtnl_class *) tc));

	find_classes(rtnl_tc_get_handle(tc), &ndata);
	find_qdiscs(rtnl_tc_get_handle(tc), &ndata);
}

static void find_qdiscs(uint32_t parent, struct rdata *rdata)
{
	struct rtnl_qdisc *filter;

	if (!(filter = rtnl_qdisc_alloc()))
		return;

	rtnl_tc_set_parent((struct rtnl_tc *) filter, parent);

	nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(filter),
				handle_qdisc, rdata);

	rtnl_qdisc_put(filter);
}

static void find_cls(int ifindex, uint32_t parent, struct rdata *rdata)
{
	struct nl_cache *cls_cache;

	if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0)
		return;

	nl_cache_foreach(cls_cache, handle_cls, rdata);

	nl_cache_free(cls_cache);
}

static void find_classes(uint32_t parent, struct rdata *rdata)
{
	struct rtnl_class *filter;

	if (!(filter = rtnl_class_alloc()))
		return;

	rtnl_tc_set_parent((struct rtnl_tc *) filter, parent);

	nl_cache_foreach_filter(class_cache, OBJ_CAST(filter),
				handle_class, rdata);

	rtnl_class_put(filter);
}

static void handle_qdisc(struct nl_object *obj, void *arg)
{
	struct rtnl_tc *tc = (struct rtnl_tc *) obj;
	struct element *e;
	struct rdata *rdata = arg;
	struct rdata ndata = {
		.level = rdata->level + 1,
	};

	if (!(e = handle_tc_obj(tc, "qdisc", rdata)))
		return;

	ndata.parent = e;

	find_cls(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc), &ndata);

	if (rtnl_tc_get_parent(tc) == TC_H_ROOT) {
		find_cls(rtnl_tc_get_ifindex(tc), TC_H_ROOT, &ndata);
		find_classes(TC_H_ROOT, &ndata);
	}

	find_classes(rtnl_tc_get_handle(tc), &ndata);
}

static void handle_tc(struct element *e, struct rtnl_link *link)
{
	struct rtnl_qdisc *qdisc;
	int ifindex = rtnl_link_get_ifindex(link);
	struct rdata rdata = {
		.level = 1,
		.parent = e,
	};

	if (rtnl_class_alloc_cache(sock, ifindex, &class_cache) < 0)
		return;

	qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_ROOT);
	if (qdisc) {
		handle_qdisc(OBJ_CAST(qdisc), &rdata);
		rtnl_qdisc_put(qdisc);
	}

	qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, 0);
	if (qdisc) {
		handle_qdisc(OBJ_CAST(qdisc), &rdata);
		rtnl_qdisc_put(qdisc);
	}

	qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_INGRESS);
	if (qdisc) {
		handle_qdisc(OBJ_CAST(qdisc), &rdata);
		rtnl_qdisc_put(qdisc);
	}

	nl_cache_free(class_cache);
}

static void update_link_infos(struct element *e, struct rtnl_link *link)
{
	char buf[64];

	snprintf(buf, sizeof(buf), "%u", rtnl_link_get_mtu(link));
	element_update_info(e, "MTU", buf);

	rtnl_link_flags2str(rtnl_link_get_flags(link), buf, sizeof(buf));
	element_update_info(e, "Flags", buf);

	rtnl_link_operstate2str(rtnl_link_get_operstate(link),
				buf, sizeof(buf));
	element_update_info(e, "Operstate", buf);

	snprintf(buf, sizeof(buf), "%u", rtnl_link_get_ifindex(link));
	element_update_info(e, "IfIndex", buf);

	nl_addr2str(rtnl_link_get_addr(link), buf, sizeof(buf));
	element_update_info(e, "Address", buf);

	nl_addr2str(rtnl_link_get_broadcast(link), buf, sizeof(buf));
	element_update_info(e, "Broadcast", buf);

	rtnl_link_mode2str(rtnl_link_get_linkmode(link),
			   buf, sizeof(buf));
	element_update_info(e, "Mode", buf);

	snprintf(buf, sizeof(buf), "%u", rtnl_link_get_txqlen(link));
	element_update_info(e, "TXQlen", buf);

	nl_af2str(rtnl_link_get_family(link), buf, sizeof(buf));
	element_update_info(e, "Family", buf);

	element_update_info(e, "Alias",
		rtnl_link_get_ifalias(link) ? : "");

	element_update_info(e, "Qdisc",
		rtnl_link_get_qdisc(link) ? : "");

	if (rtnl_link_get_link(link)) {
		snprintf(buf, sizeof(buf), "%u", rtnl_link_get_link(link));
		element_update_info(e, "SlaveOfIndex", buf);
	}
}

static void do_link(struct nl_object *obj, void *arg)
{
	struct rtnl_link *link = (struct rtnl_link *) obj;
	struct element *e, *e_parent = NULL;
	int i, master_ifindex;

	if (!cfg_show_all && !(rtnl_link_get_flags(link) & IFF_UP)) {
		/* FIXME: delete element */
		return;
	}

	/* Check if the interface is a slave of another interface */
	if ((master_ifindex = rtnl_link_get_link(link))) {
		char parent[IFNAMSIZ+1];

		rtnl_link_i2name(link_cache, master_ifindex,
				 parent, sizeof(parent));

		e_parent = element_lookup(grp, parent, master_ifindex, NULL, 0);
	}

	if (!(e = element_lookup(grp, rtnl_link_get_name(link),
				 rtnl_link_get_ifindex(link), e_parent, ELEMENT_CREAT)))
		return;

	if (e->e_flags & ELEMENT_FLAG_CREATED) {
		if (e->e_parent)
			e->e_level = e->e_parent->e_level + 1;

		if (element_set_key_attr(e, "bytes", "packets") ||
		    element_set_usage_attr(e, "bytes"))
			BUG();

		/* FIXME: Update link infos every 1s or so */
		update_link_infos(e, link);

		e->e_flags &= ~ELEMENT_FLAG_CREATED;
	}

	for (i = 0; i < ARRAY_SIZE(link_attrs); i++) {
		struct attr_map *m = &link_attrs[i];
		uint64_t c_rx = 0, c_tx = 0;
		int flags = 0;

		if (m->rxid >= 0) {
			c_rx = rtnl_link_get_stat(link, m->rxid);
			flags |= UPDATE_FLAG_RX;
		}

		if (m->txid >= 0) {
			c_tx = rtnl_link_get_stat(link, m->txid);
			flags |= UPDATE_FLAG_TX;
		}

		attr_update(e, m->attrid, c_rx, c_tx, flags);
	}

	if (!c_notc)
		handle_tc(e, link);

	element_notify_update(e, NULL);
	element_lifesign(e, 1);
}

static void netlink_read(void)
{
	int err;

	if ((err = nl_cache_resync(sock, link_cache, NULL, NULL)) < 0) {
		fprintf(stderr, "Unable to resync link cache: %s\n", nl_geterror(err));
		goto disable;
	}

	if ((err = nl_cache_resync(sock, qdisc_cache, NULL, NULL)) < 0) {
		fprintf(stderr, "Unable to resync qdisc cache: %s\n", nl_geterror(err));
		goto disable;
	}

	nl_cache_foreach(link_cache, do_link, NULL);

	return;

disable:
	netlink_ops.m_flags &= ~BMON_MODULE_ENABLED;
}

static void netlink_shutdown(void)
{
	nl_cache_free(link_cache);
	nl_cache_free(qdisc_cache);
	nl_socket_free(sock);
}
Exemple #12
0
int main(int argc, char *argv[])
{
	struct rtnl_cls *cls;
	struct rtnl_tc *tc;
	struct nl_cache *link_cache;
	int ifindex;

	sock = nl_cli_alloc_socket();
	nl_cli_connect(sock, NETLINK_ROUTE);
	link_cache = nl_cli_link_alloc_cache(sock);
	cls = nl_cli_cls_alloc();
	tc = (struct rtnl_tc *) cls;

	params.dp_fd = stdout;

	for (;;) {
		int c, optidx = 0;
		enum {
			ARG_DETAILS = 257,
			ARG_STATS = 258,
			ARG_PROTO,
			ARG_PRIO,
		};
		static struct option long_opts[] = {
			{ "details", 0, 0, ARG_DETAILS },
			{ "stats", 0, 0, ARG_STATS },
			{ "help", 0, 0, 'h' },
			{ "version", 0, 0, 'v' },
			{ "dev", 1, 0, 'd' },
			{ "parent", 1, 0, 'p' },
			{ "id", 1, 0, 'i' },
			{ "kind", 1, 0, 'k' },
			{ "proto", 1, 0, ARG_PROTO },
			{ "prio", 1, 0, ARG_PRIO },
			{ 0, 0, 0, 0 }
		};

		c = getopt_long(argc, argv, "hvd:p:i:k:", long_opts, &optidx);
		if (c == -1)
			break;

		switch (c) {
		case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break;
		case ARG_STATS: params.dp_type = NL_DUMP_STATS; break;
		case 'h': print_usage(); break;
		case 'v': nl_cli_print_version(); break;
		case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break;
		case 'p': nl_cli_tc_parse_parent(tc, optarg); break;
		case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break;
		case 'k': nl_cli_tc_parse_kind(tc, optarg); break;
		case ARG_PROTO: nl_cli_cls_parse_proto(cls, optarg); break;
		case ARG_PRIO:
			rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg));
			break;
		}
	}

	if ((ifindex = rtnl_tc_get_ifindex(tc)))
		__dump_link(ifindex, cls);
	else
		nl_cache_foreach(link_cache, dump_link, cls);

	return 0;
}