public inbox for b.a.t.m.a.n@lists.open-mesh.org
 help / color / mirror / Atom feed
From: Matthias Schiffer <mschiffer@universe-factory.net>
To: b.a.t.m.a.n@lists.open-mesh.org
Subject: Re: [B.A.T.M.A.N.] [RFC] batman-adv: add generic netlink query API to replace debugfs files
Date: Wed, 24 Jun 2015 21:00:30 +0200	[thread overview]
Message-ID: <558AFE4E.7040107@universe-factory.net> (raw)
In-Reply-To: <2ce4491cf3eb01922d0fec97147a1533cca30c41.1435170868.git.mschiffer@universe-factory.net>


[-- Attachment #1.1: Type: text/plain, Size: 368 bytes --]

This is the mentioned userspace tool.
Build with:

cc -o batnl batnl.c $(pkg-config --cflags --libs libnl-1) -Wall

It uses the outdated libnl-1 instead of the current libnl-3 API to be
compatible with OpenWrt's libnl-tiny.

It is only a quick-and-dirty example for the usage of the GENL API, so
it doesn't check for a lot of errors...

Regards,
Matthias

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: batnl.c --]
[-- Type: text/x-csrc; name="batnl.c", Size: 10341 bytes --]

#include <stdio.h>
#include <string.h>

#include <net/if.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "batman_adv.h"


static void usage()
{
	fprintf(stderr, "Usage:\n\n");
	fprintf(stderr, "  batnl <mesh> info\n");
	fprintf(stderr, "  batnl <mesh> translocal\n");
	fprintf(stderr, "  batnl <mesh> transglobal\n");
	fprintf(stderr, "  batnl <mesh> originators [<outgoing>]\n");
	exit(2);
}

static int print_error(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg)
{
	fprintf(stderr, "Error received: %s\n", strerror(-nlerr->error));
	exit(1);
}

static int info_callback(struct nl_msg *msg, void *arg)
{
	struct nlmsghdr *nlh = nlmsg_hdr(msg);
	struct genlmsghdr *ghdr;
	struct nlattr *attrs[BATADV_ATTR_MAX+1];

	if (!genlmsg_valid_hdr(nlh, 0)) {
		fputs("Received invalid data from kernel.", stderr);
		exit(1);
	}

	ghdr = nlmsg_data(nlh);

	if (ghdr->cmd != BATADV_CMD_GET_MESH_INFO)
		return NL_OK;

	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), NULL)) {
		fputs("Received invalid data from kernel.", stderr);
		exit(1);
	}

	const char *mesh_name = nla_get_string(attrs[BATADV_ATTR_MESH_IFNAME]);

	if (attrs[BATADV_ATTR_PRIMARY_IFNAME]) {
		const char *version = nla_get_string(attrs[BATADV_ATTR_VERSION]);
		const char *algo_name = nla_get_string(attrs[BATADV_ATTR_ALGO_NAME]);
		const char *primary_if = nla_get_string(attrs[BATADV_ATTR_PRIMARY_IFNAME]);
		const uint8_t *primary_mac = nla_data(attrs[BATADV_ATTR_PRIMARY_ADDRESS]);

		printf("[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%02x:%02x:%02x:%02x:%02x:%02x (%s %s)]\n",
		       version, primary_if,
		       primary_mac[0], primary_mac[1], primary_mac[2],
		       primary_mac[3], primary_mac[4], primary_mac[5],
		       mesh_name, algo_name);

	}
	else {
		printf("BATMAN mesh %s disabled\n", mesh_name);
	}

	return NL_STOP;
}

static void info(struct nl_handle *sock, int family, int ifindex)
{
	struct nl_msg *msg;
	struct nl_cb *cb;

	msg = nlmsg_alloc();
	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, 0, BATADV_CMD_GET_MESH_INFO, 1);

	nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex);

	nl_send_auto_complete(sock, msg);

	nlmsg_free(msg);

	cb = nl_cb_alloc(NL_CB_DEFAULT);
	nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, info_callback, NULL);
	nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL);

	nl_recvmsgs(sock, cb);
}

static int stop_callback(struct nl_msg *msg, void *arg)
{
	struct nlmsghdr *nlh = nlmsg_hdr(msg);
	int *error = nlmsg_data(nlh);

	if (*error)
		fprintf(stderr, "Error received: %s\n", strerror(-*error));

	return NL_STOP;
}

