Hi,
the sysfs file mesh_iface to add/remove interfaces to/from a batman-adv soft-interface was downgraded in batman-adv master to a second-class citizen. This was done because it has conceptional problems (for example locking of sysfs vs. locking of the network core code). The only direct way to modify interfaces is rtnetlink which is used by different tools like iproute2 (ip link, ip addr, ...) or netifd. sysfs still exists but has to use workers which delay the actual add/del to a later point.
This patchset now moves batctl to rtnetlink instead of the sysfs-file mesh_iface. This should work fine since this is supported by batman-adv (in Linux) since Linux v3.10 (batman-adv v2013.2).
This patchset also introduces two new interface subcommands called "new" and "destroy". "new" can be used to create a batman-adv interface without any interface attached. This is helpful when the interfaces should be configured independently of the first attached interface. The command "destroy" can be used to destroy a batman-adv interface without going through all attached interfaces and delete them manually.
It is now also possible to tell batctl to not automatically create a batman-adv interface when an interface is attached. But more important is this option to disable the automatic removal of a batman-adv interface when all attached interfaces were removed. This is useful when a number of interfaces should be added/removed without accidentally loosing the current configuration of the interface. Of course, we should also accept the related patch for batman-adv [1].
I've also added two patches with some minor cleanup because it was dealing with code in the same file with similar context.
This patchset is build on top of the netlink patchset currently waiting to be applied.
Kind regards, Sven
[1] https://patchwork.open-mesh.org/patch/16499/
Sven Eckelmann (7): batctl: Use rtnl to query list of softif devices batctl: Add command to create/destroy batman-adv interface batctl: Use rtnl to add/remove interfaces batctl: Parse interface arguments relative to last parsed option batctl: Allow to disable automatic interface new/destroy batctl: Move interface command to extra file batctl: Move check_mesh_iface* to functions.c
Makefile | 1 + functions.c | 181 +++++++++++++++++++++++ functions.h | 10 +- interface.c | 457 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ interface.h | 29 ++++ main.c | 1 + man/batctl.8 | 7 +- sys.c | 215 +--------------------------- sys.h | 3 - 9 files changed, 690 insertions(+), 214 deletions(-) create mode 100644 interface.c create mode 100644 interface.h
The normal way of network related programs to query the state of interfaces is to use the rtnetlink. Most of these data can also be queried via sysfs but it is easier to use the same way for both retrieving the list of interfaces and modifying the list of interfaces.
Signed-off-by: Sven Eckelmann sven@narfation.org --- functions.c | 64 +++++++++++++++++++++++++++++++++++ functions.h | 5 ++- sys.c | 111 +++++++++++++++++++++++++++++++++--------------------------- 3 files changed, 129 insertions(+), 51 deletions(-)
diff --git a/functions.c b/functions.c index d236d64..327ef18 100644 --- a/functions.c +++ b/functions.c @@ -37,6 +37,7 @@ #include <stdint.h> #include <linux/netlink.h> #include <net/ethernet.h> +#include <linux/if_link.h> #include <linux/rtnetlink.h> #include <linux/neighbour.h> #include <errno.h> @@ -889,3 +890,66 @@ int print_routing_algos(void) err = debug_print_routing_algos(); return err; } + +int query_rtnl_link(int ifindex, nl_recvmsg_msg_cb_t func, void *arg) +{ + struct ifinfomsg rt_hdr = { + .ifi_family = IFLA_UNSPEC, + }; + struct nl_sock *sock; + struct nl_msg *msg; + struct nl_cb *cb; + int err = 0; + int ret; + + sock = nl_socket_alloc(); + if (!sock) + return -ENOMEM; + + ret = nl_connect(sock, NETLINK_ROUTE); + if (ret < 0) { + err = -ENOMEM; + goto err_free_sock; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) { + err = -ENOMEM; + goto err_free_sock; + } + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, func, arg); + + msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP); + if (!msg) { + err = -ENOMEM; + goto err_free_cb; + } + + ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_u32(msg, IFLA_MASTER, ifindex); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nl_send_auto_complete(sock, msg); + if (ret < 0) + goto err_free_msg; + + nl_recvmsgs(sock, cb); + +err_free_msg: + nlmsg_free(msg); +err_free_cb: + nl_cb_put(cb); +err_free_sock: + nl_socket_free(sock); + + return err; +} diff --git a/functions.h b/functions.h index e24dea0..a6090b6 100644 --- a/functions.h +++ b/functions.h @@ -23,6 +23,8 @@ #define _BATCTL_FUNCTIONS_H
#include <net/ethernet.h> +#include <netlink/netlink.h> +#include <netlink/handlers.h> #include <stddef.h>
@@ -43,7 +45,8 @@ int write_file(const char *dir, const char *fname, const char *arg1, struct ether_addr *translate_mac(const char *mesh_iface, const struct ether_addr *mac); struct ether_addr *resolve_mac(const char *asc); -int vlan_get_link(const char *ifname, char **parent); +int vlan_get_link(const char *ifname, char **parent);\ +int query_rtnl_link(int ifindex, nl_recvmsg_msg_cb_t func, void *arg);
int print_routing_algos(void); extern char *line_ptr; diff --git a/sys.c b/sys.c index ca837f6..0140b28 100644 --- a/sys.c +++ b/sys.c @@ -26,6 +26,11 @@ #include <string.h> #include <errno.h> #include <dirent.h> +#include <net/if.h> +#include <linux/if_link.h> +#include <netlink/netlink.h> +#include <netlink/msg.h> +#include <netlink/attr.h>
#include "main.h" #include "sys.h" @@ -119,72 +124,78 @@ static void interface_usage(void) fprintf(stderr, " \t -h print this help\n"); }
-static int print_interfaces(char *mesh_iface) -{ - DIR *iface_base_dir; - struct dirent *iface_dir; - char *path_buff; - int res; +static struct nla_policy link_policy[IFLA_MAX + 1] = { + [IFLA_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, + [IFLA_MASTER] = { .type = NLA_U32 }, +};
- if (!file_exists(module_ver_path)) { - fprintf(stderr, "Error - batman-adv module has not been loaded\n"); +struct print_interfaces_rtnl_arg { + int ifindex; +}; + +static int print_interfaces_rtnl_parse(struct nl_msg *msg, void *arg) +{ + struct print_interfaces_rtnl_arg *print_arg = arg; + struct nlattr *attrs[IFLA_MAX + 1]; + char path_buff[PATH_BUFF_LEN]; + struct ifinfomsg *ifm; + char *ifname; + int ret; + const char *status; + int master; + + ifm = nlmsg_data(nlmsg_hdr(msg)); + ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(*ifm), attrs, IFLA_MAX, + link_policy); + if (ret < 0) goto err; - }
- path_buff = malloc(PATH_BUFF_LEN); - if (!path_buff) { - fprintf(stderr, "Error - could not allocate path buffer: out of memory ?\n"); + if (!attrs[IFLA_IFNAME]) goto err; - }
- iface_base_dir = opendir(SYS_IFACE_PATH); - if (!iface_base_dir) { - fprintf(stderr, "Error - the directory '%s' could not be read: %s\n", - SYS_IFACE_PATH, strerror(errno)); - fprintf(stderr, "Is the batman-adv module loaded and sysfs mounted ?\n"); - goto err_buff; - } + if (!attrs[IFLA_MASTER]) + goto err;
- while ((iface_dir = readdir(iface_base_dir)) != NULL) { - snprintf(path_buff, PATH_BUFF_LEN, SYS_MESH_IFACE_FMT, iface_dir->d_name); - res = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); - if (res != EXIT_SUCCESS) - continue; + ifname = nla_get_string(attrs[IFLA_IFNAME]); + master = nla_get_u32(attrs[IFLA_MASTER]);
- if (line_ptr[strlen(line_ptr) - 1] == '\n') - line_ptr[strlen(line_ptr) - 1] = '\0'; + /* required on older kernels which don't prefilter the results */ + if (master != print_arg->ifindex) + goto err;
- if (strcmp(line_ptr, "none") == 0) - goto free_line; + snprintf(path_buff, sizeof(path_buff), SYS_IFACE_STATUS_FMT, ifname); + ret = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); + if (ret != EXIT_SUCCESS) + status = "<error reading status>\n"; + else + status = line_ptr;
- if (strcmp(line_ptr, mesh_iface) != 0) - goto free_line; + printf("%s: %s", ifname, status);
- free(line_ptr); - line_ptr = NULL; + free(line_ptr); + line_ptr = NULL;
- snprintf(path_buff, PATH_BUFF_LEN, SYS_IFACE_STATUS_FMT, iface_dir->d_name); - res = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); - if (res != EXIT_SUCCESS) { - fprintf(stderr, "<error reading status>\n"); - continue; - } +err: + return NL_OK; +}
- printf("%s: %s", iface_dir->d_name, line_ptr); +static int print_interfaces(char *mesh_iface) +{ + struct print_interfaces_rtnl_arg print_arg;
-free_line: - free(line_ptr); - line_ptr = NULL; + if (!file_exists(module_ver_path)) { + fprintf(stderr, "Error - batman-adv module has not been loaded\n"); + return EXIT_FAILURE; }
- free(path_buff); - closedir(iface_base_dir); - return EXIT_SUCCESS; + print_arg.ifindex = if_nametoindex(mesh_iface); + if (!print_arg.ifindex) + return EXIT_FAILURE;
-err_buff: - free(path_buff); -err: - return EXIT_FAILURE; + query_rtnl_link(print_arg.ifindex, print_interfaces_rtnl_parse, + &print_arg); + + return EXIT_SUCCESS; }
int interface(char *mesh_iface, int argc, char **argv)
The command "new" can be used to create a batman-adv interface without any interface attached. This is helpful when the interfaces should be configured independently of the first attached interface.
The command "destroy" can be used to destroy a batman-adv interface without going through all attached interfaces and delete them manually.
Signed-off-by: Sven Eckelmann sven@narfation.org --- functions.c | 58 ++++++++++++++++++++++++ functions.h | 1 + man/batctl.8 | 3 ++ sys.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 200 insertions(+), 5 deletions(-)
diff --git a/functions.c b/functions.c index 327ef18..962efcf 100644 --- a/functions.c +++ b/functions.c @@ -953,3 +953,61 @@ err_free_sock:
return err; } + +static int ack_errno_handler(struct sockaddr_nl *nla __unused, + struct nlmsgerr *nlerr, + void *arg) +{ + int *err = arg; + + *err = nlerr->error; + + return NL_STOP; +} + +static int ack_wait_handler(struct nl_msg *msg __unused, void *arg __unused) +{ + return NL_STOP; +} + +int netlink_simple_request(struct nl_msg *msg) +{ + struct nl_sock *sock; + struct nl_cb *cb; + int err = 0; + int ret; + + sock = nl_socket_alloc(); + if (!sock) + return -ENOMEM; + + ret = nl_connect(sock, NETLINK_ROUTE); + if (ret < 0) { + err = -ENOMEM; + goto err_free_sock; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) { + err = -ENOMEM; + goto err_free_sock; + } + + nl_cb_err(cb, NL_CB_CUSTOM, ack_errno_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, NULL); + + ret = nl_send_auto_complete(sock, msg); + if (ret < 0) + goto err_free_cb; + + // ack_errno_handler sets err on errors + err = 0; + nl_recvmsgs(sock, cb); + +err_free_cb: + nl_cb_put(cb); +err_free_sock: + nl_socket_free(sock); + + return err; +} diff --git a/functions.h b/functions.h index a6090b6..7757731 100644 --- a/functions.h +++ b/functions.h @@ -47,6 +47,7 @@ struct ether_addr *translate_mac(const char *mesh_iface, struct ether_addr *resolve_mac(const char *asc); int vlan_get_link(const char *ifname, char **parent);\ int query_rtnl_link(int ifindex, nl_recvmsg_msg_cb_t func, void *arg); +int netlink_simple_request(struct nl_msg *msg);
int print_routing_algos(void); extern char *line_ptr; diff --git a/man/batctl.8 b/man/batctl.8 index 8bac727..8dcf18a 100644 --- a/man/batctl.8 +++ b/man/batctl.8 @@ -56,6 +56,9 @@ performances, is also included. If no parameter is given or the first parameter is neither "add" nor "del" the current interface settings are displayed. In order to add or delete interfaces specify "add" or "del" as first argument and append the interface names you wish to add or delete. Multiple interfaces can be specified. +.IP "\fBinterface\fP|\fBif\fP [\fBnew\fP|\fBdestroy\fP]" +A batman-adv interface without attached interfaces can be created using "new". The parameter "destroy" can be used to +free all attached interfaces and remove batman-adv interface. .br .IP "\fBorig_interval\fP|\fBit\fP [\fBinterval\fP]" If no parameter is given the current originator interval setting is displayed otherwise the parameter is used to set the diff --git a/sys.c b/sys.c index 0140b28..252864b 100644 --- a/sys.c +++ b/sys.c @@ -120,6 +120,7 @@ const struct settings_data batctl_settings[BATCTL_SETTINGS_NUM] = { static void interface_usage(void) { fprintf(stderr, "Usage: batctl [options] interface [parameters] [add|del iface(s)]\n"); + fprintf(stderr, " batctl [options] interface [parameters] [new|destroy]\n"); fprintf(stderr, "parameters:\n"); fprintf(stderr, " \t -h print this help\n"); } @@ -198,10 +199,95 @@ static int print_interfaces(char *mesh_iface) return EXIT_SUCCESS; }
+static int new_interface(const char *mesh_iface) +{ + struct ifinfomsg rt_hdr = { + .ifi_family = IFLA_UNSPEC, + }; + struct nlattr *linkinfo; + struct nl_msg *msg; + int err = 0; + int ret; + + msg = nlmsg_alloc_simple(RTM_NEWLINK, + NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); + if (!msg) { + return -ENOMEM; + } + + ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_string(msg, IFLA_IFNAME, mesh_iface); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + linkinfo = nla_nest_start(msg, IFLA_LINKINFO); + if (!linkinfo) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_string(msg, IFLA_INFO_KIND, "batadv"); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + nla_nest_end(msg, linkinfo); + + err = netlink_simple_request(msg); + +err_free_msg: + nlmsg_free(msg); + + return err; +} + +static int destroy_interface(const char *mesh_iface) +{ + struct ifinfomsg rt_hdr = { + .ifi_family = IFLA_UNSPEC, + }; + struct nl_msg *msg; + int err = 0; + int ret; + + msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK); + if (!msg) { + return -ENOMEM; + } + + ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_string(msg, IFLA_IFNAME, mesh_iface); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + err = netlink_simple_request(msg); + +err_free_msg: + nlmsg_free(msg); + + return err; +} + int interface(char *mesh_iface, int argc, char **argv) { char *path_buff; int i, res, optchar; + int ret;
while ((optchar = getopt(argc, argv, "h")) != -1) { switch (optchar) { @@ -218,16 +304,63 @@ int interface(char *mesh_iface, int argc, char **argv) return print_interfaces(mesh_iface);
if ((strcmp(argv[1], "add") != 0) && (strcmp(argv[1], "a") != 0) && - (strcmp(argv[1], "del") != 0) && (strcmp(argv[1], "d") != 0)) { + (strcmp(argv[1], "del") != 0) && (strcmp(argv[1], "d") != 0) && + (strcmp(argv[1], "new") != 0) && (strcmp(argv[1], "n") != 0) && + (strcmp(argv[1], "destroy") != 0) && (strcmp(argv[1], "D") != 0)) { fprintf(stderr, "Error - unknown argument specified: %s\n", argv[1]); interface_usage(); goto err; }
- if (argc == 2) { - fprintf(stderr, "Error - missing interface name(s) after '%s'\n", argv[1]); - interface_usage(); - goto err; + if (strcmp(argv[1], "destroy") == 0) + argv[1][0] = 'D'; + + switch (argv[1][0]) { + case 'a': + case 'd': + if (argc == 2) { + fprintf(stderr, + "Error - missing interface name(s) after '%s'\n", + argv[1]); + interface_usage(); + goto err; + } + break; + case 'n': + case 'D': + if (argc != 2) { + fprintf(stderr, + "Error - extra parameter after '%s'\n", + argv[1]); + interface_usage(); + goto err; + } + break; + default: + break; + } + + switch (argv[1][0]) { + case 'n': + ret = new_interface(mesh_iface); + if (ret < 0) { + fprintf(stderr, + "Error - failed to add new batman-adv interface: %s\n", + strerror(-ret)); + goto err; + } + return EXIT_SUCCESS; + case 'D': + ret = destroy_interface(mesh_iface); + if (ret < 0) { + fprintf(stderr, + "Error - failed to destroy batman-adv interface: %s\n", + strerror(-ret)); + goto err; + } + return EXIT_SUCCESS; + default: + break; }
path_buff = malloc(PATH_BUFF_LEN);
On Wed, Jul 13, 2016 at 05:30:17PM +0200, Sven Eckelmann wrote:
The command "new" can be used to create a batman-adv interface without any interface attached. This is helpful when the interfaces should be configured independently of the first attached interface.
Like in the title of this mail, maybe naming the commands "create" and "destroy" instead of "new"/"destroy"?
The former sounds slightly more antonymic to me.
On Dienstag, 20. September 2016 16:24:18 CEST Linus Lüssing wrote:
On Wed, Jul 13, 2016 at 05:30:17PM +0200, Sven Eckelmann wrote:
The command "new" can be used to create a batman-adv interface without any interface attached. This is helpful when the interfaces should be configured independently of the first attached interface.
Like in the title of this mail, maybe naming the commands "create" and "destroy" instead of "new"/"destroy"?
The former sounds slightly more antonymic to me.
Has anyone else an opinion about that? I personally would be fine with "create" instead of "new".
Kind regards, Sven
Hi All,
I too like create/destroy.
John
On Tue, 20 Sep 2016, Sven Eckelmann wrote:
On Dienstag, 20. September 2016 16:24:18 CEST Linus L?ssing wrote:
On Wed, Jul 13, 2016 at 05:30:17PM +0200, Sven Eckelmann wrote:
The command "new" can be used to create a batman-adv interface without any interface attached. This is helpful when the interfaces should be configured independently of the first attached interface.
Like in the title of this mail, maybe naming the commands "create" and "destroy" instead of "new"/"destroy"?
The former sounds slightly more antonymic to me.
Has anyone else an opinion about that? I personally would be fine with "create" instead of "new".
Kind regards, Sven
The sysfs interface to add/remove interfaces to/from a batman-adv soft-interface was downgraded in batman-adv master to a second-class citizen. This was done because it has conceptional problems (for example locking of sysfs vs. locking of the network core code). The only direct way to modify network interfaces is rtnetlink. sysfs still exists but has to use workers which delay the actual add/del to a later point.
It is therefore prefered to use the modern rtnetlink. Only batman-adv versions older than v2013.2.0 (Linux 3.10) will not yet support the rtnl_link operations required for it to work.
Signed-off-by: Sven Eckelmann sven@narfation.org --- sys.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 145 insertions(+), 27 deletions(-)
diff --git a/sys.c b/sys.c index 252864b..ac78d37 100644 --- a/sys.c +++ b/sys.c @@ -199,6 +199,58 @@ static int print_interfaces(char *mesh_iface) return EXIT_SUCCESS; }
+struct count_interfaces_rtnl_arg { + int ifindex; + unsigned int count; +}; + +static int count_interfaces_rtnl_parse(struct nl_msg *msg, void *arg) +{ + struct count_interfaces_rtnl_arg *count_arg = arg; + struct nlattr *attrs[IFLA_MAX + 1]; + struct ifinfomsg *ifm; + int ret; + int master; + + ifm = nlmsg_data(nlmsg_hdr(msg)); + ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(*ifm), attrs, IFLA_MAX, + link_policy); + if (ret < 0) + goto err; + + if (!attrs[IFLA_IFNAME]) + goto err; + + if (!attrs[IFLA_MASTER]) + goto err; + + master = nla_get_u32(attrs[IFLA_MASTER]); + + /* required on older kernels which don't prefilter the results */ + if (master != count_arg->ifindex) + goto err; + + count_arg->count++; + +err: + return NL_OK; +} + +static unsigned int count_interfaces(char *mesh_iface) +{ + struct count_interfaces_rtnl_arg count_arg; + + count_arg.count = 0; + count_arg.ifindex = if_nametoindex(mesh_iface); + if (!count_arg.ifindex) + return 0; + + query_rtnl_link(count_arg.ifindex, count_interfaces_rtnl_parse, + &count_arg); + + return count_arg.count; +} + static int new_interface(const char *mesh_iface) { struct ifinfomsg rt_hdr = { @@ -283,11 +335,54 @@ err_free_msg: return err; }
+static int set_master_interface(const char *iface, unsigned int ifmaster) +{ + struct ifinfomsg rt_hdr = { + .ifi_family = IFLA_UNSPEC, + }; + struct nl_msg *msg; + int err = 0; + int ret; + + msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK); + if (!msg) { + return -ENOMEM; + } + + ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_string(msg, IFLA_IFNAME, iface); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_u32(msg, IFLA_MASTER, ifmaster); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + err = netlink_simple_request(msg); + +err_free_msg: + nlmsg_free(msg); + + return err; +} + int interface(char *mesh_iface, int argc, char **argv) { - char *path_buff; - int i, res, optchar; + int i, optchar; int ret; + unsigned int ifindex; + unsigned int ifmaster; + const char *long_op; + unsigned int cnt;
while ((optchar = getopt(argc, argv, "h")) != -1) { switch (optchar) { @@ -363,46 +458,69 @@ int interface(char *mesh_iface, int argc, char **argv) break; }
- path_buff = malloc(PATH_BUFF_LEN); - if (!path_buff) { - fprintf(stderr, "Error - could not allocate path buffer: out of memory ?\n"); - goto err; - } + /* get index of batman-adv interface - or try to create it */ + ifmaster = if_nametoindex(mesh_iface); + if (!ifmaster && argv[1][0] == 'a') { + ret = new_interface(mesh_iface); + if (ret < 0) { + fprintf(stderr, + "Error - failed to add new batman-adv interface: %s\n", + strerror(-ret)); + goto err; + }
- for (i = 2; i < argc; i++) { - snprintf(path_buff, PATH_BUFF_LEN, SYS_MESH_IFACE_FMT, argv[i]); + ifmaster = if_nametoindex(mesh_iface); + }
- if (!file_exists(path_buff)) { - snprintf(path_buff, PATH_BUFF_LEN, SYS_IFACE_DIR, argv[i]); + if (!ifmaster) { + ret = -ENODEV; + fprintf(stderr, + "Error - failed to find batman-adv interface: %s\n", + strerror(-ret)); + goto err; + }
- if (!file_exists(path_buff)) { - fprintf(stderr, "Error - interface does not exist: %s\n", argv[i]); - continue; - } + /* make sure that batman-adv is loaded or was loaded by new_interface */ + if (!file_exists(module_ver_path)) { + fprintf(stderr, "Error - batman-adv module has not been loaded\n"); + goto err; + }
- if (!file_exists(module_ver_path)) { - fprintf(stderr, "Error - batman-adv module has not been loaded\n"); - goto err_buff; - } + for (i = 2; i < argc; i++) { + ifindex = if_nametoindex(argv[i]);
- fprintf(stderr, "Error - interface type not supported by batman-adv: %s\n", argv[i]); + if (!ifindex) { + fprintf(stderr, "Error - interface does not exist: %s\n", argv[i]); continue; }
if (argv[1][0] == 'a') - res = write_file("", path_buff, mesh_iface, NULL); + ifindex = ifmaster; else - res = write_file("", path_buff, "none", NULL); + ifindex = 0;
- if (res != EXIT_SUCCESS) - goto err_buff; + ret = set_master_interface(argv[i], ifindex); + if (ret < 0) { + if (argv[1][0] == 'a') + long_op = "add"; + else + long_op = "delete"; + + fprintf(stderr, "Error - failed to %s interface %s: %s\n", + long_op, argv[i], strerror(-ret)); + goto err; + } + } + + /* check if there is no interface left and then destroy mesh_iface */ + if (argv[1][0] == 'd') { + cnt = count_interfaces(mesh_iface); + if (cnt == 0) + destroy_interface(mesh_iface); }
- free(path_buff); return EXIT_SUCCESS;
-err_buff: - free(path_buff); err: return EXIT_FAILURE; }
Arguments may be added between "interface" and the subcommands "add" and "del". Thus is should not be hardcoded which positions of argv the subcommands start and instead the information from getopt should be used to calculate the correct position.
Signed-off-by: Sven Eckelmann sven@narfation.org --- sys.c | 51 ++++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-)
diff --git a/sys.c b/sys.c index ac78d37..7cd2690 100644 --- a/sys.c +++ b/sys.c @@ -383,6 +383,8 @@ int interface(char *mesh_iface, int argc, char **argv) unsigned int ifmaster; const char *long_op; unsigned int cnt; + int rest_argc; + char **rest_argv;
while ((optchar = getopt(argc, argv, "h")) != -1) { switch (optchar) { @@ -395,38 +397,41 @@ int interface(char *mesh_iface, int argc, char **argv) } }
- if (argc == 1) + rest_argc = argc - optind; + rest_argv = &argv[optind]; + + if (rest_argc == 0) return print_interfaces(mesh_iface);
- if ((strcmp(argv[1], "add") != 0) && (strcmp(argv[1], "a") != 0) && - (strcmp(argv[1], "del") != 0) && (strcmp(argv[1], "d") != 0) && - (strcmp(argv[1], "new") != 0) && (strcmp(argv[1], "n") != 0) && - (strcmp(argv[1], "destroy") != 0) && (strcmp(argv[1], "D") != 0)) { - fprintf(stderr, "Error - unknown argument specified: %s\n", argv[1]); + if ((strcmp(rest_argv[0], "add") != 0) && (strcmp(rest_argv[0], "a") != 0) && + (strcmp(rest_argv[0], "del") != 0) && (strcmp(rest_argv[0], "d") != 0) && + (strcmp(rest_argv[0], "new") != 0) && (strcmp(rest_argv[0], "n") != 0) && + (strcmp(rest_argv[0], "destroy") != 0) && (strcmp(rest_argv[0], "D") != 0)) { + fprintf(stderr, "Error - unknown argument specified: %s\n", rest_argv[0]); interface_usage(); goto err; }
- if (strcmp(argv[1], "destroy") == 0) - argv[1][0] = 'D'; + if (strcmp(rest_argv[0], "destroy") == 0) + rest_argv[0][0] = 'D';
- switch (argv[1][0]) { + switch (rest_argv[0][0]) { case 'a': case 'd': - if (argc == 2) { + if (rest_argc == 1) { fprintf(stderr, "Error - missing interface name(s) after '%s'\n", - argv[1]); + rest_argv[0]); interface_usage(); goto err; } break; case 'n': case 'D': - if (argc != 2) { + if (rest_argc != 1) { fprintf(stderr, "Error - extra parameter after '%s'\n", - argv[1]); + rest_argv[0]); interface_usage(); goto err; } @@ -435,7 +440,7 @@ int interface(char *mesh_iface, int argc, char **argv) break; }
- switch (argv[1][0]) { + switch (rest_argv[0][0]) { case 'n': ret = new_interface(mesh_iface); if (ret < 0) { @@ -460,7 +465,7 @@ int interface(char *mesh_iface, int argc, char **argv)
/* get index of batman-adv interface - or try to create it */ ifmaster = if_nametoindex(mesh_iface); - if (!ifmaster && argv[1][0] == 'a') { + if (!ifmaster && rest_argv[0][0] == 'a') { ret = new_interface(mesh_iface); if (ret < 0) { fprintf(stderr, @@ -486,34 +491,34 @@ int interface(char *mesh_iface, int argc, char **argv) goto err; }
- for (i = 2; i < argc; i++) { - ifindex = if_nametoindex(argv[i]); + for (i = 1; i < rest_argc; i++) { + ifindex = if_nametoindex(rest_argv[i]);
if (!ifindex) { - fprintf(stderr, "Error - interface does not exist: %s\n", argv[i]); + fprintf(stderr, "Error - interface does not exist: %s\n", rest_argv[i]); continue; }
- if (argv[1][0] == 'a') + if (rest_argv[0][0] == 'a') ifindex = ifmaster; else ifindex = 0;
- ret = set_master_interface(argv[i], ifindex); + ret = set_master_interface(rest_argv[i], ifindex); if (ret < 0) { - if (argv[1][0] == 'a') + if (rest_argv[0][0] == 'a') long_op = "add"; else long_op = "delete";
fprintf(stderr, "Error - failed to %s interface %s: %s\n", - long_op, argv[i], strerror(-ret)); + long_op, rest_argv[i], strerror(-ret)); goto err; } }
/* check if there is no interface left and then destroy mesh_iface */ - if (argv[1][0] == 'd') { + if (rest_argv[0][0] == 'd') { cnt = count_interfaces(mesh_iface); if (cnt == 0) destroy_interface(mesh_iface);
Users may not want to lose their configured batman-adv soft-interface when they remove a single interface from it. The default configuration may not working well enough in the network setup of the user and thus it should be possible to avoid that it gets reset to it when a new interface is added after the last one was removed.
This can be done by avoiding automatic creation of an interface when the command "add" is used together with the option "-M". The add would fail when the soft-interface disappeared for some reason and thus the soft-interface would not be created again with the default configuration. But more importantly, the "del" command can be informed with the option "-M" to not try to remove the soft-interface in the first place.
Signed-off-by: Sven Eckelmann sven@narfation.org --- man/batctl.8 | 4 +++- sys.c | 12 +++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/man/batctl.8 b/man/batctl.8 index 8dcf18a..0d77546 100644 --- a/man/batctl.8 +++ b/man/batctl.8 @@ -52,10 +52,12 @@ performances, is also included. .br .TP .I \fBcommands: -.IP "\fBinterface\fP|\fBif\fP [\fBadd\fP|\fBdel iface(s)\fP]" +.IP "\fBinterface\fP|\fBif\fP [\fB-M\fP] [\fBadd\fP|\fBdel iface(s)\fP]" If no parameter is given or the first parameter is neither "add" nor "del" the current interface settings are displayed. In order to add or delete interfaces specify "add" or "del" as first argument and append the interface names you wish to add or delete. Multiple interfaces can be specified. +The "-M" option tells batctl to not automatically create the batman-adv interface on "add" or to destroy it when "del" +removed all interfaces which belonged to it. .IP "\fBinterface\fP|\fBif\fP [\fBnew\fP|\fBdestroy\fP]" A batman-adv interface without attached interfaces can be created using "new". The parameter "destroy" can be used to free all attached interfaces and remove batman-adv interface. diff --git a/sys.c b/sys.c index 7cd2690..88dd39f 100644 --- a/sys.c +++ b/sys.c @@ -22,6 +22,7 @@
#include <unistd.h> #include <stdio.h> +#include <stdbool.h> #include <stdlib.h> #include <string.h> #include <errno.h> @@ -122,6 +123,7 @@ static void interface_usage(void) fprintf(stderr, "Usage: batctl [options] interface [parameters] [add|del iface(s)]\n"); fprintf(stderr, " batctl [options] interface [parameters] [new|destroy]\n"); fprintf(stderr, "parameters:\n"); + fprintf(stderr, " \t -M disable automatic creation/removal of batman-adv interface\n"); fprintf(stderr, " \t -h print this help\n"); }
@@ -385,12 +387,16 @@ int interface(char *mesh_iface, int argc, char **argv) unsigned int cnt; int rest_argc; char **rest_argv; + bool manual_mode = false;
- while ((optchar = getopt(argc, argv, "h")) != -1) { + while ((optchar = getopt(argc, argv, "hM")) != -1) { switch (optchar) { case 'h': interface_usage(); return EXIT_SUCCESS; + case 'M': + manual_mode = true; + break; default: interface_usage(); return EXIT_FAILURE; @@ -465,7 +471,7 @@ int interface(char *mesh_iface, int argc, char **argv)
/* get index of batman-adv interface - or try to create it */ ifmaster = if_nametoindex(mesh_iface); - if (!ifmaster && rest_argv[0][0] == 'a') { + if (!manual_mode && !ifmaster && rest_argv[0][0] == 'a') { ret = new_interface(mesh_iface); if (ret < 0) { fprintf(stderr, @@ -518,7 +524,7 @@ int interface(char *mesh_iface, int argc, char **argv) }
/* check if there is no interface left and then destroy mesh_iface */ - if (rest_argv[0][0] == 'd') { + if (!manual_mode && rest_argv[0][0] == 'd') { cnt = count_interfaces(mesh_iface); if (cnt == 0) destroy_interface(mesh_iface);
On Wed, Jul 13, 2016 at 05:30:20PM +0200, Sven Eckelmann wrote:
Users may not want to lose their configured batman-adv soft-interface when they remove a single interface from it. The default configuration may not working well enough in the network setup of the user and thus it should be possible to avoid that it gets reset to it when a new interface is added after the last one was removed.
This can be done by avoiding automatic creation of an interface when the command "add" is used together with the option "-M". The add would fail when the soft-interface disappeared for some reason and thus the soft-interface would not be created again with the default configuration. But more importantly, the "del" command can be informed with the option "-M" to not try to remove the soft-interface in the first place.
Currently wondering... have you thought about maybe memorizing when a batman-adv interface was created via the new "new" command? And in that case then transparently avoiding the automatic destruction.
Would be one less parameter to memorize for the user then.
On Dienstag, 20. September 2016 16:29:02 CEST Linus Lüssing wrote: [...]
This can be done by avoiding automatic creation of an interface when the command "add" is used together with the option "-M". The add would fail when the soft-interface disappeared for some reason and thus the soft-interface would not be created again with the default configuration. But more importantly, the "del" command can be informed with the option "-M" to not try to remove the soft-interface in the first place.
Currently wondering... have you thought about maybe memorizing when a batman-adv interface was created via the new "new" command? And in that case then transparently avoiding the automatic destruction.
Would be one less parameter to memorize for the user then.
batctl would have to remember this to work and batctl is no program which is running all the time. So it has to use temporary files... no.
One point here is to get rid of the batman-adv only files for creation/ destruction of itnerfaces and use the standard rtnl netlink. So I don't want to create a new interface which does this in the kernel which supports your idea.
Kind regards, Sven
The interface command has nothing to do anymore more with all the other configuration in sysfs. Thus all the "interface" command functions should be placed in a separate file.
Signed-off-by: Sven Eckelmann sven@narfation.org --- Makefile | 1 + functions.h | 2 + interface.c | 457 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ interface.h | 29 ++++ main.c | 1 + sys.c | 420 ------------------------------------------------------- sys.h | 1 - 7 files changed, 490 insertions(+), 421 deletions(-) create mode 100644 interface.c create mode 100644 interface.h
diff --git a/Makefile b/Makefile index 0cec413..5dc8fe8 100755 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ OBJ += debug.o OBJ += functions.o OBJ += genl.o OBJ += hash.o +OBJ += interface.o OBJ += ioctl.o OBJ += list-batman.o OBJ += main.o diff --git a/functions.h b/functions.h index 7757731..4c350f8 100644 --- a/functions.h +++ b/functions.h @@ -31,6 +31,8 @@ #define ETH_STR_LEN 17 #define BATMAN_ADV_TAG "batman-adv:"
+#define PATH_BUFF_LEN 200 + /* return time delta from start to end in milliseconds */ void start_timer(void); double end_timer(void); diff --git a/interface.c b/interface.c new file mode 100644 index 0000000..7abc80e --- /dev/null +++ b/interface.c @@ -0,0 +1,457 @@ +/* + * Copyright (C) 2009-2016 B.A.T.M.A.N. contributors: + * + * Marek Lindner mareklindner@neomailbox.ch + * + * 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 + * + */ + +#include "interface.h" + +#include <unistd.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> +#include <net/if.h> +#include <linux/if_link.h> +#include <netlink/netlink.h> +#include <netlink/msg.h> +#include <netlink/attr.h> + +#include "main.h" +#include "sys.h" +#include "functions.h" + +static void interface_usage(void) +{ + fprintf(stderr, "Usage: batctl [options] interface [parameters] [add|del iface(s)]\n"); + fprintf(stderr, " batctl [options] interface [parameters] [new|destroy]\n"); + fprintf(stderr, "parameters:\n"); + fprintf(stderr, " \t -M disable automatic creation/removal of batman-adv interface\n"); + fprintf(stderr, " \t -h print this help\n"); +} + +static struct nla_policy link_policy[IFLA_MAX + 1] = { + [IFLA_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, + [IFLA_MASTER] = { .type = NLA_U32 }, +}; + +struct print_interfaces_rtnl_arg { + int ifindex; +}; + +static int print_interfaces_rtnl_parse(struct nl_msg *msg, void *arg) +{ + struct print_interfaces_rtnl_arg *print_arg = arg; + struct nlattr *attrs[IFLA_MAX + 1]; + char path_buff[PATH_BUFF_LEN]; + struct ifinfomsg *ifm; + char *ifname; + int ret; + const char *status; + int master; + + ifm = nlmsg_data(nlmsg_hdr(msg)); + ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(*ifm), attrs, IFLA_MAX, + link_policy); + if (ret < 0) + goto err; + + if (!attrs[IFLA_IFNAME]) + goto err; + + if (!attrs[IFLA_MASTER]) + goto err; + + ifname = nla_get_string(attrs[IFLA_IFNAME]); + master = nla_get_u32(attrs[IFLA_MASTER]); + + /* required on older kernels which don't prefilter the results */ + if (master != print_arg->ifindex) + goto err; + + snprintf(path_buff, sizeof(path_buff), SYS_IFACE_STATUS_FMT, ifname); + ret = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); + if (ret != EXIT_SUCCESS) + status = "<error reading status>\n"; + else + status = line_ptr; + + printf("%s: %s", ifname, status); + + free(line_ptr); + line_ptr = NULL; + +err: + return NL_OK; +} + +static int print_interfaces(char *mesh_iface) +{ + struct print_interfaces_rtnl_arg print_arg; + + if (!file_exists(module_ver_path)) { + fprintf(stderr, "Error - batman-adv module has not been loaded\n"); + return EXIT_FAILURE; + } + + print_arg.ifindex = if_nametoindex(mesh_iface); + if (!print_arg.ifindex) + return EXIT_FAILURE; + + query_rtnl_link(print_arg.ifindex, print_interfaces_rtnl_parse, + &print_arg); + + return EXIT_SUCCESS; +} + +struct count_interfaces_rtnl_arg { + int ifindex; + unsigned int count; +}; + +static int count_interfaces_rtnl_parse(struct nl_msg *msg, void *arg) +{ + struct count_interfaces_rtnl_arg *count_arg = arg; + struct nlattr *attrs[IFLA_MAX + 1]; + struct ifinfomsg *ifm; + int ret; + int master; + + ifm = nlmsg_data(nlmsg_hdr(msg)); + ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(*ifm), attrs, IFLA_MAX, + link_policy); + if (ret < 0) + goto err; + + if (!attrs[IFLA_IFNAME]) + goto err; + + if (!attrs[IFLA_MASTER]) + goto err; + + master = nla_get_u32(attrs[IFLA_MASTER]); + + /* required on older kernels which don't prefilter the results */ + if (master != count_arg->ifindex) + goto err; + + count_arg->count++; + +err: + return NL_OK; +} + +static unsigned int count_interfaces(char *mesh_iface) +{ + struct count_interfaces_rtnl_arg count_arg; + + count_arg.count = 0; + count_arg.ifindex = if_nametoindex(mesh_iface); + if (!count_arg.ifindex) + return 0; + + query_rtnl_link(count_arg.ifindex, count_interfaces_rtnl_parse, + &count_arg); + + return count_arg.count; +} + +static int new_interface(const char *mesh_iface) +{ + struct ifinfomsg rt_hdr = { + .ifi_family = IFLA_UNSPEC, + }; + struct nlattr *linkinfo; + struct nl_msg *msg; + int err = 0; + int ret; + + msg = nlmsg_alloc_simple(RTM_NEWLINK, + NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); + if (!msg) { + return -ENOMEM; + } + + ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_string(msg, IFLA_IFNAME, mesh_iface); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + linkinfo = nla_nest_start(msg, IFLA_LINKINFO); + if (!linkinfo) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_string(msg, IFLA_INFO_KIND, "batadv"); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + nla_nest_end(msg, linkinfo); + + err = netlink_simple_request(msg); + +err_free_msg: + nlmsg_free(msg); + + return err; +} + +static int destroy_interface(const char *mesh_iface) +{ + struct ifinfomsg rt_hdr = { + .ifi_family = IFLA_UNSPEC, + }; + struct nl_msg *msg; + int err = 0; + int ret; + + msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK); + if (!msg) { + return -ENOMEM; + } + + ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_string(msg, IFLA_IFNAME, mesh_iface); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + err = netlink_simple_request(msg); + +err_free_msg: + nlmsg_free(msg); + + return err; +} + +static int set_master_interface(const char *iface, unsigned int ifmaster) +{ + struct ifinfomsg rt_hdr = { + .ifi_family = IFLA_UNSPEC, + }; + struct nl_msg *msg; + int err = 0; + int ret; + + msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK); + if (!msg) { + return -ENOMEM; + } + + ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_string(msg, IFLA_IFNAME, iface); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + ret = nla_put_u32(msg, IFLA_MASTER, ifmaster); + if (ret < 0) { + err = -ENOMEM; + goto err_free_msg; + } + + err = netlink_simple_request(msg); + +err_free_msg: + nlmsg_free(msg); + + return err; +} + +int interface(char *mesh_iface, int argc, char **argv) +{ + int i, optchar; + int ret; + unsigned int ifindex; + unsigned int ifmaster; + const char *long_op; + unsigned int cnt; + int rest_argc; + char **rest_argv; + bool manual_mode = false; + + while ((optchar = getopt(argc, argv, "hM")) != -1) { + switch (optchar) { + case 'h': + interface_usage(); + return EXIT_SUCCESS; + case 'M': + manual_mode = true; + break; + default: + interface_usage(); + return EXIT_FAILURE; + } + } + + rest_argc = argc - optind; + rest_argv = &argv[optind]; + + if (rest_argc == 0) + return print_interfaces(mesh_iface); + + if ((strcmp(rest_argv[0], "add") != 0) && (strcmp(rest_argv[0], "a") != 0) && + (strcmp(rest_argv[0], "del") != 0) && (strcmp(rest_argv[0], "d") != 0) && + (strcmp(rest_argv[0], "new") != 0) && (strcmp(rest_argv[0], "n") != 0) && + (strcmp(rest_argv[0], "destroy") != 0) && (strcmp(rest_argv[0], "D") != 0)) { + fprintf(stderr, "Error - unknown argument specified: %s\n", rest_argv[0]); + interface_usage(); + goto err; + } + + if (strcmp(rest_argv[0], "destroy") == 0) + rest_argv[0][0] = 'D'; + + switch (rest_argv[0][0]) { + case 'a': + case 'd': + if (rest_argc == 1) { + fprintf(stderr, + "Error - missing interface name(s) after '%s'\n", + rest_argv[0]); + interface_usage(); + goto err; + } + break; + case 'n': + case 'D': + if (rest_argc != 1) { + fprintf(stderr, + "Error - extra parameter after '%s'\n", + rest_argv[0]); + interface_usage(); + goto err; + } + break; + default: + break; + } + + switch (rest_argv[0][0]) { + case 'n': + ret = new_interface(mesh_iface); + if (ret < 0) { + fprintf(stderr, + "Error - failed to add new batman-adv interface: %s\n", + strerror(-ret)); + goto err; + } + return EXIT_SUCCESS; + case 'D': + ret = destroy_interface(mesh_iface); + if (ret < 0) { + fprintf(stderr, + "Error - failed to destroy batman-adv interface: %s\n", + strerror(-ret)); + goto err; + } + return EXIT_SUCCESS; + default: + break; + } + + /* get index of batman-adv interface - or try to create it */ + ifmaster = if_nametoindex(mesh_iface); + if (!manual_mode && !ifmaster && rest_argv[0][0] == 'a') { + ret = new_interface(mesh_iface); + if (ret < 0) { + fprintf(stderr, + "Error - failed to add new batman-adv interface: %s\n", + strerror(-ret)); + goto err; + } + + ifmaster = if_nametoindex(mesh_iface); + } + + if (!ifmaster) { + ret = -ENODEV; + fprintf(stderr, + "Error - failed to find batman-adv interface: %s\n", + strerror(-ret)); + goto err; + } + + /* make sure that batman-adv is loaded or was loaded by new_interface */ + if (!file_exists(module_ver_path)) { + fprintf(stderr, "Error - batman-adv module has not been loaded\n"); + goto err; + } + + for (i = 1; i < rest_argc; i++) { + ifindex = if_nametoindex(rest_argv[i]); + + if (!ifindex) { + fprintf(stderr, "Error - interface does not exist: %s\n", rest_argv[i]); + continue; + } + + if (rest_argv[0][0] == 'a') + ifindex = ifmaster; + else + ifindex = 0; + + ret = set_master_interface(rest_argv[i], ifindex); + if (ret < 0) { + if (rest_argv[0][0] == 'a') + long_op = "add"; + else + long_op = "delete"; + + fprintf(stderr, "Error - failed to %s interface %s: %s\n", + long_op, rest_argv[i], strerror(-ret)); + goto err; + } + } + + /* check if there is no interface left and then destroy mesh_iface */ + if (!manual_mode && rest_argv[0][0] == 'd') { + cnt = count_interfaces(mesh_iface); + if (cnt == 0) + destroy_interface(mesh_iface); + } + + return EXIT_SUCCESS; + +err: + return EXIT_FAILURE; +} diff --git a/interface.h b/interface.h new file mode 100644 index 0000000..0ea7bd5 --- /dev/null +++ b/interface.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2009-2016 B.A.T.M.A.N. contributors: + * + * Marek Lindner mareklindner@neomailbox.ch + * + * 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 + * + */ + +#ifndef _BATCTL_INTERFACE_H +#define _BATCTL_INTERFACE_H + +#include "main.h" + +int interface(char *mesh_iface, int argc, char **argv); + +#endif diff --git a/main.c b/main.c index 5e1ecc7..e67fc40 100644 --- a/main.c +++ b/main.c @@ -29,6 +29,7 @@ #include "main.h" #include "sys.h" #include "debug.h" +#include "interface.h" #include "ping.h" #include "translate.h" #include "traceroute.h" diff --git a/sys.c b/sys.c index 88dd39f..59cbdae 100644 --- a/sys.c +++ b/sys.c @@ -38,8 +38,6 @@ #include "functions.h" #include "debug.h"
-#define PATH_BUFF_LEN 200 - const char *sysfs_param_enable[] = { "enable", "disable", @@ -118,424 +116,6 @@ const struct settings_data batctl_settings[BATCTL_SETTINGS_NUM] = { }, };
-static void interface_usage(void) -{ - fprintf(stderr, "Usage: batctl [options] interface [parameters] [add|del iface(s)]\n"); - fprintf(stderr, " batctl [options] interface [parameters] [new|destroy]\n"); - fprintf(stderr, "parameters:\n"); - fprintf(stderr, " \t -M disable automatic creation/removal of batman-adv interface\n"); - fprintf(stderr, " \t -h print this help\n"); -} - -static struct nla_policy link_policy[IFLA_MAX + 1] = { - [IFLA_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, - [IFLA_MASTER] = { .type = NLA_U32 }, -}; - -struct print_interfaces_rtnl_arg { - int ifindex; -}; - -static int print_interfaces_rtnl_parse(struct nl_msg *msg, void *arg) -{ - struct print_interfaces_rtnl_arg *print_arg = arg; - struct nlattr *attrs[IFLA_MAX + 1]; - char path_buff[PATH_BUFF_LEN]; - struct ifinfomsg *ifm; - char *ifname; - int ret; - const char *status; - int master; - - ifm = nlmsg_data(nlmsg_hdr(msg)); - ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(*ifm), attrs, IFLA_MAX, - link_policy); - if (ret < 0) - goto err; - - if (!attrs[IFLA_IFNAME]) - goto err; - - if (!attrs[IFLA_MASTER]) - goto err; - - ifname = nla_get_string(attrs[IFLA_IFNAME]); - master = nla_get_u32(attrs[IFLA_MASTER]); - - /* required on older kernels which don't prefilter the results */ - if (master != print_arg->ifindex) - goto err; - - snprintf(path_buff, sizeof(path_buff), SYS_IFACE_STATUS_FMT, ifname); - ret = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); - if (ret != EXIT_SUCCESS) - status = "<error reading status>\n"; - else - status = line_ptr; - - printf("%s: %s", ifname, status); - - free(line_ptr); - line_ptr = NULL; - -err: - return NL_OK; -} - -static int print_interfaces(char *mesh_iface) -{ - struct print_interfaces_rtnl_arg print_arg; - - if (!file_exists(module_ver_path)) { - fprintf(stderr, "Error - batman-adv module has not been loaded\n"); - return EXIT_FAILURE; - } - - print_arg.ifindex = if_nametoindex(mesh_iface); - if (!print_arg.ifindex) - return EXIT_FAILURE; - - query_rtnl_link(print_arg.ifindex, print_interfaces_rtnl_parse, - &print_arg); - - return EXIT_SUCCESS; -} - -struct count_interfaces_rtnl_arg { - int ifindex; - unsigned int count; -}; - -static int count_interfaces_rtnl_parse(struct nl_msg *msg, void *arg) -{ - struct count_interfaces_rtnl_arg *count_arg = arg; - struct nlattr *attrs[IFLA_MAX + 1]; - struct ifinfomsg *ifm; - int ret; - int master; - - ifm = nlmsg_data(nlmsg_hdr(msg)); - ret = nlmsg_parse(nlmsg_hdr(msg), sizeof(*ifm), attrs, IFLA_MAX, - link_policy); - if (ret < 0) - goto err; - - if (!attrs[IFLA_IFNAME]) - goto err; - - if (!attrs[IFLA_MASTER]) - goto err; - - master = nla_get_u32(attrs[IFLA_MASTER]); - - /* required on older kernels which don't prefilter the results */ - if (master != count_arg->ifindex) - goto err; - - count_arg->count++; - -err: - return NL_OK; -} - -static unsigned int count_interfaces(char *mesh_iface) -{ - struct count_interfaces_rtnl_arg count_arg; - - count_arg.count = 0; - count_arg.ifindex = if_nametoindex(mesh_iface); - if (!count_arg.ifindex) - return 0; - - query_rtnl_link(count_arg.ifindex, count_interfaces_rtnl_parse, - &count_arg); - - return count_arg.count; -} - -static int new_interface(const char *mesh_iface) -{ - struct ifinfomsg rt_hdr = { - .ifi_family = IFLA_UNSPEC, - }; - struct nlattr *linkinfo; - struct nl_msg *msg; - int err = 0; - int ret; - - msg = nlmsg_alloc_simple(RTM_NEWLINK, - NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); - if (!msg) { - return -ENOMEM; - } - - ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); - if (ret < 0) { - err = -ENOMEM; - goto err_free_msg; - } - - ret = nla_put_string(msg, IFLA_IFNAME, mesh_iface); - if (ret < 0) { - err = -ENOMEM; - goto err_free_msg; - } - - linkinfo = nla_nest_start(msg, IFLA_LINKINFO); - if (!linkinfo) { - err = -ENOMEM; - goto err_free_msg; - } - - ret = nla_put_string(msg, IFLA_INFO_KIND, "batadv"); - if (ret < 0) { - err = -ENOMEM; - goto err_free_msg; - } - - nla_nest_end(msg, linkinfo); - - err = netlink_simple_request(msg); - -err_free_msg: - nlmsg_free(msg); - - return err; -} - -static int destroy_interface(const char *mesh_iface) -{ - struct ifinfomsg rt_hdr = { - .ifi_family = IFLA_UNSPEC, - }; - struct nl_msg *msg; - int err = 0; - int ret; - - msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK); - if (!msg) { - return -ENOMEM; - } - - ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); - if (ret < 0) { - err = -ENOMEM; - goto err_free_msg; - } - - ret = nla_put_string(msg, IFLA_IFNAME, mesh_iface); - if (ret < 0) { - err = -ENOMEM; - goto err_free_msg; - } - - err = netlink_simple_request(msg); - -err_free_msg: - nlmsg_free(msg); - - return err; -} - -static int set_master_interface(const char *iface, unsigned int ifmaster) -{ - struct ifinfomsg rt_hdr = { - .ifi_family = IFLA_UNSPEC, - }; - struct nl_msg *msg; - int err = 0; - int ret; - - msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK); - if (!msg) { - return -ENOMEM; - } - - ret = nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); - if (ret < 0) { - err = -ENOMEM; - goto err_free_msg; - } - - ret = nla_put_string(msg, IFLA_IFNAME, iface); - if (ret < 0) { - err = -ENOMEM; - goto err_free_msg; - } - - ret = nla_put_u32(msg, IFLA_MASTER, ifmaster); - if (ret < 0) { - err = -ENOMEM; - goto err_free_msg; - } - - err = netlink_simple_request(msg); - -err_free_msg: - nlmsg_free(msg); - - return err; -} - -int interface(char *mesh_iface, int argc, char **argv) -{ - int i, optchar; - int ret; - unsigned int ifindex; - unsigned int ifmaster; - const char *long_op; - unsigned int cnt; - int rest_argc; - char **rest_argv; - bool manual_mode = false; - - while ((optchar = getopt(argc, argv, "hM")) != -1) { - switch (optchar) { - case 'h': - interface_usage(); - return EXIT_SUCCESS; - case 'M': - manual_mode = true; - break; - default: - interface_usage(); - return EXIT_FAILURE; - } - } - - rest_argc = argc - optind; - rest_argv = &argv[optind]; - - if (rest_argc == 0) - return print_interfaces(mesh_iface); - - if ((strcmp(rest_argv[0], "add") != 0) && (strcmp(rest_argv[0], "a") != 0) && - (strcmp(rest_argv[0], "del") != 0) && (strcmp(rest_argv[0], "d") != 0) && - (strcmp(rest_argv[0], "new") != 0) && (strcmp(rest_argv[0], "n") != 0) && - (strcmp(rest_argv[0], "destroy") != 0) && (strcmp(rest_argv[0], "D") != 0)) { - fprintf(stderr, "Error - unknown argument specified: %s\n", rest_argv[0]); - interface_usage(); - goto err; - } - - if (strcmp(rest_argv[0], "destroy") == 0) - rest_argv[0][0] = 'D'; - - switch (rest_argv[0][0]) { - case 'a': - case 'd': - if (rest_argc == 1) { - fprintf(stderr, - "Error - missing interface name(s) after '%s'\n", - rest_argv[0]); - interface_usage(); - goto err; - } - break; - case 'n': - case 'D': - if (rest_argc != 1) { - fprintf(stderr, - "Error - extra parameter after '%s'\n", - rest_argv[0]); - interface_usage(); - goto err; - } - break; - default: - break; - } - - switch (rest_argv[0][0]) { - case 'n': - ret = new_interface(mesh_iface); - if (ret < 0) { - fprintf(stderr, - "Error - failed to add new batman-adv interface: %s\n", - strerror(-ret)); - goto err; - } - return EXIT_SUCCESS; - case 'D': - ret = destroy_interface(mesh_iface); - if (ret < 0) { - fprintf(stderr, - "Error - failed to destroy batman-adv interface: %s\n", - strerror(-ret)); - goto err; - } - return EXIT_SUCCESS; - default: - break; - } - - /* get index of batman-adv interface - or try to create it */ - ifmaster = if_nametoindex(mesh_iface); - if (!manual_mode && !ifmaster && rest_argv[0][0] == 'a') { - ret = new_interface(mesh_iface); - if (ret < 0) { - fprintf(stderr, - "Error - failed to add new batman-adv interface: %s\n", - strerror(-ret)); - goto err; - } - - ifmaster = if_nametoindex(mesh_iface); - } - - if (!ifmaster) { - ret = -ENODEV; - fprintf(stderr, - "Error - failed to find batman-adv interface: %s\n", - strerror(-ret)); - goto err; - } - - /* make sure that batman-adv is loaded or was loaded by new_interface */ - if (!file_exists(module_ver_path)) { - fprintf(stderr, "Error - batman-adv module has not been loaded\n"); - goto err; - } - - for (i = 1; i < rest_argc; i++) { - ifindex = if_nametoindex(rest_argv[i]); - - if (!ifindex) { - fprintf(stderr, "Error - interface does not exist: %s\n", rest_argv[i]); - continue; - } - - if (rest_argv[0][0] == 'a') - ifindex = ifmaster; - else - ifindex = 0; - - ret = set_master_interface(rest_argv[i], ifindex); - if (ret < 0) { - if (rest_argv[0][0] == 'a') - long_op = "add"; - else - long_op = "delete"; - - fprintf(stderr, "Error - failed to %s interface %s: %s\n", - long_op, rest_argv[i], strerror(-ret)); - goto err; - } - } - - /* check if there is no interface left and then destroy mesh_iface */ - if (!manual_mode && rest_argv[0][0] == 'd') { - cnt = count_interfaces(mesh_iface); - if (cnt == 0) - destroy_interface(mesh_iface); - } - - return EXIT_SUCCESS; - -err: - return EXIT_FAILURE; -} - static void log_level_usage(void) { fprintf(stderr, "Usage: batctl [options] loglevel [parameters] [level[ level[ level]]...]\n"); diff --git a/sys.h b/sys.h index da2769d..be9480e 100644 --- a/sys.h +++ b/sys.h @@ -70,7 +70,6 @@ extern const char *sysfs_param_enable[]; extern const char *sysfs_param_server[]; extern const struct settings_data batctl_settings[BATCTL_SETTINGS_NUM];
-int interface(char *mesh_iface, int argc, char **argv); int handle_loglevel(char *mesh_iface, int argc, char **argv); int handle_sys_setting(char *mesh_iface, int setting, int argc, char **argv); int handle_gw_setting(char *mesh_iface, int argc, char **argv);
The check_mesh_iface* functions are not used to modify anything in sysfs. So they are better placed in the common/shared functions file than in sys.c.
Signed-off-by: Sven Eckelmann sven@narfation.org --- functions.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ functions.h | 2 ++ sys.c | 56 -------------------------------------------------------- sys.h | 2 -- 4 files changed, 61 insertions(+), 58 deletions(-)
diff --git a/functions.c b/functions.c index 962efcf..f9feca4 100644 --- a/functions.c +++ b/functions.c @@ -32,6 +32,7 @@ #include <string.h> #include <errno.h> #include <fcntl.h> +#include <dirent.h> #include <sys/time.h> #include <netinet/in.h> #include <stdint.h> @@ -56,6 +57,8 @@ #include "debugfs.h" #include "netlink.h"
+#define PATH_BUFF_LEN 200 + static struct timeval start_time; static char *host_name; char *line_ptr = NULL; @@ -1011,3 +1014,59 @@ err_free_sock:
return err; } + +int check_mesh_iface(char *mesh_iface) +{ + char *base_dev = NULL; + char path_buff[PATH_BUFF_LEN]; + int ret = -1, vid; + DIR *dir; + + /* use the parent interface if this is a VLAN */ + vid = vlan_get_link(mesh_iface, &base_dev); + if (vid >= 0) + snprintf(path_buff, PATH_BUFF_LEN, SYS_VLAN_PATH, base_dev, vid); + else + snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, mesh_iface); + + /* try to open the mesh sys directory */ + dir = opendir(path_buff); + if (!dir) + goto out; + + closedir(dir); + + ret = 0; +out: + if (base_dev) + free(base_dev); + + return ret; +} + +int check_mesh_iface_ownership(char *mesh_iface, char *hard_iface) +{ + char path_buff[PATH_BUFF_LEN]; + int res; + + /* check if this device actually belongs to the mesh interface */ + snprintf(path_buff, sizeof(path_buff), SYS_MESH_IFACE_FMT, hard_iface); + res = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); + if (res != EXIT_SUCCESS) { + fprintf(stderr, "Error - the directory '%s' could not be read: %s\n", + path_buff, strerror(errno)); + fprintf(stderr, "Is the batman-adv module loaded and sysfs mounted ?\n"); + return EXIT_FAILURE; + } + + if (line_ptr[strlen(line_ptr) - 1] == '\n') + line_ptr[strlen(line_ptr) - 1] = '\0'; + + if (strcmp(line_ptr, mesh_iface) != 0) { + fprintf(stderr, "Error - interface %s is part of batman network %s, not %s\n", + hard_iface, line_ptr, mesh_iface); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/functions.h b/functions.h index 4c350f8..e413d6b 100644 --- a/functions.h +++ b/functions.h @@ -50,6 +50,8 @@ struct ether_addr *resolve_mac(const char *asc); int vlan_get_link(const char *ifname, char **parent);\ int query_rtnl_link(int ifindex, nl_recvmsg_msg_cb_t func, void *arg); int netlink_simple_request(struct nl_msg *msg); +int check_mesh_iface(char *mesh_iface); +int check_mesh_iface_ownership(char *mesh_iface, char *hard_iface);
int print_routing_algos(void); extern char *line_ptr; diff --git a/sys.c b/sys.c index 59cbdae..b524340 100644 --- a/sys.c +++ b/sys.c @@ -511,59 +511,3 @@ free_buff: out: return res; } - -int check_mesh_iface(char *mesh_iface) -{ - char *base_dev = NULL; - char path_buff[PATH_BUFF_LEN]; - int ret = -1, vid; - DIR *dir; - - /* use the parent interface if this is a VLAN */ - vid = vlan_get_link(mesh_iface, &base_dev); - if (vid >= 0) - snprintf(path_buff, PATH_BUFF_LEN, SYS_VLAN_PATH, base_dev, vid); - else - snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, mesh_iface); - - /* try to open the mesh sys directory */ - dir = opendir(path_buff); - if (!dir) - goto out; - - closedir(dir); - - ret = 0; -out: - if (base_dev) - free(base_dev); - - return ret; -} - -int check_mesh_iface_ownership(char *mesh_iface, char *hard_iface) -{ - char path_buff[PATH_BUFF_LEN]; - int res; - - /* check if this device actually belongs to the mesh interface */ - snprintf(path_buff, sizeof(path_buff), SYS_MESH_IFACE_FMT, hard_iface); - res = read_file("", path_buff, USE_READ_BUFF | SILENCE_ERRORS, 0, 0, 0); - if (res != EXIT_SUCCESS) { - fprintf(stderr, "Error - the directory '%s' could not be read: %s\n", - path_buff, strerror(errno)); - fprintf(stderr, "Is the batman-adv module loaded and sysfs mounted ?\n"); - return EXIT_FAILURE; - } - - if (line_ptr[strlen(line_ptr) - 1] == '\n') - line_ptr[strlen(line_ptr) - 1] = '\0'; - - if (strcmp(line_ptr, mesh_iface) != 0) { - fprintf(stderr, "Error - interface %s is part of batman network %s, not %s\n", - hard_iface, line_ptr, mesh_iface); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/sys.h b/sys.h index be9480e..1365e76 100644 --- a/sys.h +++ b/sys.h @@ -74,7 +74,5 @@ int handle_loglevel(char *mesh_iface, int argc, char **argv); int handle_sys_setting(char *mesh_iface, int setting, int argc, char **argv); int handle_gw_setting(char *mesh_iface, int argc, char **argv); int handle_ra_setting(int argc, char **argv); -int check_mesh_iface(char *mesh_iface); -int check_mesh_iface_ownership(char *mesh_iface, char *hard_iface);
#endif
b.a.t.m.a.n@lists.open-mesh.org