[PATCH usteer] usteer: add support for IPv6 remote exchange

David Bauer mail at david-bauer.net
Tue Sep 7 11:53:59 PDT 2021


From: Jan Braun <keksgesicht23 at gmail.com>

This adds optional support for message exchange using IPv6 multicast
messaging. This has the ability for routers and switches between nodes
to route traffic between usteer nodes multicast-aware.

By default, IPv4 is used. IPv6 can be enabled by configuring the ipv6
option to 1.

Signed-off-by: Jan Braun <keksgesicht23 at gmail.com>
[squash commits - adapt usock usage]
Signed-off-by: David Bauer <mail at david-bauer.net>
---
 main.c                                 |   1 -
 openwrt/usteer/files/etc/config/usteer |   3 +
 openwrt/usteer/files/etc/init.d/usteer |   1 +
 remote.c                               | 172 ++++++++++++++++++++++---
 ubus.c                                 |   3 +
 usteer.h                               |   5 +
 6 files changed, 166 insertions(+), 19 deletions(-)

diff --git a/main.c b/main.c
index ad53af8..9d01419 100644
--- a/main.c
+++ b/main.c
@@ -204,7 +204,6 @@ int main(int argc, char **argv)
 	}
 
 	ubus_add_uloop(ubus_ctx);
-	usteer_interface_init();
 	if (dump_time) {
 		dump_timer.cb = usteer_dump_timeout;
 		uloop_timeout_set(&dump_timer, dump_time * 1000);
diff --git a/openwrt/usteer/files/etc/config/usteer b/openwrt/usteer/files/etc/config/usteer
index 5e30ccf..3ba1c6d 100644
--- a/openwrt/usteer/files/etc/config/usteer
+++ b/openwrt/usteer/files/etc/config/usteer
@@ -5,6 +5,9 @@ config usteer
 	# Log messages to syslog (0/1)
 	option 'syslog' '1'
 
+	# Use IPv6 for remote exchange
+	option 'ipv6' '0'
+
 	# Minimum level of logged messages
 	# 0 = fatal
 	# 1 = info
diff --git a/openwrt/usteer/files/etc/init.d/usteer b/openwrt/usteer/files/etc/init.d/usteer
index 9289557..15add88 100755
--- a/openwrt/usteer/files/etc/init.d/usteer
+++ b/openwrt/usteer/files/etc/init.d/usteer
@@ -65,6 +65,7 @@ uci_usteer() {
 	local cfg="$1"
 
 	uci_option_to_json_bool "$cfg" syslog
+	uci_option_to_json_bool "$cfg" ipv6
 	uci_option_to_json_bool "$cfg" load_kick_enabled
 	uci_option_to_json_bool "$cfg" assoc_steering
 	uci_option_to_json_string "$cfg" node_up_script
diff --git a/remote.c b/remote.c
index 16ecb0f..dd95ece 100644
--- a/remote.c
+++ b/remote.c
@@ -277,10 +277,9 @@ interface_add_node(struct usteer_remote_host *host, struct blob_attr *data)
 }
 
 static void
-interface_recv_msg(struct interface *iface, struct in_addr *addr, void *buf, int len)
+interface_recv_msg(struct interface *iface, char *addr_str, void *buf, int len)
 {
 	struct usteer_remote_host *host;
-	char addr_str[INET_ADDRSTRLEN];
 	struct blob_attr *data = buf;
 	struct apmsg msg;
 	struct blob_attr *cur;
@@ -302,8 +301,6 @@ interface_recv_msg(struct interface *iface, struct in_addr *addr, void *buf, int
 	MSG(NETWORK, "Received message on %s (id=%08x->%08x seq=%d len=%d)\n",
 		interface_name(iface), msg.id, local_id, msg.seq, len);
 
-	inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));
-
 	host = interface_get_host(addr_str, msg.id);
 	usteer_node_set_blob(&host->host_info, msg.host_info);
 
@@ -325,11 +322,12 @@ interface_find_by_ifindex(int index)
 }
 
 static void
