Repository : ssh://git@open-mesh.org/batctl
On branch : master
>---------------------------------------------------------------
commit 74b6d3bd77630c4e86bd4ce1e7fa6e01cb87af14
Author: Sven Eckelmann <sven(a)narfation.org>
Date: Sat Feb 9 14:42:07 2019 +0100
batctl: Parse the arguments for gw_mode
The generic netlink interface requires that the configuration settings are
parsed in userspace. But it can also be used to do a more fine grained
validation of the input data before it is written to the sysfs files.
Signed-off-by: Sven Eckelmann <sven(a)narfation.org>
>---------------------------------------------------------------
74b6d3bd77630c4e86bd4ce1e7fa6e01cb87af14
functions.c | 92 +++++++++++++++++++++++++++++++
functions.h | 16 ++++++
gw_mode.c | 171 +++++++++++++++++++++++++++++++++++++++++++++------------
netlink.c | 76 +++++++++++++++++++++++++
netlink.h | 2 +
routing_algo.c | 1 -
sys.c | 7 ---
sys.h | 2 +-
8 files changed, 324 insertions(+), 43 deletions(-)
diff --git a/functions.c b/functions.c
index 953dcf9..9fbd548 100644
--- a/functions.c
+++ b/functions.c
@@ -467,6 +467,44 @@ struct ether_addr *translate_mac(const char *mesh_iface,
return mac_result;
}
+int get_algoname(const char *mesh_iface, char *algoname, size_t algoname_len)
+{
+ char *path_buff;
+ int ret;
+
+ ret = get_algoname_netlink(mesh_iface, algoname, algoname_len);
+ if (ret != -EOPNOTSUPP)
+ return ret;
+
+ path_buff = malloc(PATH_BUFF_LEN);
+ if (!path_buff) {
+ fprintf(stderr, "Error - could not allocate path buffer: out of memory ?\n");
+ return -ENOMEM;
+ }
+
+ snprintf(path_buff, PATH_BUFF_LEN, SYS_ROUTING_ALGO_FMT, mesh_iface);
+ ret = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0);
+ if (ret != EXIT_SUCCESS) {
+ ret = -ENOENT;
+ goto free_path_buf;
+ }
+
+ if (line_ptr[strlen(line_ptr) - 1] == '\n')
+ line_ptr[strlen(line_ptr) - 1] = '\0';
+
+ strncpy(algoname, line_ptr, algoname_len);
+ if (algoname_len > 0)
+ algoname[algoname_len - 1] = '\0';
+
+free_path_buf:
+ free(path_buff);
+
+ free(line_ptr);
+ line_ptr = NULL;
+
+ return ret;
+}
+
static int resolve_l3addr(int ai_family, const char *asc, void *l3addr)
{
int ret;
@@ -1130,3 +1168,57 @@ void check_root_or_die(const char *cmd)
exit(EXIT_FAILURE);
}
}
+
+bool parse_throughput(char *buff, const char *description, uint32_t *throughput)
+{
+ enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT;
+ uint64_t lthroughput;
+ char *tmp_ptr;
+ char *endptr;
+
+ if (strlen(buff) > 4) {
+ tmp_ptr = buff + strlen(buff) - 4;
+
+ if (strncasecmp(tmp_ptr, "mbit", 4) == 0)
+ bw_unit_type = BATADV_BW_UNIT_MBIT;
+
+ if (strncasecmp(tmp_ptr, "kbit", 4) == 0 ||
+ bw_unit_type == BATADV_BW_UNIT_MBIT)
+ *tmp_ptr = '\0';
+ }
+
+ lthroughput = strtoull(buff, &endptr, 10);
+ if (!endptr || *endptr != '\0') {
+ fprintf(stderr, "Invalid throughput speed for %s: %s\n",
+ description, buff);
+ return false;
+ }
+
+ switch (bw_unit_type) {
+ case BATADV_BW_UNIT_MBIT:
+ /* prevent overflow */
+ if (UINT64_MAX / 10 < lthroughput) {
+ fprintf(stderr,
+ "Throughput speed for %s too large: %s\n",
+ description, buff);
+ return false;
+ }
+
+ lthroughput *= 10;
+ break;
+ case BATADV_BW_UNIT_KBIT:
+ default:
+ lthroughput = lthroughput / 100;
+ break;
+ }
+
+ if (lthroughput > UINT32_MAX) {
+ fprintf(stderr, "Throughput speed for %s too large: %s\n",
+ description, buff);
+ return false;
+ }
+
+ *throughput = lthroughput;
+
+ return true;
+}
diff --git a/functions.h b/functions.h
index fd32d70..bffabc6 100644
--- a/functions.h
+++ b/functions.h
@@ -26,8 +26,20 @@
#include <net/ethernet.h>
#include <netlink/netlink.h>
#include <netlink/handlers.h>
+#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
+/**
+ * enum batadv_bandwidth_units - bandwidth unit types
+ */
+enum batadv_bandwidth_units {
+ /** @BATADV_BW_UNIT_KBIT: unit type kbit */
+ BATADV_BW_UNIT_KBIT,
+
+ /** @BATADV_BW_UNIT_MBIT: unit type mbit */
+ BATADV_BW_UNIT_MBIT,
+};
#define ETH_STR_LEN 17
#define BATMAN_ADV_TAG "batman-adv:"
@@ -53,12 +65,16 @@ struct ether_addr *resolve_mac(const char *asc);
int query_rtnl_link(int ifindex, nl_recvmsg_msg_cb_t func, void *arg);
int netlink_simple_request(struct nl_msg *msg);
int translate_mesh_iface(struct state *state);
+int get_algoname(const char *mesh_iface, char *algoname, size_t algoname_len);
int check_mesh_iface(struct state *state);
int check_mesh_iface_ownership(char *mesh_iface, char *hard_iface);
void get_random_bytes(void *buf, size_t buflen);
void check_root_or_die(const char *cmd);
+bool parse_throughput(char *buff, const char *description,
+ uint32_t *throughput);
+
extern char *line_ptr;
enum {
diff --git a/gw_mode.c b/gw_mode.c
index b949970..d671fab 100644
--- a/gw_mode.c
+++ b/gw_mode.c
@@ -20,23 +20,33 @@
* License-Filename: LICENSES/preferred/GPL-2.0
*/
+#include <errno.h>
#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "batman_adv.h"
#include "functions.h"
+#include "main.h"
+#include "netlink.h"
#include "sys.h"
#define SYS_GW_MODE "gw_mode"
#define SYS_GW_SEL "gw_sel_class"
#define SYS_GW_BW "gw_bandwidth"
-enum gw_modes {
- GW_MODE_OFF,
- GW_MODE_CLIENT,
- GW_MODE_SERVER,
-};
+static struct gw_data {
+ uint8_t bandwidth_down_found:1;
+ uint8_t bandwidth_up_found:1;
+ uint8_t sel_class_found:1;
+ uint8_t mode;
+ uint32_t bandwidth_down;
+ uint32_t bandwidth_up;
+ uint32_t sel_class;
+} gw_globals;
static void gw_mode_usage(void)
{
@@ -45,11 +55,117 @@ static void gw_mode_usage(void)
fprintf(stderr, " \t -h print this help\n");
}
+static bool is_throughput_select_class(struct state *state)
+{
+ char algoname[32];
+ int ret;
+
+ ret = get_algoname(state->mesh_iface, algoname, sizeof(algoname));
+
+ /* no algo name -> assume that it is a pre-B.A.T.M.A.N. V version */
+ if (ret < 0)
+ return false;
+
+ if (strcmp(algoname, "BATMAN_V") == 0)
+ return true;
+
+ return false;
+}
+static int parse_gw_limit(char *buff)
+{
+ char *slash_ptr;
+ bool ret;
+
+ slash_ptr = strchr(buff, '/');
+ if (slash_ptr)
+ *slash_ptr = 0;
+
+ ret = parse_throughput(buff, "download gateway speed",
+ &gw_globals.bandwidth_down);
+ if (!ret)
+ return -EINVAL;
+
+ gw_globals.bandwidth_down_found = 1;
+
+ /* we also got some upload info */
+ if (slash_ptr) {
+ ret = parse_throughput(slash_ptr + 1, "upload gateway speed",
+ &gw_globals.bandwidth_up);
+ if (!ret)
+ return -EINVAL;
+
+ gw_globals.bandwidth_up_found = 1;
+ }
+
+ return 0;
+}
+
+static int parse_gw(struct state *state, int argc, char *argv[])
+{
+ char buff[256];
+ char *endptr;
+ int ret;
+
+ if (argc != 2 && argc != 3) {
+ fprintf(stderr, "Error - incorrect number of arguments (expected 1/2)\n");
+ return -EINVAL;
+ }
+
+ if (strcmp(argv[1], "client") == 0) {
+ gw_globals.mode = BATADV_GW_MODE_CLIENT;
+ } else if (strcmp(argv[1], "server") == 0) {
+ gw_globals.mode = BATADV_GW_MODE_SERVER;
+ } else if (strcmp(argv[1], "off") == 0) {
+ gw_globals.mode = BATADV_GW_MODE_OFF;
+ } else {
+ fprintf(stderr, "Error - the supplied argument is invalid: %s\n", argv[1]);
+ fprintf(stderr, "The following values are allowed:\n");
+ fprintf(stderr, " * off\n");
+ fprintf(stderr, " * client\n");
+ fprintf(stderr, " * server\n");
+
+ return -EINVAL;
+ }
+
+ if (argc <= 2)
+ return 0;
+
+ strncpy(buff, argv[2], sizeof(buff));
+ buff[sizeof(buff) - 1] = '\0';
+
+ switch (gw_globals.mode) {
+ case BATADV_GW_MODE_OFF:
+ fprintf(stderr, "Error - unexpected argument for mode \"off\": %s\n", argv[2]);
+ return -EINVAL;
+ case BATADV_GW_MODE_CLIENT:
+ if (is_throughput_select_class(state)) {
+ if (!parse_throughput(buff, "sel_class",
+ &gw_globals.sel_class))
+ return -EINVAL;
+ } else {
+ gw_globals.sel_class = strtoul(buff, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ fprintf(stderr, "Error - unexpected argument for mode \"client\": %s\n", buff);
+ return -EINVAL;
+ }
+ }
+
+ gw_globals.sel_class_found = 1;
+ break;
+ case BATADV_GW_MODE_SERVER:
+ ret = parse_gw_limit(buff);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+
+ return 0;
+}
+
static int gw_mode(struct state *state, int argc, char **argv)
{
int optchar, res = EXIT_FAILURE;
char *path_buff, gw_mode;
- const char **ptr;
while ((optchar = getopt(argc, argv, "h")) != -1) {
switch (optchar) {
@@ -80,20 +196,20 @@ static int gw_mode(struct state *state, int argc, char **argv)
line_ptr[strlen(line_ptr) - 1] = '\0';
if (strcmp(line_ptr, "client") == 0)
- gw_mode = GW_MODE_CLIENT;
+ gw_mode = BATADV_GW_MODE_CLIENT;
else if (strcmp(line_ptr, "server") == 0)
- gw_mode = GW_MODE_SERVER;
+ gw_mode = BATADV_GW_MODE_SERVER;
else
- gw_mode = GW_MODE_OFF;
+ gw_mode = BATADV_GW_MODE_OFF;
free(line_ptr);
line_ptr = NULL;
switch (gw_mode) {
- case GW_MODE_CLIENT:
+ case BATADV_GW_MODE_CLIENT:
res = read_file(path_buff, SYS_GW_SEL, USE_READ_BUFF, 0, 0, 0);
break;
- case GW_MODE_SERVER:
+ case BATADV_GW_MODE_SERVER:
res = read_file(path_buff, SYS_GW_BW, USE_READ_BUFF, 0, 0, 0);
break;
default:
@@ -108,10 +224,10 @@ static int gw_mode(struct state *state, int argc, char **argv)
line_ptr[strlen(line_ptr) - 1] = '\0';
switch (gw_mode) {
- case GW_MODE_CLIENT:
+ case BATADV_GW_MODE_CLIENT:
printf("client (selection class: %s)\n", line_ptr);
break;
- case GW_MODE_SERVER:
+ case BATADV_GW_MODE_SERVER:
printf("server (announced bw: %s)\n", line_ptr);
break;
default:
@@ -125,14 +241,11 @@ static int gw_mode(struct state *state, int argc, char **argv)
check_root_or_die("batctl gw_mode");
- if (strcmp(argv[1], "client") == 0)
- gw_mode = GW_MODE_CLIENT;
- else if (strcmp(argv[1], "server") == 0)
- gw_mode = GW_MODE_SERVER;
- else if (strcmp(argv[1], "off") == 0)
- gw_mode = GW_MODE_OFF;
- else
- goto opt_err;
+ res = parse_gw(state, argc, argv);
+ if (res < 0) {
+ res = EXIT_FAILURE;
+ goto out;
+ }
res = write_file(path_buff, SYS_GW_MODE, argv[1], NULL);
if (res != EXIT_SUCCESS)
@@ -141,27 +254,17 @@ static int gw_mode(struct state *state, int argc, char **argv)
if (argc == 2)
goto out;
- switch (gw_mode) {
- case GW_MODE_CLIENT:
+ switch (gw_globals.mode) {
+ case BATADV_GW_MODE_CLIENT:
res = write_file(path_buff, SYS_GW_SEL, argv[2], NULL);
break;
- case GW_MODE_SERVER:
+ case BATADV_GW_MODE_SERVER:
res = write_file(path_buff, SYS_GW_BW, argv[2], NULL);
break;
}
goto out;
-opt_err:
- fprintf(stderr, "Error - the supplied argument is invalid: %s\n", argv[1]);
- fprintf(stderr, "The following values are allowed:\n");
-
- ptr = sysfs_param_server;
- while (*ptr) {
- fprintf(stderr, " * %s\n", *ptr);
- ptr++;
- }
-
out:
free(path_buff);
return res;
diff --git a/netlink.c b/netlink.c
index f4921df..5b9966c 100644
--- a/netlink.c
+++ b/netlink.c
@@ -853,3 +853,79 @@ int get_primarymac_netlink(const char *mesh_iface, uint8_t *primarymac)
return 0;
}
+
+struct get_algoname_netlink_opts {
+ char *algoname;
+ size_t algoname_len;
+ bool found;
+ struct nlquery_opts query_opts;
+};
+
+static int get_algoname_netlink_cb(struct nl_msg *msg, void *arg)
+{
+ static const int mandatory[] = {
+ BATADV_ATTR_ALGO_NAME,
+ };
+ struct nlattr *attrs[BATADV_ATTR_MAX + 1];
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct nlquery_opts *query_opts = arg;
+ struct get_algoname_netlink_opts *opts;
+ struct genlmsghdr *ghdr;
+ const char *algoname;
+
+ opts = container_of(query_opts, struct get_algoname_netlink_opts,
+ query_opts);
+
+ if (!genlmsg_valid_hdr(nlh, 0))
+ return NL_OK;
+
+ ghdr = nlmsg_data(nlh);
+
+ if (ghdr->cmd != BATADV_CMD_GET_MESH)
+ return NL_OK;
+
+ if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
+ genlmsg_len(ghdr), batadv_netlink_policy)) {
+ return NL_OK;
+ }
+
+ if (missing_mandatory_attrs(attrs, mandatory, ARRAY_SIZE(mandatory)))
+ return NL_OK;
+
+ algoname = nla_data(attrs[BATADV_ATTR_ALGO_NAME]);
+
+ /* save result */
+ strncpy(opts->algoname, algoname, opts->algoname_len);
+ if (opts->algoname_len > 0)
+ opts->algoname[opts->algoname_len - 1] = '\0';
+
+ opts->found = true;
+ opts->query_opts.err = 0;
+
+ return NL_STOP;
+}
+
+int get_algoname_netlink(const char *mesh_iface, char *algoname,
+ size_t algoname_len)
+{
+ struct get_algoname_netlink_opts opts = {
+ .algoname = algoname,
+ .algoname_len = algoname_len,
+ .found = false,
+ .query_opts = {
+ .err = 0,
+ },
+ };
+ int ret;
+
+ ret = netlink_query_common(mesh_iface, BATADV_CMD_GET_MESH,
+ get_algoname_netlink_cb, 0,
+ &opts.query_opts);
+ if (ret < 0)
+ return ret;
+
+ if (!opts.found)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
diff --git a/netlink.h b/netlink.h
index 6aec1a8..5766918 100644
--- a/netlink.h
+++ b/netlink.h
@@ -50,6 +50,8 @@ int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac,
int get_nexthop_netlink(const char *mesh_iface, const struct ether_addr *mac,
uint8_t *nexthop, char *ifname);
int get_primarymac_netlink(const char *mesh_iface, uint8_t *primarymac);
+int get_algoname_netlink(const char *mesh_iface, char *algoname,
+ size_t algoname_len);
extern struct nla_policy batadv_netlink_policy[];
diff --git a/routing_algo.c b/routing_algo.c
index 79116ef..4b31821 100644
--- a/routing_algo.c
+++ b/routing_algo.c
@@ -41,7 +41,6 @@
#include "sys.h"
#define SYS_SELECTED_RA_PATH "/sys/module/batman_adv/parameters/routing_algo"
-#define SYS_ROUTING_ALGO_FMT SYS_IFACE_PATH"/%s/mesh/routing_algo"
static void ra_mode_usage(void)
{
diff --git a/sys.c b/sys.c
index c408329..d4f1d6d 100644
--- a/sys.c
+++ b/sys.c
@@ -47,13 +47,6 @@ const char *sysfs_param_enable[] = {
NULL,
};
-const char *sysfs_param_server[] = {
- "off",
- "client",
- "server",
- NULL,
-};
-
static void settings_usage(struct state *state)
{
fprintf(stderr, "Usage: batctl [options] %s|%s [parameters] %s\n",
diff --git a/sys.h b/sys.h
index 008f750..f212789 100644
--- a/sys.h
+++ b/sys.h
@@ -31,6 +31,7 @@
#define SYS_MESH_IFACE_FMT SYS_IFACE_PATH"/%s/batman_adv/mesh_iface"
#define SYS_IFACE_STATUS_FMT SYS_IFACE_PATH"/%s/batman_adv/iface_status"
#define SYS_VLAN_PATH SYS_IFACE_PATH"/%s/mesh/vlan%d/"
+#define SYS_ROUTING_ALGO_FMT SYS_IFACE_PATH"/%s/mesh/routing_algo"
#define VLAN_ID_MAX_LEN 4
struct settings_data {
@@ -43,7 +44,6 @@ struct settings_data {
};
extern const char *sysfs_param_enable[];
-extern const char *sysfs_param_server[];
int handle_sys_setting(struct state *state, int argc, char **argv);