Other part of batctl may require to open and send a message to RTNL. Extract this code and put it in a generic helper to simplify future uses.
Signed-off-by: Antonio Quartulli antonio@meshcoding.com --- functions.c | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-)
diff --git a/functions.c b/functions.c index 13ba190..a73b493 100644 --- a/functions.c +++ b/functions.c @@ -497,30 +497,28 @@ static void request_mac_resolve(int ai_family, const void *l3addr) close(sock); }
-static int resolve_mac_from_cache_open(int ai_family) +/** + * rtnl_open - open a socket to rtnl and send a request + * @req: the buffer containing the request to send + * @len: the length of the buffer containing the request + * @protocol: the protocol to use when opening the socket + * + * Return 0 on success or a negative error code otherwise + */ +static int rtnl_open(void *req, size_t len, int protocol) { - int socknl; - int ret; - struct { - struct nlmsghdr hdr; - struct ndmsg msg; - } nlreq; - struct sockaddr_nl addrnl; static uint32_t nr_call = 0; uint32_t pid = (++nr_call + getpid()) & 0x3FFFFF; + struct sockaddr_nl addrnl; + int socknl; + int ret;
memset(&addrnl, 0, sizeof(addrnl)); addrnl.nl_family = AF_NETLINK; addrnl.nl_pid = pid; addrnl.nl_groups = 0;
- memset(&nlreq, 0, sizeof(nlreq)); - nlreq.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(nlreq.msg)); - nlreq.hdr.nlmsg_type = RTM_GETNEIGH; - nlreq.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - nlreq.msg.ndm_family = ai_family; - - socknl = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + socknl = socket(AF_NETLINK, SOCK_RAW, protocol); if (socknl < 0) goto out;
@@ -528,7 +526,7 @@ static int resolve_mac_from_cache_open(int ai_family) if (ret < 0) goto outclose;
- ret = send(socknl, &nlreq, nlreq.hdr.nlmsg_len, 0); + ret = send(socknl, req, len, 0); if (ret < 0) goto outclose; out: @@ -538,6 +536,22 @@ outclose: return ret; }
+static int resolve_mac_from_cache_open(int ai_family) +{ + struct { + struct nlmsghdr hdr; + struct ndmsg msg; + } nlreq; + + memset(&nlreq, 0, sizeof(nlreq)); + nlreq.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(nlreq.msg)); + nlreq.hdr.nlmsg_type = RTM_GETNEIGH; + nlreq.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlreq.msg.ndm_family = ai_family; + + return rtnl_open(&nlreq, sizeof(nlreq), NETLINK_ROUTE); +} + static ssize_t resolve_mac_from_cache_dump(int sock, void **buf, size_t *buflen) { struct iovec iov;
This functions can be used to convert a vlan interface into the real one and to extract the vlan identifier.
Signed-off-by: Antonio Quartulli antonio@meshcoding.com --- functions.c | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ functions.h | 1 + 2 files changed, 181 insertions(+)
diff --git a/functions.c b/functions.c index a73b493..4934675 100644 --- a/functions.c +++ b/functions.c @@ -40,6 +40,7 @@ #include <linux/neighbour.h> #include <sys/uio.h> #include <errno.h> +#include <net/if.h>
#include "main.h" #include "functions.h" @@ -765,3 +766,182 @@ struct ether_addr *resolve_mac(const char *asc) out: return mac_result; } + +/** + * vlan_get_link_parse - parse a get_link rtnl message and extract the important + * data + * @nh: the reply header + * @iface: pointer to the buffer where the link interface has to be stored (it + * is allocated by this function) + * + * Return the vid in case of success or -1 otherwise + */ +static int vlan_get_link_parse(struct nlmsghdr *nh, char **iface) +{ + struct ifinfomsg *ifi = NLMSG_DATA(nh); + size_t vlan_len, info_len, len = nh->nlmsg_len; + struct rtattr *rta, *info, *vlan; + int idx = -1, vid = -1; + + *iface = NULL; + + rta = IFLA_RTA(ifi); + while (RTA_OK(rta, len)) { + /* check if the interface is a vlan */ + if (rta->rta_type == IFLA_LINKINFO) { + info = RTA_DATA(rta); + info_len = RTA_PAYLOAD(rta); + + while (RTA_OK(info, info_len)) { + if (info->rta_type == IFLA_INFO_KIND && + strcmp(RTA_DATA(info), "vlan")) { + fprintf(stderr, + "Interface is not a VLAN\n"); + goto err; + } + + if (info->rta_type == IFLA_INFO_DATA) { + vlan = RTA_DATA(info); + vlan_len = RTA_PAYLOAD(info); + + while (RTA_OK(vlan, vlan_len)) { + if (vlan->rta_type == IFLA_VLAN_ID) + vid = *(int *)RTA_DATA(vlan); + vlan = RTA_NEXT(vlan, vlan_len); + } + } + info = RTA_NEXT(info, info_len); + } + } + + /* extract the name of the "link" interface */ + if (rta->rta_type == IFLA_LINK) { + idx = *(int *)RTA_DATA(rta); + + *iface = malloc(IFNAMSIZ + 1); + if (!if_indextoname(idx, *iface)) + goto err; + } + rta = RTA_NEXT(rta, len); + } + + if (vid == -1) { + fprintf(stderr, "Couldn't find a valid VLAN ID for this interface\n"); + goto err; + } + + if (idx <= 0) { + fprintf(stderr, "Couldn't find a valid parent interface\n"); + goto err; + } + + return vid; +err: + free(*iface); + return -1; +} + +/** + * vlan_get_link_dump - receive and dump a get_link rtnl reply + * @sock: the socket to listen for the reply on + * @buf: pointer to the buffer where the reply has to be dumped to + * @buflen: length of the buffer + * + * Returns the amount of dumped bytes + */ +static ssize_t vlan_get_link_dump(int sock, void *buf, size_t buflen) +{ + struct iovec iov; + struct msghdr msg; + struct sockaddr_nl nladdr; + ssize_t ret = -1; + + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_controllen = 0; + msg.msg_control = NULL; + msg.msg_flags = 0; + msg.msg_name = &nladdr; + msg.msg_namelen = sizeof(nladdr); + + iov.iov_len = buflen; + iov.iov_base = buf; + + ret = recvmsg(sock, &msg, 0); + if (ret < 0) + goto err; + + return ret; +err: + free(buf); + return ret; +} + +/** + * vlan_get_link_open - send a get_link request + * @ifname: the interface to query + * + * Returns 0 in case of success or a negative error code otherwise + */ +static int vlan_get_link_open(const char *ifname) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifi; + /* attribute has to be NLMAG aligned */ + struct rtattr rta_ifname __attribute__ ((aligned(NLMSG_ALIGNTO))); + char ifname[IFNAMSIZ + 1]; + } nlreq; + + memset(&nlreq, 0, sizeof(nlreq)); + nlreq.hdr.nlmsg_len = sizeof(nlreq); + nlreq.hdr.nlmsg_type = RTM_GETLINK; + nlreq.hdr.nlmsg_flags = NLM_F_REQUEST; + nlreq.ifi.ifi_family = AF_UNSPEC; + + nlreq.rta_ifname.rta_type = IFLA_IFNAME; + nlreq.rta_ifname.rta_len = IFNAMSIZ + 1; + strncpy(nlreq.ifname, ifname, IFNAMSIZ); + + return rtnl_open(&nlreq, sizeof(nlreq), 0); +} + +/** + * 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 (allocated by + * this function) + * + * Returns the vlan identifier on success or -1 on error + */ +int vlan_get_link(const char *ifname, char **parent) +{ + int vid = -1, socknl; + void *buf = NULL; + size_t buflen; + ssize_t len; + + buflen = 8192; + buf = malloc(buflen); + if (!buf) + goto err; + + socknl = vlan_get_link_open(ifname); + if (socknl < 0) + goto err; + + len = vlan_get_link_dump(socknl, buf, buflen); + if (len < 0) + goto err_sock; + + vid = vlan_get_link_parse(buf, parent); + +err_sock: + close(socknl); +err: + free(buf); + return vid; +} diff --git a/functions.h b/functions.h index 522c607..14ba525 100644 --- a/functions.h +++ b/functions.h @@ -42,6 +42,7 @@ int write_file(const char *dir, const char *fname, const char *arg1, const char *arg2); struct ether_addr *translate_mac(char *mesh_iface, struct ether_addr *mac); struct ether_addr *resolve_mac(const char *asc); +int vlan_get_link(const char *ifname, char **parent);
extern char *line_ptr;
On Sunday 05 January 2014 09:59:53 Antonio Quartulli wrote:
+/**
- vlan_get_link_dump - receive and dump a get_link rtnl reply
- @sock: the socket to listen for the reply on
- @buf: pointer to the buffer where the reply has to be dumped to
- @buflen: length of the buffer
- Returns the amount of dumped bytes
- */
+static ssize_t vlan_get_link_dump(int sock, void *buf, size_t buflen) +{
- struct iovec iov;
- struct msghdr msg;
- struct sockaddr_nl nladdr;
- ssize_t ret = -1;
- memset(&msg, 0, sizeof(msg));
- memset(&iov, 0, sizeof(iov));
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_controllen = 0;
- msg.msg_control = NULL;
- msg.msg_flags = 0;
- msg.msg_name = &nladdr;
- msg.msg_namelen = sizeof(nladdr);
- iov.iov_len = buflen;
- iov.iov_base = buf;
- ret = recvmsg(sock, &msg, 0);
- if (ret < 0)
goto err;
- return ret;
+err:
- free(buf);
- return ret;
+}
Do we double free 'buf' in case of an error ?
+/**
- vlan_get_link_open - send a get_link request
- @ifname: the interface to query
- Returns 0 in case of success or a negative error code otherwise
- */
+static int vlan_get_link_open(const char *ifname) +{
- struct {
struct nlmsghdr hdr;
struct ifinfomsg ifi;
/* attribute has to be NLMAG aligned */
NLMSG aligned ?
Cheers, Marek
On 05/01/14 20:07, Marek Lindner wrote:
On Sunday 05 January 2014 09:59:53 Antonio Quartulli wrote:
+static int vlan_get_link_open(const char *ifname) +{
- struct {
struct nlmsghdr hdr;
struct ifinfomsg ifi;
/* attribute has to be NLMAG aligned */
NLMSG aligned ?
Yes, you are right!
Thanks,
From: Marco Dalla Torre marco.dallato@gmail.com
Allow the batctl tool to take advantage of changes from commit e4ff5c153dab054a6cd1c4132f87bc5e77127456 "add sys framework for VLAN" recently added to batman-adv, so that users can execute commands in a per VLAN fashion.
If no directory entry corresponding to the user-selected device is found at the standard location for non VLAN interfaces (/sys/class/net/${device}/mesh/), 'batctl' now looks into directory: /sys/class/net/${base_device}/mesh/vlan${vid} Information on VLAN devices (base device, vid) necessary to construct the directory path is acquired by querying the netlink kernel module. Where: -${base_device}: the batman device on top of which the VLAN is sitting -${device}: the device interface for the VLAN, -${vid}: the identifier assigned to the VLAN.
If the user-selected command is not supported by the VLAN, an appropriate error is shown.
Signed-off-by: Marco Dalla Torre marco.dallato@gmail.com Signed-off-by: Antonio Quartulli antonio@meshcoding.com --- functions.c | 3 +++ main.c | 2 +- man/batctl.8 | 4 ++-- sys.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- sys.h | 22 ++++++++++++---------- 5 files changed, 66 insertions(+), 14 deletions(-)
diff --git a/functions.c b/functions.c index 4934675..7993c83 100644 --- a/functions.c +++ b/functions.c @@ -143,6 +143,9 @@ static void file_open_problem_dbg(const char *dir, const char *fname, fprintf(stderr, "Error - the folder '/sys/' was not found on the system\n"); fprintf(stderr, "Please make sure that the sys filesystem is properly mounted\n"); return; + } else if (strstr(dir, "/sys/devices/virtual/")) { + fprintf(stderr, "The selected feature '%s' is not supported for vlans\n", fname); + return; } }
diff --git a/main.c b/main.c index aab2e11..843fbc4 100644 --- a/main.c +++ b/main.c @@ -46,7 +46,7 @@ static void print_usage(void)
fprintf(stderr, "Usage: batctl [options] command|debug table [parameters]\n"); fprintf(stderr, "options:\n"); - fprintf(stderr, " \t-m mesh interface (default 'bat0')\n"); + fprintf(stderr, " \t-m mesh interface or mesh-based VLAN 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"); fprintf(stderr, "\n"); diff --git a/man/batctl.8 b/man/batctl.8 index fafe735..95e887d 100644 --- a/man/batctl.8 +++ b/man/batctl.8 @@ -42,7 +42,7 @@ behaviour or using the B.A.T.M.A.N. advanced protocol. .SH OPTIONS .TP .I \fBoptions: --m specify mesh interface (default 'bat0') +-m specify mesh interface or a mesh-based VLAN interface (default 'bat0') .br -h print general batctl help .br @@ -61,7 +61,7 @@ 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. +disable ap isolation. This command can be used in conjunction with "-m" option to target per VLAN configurations. .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 diff --git a/sys.c b/sys.c index 2a508e2..6424678 100644 --- a/sys.c +++ b/sys.c @@ -372,9 +372,11 @@ static void settings_usage(int setting)
int handle_sys_setting(char *mesh_iface, int setting, int argc, char **argv) { - int optchar, res = EXIT_FAILURE; + int vid = -1, optchar, res = EXIT_FAILURE; char *path_buff; const char **ptr; + char *base_dev; + ssize_t path_length;
while ((optchar = getopt(argc, argv, "h")) != -1) { switch (optchar) { @@ -391,6 +393,51 @@ int handle_sys_setting(char *mesh_iface, int setting, int argc, char **argv) snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, mesh_iface); path_buff[PATH_BUFF_LEN - 1] = '\0';
+check_path: + if (access(path_buff, F_OK) != 0) { + /* if vid is valid it means that we have already looked for a + * vlan, but the interface we found is not a valid batman-adv + * interface + */ + if (vid >= 0) { + fprintf(stderr, "Not a valid batman-adv interface: %s\n", + base_dev); + return EXIT_FAILURE; + } + + if (errno == ENOENT) { + /* path does not exist, no lan interface: check vlan */ + vid = vlan_get_link(mesh_iface, &base_dev); + if (vid < 0) + return EXIT_FAILURE; + + snprintf(path_buff, PATH_BUFF_LEN, SYS_BATIF_PATH_FMT, + base_dev); + + goto check_path; + } else if (errno == ENOTDIR) { + /* not a directory, something wrong here */ + fprintf(stderr, "Error - expected directory at '%s'\n", + path_buff); + return EXIT_FAILURE; + } + } + + /* if we are in a vlan realm we have to update the path before accessing + * the attributes + */ + if (vid >= 0) { + path_length = strlen(SYS_VLAN_PATH) + strlen(base_dev) + + VLAN_ID_MAX_LEN + 1; + path_buff = realloc(path_buff, path_length); + if (!path_buff) { + fprintf(stderr, + "Error - could not allocate path buffer: out of memory ?\n"); + return EXIT_FAILURE; + } + snprintf(path_buff, path_length, SYS_VLAN_PATH, base_dev, vid); + } + if (argc == 1) { res = read_file(path_buff, (char *)batctl_settings[setting].sysfs_name, NO_FLAGS, 0, 0, 0); diff --git a/sys.h b/sys.h index 8934978..2cbbcfb 100644 --- a/sys.h +++ b/sys.h @@ -24,16 +24,18 @@
#include "main.h"
-#define SYS_BATIF_PATH_FMT "/sys/class/net/%s/mesh/" -#define SYS_LOG_LEVEL "log_level" -#define SYS_LOG "log" -#define SYS_GW_MODE "gw_mode" -#define SYS_GW_SEL "gw_sel_class" -#define SYS_GW_BW "gw_bandwidth" -#define SYS_IFACE_PATH "/sys/class/net" -#define SYS_IFACE_DIR SYS_IFACE_PATH"/%s/" -#define SYS_MESH_IFACE_FMT SYS_IFACE_PATH"/%s/batman_adv/mesh_iface" -#define SYS_IFACE_STATUS_FMT SYS_IFACE_PATH"/%s/batman_adv/iface_status" +#define SYS_BATIF_PATH_FMT "/sys/class/net/%s/mesh/" +#define SYS_LOG_LEVEL "log_level" +#define SYS_LOG "log" +#define SYS_GW_MODE "gw_mode" +#define SYS_GW_SEL "gw_sel_class" +#define SYS_GW_BW "gw_bandwidth" +#define SYS_IFACE_PATH "/sys/class/net" +#define SYS_IFACE_DIR SYS_IFACE_PATH"/%s/" +#define SYS_MESH_IFACE_FMT SYS_IFACE_PATH"/%s/batman_adv/mesh_iface" +#define SYS_IFACE_STATUS_FMT SYS_IFACE_PATH"/%s/batman_adv/iface_status" +#define SYS_VLAN_PATH SYS_IFACE_PATH"/%s/mesh/vlan%d/" +#define VLAN_ID_MAX_LEN 4
enum batctl_settings_list { BATCTL_SETTINGS_ORIG_INTERVAL,
On Sunday 05 January 2014 09:59:52 Antonio Quartulli wrote:
@@ -528,7 +526,7 @@ static int resolve_mac_from_cache_open(int ai_family) if (ret < 0) goto outclose;
ret = send(socknl, &nlreq, nlreq.hdr.nlmsg_len, 0);
ret = send(socknl, req, len, 0); if (ret < 0) goto outclose;
out: @@ -538,6 +536,22 @@ outclose: return ret; }
+static int resolve_mac_from_cache_open(int ai_family) +{
struct {
struct nlmsghdr hdr;
struct ndmsg msg;
} nlreq;
memset(&nlreq, 0, sizeof(nlreq));
nlreq.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(nlreq.msg));
nlreq.hdr.nlmsg_type = RTM_GETNEIGH;
nlreq.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
nlreq.msg.ndm_family = ai_family;
return rtnl_open(&nlreq, sizeof(nlreq), NETLINK_ROUTE);
+}
Is 'sizeof(nlreq)' equal to 'nlreq.hdr.nlmsg_len' ? It seems not ...
Cheers, Marek
On 05/01/14 15:04, Marek Lindner wrote:
On Sunday 05 January 2014 09:59:52 Antonio Quartulli wrote:
@@ -528,7 +526,7 @@ static int resolve_mac_from_cache_open(int ai_family) if (ret < 0) goto outclose;
ret = send(socknl, &nlreq, nlreq.hdr.nlmsg_len, 0);
ret = send(socknl, req, len, 0); if (ret < 0) goto outclose;
out: @@ -538,6 +536,22 @@ outclose: return ret; }
+static int resolve_mac_from_cache_open(int ai_family) +{
struct {
struct nlmsghdr hdr;
struct ndmsg msg;
} nlreq;
memset(&nlreq, 0, sizeof(nlreq));
nlreq.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(nlreq.msg));
nlreq.hdr.nlmsg_type = RTM_GETNEIGH;
nlreq.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
nlreq.msg.ndm_family = ai_family;
return rtnl_open(&nlreq, sizeof(nlreq), NETLINK_ROUTE);
+}
Is 'sizeof(nlreq)' equal to 'nlreq.hdr.nlmsg_len' ? It seems not ...
It is because the macro NLMSG_LENGTH adds NLMSG_HDRLEN to the value passed as argument (in this case it is sizeof(nlreq.msg)) and therefore what we store in nlreq.hdr.nlmsg_len is sizeof(nlreq) (plus some possible alignment bytes).
I think I can make it more clear and always pass nlreq.hdr.nlmsg_len to send().
Thanks!
v2 is coming!
Cheers,
b.a.t.m.a.n@lists.open-mesh.org