-interface_recv(struct uloop_fd *u, unsigned int events)
+interface_recv_v4(struct uloop_fd *u, unsigned int events)
 {
 	static char buf[APMGR_BUFLEN];
 	static char cmsg_buf[( CMSG_SPACE(sizeof(struct in_pktinfo)) + sizeof(int)) + 1];
 	static struct sockaddr_in sin;
+	char addr_str[INET_ADDRSTRLEN];
 	static struct iovec iov = {
 		.iov_base = buf,
 		.iov_len = sizeof(buf)
@@ -381,11 +379,80 @@ interface_recv(struct uloop_fd *u, unsigned int events)
 			continue;
 		}
 
-		interface_recv_msg(iface, &sin.sin_addr, buf, len);
+		inet_ntop(AF_INET, &sin.sin_addr, addr_str, sizeof(addr_str));
+
+		interface_recv_msg(iface, addr_str, buf, len);
 	} while (1);
 }
 
-static void interface_send_msg(struct interface *iface, struct blob_attr *data)
+
+static void interface_recv_v6(struct uloop_fd *u, unsigned int events){
+	static char buf[APMGR_BUFLEN];
+	static char cmsg_buf[( CMSG_SPACE(sizeof(struct in6_pktinfo)) + sizeof(int)) + 1];
+	static struct sockaddr_in6 sin;
+	static struct iovec iov = {
+		.iov_base = buf,
+		.iov_len = sizeof(buf)
+	};
+	static struct msghdr msg = {
+		.msg_name = &sin,
+		.msg_namelen = sizeof(sin),
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+		.msg_control = cmsg_buf,
+		.msg_controllen = sizeof(cmsg_buf),
+	};
+	struct cmsghdr *cmsg;
+	char addr_str[INET6_ADDRSTRLEN];
+	int len;
+
+	do {
+		struct in6_pktinfo *pkti = NULL;
+		struct interface *iface;
+
+		len = recvmsg(u->fd, &msg, 0);
+		if (len < 0) {
+			switch (errno) {
+			case EAGAIN:
+				return;
+			case EINTR:
+				continue;
+			default:
+				perror("recvmsg");
+				uloop_fd_delete(u);
+				return;
+			}
+		}
+
+		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+			if (cmsg->cmsg_type != IPV6_PKTINFO)
+				continue;
+
+			pkti = (struct in6_pktinfo *) CMSG_DATA(cmsg);
+		}
+
+		if (!pkti) {
+			MSG(DEBUG, "Received packet without ifindex\n");
+			continue;
+		}
+
+		iface = interface_find_by_ifindex(pkti->ipi6_ifindex);
+		if (!iface) {
+			MSG(DEBUG, "Received packet from unconfigured interface %d\n", pkti->ipi6_ifindex);
+			continue;
+		}
+
+		inet_ntop(AF_INET6, &sin.sin6_addr, addr_str, sizeof(addr_str));
+		if (sin.sin6_addr.s6_addr[0] == 0) {
+			/* IPv4 mapped address. Ignore. */
+			continue;
+		}
+
+		interface_recv_msg(iface, addr_str, buf, len);
+	} while (1);
+}
+
+static void interface_send_msg_v4(struct interface *iface, struct blob_attr *data)
 {
 	static size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
 	static struct sockaddr_in a;
@@ -421,6 +488,28 @@ static void interface_send_msg(struct interface *iface, struct blob_attr *data)
 		perror("sendmsg");
 }
 
+
+static void interface_send_msg_v6(struct interface *iface, struct blob_attr *data) {
+	static struct sockaddr_in6 groupSock = {};
+
+	groupSock.sin6_family = AF_INET6;
+	inet_pton(AF_INET6, APMGR_V6_MCAST_GROUP, &groupSock.sin6_addr);
+	groupSock.sin6_port = htons(APMGR_PORT);
+
+	setsockopt(remote_fd.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface->ifindex, sizeof(iface->ifindex));
+
+	if (sendto(remote_fd.fd, data, blob_pad_len(data), 0, (const struct sockaddr *)&groupSock, sizeof(groupSock)) < 0)
+		perror("sendmsg");
+}
+
+static void interface_send_msg(struct interface *iface, struct blob_attr *data){
+	if (config.ipv6) {
+		interface_send_msg_v6(iface, data);
+	} else {
+		interface_send_msg_v4(iface, data);
+	}
+}
+
 static void usteer_send_sta_info(struct sta_info *sta)
 {
 	int seen = current_time - sta->seen;
@@ -558,23 +647,16 @@ usteer_init_local_id(void)
 	return 0;
 }
 
