@@ -28,7 +28,7 @@ static int get_attrs_ap_isolation(struct nl_msg *msg, void *arg)
{
struct state *state = arg;
- if (state->vid >= 0)
+ if (state->selector == SP_VLAN)
nla_put_u16(msg, BATADV_ATTR_VLANID, state->vid);
return 0;
@@ -38,7 +38,7 @@ static int get_ap_isolation(struct state *state)
{
enum batadv_nl_commands nl_cmd = BATADV_CMD_SET_MESH;
- if (state->vid >= 0)
+ if (state->selector == SP_VLAN)
nl_cmd = BATADV_CMD_GET_VLAN;
return sys_simple_nlquery(state, nl_cmd, get_attrs_ap_isolation,
@@ -53,7 +53,7 @@ static int set_attrs_ap_isolation(struct nl_msg *msg, void *arg)
nla_put_u8(msg, BATADV_ATTR_AP_ISOLATION_ENABLED, data->val);
- if (state->vid >= 0)
+ if (state->selector == SP_VLAN)
nla_put_u16(msg, BATADV_ATTR_VLANID, state->vid);
return 0;
@@ -63,7 +63,7 @@ static int set_ap_isolation(struct state *state)
{
enum batadv_nl_commands nl_cmd = BATADV_CMD_SET_MESH;
- if (state->vid >= 0)
+ if (state->selector == SP_VLAN)
nl_cmd = BATADV_CMD_SET_VLAN;
return sys_simple_nlquery(state, nl_cmd, set_attrs_ap_isolation, NULL);
@@ -81,3 +81,8 @@ COMMAND_NAMED(SUBCOMMAND, ap_isolation, "ap", handle_sys_setting,
COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
&batctl_settings_ap_isolation,
"[0|1] \tdisplay or modify ap_isolation setting");
+
+COMMAND_NAMED(SUBCOMMAND_VID, ap_isolation, "ap", handle_sys_setting,
+ COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
+ &batctl_settings_ap_isolation,
+ "[0|1] \tdisplay or modify ap_isolation setting for vlan device or id");
@@ -919,32 +919,44 @@ static int query_rtnl_link_single(int mesh_ifindex,
return 0;
}
-int translate_mesh_iface(struct state *state)
+int translate_vlan_iface(struct state *state, const char *vlandev)
{
struct rtnl_link_iface_data link_data;
unsigned int arg_ifindex;
- arg_ifindex = if_nametoindex(state->arg_iface);
+ arg_ifindex = if_nametoindex(vlandev);
if (arg_ifindex == 0)
- goto fallback_meshif;
+ return -ENODEV;
query_rtnl_link_single(arg_ifindex, &link_data);
if (!link_data.vid_found)
- goto fallback_meshif;
+ return -ENODEV;
if (!link_data.link_found)
- goto fallback_meshif;
+ return -EINVAL;
if (!link_data.kind_found)
- goto fallback_meshif;
+ return -EINVAL;
if (strcmp(link_data.kind, "vlan") != 0)
- goto fallback_meshif;
+ return -EINVAL;
if (!if_indextoname(link_data.link, state->mesh_iface))
- goto fallback_meshif;
+ return -ENODEV;
state->vid = link_data.vid;
+ state->selector = SP_VLAN;
+
+ return 0;
+}
+
+int translate_mesh_iface_vlan(struct state *state, const char *vlandev)
+{
+ int ret;
+
+ ret = translate_vlan_iface(state, vlandev);
+ if (ret < 0)
+ goto fallback_meshif;
return 0;
@@ -952,9 +964,36 @@ int translate_mesh_iface(struct state *state)
/* if there is no vid then the argument must be the
* mesh interface
*/
- snprintf(state->mesh_iface, sizeof(state->mesh_iface), "%s",
- state->arg_iface);
- state->vid = -1;
+ snprintf(state->mesh_iface, sizeof(state->mesh_iface), "%s", vlandev);
+ state->selector = SP_NONE_OR_MESHIF;
+
+ return 0;
+}
+
+int translate_vid(struct state *state, const char *vidstr)
+{
+ unsigned long vid;
+ char *endptr;
+
+ if (vidstr[0] == '\0') {
+ fprintf(stderr, "Error - unparsable vid\n");
+ return -EINVAL;
+ }
+
+ vid = strtoul(vidstr, &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ fprintf(stderr, "Error - unparsable vid\n");
+ return -EINVAL;
+ }
+
+ if (vid > 4095) {
+ fprintf(stderr, "Error - too large vid (max 4095)\n");
+ return -ERANGE;
+ }
+
+ /* get mesh interface and overwrite vid afterwards */
+ state->vid = vid;
+ state->selector = SP_VLAN;
return 0;
}
@@ -50,7 +50,9 @@ struct ether_addr *translate_mac(const char *mesh_iface,
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 translate_mesh_iface_vlan(struct state *state, const char *vlandev);
+int translate_vlan_iface(struct state *state, const char *vlandev);
+int translate_vid(struct state *state, const char *vidstr);
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(struct state *state, char *hard_iface);
@@ -28,48 +28,75 @@ extern const struct command *__stop___command[];
static void print_usage(void)
{
- enum command_type type[] = {
- SUBCOMMAND,
- DEBUGTABLE,
+ struct {
+ const char *label;
+ uint32_t types;
+ } type[] = {
+ {
+ .label = "commands:\n",
+ .types = BIT(SUBCOMMAND) |
+ BIT(SUBCOMMAND_VID),
+ },
+ {
+ .label = "debug tables: \tdisplay the corresponding debug table\n",
+ .types = BIT(DEBUGTABLE),
+ },
+ };
+ const char *default_prefixes[] = {
+ "",
+ NULL,
+ };
+ const char *vlan_prefixes[] = {
+ "vlan <vdev> ",
+ "vid <vid> ",
+ NULL,
};
const struct command **p;
- char buf[32];
+ const char **prefixes;
+ const char **prefix;
+ char buf[64];
size_t i;
fprintf(stderr, "Usage: batctl [options] command|debug table [parameters]\n");
fprintf(stderr, "options:\n");
- fprintf(stderr, " \t-m mesh interface or VLAN created on top of a mesh interface (default 'bat0')\n");
+ fprintf(stderr, " \t-m mesh interface (default 'bat0')\n");
fprintf(stderr, " \t-h print this help (or 'batctl <command|debug table> -h' for the parameter help)\n");
fprintf(stderr, " \t-v print version\n");
for (i = 0; i < sizeof(type) / sizeof(*type); i++) {
fprintf(stderr, "\n");
- switch (type[i]) {
- case SUBCOMMAND:
- fprintf(stderr, "commands:\n");
- break;
- case DEBUGTABLE:
- fprintf(stderr, "debug tables: \tdisplay the corresponding debug table\n");
- break;
- }
+ fprintf(stderr, "%s", type[i].label);
for (p = __start___command; p < __stop___command; p++) {
const struct command *cmd = *p;
- if (cmd->type != type[i])
+ if (!(BIT(cmd->type) & type[i].types))
continue;
if (!cmd->usage)
continue;
- if (strcmp(cmd->name, cmd->abbr) == 0)
- snprintf(buf, sizeof(buf), "%s", cmd->name);
- else
- snprintf(buf, sizeof(buf), "%s|%s", cmd->name,
- cmd->abbr);
+ switch (cmd->type) {
+ case SUBCOMMAND_VID:
+ prefixes = vlan_prefixes;
+ break;
+ default:
+ prefixes = default_prefixes;
+ break;
+ }
+
+ for (prefix = &prefixes[0]; *prefix; prefix++) {
+ if (strcmp(cmd->name, cmd->abbr) == 0)
+ snprintf(buf, sizeof(buf), "%s%s",
+ *prefix, cmd->name);
+ else
+ snprintf(buf, sizeof(buf), "%s%s|%s",
+ *prefix, cmd->name, cmd->abbr);
- fprintf(stderr, " \t%-27s%s\n", buf, cmd->usage);
+ fprintf(stderr, " \t%-35s%s\n", buf,
+ cmd->usage);
+ }
}
}
}
@@ -93,13 +120,17 @@ static void version(void)
exit(EXIT_SUCCESS);
}
-static const struct command *find_command(const char *name)
+static const struct command *find_command_by_types(uint32_t types,
+ const char *name)
{
const struct command **p;
for (p = __start___command; p < __stop___command; p++) {
const struct command *cmd = *p;
+ if (!(BIT(cmd->type) & types))
+ continue;
+
if (strcmp(cmd->name, name) == 0)
return cmd;
@@ -110,13 +141,123 @@ static const struct command *find_command(const char *name)
return NULL;
}
+static const struct command *find_command(struct state *state, const char *name)
+{
+ uint32_t types;
+
+ switch (state->selector) {
+ case SP_NONE_OR_MESHIF:
+ types = BIT(SUBCOMMAND) |
+ BIT(DEBUGTABLE);
+ break;
+ case SP_VLAN:
+ types = BIT(SUBCOMMAND_VID);
+ break;
+ default:
+ return NULL;
+ }
+
+ return find_command_by_types(types, name);
+}
+
+static int detect_selector_prefix(int argc, char *argv[],
+ enum selector_prefix *selector)
+{
+ /* not enough remaining arguments to detect anything */
+ if (argc < 2)
+ return -EINVAL;
+
+ /* only detect selector prefix which identifies meshif */
+ if (strcmp(argv[0], "vlan") == 0) {
+ *selector = SP_VLAN;
+ return 2;
+ }
+
+ return 0;
+}
+
+static int parse_meshif_args(struct state *state, int argc, char *argv[])
+{
+ enum selector_prefix selector;
+ int parsed_args;
+ char *dev_arg;
+ int ret;
+
+ parsed_args = detect_selector_prefix(argc, argv, &selector);
+ if (parsed_args < 1)
+ goto fallback_meshif_vlan;
+
+ dev_arg = argv[parsed_args - 1];
+
+ switch (selector) {
+ case SP_VLAN:
+ ret = translate_vlan_iface(state, dev_arg);
+ if (ret < 0) {
+ fprintf(stderr, "Error - invalid vlan device %s: %s\n",
+ dev_arg, strerror(-ret));
+ return ret;
+ }
+
+ return parsed_args;
+ case SP_NONE_OR_MESHIF:
+ /* not allowed - see detect_selector_prefix */
+ break;
+ }
+
+fallback_meshif_vlan:
+ /* parse vlan as part of -m parameter or mesh_dfl_iface */
+ translate_mesh_iface_vlan(state, state->arg_iface);
+ return 0;
+}
+
+static int parse_dev_args(struct state *state, int argc, char *argv[])
+{
+ int dev_arguments;
+ int ret;
+
+ /* try to parse selector prefix which can be used to identify meshif */
+ dev_arguments = parse_meshif_args(state, argc, argv);
+ if (dev_arguments < 0)
+ return dev_arguments;
+
+ /* try to parse secondary prefix selectors which cannot be used to
+ * identify the meshif
+ */
+ argv += dev_arguments;
+ argc -= dev_arguments;
+
+ switch (state->selector) {
+ case SP_NONE_OR_MESHIF:
+ /* continue below */
+ break;
+ default:
+ return dev_arguments;
+ }
+
+ /* enough room for additional selectors? */
+ if (argc < 2)
+ return dev_arguments;
+
+ if (strcmp(argv[0], "vid") == 0) {
+ ret = translate_vid(state, argv[1]);
+ if (ret < 0)
+ return ret;
+
+ return dev_arguments + 2;
+ }
+
+ return dev_arguments;
+}
+
int main(int argc, char **argv)
{
const struct command *cmd;
struct state state = {
.arg_iface = mesh_dfl_iface,
+ .selector = SP_NONE_OR_MESHIF,
.cmd = NULL,
};
+ int dev_arguments;
int opt;
int ret;
@@ -152,7 +293,20 @@ int main(int argc, char **argv)
argc -= optind;
optind = 0;
- cmd = find_command(argv[0]);
+ /* parse arguments to identify vlan, ... */
+ dev_arguments = parse_dev_args(&state, argc, argv);
+ if (dev_arguments < 0)
+ goto err;
+
+ argv += dev_arguments;
+ argc -= dev_arguments;
+
+ if (argc == 0) {
+ fprintf(stderr, "Error - no command specified\n");
+ goto err;
+ }
+
+ cmd = find_command(&state, argv[0]);
if (!cmd) {
fprintf(stderr,
"Error - no valid command or debug table specified: %s\n",
@@ -162,8 +316,6 @@ int main(int argc, char **argv)
state.cmd = cmd;
- translate_mesh_iface(&state);
-
if (cmd->flags & COMMAND_FLAG_MESH_IFACE &&
check_mesh_iface(&state) < 0) {
fprintf(stderr,
@@ -56,13 +56,20 @@ enum command_flags {
COMMAND_FLAG_INVERSE = BIT(2),
};
+enum selector_prefix {
+ SP_NONE_OR_MESHIF,
+ SP_VLAN,
+};
+
enum command_type {
SUBCOMMAND,
+ SUBCOMMAND_VID,
DEBUGTABLE,
};
struct state {
char *arg_iface;
+ enum selector_prefix selector;
char mesh_iface[IF_NAMESIZE];
unsigned int mesh_ifindex;
int vid;
@@ -84,7 +91,7 @@ struct command {
};
#define COMMAND_NAMED(_type, _name, _abbr, _handler, _flags, _arg, _usage) \
- static const struct command command_ ## _name = { \
+ static const struct command command_ ## _name ## _ ## _type = { \
.type = (_type), \
.name = (#_name), \
.abbr = _abbr, \
@@ -93,8 +100,8 @@ struct command {
.arg = (_arg), \
.usage = (_usage), \
}; \
- static const struct command *__command_ ## _name \
- __attribute__((__used__)) __attribute__ ((__section__ ("__command"))) = &command_ ## _name
+ static const struct command *__command_ ## _name ## _ ## _type \
+ __attribute__((__used__)) __attribute__ ((__section__ ("__command"))) = &command_ ## _name ## _ ## _type
#define COMMAND(_type, _handler, _abbr, _flags, _arg, _usage) \
COMMAND_NAMED(_type, _handler, _abbr, _handler, _flags, _arg, _usage)
@@ -46,7 +46,7 @@ performances, is also included.
.SH OPTIONS
.TP
.I \fBoptions:
-\-m specify mesh interface or VLAN created on top of a mesh interface (default 'bat0')
+\-m specify mesh interface (default 'bat0')
.br
\-h print general batctl help
.br
@@ -70,7 +70,11 @@ originator interval. The interval is in units of milliseconds.
.br
.IP "\fBap_isolation\fP|\fBap\fP [\fB0\fP|\fB1\fP]"
If no parameter is given the current ap isolation setting is displayed. Otherwise the parameter is used to enable or
-disable ap isolation. This command can be used in conjunction with "\-m" option to target per VLAN configurations.
+disable ap isolation.
+.br
+.IP "<\fBvlan <vdev>\fP|\fBvid <vid>\fP> \fBap_isolation\fP|\fBap\fP [\fB0\fP|\fB1\fP]"
+If no parameter is given the current ap isolation setting for the specified VLAN is displayed. Otherwise the parameter is used to enable or
+disable ap isolation for the specified VLAN.
.br
.IP "\fBbridge_loop_avoidance\fP|\fBbl\fP [\fB0\fP|\fB1\fP]"
If no parameter is given the current bridge loop avoidance setting is displayed. Otherwise the parameter is used to enable
@@ -141,9 +141,35 @@ int sys_simple_print_boolean(struct nl_msg *msg, void *arg,
static void settings_usage(struct state *state)
{
- fprintf(stderr, "Usage: batctl [options] %s|%s [parameters] %s\n",
- state->cmd->name, state->cmd->abbr,
- state->cmd->usage ? state->cmd->usage : "");
+ const char *default_prefixes[] = {
+ "",
+ NULL,
+ };
+ const char *vlan_prefixes[] = {
+ "vlan <vdev> ",
+ "vid <vid> ",
+ NULL,
+ };
+ const char *linestart = "Usage:";
+ const char **prefixes;
+ const char **prefix;
+
+ switch (state->cmd->type) {
+ case SUBCOMMAND_VID:
+ prefixes = vlan_prefixes;
+ break;
+ default:
+ prefixes = default_prefixes;
+ break;
+ }
+
+ for (prefix = &prefixes[0]; *prefix; prefix++) {
+ fprintf(stderr, "%s batctl [options] %s%s|%s [parameters] %s\n",
+ linestart, *prefix, state->cmd->name, state->cmd->abbr,
+ state->cmd->usage ? state->cmd->usage : "");
+
+ linestart = " ";
+ }
fprintf(stderr, "parameters:\n");
fprintf(stderr, " \t -h print this help\n");
@@ -233,15 +259,19 @@ int handle_sys_setting(struct state *state, int argc, char **argv)
return EXIT_FAILURE;
}
- /* if the specified interface is a VLAN then change the path to point
- * to the proper "vlan%{vid}" subfolder in the sysfs tree.
- */
- if (state->vid >= 0)
- snprintf(path_buff, PATH_BUFF_LEN, SYS_VLAN_PATH,
- state->mesh_iface, state->vid);
- else
+ switch (state->selector) {
+ case SP_NONE_OR_MESHIF:
snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT,
state->mesh_iface);
+ break;
+ case SP_VLAN:
+ /* if the specified interface is a VLAN then change the path to
+ * point to the proper "vlan%{vid}" subfolder in the sysfs tree.
+ */
+ snprintf(path_buff, PATH_BUFF_LEN, SYS_VLAN_PATH,
+ state->mesh_iface, state->vid);
+ break;
+ }
if (argc == 1) {
res = sys_read_setting(state, path_buff, settings->sysfs_name);