static int translocal_callback(struct nl_msg *msg, void *arg)
{
	struct nlmsghdr *nlh = nlmsg_hdr(msg);
	struct genlmsghdr *ghdr;
	struct nlattr *attrs[BATADV_ATTR_MAX+1];

	if (!genlmsg_valid_hdr(nlh, 0)) {
		fputs("Received invalid data from kernel.", stderr);
		exit(1);
	}

	ghdr = nlmsg_data(nlh);

	if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL)
		return NL_OK;

	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), NULL)) {
		fputs("Received invalid data from kernel.", stderr);
		exit(1);
	}

	uint8_t *addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]);
	int16_t vid = nla_get_u16(attrs[BATADV_ATTR_TT_VID]);
	uint32_t crc32 = nla_get_u32(attrs[BATADV_ATTR_TT_CRC32]);
	int last_seen_msecs = 0, last_seen_secs = 0;

	char r = '.', p = '.', n = '.', x = '.', w = '.', i = '.';
	if (attrs[BATADV_ATTR_FLAG_ROAM])
		r = 'R';
	if (attrs[BATADV_ATTR_FLAG_NEW])
		n = 'N';
	if (attrs[BATADV_ATTR_FLAG_PENDING])
		x = 'X';
	if (attrs[BATADV_ATTR_FLAG_WIFI])
		w = 'W';
	if (attrs[BATADV_ATTR_FLAG_ISOLA])
		i = 'I';

	if (attrs[BATADV_ATTR_FLAG_NOPURGE])  {
		p = 'P';
	}
	else {
		last_seen_msecs = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]);
		last_seen_secs = last_seen_msecs / 1000;
		last_seen_msecs = last_seen_msecs % 1000;
	}


	printf(" * %02x:%02x:%02x:%02x:%02x:%02x %4i [%c%c%c%c%c%c] %3u.%03u   (0x%.8x)\n",
	       addr[0], addr[1], addr[2],
	       addr[3], addr[4], addr[5],
	       vid, r, p, n, x, w, i,
	       last_seen_secs, last_seen_msecs,
	       crc32);

	return NL_OK;
}

static void translocal(struct nl_handle *sock, int family, int ifindex)
{
	struct nl_msg *msg;
	struct nl_cb *cb;

	msg = nlmsg_alloc();
	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_DUMP, BATADV_CMD_GET_TRANSTABLE_LOCAL, 1);

	nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex);

	nl_send_auto_complete(sock, msg);

	nlmsg_free(msg);

	cb = nl_cb_alloc(NL_CB_DEFAULT);
	nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, translocal_callback, NULL);
	nl_cb_set (cb, NL_CB_FINISH, NL_CB_CUSTOM, stop_callback, NULL);
	nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL);

	nl_recvmsgs(sock, cb);
}

static int transglobal_callback(struct nl_msg *msg, void *arg)
{
	struct nlmsghdr *nlh = nlmsg_hdr(msg);
	struct genlmsghdr *ghdr;
	struct nlattr *attrs[BATADV_ATTR_MAX+1];

	if (!genlmsg_valid_hdr(nlh, 0)) {
		fputs("Received invalid data from kernel.", stderr);
		exit(1);
	}

	ghdr = nlmsg_data(nlh);

	if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_GLOBAL)
		return NL_OK;

	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), NULL)) {
		fputs("Received invalid data from kernel.", stderr);
		exit(1);
	}

	uint8_t *addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]);
	uint8_t *orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
	int16_t vid = nla_get_u16(attrs[BATADV_ATTR_TT_VID]);
	uint8_t ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_TTVN]);
	uint8_t last_ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_LAST_TTVN]);
	uint32_t crc32 = nla_get_u32(attrs[BATADV_ATTR_TT_CRC32]);

	char c = '+', r = '.', w = '.', i = '.', t = '.';
	if (attrs[BATADV_ATTR_FLAG_BEST])
		c = '*';
	if (attrs[BATADV_ATTR_FLAG_ROAM])
		r = 'R';
	if (attrs[BATADV_ATTR_FLAG_WIFI])
		w = 'W';
	if (attrs[BATADV_ATTR_FLAG_ISOLA])
		i = 'I';
	if (attrs[BATADV_ATTR_FLAG_TEMP])
		t = 'T';


	printf(" %c %02x:%02x:%02x:%02x:%02x:%02x %4i   (%3u) via %02x:%02x:%02x:%02x:%02x:%02x     (%3u)   (0x%.8x) [%c%c%c%c]\n",
	       c,
	       addr[0], addr[1], addr[2],
	       addr[3], addr[4], addr[5],
	       vid, ttvn,
	       orig[0], orig[1], orig[2],
	       orig[3], orig[4], orig[5],
	       last_ttvn, crc32, r, w, i, t);

	return NL_OK;
}

