blob: 95effa1eeb679541cd1d1d676a2cd6ce83666b0a [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"
"github.com/golang/protobuf/ptypes/empty"
"go.chromium.org/tast-tests/cros/common/shillconst"
tdreq "go.chromium.org/tast-tests/cros/common/testdevicerequirements"
"go.chromium.org/tast-tests/cros/common/wifi/security"
"go.chromium.org/tast-tests/cros/common/wifi/security/owe"
"go.chromium.org/tast-tests/cros/remote/wificell"
ap "go.chromium.org/tast-tests/cros/remote/wificell/hostapd"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/testing"
"go.chromium.org/tast/core/testing/hwdep"
)
type apConf struct {
apOpts []ap.Option
secConfig security.ConfigFactory
}
type apDef struct {
name string
configs []apConf
expectedSecurity string
}
func init() {
testing.AddTest(&testing.Test{
Func: OweUpgrade,
Desc: "Verifies connectivity to network that has security upgraded from None to TransOwe and then to Owe",
Contacts: []string{
"chromeos-wifi-champs@google.com", // WiFi oncall rotation
"andrzejo@google.com", // author
},
BugComponent: "b:893827", // ChromeOS > Platform > Connectivity > WiFi
Attr: []string{"group:wificell", "wificell_func"},
ServiceDeps: []string{wificell.ShillServiceName},
Fixture: wificell.FixtureID(wificell.TFFeaturesCapture),
HardwareDeps: hwdep.D(hwdep.WifiNotMarvell()),
Requirements: []string{tdreq.WiFiGenSupportWiFi, tdreq.WiFiGenSupportPMF, tdreq.WiFiProcPassMatfunc, tdreq.WiFiProcPassMatfuncBeforeUpdates},
VariantCategory: `{"name": "WifiBtChipset_Soc_Kernel"}`,
})
}
// OweUpgrade checks connectivity to a public network that is:
// - initially configured to be an open network,
// - then has configuration changed to be transitional mode OWE,
// - and then switches to the pure OWE network.
// For each configuration it is expected that we will be able to connect w/o any
// problems so the steps are:
// 1. Configure an open AP.
// 2. Connect, test connection, disconnect and deconfigure the AP.
// 3. Configure transitional mode OWE AP with public BSS having the same SSID
// as in point 1 and the hidden one using different name.
// 4. Connect, test connection, disconnect and deconfigure both BSSes.
// 5. Configure a pure OWE AP using the same SSID as in point 1.
// 6. Connect, test connection, disconnect and deconfigure the AP.
// 7. Configure an open AP again (simulating a downgrade).
// 8. Connect, test connection, disconnect and deconfigure the AP.
func OweUpgrade(ctx context.Context, s *testing.State) {
tf := s.FixtValue().(*wificell.TestFixture)
servicePath := ""
// Get the name of the DUT WiFi interface (for BSS flushing/discovery).
clientIface, err := tf.ClientInterface(ctx)
if err != nil {
s.Fatal("Failed to get DUT interface: ", err)
}
// Additional scans are not a problem during security upgrades but for the downgrade
// (owe->public) there is possible following race scenario:
// - we deconfigure OWE AP,
// - flush the BSSes,
// - the background scan results come with BSSID of just deconfigured AP
// Then the security of the service will be still be matching the higher security (OWE)
// while the actual connection will be made to the public endpoint (since those late results
// are not really available). To avoid that problem let's turn off autonomous scanning for
// the test and rely on explicit requests.
ctx, restoreBgAndFg, err := tf.DUTWifiClient(wificell.DefaultDUT).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)
}
}()
connectAP := func(ctx context.Context, apDef *apDef) (retErr error) {
var ap *wificell.APIface
var cleanupCtx context.Context
var cancel context.CancelFunc
for _, cfg := range apDef.configs {
ap, err = tf.ConfigureAP(ctx, cfg.apOpts, cfg.secConfig)
if err != nil {
return errors.Wrap(err, "failed to configure the AP")
}
s.Log("Configured BSS with BSSID: ", ap.Config().BSSID)
cleanupCtx = ctx
ctx, cancel = tf.ReserveForDeconfigAP(ctx, ap)
defer cancel()
defer func(ctx context.Context) {
if err := tf.DeconfigAP(ctx, ap); err != nil {
retErr = errors.Wrap(err, "failed to deconfig the AP")
}
}(cleanupCtx)
}
// Discover the lastly configured BSS to make sure everything is up and running.
bssid := ap.Config().BSSID
ssid := ap.Config().SSID
if err := tf.DUTWifiClient(wificell.DefaultDUT).DiscoverBSSID(ctx, bssid, clientIface, []byte(ssid)); err != nil {
return errors.Wrap(err, "failed to discover AP")
}
connResp, err := tf.ConnectWifiAPFromDUT(ctx, wificell.DefaultDUT, ap)
if err != nil {
return errors.Wrap(err, "failed to connect to WiFi")
}
cleanupCtx = ctx
ctx, cancel = tf.ReserveForDisconnect(ctx)
defer cancel()
defer func(ctx context.Context) {
if err := tf.DisconnectDUTFromWifi(ctx, wificell.DefaultDUT); err != nil {
retErr = errors.Wrap(err, "failed to disconnect WiFi")
}
}(cleanupCtx)
if servicePath == "" {
servicePath = connResp.ServicePath
} else if servicePath != connResp.ServicePath {
return errors.Errorf("Service path has changed: got %s, want %s", connResp.ServicePath, servicePath)
}
srvcResp, err := tf.DUTWifiClient(wificell.DefaultDUT).QueryService(ctx)
if err != nil {
return errors.Wrap(err, "failed to get service properties")
}
s.Logf("Connected to %s with Security: %s", connResp.ServicePath, srvcResp.Wifi.Security)
if srvcResp.Wifi.Security != apDef.expectedSecurity {
return errors.Errorf("Wrong service security: got %s, want %s",
srvcResp.Wifi.Security, apDef.expectedSecurity)
}
return nil
}
ssid := ap.RandomSSID("TAST_TEST_")
commonApOpts := []ap.Option{ap.SSID(ssid), ap.Mode(ap.Mode80211g), ap.Channel(1)}
bssOpen, err := ap.RandomMAC()
if err != nil {
s.Fatal("Failed to generate random BSSID: ", err)
}
bssOwe, err := ap.RandomMAC()
if err != nil {
s.Fatal("Failed to generate random BSSID: ", err)
}
oweSsid := ap.RandomSSID("TAST_TEST_OWE_")
apDefs := []apDef{
{
name: "public",
configs: []apConf{{
apOpts: commonApOpts,
}},
expectedSecurity: shillconst.SecurityNone,
},
{
name: "transitional OWE",
configs: []apConf{{
apOpts: append(commonApOpts[1:], ap.SSID(oweSsid), ap.BSSID(bssOwe.String())),
secConfig: owe.NewConfigFactory(owe.ModeTransOWE, owe.SSID(ssid), owe.BSSID(bssOpen)),
}, { // Open BSS should be last (so that hidden is ready when open announces it).
apOpts: append(commonApOpts, ap.BSSID(bssOpen.String())),
secConfig: owe.NewConfigFactory(owe.ModeTransOpen, owe.SSID(oweSsid), owe.BSSID(bssOwe)),
}},
expectedSecurity: shillconst.SecurityTransOWE,
}, {
name: "pure OWE",
configs: []apConf{{
apOpts: commonApOpts,
secConfig: owe.NewConfigFactory(owe.ModePureOWE),
}},
expectedSecurity: shillconst.SecurityOWE,
},
}
apNum := len(apDefs)
// This +1 is intentional - it is supposed to test a downgrade back to
// public (for example too many user complains after an upgrade to owe
// and this causes the admin to revert the upgrade and go back to an
// open network).
for i := 0; i < apNum+1; i++ {
idx := i % apNum
s.Log("Testing connection to: ", apDefs[idx].name)
if err = connectAP(ctx, &apDefs[idx]); err != nil {
s.Fatal("Failure during AP connection: ", err)
}
// The connectAP function above disconnects and deconfigures the
// AP, but on disconnect shill triggers the scan automatically,
// so it might catch existing BSSes right before they get
// removed. If the scan takes long time these results might
// come after we have flushed BSSes below and after configuring
// the new APs (in the next connectAP invocation) giving
// incorrect view of the RF environment. To avoid that let's
// wait until the scan finishes before continuing.
if _, err := tf.DUTWifiClient(wificell.DefaultDUT).WaitScanIdle(ctx, &empty.Empty{}); err != nil {
s.Fatal("Failed to wait for current scan to be done: ", err)
}
s.Log("Flushing BSS cache")
if err := tf.DUTWifiClient(wificell.DefaultDUT).FlushBSS(ctx, clientIface, 0); err != nil {
s.Fatal("Failed to flush BSS list: ", err)
}
}
}