@@ -57,6 +57,7 @@ $(eval $(call add_command,bisect_iv,$(CONFIG_BATCTL_BISECT)))
$(eval $(call add_command,bonding,y))
$(eval $(call add_command,bridge_loop_avoidance,y))
$(eval $(call add_command,claimtable,y))
+$(eval $(call add_command,config,n))
$(eval $(call add_command,dat_cache,y))
$(eval $(call add_command,distributed_arp_table,y))
$(eval $(call add_command,event,y))
@@ -27,6 +27,7 @@
#define BATADV_NL_NAME "batadv"
+#define BATADV_NL_MCAST_GROUP_CONFIG "config"
#define BATADV_NL_MCAST_GROUP_TPMETER "tpmeter"
/**
@@ -344,6 +345,26 @@ enum batadv_nl_attrs {
*/
BATADV_ATTR_MCAST_FLAGS_PRIV,
+ /**
+ * @BATADV_ATTR_VLANID: VLAN id on top of soft interface
+ */
+ BATADV_ATTR_VLANID,
+
+ /**
+ * @BATADV_ATTR_OPTION_NAME: name of option to modify
+ */
+ BATADV_ATTR_OPTION_NAME,
+
+ /**
+ * @BATADV_ATTR_OPTION_TYPE: type of @BATADV_ATTR_OPTION_VALUE
+ */
+ BATADV_ATTR_OPTION_TYPE,
+
+ /**
+ * @BATADV_ATTR_OPTION_VALUE: value set for @BATADV_ATTR_OPTION_NAME
+ */
+ BATADV_ATTR_OPTION_VALUE,
+
/* add attributes above here, update the policy in netlink.c */
/**
@@ -443,6 +464,40 @@ enum batadv_nl_commands {
*/
BATADV_CMD_GET_MCAST_FLAGS,
+ /**
+ * @BATADV_CMD_GET_OPTION: Get option(s) from softif
+ */
+ BATADV_CMD_GET_OPTION,
+
+ /**
+ * @BATADV_CMD_SET_OPTION: Set option for softif
+ */
+ BATADV_CMD_SET_OPTION,
+
+ /**
+ * @BATADV_CMD_GET_OPTION_HARDIF: Get option(s) from a hardif of the
+ * current softif
+ */
+ BATADV_CMD_GET_OPTION_HARDIF,
+
+ /**
+ * @BATADV_CMD_SET_OPTION_HARDIF: Set option for hardif of the
+ * current softif
+ */
+ BATADV_CMD_SET_OPTION_HARDIF,
+
+ /**
+ * @BATADV_CMD_GET_OPTION_VLAN: Get option(s) from a VLAN of the
+ * current softif
+ */
+ BATADV_CMD_GET_OPTION_VLAN,
+
+ /**
+ * @BATADV_CMD_SET_OPTION_VLAN: Set option for VLAN of the
+ * current softif
+ */
+ BATADV_CMD_SET_OPTION_VLAN,
+
/* add new commands above here */
/**
new file mode 100644
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2007-2018 B.A.T.M.A.N. contributors:
+ *
+ * Sven Eckelmann <sven@narfation.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ * License-Filename: LICENSES/preferred/GPL-2.0
+ */
+
+#include "main.h"
+#include "functions.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "batadv_packet.h"
+#include "batman_adv.h"
+#include "bat-hosts.h"
+#include "debug.h"
+#include "functions.h"
+#include "main.h"
+#include "netlink.h"
+
+enum config_mode {
+ CONFIG_MODE_MESH,
+ CONFIG_MODE_HARDIF,
+ CONFIG_MODE_VLAN,
+};
+
+static void config_usage(void)
+{
+ fprintf(stderr, "Usage: batctl [options] config [parameters] [name [type value]]\n");
+ fprintf(stderr, "parameters:\n");
+ fprintf(stderr, " \t -H,--hardif hardif to modify\n");
+ fprintf(stderr, " \t -V,--vlan vlan to modify\n");
+}
+
+static int config_callback(struct nl_msg *msg, void *arg __maybe_unused)
+{
+ static const int config_mandatory[] = {
+ BATADV_ATTR_OPTION_NAME,
+ BATADV_ATTR_OPTION_TYPE,
+ /* BATADV_ATTR_OPTION_VALUE, */
+ };
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct nlattr *attrs[NUM_BATADV_ATTR];
+ struct genlmsghdr *ghdr;
+ const char *name;
+ uint8_t type;
+
+ if (!genlmsg_valid_hdr(nlh, 0))
+ return NL_OK;
+
+ ghdr = nlmsg_data(nlh);
+
+ if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
+ genlmsg_len(ghdr), batadv_netlink_policy)) {
+ fputs("Received invalid data from kernel.\n", stderr);
+ return NL_OK;
+ }
+
+ /* ignore entry when attributes are missing */
+ if (missing_mandatory_attrs(attrs, config_mandatory,
+ ARRAY_SIZE(config_mandatory)))
+ return NL_OK;
+
+ name = nla_get_string(attrs[BATADV_ATTR_OPTION_NAME]);
+ type = nla_get_u8(attrs[BATADV_ATTR_OPTION_TYPE]);
+
+ if (type != NLA_FLAG && !attrs[BATADV_ATTR_OPTION_VALUE])
+ return NL_OK;
+
+ switch (type) {
+ case NLA_U8:
+ printf("%s u8 %u\n", name,
+ nla_get_u8(attrs[BATADV_ATTR_OPTION_VALUE]));
+ break;
+ case NLA_U16:
+ printf("%s u16 %u\n", name,
+ nla_get_u16(attrs[BATADV_ATTR_OPTION_VALUE]));
+ break;
+ case NLA_U32:
+ printf("%s u32 %u\n", name,
+ nla_get_u32(attrs[BATADV_ATTR_OPTION_VALUE]));
+ break;
+ case NLA_NUL_STRING:
+ printf("%s string %s\n", name,
+ nla_get_string(attrs[BATADV_ATTR_OPTION_VALUE]));
+ break;
+ case NLA_FLAG:
+ printf("%s bool %s\n", name,
+ attrs[BATADV_ATTR_OPTION_VALUE] ? "true" : "false");
+ break;
+ }
+
+ return NL_OK;
+}
+
+static int nl_error_cb(struct sockaddr_nl *nla __maybe_unused,
+ struct nlmsgerr *nlerr, void *arg)
+{
+ int *ret = arg;
+
+ *ret = nlerr->error;
+
+ return NL_STOP;
+}
+
+static int nl_stop_cb(struct nl_msg *msg, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ int *ret = arg;
+ int *error = nlmsg_data(nlh);
+
+ if (*error)
+ *ret = *error;
+
+ return NL_STOP;
+}
+
+static int config_nl_request(struct state *state, int ifindex,
+ uint8_t nl_cmd, nl_recvmsg_msg_cb_t callback,
+ int flags, enum config_mode config_mode,
+ unsigned int hardif_index,
+ uint16_t vlan_id, const char *option, int type,
+ const void *val)
+{
+ struct nl_msg *msg;
+ void *user_hdr;
+ int ret;
+ int res;
+
+ ret = 0;
+
+ if (!state->sock)
+ return -EOPNOTSUPP;
+
+ if (callback)
+ nl_cb_set(state->cb, NL_CB_VALID, NL_CB_CUSTOM, config_callback, &ret);
+ else
+ nl_cb_set(state->cb, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL);
+
+ nl_cb_set(state->cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_stop_cb, &ret);
+ nl_cb_err(state->cb, NL_CB_CUSTOM, nl_error_cb, &ret);
+
+ msg = nlmsg_alloc();
+ if (!msg) {
+ fprintf(stderr, "Error - could not allocate netlink message\n");
+ return EXIT_FAILURE;
+ }
+
+ user_hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ,
+ state->batadv_family, 0, flags, nl_cmd, 1);
+ if (!user_hdr) {
+ fprintf(stderr, "Error - failed to store genl header in netlink message\n");
+ goto err_free_msg;
+ }
+
+ ret = nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex);
+ if (ret < 0) {
+ fprintf(stderr, "Error - failed to store mesh ifindex in netlink message\n");
+ goto err_free_msg;
+ }
+
+ if (config_mode == CONFIG_MODE_HARDIF) {
+ ret = nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX, hardif_index);
+ if (ret < 0) {
+ fprintf(stderr, "Error - failed to store hard ifindex in netlink message\n");
+ goto err_free_msg;
+ }
+ }
+
+ if (config_mode == CONFIG_MODE_VLAN) {
+ ret = nla_put_u16(msg, BATADV_ATTR_VLANID, vlan_id);
+ if (ret < 0) {
+ fprintf(stderr, "Error - failed to store vlan id in netlink message\n");
+ goto err_free_msg;
+ }
+ }
+
+ if (option) {
+ ret = nla_put_string(msg, BATADV_ATTR_OPTION_NAME, option);
+ if (ret < 0) {
+ fprintf(stderr, "Error - failed to store option name in netlink message\n");
+ goto err_free_msg;
+ }
+ }
+
+ if (val) {
+ switch (type) {
+ case NLA_NUL_STRING:
+ ret = nla_put_string(msg, BATADV_ATTR_OPTION_VALUE, val);
+ break;
+ case NLA_U32:
+ ret = nla_put_u32(msg, BATADV_ATTR_OPTION_VALUE,
+ *(uint32_t *)val);
+ break;
+ case NLA_U16:
+ ret = nla_put_u16(msg, BATADV_ATTR_OPTION_VALUE,
+ *(uint16_t *)val);
+ break;
+ case NLA_U8:
+ ret = nla_put_u8(msg, BATADV_ATTR_OPTION_VALUE,
+ *(uint8_t *)val);
+ break;
+ case NLA_FLAG:
+ if (*(bool *)val)
+ ret = nla_put_flag(msg,
+ BATADV_ATTR_OPTION_VALUE);
+ else
+ ret = 0;
+ break;
+ default:
+ fprintf(stderr, "Error - unsupported type for option\n");
+ ret = -EINVAL;
+ goto err_free_msg;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "Error - failed to store option value in netlink message\n");
+ goto err_free_msg;
+ }
+
+ ret = nla_put_u8(msg, BATADV_ATTR_OPTION_TYPE, type);
+ if (ret < 0) {
+ fprintf(stderr, "Error - failed to store option type in netlink message\n");
+ goto err_free_msg;
+ }
+ }
+
+ res = nl_send_auto_complete(state->sock, msg);
+ nlmsg_free(msg);
+
+ if (res < 0) {
+ fprintf(stderr, "Error - failed to send message: %s\n",
+ strerror(-res));
+ return res;
+ }
+
+ res = nl_recvmsgs(state->sock, state->cb);
+ if (res < 0 && res != -ECHILD) {
+ fprintf(stderr, "Error - failed to receive message: %s\n",
+ strerror(-res));
+ return res;
+ }
+
+ if (ret < 0) {
+ fprintf(stderr, "Error - failure while processing config request: %s\n",
+ strerror(-ret));
+ return res;
+ }
+
+ return ret;
+
+err_free_msg:
+ nlmsg_free(msg);
+ return ret;
+}
+
+static int config_dump(struct state *state, int ifindex,
+ enum config_mode config_mode, unsigned int hardif_index,
+ uint16_t vlan_id)
+{
+ enum batadv_nl_commands cmd;
+ int ret;
+
+ switch (config_mode) {
+ case CONFIG_MODE_MESH:
+ cmd = BATADV_CMD_GET_OPTION;
+ break;
+ case CONFIG_MODE_HARDIF:
+ cmd = BATADV_CMD_GET_OPTION_HARDIF;
+ break;
+ case CONFIG_MODE_VLAN:
+ cmd = BATADV_CMD_GET_OPTION_VLAN;
+ break;
+ }
+
+ ret = config_nl_request(state, ifindex, cmd, config_callback,
+ NLM_F_DUMP, config_mode, hardif_index, vlan_id,
+ NULL, NLA_UNSPEC, NULL);
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+
+static int config_get_option(struct state *state, int ifindex,
+ enum config_mode config_mode,
+ unsigned int hardif_index, uint16_t vlan_id,
+ const char *option)
+{
+ enum batadv_nl_commands cmd;
+ int ret;
+
+ switch (config_mode) {
+ case CONFIG_MODE_MESH:
+ cmd = BATADV_CMD_GET_OPTION;
+ break;
+ case CONFIG_MODE_HARDIF:
+ cmd = BATADV_CMD_GET_OPTION_HARDIF;
+ break;
+ case CONFIG_MODE_VLAN:
+ cmd = BATADV_CMD_GET_OPTION_VLAN;
+ break;
+ }
+
+ ret = config_nl_request(state, ifindex, cmd, config_callback, 0,
+ config_mode, hardif_index, vlan_id, option,
+ NLA_UNSPEC, NULL);
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+
+static int config_set_option(struct state *state, int ifindex,
+ enum config_mode config_mode,
+ unsigned int hardif_index, uint16_t vlan_id,
+ const char *option, const char *type,
+ const char *value)
+{
+ enum batadv_nl_commands cmd;
+ unsigned long int val_ul;
+ const void *val;
+ uint32_t val_u32;
+ uint16_t val_u16;
+ uint8_t val_u8;
+ bool val_bool;
+ char *endptr;
+ int nl_type;
+ int ret;
+
+ switch (config_mode) {
+ case CONFIG_MODE_MESH:
+ cmd = BATADV_CMD_SET_OPTION;
+ break;
+ case CONFIG_MODE_HARDIF:
+ cmd = BATADV_CMD_SET_OPTION_HARDIF;
+ break;
+ case CONFIG_MODE_VLAN:
+ cmd = BATADV_CMD_SET_OPTION_VLAN;
+ break;
+ }
+
+ if (strcmp(type, "u32") == 0) {
+ nl_type = NLA_U32;
+ } else if (strcmp(type, "u16") == 0) {
+ nl_type = NLA_U16;
+ } else if (strcmp(type, "u8") == 0) {
+ nl_type = NLA_U8;
+ } else if (strcmp(type, "bool") == 0) {
+ nl_type = NLA_FLAG;
+ } else if (strcmp(type, "string") == 0) {
+ nl_type = NLA_NUL_STRING;
+ } else {
+ fprintf(stderr, "Error - unsupported type %s\n", type);
+ return EXIT_FAILURE;
+ }
+
+ switch (nl_type) {
+ case NLA_U32:
+ case NLA_U16:
+ case NLA_U8:
+ val_ul = strtoul(value, &endptr, 0);
+ if (value[0] == '\0' || endptr[0] != '\0') {
+ fprintf(stderr, "Error - failed to parse value as integer\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ }
+
+ switch (nl_type) {
+ case NLA_U32:
+ if (val_ul > UINT32_MAX) {
+ fprintf(stderr, "Error - value too large for u32\n");
+ return EXIT_FAILURE;
+ }
+
+ val_u32 = val_ul;
+ val = &val_u32;
+ break;
+ case NLA_U16:
+ if (val_ul > UINT16_MAX) {
+ fprintf(stderr, "Error - value too large for u16\n");
+ return EXIT_FAILURE;
+ }
+
+ val_u16 = val_ul;
+ val = &val_u16;
+ break;
+ case NLA_U8:
+ if (val_ul > UINT8_MAX) {
+ fprintf(stderr, "Error - value too large for u8\n");
+ return EXIT_FAILURE;
+ }
+
+ val_u8 = val_ul;
+ val = &val_u8;
+ break;
+ case NLA_FLAG:
+ if (strcmp(value, "true") == 0) {
+ val_bool = true;
+ } else if (strcmp(value, "false") == 0) {
+ val_bool = false;
+ } else {
+ fprintf(stderr, "Error - unsupported boolean value %s\n", value);
+ return EXIT_FAILURE;
+ }
+ val = &val_bool;
+ break;
+ case NLA_NUL_STRING:
+ val = value;
+ break;
+ }
+
+ ret = config_nl_request(state, ifindex, cmd, NULL, 0,
+ config_mode, hardif_index, vlan_id, option,
+ nl_type, val);
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+
+static int config(struct state *state, int argc, char **argv)
+{
+ static const struct option config_options[] = {
+ { "hardif", required_argument, NULL, 'H' },
+ { "vlan", required_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ enum config_mode config_mode = CONFIG_MODE_MESH;
+ unsigned int hardif_index = 0;
+ unsigned long int vlan;
+ uint16_t vlan_id = 0;
+ const char *option;
+ const char *value;
+ const char *type;
+ char *endptr;
+ int ifindex;
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "+H:V:", config_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'H':
+ if (config_mode != CONFIG_MODE_MESH) {
+ fprintf(stderr, "Error - multiple targets specified\n");
+ return EXIT_FAILURE;
+ }
+
+ config_mode = CONFIG_MODE_HARDIF;
+ hardif_index = if_nametoindex(optarg);
+ if (hardif_index == 0) {
+ perror("Error - unable to get index for interface");
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'V':
+ if (config_mode != CONFIG_MODE_MESH) {
+ fprintf(stderr, "Error - multiple targets specified\n");
+ return EXIT_FAILURE;
+ }
+
+ config_mode = CONFIG_MODE_VLAN;
+ vlan = strtoul(optarg, &endptr, 0);
+ if (optarg[0] == '\0' || endptr[0] != '\0') {
+ fprintf(stderr, "Error - failed to parse vlan id\n");
+ return EXIT_FAILURE;
+ }
+
+ if (vlan > 4095) {
+ fprintf(stderr, "Error - invalid vlan id\n");
+ return EXIT_FAILURE;
+ }
+
+ vlan_id = vlan;
+ break;
+ default:
+ config_usage();
+ return EXIT_FAILURE;
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+ optind = 0;
+
+ ifindex = if_nametoindex(state->mesh_iface);
+ if (!ifindex) {
+ perror("Error - failed to retrieve mesh interface");
+ return EXIT_FAILURE;
+ }
+
+ if (argc <= 0)
+ return config_dump(state, ifindex, config_mode, hardif_index,
+ vlan_id);
+
+ option = argv[0];
+ if (argc == 1)
+ return config_get_option(state, ifindex, config_mode,
+ hardif_index, vlan_id, option);
+
+ if (argc != 3) {
+ config_usage();
+ return EXIT_FAILURE;
+ }
+
+ type = argv[1];
+ value = argv[2];
+
+ return config_set_option(state, ifindex, config_mode, hardif_index,
+ vlan_id, option, type, value);
+}
+
+COMMAND(SUBCOMMAND, config, "c",
+ COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK, NULL,
+ "<selector> \treceive or set config option");
@@ -338,6 +338,43 @@ specific sequence number, min. Furthermore using "\-o" you can filter the output
given batctl will not replace the MAC addresses with bat\-host names in the output.
.RE
.br
+.IP "\fBconfig\fP [\fB\-H hardif\fP][\fB\-V VLANID\fP] \fBoption\fP [\fBtype\fP \fBvalue\fP]"
+Gets or sets raw configuration options in the batman-adv module for either the
+batadv soft interface, a hard (slave) interface or a VLAN of the batadv soft
+interface. Providing no option name will dump all the configuration options
+for the selected softif/hardif/VLAN. Providing only the option name will
+retrieve the configuration configuration value. Providing a full
+option\-type\-value tripplet will set the value in the kernel. The supported
+types are u32 (0-4294967295), u16 (0-65535), u8 (0-255), bool (true/false) and
+string.
+For example:
+.sp
+.if n \{\
+.RS 10
+.\}
+.nf
+# retrieve all configuration options for the soft interface
+batctl config
+# retrieve all configuration options for the hard interface
+batctl config -H eth0
+# retrieve all configuration options for the VLANID 0
+batctl config -V 0
+
+# retrieve throughput_override for hard interface eth0
+batctl config -H eth0 throughput_override u32
+
+# set fragmentation for bat0
+batctl config fragmentation bool false
+
+# set orig_interval for bat0
+batctl config orig_interval u32 5000
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.PP
+.br
.IP "\fBthroughputmeter\fP|\fBtp\fP \fBMAC\fP"
This command starts a throughput test entirely controlled by batman module in
kernel space: the computational resources needed to align memory and copy data
@@ -107,6 +107,9 @@ struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
[BATADV_ATTR_DAT_CACHE_VID] = { .type = NLA_U16 },
[BATADV_ATTR_MCAST_FLAGS] = { .type = NLA_U32 },
[BATADV_ATTR_MCAST_FLAGS_PRIV] = { .type = NLA_U32 },
+ [BATADV_ATTR_VLANID] = { .type = NLA_U16 },
+ [BATADV_ATTR_OPTION_NAME] = { .type = NLA_NUL_STRING },
+ [BATADV_ATTR_OPTION_TYPE] = { .type = NLA_U8 },
};
int netlink_create(struct state *state)