Skip to contentSkip to conversation

batctl: introduce rtnl_open() helper function

PATCH Superseded Antonio Quartulli 2 participants 7 messages Jan 5, 2014

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/3 batctl: introduce rtnl_open() helper function

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.

---
 functions.c | 46 ++++++++++++++++++++++++++++++----------------
 1 file changed, 30 insertions(+), 16 deletions(-)
functions.c +30 -16
index 13ba190..a73b493 100644
@@ -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;
Signature · 1 line
1.8.5.2
#1 lore raw

PATCH 2/3 batctl: implement vlan-to-link helper functions

This functions can be used to convert a vlan interface into the real one and to extract the vlan identifier.

---
 functions.c | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 functions.h |   1 +
 2 files changed, 181 insertions(+)
functions.c +180 -0
index a73b493..4934675 100644
@@ -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;
+}
functions.h +1 -0
index 522c607..14ba525 100644
@@ -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;
 
Signature · 1 line
1.8.5.2
#2 lore raw

Re: [B.A.T.M.A.N.] [PATCH next 2/3] batctl: implement vlan-to-link helper functions

Quoted 28 lines · On Sunday 05 January 2014 09:59:53 Antonio Quartulli wrote:
functions.c +28 -0
+/**
+ * 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;

functions.c struct ether_addr *resolve_mac(const char *asc) · line 881

Quoted 10 lines
functions.c +10 -0
+
+	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 ?

functions.c struct ether_addr *resolve_mac(const char *asc) · line 894

Quoted 12 lines
functions.c +12 -0
+/**
+ * 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

#3 lore raw

Re: [B.A.T.M.A.N.] [PATCH next 2/3] batctl: implement vlan-to-link helper functions

Quoted 9 lines · Marek Lindner

functions.c struct ether_addr *resolve_mac(const char *asc) · line 894

Quoted 6 lines · On Sunday 05 January 2014 09:59:53 Antonio Quartulli wrote:
functions.c +6 -0
+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,

Signature · 1 line
Antonio Quartulli
#4 lore raw

PATCH 3/3 batctl: add sys framework for VLAN support

In reply to Antonio Quartulli

From: Marco Dalla Torre <[email protected]>

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.

---
 functions.c  |  3 +++
 main.c       |  2 +-
 man/batctl.8 |  4 ++--
 sys.c        | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
 sys.h        | 22 ++++++++++++----------
 5 files changed, 66 insertions(+), 14 deletions(-)
functions.c +3 -0
index 4934675..7993c83 100644
@@ -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;
 		}
 	}
 
main.c +1 -1
index aab2e11..843fbc4 100644
@@ -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");
man/batctl.8 +2 -2
index fafe735..95e887d 100644
@@ -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
sys.c +48 -1

63 lines not on this page · raw message

sys.h +12 -10
index 8934978..2cbbcfb 100644
@@ -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,
Signature · 1 line
1.8.5.2
#5 lore raw
In reply to Antonio Quartulli
Quoted 18 lines · On Sunday 05 January 2014 09:59:52 Antonio Quartulli wrote:
functions.c +6 -1
@@ -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;

functions.c outclose: · line 553

Quoted 10 lines
functions.c +10 -0
+       } 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

#6 lore raw
Quoted 23 lines · Marek Lindner
Quoted 22 lines · On Sunday 05 January 2014 09:59:52 Antonio Quartulli wrote:
functions.c +10 -1
@@ -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));
Quoted 8 lines

functions.c outclose: · line 553

Quoted 6 lines
functions.c +6 -0
+       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,

Signature · 1 line
Antonio Quartulli