[v5,05/20] batctl: Parse the arguments for gw_mode

Message ID 20190209134222.15035-6-sven@narfation.org
State Accepted, archived
Delegated to: Simon Wunderlich
Headers show
Series
  • batctl: netlink restructuring, part 3
Related show

Commit Message

Sven Eckelmann Feb. 9, 2019, 1:42 p.m.
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@narfation.org>
---
Cc: Marek Lindner <mareklindner@neomailbox.ch>
Cc: Antonio Quartulli <a@unstable.cc>
---
 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(-)

Patch

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);