Skip to content

Commit bceab90

Browse files
networkmanager: ensure Wi-Fi VIFs are created before NM activates connections
The wireless driver typically creates only the base STA interface (e.g., wlP4p1s0) at probe time. NetworkManager does not create Wi-Fi interfaces - it only manages existing ones. In multi-VIF deployments (e.g., AP+AP+STA), the connection profiles reference VIFs that do not exist at boot, causing NM to fail activating these connections. Add a systemd oneshot service to create the required VIFs before NM activates the connections. The service is not enabled by default. When enabled, it tries to wait for the STA to connect first before creating AP VIFs, avoiding mutual interference between AP operation and STA's scanning/association process on the shared radio. Signed-off-by: Wei Zhang <wei.zhang@oss.qualcomm.com>
1 parent 4297bce commit bceab90

3 files changed

Lines changed: 212 additions & 0 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[Unit]
2+
Description=Create WiFi VIFs based on NetworkManager profiles
3+
After=NetworkManager.service
4+
Wants=NetworkManager.service
5+
6+
[Service]
7+
Type=oneshot
8+
ExecStart=/usr/libexec/nm-wifi-vif-create.sh
9+
RemainAfterExit=yes
10+
11+
[Install]
12+
WantedBy=multi-user.target
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#!/bin/sh
2+
# SPDX-License-Identifier: MIT
3+
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4+
# Create virtual WiFi interfaces for NetworkManager system-connections
5+
6+
TAG="wifi-vif"
7+
CONN_DIR="/etc/NetworkManager/system-connections"
8+
TIMEOUT=30
9+
10+
# ------------------------------------------------------------
11+
# Logging helpers
12+
# ------------------------------------------------------------
13+
log() {
14+
logger -t "$TAG" "$@"
15+
}
16+
17+
log_err() {
18+
logger -t "$TAG" "ERROR: $@"
19+
}
20+
21+
# ------------------------------------------------------------
22+
# Helper functions
23+
# ------------------------------------------------------------
24+
25+
# Find NM interface-name that actually exists in `iw dev`
26+
get_base_iface() {
27+
iw dev 2>/dev/null | awk '/Interface/ {print $2}' |
28+
while read -r iw_iface; do
29+
for f in "$CONN_DIR"/*.nmconnection; do
30+
[ -f "$f" ] || continue
31+
nm_iface=$(sed -n 's/^interface-name=//p' "$f" | head -n1)
32+
[ "$iw_iface" = "$nm_iface" ] && echo "$iw_iface"
33+
done
34+
done | head -n1
35+
}
36+
37+
# Verify BASE_IFACE is in STA mode and wait until it associates.
38+
wait_for_sta_connected() {
39+
iface="$1"
40+
41+
# Bail out early if not in managed (STA) mode
42+
iw dev "$iface" info 2>/dev/null | grep -q "type managed" || {
43+
log "WARNING: $iface is not in STA mode, skip waiting"
44+
return 1
45+
}
46+
47+
log "Waiting for $iface to connect (timeout=${TIMEOUT}s)"
48+
i=0
49+
while [ "$i" -lt "$TIMEOUT" ]; do
50+
iw dev "$iface" link 2>/dev/null | grep -q "Connected to" && {
51+
log "$iface is connected"
52+
return 0
53+
}
54+
sleep 1; i=$((i + 1))
55+
done
56+
57+
log_err "Timeout waiting for $iface to connect"
58+
return 1
59+
}
60+
61+
# Wait until any interface enters AP mode
62+
wait_for_ap() {
63+
i=0
64+
log "Waiting for AP mode (timeout=${TIMEOUT}s)"
65+
66+
while [ "$i" -lt "$TIMEOUT" ]; do
67+
for iface in $(iw dev 2>/dev/null | awk '/Interface/ {print $2}'); do
68+
if iw dev "$iface" info 2>/dev/null | grep -q "type AP"; then
69+
log "AP mode detected on interface $iface"
70+
return 0
71+
fi
72+
done
73+
74+
sleep 1
75+
i=$((i + 1))
76+
done
77+
78+
log_err "Timeout waiting for AP mode"
79+
return 1
80+
}
81+
82+
# Get configured mode (ap / infrastructure) for an interface
83+
get_vif_mode() {
84+
iface="$1"
85+
86+
for f in "$CONN_DIR"/*.nmconnection; do
87+
[ -f "$f" ] || continue
88+
grep -qx "interface-name=$iface" "$f" || continue
89+
90+
sed -n '/^\[wifi\]$/,/^\[/{s/^mode=//p}' "$f" | head -n1
91+
return
92+
done
93+
94+
echo "infrastructure"
95+
}
96+
97+
# Collect VIFs declared in NM (excluding base iface)
98+
collect_vifs() {
99+
for f in "$CONN_DIR"/*.nmconnection; do
100+
[ -f "$f" ] || continue
101+
102+
conn_type=$(sed -n 's/^type=//p' "$f" | head -n1)
103+
iface=$(sed -n 's/^interface-name=//p' "$f" | head -n1)
104+
105+
[ "$conn_type" = "wifi" ] &&
106+
[ -n "$iface" ] &&
107+
[ "$iface" != "$BASE_IFACE" ] &&
108+
echo "$iface"
109+
done
110+
}
111+
112+
# ------------------------------------------------------------
113+
# Main
114+
# ------------------------------------------------------------
115+
116+
log "==== start ===="
117+
118+
BASE_IFACE=$(get_base_iface)
119+
120+
log "BASE_IFACE=$BASE_IFACE"
121+
log "CONN_DIR=$CONN_DIR"
122+
log "TIMEOUT=$TIMEOUT"
123+
124+
[ -z "$BASE_IFACE" ] && {
125+
log_err "Failed to detect base WiFi interface"
126+
exit 1
127+
}
128+
129+
vif_list=$(
130+
collect_vifs |
131+
sort -u |
132+
while read -r iface; do
133+
if ip link show "$iface" >/dev/null 2>&1; then
134+
log "Interface $iface already exists, skip"
135+
else
136+
log "Interface $iface needs to be created"
137+
echo "$iface"
138+
fi
139+
done
140+
)
141+
142+
vif_count=$(printf "%s\n" "$vif_list" | grep -c .)
143+
144+
log "vif_list:"
145+
printf "%s\n" "$vif_list" | while read -r l; do log " $l"; done
146+
log "vif_count=$vif_count"
147+
148+
# Warn if BASE_IFACE is not STA mode; poll until connected or timeout
149+
wait_for_sta_connected "$BASE_IFACE" || true
150+
151+
case "$vif_count" in
152+
0)
153+
log "No virtual interfaces to create"
154+
;;
155+
1)
156+
iface=$(printf "%s\n" "$vif_list")
157+
iw dev "$BASE_IFACE" interface add "$iface" type managed || exit 1
158+
;;
159+
2)
160+
first=$(printf "%s\n" "$vif_list" | sed -n '1p')
161+
second=$(printf "%s\n" "$vif_list" | sed -n '2p')
162+
163+
# Ensure AP interface is created first
164+
[ "$(get_vif_mode "$first")" != "ap" ] && {
165+
tmp="$first"; first="$second"; second="$tmp"
166+
}
167+
168+
iw dev "$BASE_IFACE" interface add "$first" type managed || exit 1
169+
wait_for_ap || exit 1
170+
iw dev "$BASE_IFACE" interface add "$second" type managed || exit 1
171+
;;
172+
*)
173+
log_err "Too many VIFs requested ($vif_count), max supported is 2"
174+
exit 1
175+
;;
176+
esac
177+
178+
log "==== end ===="
179+
exit 0
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:"
2+
3+
SRC_URI:append:qcom-distro = " \
4+
file://nm-wifi-vif-create.sh \
5+
file://nm-wifi-vif-create.service \
6+
"
7+
8+
do_install:append:qcom-distro() {
9+
install -d ${D}${libexecdir}
10+
install -m 0755 ${UNPACKDIR}/nm-wifi-vif-create.sh \
11+
${D}${libexecdir}/nm-wifi-vif-create.sh
12+
13+
install -d ${D}${systemd_system_unitdir}
14+
install -m 0644 ${UNPACKDIR}/nm-wifi-vif-create.service \
15+
${D}${systemd_system_unitdir}/nm-wifi-vif-create.service
16+
}
17+
18+
FILES:${PN}:append:qcom-distro = " \
19+
${libexecdir}/nm-wifi-vif-create.sh \
20+
${systemd_system_unitdir}/nm-wifi-vif-create.service \
21+
"

0 commit comments

Comments
 (0)