blob: 6b7cc5a9d7cece46482cd26859c958bb1751f354 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"fmt"
"os"
"time"
"app/context"
"fidl/fuchsia/wlan/common"
wlan_service "fidl/fuchsia/wlan/service"
)
const (
cmdScan = "scan"
cmdConnect = "connect"
cmdDisconnect = "disconnect"
cmdStatus = "status"
cmdStartBSS = "start-bss"
cmdStopBSS = "stop-bss"
cmdStats = "stats"
)
type ToolApp struct {
ctx *context.Context
wlan *wlan_service.WlanInterface
}
// LINT.IfChange
func CbwToStr(cbw common.Cbw) string {
switch cbw {
case common.CbwCbw20:
return " "
case common.CbwCbw40:
return "+"
case common.CbwCbw40Below:
return "-"
case common.CbwCbw80:
return "V"
case common.CbwCbw160:
return "W"
case common.CbwCbw80P80:
return "P"
default:
return "(unknown CBW)"
}
}
func ChanToStr(ch common.WlanChan) string {
return fmt.Sprintf("%3d%s", ch.Primary, CbwToStr(ch.Cbw))
}
// LINT.ThenChange(//garnet/lib/wlan/common/channel.cpp)
func (a *ToolApp) Scan(seconds uint8) wlan_service.ErrCode {
expiry := 25 * time.Second
if seconds > 0 {
expiry = time.Duration(seconds) * time.Second
}
t := time.NewTimer(expiry)
rxed := make(chan struct{})
go func() {
res, err := a.wlan.Scan(wlan_service.ScanRequest{Timeout: seconds})
if err != nil {
fmt.Println("Error:", err)
} else if res.Error.Code != wlan_service.ErrCodeOk {
fmt.Println("Error:", res.Error.Description)
} else {
for _, ap := range *res.Aps {
prot := " "
if ap.IsSecure {
prot = "*"
}
compatStr := "[NoSupport]"
if ap.IsCompatible {
compatStr = ""
}
fmt.Printf("%12s %x (RSSI: %4d) Chan %s %v %q\n",
compatStr, ap.Bssid, ap.RssiDbm, ChanToStr(ap.Chan), prot, ap.Ssid)
}
}
rxed <- struct{}{}
}()
select {
case <-rxed:
// Received scan results.
case <-t.C:
fmt.Printf("Scan timed out; aborting.\n")
return wlan_service.ErrCodeInternal
}
return wlan_service.ErrCodeOk
}
func (a *ToolApp) Connect(ssid string, bssid string, passPhrase string,
seconds uint8) wlan_service.ErrCode {
if len(ssid) > 32 {
fmt.Println("ssid is too long")
return wlan_service.ErrCodeInvalidArgs
}
werr, err := a.wlan.Connect(wlan_service.ConnectConfig{
Ssid: ssid,
PassPhrase: passPhrase,
ScanInterval: seconds,
Bssid: bssid,
})
if err != nil {
fmt.Println("Error:", err)
return wlan_service.ErrCodeInternal
} else if werr.Code != wlan_service.ErrCodeOk {
fmt.Println("Error:", werr.Description)
return werr.Code
}
return wlan_service.ErrCodeOk
}
func (a *ToolApp) Disconnect() wlan_service.ErrCode {
werr, err := a.wlan.Disconnect()
if err != nil {
fmt.Println("Error:", err)
return wlan_service.ErrCodeInternal
} else if werr.Code != wlan_service.ErrCodeOk {
fmt.Println("Error:", werr.Description)
return werr.Code
}
return wlan_service.ErrCodeOk
}
func (a *ToolApp) StartBSS(ssid string, beaconPeriod int32, dtimPeriod int32,
channel uint8) wlan_service.ErrCode {
if len(ssid) > 32 {
fmt.Println("ssid is too long")
return wlan_service.ErrCodeInvalidArgs
}
if len(ssid) == 0 {
fmt.Println("ssid is too short")
return wlan_service.ErrCodeInvalidArgs
}
werr, err := a.wlan.StartBss(wlan_service.BssConfig{
Ssid: ssid,
BeaconPeriod: beaconPeriod,
DtimPeriod: dtimPeriod,
Channel: channel,
})
if err != nil {
fmt.Println("Error:", err)
return wlan_service.ErrCodeInternal
} else if werr.Code != wlan_service.ErrCodeOk {
fmt.Println("Error:", werr.Description)
return werr.Code
}
return wlan_service.ErrCodeOk
}
func (a *ToolApp) StopBSS() wlan_service.ErrCode {
werr, err := a.wlan.StopBss()
if err != nil {
fmt.Println("Error:", err)
return wlan_service.ErrCodeInternal
} else if werr.Code != wlan_service.ErrCodeOk {
fmt.Println("Error:", werr.Description)
return werr.Code
}
return wlan_service.ErrCodeOk
}
func (a *ToolApp) Status() wlan_service.ErrCode {
res, err := a.wlan.Status()
if err != nil {
fmt.Println("Error:", err)
return wlan_service.ErrCodeInternal
} else if res.Error.Code != wlan_service.ErrCodeOk {
fmt.Println("Error:", res.Error.Description)
return res.Error.Code
} else {
state := "unknown"
switch res.State {
case wlan_service.StateBss:
state = "starting-bss"
case wlan_service.StateQuerying:
state = "querying"
case wlan_service.StateScanning:
state = "scanning"
case wlan_service.StateJoining:
state = "joining"
case wlan_service.StateAuthenticating:
state = "authenticating"
case wlan_service.StateAssociating:
state = "associating"
case wlan_service.StateAssociated:
state = "associated"
default:
state = "unknown"
}
fmt.Printf("Status: %v\n", state)
if res.CurrentAp != nil {
ap := res.CurrentAp
prot := " "
if ap.IsSecure {
prot = "*"
}
compatStr := "[NoSupport]"
if ap.IsCompatible {
compatStr = ""
}
fmt.Printf("%12s %x (RSSI: %d) Chan %s %v %q\n",
compatStr, ap.Bssid, ap.RssiDbm, ChanToStr(ap.Chan), prot, ap.Ssid)
}
}
return wlan_service.ErrCodeOk
}
func (a *ToolApp) ShowStats() wlan_service.ErrCode {
result, err := a.wlan.Stats()
if err != nil {
fmt.Printf("Cannot get stats. Error: %+v\n", err)
return wlan_service.ErrCodeInternal
}
stats := result.Stats
fmt.Printf("Dispatcher stats:\n%+v\n", stats.DispatcherStats)
if stats.MlmeStats != nil {
fmt.Printf("\nMLME stats:\n%+v\n", stats.MlmeStats)
}
return wlan_service.ErrCodeOk
}
var Usage = func() {
fmt.Printf("Usage: %v %v [-t <timeout>]\n", os.Args[0], cmdScan)
fmt.Printf(" %v %v [-p <passphrase>] [-t <timeout>] [-b <bssid>] ssid\n", os.Args[0], cmdConnect)
fmt.Printf(" %v %v\n", os.Args[0], cmdDisconnect)
fmt.Printf(" %v %v\n", os.Args[0], cmdStatus)
fmt.Printf(" %v %v [-b <beacon period>] [-d <DTIM period>] [-c channel] ssid\n", os.Args[0], cmdStartBSS)
fmt.Printf(" %v %v\n", os.Args[0], cmdStopBSS)
fmt.Printf(" %v %v\n", os.Args[0], cmdStats)
}
func mainFunc() wlan_service.ErrCode {
scanFlagSet := flag.NewFlagSet(cmdScan, flag.ExitOnError)
scanTimeout := scanFlagSet.Int("t", 0, "scan timeout (1 - 255 seconds)")
connectFlagSet := flag.NewFlagSet(cmdConnect, flag.ExitOnError)
connectScanTimeout := connectFlagSet.Int("t", 0, "scan timeout (1 to 255 seconds)")
connectPassPhrase := connectFlagSet.String("p", "", "pass-phrase (8 to 63 ASCII characters")
connectBSSID := connectFlagSet.String("b", "", "BSSID")
a := &ToolApp{ctx: context.CreateFromStartupInfo()}
req, pxy, err := wlan_service.NewWlanInterfaceRequest()
if err != nil {
panic(err.Error())
}
a.wlan = pxy
defer a.wlan.Close()
a.ctx.ConnectToEnvService(req)
if len(os.Args) < 2 {
Usage()
return wlan_service.ErrCodeInvalidArgs
}
cmd := os.Args[1]
switch cmd {
case cmdScan:
scanFlagSet.Parse(os.Args[2:])
if *scanTimeout != 0 && !IsValidTimeout(*scanTimeout) {
scanFlagSet.PrintDefaults()
return wlan_service.ErrCodeInvalidArgs
}
if scanFlagSet.NArg() != 0 {
Usage()
return wlan_service.ErrCodeInvalidArgs
}
return a.Scan(uint8(*scanTimeout))
case cmdConnect:
connectFlagSet.Parse(os.Args[2:])
if *connectScanTimeout != 0 && !IsValidTimeout(*connectScanTimeout) {
connectFlagSet.PrintDefaults()
return wlan_service.ErrCodeInvalidArgs
}
if *connectPassPhrase != "" && !IsValidPSKPassPhrase(*connectPassPhrase) {
connectFlagSet.PrintDefaults()
return wlan_service.ErrCodeInvalidArgs
}
if *connectBSSID != "" && !IsValidBSSID(*connectBSSID) {
connectFlagSet.PrintDefaults()
return wlan_service.ErrCodeInvalidArgs
}
if connectFlagSet.NArg() != 1 {
Usage()
return wlan_service.ErrCodeInvalidArgs
}
ssid := connectFlagSet.Arg(0)
return a.Connect(ssid, *connectBSSID, *connectPassPhrase, uint8(*connectScanTimeout))
case cmdDisconnect:
disconnectFlagSet := flag.NewFlagSet(cmdDisconnect, flag.ExitOnError)
disconnectFlagSet.Parse(os.Args[2:])
if disconnectFlagSet.NArg() != 0 {
Usage()
return wlan_service.ErrCodeInvalidArgs
}
return a.Disconnect()
case cmdStatus:
statusFlagSet := flag.NewFlagSet(cmdStatus, flag.ExitOnError)
statusFlagSet.Parse(os.Args[2:])
if statusFlagSet.NArg() != 0 {
Usage()
return wlan_service.ErrCodeInvalidArgs
}
return a.Status()
case cmdStartBSS:
startBSSFlagSet := flag.NewFlagSet(cmdStartBSS, flag.ExitOnError)
startBSSBeaconPeriod := startBSSFlagSet.Int("b", 100, "Beacon period")
startBSSDTIMPeriod := startBSSFlagSet.Int("d", 1, "DTIM period")
startBSSChannel := startBSSFlagSet.Int("c", 48, "Channel")
startBSSFlagSet.Parse(os.Args[2:])
if startBSSFlagSet.NArg() != 1 {
Usage()
return wlan_service.ErrCodeInvalidArgs
}
ssid := startBSSFlagSet.Arg(0)
return a.StartBSS(ssid, int32(*startBSSBeaconPeriod), int32(*startBSSDTIMPeriod), uint8(*startBSSChannel))
case cmdStopBSS:
stopBSSFlagSet := flag.NewFlagSet(cmdStartBSS, flag.ExitOnError)
stopBSSFlagSet.Parse(os.Args[2:])
if stopBSSFlagSet.NArg() != 0 {
Usage()
return wlan_service.ErrCodeInvalidArgs
}
return a.StopBSS()
case cmdStats:
statusFlagSet := flag.NewFlagSet(cmdStatus, flag.ExitOnError)
statusFlagSet.Parse(os.Args[2:])
if statusFlagSet.NArg() != 0 {
Usage()
return wlan_service.ErrCodeInvalidArgs
}
return a.ShowStats()
default:
Usage()
}
return wlan_service.ErrCodeOk
}
func main() {
os.Exit(int(mainFunc()))
}
func IsValidTimeout(timeout int) bool {
if timeout < 1 || timeout > 255 {
fmt.Println("Timeout must be 1 to 255 seconds")
return false
}
return true
}
func IsValidPSKPassPhrase(passPhrase string) bool {
// len(s) can be used because PSK always operates on ASCII characters.
if len(passPhrase) < 8 || len(passPhrase) > 63 {
fmt.Println("Pass phrase must be 8 to 63 characters")
return false
}
for _, c := range passPhrase {
if c < 32 || c > 126 {
fmt.Println("Pass phrase must be ASCII characters")
return false
}
}
return true
}
func IsValidBSSID(bssid string) bool {
if len(bssid) != 17 {
fmt.Println("BSSID must be 17 characters")
return false
}
for i, c := range bssid {
if (c < 'a' || c > 'f') && (c < 'A' || c > 'F') {
if c < '0' || c > '9' {
if c != ':' {
fmt.Println("BSSID must be hexadecimal")
return false
} else if i%3 != 2 {
fmt.Println("BSSID must be of the form xx:xx:xx:xx:xx:xx")
return false
}
}
}
}
return true
}