From e4ea2045ebaac63e663765b29178241932322406 Mon Sep 17 00:00:00 2001 From: Jan Braun Date: Sun, 5 Sep 2021 02:43:59 +0200 Subject: [PATCH] usteer: add support for IPv6 remote exchange 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 [squash commits - adapt usock usage] Signed-off-by: David Bauer --- main.c | 1 - openwrt/usteer/files/etc/config/usteer | 3 + openwrt/usteer/files/etc/init.d/usteer | 1 + remote.c | 171 ++++++++++++++++++++++--- ubus.c | 3 + usteer.h | 5 + 6 files changed, 165 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 eeb0632..38239ba 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,61 @@ 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) { + 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..81d1c5a 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::4150" + #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.30.2