From f766d8047d188a49d0a186815cc03221cf430f5a Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Sun, 9 May 2021 09:21:13 +0800 Subject: [PATCH] [posix] support interface name in ot-ctl (#6552) This commit adds the Thread interface name to unix socket of OpenThread daemon, so that ot-ctl can specify which daemon to connect to. --- script/check-posix-pty | 17 ++-- src/posix/client.cpp | 93 +++++++++++++++++--- src/posix/main.c | 1 + src/posix/platform/daemon.cpp | 22 ++++- src/posix/platform/openthread-posix-config.h | 6 +- src/posix/platform/system.cpp | 2 + tests/scripts/misc/test_multicast_join.py | 9 +- 7 files changed, 122 insertions(+), 28 deletions(-) diff --git a/script/check-posix-pty b/script/check-posix-pty index 12be11f41..94aa1782e 100755 --- a/script/check-posix-pty +++ b/script/check-posix-pty @@ -117,18 +117,21 @@ do_check() RADIO_URL="spinel+hdlc+uart://${CORE_PTY}?region=US&max-power-table=11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26" if [[ ${OT_DAEMON} == 'on' ]]; then - sudo "$PWD/build/posix/src/posix/ot-daemon" -d7 -v -I "${VALID_NETIF_NAME}" "${RADIO_URL}" & + sudo "$PWD/build/posix/src/posix/ot-daemon" -d7 -v -I "${VALID_NETIF_NAME}" "${RADIO_URL}" 2>&1 | tee "${OT_OUTPUT}" & sleep 3 - OT_CLI_CMD="$PWD/build/posix/src/posix/ot-ctl" - sudo "${OT_CLI_CMD}" panid 0xface | grep 'Done' || die 'failed to set panid with ot-ctl' + # macOS cannot explicitly set network interface name + NETIF_NAME=$(grep -o 'Thread interface: .\+' "${OT_OUTPUT}" | cut -d: -f2 | tr -d ' \r\n') + OT_CTL="$PWD/build/posix/src/posix/ot-ctl" + sudo "${OT_CTL}" -I "${NETIF_NAME}" panid 0xface | grep 'Done' || die 'failed to set panid with ot-ctl' # verify this reset and factoryreset end immediately - sudo "${OT_CLI_CMD}" reset + sudo "${OT_CTL}" -I "${NETIF_NAME}" reset # sleep a while for daemon ready sleep 2 - sudo "${OT_CLI_CMD}" factoryreset + sudo "${OT_CTL}" -I "${NETIF_NAME}" factoryreset # sleep a while for daemon ready sleep 2 + OT_CLI_CMD="${OT_CTL} -I ${NETIF_NAME}" else OT_CLI="$PWD/build/posix/src/posix/ot-cli" sudo "${OT_CLI}" -I "${VALID_NETIF_NAME}" -n "${RADIO_URL}" @@ -201,9 +204,9 @@ EOF if [[ ${OT_DAEMON} == 'on' ]]; then sudo killall -9 expect || true sudo killall -9 ot-ctl || true - NETIF_INDEX=$(ip link show "${VALID_NETIF_NAME}" | cut -f 1 -d ":" | head -n 1) + NETIF_INDEX=$(ip link show "${NETIF_NAME}" | cut -f 1 -d ":" | head -n 1) sudo PATH="$(dirname "${OT_CLI_CMD}"):${PATH}" \ - python3 "$PWD/tests/scripts/misc/test_multicast_join.py" "${NETIF_INDEX}" \ + python3 "$PWD/tests/scripts/misc/test_multicast_join.py" "${NETIF_INDEX}" "${NETIF_NAME}" \ || die 'multicast group join failed' fi diff --git a/src/posix/client.cpp b/src/posix/client.cpp index 5225a6e4b..a80a5f4b4 100644 --- a/src/posix/client.cpp +++ b/src/posix/client.cpp @@ -43,6 +43,7 @@ #define OPENTHREAD_USE_READLINE (HAVE_LIBEDIT || HAVE_LIBREADLINE) #include +#include #include #include #include @@ -62,6 +63,13 @@ #include "platform-posix.h" +namespace { + +struct Config +{ + const char *mNetifName; +}; + enum { kLineBufferSize = OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH, @@ -71,10 +79,10 @@ static_assert(kLineBufferSize >= sizeof("> "), "kLineBufferSize is too small"); static_assert(kLineBufferSize >= sizeof("Done\r\n"), "kLineBufferSize is too small"); static_assert(kLineBufferSize >= sizeof("Error "), "kLineBufferSize is too small"); -static int sSessionFd = -1; +int sSessionFd = -1; #if OPENTHREAD_USE_READLINE -static void InputCallback(char *aLine) +void InputCallback(char *aLine) { if (aLine != nullptr) { @@ -89,7 +97,7 @@ static void InputCallback(char *aLine) } #endif // OPENTHREAD_USE_READLINE -static bool DoWrite(int aFile, const void *aBuffer, size_t aSize) +bool DoWrite(int aFile, const void *aBuffer, size_t aSize) { bool ret = true; @@ -111,7 +119,7 @@ exit: return ret; } -static int ConnectSession(void) +int ConnectSession(const Config &aConfig) { int ret; @@ -128,7 +136,12 @@ static int ConnectSession(void) memset(&sockname, 0, sizeof(struct sockaddr_un)); sockname.sun_family = AF_UNIX; - strncpy(sockname.sun_path, OPENTHREAD_POSIX_DAEMON_SOCKET_NAME, sizeof(sockname.sun_path) - 1); + ret = snprintf(sockname.sun_path, sizeof(sockname.sun_path), OPENTHREAD_POSIX_DAEMON_SOCKET_NAME, + aConfig.mNetifName); + VerifyOrExit(ret >= 0 && static_cast(ret) < sizeof(sockname.sun_path), { + errno = EINVAL; + ret = -1; + }); ret = connect(sSessionFd, reinterpret_cast(&sockname), sizeof(struct sockaddr_un)); } @@ -137,7 +150,7 @@ exit: return ret; } -static bool ReconnectSession(void) +bool ReconnectSession(Config &aConfig) { bool ok = false; uint32_t delay = 0; // 100ms @@ -149,7 +162,7 @@ static bool ReconnectSession(void) usleep(delay); delay = delay > 0 ? delay * 2 : 100000; - rval = ConnectSession(); + rval = ConnectSession(aConfig); VerifyOrExit(rval == -1, ok = true); @@ -161,11 +174,64 @@ exit: return ok; } -static bool IsSeparator(char aChar) +enum +{ + kOptInterfaceName = 'I', + kOptHelp = 'h', +}; + +const struct option kOptions[] = { + {"interface-name", required_argument, NULL, kOptInterfaceName}, + {"help", required_argument, NULL, kOptHelp}, +}; + +void PrintUsage(const char *aProgramName, FILE *aStream, int aExitCode) +{ + fprintf(aStream, + "Syntax:\n" + " %s [Options] [--] ...\n" + "Options:\n" + " -h --help Display this usage information.\n" + " -I --interface-name name Thread network interface name.\n", + aProgramName); + exit(aExitCode); +} + +bool IsSeparator(char aChar) { return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n'); } +Config ParseArg(int &aArgCount, char **&aArgVector) +{ + Config config = {"wpan0"}; + + optind = 1; + + for (int index, option; (option = getopt_long(aArgCount, aArgVector, "I:h", kOptions, &index)) != -1;) + { + switch (option) + { + case kOptInterfaceName: + config.mNetifName = optarg; + break; + case kOptHelp: + PrintUsage(aArgVector[0], stdout, OT_EXIT_SUCCESS); + break; + default: + PrintUsage(aArgVector[0], stderr, OT_EXIT_FAILURE); + break; + } + } + + aArgCount -= optind; + aArgVector += optind; + + return config; +} + +} // namespace + int main(int argc, char *argv[]) { bool isInteractive = true; @@ -174,15 +240,18 @@ int main(int argc, char *argv[]) char lineBuffer[kLineBufferSize]; size_t lineBufferWritePos = 0; int ret; + Config config; - VerifyOrExit(ConnectSession() != -1, perror("connect session failed"); ret = OT_EXIT_FAILURE); + config = ParseArg(argc, argv); - if (argc > 1) + VerifyOrExit(ConnectSession(config) != -1, perror("connect session failed"); ret = OT_EXIT_FAILURE); + + if (argc > 0) { char buffer[kLineBufferSize]; size_t count = 0; - for (int i = 1; i < argc; i++) + for (int i = 0; i < argc; i++) { for (const char *c = argv[i]; *c; ++c) { @@ -259,7 +328,7 @@ int main(int argc, char *argv[]) if (rval == 0) { // daemon closed sSessionFd - if (isInteractive && ReconnectSession()) + if (isInteractive && ReconnectSession(config)) { continue; } diff --git a/src/posix/main.c b/src/posix/main.c index e00ed2449..46e881e43 100644 --- a/src/posix/main.c +++ b/src/posix/main.c @@ -296,6 +296,7 @@ static otInstance *InitInstance(PosixConfig *aConfig) IgnoreError(otLoggingSetLevel(aConfig->mLogLevel)); instance = otSysInit(&aConfig->mPlatformConfig); + syslog(LOG_INFO, "Thread interface: %s", otSysGetThreadNetifName()); atexit(otSysDeinit); diff --git a/src/posix/platform/daemon.cpp b/src/posix/platform/daemon.cpp index 3d335752f..be3f86de2 100644 --- a/src/posix/platform/daemon.cpp +++ b/src/posix/platform/daemon.cpp @@ -148,7 +148,18 @@ void platformDaemonEnable(otInstance *aInstance) DieNow(OT_EXIT_FAILURE); } - sDaemonLock = open(OPENTHREAD_POSIX_DAEMON_SOCKET_LOCK, O_CREAT | O_RDONLY | O_CLOEXEC, 0600); + { + static_assert(sizeof(OPENTHREAD_POSIX_DAEMON_SOCKET_LOCK) == sizeof(OPENTHREAD_POSIX_DAEMON_SOCKET_NAME), + "sock and lock file name pattern should have the same length!"); + char lockfile[sizeof(sockname.sun_path)]; + + ret = snprintf(lockfile, sizeof(lockfile), OPENTHREAD_POSIX_DAEMON_SOCKET_LOCK, gNetifName); + if (ret < 0 && static_cast(ret) >= sizeof(lockfile)) + { + DieNowWithMessage("lockfile", OT_EXIT_INVALID_ARGUMENTS); + } + sDaemonLock = open(lockfile, O_CREAT | O_RDONLY | O_CLOEXEC, 0600); + } if (sDaemonLock == -1) { @@ -162,10 +173,13 @@ void platformDaemonEnable(otInstance *aInstance) memset(&sockname, 0, sizeof(struct sockaddr_un)); - (void)unlink(OPENTHREAD_POSIX_DAEMON_SOCKET_NAME); - sockname.sun_family = AF_UNIX; - strncpy(sockname.sun_path, OPENTHREAD_POSIX_DAEMON_SOCKET_NAME, sizeof(sockname.sun_path) - 1); + ret = snprintf(sockname.sun_path, sizeof(sockname.sun_path), OPENTHREAD_POSIX_DAEMON_SOCKET_NAME, gNetifName); + if (ret < 0 && static_cast(ret) >= sizeof(sockname.sun_path)) + { + DieNowWithMessage("sockfile", OT_EXIT_INVALID_ARGUMENTS); + } + (void)unlink(sockname.sun_path); ret = bind(sListenSocket, (const struct sockaddr *)&sockname, sizeof(struct sockaddr_un)); diff --git a/src/posix/platform/openthread-posix-config.h b/src/posix/platform/openthread-posix-config.h index 5d5552799..229b67b33 100644 --- a/src/posix/platform/openthread-posix-config.h +++ b/src/posix/platform/openthread-posix-config.h @@ -83,7 +83,11 @@ * */ #ifndef OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME -#define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/tmp/openthread" +#ifdef __linux__ +#define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/run/openthread-%s" +#else +#define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/tmp/openthread-%s" +#endif #endif /** diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp index 908f3943d..416156358 100644 --- a/src/posix/platform/system.cpp +++ b/src/posix/platform/system.cpp @@ -162,6 +162,8 @@ otInstance *otSysInit(otPlatformConfig *aPlatformConfig) platformNetifInit(instance, aPlatformConfig->mInterfaceName); #elif OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE platformUdpInit(aPlatformConfig->mInterfaceName); +#else + gNetifName[0] = '\0'; #endif #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE || OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE diff --git a/tests/scripts/misc/test_multicast_join.py b/tests/scripts/misc/test_multicast_join.py index aa5492646..27059fcfb 100755 --- a/tests/scripts/misc/test_multicast_join.py +++ b/tests/scripts/misc/test_multicast_join.py @@ -36,21 +36,22 @@ import time from ipaddress import ip_address -def get_maddrs(): - lines = subprocess.run(['ot-ctl', 'ipmaddr'], stdout=subprocess.PIPE).stdout.decode().split() +def get_maddrs(if_name): + lines = subprocess.run(['ot-ctl', '-I', if_name, 'ipmaddr'], stdout=subprocess.PIPE).stdout.decode().split() return [ip_address(l) for l in lines if l.startswith('ff')] def main(): group = 'ff02::158' if_index = int(sys.argv[1]) + if_name = sys.argv[2] with socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) as s: s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, if_index) s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, struct.pack('16si', socket.inet_pton(socket.AF_INET6, group), if_index)) time.sleep(2) - maddrs = get_maddrs() + maddrs = get_maddrs(if_name) print(maddrs) if not any(addr == ip_address(group) for addr in maddrs): return -1 @@ -59,7 +60,7 @@ def main(): struct.pack('16si', socket.inet_pton(socket.AF_INET6, group), if_index)) time.sleep(2) - maddrs = get_maddrs() + maddrs = get_maddrs(if_name) print(maddrs) if any(addr == ip_address(group) for addr in maddrs): return -1