-static void
-usteer_reload_timer(struct uloop_timeout *t)
-{
+static int usteer_create_v4_socket() {
 	int yes = 1;
 	int fd;
 
-	if (remote_fd.registered) {
-		uloop_fd_delete(&remote_fd);
-		close(remote_fd.fd);
-	}
-
 	fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK |
 		   USOCK_NUMERIC | USOCK_IPV4ONLY,
 		   "0.0.0.0", APMGR_PORT_STR);
 	if (fd < 0) {
 		perror("usock");
-		return;
+		return - 1;
 	}
 
 	if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0)
@@ -583,8 +665,62 @@ usteer_reload_timer(struct uloop_timeout *t)
 	if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)) < 0)
 		perror("setsockopt(SO_BROADCAST)");
 
-	remote_fd.fd = fd;
-	remote_fd.cb = interface_recv;
+	return fd;
+}
+
+
+static int usteer_create_v6_socket() {
+	struct interface *iface;
+	struct ipv6_mreq group;
+	int yes = 1;
+	int fd;
+
+	fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK |
+		   USOCK_NUMERIC | USOCK_IPV6ONLY,
+		   "::", APMGR_PORT_STR);
+	if (fd < 0) {
+		perror("usock");
+		return fd;
+	}
+
+	if (!inet_pton(AF_INET6, APMGR_V6_MCAST_GROUP, &group.ipv6mr_multiaddr.s6_addr))
+		perror("inet_pton(AF_INET6)");
+
+	/* Membership has to be added for every interface we listen on. */
+	vlist_for_each_element(&interfaces, iface, node) {
+		printf("Adding group membership for %s on interface %d", APMGR_V6_MCAST_GROUP, iface->ifindex);
+		group.ipv6mr_interface = iface->ifindex;
+		if(setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&group, sizeof group) < 0)
+			perror("setsockopt(IPV6_ADD_MEMBERSHIP)");
+	}
+
+	if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0)
+		perror("setsockopt(IPV6_RECVPKTINFO)");
+
+	if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)) < 0)
+		perror("setsockopt(SO_BROADCAST)");
+
+	return fd;
+}
+
+static void usteer_reload_timer(struct uloop_timeout *t) {
+	/* Remove uloop descriptor */
+	if (remote_fd.fd && remote_fd.registered) {
+		uloop_fd_delete(&remote_fd);
+		close(remote_fd.fd);
+	}
+
+	if (config.ipv6) {
+		remote_fd.fd = usteer_create_v6_socket();
+		remote_fd.cb = interface_recv_v6;
+	} else {
+		remote_fd.fd = usteer_create_v4_socket();
+		remote_fd.cb = interface_recv_v4;
+	}
+
+	if (remote_fd.fd < 0)
+		return;
+
 	uloop_fd_add(&remote_fd, ULOOP_READ);
 }
 
diff --git a/ubus.c b/ubus.c
index fe36384..00843ef 100644
--- a/ubus.c
+++ b/ubus.c
@@ -142,6 +142,7 @@ struct cfg_item {
 #define __config_items \
 	_cfg(BOOL, syslog), \
 	_cfg(U32, debug_level), \
+	_cfg(BOOL, ipv6), \
 	_cfg(U32, sta_block_timeout), \
 	_cfg(U32, local_sta_timeout), \
 	_cfg(U32, local_sta_update), \
@@ -264,6 +265,8 @@ usteer_ubus_set_config(struct ubus_context *ctx, struct ubus_object *obj,
 		}
 	}
 
+	usteer_interface_init();
+
 	return 0;
 }
 
diff --git a/usteer.h b/usteer.h
index 749075e..4ed3b95 100644
--- a/usteer.h
+++ b/usteer.h
@@ -33,6 +33,9 @@
 
 #define __STR(x)		#x
 #define _STR(x)			__STR(x)
+
+#define APMGR_V6_MCAST_GROUP	"ff02::4321"
+
 #define APMGR_PORT		16720 /* AP */
 #define APMGR_PORT_STR		_STR(APMGR_PORT)
 #define APMGR_BUFLEN		(64 * 1024)
@@ -122,6 +125,8 @@ struct usteer_config {
 	bool syslog;
 	uint32_t debug_level;
 
+	bool ipv6;
+
 	uint32_t sta_block_timeout;
 	uint32_t local_sta_timeout;
 	uint32_t local_sta_update;
-- 
2.33.0




More information about the openwrt-devel mailing list