Extend batctl to use the netlink socket where available. If it is not available, fall back to debugfs.
No attempt has been made to keep the netlink output compatible with the debugfs output. Anything why tries the parse the output is probably broken.
BLA has not been tested, so is also probably broken.
Andrew Lunn (2): batctl: Document a network namespaces example. batctl: Use netlink when available, rather than debugfs
Makefile | 20 +- README | 58 +++++ debug.c | 19 +- debug.h | 4 +- functions.c | 11 + functions.h | 1 + main.h | 1 + netlink.c | 810 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 920 insertions(+), 4 deletions(-) create mode 100644 netlink.c
Add example of has BATMAN can be used with network name spaces in the README.
Signed-off-by: Andrew Lunn andrew@lunn.ch --- README | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+)
diff --git a/README b/README index f87c551..431c62e 100644 --- a/README +++ b/README @@ -520,3 +520,61 @@ where: - IPv4 is the IP address of a client in the mesh network - MAC is the MAC address associated to that IP - last-seen is the amount of time since last refresh of this entry + +batctl and network name spaces +============================== + +The batman-adv kernel module is netns aware. Mesh instances can be +created in name spaces, and interfaces in that name space added to the +mesh. The mesh interface cannot be moved between name spaces, as is +typical for virtual interfaces. + +The following example creates two network namespaces, and uses veth +pairs to connect them together into a mesh of three nodes. + +EMU1="ip netns exec emu1" +EMU2="ip netns exec emu2" + +ip netns add emu1 +ip netns add emu2 + +ip link add emu1-veth1 type veth peer name emu2-veth1 +ip link set emu1-veth1 netns emu1 +ip link set emu2-veth1 netns emu2 + +$EMU1 ip link set emu1-veth1 name veth1 +$EMU2 ip link set emu2-veth1 name veth1 + +$EMU1 ip link set veth1 up +$EMU2 ip link set veth1 up + +ip link add emu1-veth2 type veth peer name veth2 +ip link set emu1-veth2 netns emu1 +$EMU1 ip link set emu1-veth2 name veth2 + +$EMU1 ip link set veth2 up +ip link set veth2 up + +$EMU1 batctl if add veth1 +$EMU1 batctl if add veth2 +$EMU1 ip link set bat0 up + +$EMU2 batctl if add veth1 +$EMU2 ip link set bat0 up + +batctl if add veth2 +ip link set bat0 up + +alfred and batadv-vis can also be used with name spaces. In this +example, only netns has been used, so there are no filesystem name +spaces. Hence the unix domain socket used by alfred needs to be given +a unique name per instance. + +($EMU1 alfred -m -i bat0 -u /var/run/emu1-alfred.soc) & +($EMU2 alfred -m -i bat0 -u /var/run/emu2-alfred.soc) & +alfred -m -i bat0 & + +($EMU1 batadv-vis -s -u /var/run/emu1-alfred.soc) & +($EMU2 batadv-vis -s -u /var/run/emu2-alfred.soc) & +batadv-vis -s & +
The kernel has gained support for exporting information via netlink. Use this when available, rather than debugfs. Netlink has the advantage of being network name space aware, where as debugfs is not.
If netlink is not available, batctl will fall back to debugfs, so should be backwards compatible with older kernel versions.
Signed-off-by: Andrew Lunn andrew@lunn.ch --- Makefile | 20 +- debug.c | 19 +- debug.h | 4 +- functions.c | 11 + functions.h | 1 + main.h | 1 + netlink.c | 810 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 862 insertions(+), 4 deletions(-) create mode 100644 netlink.c
diff --git a/Makefile b/Makefile index b82c0c6..debca29 100755 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ export CONFIG_BATCTL_BISECT=n
# batctl build BINARY_NAME = batctl -OBJ = main.o bat-hosts.o functions.o sys.o debug.o ping.o traceroute.o tcpdump.o hash.o debugfs.o ioctl.o list-batman.o translate.o +OBJ = main.o bat-hosts.o functions.o sys.o debug.o ping.o traceroute.o tcpdump.o hash.o debugfs.o ioctl.o list-batman.o translate.o netlink.o OBJ_BISECT = bisect_iv.o MANPAGE = man/batctl.8
@@ -43,6 +43,13 @@ ifndef V endif endif
+KERNELPATH ?= /lib/modules/$(shell uname -r)/build +# sanity check: does KERNELPATH exist? +ifeq ($(shell cd $(KERNELPATH) && pwd),) +$(warning $(KERNELPATH) is missing, please set KERNELPATH) +endif +CFLAGS += -I $(KERNELPATH)/include/uapi/linux/ + ifeq ($(origin PKG_CONFIG), undefined) PKG_CONFIG = pkg-config ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null),) @@ -61,6 +68,17 @@ endif CFLAGS += $(LIBNL_CFLAGS) LDLIBS += $(LIBNL_LDLIBS)
+ifeq ($(origin LIBNL_GENL_CFLAGS) $(origin LIBNL_GENL_LDLIBS), undefined undefined) + LIBNL_GENL_NAME ?= libnl-genl-3.0 + ifeq ($(shell $(PKG_CONFIG) --modversion $(LIBNL_GENL_NAME) 2>/dev/null),) + $(error No $(LIBNL_GENL_NAME) development libraries found!) + endif + LIBNL_GENL_CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBNL_GENL_NAME)) + LIBNL_GENL_LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBNL_GENL_NAME)) +endif +CFLAGS += $(LIBNL_GENL_CFLAGS) +LDLIBS += $(LIBNL_GENL_LDLIBS) + # standard build tools ifeq ($(CONFIG_BATCTL_BISECT),y) OBJ += $(OBJ_BISECT) diff --git a/debug.c b/debug.c index 3db3ed9..5d08b42 100644 --- a/debug.c +++ b/debug.c @@ -23,10 +23,12 @@ #include <unistd.h> #include <stdio.h> #include <stdlib.h> +#include <errno.h>
#include "debug.h" #include "debugfs.h" #include "functions.h" +#include "netlink.h" #include "sys.h"
const struct debug_table_data batctl_debug_tables[BATCTL_TABLE_NUM] = { @@ -35,36 +37,42 @@ const struct debug_table_data batctl_debug_tables[BATCTL_TABLE_NUM] = { .opt_short = "n", .debugfs_name = "neighbors", .header_lines = 2, + .netlink_fn = netlink_print_neighbors, }, { .opt_long = "originators", .opt_short = "o", .debugfs_name = "originators", .header_lines = 2, + .netlink_fn = netlink_print_originators, }, { .opt_long = "gateways", .opt_short = "gwl", .debugfs_name = "gateways", .header_lines = 1, + .netlink_fn = netlink_print_gateways, }, { .opt_long = "translocal", .opt_short = "tl", .debugfs_name = "transtable_local", .header_lines = 2, + .netlink_fn = netlink_print_translocal, }, { .opt_long = "transglobal", .opt_short = "tg", .debugfs_name = "transtable_global", .header_lines = 2, + .netlink_fn = netlink_print_transglobal, }, { .opt_long = "claimtable", .opt_short = "cl", .debugfs_name = "bla_claim_table", .header_lines = 2, + .netlink_fn = netlink_print_bla_claim, }, { .opt_long = "backbonetable", @@ -115,7 +123,7 @@ int handle_debug_table(char *mesh_iface, int debug_table, int argc, char **argv) char *orig_iface = NULL; float orig_timeout = 0.0f; float watch_interval = 1; - opterr = 0; + int err;
while ((optchar = getopt(argc, argv, "hnw:t:Humi:")) != -1) { switch (optchar) { @@ -220,12 +228,19 @@ int handle_debug_table(char *mesh_iface, int debug_table, int argc, char **argv) debugfs_make_path(DEBUG_BATIF_PATH_FMT "/", orig_iface, full_path, sizeof(full_path)); else debugfs_make_path(DEBUG_BATIF_PATH_FMT "/", mesh_iface, full_path, sizeof(full_path)); + + if (batctl_debug_tables[debug_table].netlink_fn) { + err = batctl_debug_tables[debug_table].netlink_fn( + mesh_iface, read_opt, orig_timeout, watch_interval); + if (err != EOPNOTSUPP) + return err; + } return read_file(full_path, (char *)batctl_debug_tables[debug_table].debugfs_name, read_opt, orig_timeout, watch_interval, batctl_debug_tables[debug_table].header_lines); }
-int print_routing_algos(void) { +int debug_print_routing_algos(void) { char full_path[MAX_PATH+1]; char *debugfs_mnt;
diff --git a/debug.h b/debug.h index df65f50..b09def7 100644 --- a/debug.h +++ b/debug.h @@ -48,13 +48,15 @@ struct debug_table_data { const char opt_short[OPT_SHORT_MAX_LEN]; const char debugfs_name[DEBUG_TABLE_PATH_MAX_LEN]; size_t header_lines; + int (*netlink_fn)(char *mesh_iface, int read_opt, + float orig_timeout, float watch_interval); };
extern const struct debug_table_data batctl_debug_tables[BATCTL_TABLE_NUM];
int handle_debug_table(char *mesh_iface, int debug_table, int argc, char **argv); int log_print(char *mesh_iface, int argc, char **argv); -int print_routing_algos(void); +int debug_print_routing_algos(void); int print_vis_info(char *mesh_iface);
#endif diff --git a/functions.c b/functions.c index be8f8b0..df7d159 100644 --- a/functions.c +++ b/functions.c @@ -52,6 +52,7 @@ #include "sys.h" #include "debug.h" #include "debugfs.h" +#include "netlink.h"
static struct timeval start_time; static char *host_name; @@ -849,3 +850,13 @@ err:
return arg.vid; } + +int print_routing_algos(void) +{ + int err; + + err = netlink_print_routing_algos(); + if (err == EOPNOTSUPP) + err = debug_print_routing_algos(); + return err; +} diff --git a/functions.h b/functions.h index d0b05b9..1e6cfec 100644 --- a/functions.h +++ b/functions.h @@ -44,6 +44,7 @@ struct ether_addr *translate_mac(char *mesh_iface, struct ether_addr *mac); struct ether_addr *resolve_mac(const char *asc); int vlan_get_link(const char *ifname, char **parent);
+int print_routing_algos(void); extern char *line_ptr;
enum { diff --git a/main.h b/main.h index 81f223c..a05d1f0 100644 --- a/main.h +++ b/main.h @@ -47,6 +47,7 @@ #endif
#define __packed __attribute((packed)) /* linux kernel compat */ +#define __unused __attribute__((unused)) #define BIT(nr) (1UL << (nr)) /* linux kernel compat */
typedef uint8_t u8; /* linux kernel compat */ diff --git a/netlink.c b/netlink.c new file mode 100644 index 0000000..cddc42a --- /dev/null +++ b/netlink.c @@ -0,0 +1,810 @@ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.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 "bat-hosts.h" +#include "batman_adv.h" +#include "functions.h" +#include "main.h" + +struct print_opts { + int read_opt; + float orig_timeout; + float watch_interval; +}; + +static int last_err; + +static int print_error(struct sockaddr_nl *nla __unused, + struct nlmsgerr *nlerr, + void *arg __unused) +{ + if (nlerr->error != -EOPNOTSUPP) + fprintf(stderr, "Error received: %s\n", + strerror(-nlerr->error)); + + last_err = -nlerr->error; + + return NL_STOP; +} + +static int stop_callback(struct nl_msg *msg, void *arg __unused) +{ + 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 info_callback(struct nl_msg *msg, void *arg __unused) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genlmsghdr *ghdr; + const uint8_t *primary_mac ; + const char *primary_if; + const char *algo_name; + const char *mesh_name; + const char *version; + + 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); + } + + mesh_name = nla_get_string(attrs[BATADV_ATTR_MESH_IFNAME]); + + if (attrs[BATADV_ATTR_HARD_IFNAME]) { + version = nla_get_string(attrs[BATADV_ATTR_VERSION]); + algo_name = nla_get_string(attrs[BATADV_ATTR_ALGO_NAME]); + primary_if = nla_get_string(attrs[BATADV_ATTR_HARD_IFNAME]); + primary_mac = nla_data(attrs[BATADV_ATTR_HARD_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 netlink_print_info(int ifindex) +{ + struct nl_sock *sock; + struct nl_msg *msg; + struct nl_cb *cb; + int family; + + sock = nl_socket_alloc(); + genl_connect(sock); + + family = genl_ctrl_resolve(sock, BATADV_NL_NAME); + if (family < 0) { + return; + } + + 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); + + nl_socket_free(sock); +} + +static int routing_algos_callback(struct nl_msg *msg, void *arg __unused) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genlmsghdr *ghdr; + + 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_ROUTING_ALGOS) + 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 *algo_name = nla_get_string(attrs[BATADV_ATTR_ALGO_NAME]); + + printf(" * %s\n", algo_name); + + return NL_OK; +} + +int netlink_print_routing_algos(void) +{ + struct nl_sock *sock; + struct nl_msg *msg; + struct nl_cb *cb; + int family; + + sock = nl_socket_alloc(); + genl_connect(sock); + + family = genl_ctrl_resolve(sock, BATADV_NL_NAME); + if (family < 0) { + return EOPNOTSUPP; + } + + msg = nlmsg_alloc(); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_DUMP, + BATADV_CMD_GET_ROUTING_ALGOS, 1); + + 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, routing_algos_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); + + printf("Available routing algorithms:\n"); + + nl_recvmsgs(sock, cb); + + nl_socket_free(sock); + + return 0; +} + +static int originators_callback(struct nl_msg *msg, void *arg) +{ + unsigned throughput_mbits, throughput_kbits; + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + int last_seen_msecs, last_seen_secs; + struct print_opts *opts = arg; + struct bat_host *bat_host; + struct genlmsghdr *ghdr; + char ifname[IF_NAMESIZE]; + float last_seen; + uint8_t *neigh; + uint8_t *orig; + char c = ' '; + uint8_t tq; + + 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); + } + + orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); + neigh = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); + + if (!if_indextoname(nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]), + ifname)) + ifname[0] = '\0'; + + if (attrs[BATADV_ATTR_FLAG_BEST]) + c = '*'; + + last_seen_msecs = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); + last_seen = (float)last_seen_msecs / 1000.0; + last_seen_secs = last_seen_msecs / 1000; + last_seen_msecs = last_seen_msecs % 1000; + + /* skip timed out originators */ + if (opts->read_opt & NO_OLD_ORIGS) + if (last_seen > opts->orig_timeout) + return NL_OK; + + if (attrs[BATADV_ATTR_THROUGHPUT]) { + throughput_kbits = nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]); + throughput_mbits = throughput_kbits / 1000; + throughput_kbits = throughput_kbits % 1000; + + if (!(opts->read_opt & USE_BAT_HOSTS)) { + printf(" %c %02x:%02x:%02x:%02x:%02x:%02x %4i.%03is (%9u.%1u) %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, + throughput_mbits, throughput_kbits / 100, + neigh[0], neigh[1], neigh[2], + neigh[3], neigh[4], neigh[5], + ifname); + } else { + bat_host = bat_hosts_find_by_mac((char *)orig); + if (bat_host) + printf(" %c %17s ", c, bat_host->name); + else + printf(" %c %02x:%02x:%02x:%02x:%02x:%02x ", + c, + orig[0], orig[1], orig[2], + orig[3], orig[4], orig[5]); + printf("%4i.%03is (%9u.%1u) ", + last_seen_secs, last_seen_msecs, + throughput_mbits, throughput_kbits / 100); + bat_host = bat_hosts_find_by_mac((char *)neigh); + if (bat_host) + printf(" %c %17s ", c, bat_host->name); + else + printf(" %02x:%02x:%02x:%02x:%02x:%02x ", + neigh[0], neigh[1], neigh[2], + neigh[3], neigh[4], neigh[5]); + printf("[%10s]\n", ifname); + } + } + else { + tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); + + if (!(opts->read_opt & USE_BAT_HOSTS)) { + 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); + } else { + bat_host = bat_hosts_find_by_mac((char *)orig); + if (bat_host) + printf(" %c %17s ", c, bat_host->name); + else + printf(" %c %02x:%02x:%02x:%02x:%02x:%02x ", + c, + orig[0], orig[1], orig[2], + orig[3], orig[4], orig[5]); + printf("%4i.%03is (%3i) ", + last_seen_secs, last_seen_msecs, tq); + bat_host = bat_hosts_find_by_mac((char *)neigh); + if (bat_host) + printf("%17s ", bat_host->name); + else + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + neigh[0], neigh[1], neigh[2], + neigh[3], neigh[4], neigh[5]); + printf("[%10s]\n", ifname); + } + } + + return NL_OK; +} + +static int neighbors_callback(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct print_opts *opts = arg; + struct bat_host *bat_host; + char ifname[IF_NAMESIZE]; + struct genlmsghdr *ghdr; + uint8_t *neigh; + + 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_NEIGHBORS) + 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); + } + + neigh = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); + bat_host = bat_hosts_find_by_mac((char *)neigh); + + if (!if_indextoname(nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]), + ifname)) + ifname[0] = '\0'; + + 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; + + if (attrs[BATADV_ATTR_THROUGHPUT]) { + unsigned throughput_mbits, throughput_kbits; + throughput_kbits = nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]); + throughput_mbits = throughput_kbits / 1000; + throughput_kbits = throughput_kbits % 1000; + + if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + neigh[0], neigh[1], neigh[2], + neigh[3], neigh[4], neigh[5]); + else + printf("%17s ", bat_host->name); + + printf("%4i.%03is (%9u.%1u) [%10s]\n", + last_seen_secs, last_seen_msecs, + throughput_mbits, throughput_kbits / 100, + ifname); + } else { + printf(" %10s ", ifname); + + if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + neigh[0], neigh[1], neigh[2], + neigh[3], neigh[4], neigh[5]); + else + printf("%17s ", bat_host->name); + + printf("%4i.%03is\n", last_seen_secs, last_seen_msecs); + } + + return NL_OK; +} + +static int transglobal_callback(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct print_opts *opts = arg; + struct bat_host *bat_host; + struct genlmsghdr *ghdr; + uint8_t last_ttvn; + uint32_t crc32; + uint32_t flags; + uint8_t *addr; + uint8_t *orig; + uint8_t ttvn; + int16_t vid; + + 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); + } + + addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]); + orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); + vid = nla_get_u16(attrs[BATADV_ATTR_TT_VID]); + ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_TTVN]); + last_ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_LAST_TTVN]); + crc32 = nla_get_u32(attrs[BATADV_ATTR_TT_CRC32]); + flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]); + + char c = ' ', r = '.', w = '.', i = '.', t = '.'; + if (attrs[BATADV_ATTR_FLAG_BEST]) + c = '*'; + if (flags & BATADV_TT_CLIENT_ROAM) + r = 'R'; + if (flags & BATADV_TT_CLIENT_WIFI) + w = 'W'; + if (flags & BATADV_TT_CLIENT_ISOLA) + i = 'I'; + if (flags & BATADV_TT_CLIENT_TEMP) + t = 'T'; + + + printf(" %c ", c); + + bat_host = bat_hosts_find_by_mac((char *)addr); + if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5]); + else + printf("%17s ", bat_host->name); + + printf("%4i [%c%c%c%c] (%3u) ", + vid, r, w, i, t, ttvn); + + bat_host = bat_hosts_find_by_mac((char *)orig); + if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + orig[0], orig[1], orig[2], + orig[3], orig[4], orig[5]); + else + printf("%17s ", bat_host->name); + + printf("(%3u) (0x%.8x)\n", + last_ttvn, crc32); + + return NL_OK; +} + +static int translocal_callback(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct print_opts *opts = arg; + struct bat_host *bat_host; + struct genlmsghdr *ghdr; + uint8_t *addr; + int16_t vid; + uint32_t crc32; + uint32_t flags; + int last_seen_msecs = 0, last_seen_secs = 0; + + 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); + } + + addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]); + vid = nla_get_u16(attrs[BATADV_ATTR_TT_VID]); + crc32 = nla_get_u32(attrs[BATADV_ATTR_TT_CRC32]); + flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]); + last_seen_msecs = 0, last_seen_secs = 0; + + char r = '.', p = '.', n = '.', x = '.', w = '.', i = '.'; + if (flags & BATADV_TT_CLIENT_ROAM) + r = 'R'; + if (flags & BATADV_TT_CLIENT_NEW) + n = 'N'; + if (flags & BATADV_TT_CLIENT_PENDING) + x = 'X'; + if (flags & BATADV_TT_CLIENT_WIFI) + w = 'W'; + if (flags & BATADV_TT_CLIENT_ISOLA) + i = 'I'; + + if (flags & BATADV_TT_CLIENT_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; + } + + bat_host = bat_hosts_find_by_mac((char *)addr); + if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5]); + else + printf("%17s ", bat_host->name); + + printf("%4i [%c%c%c%c%c%c] %3u.%03u (0x%.8x)\n", + vid, r, p, n, x, w, i, + last_seen_secs, last_seen_msecs, + crc32); + + return NL_OK; +} + +static int gateways_callback(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct print_opts *opts = arg; + struct bat_host *bat_host; + struct genlmsghdr *ghdr; + const char *primary_if; + uint32_t bandwidth_down; + uint32_t bandwidth_up; + uint8_t *router; + uint8_t *orig; + char c = ' '; + uint8_t tq; + + 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_GATEWAYS) + 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); + } + + if (attrs[BATADV_ATTR_FLAG_BEST]) + c = '*'; + + orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); + tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); + router = nla_data(attrs[BATADV_ATTR_ROUTER]); + primary_if = nla_get_string(attrs[BATADV_ATTR_HARD_IFNAME]); + bandwidth_down = nla_get_u32(attrs[BATADV_ATTR_BANDWIDTH_DOWN]); + bandwidth_up = nla_get_u32(attrs[BATADV_ATTR_BANDWIDTH_UP]); + + printf("%c ", c); + + bat_host = bat_hosts_find_by_mac((char *)orig); + if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + orig[0], orig[1], orig[2], + orig[3], orig[4], orig[5]); + else + printf("%17s ", bat_host->name); + + printf("(%3i) ", tq); + + bat_host = bat_hosts_find_by_mac((char *)router); + if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + router[0], router[1], router[2], + router[3], router[4], router[5]); + else + printf("%17s ", bat_host->name); + + printf("[%10s]: %u.%u/%u.%u MBit\n", + primary_if, bandwidth_down / 10, bandwidth_down % 10, + bandwidth_up / 10, bandwidth_up % 10); + + return NL_OK; +} + +static int bla_claim_callback(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct print_opts *opts = arg; + struct bat_host *bat_host; + struct genlmsghdr *ghdr; + uint16_t backbone_crc; + uint8_t *backbone; + uint8_t *client; + uint16_t vid; + char c = ' '; + + 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_BLA_CLAIM) + 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); + } + + if (attrs[BATADV_ATTR_BLA_OWN]) + c = '*'; + + client = nla_data(attrs[BATADV_ATTR_BLA_ADDRESS]); + vid = nla_get_u16(attrs[BATADV_ATTR_BLA_VID]); + backbone = nla_data(attrs[BATADV_ATTR_BLA_BACKBONE]); + backbone_crc = nla_get_u16(attrs[BATADV_ATTR_BLA_CRC]); + + bat_host = bat_hosts_find_by_mac((char *)client); + if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + client[0], client[1], client[2], + client[3], client[4], client[5]); + else + printf("%17s ", bat_host->name); + + printf("%5d ", vid); + + bat_host = bat_hosts_find_by_mac((char *)backbone); + if (!(opts->read_opt & USE_BAT_HOSTS) || !bat_host) + printf("%02x:%02x:%02x:%02x:%02x:%02x ", + backbone[0], backbone[1], backbone[2], + backbone[3], backbone[4], backbone[5]); + else + printf("%17s ", bat_host->name); + + printf("%c %#.4x", c, backbone_crc); + + return NL_OK; +} + +static int netlink_print_common(char *mesh_iface, int read_opt, + float orig_timeout, float watch_interval, + const char *header, uint8_t nl_cmd, + nl_recvmsg_msg_cb_t callback) +{ + struct print_opts opts = { + .read_opt = read_opt, + .orig_timeout = orig_timeout, + .watch_interval = watch_interval + }; + struct nl_sock *sock; + struct nl_msg *msg; + struct nl_cb *cb; + int ifindex; + int family; + + sock = nl_socket_alloc(); + genl_connect(sock); + + family = genl_ctrl_resolve(sock, BATADV_NL_NAME); + if (family < 0) { + return EOPNOTSUPP; + } + + ifindex = if_nametoindex(mesh_iface); + if (!ifindex) { + fprintf(stderr, "Interface %s is unknown\n", mesh_iface); + return ENODEV; + } + + bat_hosts_init(read_opt); + + cb = nl_cb_alloc(NL_CB_DEFAULT); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, callback, &opts); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, stop_callback, NULL); + nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL); + + do { + if (read_opt & CLR_CONT_READ) + /* clear screen, set cursor back to 0,0 */ + printf("\033[2J\033[0;0f"); + + if (!(read_opt & SKIP_HEADER)) { + netlink_print_info(ifindex); + if (header) + printf(header); + } + + msg = nlmsg_alloc(); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, + NLM_F_DUMP, nl_cmd, 1); + + nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex); + nl_send_auto_complete(sock, msg); + + nlmsg_free(msg); + + last_err = 0; + nl_recvmsgs(sock, cb); + if (!last_err && read_opt & (CONT_READ|CLR_CONT_READ)) + usleep(1000000 * watch_interval); + + } while (!last_err && read_opt & (CONT_READ|CLR_CONT_READ)); + + bat_hosts_free(); + + nl_socket_free(sock); + + return last_err; +} + + +int netlink_print_originators(char *mesh_iface, int read_opts, + float orig_timeout, + float watch_interval) +{ + return netlink_print_common(mesh_iface, read_opts, orig_timeout, + watch_interval, "", + BATADV_CMD_GET_ORIGINATORS, + originators_callback); +} + +int netlink_print_neighbors(char *mesh_iface, int read_opts, + float orig_timeout, + float watch_interval) +{ + return netlink_print_common(mesh_iface, read_opts, orig_timeout, + watch_interval, + " IF Neighbor last-seen\n""", + BATADV_CMD_GET_NEIGHBORS, + neighbors_callback); +} + +int netlink_print_transglobal(char *mesh_iface, int read_opts, + float orig_timeout, + float watch_interval) +{ + return netlink_print_common(mesh_iface, read_opts, orig_timeout, + watch_interval, + " Client VID Flags Last ttvn Via ttvn (CRC )\n", + BATADV_CMD_GET_TRANSTABLE_GLOBAL, + transglobal_callback); +} + +int netlink_print_translocal(char *mesh_iface, int read_opts, + float orig_timeout, + float watch_interval) +{ + return netlink_print_common(mesh_iface, read_opts, orig_timeout, + watch_interval, + " Client VID Flags Last seen (CRC )\n", + BATADV_CMD_GET_TRANSTABLE_LOCAL, + translocal_callback); +} + +int netlink_print_gateways(char *mesh_iface, int read_opts, + float orig_timeout, + float watch_interval) +{ + return netlink_print_common(mesh_iface, read_opts, orig_timeout, + watch_interval, + " Router TQ Next Hop outgoingIf Bandwidth\n", + BATADV_CMD_GET_GATEWAYS, + gateways_callback); +} + +int netlink_print_bla_claim(char *mesh_iface, int read_opts, + float orig_timeout, + float watch_interval) +{ + return netlink_print_common(mesh_iface, read_opts, orig_timeout, + watch_interval, + "Client VID Originator [o] (CRC )\n", + BATADV_CMD_GET_BLA_CLAIM, + bla_claim_callback); +} + +
On Thursday 28 April 2016 22:54:57 Andrew Lunn wrote:
The kernel has gained support for exporting information via netlink. Use this when available, rather than debugfs. Netlink has the advantage of being network name space aware, where as debugfs is not.
If netlink is not available, batctl will fall back to debugfs, so should be backwards compatible with older kernel versions.
Signed-off-by: Andrew Lunn andrew@lunn.ch
[...]
- addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]);
- orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
- vid = nla_get_u16(attrs[BATADV_ATTR_TT_VID]);
- ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_TTVN]);
- last_ttvn = nla_get_u8(attrs[BATADV_ATTR_TT_LAST_TTVN]);
- crc32 = nla_get_u32(attrs[BATADV_ATTR_TT_CRC32]);
- flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]);
You are accessing a lot of data without checking if it exists and is from the correct type. This was discussed in an earlier mail [1].
Kind regards, Sven
[1] https://lists.open-mesh.org/pipermail/b.a.t.m.a.n/2016-March/014722.html
On Thursday 28 April 2016 22:54:57 Andrew Lunn wrote:
The kernel has gained support for exporting information via netlink. Use this when available, rather than debugfs. Netlink has the advantage of being network name space aware, where as debugfs is not.
If netlink is not available, batctl will fall back to debugfs, so should be backwards compatible with older kernel versions.
Signed-off-by: Andrew Lunn andrew@lunn.ch
Makefile | 20 +- debug.c | 19 +- debug.h | 4 +- functions.c | 11 + functions.h | 1 + main.h | 1 + netlink.c | 810 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 862 insertions(+), 4 deletions(-) create mode 100644 netlink.c
It looks like this patch currently only adds the printing of netlink information in a way that looks similar to the output from the legacy debugfs files. This is ok but I find it weird that it claims in the commit message that it uses (for everything?) netlink instead of debugfs when things like translate_mac(...) and ping/traceroute still have to be ported to netlink.
Kind regards, Sven
On Fri, Apr 29, 2016 at 08:12:47AM +0200, Sven Eckelmann wrote:
On Thursday 28 April 2016 22:54:57 Andrew Lunn wrote:
The kernel has gained support for exporting information via netlink. Use this when available, rather than debugfs. Netlink has the advantage of being network name space aware, where as debugfs is not.
If netlink is not available, batctl will fall back to debugfs, so should be backwards compatible with older kernel versions.
Signed-off-by: Andrew Lunn andrew@lunn.ch
Makefile | 20 +- debug.c | 19 +- debug.h | 4 +- functions.c | 11 + functions.h | 1 + main.h | 1 + netlink.c | 810 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 862 insertions(+), 4 deletions(-) create mode 100644 netlink.c
It looks like this patch currently only adds the printing of netlink information in a way that looks similar to the output from the legacy debugfs files. This is ok but I find it weird that it claims in the commit message that it uses (for everything?) netlink instead of debugfs when things like translate_mac(...) and ping/traceroute still have to be ported to netlink.
Hi Sven
It never makes the claim it uses it for everything. I converted what i needed for netns in my setup, and a bit more. I pretty much never use ping/traceroute/BLA, gateways, etc, in my simulations, so i have no itch to scratch there.
I would suggest getting the current code in a state it can be accepted, i.e. add the extra error checking, etc. It is mostly mechanical work, once you have an example to copy. So it should not be too hard to finish off the debugfs equivalents by somebody who has a need for them.
Andrew
On Friday 29 April 2016 14:27:53 Andrew Lunn wrote: [...]
It looks like this patch currently only adds the printing of netlink information in a way that looks similar to the output from the legacy debugfs files. This is ok but I find it weird that it claims in the commit message that it uses (for everything?) netlink instead of debugfs when things like translate_mac(...) and ping/traceroute still have to be ported to netlink.
[...]
It never makes the claim it uses it for everything. I converted what i needed for netns in my setup, and a bit more. I pretty much never use ping/traceroute/BLA, gateways, etc, in my simulations, so i have no itch to scratch there.
I would suggest getting the current code in a state it can be accepted, i.e. add the extra error checking, etc. It is mostly mechanical work, once you have an example to copy. So it should not be too hard to finish off the debugfs equivalents by somebody who has a need for them.
It was not about the code but about the commit message. I've already said that the print-only implementation "is ok" and is everything we need at the beginning.
Kind regards, Sven
b.a.t.m.a.n@lists.open-mesh.org