mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-24 09:43:02 +02:00
@@ -12,10 +12,6 @@ on:
|
||||
- bananapi-bpi-r3
|
||||
- friendlyarm-nanopi-r2s
|
||||
default: 'raspberrypi-rpi64'
|
||||
use_latest_release:
|
||||
description: 'Use latest release artifacts instead of workflow artifacts'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
create-image:
|
||||
@@ -54,19 +50,19 @@ jobs:
|
||||
run: |
|
||||
case "${{ inputs.board }}" in
|
||||
raspberrypi-rpi64)
|
||||
echo "BOOTLOADER=rpi64_boot" >> $GITHUB_ENV
|
||||
echo "TARGET=aarch64" >> $GITHUB_ENV
|
||||
echo "BOOTLOADER=rpi64-boot" >> $GITHUB_ENV
|
||||
echo "ARCH=aarch64" >> $GITHUB_ENV
|
||||
echo "BUILD_EMMC=false" >> $GITHUB_ENV
|
||||
;;
|
||||
bananapi-bpi-r3)
|
||||
echo "BOOTLOADER_SD=bpi_r3_sd_boot" >> $GITHUB_ENV
|
||||
echo "BOOTLOADER_EMMC=bpi_r3_emmc_boot" >> $GITHUB_ENV
|
||||
echo "TARGET=aarch64" >> $GITHUB_ENV
|
||||
echo "BOOTLOADER_SD=bpi-r3-sd-boot" >> $GITHUB_ENV
|
||||
echo "BOOTLOADER_EMMC=bpi-r3-emmc_boot" >> $GITHUB_ENV
|
||||
echo "ARCH=aarch64" >> $GITHUB_ENV
|
||||
echo "BUILD_EMMC=true" >> $GITHUB_ENV
|
||||
;;
|
||||
friendlyarm-nanopi-r2s)
|
||||
echo "BOOTLOADER=nanopi_r2s_boot" >> $GITHUB_ENV
|
||||
echo "TARGET=aarch64" >> $GITHUB_ENV
|
||||
echo "BOOTLOADER=nanopi-r2s-boot" >> $GITHUB_ENV
|
||||
echo "ARCH=aarch64" >> $GITHUB_ENV
|
||||
echo "BUILD_EMMC=false" >> $GITHUB_ENV
|
||||
;;
|
||||
*)
|
||||
@@ -74,7 +70,7 @@ jobs:
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
echo "Target: $TARGET for board: ${{ inputs.board }}"
|
||||
echo "Arch: $ARCH for board: ${{ inputs.board }}"
|
||||
if [ "$BUILD_EMMC" = "true" ]; then
|
||||
echo "Building both SD and eMMC images"
|
||||
echo "SD Bootloader: $BOOTLOADER_SD"
|
||||
@@ -85,16 +81,12 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Download bootloader artifacts
|
||||
if: ${{ !inputs.use_latest_release }}
|
||||
run: |
|
||||
# Download from latest bootloader build workflow on main branch
|
||||
gh run list --workflow=build-boot.yml --branch=main --limit=1 --status=success --json databaseId --jq '.[0].databaseId' > latest_boot_run_id
|
||||
BOOT_RUN_ID=$(cat latest_boot_run_id)
|
||||
|
||||
# Download bootloader from latest-boot release tag
|
||||
if [ "$BUILD_EMMC" = "true" ]; then
|
||||
# Download both SD and eMMC bootloaders for boards that support both
|
||||
echo "Downloading SD bootloader: ${BOOTLOADER_SD}"
|
||||
gh run download ${BOOT_RUN_ID} --name artifact-${BOOTLOADER_SD} --dir temp_bootloader_sd/
|
||||
gh release download latest-boot --pattern "*${BOOTLOADER_SD}*" --dir temp_bootloader_sd/
|
||||
mkdir -p output_sd/images
|
||||
cd temp_bootloader_sd/
|
||||
tar -xzf *.tar.gz --strip-components=1 -C ../output_sd/images/
|
||||
@@ -102,7 +94,7 @@ jobs:
|
||||
rm -rf temp_bootloader_sd/
|
||||
|
||||
echo "Downloading eMMC bootloader: ${BOOTLOADER_EMMC}"
|
||||
gh run download ${BOOT_RUN_ID} --name artifact-${BOOTLOADER_EMMC} --dir temp_bootloader_emmc/
|
||||
gh release download latest-boot --pattern "*${BOOTLOADER_EMMC}*" --dir temp_bootloader_emmc/
|
||||
mkdir -p output_emmc/images
|
||||
cd temp_bootloader_emmc/
|
||||
tar -xzf *.tar.gz --strip-components=1 -C ../output_emmc/images/
|
||||
@@ -115,7 +107,7 @@ jobs:
|
||||
ls -la output_emmc/images/
|
||||
else
|
||||
# Single bootloader for boards that only support SD
|
||||
gh run download ${BOOT_RUN_ID} --name artifact-${BOOTLOADER} --dir temp_bootloader/
|
||||
gh release download latest-boot --pattern "*${BOOTLOADER}*" --dir temp_bootloader/
|
||||
|
||||
# Extract bootloader directly to output/images
|
||||
cd temp_bootloader/
|
||||
@@ -130,7 +122,6 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Download Infix artifacts
|
||||
if: ${{ !inputs.use_latest_release }}
|
||||
run: |
|
||||
# Download from latest Kernelkit Trigger workflow for main branch
|
||||
gh run list --workflow=164295764 --branch=main --limit=1 --status=success --json databaseId --jq '.[0].databaseId' > latest_infix_run_id
|
||||
@@ -138,7 +129,7 @@ jobs:
|
||||
|
||||
if [ "$BUILD_EMMC" = "true" ]; then
|
||||
# Copy Infix artifacts to both SD and eMMC output directories
|
||||
gh run download ${INFIX_RUN_ID} --name artifact-${TARGET} --dir temp_infix/
|
||||
gh run download ${INFIX_RUN_ID} --name artifact-${ARCH} --dir temp_infix/
|
||||
|
||||
cd temp_infix/
|
||||
tar -xzf *.tar.gz --strip-components=1 -C ../output_sd/images/
|
||||
@@ -152,7 +143,7 @@ jobs:
|
||||
ls -la output_emmc/images/
|
||||
else
|
||||
# Single output directory for SD-only boards
|
||||
gh run download ${INFIX_RUN_ID} --name artifact-${TARGET} --dir temp_infix/
|
||||
gh run download ${INFIX_RUN_ID} --name artifact-${ARCH} --dir temp_infix/
|
||||
|
||||
# Extract Infix directly to output/images
|
||||
cd temp_infix/
|
||||
@@ -166,58 +157,6 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Download from latest releases
|
||||
if: ${{ inputs.use_latest_release }}
|
||||
run: |
|
||||
if [ "$BUILD_EMMC" = "true" ]; then
|
||||
# Download both SD and eMMC bootloaders
|
||||
gh release download latest-boot --pattern "*${BOOTLOADER_SD}*" --dir temp_bootloader_sd/
|
||||
mkdir -p output_sd/images
|
||||
cd temp_bootloader_sd/
|
||||
tar -xzf *.tar.gz --strip-components=1 -C ../output_sd/images/
|
||||
cd ../
|
||||
rm -rf temp_bootloader_sd/
|
||||
|
||||
gh release download latest-boot --pattern "*${BOOTLOADER_EMMC}*" --dir temp_bootloader_emmc/
|
||||
mkdir -p output_emmc/images
|
||||
cd temp_bootloader_emmc/
|
||||
tar -xzf *.tar.gz --strip-components=1 -C ../output_emmc/images/
|
||||
cd ../
|
||||
rm -rf temp_bootloader_emmc/
|
||||
|
||||
# Download Infix once and extract to both directories
|
||||
gh release download latest --pattern "*${TARGET}*" --dir temp_infix/
|
||||
cd temp_infix/
|
||||
tar -xzf *.tar.gz --strip-components=1 -C ../output_sd/images/
|
||||
tar -xzf *.tar.gz --strip-components=1 -C ../output_emmc/images/
|
||||
cd ../
|
||||
rm -rf temp_infix/
|
||||
|
||||
echo "SD files extracted to output_sd/images:"
|
||||
ls -la output_sd/images/
|
||||
echo "eMMC files extracted to output_emmc/images:"
|
||||
ls -la output_emmc/images/
|
||||
else
|
||||
# Download latest bootloader release
|
||||
gh release download latest-boot --pattern "*${BOOTLOADER}*" --dir temp_bootloader/
|
||||
cd temp_bootloader/
|
||||
tar -xzf *.tar.gz --strip-components=1 -C ../output/images/
|
||||
cd ../
|
||||
rm -rf temp_bootloader/
|
||||
|
||||
# Download latest Infix release
|
||||
gh release download latest --pattern "*${TARGET}*" --dir temp_infix/
|
||||
cd temp_infix/
|
||||
tar -xzf *.tar.gz --strip-components=1 -C ../output/images/
|
||||
cd ../
|
||||
rm -rf temp_infix/
|
||||
|
||||
echo "All files extracted to output/images:"
|
||||
ls -la output/images/
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Verify extracted files
|
||||
run: |
|
||||
if [ "$BUILD_EMMC" = "true" ]; then
|
||||
@@ -249,7 +188,7 @@ jobs:
|
||||
export BR2_EXTERNAL_INFIX_PATH=$PWD
|
||||
export RELEASE=""
|
||||
export INFIX_ID="infix"
|
||||
./utils/mkimage.sh ${{ inputs.board }}
|
||||
./utils/mkimage.sh -t sdcard ${{ inputs.board }}
|
||||
fi
|
||||
|
||||
- name: Create eMMC image
|
||||
@@ -347,10 +286,11 @@ jobs:
|
||||
# SD Card & eMMC Image Build Complete! 🚀
|
||||
|
||||
**Board:** ${{ inputs.board }}
|
||||
**Target:** ${{ env.TARGET }}
|
||||
**Arch:** ${{ env.ARCH }}
|
||||
**SD Bootloader:** ${{ env.BOOTLOADER_SD }}
|
||||
**eMMC Bootloader:** ${{ env.BOOTLOADER_EMMC }}
|
||||
**Artifact Source:** ${{ inputs.use_latest_release && 'Latest Release' || 'Latest Workflow Run' }}
|
||||
**Bootloader Source:** latest-boot release
|
||||
**Infix Source:** Latest workflow run on main
|
||||
|
||||
## Created Images
|
||||
$(find output/images/ -name "*.img" -o -name "*.img.bmap" | xargs ls -lh 2>/dev/null | awk '{print "- " $9 " (" $5 ")"}' || echo "- No images found")
|
||||
@@ -363,9 +303,10 @@ jobs:
|
||||
# SD Card Image Build Complete! 🚀
|
||||
|
||||
**Board:** ${{ inputs.board }}
|
||||
**Target:** ${{ env.TARGET }}
|
||||
**Arch:** ${{ env.ARCH }}
|
||||
**Bootloader:** ${{ env.BOOTLOADER }}
|
||||
**Artifact Source:** ${{ inputs.use_latest_release && 'Latest Release' || 'Latest Workflow Run' }}
|
||||
**Bootloader Source:** latest-boot release
|
||||
**Infix Source:** Latest workflow run on main
|
||||
|
||||
## Created Images
|
||||
$(find output/images/ -name "*.img" -o -name "*.img.bmap" | xargs ls -lh 2>/dev/null | awk '{print "- " $9 " (" $5 ")"}' || echo "- No images found")
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Rename WiFi PHY devices from phy0 to radio0 to avoid name clashes
|
||||
SUBSYSTEM=="ieee80211", ACTION=="add", KERNEL=="phy*", \
|
||||
RUN+="/usr/libexec/infix/rename-wifi-phy %k"
|
||||
RUN+="/bin/sh -c '/usr/sbin/iw phy %k set name radio%n'"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Rename WiFi PHY to radio{N}, enumerating from 0
|
||||
phy="$1"
|
||||
n=0
|
||||
while [ -d "/sys/class/ieee80211/radio$n" ]; do
|
||||
n=$((n + 1))
|
||||
done
|
||||
/usr/sbin/iw phy "$phy" set name "radio$n"
|
||||
@@ -72,10 +72,12 @@ Noteworthy changes and additions in this release are marked below in bold text.
|
||||
- Fix default password hash in `do password encrypt` command. New hash is the
|
||||
same as the more commonly used `change password` command, *yescrypt*
|
||||
- Prevent MOTD from showing on non-shell user login attempts
|
||||
- Fix mDNS reflector.
|
||||
|
||||
[wifi]: https://kernelkit.org/infix/latest/wifi/
|
||||
[sd card image]: https://github.com/kernelkit/infix/releases/download/latest-boot/infix-rpi64-sdcard.img
|
||||
|
||||
|
||||
[v25.11.0][] - 2025-12-02
|
||||
-------------------------
|
||||
|
||||
|
||||
+3
-3
@@ -86,7 +86,7 @@ admin@example:/config/hardware/component/radio0/wifi-radio/> leave
|
||||
- `country-code`: Two-letter ISO 3166-1 code - determines allowed channels and maximum power. Examples: US, DE, GB, SE, FR, JP. **Must match your physical location for legal compliance.**
|
||||
- `band`: 2.4GHz, 5GHz, or 6GHz (required for AP mode). Band selection automatically enables appropriate WiFi standards (2.4GHz: 802.11n, 5GHz: 802.11n/ac, 6GHz: 802.11n/ac/ax)
|
||||
- `channel`: Channel number (1-196) or "auto" (required for AP mode). When set to "auto", defaults to channel 6 for 2.4GHz, channel 36 for 5GHz, or channel 109 for 6GHz
|
||||
- `enable-wifi6`: Boolean (default: false). Opt-in to enable WiFi 6 (802.11ax) on 2.4GHz and 5GHz bands. The 6GHz band always uses WiFi 6 regardless of this setting
|
||||
- `enable-80211ax`: Boolean (default: false). Opt-in to enable 802.11ax (WiFi 6) on 2.4GHz and 5GHz bands. The 6GHz band always uses 802.11ax regardless of this setting
|
||||
|
||||
> [!NOTE]
|
||||
> TX power and channel width are automatically determined by the driver based on regulatory constraints, PHY mode, and hardware capabilities.
|
||||
@@ -105,7 +105,7 @@ admin@example:/config/> edit hardware component radio0 wifi-radio
|
||||
admin@example:/config/hardware/component/radio0/wifi-radio/> set country-code DE
|
||||
admin@example:/config/hardware/component/radio0/wifi-radio/> set band 5GHz
|
||||
admin@example:/config/hardware/component/radio0/wifi-radio/> set channel 36
|
||||
admin@example:/config/hardware/component/radio0/wifi-radio/> set enable-wifi6 true
|
||||
admin@example:/config/hardware/component/radio0/wifi-radio/> set enable-80211ax true
|
||||
admin@example:/config/hardware/component/radio0/wifi-radio/> leave
|
||||
```
|
||||
|
||||
@@ -121,7 +121,7 @@ admin@example:/config/hardware/component/radio0/wifi-radio/> leave
|
||||
- Older WiFi 5/4 clients can still connect but won't use WiFi 6 features
|
||||
|
||||
> [!NOTE]
|
||||
> The 6GHz band always uses WiFi 6 (802.11ax) regardless of the `enable-wifi6`
|
||||
> The 6GHz band always uses WiFi 6 (802.11ax) regardless of the `enable-80211ax`
|
||||
> setting, as WiFi 6E requires 802.11ax support.
|
||||
|
||||
## Discovering Available Networks (Scanning)
|
||||
|
||||
+22
-9
@@ -1,11 +1,12 @@
|
||||
From 6d6e9958d1066f53e1359c09ff3e92386244c89e Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Walström <lazzer@gmail.com>
|
||||
From d571b8417741b2dbed814bc38c133dcf37055c66 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
|
||||
Date: Thu, 15 Jan 2026 22:47:37 +0100
|
||||
Subject: [PATCH 29/30] wifi: brcmfmac: support deletion and recreation of
|
||||
Subject: [PATCH 29/32] wifi: brcmfmac: support deletion and recreation of
|
||||
primary interface
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
The Broadcom FullMAC firmware does not allow the primary interface
|
||||
(bsscfgidx 0) to be deleted - it always exists in firmware. However,
|
||||
@@ -37,14 +38,14 @@ Add support for this by:
|
||||
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 177 ++++++++++++++++--
|
||||
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 179 ++++++++++++++++--
|
||||
.../broadcom/brcm80211/brcmfmac/cfg80211.h | 4 +-
|
||||
.../broadcom/brcm80211/brcmfmac/core.c | 6 +-
|
||||
.../broadcom/brcm80211/brcmfmac/p2p.c | 16 +-
|
||||
4 files changed, 183 insertions(+), 20 deletions(-)
|
||||
4 files changed, 185 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
||||
index bb96b87b2a6e..9c8128551072 100644
|
||||
index bb96b87b2a6e..41b75ebc9d4f 100644
|
||||
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
||||
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
||||
@@ -124,6 +124,9 @@ struct cca_msrmnt_query {
|
||||
@@ -269,7 +270,16 @@ index bb96b87b2a6e..9c8128551072 100644
|
||||
return brcmf_cfg80211_del_apsta_iface(wiphy, wdev);
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
@@ -8114,11 +8261,11 @@ brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req,
|
||||
@@ -7790,6 +7937,8 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
|
||||
return err;
|
||||
|
||||
ndev = cfg_to_ndev(cfg);
|
||||
+ if (!ndev)
|
||||
+ return -ENODEV;
|
||||
wdev = ndev->ieee80211_ptr;
|
||||
ifp = netdev_priv(ndev);
|
||||
|
||||
@@ -8114,11 +8263,11 @@ brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req,
|
||||
}
|
||||
|
||||
static s32
|
||||
@@ -283,7 +293,7 @@ index bb96b87b2a6e..9c8128551072 100644
|
||||
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
return -EINVAL;
|
||||
@@ -8144,7 +8291,7 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
|
||||
@@ -8144,7 +8293,7 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
|
||||
int idx, struct survey_info *info)
|
||||
{
|
||||
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
|
||||
@@ -292,7 +302,7 @@ index bb96b87b2a6e..9c8128551072 100644
|
||||
struct brcmf_dump_survey survey = {};
|
||||
struct ieee80211_supported_band *band;
|
||||
enum nl80211_band band_id;
|
||||
@@ -8175,21 +8322,21 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
|
||||
@@ -8175,21 +8324,21 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
|
||||
if (band_id == NUM_NL80211_BANDS)
|
||||
return -ENOENT;
|
||||
|
||||
@@ -396,3 +406,6 @@ index e1752a513c73..2dc0fbba271d 100644
|
||||
if (brcmf_cfg80211_vif_event_armed(cfg))
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
--
|
||||
2.43.0
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
From f4d3d21bbb0bf21d7ad12cbb419e6a4c464ea619 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
|
||||
Date: Tue, 20 Jan 2026 20:12:10 +0100
|
||||
Subject: [PATCH 31/32] wifi: brcmfmac: suppress log spam for
|
||||
regulatory-restricted channels
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
When scanning, the driver attempts to set each channel and logs an
|
||||
error if the firmware rejects it. For regulatory-restricted channels,
|
||||
the firmware returns -52 (EBADE), which is expected behavior.
|
||||
|
||||
Suppress the error message for -52 errors to avoid spamming the kernel
|
||||
log during normal scan operations.
|
||||
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
.../net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
||||
index 1d937e41b95f..f49e11160d02 100644
|
||||
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
||||
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
||||
@@ -8280,7 +8280,12 @@ brcmf_set_channel(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
|
||||
if (chspec != INVCHANSPEC) {
|
||||
err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec);
|
||||
if (err) {
|
||||
- brcmf_err("set chanspec 0x%04x fail, reason %d\n", chspec, err);
|
||||
+ /* -52 (EBADE) is expected for regulatory-restricted
|
||||
+ * channels, don't spam the log for these.
|
||||
+ */
|
||||
+ if (err != -EBADE)
|
||||
+ brcmf_err("set chanspec 0x%04x fail, reason %d\n",
|
||||
+ chspec, err);
|
||||
err = -EINVAL;
|
||||
}
|
||||
} else {
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
From 3592365d811531eaafd1973ba4954b8131c47bf8 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
|
||||
Date: Tue, 20 Jan 2026 20:18:45 +0100
|
||||
Subject: [PATCH 32/32] wifi: brcmfmac: reduce log noise during AP to station
|
||||
transition
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
When transitioning from AP mode to station mode (e.g., hostapd stopping
|
||||
and wpa_supplicant starting), several non-fatal errors can occur:
|
||||
|
||||
1. SET SSID error (-512) in stop_ap: This happens when hostapd is killed
|
||||
before the firmware operation completes. -512 is ERESTARTSYS which is
|
||||
expected in this scenario.
|
||||
|
||||
2. MPC (Minimum Power Consumption) setting failures: These can occur
|
||||
during mode transitions when the firmware is in an intermediate state.
|
||||
MPC is a power optimization and failures are not critical.
|
||||
|
||||
Suppress the ERESTARTSYS error in stop_ap and downgrade the MPC error
|
||||
to debug level to reduce log spam during normal operation.
|
||||
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
.../wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 13 +++++++++----
|
||||
1 file changed, 9 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
||||
index f49e11160d02..101cbb4e87e1 100644
|
||||
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
||||
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
|
||||
@@ -1130,13 +1130,15 @@ static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
|
||||
|
||||
void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
|
||||
{
|
||||
- struct brcmf_pub *drvr = ifp->drvr;
|
||||
s32 err = 0;
|
||||
|
||||
if (check_vif_up(ifp->vif)) {
|
||||
err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
|
||||
if (err) {
|
||||
- bphy_err(drvr, "fail to set mpc\n");
|
||||
+ /* MPC setting can fail during mode transitions,
|
||||
+ * this is not critical.
|
||||
+ */
|
||||
+ brcmf_dbg(INFO, "fail to set mpc to %d: %d\n", mpc, err);
|
||||
return;
|
||||
}
|
||||
brcmf_dbg(INFO, "MPC : %d\n", mpc);
|
||||
@@ -5558,8 +5560,11 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev,
|
||||
memset(&join_params, 0, sizeof(join_params));
|
||||
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
|
||||
&join_params, sizeof(join_params));
|
||||
- if (err < 0)
|
||||
- bphy_err(drvr, "SET SSID error (%d)\n", err);
|
||||
+ if (err < 0) {
|
||||
+ /* -ERESTARTSYS is expected if hostapd was killed */
|
||||
+ if (err != -ERESTARTSYS)
|
||||
+ bphy_err(drvr, "SET SSID error (%d)\n", err);
|
||||
+ }
|
||||
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
|
||||
if (err < 0)
|
||||
bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err);
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Rename cleartext-symmetric-key to symmetric-key
|
||||
# Rename cleartext-key to symmetric-key
|
||||
|
||||
file=$1
|
||||
temp=${file}.tmp
|
||||
@@ -7,12 +7,12 @@ temp=${file}.tmp
|
||||
jq '
|
||||
if .["ietf-keystore:keystore"]?."symmetric-keys"?."symmetric-key" then
|
||||
.["ietf-keystore:keystore"]."symmetric-keys"."symmetric-key" |= map(
|
||||
if ."key-type"?."cleartext-key" then
|
||||
# Rename cleartext-key to cleartext-symmetric-key
|
||||
."key-type"."cleartext-key" as $key_value |
|
||||
."key-type" |= (del(."cleartext-key") | . + {
|
||||
"symmetric-key": $key_value
|
||||
})
|
||||
if ."infix-keystore:cleartext-key" then
|
||||
# Rename cleartext-key to symmetric-key
|
||||
."infix-keystore:cleartext-key" as $key_value |
|
||||
del(."infix-keystore:cleartext-key") | . + {
|
||||
"infix-keystore:symmetric-key": $key_value
|
||||
}
|
||||
else
|
||||
.
|
||||
end
|
||||
|
||||
+122
-80
@@ -3,6 +3,8 @@
|
||||
#include <srx/srx_val.h>
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
|
||||
#include "interfaces.h"
|
||||
#include "core.h"
|
||||
|
||||
struct confd confd;
|
||||
@@ -64,7 +66,7 @@ static confd_dependency_t add_dependencies(struct lyd_node **diff, const char *x
|
||||
|
||||
static confd_dependency_t handle_dependencies(struct lyd_node **diff, struct lyd_node *config)
|
||||
{
|
||||
struct lyd_node *dkeys, *dkey, *hostname;
|
||||
struct lyd_node *dkeys, *dkey, *hostname, *dcomponents, *dcomponent;
|
||||
confd_dependency_t result = CONFD_DEP_DONE;
|
||||
const char *key_name;
|
||||
|
||||
@@ -138,126 +140,166 @@ static confd_dependency_t handle_dependencies(struct lyd_node **diff, struct lyd
|
||||
}
|
||||
}
|
||||
|
||||
/* When WiFi interfaces change, add their radios to the diff */
|
||||
struct lyd_node *difs = lydx_get_descendant(*diff, "interfaces", "interface", NULL);
|
||||
if (difs) {
|
||||
struct lyd_node *dif;
|
||||
struct lyd_node *dif, *cif;
|
||||
|
||||
LYX_LIST_FOR_EACH(difs, dif, "interface") {
|
||||
struct lyd_node *dwifi, *radio_node;
|
||||
struct lyd_node *dwifi, *dcustom_phys, *radio_node, *cwifi, *ap;
|
||||
const char *ifname, *radio_name;
|
||||
bool is_wifi_change = false;
|
||||
enum iftype type;
|
||||
char xpath[256];
|
||||
|
||||
/* Check if this interface has a wifi container in the diff */
|
||||
dwifi = lydx_get_child(dif, "wifi");
|
||||
if (!dwifi)
|
||||
continue;
|
||||
|
||||
/* Get the interface name */
|
||||
ifname = lydx_get_cattr(dif, "name");
|
||||
if (!ifname)
|
||||
continue;
|
||||
|
||||
/* Get radio reference from config tree using xpath */
|
||||
radio_node = lydx_get_xpathf(config,
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi/radio",
|
||||
ifname);
|
||||
if (!radio_node)
|
||||
continue;
|
||||
cif = lydx_get_xpathf(config, "/ietf-interfaces:interfaces/interface[name='%s']", ifname);
|
||||
|
||||
radio_name = lyd_get_value(radio_node);
|
||||
if (!radio_name)
|
||||
continue;
|
||||
|
||||
/* Add the radio to the diff */
|
||||
snprintf(xpath, sizeof(xpath), "/ietf-hardware:hardware/component[name='%s']/infix-hardware:wifi-radio", radio_name);
|
||||
result = add_dependencies(diff, xpath, radio_name);
|
||||
if (result == CONFD_DEP_ERROR) {
|
||||
ERROR("Failed to add radio %s to diff for WiFi interface %s", radio_name, ifname);
|
||||
return result;
|
||||
type = iftype_from_iface(dif);
|
||||
if (type == IFT_UNKNOWN)
|
||||
type = iftype_from_iface(cif);
|
||||
|
||||
if (type == IFT_WIFI) {
|
||||
/* Check if this interface has a wifi container in the diff */
|
||||
dwifi = lydx_get_child(dif, "wifi");
|
||||
if (dwifi)
|
||||
is_wifi_change = true;
|
||||
|
||||
ap = lydx_get_child(dwifi, "access-point");
|
||||
if (ap)
|
||||
is_wifi_change = true;
|
||||
|
||||
/* Check if custom-phys-address changed on a WiFi interface */
|
||||
if (!is_wifi_change) {
|
||||
dcustom_phys = lydx_get_child(dif, "custom-phys-address");
|
||||
if (dcustom_phys) {
|
||||
/* Check if this interface is a WiFi interface in the config */
|
||||
cwifi = lydx_get_xpathf(config,
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi",
|
||||
ifname);
|
||||
if (cwifi)
|
||||
is_wifi_change = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get radio reference from config tree using xpath */
|
||||
radio_node = lydx_get_xpathf(config,
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi/radio",
|
||||
ifname);
|
||||
if (!radio_node)
|
||||
continue;
|
||||
|
||||
radio_name = lyd_get_value(radio_node);
|
||||
|
||||
if (!radio_name)
|
||||
continue;
|
||||
|
||||
/* Add the radio to the diff */
|
||||
snprintf(xpath, sizeof(xpath), "/ietf-hardware:hardware/component[name='%s']/infix-hardware:wifi-radio", radio_name);
|
||||
result = add_dependencies(diff, xpath, radio_name);
|
||||
if (result == CONFD_DEP_ERROR) {
|
||||
ERROR("Failed to add radio %s to diff for WiFi interface %s", radio_name, ifname);
|
||||
return result;
|
||||
}
|
||||
|
||||
DEBUG("Added radio %s to diff for WiFi interface %s", radio_name, ifname);
|
||||
}
|
||||
|
||||
DEBUG("Added radio %s to diff for WiFi interface %s", radio_name, ifname);
|
||||
}
|
||||
}
|
||||
dcomponents = lydx_get_descendant(*diff, "hardware", "component", NULL);
|
||||
LYX_LIST_FOR_EACH(dcomponents, dcomponent, "component") {
|
||||
const char *class, *name;
|
||||
|
||||
/* When WiFi radios change, add all interfaces using that radio to the diff */
|
||||
struct ly_set *radios = lydx_find_xpathf(*diff,
|
||||
"/ietf-hardware:hardware/component[infix-hardware:wifi-radio]");
|
||||
if (radios && radios->count > 0) {
|
||||
for (uint32_t i = 0; i < radios->count; i++) {
|
||||
struct lyd_node *hradio = radios->dnodes[i];
|
||||
const char *radio_name = lydx_get_cattr(hradio, "name");
|
||||
name = lydx_get_cattr(dcomponent, "name");
|
||||
if (!name)
|
||||
continue;
|
||||
|
||||
class = lydx_get_cattr(dcomponent, "class");
|
||||
if (!class) {
|
||||
struct lyd_node *class_node = lydx_get_xpathf(config, "/ietf-hardware:hardware/component[name='%s']/class", name);
|
||||
if (class_node)
|
||||
class = lyd_get_value(class_node);
|
||||
}
|
||||
if (!strcmp(class, "infix-hardware:wifi"))
|
||||
{
|
||||
struct ly_set *ifaces;
|
||||
uint32_t j;
|
||||
char xpath[256];
|
||||
|
||||
if (!radio_name)
|
||||
continue;
|
||||
|
||||
/* Find all interfaces that reference this radio */
|
||||
ifaces = lydx_find_xpathf(config,
|
||||
"/ietf-interfaces:interfaces/interface[infix-interfaces:wifi/radio='%s']",
|
||||
radio_name);
|
||||
if (ifaces && ifaces->count > 0) {
|
||||
for (j = 0; j < ifaces->count; j++) {
|
||||
const char *ifname = lydx_get_cattr(ifaces->dnodes[j], "name");
|
||||
"/ietf-interfaces:interfaces/interface/infix-interfaces:wifi/radio[.='%s']",
|
||||
name);
|
||||
if (!ifaces || ifaces->count == 0) {
|
||||
if (ifaces)
|
||||
ly_set_free(ifaces, NULL);
|
||||
continue;
|
||||
}
|
||||
DEBUG("Found %d interfaces using radio %s", ifaces->count, name);
|
||||
|
||||
/* Add the wifi container */
|
||||
for (j = 0; j < ifaces->count; j++) {
|
||||
/* ifaces->dnodes[j] is the radio leaf, navigate up: radio -> wifi -> interface */
|
||||
struct lyd_node *wifi_node = lyd_parent(ifaces->dnodes[j]);
|
||||
struct lyd_node *iface_node = lyd_parent(wifi_node);
|
||||
const char *ifname;
|
||||
|
||||
ifname = lydx_get_cattr(iface_node, "name");
|
||||
if (!ifname)
|
||||
continue;
|
||||
|
||||
/* Add the wifi container */
|
||||
snprintf(xpath, sizeof(xpath),
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi",
|
||||
ifname);
|
||||
result = add_dependencies(diff, xpath, ifname);
|
||||
if (result == CONFD_DEP_ERROR) {
|
||||
ERROR("Failed to add interface wifi for %s (radio %s)", ifname, name);
|
||||
ly_set_free(ifaces, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Add the radio leaf */
|
||||
snprintf(xpath, sizeof(xpath),
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi/radio",
|
||||
ifname);
|
||||
result = add_dependencies(diff, xpath, name);
|
||||
if (result == CONFD_DEP_ERROR) {
|
||||
ERROR("Failed to add radio leaf for interface %s (radio %s)", ifname, name);
|
||||
ly_set_free(ifaces, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Add station or access-point container depending on mode */
|
||||
if (lydx_get_child(wifi_node, "station")) {
|
||||
snprintf(xpath, sizeof(xpath),
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi",
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi/station",
|
||||
ifname);
|
||||
result = add_dependencies(diff, xpath, ifname);
|
||||
if (result == CONFD_DEP_ERROR) {
|
||||
ERROR("Failed to add interface wifi for %s (radio %s)", ifname, radio_name);
|
||||
ERROR("Failed to add station for interface %s (radio %s)", ifname, name);
|
||||
ly_set_free(ifaces, NULL);
|
||||
ly_set_free(radios, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Add the radio leaf */
|
||||
} else if (lydx_get_child(wifi_node, "access-point")) {
|
||||
snprintf(xpath, sizeof(xpath),
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi/radio",
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi/access-point",
|
||||
ifname);
|
||||
result = add_dependencies(diff, xpath, radio_name);
|
||||
result = add_dependencies(diff, xpath, ifname);
|
||||
if (result == CONFD_DEP_ERROR) {
|
||||
ERROR("Failed to add radio leaf for interface %s (radio %s)", ifname, radio_name);
|
||||
ERROR("Failed to add access-point for interface %s (radio %s)", ifname, name);
|
||||
ly_set_free(ifaces, NULL);
|
||||
ly_set_free(radios, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Add station or access-point container depending on mode */
|
||||
if (lydx_get_descendant(ifaces->dnodes[j], "interface", "wifi", "station", NULL)) {
|
||||
snprintf(xpath, sizeof(xpath),
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi/station",
|
||||
ifname);
|
||||
result = add_dependencies(diff, xpath, ifname);
|
||||
if (result == CONFD_DEP_ERROR) {
|
||||
ERROR("Failed to add station for interface %s (radio %s)", ifname, radio_name);
|
||||
ly_set_free(ifaces, NULL);
|
||||
ly_set_free(radios, NULL);
|
||||
return result;
|
||||
}
|
||||
} else if (lydx_get_descendant(ifaces->dnodes[j], "interface", "wifi", "access-point", NULL)) {
|
||||
snprintf(xpath, sizeof(xpath),
|
||||
"/ietf-interfaces:interfaces/interface[name='%s']/infix-interfaces:wifi/access-point",
|
||||
ifname);
|
||||
result = add_dependencies(diff, xpath, ifname);
|
||||
if (result == CONFD_DEP_ERROR) {
|
||||
ERROR("Failed to add access-point for interface %s (radio %s)", ifname, radio_name);
|
||||
ly_set_free(ifaces, NULL);
|
||||
ly_set_free(radios, NULL);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG("Added interface %s to diff for radio %s", ifname, radio_name);
|
||||
}
|
||||
ly_set_free(ifaces, NULL);
|
||||
|
||||
DEBUG("Added interface %s to diff for radio %s", ifname, name);
|
||||
}
|
||||
ly_set_free(ifaces, NULL);
|
||||
}
|
||||
ly_set_free(radios, NULL);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
+22
-176
@@ -16,8 +16,6 @@
|
||||
#define XPATH_BASE_ "/ietf-hardware:hardware"
|
||||
#define HOSTAPD_CONF "/etc/hostapd-%s.conf"
|
||||
#define HOSTAPD_CONF_NEXT HOSTAPD_CONF"+"
|
||||
#define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant-%s.conf"
|
||||
#define WPA_SUPPLICANT_CONF_NEXT WPA_SUPPLICANT_CONF"+"
|
||||
|
||||
static int dir_cb(const char *fpath, const struct stat *sb,
|
||||
int typeflag, struct FTW *ftwbuf)
|
||||
@@ -189,82 +187,6 @@ static int wifi_find_interfaces_on_radio(struct lyd_node *ifs, const char *radio
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wifi_gen_station(const char *ifname, struct lyd_node *station,
|
||||
const char *radio, struct lyd_node *config)
|
||||
{
|
||||
const char *ssid, *secret_name, *secret, *security_mode;
|
||||
struct lyd_node *security, *secret_node, *radio_node;
|
||||
FILE *wpa_supplicant = NULL;
|
||||
char *security_str = NULL;
|
||||
const char *country;
|
||||
int rc = SR_ERR_OK;
|
||||
|
||||
/* If station is NULL, we're in scan-only mode (no station container) */
|
||||
if (station) {
|
||||
ssid = lydx_get_cattr(station, "ssid");
|
||||
security = lydx_get_child(station, "security");
|
||||
security_mode = lydx_get_cattr(security, "mode");
|
||||
secret_name = lydx_get_cattr(security, "secret");
|
||||
} else {
|
||||
ssid = NULL;
|
||||
security = NULL;
|
||||
security_mode = "disabled";
|
||||
secret_name = NULL;
|
||||
}
|
||||
|
||||
radio_node = lydx_get_xpathf(config,
|
||||
"/hardware/component[name='%s']/wifi-radio", radio);
|
||||
country = radio_node ? lydx_get_cattr(radio_node, "country-code") : NULL;
|
||||
|
||||
if (secret_name && strcmp(security_mode, "disabled") != 0) {
|
||||
secret_node = lydx_get_xpathf(config,
|
||||
"/keystore/symmetric-keys/symmetric-key[name='%s']/symmetric-key",
|
||||
secret_name);
|
||||
secret = secret_node ? lyd_get_value(secret_node) : NULL;
|
||||
} else {
|
||||
secret = NULL;
|
||||
}
|
||||
|
||||
wpa_supplicant = fopenf("w", WPA_SUPPLICANT_CONF_NEXT, ifname);
|
||||
if (!wpa_supplicant) {
|
||||
rc = SR_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fprintf(wpa_supplicant,
|
||||
"ctrl_interface=/run/wpa_supplicant\n"
|
||||
"autoscan=periodic:10\n"
|
||||
"ap_scan=1\n");
|
||||
|
||||
if (country)
|
||||
fprintf(wpa_supplicant, "country=%s\n", country);
|
||||
|
||||
/* If SSID is present, create network block. Otherwise, scan-only mode */
|
||||
if (ssid) {
|
||||
/* Station mode with network configured */
|
||||
if (!strcmp(security_mode, "disabled")) {
|
||||
asprintf(&security_str, "key_mgmt=NONE");
|
||||
} else if (secret) {
|
||||
asprintf(&security_str, "key_mgmt=SAE WPA-PSK\npsk=\"%s\"", secret);
|
||||
}
|
||||
fprintf(wpa_supplicant,
|
||||
"network={\n"
|
||||
"bgscan=\"simple: 30:-45:300\"\n"
|
||||
"ssid=\"%s\"\n"
|
||||
"%s\n"
|
||||
"}\n", ssid, security_str);
|
||||
free(security_str);
|
||||
} else {
|
||||
/* Scan-only mode - no station container configured */
|
||||
fprintf(wpa_supplicant, "# Scan-only mode - no network configured\n");
|
||||
}
|
||||
|
||||
out:
|
||||
if (wpa_supplicant)
|
||||
fclose(wpa_supplicant);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Helper: Find all AP interfaces on a specific radio */
|
||||
static int wifi_find_radio_aps(struct lyd_node *cifs, const char *radio_name,
|
||||
char ***ap_list, int *count)
|
||||
@@ -347,6 +269,7 @@ static int wifi_gen_bss_section(FILE *hostapd, struct lyd_node *cifs, const char
|
||||
if (hidden && !strcmp(hidden, "true"))
|
||||
fprintf(hostapd, "ignore_broadcast_ssid=1\n");
|
||||
|
||||
fprintf(hostapd, "wmm_enabled=1\n");
|
||||
/* Security configuration */
|
||||
security = lydx_get_child(ap, "security");
|
||||
security_mode = lydx_get_cattr(security, "mode");
|
||||
@@ -413,8 +336,9 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,
|
||||
char hostapd_conf[256];
|
||||
char **ap_list = NULL;
|
||||
FILE *hostapd = NULL;
|
||||
bool wifi6_enabled;
|
||||
bool ax_enabled;
|
||||
int ap_count = 0;
|
||||
char bssid[18];
|
||||
int i;
|
||||
|
||||
int rc = SR_ERR_OK;
|
||||
@@ -458,7 +382,7 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,
|
||||
country = lydx_get_cattr(radio_node, "country-code");
|
||||
band = lydx_get_cattr(radio_node, "band");
|
||||
channel = lydx_get_cattr(radio_node, "channel");
|
||||
wifi6_enabled = lydx_get_bool(radio_node, "enable_wifi6");
|
||||
ax_enabled = lydx_get_bool(radio_node, "enable-80211ax");
|
||||
|
||||
/* Get secret from keystore if not open network */
|
||||
if (secret_name && strcmp(security_mode, "open") != 0) {
|
||||
@@ -492,7 +416,6 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,
|
||||
fprintf(hostapd, "ctrl_interface=/run/hostapd\n");
|
||||
|
||||
/* Set BSSID if custom MAC is configured */
|
||||
char bssid[18];
|
||||
if (!interface_get_phys_addr(primary_cif, bssid))
|
||||
fprintf(hostapd, "bssid=%s\n", bssid);
|
||||
fprintf(hostapd, "\n");
|
||||
@@ -508,12 +431,17 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,
|
||||
/* Enable 802.11d (regulatory domain) and 802.11h (spectrum management/DFS) */
|
||||
fprintf(hostapd, "ieee80211d=1\n");
|
||||
fprintf(hostapd, "ieee80211h=1\n");
|
||||
fprintf(hostapd, "wmm_enabled=1\n");
|
||||
|
||||
/* Band and channel configuration */
|
||||
if (band) {
|
||||
/* Set hardware mode based on band */
|
||||
if (!strcmp(band, "2.4GHz")) {
|
||||
fprintf(hostapd, "hw_mode=g\n");
|
||||
|
||||
/* Disable 802.11b rates, ancient devices. This will improve range. */
|
||||
fprintf(hostapd, "supported_rates=60 90 120 180 240 360 480 540\n");
|
||||
fprintf(hostapd, "basic_rates=60 120 240\n");
|
||||
} else if (!strcmp(band, "5GHz") || !strcmp(band, "6GHz")) {
|
||||
fprintf(hostapd, "hw_mode=a\n");
|
||||
}
|
||||
@@ -544,20 +472,20 @@ static int wifi_gen_aps_on_radio(const char *radio_name, struct lyd_node *cifs,
|
||||
|
||||
if (band) {
|
||||
if (!strcmp(band, "2.4GHz")) {
|
||||
/* 2.4GHz: Enable 802.11n (HT), optionally WiFi 6 */
|
||||
/* 2.4GHz: Enable 802.11n (HT), optionally 802.11ax */
|
||||
fprintf(hostapd, "ieee80211n=1\n");
|
||||
if (wifi6_enabled) {
|
||||
if (ax_enabled) {
|
||||
fprintf(hostapd, "ieee80211ax=1\n");
|
||||
}
|
||||
} else if (!strcmp(band, "5GHz")) {
|
||||
/* 5GHz: Enable 802.11n and 802.11ac, optionally WiFi 6 */
|
||||
/* 5GHz: Enable 802.11n and 802.11ac, optionally 802.11ax */
|
||||
fprintf(hostapd, "ieee80211n=1\n");
|
||||
fprintf(hostapd, "ieee80211ac=1\n");
|
||||
if (wifi6_enabled) {
|
||||
if (ax_enabled) {
|
||||
fprintf(hostapd, "ieee80211ax=1\n");
|
||||
}
|
||||
} else if (!strcmp(band, "6GHz")) {
|
||||
/* 6GHz: Enable 802.11ax (WiFi 6E required) */
|
||||
/* 6GHz: Enable 802.11ax (required for 6GHz) */
|
||||
fprintf(hostapd, "ieee80211n=1\n");
|
||||
fprintf(hostapd, "ieee80211ac=1\n");
|
||||
fprintf(hostapd, "ieee80211ax=1\n");
|
||||
@@ -696,7 +624,7 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
|
||||
} else if (!strcmp(class, "infix-hardware:wifi")) {
|
||||
struct lyd_node *interfaces_config, *interfaces_diff;
|
||||
struct lyd_node **wifi_iface_list = NULL;
|
||||
struct lyd_node *station, *ap;
|
||||
struct lyd_node *ap;
|
||||
struct lyd_node *cwifi_radio;
|
||||
int wifi_iface_count = 0;
|
||||
char src[40], dst[40];
|
||||
@@ -710,58 +638,13 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
|
||||
case SR_EV_DONE:
|
||||
interfaces_diff = lydx_get_descendant(diff, "interfaces", "interface", NULL);
|
||||
|
||||
/* Handle deleted WiFi interfaces - stop wpa_supplicant */
|
||||
if (interfaces_diff) {
|
||||
struct lyd_node *iface, *wifi;
|
||||
const char *radio;
|
||||
|
||||
LYX_LIST_FOR_EACH(interfaces_diff, iface, "interface") {
|
||||
const char *ifname;
|
||||
if (lydx_get_op(iface) != LYDX_OP_DELETE)
|
||||
continue;
|
||||
wifi = lydx_get_child(iface, "wifi");
|
||||
if (!wifi)
|
||||
continue;
|
||||
radio = lydx_get_cattr(wifi, "radio");
|
||||
if (!radio || strcmp(radio, name))
|
||||
continue;
|
||||
|
||||
ifname = lydx_get_cattr(iface, "name");
|
||||
erasef(WPA_SUPPLICANT_CONF, ifname);
|
||||
erasef(WPA_SUPPLICANT_CONF_NEXT, ifname);
|
||||
systemf("initctl -bfq disable wifi@%s", ifname);
|
||||
}
|
||||
}
|
||||
|
||||
wifi_find_interfaces_on_radio(interfaces_diff, name,
|
||||
&wifi_iface_list, &wifi_iface_count);
|
||||
if (wifi_iface_count > 0) {
|
||||
bool running, enabled;
|
||||
station = lydx_get_descendant(wifi_iface_list[0], "interface", "wifi", "station", NULL);
|
||||
|
||||
ap = lydx_get_descendant(wifi_iface_list[0], "interface", "wifi", "access-point", NULL);
|
||||
|
||||
if (station || !ap || lydx_get_op(ap) == LYDX_OP_DELETE) {
|
||||
const char *ifname = lydx_get_cattr(wifi_iface_list[0], "name");
|
||||
running = !systemf("initctl -bfq status wpa_supplicant:%s", ifname);
|
||||
if (lydx_get_op(station) == LYDX_OP_DELETE) {
|
||||
erasef(WPA_SUPPLICANT_CONF, ifname);
|
||||
erasef(WPA_SUPPLICANT_CONF_NEXT, ifname);
|
||||
systemf("initctl -bfq disable wifi@%s", ifname);
|
||||
} else {
|
||||
snprintf(src, sizeof(src), WPA_SUPPLICANT_CONF_NEXT, ifname);
|
||||
snprintf(dst, sizeof(dst), WPA_SUPPLICANT_CONF, ifname);
|
||||
running = !systemf("initctl -bfq status wpa_supplicant:%s", ifname);
|
||||
enabled = fexistf(WPA_SUPPLICANT_CONF_NEXT, ifname);
|
||||
|
||||
if (enabled) {
|
||||
(void)rename(src, dst);
|
||||
if (running)
|
||||
systemf("initctl -bfq touch wifi@%s", ifname);
|
||||
else
|
||||
systemf("initctl -bfq enable wifi@%s", ifname);
|
||||
}
|
||||
}
|
||||
} else if (wifi_iface_count > 0) {
|
||||
if (ap && lydx_get_op(ap) != LYDX_OP_DELETE) {
|
||||
/* AP mode - activate hostapd for radio */
|
||||
snprintf(src, sizeof(src), HOSTAPD_CONF_NEXT, name);
|
||||
snprintf(dst, sizeof(dst), HOSTAPD_CONF, name);
|
||||
@@ -802,48 +685,11 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
|
||||
|
||||
if (!wifi_iface_count)
|
||||
continue;
|
||||
/*
|
||||
* A radio operates in one of three modes:
|
||||
* 1. Station mode: One station interface (client mode)
|
||||
* 2. AP mode: One or more AP interfaces (hostapd multi-SSID)
|
||||
* 3. Scan-only mode: WiFi interface with radio but no mode configured
|
||||
*
|
||||
* Check for station first - there can be at most one per radio.
|
||||
* If no station or AP is configured, default to scan-only mode.
|
||||
*/
|
||||
station = lydx_get_descendant(wifi_iface_list[0], "interface", "wifi", "station", NULL);
|
||||
ap = lydx_get_descendant(wifi_iface_list[0], "interface", "wifi", "access-point", NULL);
|
||||
if (wifi_iface_count == 1 && station) {
|
||||
/* Station mode (with or without SSID for scan-only) */
|
||||
struct lyd_node *iface = wifi_iface_list[0];
|
||||
if (lydx_is_enabled(iface, "enabled")) {
|
||||
const char *ifname = lydx_get_cattr(iface, "name");
|
||||
rc = wifi_gen_station(ifname, station, name, config);
|
||||
if (rc != SR_ERR_OK) {
|
||||
ERROR("Failed to generate station config for %s", ifname);
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
} else if (!station && !ap) {
|
||||
/* No station/AP configured - default to scan-only mode */
|
||||
struct lyd_node *iface = wifi_iface_list[0];
|
||||
if (lydx_is_enabled(iface, "enabled")) {
|
||||
const char *ifname = lydx_get_cattr(iface, "name");
|
||||
rc = wifi_gen_station(ifname, NULL, name, config);
|
||||
if (rc != SR_ERR_OK) {
|
||||
ERROR("Failed to generate scan-only config for %s", ifname);
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Multiple interfaces or APs */
|
||||
rc = wifi_gen_aps_on_radio(name, interfaces_config, cwifi_radio, config);
|
||||
if (rc != SR_ERR_OK) {
|
||||
ERROR("Failed to generate AP config for radio %s", name);
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
next:
|
||||
|
||||
/* Generate AP config (hostapd) for all APs on this radio */
|
||||
rc = wifi_gen_aps_on_radio(name, interfaces_config, cwifi_radio, config);
|
||||
if (rc != SR_ERR_OK)
|
||||
ERROR("Failed to generate AP config for radio %s", name);
|
||||
/* Free the interface list */
|
||||
free(wifi_iface_list);
|
||||
wifi_iface_list = NULL;
|
||||
|
||||
+112
-10
@@ -3,12 +3,14 @@
|
||||
|
||||
#include "interfaces.h"
|
||||
|
||||
#define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant-%s.conf"
|
||||
|
||||
/*
|
||||
* WiFi Interface Management
|
||||
*
|
||||
* This file handles only virtual WiFi interface creation/deletion.
|
||||
* WiFi daemon configuration (hostapd/wpa_supplicant) is handled by
|
||||
* hardware.c when the WiFi radio (phy) is configured.
|
||||
* This file handles virtual WiFi interface creation/deletion and
|
||||
* station mode configuration (wpa_supplicant). Access point mode
|
||||
* configuration (hostapd) is handled by hardware.c.
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -19,9 +21,12 @@ typedef enum wifi_mode_t {
|
||||
wifi_ap,
|
||||
wifi_unknown
|
||||
} wifi_mode_t;
|
||||
|
||||
static wifi_mode_t wifi_get_mode(struct lyd_node *wifi)
|
||||
{
|
||||
if (lydx_get_child(wifi, "access-point"))
|
||||
struct lyd_node *ap = lydx_get_child(wifi, "access-point");
|
||||
|
||||
if (ap && (lydx_get_op(ap) != LYDX_OP_DELETE))
|
||||
return wifi_ap;
|
||||
else
|
||||
return wifi_station; /* Need to return station even if "station" also is false, since that is the default scanning mode */
|
||||
@@ -29,20 +34,106 @@ static wifi_mode_t wifi_get_mode(struct lyd_node *wifi)
|
||||
|
||||
int wifi_mode_changed(struct lyd_node *wifi)
|
||||
{
|
||||
struct lyd_node *station, *ap;
|
||||
enum lydx_op station_op, ap_op;
|
||||
struct lyd_node *ap;
|
||||
enum lydx_op ap_op;
|
||||
|
||||
if (!wifi)
|
||||
return 0;
|
||||
station = lydx_get_child(wifi, "station");
|
||||
|
||||
ap = lydx_get_child(wifi, "access-point");
|
||||
if (station)
|
||||
station_op = lydx_get_op(station);
|
||||
|
||||
if (ap)
|
||||
ap_op = lydx_get_op(ap);
|
||||
|
||||
return ((station && station_op == LYDX_OP_DELETE) || (ap && ap_op == LYDX_OP_DELETE));
|
||||
ERROR("MODE CHANGED: %d", ap && (ap_op == LYDX_OP_CREATE || ap_op == LYDX_OP_DELETE));
|
||||
return (ap && (ap_op == LYDX_OP_CREATE || ap_op == LYDX_OP_DELETE));
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate wpa_supplicant config for station mode
|
||||
*/
|
||||
static int wifi_gen_station(struct lyd_node *cif)
|
||||
{
|
||||
const char *ifname, *ssid, *secret_name, *secret, *security_mode, *radio;
|
||||
struct lyd_node *security, *secret_node, *radio_node, *station, *wifi;
|
||||
FILE *wpa_supplicant = NULL;
|
||||
char *security_str = NULL;
|
||||
const char *country;
|
||||
int rc = SR_ERR_OK;
|
||||
|
||||
ifname = lydx_get_cattr(cif, "name");
|
||||
wifi = lydx_get_child(cif, "wifi");
|
||||
if (!wifi)
|
||||
return SR_ERR_OK;
|
||||
|
||||
radio = lydx_get_cattr(wifi, "radio");
|
||||
station = lydx_get_child(wifi, "station");
|
||||
/* If station is NULL, we're in scan-only mode (no station container) */
|
||||
if (station) {
|
||||
ssid = lydx_get_cattr(station, "ssid");
|
||||
security = lydx_get_child(station, "security");
|
||||
security_mode = lydx_get_cattr(security, "mode");
|
||||
secret_name = lydx_get_cattr(security, "secret");
|
||||
} else {
|
||||
ssid = NULL;
|
||||
security = NULL;
|
||||
security_mode = "disabled";
|
||||
secret_name = NULL;
|
||||
}
|
||||
|
||||
radio_node = lydx_get_xpathf(cif,
|
||||
"../../hardware/component[name='%s']/wifi-radio", radio);
|
||||
country = lydx_get_cattr(radio_node, "country-code");
|
||||
|
||||
if (secret_name && strcmp(security_mode, "disabled") != 0) {
|
||||
secret_node = lydx_get_xpathf(cif,
|
||||
"../../keystore/symmetric-keys/symmetric-key[name='%s']",
|
||||
secret_name);
|
||||
secret = lydx_get_cattr(secret_node, "symmetric-key");
|
||||
} else {
|
||||
secret = NULL;
|
||||
}
|
||||
|
||||
wpa_supplicant = fopenf("w", WPA_SUPPLICANT_CONF, ifname);
|
||||
if (!wpa_supplicant) {
|
||||
rc = SR_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fprintf(wpa_supplicant,
|
||||
"ctrl_interface=/run/wpa_supplicant\n"
|
||||
"autoscan=periodic:10\n"
|
||||
"ap_scan=1\n");
|
||||
|
||||
if (country)
|
||||
fprintf(wpa_supplicant, "country=%s\n", country);
|
||||
|
||||
/* If SSID is present, create network block. Otherwise, scan-only mode */
|
||||
if (ssid) {
|
||||
/* Station mode with network configured */
|
||||
if (!strcmp(security_mode, "disabled")) {
|
||||
asprintf(&security_str, "key_mgmt=NONE");
|
||||
} else if (secret) {
|
||||
asprintf(&security_str, "key_mgmt=SAE WPA-PSK\npsk=\"%s\"", secret);
|
||||
}
|
||||
fprintf(wpa_supplicant,
|
||||
"network={\n"
|
||||
"bgscan=\"simple: 30:-45:300\"\n"
|
||||
"ssid=\"%s\"\n"
|
||||
"%s\n"
|
||||
"}\n", ssid, security_str);
|
||||
free(security_str);
|
||||
} else {
|
||||
/* Scan-only mode - no station container configured */
|
||||
fprintf(wpa_supplicant, "# Scan-only mode - no network configured\n");
|
||||
}
|
||||
|
||||
out:
|
||||
if (wpa_supplicant)
|
||||
fclose(wpa_supplicant);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add WiFi virtual interface using iw
|
||||
*/
|
||||
@@ -83,6 +174,9 @@ int wifi_add_iface(struct lyd_node *cif, struct dagger *net)
|
||||
switch(mode) {
|
||||
case wifi_station:
|
||||
fprintf(iw, "iw phy %s interface add %s type managed\n", radio, ifname);
|
||||
wifi_gen_station(cif);
|
||||
fprintf(iw, "initctl -bfq enable wifi@%s\n", ifname);
|
||||
fprintf(iw, "initctl -bfq touch wifi@%s\n", ifname);
|
||||
break;
|
||||
case wifi_ap:
|
||||
fprintf(iw, "iw phy %s interface add %s type __ap\n", radio, ifname);
|
||||
@@ -102,6 +196,7 @@ out:
|
||||
*/
|
||||
int wifi_del_iface(struct lyd_node *dif, struct dagger *net)
|
||||
{
|
||||
struct lyd_node *wifi;
|
||||
const char *ifname;
|
||||
FILE *iw;
|
||||
|
||||
@@ -117,6 +212,13 @@ int wifi_del_iface(struct lyd_node *dif, struct dagger *net)
|
||||
fprintf(iw, "ip link set %s down\n", ifname); /* Required to change modes. */
|
||||
fprintf(iw, "iw dev %s disconnect\n", ifname);
|
||||
fprintf(iw, "iw dev %s del\n", ifname);
|
||||
|
||||
wifi = lydx_get_child(dif, "wifi");
|
||||
if (wifi && wifi_get_mode(wifi) == wifi_ap) { /* if not station or scanning mode */
|
||||
erasef(WPA_SUPPLICANT_CONF, ifname);
|
||||
fprintf(iw, "initctl -bfq disable wifi@%s\n", ifname);
|
||||
}
|
||||
|
||||
fclose(iw);
|
||||
|
||||
return SR_ERR_OK;
|
||||
|
||||
@@ -150,6 +150,8 @@ int wireguard_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip, struct d
|
||||
|
||||
/* Create activation script */
|
||||
wg_sh = dagger_fopen_net_init(net, ifname, NETDAG_INIT_POST, "enable-wireguard.sh");
|
||||
if (!wg_sh)
|
||||
return SR_ERR_INTERNAL;
|
||||
|
||||
fprintf(wg_sh, "wg setconf %s ", ifname);
|
||||
fprintf(wg_sh, WIREGUARD_CONFIG, ifname);
|
||||
|
||||
@@ -238,7 +238,7 @@ static void mdns_conf(struct lyd_node *cfg)
|
||||
fprintf(fp, "\n[reflector]\n");
|
||||
ctx = lydx_get_descendant(lyd_child(cfg), "reflector", NULL);
|
||||
if (ctx) {
|
||||
fprintf(fp, "enable-reflector=%s\n", lydx_is_enabled(ctx, "enabled") ? "on" : "off");
|
||||
fprintf(fp, "enable-reflector=%s\n", lydx_is_enabled(ctx, "enabled") ? "yes" : "no");
|
||||
fput_list(fp, ctx, "service-filter", "reflect-filters=");
|
||||
}
|
||||
|
||||
|
||||
@@ -262,16 +262,15 @@ module infix-hardware {
|
||||
congestion in most environments.";
|
||||
}
|
||||
|
||||
leaf enable-wifi6 {
|
||||
leaf enable-80211ax {
|
||||
type boolean;
|
||||
default false;
|
||||
description
|
||||
"Enable WiFi 6 (802.11ax) on 2.4GHz and 5GHz bands.
|
||||
"Enable 802.11ax (WiFi 6) on 2.4GHz and 5GHz bands.
|
||||
|
||||
By default, WiFi 6 is enabled only on 6GHz (WiFi 6E).
|
||||
Set to 'true' to enable WiFi 6 on 2.4GHz and 5GHz bands.
|
||||
By default, 802.11ax is enabled only on 6GHz (WiFi 6E).
|
||||
Set to 'true' to enable 802.11ax on 2.4GHz and 5GHz bands.
|
||||
|
||||
WiFi 6 provides:
|
||||
802.11ax provides:
|
||||
- OFDMA (better multi-user efficiency)
|
||||
- Target Wake Time (better battery life)
|
||||
- 1024-QAM (higher throughput)
|
||||
@@ -281,7 +280,7 @@ module infix-hardware {
|
||||
- Hardware support for 802.11ax
|
||||
- Compatible clients for full benefits
|
||||
|
||||
Note: 6GHz band always uses WiFi 6 regardless of this setting.";
|
||||
Note: 6GHz band always uses 802.11ax regardless of this setting.";
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -77,10 +77,8 @@ submodule infix-if-wifi {
|
||||
description
|
||||
"Applies only to interfaces of type 'wifi'.";
|
||||
}
|
||||
|
||||
container wifi {
|
||||
if-feature wifi;
|
||||
presence "Configure Wi-Fi virtual interface";
|
||||
|
||||
description
|
||||
"WiFi virtual interface configuration.
|
||||
@@ -99,6 +97,9 @@ submodule infix-if-wifi {
|
||||
must "derived-from-or-self(/iehw:hardware/iehw:component[iehw:name=current()]/iehw:class, 'ih:wifi')" {
|
||||
error-message "Referenced hardware component must be a WiFi radio (class 'ih:wifi')";
|
||||
}
|
||||
must "count(/if:interfaces/if:interface[wifi/radio = current()][not(wifi/access-point)]) <= 1" {
|
||||
error-message "Only one station or scan interface is allowed per radio";
|
||||
}
|
||||
|
||||
description
|
||||
"Reference to parent WiFi radio (PHY).
|
||||
@@ -134,10 +135,6 @@ submodule infix-if-wifi {
|
||||
container station {
|
||||
presence "Configure WiFi station (client) mode";
|
||||
|
||||
must "count(/if:interfaces/if:interface[wifi/radio = current()/../radio][wifi/station]) <= 1" {
|
||||
error-message "Only one station interface is allowed per radio";
|
||||
}
|
||||
|
||||
description
|
||||
"WiFi Station mode configuration.
|
||||
|
||||
@@ -401,23 +398,23 @@ submodule infix-if-wifi {
|
||||
}
|
||||
|
||||
leaf rx-packets {
|
||||
type uint32;
|
||||
type yang:counter64;
|
||||
description "Packets received from this client.";
|
||||
}
|
||||
|
||||
leaf tx-packets {
|
||||
type uint32;
|
||||
type yang:counter64;
|
||||
description "Packets transmitted to this client.";
|
||||
}
|
||||
|
||||
leaf rx-bytes {
|
||||
type uint32;
|
||||
type yang:counter64;
|
||||
units "octets";
|
||||
description "Bytes received from this client.";
|
||||
}
|
||||
|
||||
leaf tx-bytes {
|
||||
type uint32;
|
||||
type yang:counter64;
|
||||
units "octets";
|
||||
description "Bytes transmitted to this client.";
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ submodule infix-if-wireguard {
|
||||
}
|
||||
|
||||
leaf endpoint {
|
||||
type inet:host;
|
||||
type inet:ip-address;
|
||||
description
|
||||
"Peer endpoint address (IP address or DNS hostname).
|
||||
|
||||
|
||||
@@ -190,11 +190,11 @@ def get_wifi_phy_info():
|
||||
# Build descriptions
|
||||
for phy, info in phy_info.items():
|
||||
if info["iface"] and info["band"] != "Unknown":
|
||||
info["description"] = f"WiFi Radio {info['iface']} ({info['band']})"
|
||||
info["description"] = f"WiFi Radio {phy}"
|
||||
elif info["band"] != "Unknown":
|
||||
info["description"] = f"WiFi Radio ({info['band']})"
|
||||
elif info["iface"]:
|
||||
info["description"] = f"WiFi Radio {info['iface']}"
|
||||
info["description"] = f"WiFi Radio {phy}"
|
||||
else:
|
||||
info["description"] = "WiFi Radio"
|
||||
|
||||
|
||||
@@ -152,13 +152,13 @@ def parse_iw_stations(output):
|
||||
seconds = int(value.split()[0])
|
||||
current_station["connected-time"] = seconds
|
||||
elif key == "rx packets":
|
||||
current_station["rx-packets"] = int(value)
|
||||
current_station["rx-packets"] = value
|
||||
elif key == "tx packets":
|
||||
current_station["tx-packets"] = int(value)
|
||||
current_station["tx-packets"] = value
|
||||
elif key == "rx bytes":
|
||||
current_station["rx-bytes"] = int(value)
|
||||
current_station["rx-bytes"] = value
|
||||
elif key == "tx bytes":
|
||||
current_station["tx-bytes"] = int(value)
|
||||
current_station["tx-bytes"] = value
|
||||
elif key == "tx bitrate":
|
||||
# Format: "866.7 MBit/s ..." - extract speed and convert to 100kbit/s units
|
||||
speed_mbps = float(value.split()[0])
|
||||
|
||||
@@ -4,3 +4,6 @@
|
||||
|
||||
- name: mDNS allow/deny interfaces
|
||||
case: mdns_allow_deny/test.py
|
||||
|
||||
- name: mDNS reflector
|
||||
case: mdns_reflector/test.py
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
test.adoc
|
||||
@@ -0,0 +1,29 @@
|
||||
=== mDNS reflector
|
||||
|
||||
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/services/mdns/mdns_reflector]
|
||||
|
||||
==== Description
|
||||
|
||||
Verify the mDNS reflector functionality. The reflector forwards mDNS
|
||||
requests and responses between different network interfaces, allowing
|
||||
service discovery across network segments.
|
||||
|
||||
We verify operation with two scenarios:
|
||||
|
||||
1. Reflector enabled: mDNS traffic from host:data1 SHOULD be reflected
|
||||
to host:data2 and host:data3
|
||||
2. Reflector disabled: mDNS traffic from host:data1 should NOT be
|
||||
reflected to host:data2 and host:data3
|
||||
|
||||
==== Topology
|
||||
|
||||
image::topology.svg[mDNS reflector topology, align=center, scaledwidth=75%]
|
||||
|
||||
==== Sequence
|
||||
|
||||
. Set up topology and attach to target DUT
|
||||
. Configure device interfaces and enable mDNS
|
||||
. Verify mDNS from host:data1 is reflected to host:data2 and host:data3
|
||||
. Verify mDNS from host:data1 is not reflected after disabling reflector
|
||||
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env python3
|
||||
"""mDNS reflector
|
||||
|
||||
Verify the mDNS reflector functionality. The reflector forwards mDNS
|
||||
requests and responses between different network interfaces, allowing
|
||||
service discovery across network segments.
|
||||
|
||||
We verify operation with two scenarios:
|
||||
|
||||
1. Reflector enabled: mDNS traffic from host:data1 SHOULD be reflected
|
||||
to host:data2 and host:data3
|
||||
2. Reflector disabled: mDNS traffic from host:data1 should NOT be
|
||||
reflected to host:data2 and host:data3
|
||||
|
||||
"""
|
||||
import infamy
|
||||
from time import sleep
|
||||
|
||||
UNIQUE_MARKER = "infix-reflector-test"
|
||||
|
||||
|
||||
def mdns_reflect_test():
|
||||
"""Send mDNS from host:data1, check if it's reflected to host:data2/data3"""
|
||||
snif2 = infamy.Sniffer(hdata2, "port 5353")
|
||||
snif3 = infamy.Sniffer(hdata3, "port 5353")
|
||||
|
||||
with snif2, snif3:
|
||||
# Send mDNS query for "infix-reflector-test.local" (raw DNS packet)
|
||||
hdata1.runsh(
|
||||
"echo -ne '\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00"
|
||||
"\\x14infix-reflector-test\\x05local\\x00\\x00\\xff\\x00\\x01' | "
|
||||
"socat - UDP4-DATAGRAM:224.0.0.251:5353,bind=10.0.1.2"
|
||||
)
|
||||
sleep(5)
|
||||
|
||||
out2 = snif2.packets()
|
||||
out3 = snif3.packets()
|
||||
|
||||
return (UNIQUE_MARKER in out2, UNIQUE_MARKER in out3)
|
||||
|
||||
|
||||
def disable_reflector():
|
||||
"""Disable mDNS reflector"""
|
||||
dut.put_config_dict("infix-services", {
|
||||
"mdns": {
|
||||
"reflector": {
|
||||
"enabled": False
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Set up topology and attach to target DUT"):
|
||||
env = infamy.Env()
|
||||
dut = env.attach("dut", "mgmt")
|
||||
_, ddata1 = env.ltop.xlate("dut", "data1")
|
||||
_, ddata2 = env.ltop.xlate("dut", "data2")
|
||||
_, ddata3 = env.ltop.xlate("dut", "data3")
|
||||
_, hdata1 = env.ltop.xlate("host", "data1")
|
||||
_, hdata2 = env.ltop.xlate("host", "data2")
|
||||
_, hdata3 = env.ltop.xlate("host", "data3")
|
||||
|
||||
with test.step("Configure device interfaces and enable mDNS"):
|
||||
dut.put_config_dicts(
|
||||
{
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [{
|
||||
"name": ddata1,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "10.0.1.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": ddata2,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "10.0.2.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": ddata3,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "10.0.3.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"infix-services": {
|
||||
"mdns": {
|
||||
"enabled": True,
|
||||
"reflector": {
|
||||
"enabled": True
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
with infamy.IsolatedMacVlan(hdata1) as hdata1, \
|
||||
infamy.IsolatedMacVlan(hdata2) as hdata2, \
|
||||
infamy.IsolatedMacVlan(hdata3) as hdata3:
|
||||
hdata1.addip("10.0.1.2")
|
||||
hdata2.addip("10.0.2.2")
|
||||
hdata3.addip("10.0.3.2")
|
||||
|
||||
with test.step("Verify mDNS from host:data1 is reflected to host:data2 and host:data3"):
|
||||
hdata2.must_receive("port 5353")
|
||||
hdata3.must_receive("port 5353")
|
||||
|
||||
reflected = mdns_reflect_test()
|
||||
if reflected != (True, True):
|
||||
test.fail(f"Expected reflection (True, True), got {reflected}")
|
||||
|
||||
with test.step("Verify mDNS from host:data1 is not reflected after disabling reflector"):
|
||||
disable_reflector()
|
||||
hdata2.must_receive("port 5353")
|
||||
hdata3.must_receive("port 5353")
|
||||
|
||||
reflected = mdns_reflect_test()
|
||||
if reflected != (False, False):
|
||||
test.fail(f"Expected no reflection (False, False), got {reflected}")
|
||||
|
||||
test.succeed()
|
||||
@@ -0,0 +1,26 @@
|
||||
graph "1x4" {
|
||||
layout="neato";
|
||||
overlap="false";
|
||||
esep="+80";
|
||||
|
||||
node [shape=record, fontname="DejaVu Sans Mono, Book"];
|
||||
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
|
||||
|
||||
host [
|
||||
label="host | { <mgmt> mgmt | <data1> data1 | <data2> data2 | <data3> data3 }",
|
||||
pos="0,12!",
|
||||
requires="controller",
|
||||
];
|
||||
|
||||
dut [
|
||||
label="{ <mgmt> mgmt | <data1> data1 | <data2> data2 | <data3> data3 } | dut \n\n10.0.X.1/24",
|
||||
pos="10,12!",
|
||||
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
host:mgmt -- dut:mgmt [requires="mgmt", color="lightgray"]
|
||||
host:data1 -- dut:data1 [color=black, fontcolor=black, taillabel="10.0.1.2/24"]
|
||||
host:data2 -- dut:data2 [color=black, fontcolor=black, taillabel="10.0.2.2/24"]
|
||||
host:data3 -- dut:data3 [color=black, fontcolor=black, taillabel="10.0.3.2/24"]
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: 1x4 Pages: 1 -->
|
||||
<svg width="481pt" height="101pt"
|
||||
viewBox="0.00 0.00 481.03 101.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 97)">
|
||||
<title>1x4</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-97 477.03,-97 477.03,4 -4,4"/>
|
||||
<!-- host -->
|
||||
<g id="node1" class="node">
|
||||
<title>host</title>
|
||||
<polygon fill="none" stroke="black" points="0,-0.5 0,-92.5 108,-92.5 108,-0.5 0,-0.5"/>
|
||||
<text text-anchor="middle" x="25" y="-42.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
|
||||
<polyline fill="none" stroke="black" points="50,-0.5 50,-92.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-77.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="50,-69.5 108,-69.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
|
||||
<polyline fill="none" stroke="black" points="50,-46.5 108,-46.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
|
||||
<polyline fill="none" stroke="black" points="50,-23.5 108,-23.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data3</text>
|
||||
</g>
|
||||
<!-- dut -->
|
||||
<g id="node2" class="node">
|
||||
<title>dut</title>
|
||||
<polygon fill="none" stroke="black" points="308.03,-0.5 308.03,-92.5 473.03,-92.5 473.03,-0.5 308.03,-0.5"/>
|
||||
<text text-anchor="middle" x="337.03" y="-77.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="308.03,-69.5 366.03,-69.5 "/>
|
||||
<text text-anchor="middle" x="337.03" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
|
||||
<polyline fill="none" stroke="black" points="308.03,-46.5 366.03,-46.5 "/>
|
||||
<text text-anchor="middle" x="337.03" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
|
||||
<polyline fill="none" stroke="black" points="308.03,-23.5 366.03,-23.5 "/>
|
||||
<text text-anchor="middle" x="337.03" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data3</text>
|
||||
<polyline fill="none" stroke="black" points="366.03,-0.5 366.03,-92.5 "/>
|
||||
<text text-anchor="middle" x="419.53" y="-58.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">dut </text>
|
||||
<text text-anchor="middle" x="419.53" y="-27.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">10.0.X.1/24</text>
|
||||
</g>
|
||||
<!-- host--dut -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>host:mgmt--dut:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M108,-81.5C108,-81.5 307.53,-81.5 307.53,-81.5"/>
|
||||
</g>
|
||||
<!-- host--dut -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>host:data1--dut:data1</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M108,-58.5C108,-58.5 307.53,-58.5 307.53,-58.5"/>
|
||||
<text text-anchor="middle" x="149" y="-62.3" font-family="DejaVu Serif, Book" font-size="14.00">10.0.1.2/24</text>
|
||||
</g>
|
||||
<!-- host--dut -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>host:data2--dut:data2</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M108,-34.5C108,-34.5 307.53,-34.5 307.53,-34.5"/>
|
||||
<text text-anchor="middle" x="149" y="-38.3" font-family="DejaVu Serif, Book" font-size="14.00">10.0.2.2/24</text>
|
||||
</g>
|
||||
<!-- host--dut -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>host:data3--dut:data3</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M108,-11.5C108,-11.5 307.53,-11.5 307.53,-11.5"/>
|
||||
<text text-anchor="middle" x="149" y="-15.3" font-family="DejaVu Serif, Book" font-size="14.00">10.0.3.2/24</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -4,6 +4,7 @@ import signal
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import infamy.util as util
|
||||
|
||||
class Sniffer:
|
||||
"""Helper class for tcpdump"""
|
||||
@@ -20,7 +21,13 @@ class Sniffer:
|
||||
def __enter__(self):
|
||||
cmd = f"tshark -lni iface -w {self.pcap.name} {self.expr}"
|
||||
arg = cmd.split(" ")
|
||||
self.proc = self.netns.popen(arg, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
self.proc = self.netns.popen(arg,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True)
|
||||
|
||||
util.until(lambda: " -- Capture started." in self.proc.stderr.readline())
|
||||
|
||||
def __exit__(self, _, __, ___):
|
||||
if not self.proc:
|
||||
|
||||
Reference in New Issue
Block a user