Merge pull request #1058 from kernelkit/wifi-support

This commit is contained in:
Mattias Walström
2025-06-19 18:34:35 +02:00
committed by GitHub
45 changed files with 1635 additions and 80 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ config QEMU_MACHINE
config QEMU_MACHINE_RAM
string "RAM size (k/M/G)"
default "384M"
default "448M"
help
The default, 384 MiB, works for most configurations. However,
if you get kernel panic with: "System is deadlocked on memory",
@@ -0,0 +1,5 @@
service name:wpa_supplicant :%i \
[2345] wpa_supplicant -s -i %i -c /etc/wpa_supplicant-%i.conf -P/var/run/wpa_supplicant-%i.pid \
-- WPA supplicant @%i
task name:wifi-scanner :%i [2345] <pid/wpa_supplicant:%i> /usr/libexec/infix/wifi-scanner %i -- Start scanning for SSID @ %i
@@ -0,0 +1 @@
SUBSYSTEM=="net", ACTION=="add", TEST=="/sys/class/net/$name/wireless", NAME="wifi%n"
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
if [ $# -ne 1 ]; then
echo "usage: $0 <ifname>"
exit 1
fi
ifname=$1
TIMEOUT=300
status=$(wpa_cli -i $ifname scan)
while [ "$status" != "OK" ]; do
status=$(wpa_cli -i $ifname scan)
TIMEOUT=$((TIMEOUT-1))
[ $TIMEOUT -eq 0 ] && logger -t wifi-scanner "Failed to start scanning $ifname" && exit 1
sleep 0.5
done
+2
View File
@@ -165,6 +165,8 @@ BR2_PACKAGE_PODMAN_DRIVER_VFS=y
BR2_PACKAGE_TETRIS=y
BR2_PACKAGE_ROUSETTE=y
BR2_PACKAGE_RAUC_INSTALLATION_STATUS=y
BR2_PACKAGE_FEATURE_WIFI=y
BR2_PACKAGE_FEATURE_WIFI_DONGLE_REALTEK=y
BR2_PACKAGE_HOST_PYTHON_YANGDOC=y
BR2_DOWNLOAD_FORCE_CHECK_HASHES=y
BR2_PACKAGE_GETENT=y
+2
View File
@@ -167,6 +167,8 @@ BR2_PACKAGE_PODMAN_DRIVER_VFS=y
BR2_PACKAGE_TETRIS=y
BR2_PACKAGE_ROUSETTE=y
BR2_PACKAGE_RAUC_INSTALLATION_STATUS=y
BR2_PACKAGE_FEATURE_WIFI=y
BR2_PACKAGE_FEATURE_WIFI_DONGLE_REALTEK=y
BR2_PACKAGE_HOST_PYTHON_YANGDOC=y
BR2_DOWNLOAD_FORCE_CHECK_HASHES=y
BR2_PACKAGE_GETENT=y
+11 -1
View File
@@ -3,11 +3,21 @@ Change Log
All notable changes to the project are documented in this file.
[v25.05.1][] - 2025-06-12
[v25.06.0][UNRELEASED] -
-------------------------
### Changes
- Upgrade Linux kernel to 6.12.33 (LTS)
- Add Wi-Fi client support and add support for some USB-Wi-Fi cards
### Fixes
[v25.05.1][] - 2025-06-12
-------------------------
### Changes
- Upgrade Linux kernel to 6.12.32 (LTS)
### Fixes
- Fix #1060: Restore of missing CLI commands, regression in Infix v25.05.0
+152
View File
@@ -0,0 +1,152 @@
# Wi-Fi (Wireless LAN)
Infix includes built-in Wi-Fi client support for connecting to
wireless networks. When a compatible Wi-Fi adapter is detected, the
system automatically begins scanning for available networks.
## Current Limitations
- Only client mode is supported (no access point functionality)
- USB hotplug is not supported - adapters must be present at boot
- Interface naming may be inconsistent with multiple USB Wi-Fi adapters
## Supported Wi-Fi Adapters
Wi-Fi support is primarily tested with Realtek chipset-based adapters.
### Known Working Chipsets
- RTL8821CU
- Other Realtek chipsets may work but are not guaranteed
> [!NOTE] Some Realtek chipsets require proprietary drivers not included in the standard kernel
> Firmware requirements vary by chipset
> Check kernel logs if your adapter is not detected
## Configuration
Add a supported Wi-Fi network device. To verify that it has been
detected, look for `wifi0` in `show interfaces`
```
admin@example:/> show interfaces
INTERFACE PROTOCOL STATE DATA
lo loopback UP
ipv4 127.0.0.1/8 (static)
ipv6 ::1/128 (static)
e1 ethernet UP 02:00:00:00:00:01
ipv6 fe80::ff:fe00:1/64 (link-layer)
ipv6 fec0::ff:fe00:1/64 (link-layer)
wifi0 ethernet DOWN f0:09:0d:36:5f:86
wifi ssid: ------, signal: ------
```
Add the new Wi-Fi interface to the configuration to start scanning.
```
admin@example:/config/> set interface wifi0
admin@example:/config/> leave
```
Now the system will now start scanning in the background. To
see the result read the operational datastore for interface `wifi0` or
use the CLI
```
admin@infix-00-00-00:/> show interfaces name wifi0
name : wifi0
type : wifi
index : 3
mtu : 1500
operational status : down
physical address : f0:09:0d:36:5f:86
ipv4 addresses :
ipv6 addresses :
SSID : ----
Signal : ----
SSID ENCRYPTION SIGNAL
ssid1 WPA2-Personal excellent
ssid2 WPA2-Personal excellent
ssid3 WPA2-Personal excellent
ssid4 WPA2-Personal good
ssid5 WPA2-Personal good
ssid6 WPA2-Personal good
```
In the CLI, signal strength is reported as: excellent, good, poor or
bad. For precise values, use NETCONF or RESTCONF, where the RSSI (in
dBm) is available in the operational datastore.
Configure your Wi-Fi secret in the keystore, it should be between 8
and 63 characters
```
admin@example:/> configure
admin@example:/config/> edit keystore symmetric-key example
admin@example:/config/keystore/symmetric-key/example/> set key-format wifi-preshared-key-format
admin@example:/config/keystore/symmetric-key/example/> set cleartext-key mysecret
admin@example:/config/keystore/symmetric-key/example/> leave
admin@example:/>
```
Configure the Wi-Fi settings, set secret to the name selected above
for the symmetric key, in this case `example`.
WPA2 or WPA3 encryption will be automatically selected based on what
the access point supports. No manual selection is required unless
connecting to an open network. No support for certificate based
authentication yet.
Unencrypted network is also supported, to connect to an unencrypted
network (generally not recommended):
```
admin@example:/config/interface/wifi0/> set wifi encryption disabled
```
A valid `country-code` is also required for regulatory compliance, the
valid codes are documented in the YANG model `infix-wifi-country-codes`
```
admin@example:/> configure
admin@example:/config/> edit interface wifi0
admin@example:/config/interface/wifi0/>
admin@example:/config/interface/wifi0/> set wifi ssid ssid1
admin@example:/config/interface/wifi0/> set wifi secret example
admin@example:/config/interface/wifi0/> set wifi country-code SE
admin@example:/config/interface/wifi0/> leave
```
The Wi-Fi negotiation should now start immediately, provided that the
SSID and pre-shared key are correct. You can verify the connection by
running `show interfaces` again.
```
admin@example:/> show interfaces
INTERFACE PROTOCOL STATE DATA
lo loopback UP
ipv4 127.0.0.1/8 (static)
ipv6 ::1/128 (static)
e1 ethernet UP 02:00:00:00:00:01
ipv6 fe80::ff:fe00:1/64 (link-layer)
ipv6 fec0::ff:fe00:1/64 (link-layer)
wifi0 ethernet UP f0:09:0d:36:5f:86
wifi ssid: ssid1, signal: excellent
admin@example:/>
```
## Troubleshooting Connection Issues
Use `show wifi scan wifi0` and `show interfaces` to verify signal strength
and connection status. If issues arise, try the following
troubleshooting steps:
1. **Verify signal strength**: Check that the target network shows "good" or "excellent" signal
2. **Check credentials**: Verify the preshared key in `ietf-keystore`
3. **Review logs**: Check system logs with `show log` for Wi-Fi related errors
4. **Regulatory compliance**: Ensure the country-code matches your location
5. **Hardware detection**: Confirm the adapter appears in `show interfaces`
If issues persist, check the system log for specific error messages that can help identify the root cause.
+1 -1
View File
@@ -2,6 +2,7 @@ menu "Packages"
comment "Hardware Support"
source "$BR2_EXTERNAL_INFIX_PATH/package/board/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/feature-wifi/Config.in"
comment "Software Packages"
source "$BR2_EXTERNAL_INFIX_PATH/package/bin/Config.in"
@@ -42,5 +43,4 @@ source "$BR2_EXTERNAL_INFIX_PATH/package/rousette/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/nghttp2-asio/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/date-cpp/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/rauc-installation-status/Config.in"
endmenu
+12
View File
@@ -25,6 +25,11 @@ else
CONFD_CONF_OPTS += --disable-containers
endif
ifeq ($(BR2_PACKAGE_FEATURE_WIFI),y)
CONFD_CONF_OPTS += --enable-wifi
else
CONFD_CONF_OPTS += --disable-wifi
endif
define CONFD_INSTALL_EXTRA
for fn in confd.conf resolvconf.conf; do \
cp $(CONFD_PKGDIR)/$$fn $(FINIT_D)/available/; \
@@ -69,6 +74,12 @@ define CONFD_INSTALL_YANG_MODULES_CONTAINERS
$(BR2_EXTERNAL_INFIX_PATH)/utils/srload $(@D)/yang/containers.inc
endef
endif
ifeq ($(BR2_PACKAGE_FEATURE_WIFI),y)
define CONFD_INSTALL_YANG_MODULES_WIFI
$(COMMON_SYSREPO_ENV) \
$(BR2_EXTERNAL_INFIX_PATH)/utils/srload $(@D)/yang/wifi.inc
endef
endif
# PER_PACKAGE_DIR
# Since the last package in the dependency chain that runs sysrepoctl is confd, we need to
@@ -97,6 +108,7 @@ CONFD_PRE_BUILD_HOOKS += CONFD_CLEANUP
CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_EXTRA
CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES
CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_CONTAINERS
CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_WIFI
CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_IN_ROMFS
CONFD_TARGET_FINALIZE_HOOKS += CONFD_CLEANUP
+18
View File
@@ -0,0 +1,18 @@
config BR2_PACKAGE_FEATURE_WIFI
bool "Feature Wi-Fi"
select BR2_PACKAGE_WPA_SUPPLICANT
select BR2_PACKAGE_WPA_SUPPLICANT_WPA3
select BR2_PACKAGE_WPA_SUPPLICANT_DEBUG_SYSLOG
select BR2_PACKAGE_WPA_SUPPLICANT_AUTOSCAN
select BR2_PACKAGE_WPA_SUPPLICANT_CLI
help
Enables WiFi in Infix. Enables all requried applications.
config BR2_PACKAGE_FEATURE_WIFI_DONGLE_REALTEK
bool "Realtek USB WiFi Dongles"
depends on BR2_PACKAGE_FEATURE_WIFI
select BR2_PACKAGE_LINUX_FIRMWARE
select BR2_PACKAGE_LINUX_FIRMWARE_RTL_RTW88
select BR2_PACKAGE_LINUX_FIRMWARE_RTL_RTW89
help
Enables Support for RTW88 and RTW89 USB dongles.
+41
View File
@@ -0,0 +1,41 @@
################################################################################
#
# Wi-Fi support
#
################################################################################
FEATURE_WIFI_PACKAGE_VERSION = 1.0
FEATURE_WIFI_PACKAGE_LICENSE = MIT
define FEATURE_WIFI_LINUX_CONFIG_FIXUPS
$(call KCONFIG_ENABLE_OPT,CONFIG_WLAN)
$(call KCONFIG_ENABLE_OPT,CONFIG_RFKILL)
$(call KCONFIG_ENABLE_OPT,CONFIG_MAC80211)
$(call KCONFIG_ENABLE_OPT,CONFIG_CFG80211)
$(if $(filter y,$(BR2_PACKAGE_FEATURE_WIFI_DONGLE_REALTEK)),
$(call KCONFIG_ENABLE_OPT,CONFIG_WLAN_VENDOR_REALTEK)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW89)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8703B)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8723CS)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8723D)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8723DE)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8723DS)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8723DU)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8723X)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8821C)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8821CE)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8821CS)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8821CU)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8822B)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8822BE)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8822BS)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8822BU)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8822C)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8822CU)
$(call KCONFIG_ENABLE_OPT,CONFIG_RTW88_8822CE)
)
endef
$(eval $(generic-package))
@@ -28,7 +28,6 @@ KLISH_PLUGIN_INFIX_CONF_OPTS += --enable-shell
else
KLISH_PLUGIN_INFIX_CONF_OPTS += --disable-shell
endif
ifeq ($(BR2_PACKAGE_BASH),y)
KLISH_PLUGIN_INFIX_CONF_OPTS += --with-shell=/bin/bash
else
@@ -0,0 +1,232 @@
From 9df1957e656940a50b844da879911ab5981ad1a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
Date: Wed, 18 Jun 2025 21:21:18 +0200
Subject: [PATCH] Add pidfile support when not daemonize
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
Adds pidfile.c from libite
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
wpa_supplicant/Makefile | 1 +
wpa_supplicant/pidfile.c | 153 ++++++++++++++++++++++++++++++++
wpa_supplicant/wpa_supplicant.c | 7 +-
3 files changed, 160 insertions(+), 1 deletion(-)
create mode 100644 wpa_supplicant/pidfile.c
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index fd7d7a0bd..e14c7732a 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -104,6 +104,7 @@ endif
OBJS = config.o
OBJS += notify.o
OBJS += bss.o
+OBJS += pidfile.o
OBJS += eap_register.o
OBJS += ../src/utils/common.o
OBJS += ../src/utils/config.o
diff --git a/wpa_supplicant/pidfile.c b/wpa_supplicant/pidfile.c
new file mode 100644
index 000000000..29dfbe242
--- /dev/null
+++ b/wpa_supplicant/pidfile.c
@@ -0,0 +1,153 @@
+/* Updated by troglobit for libite/finit/uftpd projects 2016/07/04 */
+/* $OpenBSD: pidfile.c,v 1.11 2015/06/03 02:24:36 millert Exp $ */
+/* $NetBSD: pidfile.c,v 1.4 2001/02/19 22:43:42 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file pidfile.c
+ * @author NetBSD Foundation Inc.
+ * @date 1999
+ * @copyright 2-clause BSD License
+ */
+
+#include <sys/stat.h> /* utimensat() */
+#include <sys/time.h> /* utimensat() on *BSD */
+#include <sys/types.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifndef pidfile
+static char *pidfile_path = NULL;
+static pid_t pidfile_pid = 0;
+
+static void pidfile_cleanup(void);
+
+const char *__pidfile_path = _PATH_VARRUN; /* Note: includes trailing slash '/' */
+const char *__pidfile_name = NULL;
+extern char *__progname;
+
+/**
+ * Create or update mtime of process PID file.
+ * @param basename Program name, or @c NULL, may start with '/'
+ *
+ * This function is intended to be used by UNIX daemons to save the PID of the main process
+ * responsible for handling signals. If @p basename is @c NULL the implicit @a __progname
+ * variable from the C-library is used. The @p basename may also start with '/', in which
+ * case it is interpreted as the absolute path to the PID file.
+ *
+ * @returns POSIX OK(0) on success, otherwise non-zero on error.
+ */
+int pidfile(const char *basename)
+{
+ int save_errno;
+ int atexit_already;
+ pid_t pid;
+ FILE *f;
+
+ if (basename == NULL)
+ basename = __progname;
+
+ pid = getpid();
+ atexit_already = 0;
+
+ if (pidfile_path != NULL) {
+ if (!access(pidfile_path, R_OK) && pid == pidfile_pid) {
+ utimensat(0, pidfile_path, NULL, 0);
+ return (0);
+ }
+ free(pidfile_path);
+ pidfile_path = NULL;
+ __pidfile_name = NULL;
+ atexit_already = 1;
+ }
+
+ if (basename[0] != '/') {
+ if (asprintf(&pidfile_path, "%s.pid", basename) == -1)
+ return (-1);
+ } else {
+ if (asprintf(&pidfile_path, "%s", basename) == -1)
+ return (-1);
+ }
+
+ if ((f = fopen(pidfile_path, "w")) == NULL) {
+ save_errno = errno;
+ free(pidfile_path);
+ pidfile_path = NULL;
+ errno = save_errno;
+ return (-1);
+ }
+
+ if (fprintf(f, "%ld\n", (long)pid) <= 0 || fflush(f) != 0) {
+ save_errno = errno;
+ (void) fclose(f);
+ (void) unlink(pidfile_path);
+ free(pidfile_path);
+ pidfile_path = NULL;
+ errno = save_errno;
+ return (-1);
+ }
+ (void) fclose(f);
+ __pidfile_name = pidfile_path;
+
+ /*
+ * LITE extension, no need to set up another atexit() handler
+ * if user only called us to update the mtime of the PID file
+ */
+ if (atexit_already)
+ return (0);
+
+ pidfile_pid = pid;
+ if (atexit(pidfile_cleanup) < 0) {
+ save_errno = errno;
+ (void) unlink(pidfile_path);
+ free(pidfile_path);
+ pidfile_path = NULL;
+ pidfile_pid = 0;
+ errno = save_errno;
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+pidfile_cleanup(void)
+{
+ if (pidfile_path != NULL && pidfile_pid == getpid()) {
+ (void) unlink(pidfile_path);
+ free(pidfile_path);
+ pidfile_path = NULL;
+ }
+}
+#endif
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index dfbd63163..3a762ebc2 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -123,6 +123,7 @@ const char *const wpa_supplicant_full_license5 =
"\n";
#endif /* CONFIG_NO_STDOUT_DEBUG */
+int pidfile(const char *basename);
static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx);
static void wpas_verify_ssid_beacon(void *eloop_ctx, void *timeout_ctx);
@@ -1490,6 +1491,9 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
/* Ignore errors since we cannot really do much to fix this */
wpa_printf(MSG_DEBUG, "Could not reopen debug log file");
}
+
+ if (!global->params.daemonize)
+ pidfile(global->params.pid_file);
}
@@ -8555,7 +8559,6 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
return global;
}
-
/**
* wpa_supplicant_run - Run the %wpa_supplicant main event loop
* @global: Pointer to global data from wpa_supplicant_init()
@@ -8589,6 +8592,8 @@ int wpa_supplicant_run(struct wpa_global *global)
eloop_register_signal_terminate(wpa_supplicant_terminate, global);
eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
+ if (!global->params.daemonize)
+ pidfile(global->params.pid_file);
eloop_run();
return 0;
--
2.43.0
+1 -1
View File
@@ -1,4 +1,4 @@
pkglibexec_SCRIPTS = bootstrap error load gen-service gen-hostname \
gen-interfaces gen-motd gen-hardware gen-version \
mstpd-wait-online
mstpd-wait-online wait-interface
sbin_SCRIPTS = dagger migrate
+21
View File
@@ -0,0 +1,21 @@
#!/bin/sh
if [ $# -lt 2 ]; then
echo "usage: $0 <ifname> <timeout>"
exit 1
fi
ifname=$1
timeout=$2
while true; do
if ip link show $ifname &>/dev/null; then
break
fi
retries=$(($retries + 1))
if [ $retries -ge $timeout ]; then
logger -t wait-interface "Timeout: Interface $ifname not found after $timeout seconds"
exit 1
fi
sleep 1
done
+8
View File
@@ -41,6 +41,10 @@ AC_ARG_ENABLE(containers,
AS_HELP_STRING([--enable-containers], [Enable support for containers]),,[
enable_containers=no])
AC_ARG_ENABLE(wifi,
AS_HELP_STRING([--enable-wifi], [Enable support for Wi-Fi]),,[
enable_wifi=no])
AC_ARG_WITH(login-shell,
AS_HELP_STRING([--with-login-shell=shell], [Login shell for new users, default: /bin/false]),
[login_shell=$withval], [login_shell=yes])
@@ -52,6 +56,9 @@ AC_ARG_WITH(crypt,
AS_IF([test "x$enable_containers" = "xyes"], [
AC_DEFINE(CONTAINERS, 1, [Built with container support])])
AS_IF([test "x$enable_wifi" = "xyes"], [
AC_DEFINE(HAVE_WIFI, 1, [Built with Wi-Fi support])])
AS_IF([test "x$with_login_shell" != "xno"], [
AS_IF([test "x$login_shell" = "xyes"], [login_shell=/bin/false])
AC_DEFINE_UNQUOTED(LOGIN_SHELL, "$login_shell", [Default: /bin/false])],[
@@ -122,6 +129,7 @@ cat <<EOF
Optional features:
Container support ....: $enable_containers
Wi-Fi support ........: $enable_wifi
Login shell ..........: $login_shell
Default crypt algo ...: $crypt
+1
View File
@@ -37,6 +37,7 @@ confd_plugin_la_SOURCES = \
infix-if-vlan.c \
infix-if-gre.c \
infix-if-vxlan.c \
infix-if-wifi.c \
ietf-keystore.c \
ietf-system.c \
ietf-syslog.c \
+14 -9
View File
@@ -12,6 +12,7 @@
#include <sys/time.h>
#include <sys/param.h>
#include <unistd.h>
#include <stdbool.h>
#include <libite/lite.h>
#include <libite/queue.h>
@@ -137,9 +138,20 @@ static inline int register_change(sr_session_ctx_t *session, const char *module,
int flags, sr_module_change_cb cb, void *arg, sr_subscription_ctx_t **sub)
{
struct confd *ptr = (struct confd *)arg;
bool need_core_hooks;
int rc;
if (!flags) {
/*
* For standard subscribtions we hook into the callback chain
* for all modules to figure out, per changeset, which of the
* callbacks is the last one. This is where we want to call the
* global commit-done hook for candidate -> running changes and
* the startup-save hook for running -> startup copying.
*/
need_core_hooks = !(flags & SR_SUBSCR_UPDATE);
if (need_core_hooks) {
sr_module_change_subscribe(ptr->session, module, xpath, core_pre_hook, NULL,
0, SR_SUBSCR_PASSIVE, sub);
}
@@ -151,14 +163,7 @@ static inline int register_change(sr_session_ctx_t *session, const char *module,
return rc;
}
/*
* For standard subscribtions we hook into the callback chain
* for all modules to figure out, per changeset, which of the
* callbacks is the last one. This is where we want to call the
* global commit-done hook for candidate -> running changes and
* the startup-save hook for running -> startup copying.
*/
if (!flags) {
if (need_core_hooks) {
sr_module_change_subscribe(ptr->session, module, xpath, core_post_hook, NULL,
core_hook_prio(), SR_SUBSCR_PASSIVE, sub);
sr_module_change_subscribe(ptr->startup, module, xpath, core_startup_save, NULL,
+3
View File
@@ -52,6 +52,9 @@ enum netdag_exit {
};
enum netdag_init {
/* Timeout for slow interfaces */
NETDAG_INIT_TIMEOUT = 00,
/* Configure link layer */
NETDAG_INIT_PHYS = 10,
@@ -114,23 +114,6 @@ int netdag_gen_ethtool(struct dagger *net, struct lyd_node *cif, struct lyd_node
struct lyd_node *eth = lydx_get_child(dif, "ethernet");
int err;
/*
* Story time: when assigning a physical interface to a container, and then
* removing it, even though our type may be 'etherlike' we will
* get the following from sysrepo:
*
* "ieee802-ethernet-interface:ethernet": {
* "@": {
* "yang:operation": "delete"
* },
* "duplex": "full"
* },
*
* Hence this "redundant" check.
*/
if (iftype_from_iface(cif) != IFT_ETH)
return 0;
if (!eth)
return 0;
+111 -21
View File
@@ -9,8 +9,11 @@
#include <srx/common.h>
#include <srx/lyx.h>
#include <srx/srx_val.h>
#include <libyang/libyang.h>
#include "ietf-interfaces.h"
#define IFACE_PROBE_TIMEOUT 40
bool iface_has_quirk(const char *ifname, const char *quirkname)
{
@@ -27,6 +30,7 @@ bool iface_has_quirk(const char *ifname, const char *quirkname)
return quirk ? json_is_true(quirk) : false;
}
static bool iface_is_phys(const char *ifname)
{
bool is_phys = false;
@@ -84,7 +88,9 @@ static int ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path)
goto out;
}
if (iface_is_phys(ifname))
if (!fnmatch("wifi+([0-9])", ifname, FNM_EXTMATCH))
inferred.data.string_val = "infix-if-type:wifi";
else if (iface_is_phys(ifname))
inferred.data.string_val = "infix-if-type:ethernet";
else if (!fnmatch("br+([0-9])", ifname, FNM_EXTMATCH))
inferred.data.string_val = "infix-if-type:bridge";
@@ -112,6 +118,7 @@ static int ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path)
inferred.data.string_val = "infix-if-type:gretap";
else if (!fnmatch("vxlan+([0-9])", ifname, FNM_EXTMATCH))
inferred.data.string_val = "infix-if-type:vxlan";
free(ifname);
if (inferred.data.string_val)
@@ -416,9 +423,13 @@ static int netdag_gen_afspec_add(sr_session_ctx_t *session, struct dagger *net,
return vlan_gen(NULL, cif, ip);
case IFT_VXLAN:
return vxlan_gen(NULL, cif, ip);
case IFT_WIFI:
return wifi_gen(NULL, cif, net);
case IFT_ETH:
return netdag_gen_ethtool(net, cif, dif);
case IFT_LO:
return 0;
case IFT_UNKNOWN:
sr_session_set_error_message(net->session, "%s: unsupported interface type \"%s\"",
ifname, lydx_get_cattr(cif, "type"));
@@ -440,16 +451,18 @@ static int netdag_gen_afspec_set(sr_session_ctx_t *session, struct dagger *net,
return lag_gen(dif, cif, ip, 0);
case IFT_VLAN:
return vlan_gen(dif, cif, ip);
case IFT_ETH:
return netdag_gen_ethtool(net, cif, dif);
case IFT_WIFI:
return wifi_gen(dif, cif, net);
case IFT_DUMMY:
case IFT_GRE:
case IFT_GRETAP:
case IFT_VETH:
case IFT_VXLAN:
case IFT_LO:
return 0;
case IFT_ETH:
case IFT_LO:
case IFT_UNKNOWN:
return ERR_IFACE(cif, -ENOSYS, "unsupported interface type \"%s\"",
lydx_get_cattr(cif, "type"));
@@ -466,10 +479,11 @@ static bool netdag_must_del(struct lyd_node *dif, struct lyd_node *cif)
case IFT_DUMMY:
case IFT_LO:
break;
case IFT_WIFI:
case IFT_ETH:
/* case IFT_LAG: */
/* ... REMEMBER WHEN ADDING BOND SUPPORT ... */
return lydx_get_child(dif, "custom-phys-address");
case IFT_GRE:
case IFT_GRETAP:
return lydx_get_descendant(lyd_child(dif), "gre", NULL);
@@ -519,12 +533,12 @@ static int link_gen_del(struct lyd_node *dif, FILE *ip)
return 0;
}
static int veth_gen_del(struct lyd_node *dif, FILE *ip)
static int veth_gen_del(struct lyd_node *dif, FILE *sh)
{
if (!veth_is_primary(dif))
return 0;
return link_gen_del(dif, ip);
return link_gen_del(dif, sh);
}
static int netdag_gen_iface_del(struct dagger *net, struct lyd_node *dif,
@@ -554,6 +568,10 @@ static int netdag_gen_iface_del(struct dagger *net, struct lyd_node *dif,
case IFT_LO:
eth_gen_del(dif, ip);
break;
case IFT_WIFI:
eth_gen_del(dif, ip);
wifi_gen_del(dif, net);
break;
case IFT_VETH:
veth_gen_del(dif, ip);
break;
@@ -573,16 +591,35 @@ static int netdag_gen_iface_del(struct dagger *net, struct lyd_node *dif,
return 0;
}
static sr_error_t netdag_gen_iface_timeout(struct dagger *net, const char *ifname, const char *iftype)
{
if (!strcmp(iftype, "infix-if-type:ethernet") || !strcmp(iftype, "infix-if-type:wifi")) {
FILE *wait = dagger_fopen_net_init(net, ifname, NETDAG_INIT_TIMEOUT, "wait-interface.sh");
if (!wait) {
return -EIO;
}
fprintf(wait, "/usr/libexec/confd/wait-interface %s %d\n", ifname, IFACE_PROBE_TIMEOUT);
fclose(wait);
}
return SR_ERR_OK;
}
static sr_error_t netdag_gen_iface(sr_session_ctx_t *session, struct dagger *net,
struct lyd_node *dif, struct lyd_node *cif)
{
const char *ifname = lydx_get_cattr(dif, "name");
const char *iftype = lydx_get_cattr(dif, "type")?:lydx_get_cattr(cif, "type");
enum lydx_op op = lydx_get_op(dif);
const char *attr;
int err = 0;
bool fixed;
FILE *ip;
err = netdag_gen_iface_timeout(net, ifname, iftype);
if (err)
goto err;
if ((err = cni_netdag_gen_iface(net, ifname, dif, cif))) {
/* error or managed by CNI/podman */
if (err > 0)
@@ -590,9 +627,7 @@ static sr_error_t netdag_gen_iface(sr_session_ctx_t *session, struct dagger *net
goto err;
}
fixed = iface_is_phys(ifname) || !strcmp(ifname, "lo");
DEBUG("%s(%s) %s", ifname, fixed ? "fixed" : "dynamic",
DEBUG("%s %s", ifname,
(op == LYDX_OP_NONE) ? "mod" : ((op == LYDX_OP_CREATE) ? "add" : "del"));
if (op == LYDX_OP_DELETE) {
@@ -630,7 +665,7 @@ static sr_error_t netdag_gen_iface(sr_session_ctx_t *session, struct dagger *net
goto err;
}
if (!fixed && op == LYDX_OP_CREATE) {
if (op == LYDX_OP_CREATE) {
err = netdag_gen_afspec_add(session, net, dif, cif, ip);
if (err)
goto err_close_ip;
@@ -655,7 +690,7 @@ static sr_error_t netdag_gen_iface(sr_session_ctx_t *session, struct dagger *net
goto err_close_ip;
/* Set type specific attributes */
if (!fixed && op != LYDX_OP_CREATE) {
if (op != LYDX_OP_CREATE) {
err = netdag_gen_afspec_set(session, net, dif, cif, ip);
if (err)
goto err_close_ip;
@@ -679,8 +714,6 @@ static sr_error_t netdag_gen_iface(sr_session_ctx_t *session, struct dagger *net
err = err ? : netdag_gen_sysctl(net, cif, dif);
err = err ? : netdag_gen_ethtool(net, cif, dif);
err_close_ip:
fclose(ip);
err:
@@ -710,6 +743,7 @@ static int netdag_init_iface(struct lyd_node *cif)
case IFT_DUMMY:
case IFT_ETH:
case IFT_WIFI:
case IFT_GRE:
case IFT_GRETAP:
case IFT_LO:
@@ -790,7 +824,7 @@ static int ifchange(sr_session_ctx_t *session, uint32_t sub_id, const char *modu
if (err)
return err;
err = sr_get_data(session, "/interfaces/interface", 0, 0, 0, &cfg);
err = sr_get_data(session, "//.", 0, 0, 0, &cfg);
if (err || !cfg)
goto err_abandon;
@@ -800,7 +834,6 @@ static int ifchange(sr_session_ctx_t *session, uint32_t sub_id, const char *modu
cifs = lydx_get_descendant(cfg->tree, "interfaces", "interface", NULL);
difs = lydx_get_descendant(diff, "interfaces", "interface", NULL);
err = netdag_init(session, &confd->netdag, cifs, difs);
if (err)
goto err_free_diff;
@@ -829,15 +862,72 @@ err_abandon:
return err;
}
static int keystorecb(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *_confd)
{
const struct lyd_node *diff;
const char *secret_name;
struct confd *confd = _confd;
struct lyd_node *interfaces, *interface, *dkeys, *dkey, *wifi;
sr_data_t *cfg = NULL;
int err = SR_ERR_OK;
switch (event) {
case SR_EV_CHANGE:
break;
default:
return SR_ERR_OK;
}
err = sr_get_data(session, "/interfaces/interface", 0, 0, 0, &cfg);
if (err || !cfg)
return err;
diff = sr_get_change_diff(session);
if (!diff)
goto cleanup;
interfaces = lydx_get_descendant(cfg->tree, "interfaces", "interface", NULL);
dkeys = lydx_get_descendant((struct lyd_node*) diff, "keystore", "symmetric-keys", "symmetric-key", NULL);
LYX_LIST_FOR_EACH(dkeys, dkey, "symmetric-key") {
secret_name = lydx_get_cattr(dkey, "name");
if (!secret_name)
continue;
LYX_LIST_FOR_EACH(interfaces, interface, "interface") {
const char *name;
if (iftype_from_iface(interface) != IFT_WIFI)
continue;
wifi = lydx_get_child(interface, "wifi");
if (!wifi)
continue;
name = lydx_get_cattr(wifi, "secret");
if (!name || strcmp(name, secret_name))
continue;
wifi_gen(NULL, interface, &confd->netdag);
}
}
cleanup:
if (cfg)
sr_release_data(cfg);
return err;
}
int ietf_interfaces_init(struct confd *confd)
{
int rc;
REGISTER_CHANGE(confd->session, "ietf-interfaces", "/ietf-interfaces:interfaces//.",
0, ifchange, confd, &confd->sub);
SR_SUBSCR_CHANGE_ALL_MODULES, ifchange, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "ietf-keystore", "//*",
SR_SUBSCR_CHANGE_ALL_MODULES, keystorecb, confd, &confd->sub);
REGISTER_CHANGE(confd->cand, "ietf-interfaces", "/ietf-interfaces:interfaces//.",
SR_SUBSCR_UPDATE, ifchange_cand, confd, &confd->sub);
return SR_ERR_OK;
fail:
ERROR("failed, error %d: %s", rc, sr_strerror(rc));
+5
View File
@@ -25,6 +25,7 @@
_map(IFT_BRIDGE, "infix-if-type:bridge") \
_map(IFT_DUMMY, "infix-if-type:dummy") \
_map(IFT_ETH, "infix-if-type:ethernet") \
_map(IFT_WIFI, "infix-if-type:wifi") \
_map(IFT_GRE, "infix-if-type:gre") \
_map(IFT_GRETAP, "infix-if-type:gretap") \
_map(IFT_LAG, "infix-if-type:lag") \
@@ -119,6 +120,10 @@ int bridge_mcd_gen(struct lyd_node *cifs);
/* infix-if-bridge-port.c */
int bridge_port_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip);
/* infix-if-wifi.c */
int wifi_gen(struct lyd_node *dif, struct lyd_node *cif, struct dagger *net);
int wifi_gen_del(struct lyd_node *dif, struct dagger *net);
/* infix-if-gre.c */
int gre_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip);
+111
View File
@@ -0,0 +1,111 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include <srx/lyx.h>
#include "ietf-interfaces.h"
#define WPA_SUPPLICANT_FINIT_CONF "/etc/finit.d/available/wpa_supplicant-%s.conf"
#define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant-%s.conf"
static int wifi_gen_config(const char *ifname, const char *ssid, const char *country, const char *secret, const char* encryption, struct dagger *net)
{
FILE *wpa_supplicant = NULL, *wpa = NULL;
char *encryption_str;
int rc = SR_ERR_OK;
if (!secret || !ssid || !country || !encryption) {
/* Not an error, updated from two ways, interface cb and keystore cb. */
return 0;
}
wpa = dagger_fopen_net_init(net, ifname, NETDAG_INIT_POST, "wpa_supplicant.sh");
if (!wpa) {
rc = SR_ERR_INTERNAL;
goto out;
}
fprintf(wpa, "# Generated by Infix confd\n");
fprintf(wpa, "initctl -bfqn enable wifi@%s\n", ifname);
fclose(wpa);
wpa_supplicant = fopenf("w", WPA_SUPPLICANT_CONF, ifname);
if (!wpa_supplicant) {
rc = SR_ERR_INTERNAL;
goto out;
}
if (ssid) {
if (!strcmp(encryption, "disabled")) {
asprintf(&encryption_str, "key_mgmt=NONE");
} else {
asprintf(&encryption_str, "key_mgmt=SAE WPA-PSK\npsk=\"%s\"", secret);
}
fprintf(wpa_supplicant,
"country=%s\n"
"ctrl_interface=/run/wpa_supplicant\n"
"autoscan=periodic:10\n"
"ap_scan=1\n"
"network={\n"
"bgscan=\"simple: 30:-45:300\"\n"
"ssid=\"%s\"\n"
"%s\n"
"}\n", country, ssid, encryption_str);
free(encryption_str);
} else {
fprintf(wpa_supplicant,
"ctrl_interface=/run/wpa_supplicant\n"
"autoscan=periodic:10\n"
"ap_scan=1\n");
}
fclose(wpa_supplicant);
out:
return rc;
}
int wifi_gen(struct lyd_node *dif, struct lyd_node *cif, struct dagger *net)
{
const char *ssid, *secret_name, *secret, *ifname, *country, *encryption;
struct lyd_node *wifi, *secret_node;
bool enabled;
ifname = lydx_get_cattr(cif, "name");
if (cif && !lydx_get_child(cif, "wifi")) {
return wifi_gen_config(ifname, NULL, NULL, NULL, NULL, net);
}
if (dif && !lydx_get_child(dif, "wifi")) {
return SR_ERR_OK;
}
enabled = lydx_get_bool(cif, "enabled");
wifi = lydx_get_child(cif, "wifi");
ssid = lydx_get_cattr(wifi, "ssid");
secret_name = lydx_get_cattr(wifi, "secret");
country = lydx_get_cattr(wifi, "country-code");
encryption = lydx_get_cattr(wifi, "encryption");
secret_node = lydx_get_xpathf(cif, "../../keystore/symmetric-keys/symmetric-key[name='%s']", secret_name);
secret = lydx_get_cattr(secret_node, "cleartext-key");
if (!enabled)
return wifi_gen_del(cif, net);
return wifi_gen_config(ifname, ssid, country, secret, encryption, net);
}
int wifi_gen_del(struct lyd_node *dif, struct dagger *net)
{
const char *ifname = lydx_get_cattr(dif, "name");
FILE *iw = dagger_fopen_net_exit(net, ifname, NETDAG_EXIT_PRE, "iw.sh");
fprintf(iw, "# Generated by Infix confd\n");
fprintf(iw, "iw dev %s disconnect\n", ifname);
fprintf(iw, "initctl -bfqn disable wifi@%s\n", ifname);
fclose(iw);
erasef(WPA_SUPPLICANT_CONF, ifname);
return SR_ERR_OK;
}
+5 -4
View File
@@ -1,5 +1,4 @@
# -*- sh -*-
# REMEMBER TO UPDATE infix-interfaces ALSO IN containers.inc
MODULES=(
"ietf-system@2014-08-06.yang -e authentication -e local-users -e ntp -e ntp-udp-port -e timezone-name"
@@ -35,7 +34,9 @@ MODULES=(
"ieee802-ethernet-interface@2019-06-21.yang"
"infix-ethernet-interface@2024-02-27.yang"
"infix-factory-default@2023-06-28.yang"
"infix-interfaces@2025-01-09.yang -e vlan-filtering"
"infix-crypto-types@2025-02-04.yang"
"infix-keystore@2025-02-04.yang"
"infix-interfaces@2025-06-17.yang -e vlan-filtering"
"ietf-crypto-types -e cleartext-symmetric-keys"
"infix-crypto-types@2025-06-17.yang"
"ietf-keystore -e symmetric-keys"
"infix-keystore@2025-06-17.yang"
)
@@ -5,6 +5,10 @@ module infix-crypto-types {
import ietf-crypto-types {
prefix ct;
}
revision 2025-06-17 {
description "Add Wi-Fi secret support.";
}
revision 2025-02-04 {
description "Initial";
}
@@ -24,4 +28,14 @@ module infix-crypto-types {
base public-key-format;
base ct:ssh-public-key-format;
}
identity symmetric-key-format {
description
"Base for symmetric key format";
}
identity wifi-preshared-key-format {
base ct:symmetric-key-format;
base symmetric-key-format;
description
"WiFi secret key";
}
}
+14
View File
@@ -38,6 +38,14 @@ module infix-if-type {
reference "internal";
}
/*
* Features
*/
feature wifi {
description "WiFi support is an optional build-time feature in Infix.";
}
/*
* Identities
*/
@@ -98,4 +106,10 @@ module infix-if-type {
base ianaift:l2vlan;
description "Layer 2 Virtual LAN using 802.1Q.";
}
identity wifi {
if-feature wifi;
base infix-interface-type;
base ianaift:ieee80211;
description "WiFi interface";
}
}
+185
View File
@@ -0,0 +1,185 @@
submodule infix-if-wifi {
yang-version 1.1;
belongs-to infix-interfaces {
prefix infix-if;
}
import ietf-interfaces {
prefix if;
}
import ietf-yang-types {
prefix yang;
}
import ietf-netconf-acm {
prefix nacm;
}
import ietf-keystore {
prefix ks;
}
import infix-crypto-types {
prefix ixct;
}
import infix-if-type {
prefix infixift;
}
import infix-wifi-country-codes {
prefix iwcc;
}
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description
"WiFi-specific extensions to the standard IETF interfaces model.
This submodule defines configuration and operational data relevant to
WiFi interfaces, including security settings, network
discovery, and regulatory compliance.
It supports WiFi client mode and enables comprehensive management of
wireless connections, including encryption, country codes, and scanning.";
revision 2025-05-27 {
description "Initial revision.";
reference "internal";
}
feature wifi {
description "WiFi support is an optional build-time feature in Infix.";
}
typedef encryption {
type enumeration {
enum auto {
description
"Enables WPA/WPA2/WPA3 encryption with automatic protocol
negotiation. The system uses the strongest supported variant supported by Access Point.";
}
enum disabled {
description
"Disables encryption for an open network.
WARNING: Open networks transmit data unencrypted and should only
be used in trusted environments.";
}
}
description
"Encryption modes available for WiFi connections.
- auto: Secure connection using WPA3/WPA2/WPA (auto-selected)
- disabled: Open network (unencrypted)";
}
augment "/if:interfaces/if:interface" {
when "derived-from-or-self(if:type, 'infixift:wifi')" {
description
"Applies only to interfaces of type 'wifi'.";
}
container wifi {
if-feature wifi;
presence "Configure Wi-Fi settings";
description
"WiFi-specific configuration and operational data.";
leaf country-code {
type iwcc:country-code;
mandatory true;
description
"Two-letter ISO 3166-1 country code for regulatory compliance.
Examples: 'US', 'DE', 'JP'.
WARNING: Incorrect values may violate local laws.";
}
leaf encryption {
default auto;
type encryption;
description
"WiFi encryption method.
- auto (default): Enables WPA2/WPA3 auto-negotiation
- disabled: Disables encryption (open network)";
}
leaf ssid {
type string {
length "1..32";
}
mandatory true;
description
"WiFi network name (SSID).
Case-sensitive, must match the target network.
Length: 132 characters.";
}
leaf secret {
type ks:symmetric-key-ref;
mandatory true;
must "../encryption != 'disabled'" {
error-message
"Pre-shared key required unless encryption is disabled.";
}
description
"Pre-shared key (PSK) for WPA-secured networks.";
}
leaf rssi {
config false;
type int16;
units "dBm";
description
"Current received signal strength (RSSI) in dBm.
Lower (more negative) values indicate stronger signals.";
}
list scan-results {
config false;
key ssid;
description
"List of discovered networks.";
leaf ssid {
type string;
description
"SSID of the discovered network.";
}
leaf bssid {
type string;
description
"BSSID of the discovered network.";
}
leaf rssi {
type int16;
units "dBm";
description
"Signal strength of the network.";
}
leaf channel {
type int16;
description
"Channel on which the network was detected.";
}
leaf-list encryption {
ordered-by user;
type string;
description
"Human-readable description of the detected security.";
}
}
}
}
}
+1
View File
@@ -0,0 +1 @@
infix-if-wifi.yang
+13 -4
View File
@@ -3,9 +3,6 @@ module infix-interfaces {
namespace "urn:infix:interfaces:ns:yang:1.0";
prefix infix-if;
import infix-if-type {
prefix infix-ift;
}
import ietf-interfaces {
prefix if;
}
@@ -15,6 +12,12 @@ module infix-interfaces {
import ietf-inet-types {
prefix inet;
}
import ietf-keystore {
prefix ks;
}
import infix-if-type {
prefix infix-ift;
}
include infix-if-base;
include infix-if-bridge;
@@ -24,12 +27,18 @@ module infix-interfaces {
include infix-if-vlan;
include infix-if-gre;
include infix-if-vxlan;
include infix-if-wifi;
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description "Linux bridge and lag extensions for ietf-interfaces.";
revision 2025-01-09 {
revision 2025-06-17 {
description "Add support for Wi-Fi client.";
reference "internal";
}
revision 2025-01-09 {
description "Add support for link aggregation, static and LACP.";
reference "internal";
}
+37
View File
@@ -5,9 +5,15 @@ module infix-keystore {
import ietf-keystore {
prefix ks;
}
import ietf-crypto-types {
prefix ct;
}
import infix-crypto-types {
prefix infix-ct;
}
revision 2025-06-17 {
description "Add Wi-Fi secrets support";
}
revision 2025-02-04 {
description "Initial";
}
@@ -25,4 +31,35 @@ module infix-keystore {
}
}
}
deviation "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-format" {
deviate not-supported;
}
augment "/ks:keystore/ks:symmetric-keys/ks:symmetric-key" {
leaf key-format {
type identityref {
base infix-ct:symmetric-key-format;
}
description
"Identifies the symmetric key's format
Valid symmetric key formats are:
wifi-preshared-key-format - WiFi preshared key";
}
}
deviation "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-type/ks:cleartext-key/ks:cleartext-key" {
deviate not-supported;
}
augment "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-type" {
case cleartext-key {
leaf cleartext-key {
type string;
must "../../ks:key-format != 'infix-ct:wifi-preshared-key-format' or " +
"(string-length(.) >= 8 and string-length(.) <= 63)" {
error-message "WiFi pre-shared key must be 8-63 characters long";
}
description "WiFi pre-shared key: 8-63 printable ASCII characters";
}
}
}
}
+11
View File
@@ -3,10 +3,15 @@ module infix-meta {
namespace "urn:infix:meta:ns:yang:1.0";
prefix infix-meta;
import ietf-yang-metadata {
prefix md;
}
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description "Infix metadata.";
revision 2024-10-18 {
description "Mark entire meta container obsolete.";
reference "internal";
@@ -16,6 +21,12 @@ module infix-meta {
reference "internal";
}
md:annotation dirty {
type string;
description
"data";
}
container meta {
description "Meta data";
status obsolete;
@@ -0,0 +1,287 @@
module infix-wifi-country-codes {
yang-version 1.1;
namespace "urn:infix:wifi-country-codes";
prefix iwcc;
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description
"This module defines country codes for WiFi regulatory domain
configuration based on ISO 3166-1 alpha-2 standard.
This model provides country code definitions for use in
802.11 wireless LAN regulatory compliance configuration.
The regulatory domain configuration follows the principles
established in IETF RFCs for wireless access point management.";
revision 2025-06-02 {
description
"Initial revision for WiFi country code support.";
reference
"RFC 5415: Control And Provisioning of Wireless Access Points (CAPWAP) Protocol Specification
RFC 5416: Control and Provisioning of Wireless Access Points (CAPWAP) Protocol Binding for IEEE 802.11";
}
typedef country-code {
type enumeration {
enum "AD" { description "Andorra"; }
enum "AE" { description "United Arab Emirates"; }
enum "AF" { description "Afghanistan"; }
enum "AG" { description "Antigua and Barbuda"; }
enum "AI" { description "Anguilla"; }
enum "AL" { description "Albania"; }
enum "AM" { description "Armenia"; }
enum "AO" { description "Angola"; }
enum "AQ" { description "Antarctica"; }
enum "AR" { description "Argentina"; }
enum "AS" { description "American Samoa"; }
enum "AT" { description "Austria"; }
enum "AU" { description "Australia"; }
enum "AW" { description "Aruba"; }
enum "AX" { description "Åland Islands"; }
enum "AZ" { description "Azerbaijan"; }
enum "BA" { description "Bosnia and Herzegovina"; }
enum "BB" { description "Barbados"; }
enum "BD" { description "Bangladesh"; }
enum "BE" { description "Belgium"; }
enum "BF" { description "Burkina Faso"; }
enum "BG" { description "Bulgaria"; }
enum "BH" { description "Bahrain"; }
enum "BI" { description "Burundi"; }
enum "BJ" { description "Benin"; }
enum "BL" { description "Saint Barthélemy"; }
enum "BM" { description "Bermuda"; }
enum "BN" { description "Brunei Darussalam"; }
enum "BO" { description "Bolivia"; }
enum "BQ" { description "Bonaire, Sint Eustatius and Saba"; }
enum "BR" { description "Brazil"; }
enum "BS" { description "Bahamas"; }
enum "BT" { description "Bhutan"; }
enum "BV" { description "Bouvet Island"; }
enum "BW" { description "Botswana"; }
enum "BY" { description "Belarus"; }
enum "BZ" { description "Belize"; }
enum "CA" { description "Canada"; }
enum "CC" { description "Cocos (Keeling) Islands"; }
enum "CD" { description "Congo, Democratic Republic of the"; }
enum "CF" { description "Central African Republic"; }
enum "CG" { description "Congo"; }
enum "CH" { description "Switzerland"; }
enum "CI" { description "Côte d'Ivoire"; }
enum "CK" { description "Cook Islands"; }
enum "CL" { description "Chile"; }
enum "CM" { description "Cameroon"; }
enum "CN" { description "China"; }
enum "CO" { description "Colombia"; }
enum "CR" { description "Costa Rica"; }
enum "CU" { description "Cuba"; }
enum "CV" { description "Cabo Verde"; }
enum "CW" { description "Curaçao"; }
enum "CX" { description "Christmas Island"; }
enum "CY" { description "Cyprus"; }
enum "CZ" { description "Czechia"; }
enum "DE" { description "Germany"; }
enum "DJ" { description "Djibouti"; }
enum "DK" { description "Denmark"; }
enum "DM" { description "Dominica"; }
enum "DO" { description "Dominican Republic"; }
enum "DZ" { description "Algeria"; }
enum "EC" { description "Ecuador"; }
enum "EE" { description "Estonia"; }
enum "EG" { description "Egypt"; }
enum "EH" { description "Western Sahara"; }
enum "ER" { description "Eritrea"; }
enum "ES" { description "Spain"; }
enum "ET" { description "Ethiopia"; }
enum "FI" { description "Finland"; }
enum "FJ" { description "Fiji"; }
enum "FK" { description "Falkland Islands (Malvinas)"; }
enum "FM" { description "Micronesia"; }
enum "FO" { description "Faroe Islands"; }
enum "FR" { description "France"; }
enum "GA" { description "Gabon"; }
enum "GB" { description "United Kingdom"; }
enum "GD" { description "Grenada"; }
enum "GE" { description "Georgia"; }
enum "GF" { description "French Guiana"; }
enum "GG" { description "Guernsey"; }
enum "GH" { description "Ghana"; }
enum "GI" { description "Gibraltar"; }
enum "GL" { description "Greenland"; }
enum "GM" { description "Gambia"; }
enum "GN" { description "Guinea"; }
enum "GP" { description "Guadeloupe"; }
enum "GQ" { description "Equatorial Guinea"; }
enum "GR" { description "Greece"; }
enum "GS" { description "South Georgia and the South Sandwich Islands"; }
enum "GT" { description "Guatemala"; }
enum "GU" { description "Guam"; }
enum "GW" { description "Guinea-Bissau"; }
enum "GY" { description "Guyana"; }
enum "HK" { description "Hong Kong"; }
enum "HM" { description "Heard Island and McDonald Islands"; }
enum "HN" { description "Honduras"; }
enum "HR" { description "Croatia"; }
enum "HT" { description "Haiti"; }
enum "HU" { description "Hungary"; }
enum "ID" { description "Indonesia"; }
enum "IE" { description "Ireland"; }
enum "IL" { description "Israel"; }
enum "IM" { description "Isle of Man"; }
enum "IN" { description "India"; }
enum "IO" { description "British Indian Ocean Territory"; }
enum "IQ" { description "Iraq"; }
enum "IR" { description "Iran"; }
enum "IS" { description "Iceland"; }
enum "IT" { description "Italy"; }
enum "JE" { description "Jersey"; }
enum "JM" { description "Jamaica"; }
enum "JO" { description "Jordan"; }
enum "JP" { description "Japan"; }
enum "KE" { description "Kenya"; }
enum "KG" { description "Kyrgyzstan"; }
enum "KH" { description "Cambodia"; }
enum "KI" { description "Kiribati"; }
enum "KM" { description "Comoros"; }
enum "KN" { description "Saint Kitts and Nevis"; }
enum "KP" { description "Korea, Democratic People's Republic of"; }
enum "KR" { description "Korea, Republic of"; }
enum "KW" { description "Kuwait"; }
enum "KY" { description "Cayman Islands"; }
enum "KZ" { description "Kazakhstan"; }
enum "LA" { description "Lao People's Democratic Republic"; }
enum "LB" { description "Lebanon"; }
enum "LC" { description "Saint Lucia"; }
enum "LI" { description "Liechtenstein"; }
enum "LK" { description "Sri Lanka"; }
enum "LR" { description "Liberia"; }
enum "LS" { description "Lesotho"; }
enum "LT" { description "Lithuania"; }
enum "LU" { description "Luxembourg"; }
enum "LV" { description "Latvia"; }
enum "LY" { description "Libya"; }
enum "MA" { description "Morocco"; }
enum "MC" { description "Monaco"; }
enum "MD" { description "Moldova"; }
enum "ME" { description "Montenegro"; }
enum "MF" { description "Saint Martin (French part)"; }
enum "MG" { description "Madagascar"; }
enum "MH" { description "Marshall Islands"; }
enum "MK" { description "North Macedonia"; }
enum "ML" { description "Mali"; }
enum "MM" { description "Myanmar"; }
enum "MN" { description "Mongolia"; }
enum "MO" { description "Macao"; }
enum "MP" { description "Northern Mariana Islands"; }
enum "MQ" { description "Martinique"; }
enum "MR" { description "Mauritania"; }
enum "MS" { description "Montserrat"; }
enum "MT" { description "Malta"; }
enum "MU" { description "Mauritius"; }
enum "MV" { description "Maldives"; }
enum "MW" { description "Malawi"; }
enum "MX" { description "Mexico"; }
enum "MY" { description "Malaysia"; }
enum "MZ" { description "Mozambique"; }
enum "NA" { description "Namibia"; }
enum "NC" { description "New Caledonia"; }
enum "NE" { description "Niger"; }
enum "NF" { description "Norfolk Island"; }
enum "NG" { description "Nigeria"; }
enum "NI" { description "Nicaragua"; }
enum "NL" { description "Netherlands"; }
enum "NO" { description "Norway"; }
enum "NP" { description "Nepal"; }
enum "NR" { description "Nauru"; }
enum "NU" { description "Niue"; }
enum "NZ" { description "New Zealand"; }
enum "OM" { description "Oman"; }
enum "PA" { description "Panama"; }
enum "PE" { description "Peru"; }
enum "PF" { description "French Polynesia"; }
enum "PG" { description "Papua New Guinea"; }
enum "PH" { description "Philippines"; }
enum "PK" { description "Pakistan"; }
enum "PL" { description "Poland"; }
enum "PM" { description "Saint Pierre and Miquelon"; }
enum "PN" { description "Pitcairn"; }
enum "PR" { description "Puerto Rico"; }
enum "PS" { description "Palestine, State of"; }
enum "PT" { description "Portugal"; }
enum "PW" { description "Palau"; }
enum "PY" { description "Paraguay"; }
enum "QA" { description "Qatar"; }
enum "RE" { description "Réunion"; }
enum "RO" { description "Romania"; }
enum "RS" { description "Serbia"; }
enum "RU" { description "Russian Federation"; }
enum "RW" { description "Rwanda"; }
enum "SA" { description "Saudi Arabia"; }
enum "SB" { description "Solomon Islands"; }
enum "SC" { description "Seychelles"; }
enum "SD" { description "Sudan"; }
enum "SE" { description "Sweden"; }
enum "SG" { description "Singapore"; }
enum "SH" { description "Saint Helena, Ascension and Tristan da Cunha"; }
enum "SI" { description "Slovenia"; }
enum "SJ" { description "Svalbard and Jan Mayen"; }
enum "SK" { description "Slovakia"; }
enum "SL" { description "Sierra Leone"; }
enum "SM" { description "San Marino"; }
enum "SN" { description "Senegal"; }
enum "SO" { description "Somalia"; }
enum "SR" { description "Suriname"; }
enum "SS" { description "South Sudan"; }
enum "ST" { description "Sao Tome and Principe"; }
enum "SV" { description "El Salvador"; }
enum "SX" { description "Sint Maarten (Dutch part)"; }
enum "SY" { description "Syrian Arab Republic"; }
enum "SZ" { description "Eswatini"; }
enum "TC" { description "Turks and Caicos Islands"; }
enum "TD" { description "Chad"; }
enum "TF" { description "French Southern Territories"; }
enum "TG" { description "Togo"; }
enum "TH" { description "Thailand"; }
enum "TJ" { description "Tajikistan"; }
enum "TK" { description "Tokelau"; }
enum "TL" { description "Timor-Leste"; }
enum "TM" { description "Turkmenistan"; }
enum "TN" { description "Tunisia"; }
enum "TO" { description "Tonga"; }
enum "TR" { description "Turkey"; }
enum "TT" { description "Trinidad and Tobago"; }
enum "TV" { description "Tuvalu"; }
enum "TW" { description "Taiwan"; }
enum "TZ" { description "Tanzania"; }
enum "UA" { description "Ukraine"; }
enum "UG" { description "Uganda"; }
enum "UM" { description "United States Minor Outlying Islands"; }
enum "US" { description "United States of America"; }
enum "UY" { description "Uruguay"; }
enum "UZ" { description "Uzbekistan"; }
enum "VA" { description "Holy See (Vatican City State)"; }
enum "VC" { description "Saint Vincent and the Grenadines"; }
enum "VE" { description "Venezuela"; }
enum "VG" { description "Virgin Islands, British"; }
enum "VI" { description "Virgin Islands, U.S."; }
enum "VN" { description "Viet Nam"; }
enum "VU" { description "Vanuatu"; }
enum "WF" { description "Wallis and Futuna"; }
enum "WS" { description "Samoa"; }
enum "YE" { description "Yemen"; }
enum "YT" { description "Mayotte"; }
enum "ZA" { description "South Africa"; }
enum "ZM" { description "Zambia"; }
enum "ZW" { description "Zimbabwe"; }
}
description
"Complete list of ISO 3166-1 alpha-2 country codes for
regulatory domain configuration.";
reference
"ISO 3166-1:2020 Codes for the representation of names of countries
and their subdivisions -- Part 1: Country codes";
}
}
@@ -0,0 +1 @@
infix-wifi-country-codes.yang
+1 -2
View File
@@ -1,6 +1,5 @@
# -*- sh -*-
# REMEMBER TO UPDATE infix-interfaces ALSO IN confd.inc
MODULES=(
"infix-interfaces@2025-01-09.yang -e vlan-filtering -e containers"
"infix-interfaces -e containers"
"infix-containers@2025-05-14.yang"
)
+4
View File
@@ -0,0 +1,4 @@
MODULES=(
"infix-interfaces -e wifi"
"infix-if-type -e wifi"
)
+15
View File
@@ -164,6 +164,20 @@ def lldp(args: List[str]):
return
cli_pretty(data, "show-lldp")
def wifi(args: List[str]):
iface = args[0]
if len(args) == 0:
print("Illigal usage")
return
if is_valid_interface_name(iface):
if not os.path.exists(f"/sys/class/net/{iface}/wireless"):
print("Not a Wi-Fi interface")
return
data = run_sysrepocfg("/ietf-interfaces:interfaces")
cli_pretty(data, "show-wifi-scan", "-n", iface)
else:
print(f"Invalid interface name: {iface}")
def execute_command(command: str, args: List[str]):
command_mapping = {
'dhcp': dhcp,
@@ -174,6 +188,7 @@ def execute_command(command: str, args: List[str]):
'lldp': lldp,
'software' : software,
'stp': stp,
'wifi': wifi
}
if command in command_mapping:
+96 -10
View File
@@ -82,6 +82,20 @@ class PadNtpSource:
poll = 14
class PadWifiScan:
ssid = 40
encryption = 30
signal = 9
class PadLldp:
interface = 16
rem_idx = 10
time = 12
chassis_id = 20
port_id = 20
class Decore():
@staticmethod
def decorate(sgr, txt, restore="0"):
@@ -103,6 +117,10 @@ class Decore():
def green(txt):
return Decore.decorate("32", txt, "39")
@staticmethod
def bright_green(txt):
return Decore.decorate("1;32", txt, "39")
@staticmethod
def yellow(txt):
return Decore.decorate("33", txt, "39")
@@ -116,12 +134,17 @@ class Decore():
return Decore.decorate("100", txt)
class PadLldp:
interface = 16
rem_idx = 10
time = 12
chassis_id = 20
port_id = 20
def rssi_to_status(rssi):
if rssi <= -75:
status = Decore.bright_green("excellent")
elif rssi <= -65:
status = Decore.green("good")
elif rssi <= -50:
status = Decore.yellow("poor")
else:
status = Decore.red("bad")
return status
def datetime_now():
@@ -196,7 +219,6 @@ class Date(datetime):
tz = tz.replace(":", "")
return cls.strptime(f"{date}+{tz}", "%Y-%m-%dT%H:%M:%S%z")
class Route:
def __init__(self, data, ip):
self.data = data
@@ -524,12 +546,16 @@ class Iface:
self.gre = self.data.get('infix-interfaces:gre')
self.vxlan = self.data.get('infix-interfaces:vxlan')
self.wifi = self.data.get('infix-interfaces:wifi')
if self.data.get('infix-interfaces:vlan'):
self.lower_if = self.data.get('infix-interfaces:vlan', None).get('lower-layer-if',None)
else:
self.lower_if = ''
def is_wifi(self):
return self.type == "infix-if-type:wifi"
def is_vlan(self):
return self.type == "infix-if-type:vlan"
@@ -554,7 +580,7 @@ class Iface:
def is_gretap(self):
return self.data['type'] == "infix-if-type:gretap"
def oper(self, detail=False):
"""Remap in brief overview to fit column widths."""
if not detail and self.oper_status == "lower-layer-down":
@@ -625,6 +651,47 @@ class Iface:
row = self._pr_proto_common("loopback", False, pipe);
print(row)
def pr_wifi_ssids(self):
hdr = (f"{'SSID':<{PadWifiScan.ssid}}"
f"{'ENCRYPTION':<{PadWifiScan.encryption}}"
f"{'SIGNAL':<{PadWifiScan.signal}}"
)
print(Decore.invert(hdr))
results=self.wifi.get("scan-results", {})
for result in results:
encstr = ",".join(result["encryption"])
status=rssi_to_status(result["rssi"])
row = f"{result['ssid']:<{PadWifiScan.ssid}}"
row += f"{encstr:<{PadWifiScan.encryption}}"
row += f"{status:<{PadWifiScan.signal}}"
print(row)
def pr_proto_wifi(self, pipe=''):
row = self._pr_proto_common("ethernet", True, pipe);
print(row)
ssid = None
rssi = None
if self.wifi:
rssi=self.wifi.get("rssi")
ssid=self.wifi.get("ssid")
if ssid is None:
ssid="------"
if rssi is None:
signal="------"
else:
signal=rssi_to_status(rssi)
data_str = f"ssid: {ssid}, signal: {signal}"
row = f"{'':<{Pad.iface}}"
row += f"{'wifi':<{Pad.proto}}"
row += f"{'':<{Pad.state}}{data_str}"
print(row)
def pr_proto_br(self, br_vlans):
data_str = ""
@@ -765,6 +832,12 @@ class Iface:
self.pr_proto_ipv4()
self.pr_proto_ipv6()
def pr_wifi(self):
self.pr_name(pipe="")
self.pr_proto_wifi()
self.pr_proto_ipv4()
self.pr_proto_ipv6()
def pr_vlan(self, _ifaces):
self.pr_name(pipe="")
self.pr_proto_eth()
@@ -874,6 +947,14 @@ class Iface:
else:
print(f"{'ipv6 addresses':<{20}}:")
if self.wifi:
ssid=self.wifi.get('ssid', "----")
rssi=self.wifi.get('rssi', "----")
print(f"{'SSID':<{20}}: {ssid}")
print(f"{'Signal':<{20}}: {rssi}")
print("")
self.pr_wifi_ssids()
if self.gre:
print(f"{'local address':<{20}}: {self.gre['local']}")
print(f"{'remote address':<{20}}: {self.gre['remote']}")
@@ -1045,6 +1126,10 @@ def pr_interface_list(json):
iface.pr_vxlan()
continue
if iface.is_wifi():
iface.pr_wifi()
continue
if iface.is_vlan():
iface.pr_vlan(ifaces)
continue
@@ -1320,13 +1405,13 @@ def show_lldp(json):
f"{'CHASSIS-ID':<{PadLldp.chassis_id}}"
f"{'PORT-ID':<{PadLldp.port_id}}"
)
print(Decore.invert(header))
for port_data in lldp_ports:
port_name = port_data["name"]
neighbors = port_data.get("remote-systems-data", [])
for neighbor in neighbors:
entry = LldpNeighbor(port_name, neighbor)
entry.print()
@@ -1394,6 +1479,7 @@ def main():
show_routing_table(json_data, args.ip)
elif args.command == "show-software":
show_software(json_data, args.name)
else:
print(f"Error, unknown command '{args.command}'")
sys.exit(1)
@@ -1,3 +1,4 @@
from ..host import HOST
from . import common
from . import bridge
@@ -7,6 +8,7 @@ from . import lag
from . import tun
from . import veth
from . import vlan
from . import wifi
def statistics(iplink):
@@ -24,13 +26,16 @@ def statistics(iplink):
def iplink2yang_type(iplink):
ifname=iplink["ifname"]
match iplink["link_type"]:
case "loopback":
return "infix-if-type:loopback"
case "gre"|"gre6":
return "infix-if-type:gre"
case "ether":
pass
data = HOST.run(tuple(f"ls /sys/class/net/{ifname}/wireless/".split()), default="no")
if data != "no":
return "infix-if-type:wifi"
case _:
return "infix-if-type:other"
@@ -133,6 +138,9 @@ def interface(iplink, ipaddr):
case "infix-if-type:vlan":
if v := vlan.vlan(iplink):
interface["infix-interfaces:vlan"] = v
case "infix-if-type:wifi":
if w := wifi.wifi(iplink["ifname"]):
interface["infix-interfaces:wifi"] = w
match iplink2yang_lower(iplink):
case "infix-interfaces:bridge-port":
@@ -0,0 +1,160 @@
from ..host import HOST
import json
import re
def wifi(ifname):
data=HOST.run(tuple(f"wpa_cli -i {ifname} status".split()), default="")
wifi_data={}
if data != "":
for line in data.splitlines():
k,v = line.split("=")
if k == "ssid":
wifi_data["ssid"] = v
if k == "wpa_state" and v == "DISCONNECTED": # wpa_suppicant has most likely restarted, restart scanning
HOST.run(tuple(f"wpa_cli -i {ifname} scan".split()), default="")
data=HOST.run(tuple(f"wpa_cli -i {ifname} signal_poll".split()), default="FAIL")
# signal_poll return FAIL not connected
if data.strip() != "FAIL":
for line in data.splitlines():
k,v = line.strip().split("=")
if k == "RSSI":
wifi_data["rssi"]=int(v)
data=HOST.run(tuple(f"wpa_cli -i {ifname} scan_result".split()), default="FAIL")
if data != "FAIL":
wifi_data["scan-results"] = parse_wpa_scan_result(data)
return wifi_data
def parse_wpa_scan_result(scan_output):
networks = {}
lines = scan_output.strip().split('\n')
# Skip header line and any empty lines
for line in lines:
line = line.strip()
if not line or 'bssid / frequency' in line.lower():
continue
# Split by tabs or multiple spaces
parts = re.split(r'\t+|\s{2,}', line)
if len(parts) >= 5:
bssid = parts[0].strip()
frequency = int(parts[1].strip())
rssi = int(parts[2].strip())
flags = parts[3].strip()
ssid = parts[4].strip() if len(parts) > 4 else ""
# Skip hidden SSIDs (empty or whitespace only)
if not ssid or ssid.isspace() or '\\x00' in ssid:
continue
# Extract encryption information from flags
encryption = extract_encryption(flags)
# Convert frequency to channel
channel = frequency_to_channel(frequency)
# Keep only the network with best (highest) RSSI per SSID
if ssid not in networks or rssi < networks[ssid]['rssi']:
networks[ssid] = {
'bssid': bssid,
'ssid': ssid,
'rssi': rssi,
'encryption': encryption,
'channel': channel
}
# Convert to list and sort by RSSI (best first)
result = list(networks.values())
result.sort(key=lambda x: x['rssi'], reverse=False)
return result
def frequency_to_channel(frequency):
"""Convert frequency (MHz) to WiFi channel number"""
freq = int(frequency)
# 2.4 GHz band (channels 1-14)
if 2412 <= freq <= 2484: # Channel 14 is special
if freq == 2484:
return 14
return (freq - 2412) // 5 + 1
# 5 GHz band (channels 36-165)
elif 5170 <= freq <= 5825:
return (freq - 5000) // 5
# 6 GHz band (channels 1-233)
elif 5955 <= freq <= 7115:
return (freq - 5950) // 5
else:
return f"Unknown ({freq} MHz)"
def extract_encryption(flags):
"""Extract detailed encryption information from flags string"""
flags = flags.upper()
encryption_info = {
'protocols': [],
'key_mgmt': [],
'ciphers': [],
'auth_type': 'Unknown'
}
# Extract WPA protocols
if 'WPA3' in flags:
encryption_info['protocols'].append('WPA3')
if 'WPA2' in flags:
encryption_info['protocols'].append('WPA2')
if 'WPA-' in flags and 'WPA2' not in flags and 'WPA3' not in flags:
encryption_info['protocols'].append('WPA')
# Extract key management methods
if 'PSK' in flags:
encryption_info['key_mgmt'].append('PSK')
encryption_info['auth_type'] = 'Personal'
if 'EAP' in flags:
encryption_info['key_mgmt'].append('EAP')
encryption_info['auth_type'] = 'Enterprise'
if 'SAE' in flags: # WPA3 Personal
encryption_info['key_mgmt'].append('SAE')
encryption_info['auth_type'] = 'Personal'
if 'OWE' in flags: # Enhanced Open (WPA3)
encryption_info['key_mgmt'].append('OWE')
encryption_info['auth_type'] = 'Enhanced Open'
if 'FT' in flags:
encryption_info['key_mgmt'].append('FT')
# Extract cipher suites
if 'CCMP' in flags:
encryption_info['ciphers'].append('CCMP')
if 'TKIP' in flags:
encryption_info['ciphers'].append('TKIP')
if 'GCMP' in flags:
encryption_info['ciphers'].append('GCMP')
# Handle special cases
if 'WEP' in flags:
return ['WEP']
if not encryption_info['protocols'] and 'ESS' in flags:
return ['Open']
# Return array of supported protocols with auth type
result = []
for protocol in encryption_info['protocols']:
if encryption_info['auth_type'] == 'Enterprise':
result.append(f"{protocol}-Enterprise")
elif encryption_info['auth_type'] == 'Personal':
result.append(f"{protocol}-Personal")
elif encryption_info['auth_type'] == 'Enhanced Open':
result.append(f"{protocol}-Enhanced-Open")
else:
result.append(protocol)
return result if result else ['Unknown']
-1
View File
@@ -90,7 +90,6 @@ with infamy.Test() as test:
"interface": [
{
"name": server_link,
"type": "infix-if-type:bridge",
"enabled": True,
"ipv4": {
"address": [
+13 -6
View File
@@ -82,7 +82,10 @@ chperm()
enable()
{
$SYSREPOCTL -c "$1" -e "$2" -v2
local module=$1
local feature=$2
echo "*** Enable feature $feature in $module."
$SYSREPOCTL -c $module -e $feature -v2
local rc=$?
if [ $rc -ne 0 ]; then
exit $rc
@@ -93,7 +96,7 @@ enable()
SCTL_MODULES=$($SYSREPOCTL -l |tail -n +5 |head -n -3)
for module in "${MODULES[@]}"; do
name=$(echo "$module" | awk -F'[@.]' '{print $1}')
name=$(echo "$module" | awk '{match($1, /[^@.]+/); print substr($1, RSTART, RLENGTH)}')
date=$(echo "$module" | awk -F'[@.]' '{print $2}')
for dir in $SEARCH_PATH; do
if [ -n "${date}" ]; then
@@ -105,10 +108,15 @@ for module in "${MODULES[@]}"; do
MODDIR="${dir}"
fi
done
[ -z "${MODDIR}" ] && echo "Could not find ${yang} in ${SEARCH_PATH}" && exit 1
# If no .yang we just modfify an existing model
if [[ "$module" == *.yang* && -z "$MODDIR" ]]; then
echo "Could not find ${yang} in ${SEARCH_PATH}"
exit 1
fi
SCTL_MODULE=$(echo "$SCTL_MODULES" | grep "^$name \+|[^|]*| I")
if [ -z "$SCTL_MODULE" ]; then
# prepare command to install module with all its features
echo "*** Installing YANG model $name ..."
@@ -117,7 +125,7 @@ for module in "${MODULES[@]}"; do
fi
rev=$(echo "$SCTL_MODULE" | awk '{print $3}')
if [ "$rev" != "$date" ] || echo "$FORCE_UPDATE" | grep -qw "$name"; then
if [ -n "$date" ] && [ "$rev" != "$date" ] || echo "$FORCE_UPDATE" | grep -qw "$name"; then
# update module without any features
file=$(echo "$module" | cut -d' ' -f 1)
echo "*** Updating YANG model $name ($file) ..."
@@ -133,8 +141,7 @@ for module in "${MODULES[@]}"; do
chperm "$name"
fi
# parse sysrepoctl features and add extra space at the end for easier matching
sctl_features="`echo "$SCTL_MODULE" | sed 's/\([^|]*|\)\{6\}\(.*\)/\2/'` "
sctl_features="`echo "$SCTL_MODULE" | awk -F '|' '{print $NF}' | xargs`"
# parse features we want to enable
features=`echo "$module" | sed 's/[^ ]* \(.*\)/\1/'`
while [ "${features:0:3}" = "-e " ]; do