Hi,
some functionality in batctl was previously using sysfs to implement some of its features. Since the sysfs support can now be disabled in the kernel, these features were broken in such setups. This was especially problematic for functionality which depends on the main mesh interface. The check to make sure that this is interface is a valid batadv interface were exclusively using sysfs.
Switching them to (or actually extending them to support) netlink should fix this. The old sysfs functionality is still here but can be removed in the future.
Kind regards, Sven
Sven Eckelmann (3): batctl: Support checking of meshif without sysfs batctl: Reimplement VLAN translation using helper batctl: Add netlink fallback for sysfs' iface_status
debug.c | 2 +- functions.c | 407 ++++++++++++++++++++++++++++++---------------------- functions.h | 2 +- interface.c | 92 +++++++++++- 4 files changed, 331 insertions(+), 172 deletions(-)
batctl checks whether the supplied interface is a batadv interface. And for hardif filters, whether this hardif is part of the selected meshif. This was done traditionally using the sysfs files which batman-adv creates.
It is now possible to build the kernel module without sysfs support. These checks must therefore also work when sysfs is not available. And since the sysfs interface support was replaced in batctl by netlink commands, the check should also be implemented using using NETLINK_ROUTE.
Signed-off-by: Sven Eckelmann sven@narfation.org --- debug.c | 2 +- functions.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++----- functions.h | 2 +- 3 files changed, 176 insertions(+), 20 deletions(-)
diff --git a/debug.c b/debug.c index 4a3b6cb..597d59c 100644 --- a/debug.c +++ b/debug.c @@ -115,7 +115,7 @@ int handle_debug_table(struct state *state, int argc, char **argv) return EXIT_FAILURE; }
- if (check_mesh_iface_ownership(state->mesh_iface, optarg) != EXIT_SUCCESS) + if (check_mesh_iface_ownership(state, optarg) != EXIT_SUCCESS) return EXIT_FAILURE;
orig_iface = optarg; diff --git a/functions.c b/functions.c index 9720257..b52db08 100644 --- a/functions.c +++ b/functions.c @@ -1039,37 +1039,178 @@ int translate_mesh_iface(struct state *state) return 0; }
-int check_mesh_iface(struct state *state) +struct rtnl_link_iface_data { + uint8_t kind_found:1; + uint8_t master_found:1; + char kind[IF_NAMESIZE]; + unsigned int master; +}; + +static int query_rtnl_link_single_parse(struct nl_msg *msg, void *arg) +{ + static struct nla_policy link_policy[IFLA_MAX + 1] = { + [IFLA_LINKINFO] = { .type = NLA_NESTED }, + [IFLA_MASTER] = { .type = NLA_U32 }, + }; + static struct nla_policy link_info_policy[IFLA_INFO_MAX + 1] = { + [IFLA_INFO_KIND] = { .type = NLA_STRING }, + }; + + struct rtnl_link_iface_data *link_data = arg; + struct nlattr *li[IFLA_INFO_MAX + 1]; + struct nlmsghdr *n = nlmsg_hdr(msg); + struct nlattr *tb[IFLA_MAX + 1]; + char *type; + int ret; + + if (!nlmsg_valid_hdr(n, sizeof(struct ifinfomsg))) + return NL_OK; + + ret = nlmsg_parse(n, sizeof(struct ifinfomsg), tb, IFLA_MAX, + link_policy); + if (ret < 0) + return NL_OK; + + if (tb[IFLA_MASTER]) { + link_data->master = nla_get_u32(tb[IFLA_MASTER]); + link_data->master_found = true; + } + + /* parse subattributes linkinfo */ + if (!tb[IFLA_LINKINFO]) + return NL_OK; + + ret = nla_parse_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], + link_info_policy); + if (ret < 0) + return NL_OK; + + if (li[IFLA_INFO_KIND]) { + type = nla_get_string(li[IFLA_INFO_KIND]); + strncpy(link_data->kind, type, sizeof(link_data->kind)); + link_data->kind[sizeof(link_data->kind) - 1] = '\0'; + + link_data->kind_found = true; + } + + return NL_STOP; +} + +static int query_rtnl_link_single(int mesh_ifindex, + struct rtnl_link_iface_data *link_data) +{ + struct ifinfomsg ifinfo = { + .ifi_family = AF_UNSPEC, + .ifi_index = mesh_ifindex, + }; + struct nl_cb *cb = NULL; + struct nl_sock *sock; + int ret; + + link_data->kind_found = false; + link_data->master_found = false; + + sock = nl_socket_alloc(); + if (!sock) + return -1; + + ret = nl_connect(sock, NETLINK_ROUTE); + if (ret < 0) + goto free_sock; + + ret = nl_send_simple(sock, RTM_GETLINK, NLM_F_REQUEST, + &ifinfo, sizeof(ifinfo)); + if (ret < 0) + goto free_sock; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + goto free_sock; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, query_rtnl_link_single_parse, + link_data); + nl_recvmsgs(sock, cb); + + nl_cb_put(cb); +free_sock: + nl_socket_free(sock); + + return 0; +} + +int check_mesh_iface_netlink(struct state *state) +{ + struct rtnl_link_iface_data link_data; + + query_rtnl_link_single(state->mesh_ifindex, &link_data); + if (!link_data.kind_found) + return -1; + + if (strcmp(link_data.kind, "batadv") != 0) + return -1; + + return 0; +} + +static int check_mesh_iface_sysfs(struct state *state) { char path_buff[PATH_BUFF_LEN]; - int ret = -1; DIR *dir;
- /* use the parent interface if this is a VLAN */ - if (state->vid >= 0) - snprintf(path_buff, PATH_BUFF_LEN, SYS_VLAN_PATH, - state->mesh_iface, state->vid); - else - snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, - state->mesh_iface); - /* try to open the mesh sys directory */ + snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, + state->mesh_iface); + dir = opendir(path_buff); if (!dir) - goto out; + return -1;
closedir(dir);
+ return 0; +} + +int check_mesh_iface(struct state *state) +{ + int ret; + state->mesh_ifindex = if_nametoindex(state->mesh_iface); if (state->mesh_ifindex == 0) - goto out; + return -1;
- ret = 0; -out: - return ret; + ret = check_mesh_iface_netlink(state); + if (ret == 0) + return ret; + + ret = check_mesh_iface_sysfs(state); + if (ret == 0) + return ret; + + return -1; }
-int check_mesh_iface_ownership(char *mesh_iface, char *hard_iface) +static int check_mesh_iface_ownership_netlink(struct state *state, + char *hard_iface) +{ + struct rtnl_link_iface_data link_data; + unsigned int hardif_index; + + hardif_index = if_nametoindex(hard_iface); + if (hardif_index == 0) + return EXIT_FAILURE; + + query_rtnl_link_single(hardif_index, &link_data); + if (!link_data.master_found) + return EXIT_FAILURE; + + if (state->mesh_ifindex != link_data.master) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +static int check_mesh_iface_ownership_sysfs(struct state *state, + char *hard_iface) { char path_buff[PATH_BUFF_LEN]; int res; @@ -1087,15 +1228,30 @@ int check_mesh_iface_ownership(char *mesh_iface, char *hard_iface) if (line_ptr[strlen(line_ptr) - 1] == '\n') line_ptr[strlen(line_ptr) - 1] = '\0';
- if (strcmp(line_ptr, mesh_iface) != 0) { + if (strcmp(line_ptr, state->mesh_iface) != 0) { fprintf(stderr, "Error - interface %s is part of batman network %s, not %s\n", - hard_iface, line_ptr, mesh_iface); + hard_iface, line_ptr, state->mesh_iface); return EXIT_FAILURE; }
return EXIT_SUCCESS; }
+int check_mesh_iface_ownership(struct state *state, char *hard_iface) +{ + int ret; + + ret = check_mesh_iface_ownership_netlink(state, hard_iface); + if (ret == EXIT_SUCCESS) + return EXIT_SUCCESS; + + ret = check_mesh_iface_ownership_sysfs(state, hard_iface); + if (ret == EXIT_SUCCESS) + return ret; + + return EXIT_FAILURE; +} + static int get_random_bytes_syscall(void *buf __maybe_unused, size_t buflen __maybe_unused) { diff --git a/functions.h b/functions.h index 23186e5..02f7823 100644 --- a/functions.h +++ b/functions.h @@ -53,7 +53,7 @@ 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); +int check_mesh_iface_ownership(struct state *state, char *hard_iface);
void get_random_bytes(void *buf, size_t buflen); void check_root_or_die(const char *cmd);
The functionality to translate VLAN interfaces to mesh interfaces and VID is using NETLINK_ROUTE like the query_rtnl_link_single helper function. Only minimal changes are required to also provide the link to the underlying device and the VID.
Signed-off-by: Sven Eckelmann sven@narfation.org --- functions.c | 229 ++++++++++++++++------------------------------------ 1 file changed, 71 insertions(+), 158 deletions(-)
diff --git a/functions.c b/functions.c index b52db08..bb7f517 100644 --- a/functions.c +++ b/functions.c @@ -760,149 +760,6 @@ struct ether_addr *resolve_mac(const char *asc) return mac_result; }
-struct vlan_get_link_nl_arg { - char *iface; - int vid; -}; - -static struct nla_policy info_data_link_policy[IFLA_MAX + 1] = { - [IFLA_LINKINFO] = { .type = NLA_NESTED }, - [IFLA_LINK] = { .type = NLA_U32 }, -}; - -static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = { - [IFLA_INFO_DATA] = { .type = NLA_NESTED }, -}; - -static struct nla_policy vlan_policy[IFLA_VLAN_MAX + 1] = { - [IFLA_VLAN_ID] = { .type = NLA_U16 }, -}; - -/** - * vlan_get_link_parse - parse a get_link rtnl message and extract the important - * data - * @msg: the reply msg - * @arg: pointer to the buffer which will store the return values - * - * Saves the vid in arg::vid in case of success or -1 otherwise - */ -static int vlan_get_link_parse(struct nl_msg *msg, void *arg) -{ - struct vlan_get_link_nl_arg *nl_arg = arg; - struct nlmsghdr *n = nlmsg_hdr(msg); - struct nlattr *tb[IFLA_MAX + 1]; - struct nlattr *li[IFLA_INFO_MAX + 1]; - struct nlattr *vi[IFLA_VLAN_MAX + 1]; - int ret; - int idx; - - if (!nlmsg_valid_hdr(n, sizeof(struct ifinfomsg))) - return -NLE_MSG_TOOSHORT; - - ret = nlmsg_parse(n, sizeof(struct ifinfomsg), tb, IFLA_MAX, - info_data_link_policy); - if (ret < 0) - return ret; - - if (!tb[IFLA_LINK]) - return -NLE_MISSING_ATTR; - - /* parse subattributes linkinfo */ - if (!tb[IFLA_LINKINFO]) - return -NLE_MISSING_ATTR; - - ret = nla_parse_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], - info_data_link_info_policy); - if (ret < 0) - return ret; - - if (!li[IFLA_INFO_KIND]) - return -NLE_MISSING_ATTR; - - if (strcmp(nla_data(li[IFLA_INFO_KIND]), "vlan") != 0) - goto err; - - /* parse subattributes info_data for vlan */ - if (!li[IFLA_INFO_DATA]) - return -NLE_MISSING_ATTR; - - ret = nla_parse_nested(vi, IFLA_VLAN_MAX, li[IFLA_INFO_DATA], - vlan_policy); - if (ret < 0) - return ret; - - if (!vi[IFLA_VLAN_ID]) - return -NLE_MISSING_ATTR; - - /* get parent link name */ - idx = *(int *)nla_data(tb[IFLA_LINK]); - - if (!if_indextoname(idx, nl_arg->iface)) - goto err; - - /* get the corresponding vid */ - nl_arg->vid = *(int *)nla_data(vi[IFLA_VLAN_ID]); - -err: - if (nl_arg->vid >= 0) - return NL_STOP; - else - return NL_OK; -} - -/** - * vlan_get_link - convert a VLAN interface into its parent one - * @ifname: the interface to convert - * @parent: buffer where the parent interface name will be written - * (minimum IF_NAMESIZE) - * - * Returns the vlan identifier on success or -1 on error - */ -static int vlan_get_link(const char *ifname, char *parent) -{ - struct nl_sock *sock; - int ret; - struct ifinfomsg ifinfo = { - .ifi_family = AF_UNSPEC, - .ifi_index = if_nametoindex(ifname), - }; - struct nl_cb *cb = NULL; - struct vlan_get_link_nl_arg arg = { - .iface = parent, - .vid = -1, - }; - - sock = nl_socket_alloc(); - if (!sock) - goto err; - - ret = nl_connect(sock, NETLINK_ROUTE); - if (ret < 0) - goto err; - - ret = nl_send_simple(sock, RTM_GETLINK, NLM_F_REQUEST, - &ifinfo, sizeof(ifinfo)); - if (ret < 0) - goto err; - - cb = nl_cb_alloc(NL_CB_DEFAULT); - if (!cb) - goto err; - - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, vlan_get_link_parse, &arg); - ret = nl_recvmsgs(sock, cb); - if (ret < 0) - goto err; - -err: - if (cb) - nl_cb_put(cb); - if (sock) - nl_socket_free(sock); - - return arg.vid; -} - int query_rtnl_link(int ifindex, nl_recvmsg_msg_cb_t func, void *arg) { struct ifinfomsg rt_hdr = { @@ -1025,25 +882,15 @@ int netlink_simple_request(struct nl_msg *msg) return err; }
-int translate_mesh_iface(struct state *state) -{ - state->vid = vlan_get_link(state->arg_iface, state->mesh_iface); - if (state->vid < 0) { - /* if there is no iface then the argument must be the - * mesh interface - */ - snprintf(state->mesh_iface, sizeof(state->mesh_iface), "%s", - state->arg_iface); - } - - return 0; -} - struct rtnl_link_iface_data { uint8_t kind_found:1; uint8_t master_found:1; + uint8_t link_found:1; + uint8_t vid_found:1; char kind[IF_NAMESIZE]; unsigned int master; + unsigned int link; + uint16_t vid; };
static int query_rtnl_link_single_parse(struct nl_msg *msg, void *arg) @@ -1051,13 +898,19 @@ static int query_rtnl_link_single_parse(struct nl_msg *msg, void *arg) static struct nla_policy link_policy[IFLA_MAX + 1] = { [IFLA_LINKINFO] = { .type = NLA_NESTED }, [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_LINK] = { .type = NLA_U32 }, }; static struct nla_policy link_info_policy[IFLA_INFO_MAX + 1] = { [IFLA_INFO_KIND] = { .type = NLA_STRING }, + [IFLA_INFO_DATA] = { .type = NLA_NESTED }, + }; + static struct nla_policy vlan_policy[IFLA_VLAN_MAX + 1] = { + [IFLA_VLAN_ID] = { .type = NLA_U16 }, };
struct rtnl_link_iface_data *link_data = arg; struct nlattr *li[IFLA_INFO_MAX + 1]; + struct nlattr *vi[IFLA_VLAN_MAX + 1]; struct nlmsghdr *n = nlmsg_hdr(msg); struct nlattr *tb[IFLA_MAX + 1]; char *type; @@ -1076,6 +929,11 @@ static int query_rtnl_link_single_parse(struct nl_msg *msg, void *arg) link_data->master_found = true; }
+ if (tb[IFLA_LINK]) { + link_data->link = nla_get_u32(tb[IFLA_LINK]); + link_data->link_found = true; + } + /* parse subattributes linkinfo */ if (!tb[IFLA_LINKINFO]) return NL_OK; @@ -1093,6 +951,19 @@ static int query_rtnl_link_single_parse(struct nl_msg *msg, void *arg) link_data->kind_found = true; }
+ if (!li[IFLA_INFO_DATA]) + return NL_OK; + + ret = nla_parse_nested(vi, IFLA_VLAN_MAX, li[IFLA_INFO_DATA], + vlan_policy); + if (ret < 0) + return NL_OK; + + if (vi[IFLA_VLAN_ID]) { + link_data->vid = nla_get_u16(vi[IFLA_VLAN_ID]); + link_data->vid_found = true; + } + return NL_STOP; }
@@ -1109,6 +980,8 @@ static int query_rtnl_link_single(int mesh_ifindex,
link_data->kind_found = false; link_data->master_found = false; + link_data->link_found = false; + link_data->vid_found = false;
sock = nl_socket_alloc(); if (!sock) @@ -1138,7 +1011,47 @@ static int query_rtnl_link_single(int mesh_ifindex, return 0; }
-int check_mesh_iface_netlink(struct state *state) +int translate_mesh_iface(struct state *state) +{ + struct rtnl_link_iface_data link_data; + unsigned int arg_ifindex; + + arg_ifindex = if_nametoindex(state->arg_iface); + if (arg_ifindex == 0) + goto fallback_meshif; + + query_rtnl_link_single(arg_ifindex, &link_data); + if (!link_data.vid_found) + goto fallback_meshif; + + if (!link_data.link_found) + goto fallback_meshif; + + if (!link_data.kind_found) + goto fallback_meshif; + + if (strcmp(link_data.kind, "vlan") != 0) + goto fallback_meshif; + + if (!if_indextoname(link_data.link, state->mesh_iface)) + goto fallback_meshif; + + state->vid = link_data.vid; + + return 0; + +fallback_meshif: + /* 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; + + return 0; +} + +static int check_mesh_iface_netlink(struct state *state) { struct rtnl_link_iface_data link_data;
The batman-adv kernel module can now be compiled without support for sysfs. But the status of an interface in the list generated by `batctl interface` can only get the status via the per hardif sysfs file iface_status. To still have some information, fallback to BATADV_CMD_GET_HARDIF if the sysfs file is not available.
Signed-off-by: Sven Eckelmann sven@narfation.org --- interface.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-)
diff --git a/interface.c b/interface.c index 667a308..630dd91 100644 --- a/interface.c +++ b/interface.c @@ -24,6 +24,8 @@ #include "sys.h" #include "functions.h"
+#define IFACE_STATUS_LEN 256 + static void interface_usage(void) { fprintf(stderr, "Usage: batctl [options] interface [parameters] [add|del iface(s)]\n"); @@ -33,6 +35,92 @@ static void interface_usage(void) fprintf(stderr, " \t -h print this help\n"); }
+static int get_iface_status_netlink_parse(struct nl_msg *msg, void *arg) +{ + + struct nlattr *attrs[NUM_BATADV_ATTR]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + char *iface_status = arg; + struct genlmsghdr *ghdr; + + if (!genlmsg_valid_hdr(nlh, 0)) + return NL_OK; + + ghdr = nlmsg_data(nlh); + if (ghdr->cmd != BATADV_CMD_GET_HARDIF) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_netlink_policy)) + return NL_OK; + + if (attrs[BATADV_ATTR_ACTIVE]) + strncpy(iface_status, "active\n", IFACE_STATUS_LEN); + else + strncpy(iface_status, "inactive\n", IFACE_STATUS_LEN); + + iface_status[IFACE_STATUS_LEN - 1] = '\0'; + + return NL_STOP; +} + +static char *get_iface_status_netlink(unsigned int meshif, unsigned int hardif, + char *iface_status) +{ + struct nl_sock *sock; + struct nl_msg *msg; + int batadv_family; + struct nl_cb *cb; + int ret; + + strncpy(iface_status, "<error reading status>\n", IFACE_STATUS_LEN); + iface_status[IFACE_STATUS_LEN - 1] = '\0'; + + sock = nl_socket_alloc(); + if (!sock) + return iface_status; + + ret = genl_connect(sock); + if (ret < 0) + goto err_free_sock; + + batadv_family = genl_ctrl_resolve(sock, BATADV_NL_NAME); + if (ret < 0) + goto err_free_sock; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + goto err_free_sock; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_iface_status_netlink_parse, + iface_status); + + msg = nlmsg_alloc(); + if (!msg) + goto err_free_cb; + + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, batadv_family, + 0, 0, BATADV_CMD_GET_HARDIF, 1); + + nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, meshif); + nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX, hardif); + + 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 iface_status; +} + static struct nla_policy link_policy[IFLA_MAX + 1] = { [IFLA_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [IFLA_MASTER] = { .type = NLA_U32 }, @@ -45,6 +133,7 @@ struct print_interfaces_rtnl_arg { static int print_interfaces_rtnl_parse(struct nl_msg *msg, void *arg) { struct print_interfaces_rtnl_arg *print_arg = arg; + char iface_status[IFACE_STATUS_LEN]; struct nlattr *attrs[IFLA_MAX + 1]; char path_buff[PATH_BUFF_LEN]; struct ifinfomsg *ifm; @@ -75,7 +164,8 @@ static int print_interfaces_rtnl_parse(struct nl_msg *msg, void *arg) 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"; + status = get_iface_status_netlink(master, ifm->ifi_index, + iface_status); else status = line_ptr;
b.a.t.m.a.n@lists.open-mesh.org