blob: ff21503fea2f446420c1e747a7871654a7bc4264 [file] [log] [blame]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package wifi
import (
"context"
"time"
"github.com/google/gopacket/layers"
"go.chromium.org/tast-tests/cros/common/shillconst"
"go.chromium.org/tast-tests/cros/common/tbdep"
tdreq "go.chromium.org/tast-tests/cros/common/testdevicerequirements"
"go.chromium.org/tast-tests/cros/common/wifi/security/wpa"
"go.chromium.org/tast-tests/cros/remote/network/ip"
"go.chromium.org/tast-tests/cros/remote/wificell"
"go.chromium.org/tast-tests/cros/remote/wificell/dutcfg"
"go.chromium.org/tast-tests/cros/remote/wificell/hostapd"
"go.chromium.org/tast-tests/cros/remote/wificell/pcap"
"go.chromium.org/tast-tests/cros/services/cros/wifi"
"go.chromium.org/tast/core/ctxutil"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/testing"
"go.chromium.org/tast/core/testing/hwdep"
)
// scanAndConnect6GHzTestCase holds parameters of a ScanAndConnect6GHz test variant.
type scanAndConnect6GHzTestCase struct {
// isOutOfBand indicates if it is out-of-band discovery
isOutOfBand bool
}
func init() {
testing.AddTest(&testing.Test{
Func: ScanAndConnect6GHz,
Desc: "Verifies that the DUT supports both in-band and out-of-band discovery",
Contacts: []string{
"chromeos-wifi-champs@google.com", // WiFi oncall rotation; or http://b/new?component=893827
"kaidong@google.com", // Test author
},
BugComponent: "b:893827", // ChromeOS > Platform > Connectivity > WiFi
Attr: []string{"group:wificell", "wificell_func", "wificell_unstable"},
TestBedDeps: []string{tbdep.Wificell, tbdep.WifiStateNormal, tbdep.BluetoothStateNormal, tbdep.PeripheralWifiStateWorking, "wifi_router_features:WIFI_ROUTER_FEATURE_IEEE_802_11_AX_E"},
ServiceDeps: []string{
wificell.ShillServiceName,
},
HardwareDeps: hwdep.D(hwdep.Wifi80211ax6E()),
Fixture: wificell.FixtureID(wificell.TFFeaturesCapture),
Requirements: []string{tdreq.WiFiGenSupportWiFi, tdreq.WiFiProcPassFW, tdreq.WiFiProcPassAVL, tdreq.WiFiProcPassAVLBeforeUpdates, tdreq.WiFiProcPassMatfunc, tdreq.WiFiProcPassMatfuncBeforeUpdates},
Params: []testing.Param{
{
Name: "in_band",
Val: scanAndConnect6GHzTestCase{
isOutOfBand: false,
},
},
{
Name: "out_of_band",
Val: scanAndConnect6GHzTestCase{
isOutOfBand: true,
},
},
},
})
}
func ScanAndConnect6GHz(ctx context.Context, s *testing.State) {
/*
This test verifies WiFi 6E devices supports both in-band and out-of-band discovery.
1. Turn off the background scan on the DUT
2. Turn off MAC randomization on the DUT
3. Set WiFi RequestScan type on the DUT
4. Set up the APs. The 5 GHz AP is enabled only if the 6 GHz AP is co-located
5. Connect the DUT to the 6 GHz AP
6. Verify the RequestScan type is as expected by checking probe requests in the pcap file
*/
const (
apChannel = 193
)
tf := s.FixtValue().(*wificell.TestFixture)
tc := s.Param().(scanAndConnect6GHzTestCase)
clientIface, err := tf.ClientInterface(ctx)
if err != nil {
s.Fatal("Unable to get client interface name: ", err)
}
ipr := ip.NewRemoteRunner(s.DUT().Conn())
dutMAC, err := ipr.MAC(ctx, clientIface)
if err != nil {
s.Fatal("Failed to get MAC of WiFi interface: ", err)
}
// Turn off MAC randomization for scans, so that MAC address used for scan is not randomized,
// the probe requests from the DUT can be identified by its MAC address
resp, err := tf.WifiClient().SetMACRandomize(ctx, &wifi.SetMACRandomizeRequest{Enable: false})
if err != nil {
s.Fatal("Failed to turn off MAC randomization: ", err)
}
if resp.OldSetting {
testing.ContextLog(ctx, "Switched MAC randomization for scans to false")
// Restore the setting on leaving.
defer func(ctx context.Context) {
if _, err := tf.WifiClient().SetMACRandomize(ctx, &wifi.SetMACRandomizeRequest{Enable: true}); err != nil {
s.Fatal("Failed to restore MAC randomization setting back: ", err)
}
}(ctx)
}
// Turn off background and foreground scans, so that all the WiFi scans are triggered by shill::Manager::RequestScan()
ctx, restoreBgAndFg, err := tf.WifiClient().TurnOffBgAndFgscan(ctx)
if err != nil {
s.Fatal("Failed to turn off the background and/or foreground scan: ", err)
}
defer func() {
if err := restoreBgAndFg(); err != nil {
s.Error("Failed to restore the background and/or foreground scan config: ", err)
}
}()
ctx, cancel := ctxutil.Shorten(ctx, time.Second)
defer cancel()
capturer, err := func(ctx context.Context) (*pcap.Capturer, error) {
// Set WiFi request scan type on the DUT
var requestScanType string
if tc.isOutOfBand {
requestScanType = shillconst.WiFiRequestScanTypeActive
} else {
requestScanType = shillconst.WiFiRequestScanTypePassive
}
originalRequestScanType, err := tf.WifiClient().GetRequestScanTypeProperty(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to get WiFi RequestScan type")
}
if originalRequestScanType != requestScanType {
defer func(ctx context.Context) {
if err := tf.WifiClient().SetRequestScanTypeProperty(ctx, originalRequestScanType); err != nil {
s.Errorf("Failed to reset WiFi RequestScan type to %s: %v", originalRequestScanType, err)
}
s.Log("Reset WiFi RequestScan type to ", originalRequestScanType)
}(ctx)
ctx, cancel = ctxutil.Shorten(ctx, 500*time.Millisecond)
defer cancel()
if err := tf.WifiClient().SetRequestScanTypeProperty(ctx, requestScanType); err != nil {
s.Fatalf("Failed to set WiFi RequestScan type to %s: %v", requestScanType, err)
}
s.Log("Set WiFi RequestScan type to ", requestScanType)
}
ap6GHzOpts := []hostapd.Option{hostapd.SSID(hostapd.RandomSSID("6GHz_")), hostapd.Mode(hostapd.Mode80211axPure),
hostapd.HTCaps(hostapd.HTCapHT20), hostapd.HEChWidth(hostapd.HEChWidth20Or40), hostapd.Is6GHz(),
hostapd.Channel(apChannel), hostapd.SpectrumManagement(), hostapd.PMF(hostapd.PMFRequired)}
secConfFac := wpa.NewConfigFactory("chromeos",
wpa.Mode(wpa.ModePureWPA3), wpa.Ciphers2(wpa.CipherCCMP))
var apConfigs []hostapd.ApConfig
if tc.isOutOfBand {
// Set up the 5 GHz AP for an out-of-band discovery, i.e., the 6 GHz AP is co-located
ap5GHzOpts := []hostapd.Option{hostapd.SSID(hostapd.RandomSSID("5GHz_")), hostapd.Mode(hostapd.Mode80211acPure),
hostapd.HTCaps(hostapd.HTCapHT20), hostapd.Channel(40), hostapd.SpectrumManagement()}
apConfigs = append(apConfigs, hostapd.ApConfig{ApOpts: ap6GHzOpts, SecConfFac: secConfFac})
apConfigs = append(apConfigs, hostapd.ApConfig{ApOpts: ap5GHzOpts, SecConfFac: nil})
} else {
// Intel driver doesn't support FILS Discovery frame or unsolicited broadcast Probe Response frame transmission,
// set beacon interval to 20 TUs so that the DUT receives beacon frames within the channel dwell time
ap6GHzOpts = append(ap6GHzOpts, hostapd.BeaconInterval(20))
apConfigs = append(apConfigs, hostapd.ApConfig{ApOpts: ap6GHzOpts, SecConfFac: secConfFac})
}
ap, err := tf.ConfigureAPOnRouterIDWithConfs(ctx, 0, apConfigs, "", true, false, false)
if err != nil {
return nil, errors.Wrap(err, "failed to configure the APs")
}
defer func(ctx context.Context) {
if err := tf.DeconfigAP(ctx, ap); err != nil {
s.Error("Failed to deconfig the APs: ", err)
}
}(ctx)
ctx, cancel := tf.ReserveForDeconfigAP(ctx, ap)
defer cancel()
var conf *hostapd.Config
for _, conf = range ap.Configs() {
if conf.Is6GHz && conf.Channel == apChannel {
break
}
}
if conf == nil {
s.Fatal("Failed to get the Config of the 6 GHz AP")
}
_, err = tf.ConnectWifiFromDUT(ctx, wificell.DefaultDUT, conf.SSID, dutcfg.ConnSecurity(conf.SecurityConfig))
if err != nil {
return nil, errors.Wrap(err, "failed to connect to WiFi")
}
defer func(ctx context.Context) {
if err := tf.CleanDisconnectWifi(ctx); err != nil {
s.Error("Failed to disconnect WiFi: ", err)
}
}(ctx)
ctx, cancel = tf.ReserveForDisconnect(ctx)
defer cancel()
s.Log("Connected to the 6 GHz AP")
// The AP may have two capturers for both 5 GHz and 6 GHz BSSs
capturers, ok := tf.Capturers(ap)
if !ok {
return nil, errors.Wrap(err, "failed to get the capturers")
}
freq, err := hostapd.ChannelToFrequencyWithBand(apChannel, true)
if err != nil {
s.Fatal(err, "Failed to get the frequency of the 6 GHz AP")
}
capturer, ok := capturers[freq]
if !ok {
return nil, errors.Wrapf(err, "failed to get the capturer of frequency %d", freq)
}
return capturer, nil
}(ctx)
if err != nil {
s.Fatal("Failed to scan and connect to the 6 GHz AP: ", err)
}
pcapPath, err := capturer.PacketPath(ctx)
if err != nil {
s.Fatal("Failed to get packet capture: ", err)
}
probeReqFilter := []pcap.Filter{
pcap.TypeFilter(layers.LayerTypeDot11MgmtProbeReq, nil),
pcap.TransmitterAddress(dutMAC),
}
probeReqPackets, err := pcap.ReadPackets(pcapPath, probeReqFilter...)
if err != nil {
s.Fatal("Failed to read probe requests from packet capture: ", err)
}
if tc.isOutOfBand && len(probeReqPackets) == 0 {
s.Fatal("The DUT did not perform active scan during out-of-band discovery")
}
if !tc.isOutOfBand && len(probeReqPackets) > 0 {
s.Fatal("The DUT did not perform passive scan during in-band discovery")
}
}