diff --git a/backbonetable.c b/backbonetable.c
index 2512142..d799d8c 100644
--- a/backbonetable.c
+++ b/backbonetable.c
@@ -103,11 +103,11 @@ static int bla_backbone_callback(struct nl_msg *msg, void *arg)
 	return NL_OK;
 }
 
-static int netlink_print_bla_backbone(char *mesh_iface, char *orig_iface,
+static int netlink_print_bla_backbone(struct state *state, char *orig_iface,
 				      int read_opts, float orig_timeout,
 				      float watch_interval)
 {
-	return netlink_print_common(mesh_iface, orig_iface, read_opts,
+	return netlink_print_common(state, orig_iface, read_opts,
 				    orig_timeout, watch_interval,
 				    "Originator           VID   last seen (CRC   )\n",
 				    BATADV_CMD_GET_BLA_BACKBONE,
@@ -121,4 +121,5 @@ static struct debug_table_data batctl_debug_table_backbonetable = {
 };
 
 COMMAND_NAMED(DEBUGTABLE, backbonetable, "bbt", handle_debug_table,
-	      COMMAND_FLAG_MESH_IFACE, &batctl_debug_table_backbonetable, "");
+	      COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+	      &batctl_debug_table_backbonetable, "");
diff --git a/claimtable.c b/claimtable.c
index ca79fc3..989879f 100644
--- a/claimtable.c
+++ b/claimtable.c
@@ -108,11 +108,11 @@ static int bla_claim_callback(struct nl_msg *msg, void *arg)
 	return NL_OK;
 }
 
-static int netlink_print_bla_claim(char *mesh_iface, char *orig_iface,
+static int netlink_print_bla_claim(struct state *state, char *orig_iface,
 				   int read_opts, float orig_timeout,
 				   float watch_interval)
 {
-	return netlink_print_common(mesh_iface, orig_iface, read_opts,
+	return netlink_print_common(state, orig_iface, read_opts,
 				    orig_timeout, watch_interval,
 				    "Client               VID      Originator        [o] (CRC   )\n",
 				    BATADV_CMD_GET_BLA_CLAIM,
@@ -126,4 +126,5 @@ static struct debug_table_data batctl_debug_table_claimtable = {
 };
 
 COMMAND_NAMED(DEBUGTABLE, claimtable, "cl", handle_debug_table,
-	      COMMAND_FLAG_MESH_IFACE, &batctl_debug_table_claimtable, "");
+	      COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+	      &batctl_debug_table_claimtable, "");
diff --git a/dat_cache.c b/dat_cache.c
index 39f8f18..1e1f028 100644
--- a/dat_cache.c
+++ b/dat_cache.c
@@ -112,7 +112,7 @@ static int dat_cache_callback(struct nl_msg *msg, void *arg)
 	return NL_OK;
 }
 
-static int netlink_print_dat_cache(char *mesh_iface, char *orig_iface,
+static int netlink_print_dat_cache(struct state *state, char *orig_iface,
 				   int read_opts, float orig_timeout,
 				   float watch_interval)
 {
@@ -120,13 +120,13 @@ static int netlink_print_dat_cache(char *mesh_iface, char *orig_iface,
 	int ret;
 
 	ret = asprintf(&header, "Distributed ARP Table (%s):\n%s\n",
-		       mesh_iface,
+		       state->mesh_iface,
 		       "          IPv4             MAC        VID   last-seen");
 
 	if (ret < 0)
 		return ret;
 
-	ret = netlink_print_common(mesh_iface, orig_iface, read_opts,
+	ret = netlink_print_common(state, orig_iface, read_opts,
 				   orig_timeout, watch_interval, header,
 				   BATADV_CMD_GET_DAT_CACHE,
 				   dat_cache_callback);
@@ -142,4 +142,5 @@ static struct debug_table_data batctl_debug_table_dat_cache = {
 };
 
 COMMAND_NAMED(DEBUGTABLE, dat_cache, "dc", handle_debug_table,
-	      COMMAND_FLAG_MESH_IFACE, &batctl_debug_table_dat_cache, "");
+	      COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+	      &batctl_debug_table_dat_cache, "");
diff --git a/debug.c b/debug.c
index 17dde92..b908e00 100644
--- a/debug.c
+++ b/debug.c
@@ -168,9 +168,8 @@ int handle_debug_table(struct state *state, int argc, char **argv)
 	}
 
 	if (debug_table->netlink_fn) {
-		err = debug_table->netlink_fn(
-			state->mesh_iface, orig_iface, read_opt, orig_timeout,
-			watch_interval);
+		err = debug_table->netlink_fn(state , orig_iface, read_opt,
+					      orig_timeout, watch_interval);
 		if (err != -EOPNOTSUPP)
 			return err;
 	}
diff --git a/debug.h b/debug.h
index 79c489a..525a46b 100644
--- a/debug.h
+++ b/debug.h
@@ -39,7 +39,7 @@
 struct debug_table_data {
 	const char *debugfs_name;
 	size_t header_lines;
-	int (*netlink_fn)(char *mesh_iface, char *hard_iface, int read_opt,
+	int (*netlink_fn)(struct state *state, char *hard_iface, int read_opt,
 			 float orig_timeout, float watch_interval);
 	unsigned int option_unicast_only:1;
 	unsigned int option_multicast_only:1;
diff --git a/gateways.c b/gateways.c
index d027a32..b0a36c7 100644
--- a/gateways.c
+++ b/gateways.c
@@ -126,7 +126,7 @@ static int gateways_callback(struct nl_msg *msg, void *arg)
 	return NL_OK;
 }
 
-static int netlink_print_gateways(char *mesh_iface, char *orig_iface,
+static int netlink_print_gateways(struct state *state, char *orig_iface,
 				  int read_opts, float orig_timeout,
 				  float watch_interval)
 {
@@ -134,9 +134,9 @@ static int netlink_print_gateways(char *mesh_iface, char *orig_iface,
 	char *info_header;
 	int ifindex;
 
-	ifindex = if_nametoindex(mesh_iface);
+	ifindex = if_nametoindex(state->mesh_iface);
 	if (!ifindex) {
-		fprintf(stderr, "Interface %s is unknown\n", mesh_iface);
+		fprintf(stderr, "Interface %s is unknown\n", state->mesh_iface);
 		return -ENODEV;
 	}
 
@@ -156,7 +156,7 @@ static int netlink_print_gateways(char *mesh_iface, char *orig_iface,
 	if (!header)
 		return -EINVAL;
 
-	return netlink_print_common(mesh_iface, orig_iface, read_opts,
+	return netlink_print_common(state, orig_iface, read_opts,
 				    orig_timeout, watch_interval,
 				    header,
 				    BATADV_CMD_GET_GATEWAYS,
@@ -170,4 +170,5 @@ static struct debug_table_data batctl_debug_table_gateways = {
 };
 
 COMMAND_NAMED(DEBUGTABLE, gateways, "gwl", handle_debug_table,
-	      COMMAND_FLAG_MESH_IFACE, &batctl_debug_table_gateways, "");
+	      COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+	      &batctl_debug_table_gateways, "");
diff --git a/main.c b/main.c
index 9fc6bfb..ab7da45 100644
--- a/main.c
+++ b/main.c
@@ -21,7 +21,7 @@
  */
 
 
-
+#include <errno.h>
 #include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -32,6 +32,7 @@
 #include "sys.h"
 #include "debug.h"
 #include "functions.h"
+#include "netlink.h"
 
 char mesh_dfl_iface[] = "bat0";
 char module_ver_path[] = "/sys/module/batman_adv/version";
@@ -180,8 +181,23 @@ int main(int argc, char **argv)
 		exit(EXIT_FAILURE);
 	}
 
+	if (cmd->flags & COMMAND_FLAG_NETLINK) {
+		ret = netlink_create(&state);
+		if (ret < 0 && ret != -EOPNOTSUPP) {
+			/* TODO handle -EOPNOTSUPP as error when fallbacks were
+			 * removed
+			 */
+			fprintf(stderr,
+				"Error - failed to connect to batadv\n");
+			exit(EXIT_FAILURE);
+		}
+	}
+
 	ret = cmd->handler(&state, argc, argv);
 
+	if (cmd->flags & COMMAND_FLAG_NETLINK)
+		netlink_destroy(&state);
+
 	return ret;
 
 err:
diff --git a/main.h b/main.h
index 2656dcf..4a48f61 100644
--- a/main.h
+++ b/main.h
@@ -25,6 +25,10 @@
 
 #include <stdint.h>
 
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/genl.h>
+#include <netlink/netlink.h>
+
 #ifndef SOURCE_VERSION
 #define SOURCE_VERSION "2018.4"
 #endif
@@ -61,6 +65,7 @@ extern char module_ver_path[];
 
 enum command_flags {
 	COMMAND_FLAG_MESH_IFACE = BIT(0),
+	COMMAND_FLAG_NETLINK = BIT(1),
 };
 
 enum command_type {
@@ -71,6 +76,10 @@ enum command_type {
 struct state {
 	char *mesh_iface;
 	const struct command *cmd;
+
+	struct nl_sock *sock;
+	struct nl_cb *cb;
+	int batadv_family;
 };
 
 struct command {
diff --git a/mcast_flags.c b/mcast_flags.c
index 9a1b819..bcbe17c 100644
--- a/mcast_flags.c
+++ b/mcast_flags.c
@@ -104,7 +104,7 @@ static int mcast_flags_callback(struct nl_msg *msg, void *arg)
 	return NL_OK;
 }
 
-static int netlink_print_mcast_flags(char *mesh_iface, char *orig_iface,
+static int netlink_print_mcast_flags(struct state *state, char *orig_iface,
 				     int read_opts, float orig_timeout,
 				     float watch_interval)
 {
@@ -115,9 +115,9 @@ static int netlink_print_mcast_flags(char *mesh_iface, char *orig_iface,
 	int ifindex;
 	int ret;
 
-	ifindex = if_nametoindex(mesh_iface);
+	ifindex = if_nametoindex(state->mesh_iface);
 	if (!ifindex) {
-		fprintf(stderr, "Interface %s is unknown\n", mesh_iface);
+		fprintf(stderr, "Interface %s is unknown\n", state->mesh_iface);
 		return -ENODEV;
 	}
 
@@ -159,7 +159,7 @@ static int netlink_print_mcast_flags(char *mesh_iface, char *orig_iface,
 	if (ret < 0)
 		return ret;
 
-	ret = netlink_print_common(mesh_iface, orig_iface, read_opts,
+	ret = netlink_print_common(state, orig_iface, read_opts,
 				   orig_timeout, watch_interval, header,
 				   BATADV_CMD_GET_MCAST_FLAGS,
 				   mcast_flags_callback);
@@ -175,4 +175,5 @@ static struct debug_table_data batctl_debug_table_mcast_flags = {
 };
 
 COMMAND_NAMED(DEBUGTABLE, mcast_flags, "mf", handle_debug_table,
-	      COMMAND_FLAG_MESH_IFACE, &batctl_debug_table_mcast_flags, "");
+	      COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+	      &batctl_debug_table_mcast_flags, "");
diff --git a/neighbors.c b/neighbors.c
index c7a0f45..cd04be8 100644
--- a/neighbors.c
+++ b/neighbors.c
@@ -119,11 +119,11 @@ static int neighbors_callback(struct nl_msg *msg, void *arg)
 	return NL_OK;
 }
 
-static int netlink_print_neighbors(char *mesh_iface, char *orig_iface,
+static int netlink_print_neighbors(struct state *state, char *orig_iface,
 				   int read_opts, float orig_timeout,
 				   float watch_interval)
 {
-	return netlink_print_common(mesh_iface, orig_iface, read_opts,
+	return netlink_print_common(state, orig_iface, read_opts,
 				    orig_timeout, watch_interval,
 				    "IF             Neighbor              last-seen\n",
 				    BATADV_CMD_GET_NEIGHBORS,
@@ -137,4 +137,5 @@ static struct debug_table_data batctl_debug_table_neighbors = {
 };
 
 COMMAND_NAMED(DEBUGTABLE, neighbors, "n", handle_debug_table,
-	      COMMAND_FLAG_MESH_IFACE, &batctl_debug_table_neighbors, "");
+	      COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+	      &batctl_debug_table_neighbors, "");
diff --git a/netlink.c b/netlink.c
index 26ae27d..8268037 100644
--- a/netlink.c
+++ b/netlink.c
@@ -109,6 +109,59 @@ struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
 	[BATADV_ATTR_MCAST_FLAGS_PRIV]		= { .type = NLA_U32 },
 };
 
+int netlink_create(struct state *state)
+{
+	int ret;
+
+	state->sock = NULL;
+	state->cb = NULL;
+	state->batadv_family = 0;
+
+	state->sock = nl_socket_alloc();
+	if (!state->sock)
+		return -ENOMEM;
+
+	ret = genl_connect(state->sock);
+	if (ret < 0)
+		goto err_free_sock;
+
+	state->batadv_family = genl_ctrl_resolve(state->sock, BATADV_NL_NAME);
+	if (state->batadv_family < 0) {
+		ret = -EOPNOTSUPP;
+		goto err_free_sock;
+	}
+
+	state->cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (!state->cb) {
+		ret = -ENOMEM;
+		goto err_free_family;
+	}
+
+	return 0;
+
+err_free_family:
+	state->batadv_family = 0;
+
+err_free_sock:
+	nl_socket_free(state->sock);
+	state->sock = NULL;
+
+	return ret;
+}
+
+void netlink_destroy(struct state *state)
+{
+	if (state->cb) {
+		nl_cb_put(state->cb);
+		state->cb = NULL;
+	}
+
+	if (state->sock) {
+		nl_socket_free(state->sock);
+		state->sock = NULL;
+	}
+}
+
 int last_err;
 char algo_name_buf[256] = "";
 int64_t mcast_flags = -EOPNOTSUPP;
@@ -456,7 +509,7 @@ int netlink_print_routing_algos(void)
 	return last_err;
 }
 
-int netlink_print_common(char *mesh_iface, char *orig_iface, int read_opt,
+int netlink_print_common(struct state *state, char *orig_iface, int read_opt,
 			 float orig_timeout, float watch_interval,
 			 const char *header, uint8_t nl_cmd,
 			 nl_recvmsg_msg_cb_t callback)
@@ -469,29 +522,19 @@ int netlink_print_common(char *mesh_iface, char *orig_iface, int read_opt,
 		.callback = callback,
 	};
 	int hardifindex = 0;
-	struct nl_sock *sock;
 	struct nl_msg *msg;
-	struct nl_cb *cb;
 	int ifindex;
-	int family;
 
-	sock = nl_socket_alloc();
-	if (!sock)
-		return -ENOMEM;
-
-	genl_connect(sock);
-
-	family = genl_ctrl_resolve(sock, BATADV_NL_NAME);
-	if (family < 0) {
+	if (!state->sock) {
 		last_err = -EOPNOTSUPP;
-		goto err_free_sock;
+		return last_err;
 	}
 
-	ifindex = if_nametoindex(mesh_iface);
+	ifindex = if_nametoindex(state->mesh_iface);
 	if (!ifindex) {
-		fprintf(stderr, "Interface %s is unknown\n", mesh_iface);
+		fprintf(stderr, "Interface %s is unknown\n", state->mesh_iface);
 		last_err = -ENODEV;
-		goto err_free_sock;
+		return last_err;
 	}
 
 	if (orig_iface) {
@@ -500,21 +543,15 @@ int netlink_print_common(char *mesh_iface, char *orig_iface, int read_opt,
 			fprintf(stderr, "Interface %s is unknown\n",
 				orig_iface);
 			last_err = -ENODEV;
-			goto err_free_sock;
+			return last_err;
 		}
 	}
 
-	cb = nl_cb_alloc(NL_CB_DEFAULT);
-	if (!cb) {
-		last_err = -ENOMEM;
-		goto err_free_sock;
-	}
-
 	bat_hosts_init(read_opt);
 
-	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, netlink_print_common_cb, &opts);
-	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, stop_callback, NULL);
-	nl_cb_err(cb, NL_CB_CUSTOM, print_error, NULL);
+	nl_cb_set(state->cb, NL_CB_VALID, NL_CB_CUSTOM, netlink_print_common_cb, &opts);
+	nl_cb_set(state->cb, NL_CB_FINISH, NL_CB_CUSTOM, stop_callback, NULL);
+	nl_cb_err(state->cb, NL_CB_CUSTOM, print_error, NULL);
 
 	do {
 		if (read_opt & CLR_CONT_READ)
@@ -530,20 +567,20 @@ int netlink_print_common(char *mesh_iface, char *orig_iface, int read_opt,
 		if (!msg)
 			continue;
 
-		genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0,
-			    NLM_F_DUMP, nl_cmd, 1);
+		genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, state->batadv_family,
+			    0, NLM_F_DUMP, nl_cmd, 1);
 
 		nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex);
 		if (hardifindex)
 			nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
 				    hardifindex);
 
-		nl_send_auto_complete(sock, msg);
+		nl_send_auto_complete(state->sock, msg);
 
 		nlmsg_free(msg);
 
 		last_err = 0;
-		nl_recvmsgs(sock, cb);
+		nl_recvmsgs(state->sock, state->cb);
 
 		/* the header should still be printed when no entry was received */
 		if (!last_err)
@@ -556,9 +593,6 @@ int netlink_print_common(char *mesh_iface, char *orig_iface, int read_opt,
 
 	bat_hosts_free();
 
-err_free_sock:
-	nl_socket_free(sock);
-
 	return last_err;
 }
 
diff --git a/netlink.h b/netlink.h
index 0b21ac1..2526b07 100644
--- a/netlink.h
+++ b/netlink.h
@@ -27,6 +27,8 @@
 #include <netlink/genl/ctrl.h>
 #include <stdint.h>
 
+struct state;
+
 struct print_opts {
 	int read_opt;
 	float orig_timeout;
@@ -39,6 +41,9 @@ struct print_opts {
 
 struct ether_addr;
 
+int netlink_create(struct state *state);
+void netlink_destroy(struct state *state);
+
 int netlink_print_routing_algos(void);
 
 char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header);
@@ -52,7 +57,7 @@ extern struct nla_policy batadv_netlink_policy[];
 
 int missing_mandatory_attrs(struct nlattr *attrs[], const int mandatory[],
 			    int num);
-int netlink_print_common(char *mesh_iface, char *orig_iface, int read_opt,
+int netlink_print_common(struct state *state, char *orig_iface, int read_opt,
 			 float orig_timeout, float watch_interval,
 			 const char *header, uint8_t nl_cmd,
 			 nl_recvmsg_msg_cb_t callback);
diff --git a/originators.c b/originators.c
index fd2aa66..a9c0606 100644
--- a/originators.c
+++ b/originators.c
@@ -177,7 +177,7 @@ static int originators_callback(struct nl_msg *msg, void *arg)
 	return NL_OK;
 }
 
-static int netlink_print_originators(char *mesh_iface, char *orig_iface,
+static int netlink_print_originators(struct state *state, char *orig_iface,
 				     int read_opts, float orig_timeout,
 				     float watch_interval)
 {
@@ -185,9 +185,9 @@ static int netlink_print_originators(char *mesh_iface, char *orig_iface,
 	char *info_header;
 	int ifindex;
 
-	ifindex = if_nametoindex(mesh_iface);
+	ifindex = if_nametoindex(state->mesh_iface);
 	if (!ifindex) {
-		fprintf(stderr, "Interface %s is unknown\n", mesh_iface);
+		fprintf(stderr, "Interface %s is unknown\n", state->mesh_iface);
 		return -ENODEV;
 	}
 
@@ -207,7 +207,7 @@ static int netlink_print_originators(char *mesh_iface, char *orig_iface,
 	if (!header)
 		return -EINVAL;
 
-	return netlink_print_common(mesh_iface, orig_iface, read_opts,
+	return netlink_print_common(state, orig_iface, read_opts,
 				    orig_timeout, watch_interval, header,
 				    BATADV_CMD_GET_ORIGINATORS,
 				    originators_callback);
@@ -222,4 +222,5 @@ static struct debug_table_data batctl_debug_table_originators = {
 };
 
 COMMAND_NAMED(DEBUGTABLE, originators, "o", handle_debug_table,
-	      COMMAND_FLAG_MESH_IFACE, &batctl_debug_table_originators, "");
+	      COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+	      &batctl_debug_table_originators, "");
diff --git a/transglobal.c b/transglobal.c
index f67682a..da2d687 100644
--- a/transglobal.c
+++ b/transglobal.c
@@ -136,11 +136,11 @@ static int transglobal_callback(struct nl_msg *msg, void *arg)
 	return NL_OK;
 }
 
-static int netlink_print_transglobal(char *mesh_iface, char *orig_iface,
+static int netlink_print_transglobal(struct state *state, char *orig_iface,
 				     int read_opts, float orig_timeout,
 				     float watch_interval)
 {
-	return netlink_print_common(mesh_iface, orig_iface, read_opts,
+	return netlink_print_common(state, orig_iface, read_opts,
 				    orig_timeout, watch_interval,
 				    "   Client             VID Flags Last ttvn     Via        ttvn  (CRC       )\n",
 				    BATADV_CMD_GET_TRANSTABLE_GLOBAL,
@@ -156,4 +156,5 @@ static struct debug_table_data batctl_debug_table_transglobal = {
 };
 
 COMMAND_NAMED(DEBUGTABLE, transglobal, "tg", handle_debug_table,
-	      COMMAND_FLAG_MESH_IFACE, &batctl_debug_table_transglobal, "");
+	      COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+	      &batctl_debug_table_transglobal, "");
diff --git a/translocal.c b/translocal.c
index 8b4cd59..a03b797 100644
--- a/translocal.c
+++ b/translocal.c
@@ -132,11 +132,11 @@ static int translocal_callback(struct nl_msg *msg, void *arg)
 	return NL_OK;
 }
 
-static int netlink_print_translocal(char *mesh_iface, char *orig_iface,
+static int netlink_print_translocal(struct state *state, char *orig_iface,
 				    int read_opts, float orig_timeout,
 				    float watch_interval)
 {
-	return netlink_print_common(mesh_iface, orig_iface, read_opts,
+	return netlink_print_common(state, orig_iface, read_opts,
 				    orig_timeout, watch_interval,
 				    "Client             VID Flags    Last seen (CRC       )\n",
 				    BATADV_CMD_GET_TRANSTABLE_LOCAL,
@@ -152,4 +152,5 @@ static struct debug_table_data batctl_debug_table_translocal = {
 };
 
 COMMAND_NAMED(DEBUGTABLE, translocal, "tl", handle_debug_table,
-	      COMMAND_FLAG_MESH_IFACE, &batctl_debug_table_translocal, "");
+	      COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+	      &batctl_debug_table_translocal, "");
