From 83dbbc182f82d40fd7dd8a091e0299227bfd1d19 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Tue, 31 Jan 2023 22:57:28 +0100 Subject: [PATCH] gluon-setup-mode: add remote setup-mode --- package/gluon-setup-mode/Makefile | 3 + .../files/lib/preinit/89_network_setup_mode | 55 +++++++ .../files/lib/preinit/90_setup_mode | 1 - .../luasrc/usr/lib/lua/gluon/setup-mode.lua | 14 ++ package/gluon-setup-mode/src/.gitignore | 2 + package/gluon-setup-mode/src/Makefile | 16 ++ .../src/gluon-remote-setup-mode.h | 18 ++ .../src/send-network-request.c | 91 +++++++++++ .../src/wait-network-request.c | 154 ++++++++++++++++++ 9 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 package/gluon-setup-mode/files/lib/preinit/89_network_setup_mode create mode 100644 package/gluon-setup-mode/luasrc/usr/lib/lua/gluon/setup-mode.lua create mode 100644 package/gluon-setup-mode/src/.gitignore create mode 100644 package/gluon-setup-mode/src/Makefile create mode 100644 package/gluon-setup-mode/src/gluon-remote-setup-mode.h create mode 100644 package/gluon-setup-mode/src/send-network-request.c create mode 100644 package/gluon-setup-mode/src/wait-network-request.c diff --git a/package/gluon-setup-mode/Makefile b/package/gluon-setup-mode/Makefile index cb5aac40..8427e970 100644 --- a/package/gluon-setup-mode/Makefile +++ b/package/gluon-setup-mode/Makefile @@ -35,6 +35,9 @@ init_links := \ define Package/gluon-setup-mode/install $(Gluon/Build/Install) + $(INSTALL_DIR) $(1)/lib/gluon/setup-mode + $(INSTALL_BIN) $(PKG_BUILD_DIR)/wait-network-request $(1)/lib/gluon/setup-mode + $(LN) S20network $(1)/lib/gluon/setup-mode/rc.d/K90network for link in $(init_links); do \ diff --git a/package/gluon-setup-mode/files/lib/preinit/89_network_setup_mode b/package/gluon-setup-mode/files/lib/preinit/89_network_setup_mode new file mode 100644 index 00000000..3df0cf29 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/preinit/89_network_setup_mode @@ -0,0 +1,55 @@ +#!/bin/sh + +. /lib/functions.sh + +device_supports_networked_setup_mode() { + local is_networked="$(lua -e 'print(require("gluon.setup-mode").supports_networked_activation())')" + + if [ "${is_networked}" = "true" ]; then + return 0 + fi + + return 1 +} + +should_activate_networked() { + local setup_ifnames="$(lua -e 'print(require("gluon.sysconfig").setup_ifname)')" + local should_start=1 + + if ! device_supports_networked_setup_mode; then + return 1 + fi + + for iface in $setup_ifnames + do + ip link set dev "${iface}" up + done + + # shellcheck disable=SC2086 + if /lib/gluon/setup-mode/wait-network-request ${setup_ifnames}; then + should_start=0 + fi + + for iface in $setup_ifnames + do + ip link set dev "${iface}" down + done + + return $should_start +} + +network_setup_mode_enable() { + local enabled="$(uci -q get 'gluon-setup-mode.@setup_mode[0].enabled')" + local configured="$(uci -q get 'gluon-setup-mode.@setup_mode[0].configured')" + + if [ "$enabled" = 1 ] || [ "$configured" != 1 ]; then + return 0 + fi + + if should_activate_networked; then + uci -q set 'gluon-setup-mode.@setup_mode[0].enabled=1' + fi + return 0 +} + +boot_hook_add preinit_main network_setup_mode_enable diff --git a/package/gluon-setup-mode/files/lib/preinit/90_setup_mode b/package/gluon-setup-mode/files/lib/preinit/90_setup_mode index b9365be1..a184248a 100644 --- a/package/gluon-setup-mode/files/lib/preinit/90_setup_mode +++ b/package/gluon-setup-mode/files/lib/preinit/90_setup_mode @@ -1,6 +1,5 @@ #!/bin/sh - setup_mode_enable() { local enabled="$(uci -q get 'gluon-setup-mode.@setup_mode[0].enabled')" local configured="$(uci -q get 'gluon-setup-mode.@setup_mode[0].configured')" diff --git a/package/gluon-setup-mode/luasrc/usr/lib/lua/gluon/setup-mode.lua b/package/gluon-setup-mode/luasrc/usr/lib/lua/gluon/setup-mode.lua new file mode 100644 index 00000000..d6395066 --- /dev/null +++ b/package/gluon-setup-mode/luasrc/usr/lib/lua/gluon/setup-mode.lua @@ -0,0 +1,14 @@ +local platform = require 'gluon.platform' + + +local M = {} + +function M.supports_networked_activation() + if platform.match('ramips', 'mt7621', { + 'zyxel,nwa55axe', + }) then + return true + end +end + +return M diff --git a/package/gluon-setup-mode/src/.gitignore b/package/gluon-setup-mode/src/.gitignore new file mode 100644 index 00000000..21b652bd --- /dev/null +++ b/package/gluon-setup-mode/src/.gitignore @@ -0,0 +1,2 @@ +enter-setup-mode +check-setup-mode diff --git a/package/gluon-setup-mode/src/Makefile b/package/gluon-setup-mode/src/Makefile new file mode 100644 index 00000000..c83210ee --- /dev/null +++ b/package/gluon-setup-mode/src/Makefile @@ -0,0 +1,16 @@ +CC:=gcc +CFLAGS:= +LDFLAGS:= + +all: wait-network-request send-network-request + +wait-network-request: + $(CC) $(CFLAGS) $(LDFLAGS) -o wait-network-request wait-network-request.c + +send-network-request: + $(CC) $(CFLAGS) $(LDFLAGS) -o send-network-request send-network-request.c + +clean: + rm -rf send-network-request wait-network-request + +.PHONY: clean all diff --git a/package/gluon-setup-mode/src/gluon-remote-setup-mode.h b/package/gluon-setup-mode/src/gluon-remote-setup-mode.h new file mode 100644 index 00000000..2be8158c --- /dev/null +++ b/package/gluon-setup-mode/src/gluon-remote-setup-mode.h @@ -0,0 +1,18 @@ +#pragma once + +/* Seconds*/ +#define REMOTE_SETUP_MODE_RX_TIMEOUT 5 +#define REMOTE_SETUP_MODE_TX_INTERVAL 1 + +#define REMOTE_SETUP_MODE_MAC_LEN 6 +#define REMOTE_SETUP_MODE_DST_MAC_OFFSET 0 +#define REMOTE_SETUP_MODE_DST_MAC 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e +#define REMOTE_SETUP_MODE_SRC_MAC_OFFSET 6 +#define REMOTE_SETUP_MODE_SRC_MAC 0xda, 0xec, 0xe5, 0x8f, 0x60, 0x14 + +#define REMOTE_SETUP_MODE_ETHERTYPE_OFFSET (REMOTE_SETUP_MODE_MAC_LEN + REMOTE_SETUP_MODE_MAC_LEN) +#define REMOTE_SETUP_MODE_ETHERTYPE_LEN 2 +#define REMOTE_SETUP_MODE_ETHERTYPE 0x23, 0x42 + +#define REMOTE_SETUP_MODE_DATA_CMD_OFFSET (REMOTE_SETUP_MODE_ETHERTYPE_OFFSET + REMOTE_SETUP_MODE_ETHERTYPE_LEN) +#define REMOTE_SETUP_MODE_DATA_CMD_SETUP "SETUP" diff --git a/package/gluon-setup-mode/src/send-network-request.c b/package/gluon-setup-mode/src/send-network-request.c new file mode 100644 index 00000000..38eaa4be --- /dev/null +++ b/package/gluon-setup-mode/src/send-network-request.c @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Utility for performing remote setup-mode activation + * + * Copyright (c) David Bauer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gluon-remote-setup-mode.h" + +#define LLDP_DELAY (REMOTE_SETUP_MODE_TX_INTERVAL * 1000) + +char buf[] = { + /* Destination - LLDP Multicast */ + REMOTE_SETUP_MODE_DST_MAC, + /* Source */ + REMOTE_SETUP_MODE_SRC_MAC, + /* Type */ + REMOTE_SETUP_MODE_ETHERTYPE, + /* Magic*/ + 'S', 'E', 'T', 'U', 'P', + /* Trail */ + 0x00, +}; + +int get_ifindex(int sock, const char *ifname) +{ + struct ifreq ifr; + + strncpy((char *)ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(sock, SIOCGIFINDEX, &ifr)) { + printf("IOCTL error: %s\n", strerror(errno)); + return -1; + } + + return ifr.ifr_ifindex; +} + +int main(int argc, char *argv[]) +{ + struct sockaddr_ll sll; + int ifindex; + int s_fd; + + if (argc < 2 || !strcmp("help", argv[1])) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + s_fd = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL)); + if (s_fd < 0) { + printf("Socket error: %s\n", strerror(errno)); + return 1; + } + + ifindex = get_ifindex(s_fd, argv[1]); + if (ifindex < 0) { + return 1; + } + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = ifindex; + + printf("Sending Reset command\n"); + while (1) { + if (sendto(s_fd, buf, sizeof(buf), 0, (struct sockaddr *) &sll, sizeof(sll)) < 0) { + printf("Error sending packet: %s\n", strerror(errno)); + return 1; + } + + usleep(LLDP_DELAY); + } + + close(s_fd); + + return 0; +} diff --git a/package/gluon-setup-mode/src/wait-network-request.c b/package/gluon-setup-mode/src/wait-network-request.c new file mode 100644 index 00000000..b9f06351 --- /dev/null +++ b/package/gluon-setup-mode/src/wait-network-request.c @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Utility for entering Setup_mode using network + * + * Copyright (c) David Bauer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gluon-remote-setup-mode.h" + +#define MAX_INTERFACES 64 +#define BUFSIZE 1024 + + +char destination[] = { REMOTE_SETUP_MODE_DST_MAC }; +char type[] = { REMOTE_SETUP_MODE_ETHERTYPE }; + + +static int bind_socket_to_device(int fd, int ifindex) +{ + struct sockaddr_ll sll = { + .sll_family = AF_PACKET, + .sll_ifindex = ifindex, + .sll_protocol = htons(ETH_P_ALL) + }; + + if((bind(fd , (struct sockaddr *)&sll , sizeof(sll))) < 0) { + return 1; + } + + return 0; +} + +static int should_reset(char *buf, size_t size) +{ + int should_reset; + char *tlv_ptr; + + should_reset = 0; + + if (size < REMOTE_SETUP_MODE_DATA_CMD_OFFSET + strlen(REMOTE_SETUP_MODE_DATA_CMD_SETUP) + 1) { + /* Length mismatch */ + return 0; + } + + if (buf[size - 1] != 0x00) { + /* Wrong trailer */ + return 0; + } + + if (memcmp(destination, &buf[REMOTE_SETUP_MODE_DST_MAC_OFFSET], sizeof(destination))) { + /* Wrong destination-etheraddr */ + return 0; + } + + if (memcmp(type, &buf[REMOTE_SETUP_MODE_ETHERTYPE_OFFSET], sizeof(type))) { + /* Wrong Ethertype */ + return 0; + } + + if (strcmp(&buf[REMOTE_SETUP_MODE_DATA_CMD_OFFSET], REMOTE_SETUP_MODE_DATA_CMD_SETUP)) { + /* Wrong Command */ + return 0; + } + + return 1; +} + +int main(int argc, char *argv[]) +{ + time_t start_time, current_time; + int ifindex[MAX_INTERFACES]; + int sockets[MAX_INTERFACES]; + char recvbuf[BUFSIZE]; + int num_interfaces; + int received; + int i; + + start_time = time(NULL); + + if (argc < 2) { + fprintf(stderr, "At least one Interface name is required\n", MAX_INTERFACES); + return 1; + } + + if (argc > MAX_INTERFACES) { + fprintf(stderr, "Exceeded maximum number of supported interfaces of %d\n", MAX_INTERFACES); + return 1; + } + + num_interfaces = 0; + for (i = 1; i < argc; i++) { + ifindex[num_interfaces] = if_nametoindex(argv[i]); + if (!ifindex[num_interfaces]) { + fprintf(stderr, "Interface %s not found!\n", argv[i]); + } + num_interfaces++; + } + + for (i = 0; i < num_interfaces; i++) { + sockets[i] = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (sockets[i] < 0) { + fprintf(stderr, "Could not create socket for ifindex %d\n", ifindex[i]); + return 1; + } + + if (bind_socket_to_device(sockets[i], ifindex[i])) { + fprintf(stderr, "Error binding socket to Interface %d\n", ifindex[i]); + return 1; + } + } + + /* Start receiving */ + while (1) { + /* Check if timeout is exceeded */ + current_time = time(NULL); + if (current_time - start_time > REMOTE_SETUP_MODE_RX_TIMEOUT) { + break; + } + + for (i = 0; i < num_interfaces; i++) { + received = recv(sockets[i], recvbuf, BUFSIZE, MSG_DONTWAIT); + if (received < 0 && errno != EAGAIN && errno != EAGAIN) { + fprintf(stderr, "Error receiving from ifindex %d - ret %d\n", ifindex[i], errno); + continue; + } + if (received <= 0) { + continue; + } + + if (!should_reset(recvbuf, received)) { + continue; + } + + return 0; + } + } + + return 1; +}