static void transglobal(struct nl_handle *sock, int family, int ifindex)
{
	struct nl_msg *msg;
	struct nl_cb *cb;

	msg = nlmsg_alloc();
	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_DUMP, BATADV_CMD_GET_TRANSTABLE_GLOBAL, 1);

	nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex);

	nl_send_auto_complete(sock, msg);

	nlmsg_free(msg);

	cb = nl_cb_alloc(NL_CB_DEFAULT);
	nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, transglobal_callback, NULL);
	nl_cb_set (cb, NL_CB_FINISH, NL_CB_CUSTOM, stop_callback, NULL);
	nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL);

	nl_recvmsgs(sock, cb);
}

static int originators_callback(struct nl_msg *msg, void *arg)
{
	struct nlmsghdr *nlh = nlmsg_hdr(msg);
	struct genlmsghdr *ghdr;
	struct nlattr *attrs[BATADV_ATTR_MAX+1];

	if (!genlmsg_valid_hdr(nlh, 0)) {
		fputs("Received invalid data from kernel.", stderr);
		exit(1);
	}

	ghdr = nlmsg_data(nlh);

	if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS)
		return NL_OK;

	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), genlmsg_len(ghdr), NULL)) {
		fputs("Received invalid data from kernel.", stderr);
		exit(1);
	}

	uint8_t *orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
	uint8_t *neigh = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]);
	uint8_t tq = nla_get_u8(attrs[BATADV_ATTR_TQ]);

	char ifname[IF_NAMESIZE];
	if (!if_indextoname(nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]), ifname))
		ifname[0] = '\0';

	char c = '+';
	if (attrs[BATADV_ATTR_FLAG_BEST])
		c = '*';

	int last_seen_msecs, last_seen_secs;
	last_seen_msecs = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]);
	last_seen_secs = last_seen_msecs / 1000;
	last_seen_msecs = last_seen_msecs % 1000;

	printf(" %c %02x:%02x:%02x:%02x:%02x:%02x %4i.%03is   (%3i) %02x:%02x:%02x:%02x:%02x:%02x [%10s]\n",
	       c,
	       orig[0], orig[1], orig[2],
	       orig[3], orig[4], orig[5],
	       last_seen_secs, last_seen_msecs, tq,
	       neigh[0], neigh[1], neigh[2],
	       neigh[3], neigh[4], neigh[5],
	       ifname);

	return NL_OK;
}

static void originators(struct nl_handle *sock, int family, int ifindex, int outgoing_ifindex)
{
	struct nl_msg *msg;
	struct nl_cb *cb;

	msg = nlmsg_alloc();
	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_DUMP, BATADV_CMD_GET_ORIGINATORS, 1);

	nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex);

	if (outgoing_ifindex)
		nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX, outgoing_ifindex);

	nl_send_auto_complete(sock, msg);

	nlmsg_free(msg);

	cb = nl_cb_alloc(NL_CB_DEFAULT);
	nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, originators_callback, NULL);
	nl_cb_set (cb, NL_CB_FINISH, NL_CB_CUSTOM, stop_callback, NULL);
	nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL);

	nl_recvmsgs(sock, cb);
}

int main(int argc, char *argv[])
{
	struct nl_handle *sock;
	int family;

	sock = nl_handle_alloc();
	genl_connect(sock);

	family = genl_ctrl_resolve(sock, BATADV_NL_NAME);
	if (family < 0) {
		fputs("Your kernel doesn't support the batman-adv netlink interface.\n", stderr);
		exit(1);
	}

	if (argc != 3 && argc != 4)
		usage();

	int ifindex = if_nametoindex(argv[1]);
	if (!ifindex)
		usage();

	int outgoing_ifindex = 0;

	if (argc == 4) {
		outgoing_ifindex = if_nametoindex(argv[3]);
		if (!outgoing_ifindex)
			usage();
	}

	if (!strcmp(argv[2], "info"))
		info(sock, family, ifindex);
	else if (!strcmp(argv[2], "translocal"))
		translocal(sock, family, ifindex);
	else if (!strcmp(argv[2], "transglobal"))
		transglobal(sock, family, ifindex);
	else if (!strcmp(argv[2], "originators"))
		originators(sock, family, ifindex, outgoing_ifindex);
	else
		usage();

	return 0;
}

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

  reply	other threads:[~2015-06-24 19:00 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-06-24 18:34 [B.A.T.M.A.N.] [RFC] batman-adv: add generic netlink query API to replace debugfs files Matthias Schiffer
2015-06-24 19:00 ` Matthias Schiffer [this message]
2015-08-07 16:16 ` Linus Lüssing
2015-08-07 17:37   ` Matthias Schiffer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=558AFE4E.7040107@universe-factory.net \
    --to=mschiffer@universe-factory.net \
    --cc=b.a.t.m.a.n@lists.open-mesh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox