Fix cli DHCP options, started WiFi config for CLI, wrapped wpa_supplicant into service

This commit is contained in:
mame82 2018-05-28 14:34:59 +00:00
parent 19d352fb37
commit 848fd50815
6 changed files with 307 additions and 9 deletions

View File

@ -33,7 +33,6 @@ func init(){
}
*/
// usbCmd represents the usb command
var netCmd = &cobra.Command{
Use: "NET",
Short: "Configure Network settings of ethernet interfaces (including USB ethernet if enabled)",
@ -147,10 +146,13 @@ func createManualSettings(iface string, ip4 string, mask4 string, disabled bool)
func parseDhcpServerOptions(strOptions []string) (options map[uint32]string, err error) {
options = map[uint32]string{}
for _, strOpt := range strOptions {
splOpt := strings.SplitN(strOpt,":", 2)
if len(splOpt) == 0 {
return nil,errors.New(fmt.Sprintf("Invalid DHCP option: %s\nOption format is \"<DHCPOptionNumber>:[DHCPOptionValue]\"", strOpt))
//return nil,errors.New(fmt.Sprintf("Invalid DHCP option: %s\nOption format is \"<DHCPOptionNumber>:[DHCPOptionValue]\"", strOpt))
//ignore empty options
continue
}
optNum, err := strconv.Atoi(splOpt[0])
if err != nil || optNum < 0 {
@ -174,10 +176,14 @@ func parseDhcpServerOptions(strOptions []string) (options map[uint32]string, err
func parseDhcpServerRanges(strRanges []string) (ranges []*pb.DHCPServerRange, err error) {
ranges = []*pb.DHCPServerRange{}
if len(strRanges) == 0 {
return nil,errors.New(fmt.Sprintf("Missing DHCP range: At least one range should be provided to allow assigning IP addresses to clients\n"))
}
for _,strRange := range strRanges {
splRange := strings.Split(strRange, "|")
if len(splRange) != 3 && len(splRange) != 2 {
return nil,errors.New(fmt.Sprintf("Invalid DHCP range: %s\nOption format is \"<first IPv4>|<last IPv4>[|leaseTime]\"", strRange))
return nil,errors.New(fmt.Sprintf("Invalid DHCP range: %s\nformat is \"<first IPv4>|<last IPv4>[|leaseTime]\"", strRange))
}
if net.ParseIP(splRange[0]) == nil {
@ -277,6 +283,6 @@ func init() {
netSetManualCmd.Flags().StringVarP(&tmpStrNetmask4, "netmask","m", "", "The IPv4 netmask to use for the interface")
netSetDHCPServerCmd.Flags().StringVarP(&tmpStrAddress4, "address","a", "", "The IPv4 address to use for the interface")
netSetDHCPServerCmd.Flags().StringVarP(&tmpStrNetmask4, "netmask","m", "", "The IPv4 netmask to use for the interface")
netSetDHCPServerCmd.Flags().StringSliceVarP(&tmpDHCPSrvRanges, "range", "r",[]string{""}, "A DHCP Server range in form \"<lowest IPv4>|<highest IPv4>[|lease time]\" (the flag could be used multiple times)")
netSetDHCPServerCmd.Flags().StringSliceVarP(&tmpDHCPSrvOptions, "option", "o",[]string{""}, "A DHCP Server option in form \"<option number>:<value1|value2>\" (Option values have to be separated by '|' not by ','. The flag could be used multiple times)")
netSetDHCPServerCmd.Flags().StringSliceVarP(&tmpDHCPSrvRanges, "range", "r",[]string{}, "A DHCP Server range in form \"<lowest IPv4>|<highest IPv4>[|lease time]\" (the flag could be used multiple times)")
netSetDHCPServerCmd.Flags().StringSliceVarP(&tmpDHCPSrvOptions, "option", "o",[]string{}, "A DHCP Server option in form \"<option number>:<value1|value2>\" (Option values have to be separated by '|' not by ','. The flag could be used multiple times)")
}

160
cli_client/cmd_wifi.go Normal file
View File

@ -0,0 +1,160 @@
package cli_client
import (
"github.com/spf13/cobra"
pb "../proto"
"fmt"
"google.golang.org/grpc/status"
"os"
"errors"
"strings"
)
//Empty settings used to store cobra flags
var (
tmpWifiStrReg string = ""
tmpWifiStrChannel uint8 = 0
tmpWifiHideSSID bool = false
tmpWifiDisabled bool = false
tmpWifiDisableNexmon bool = false
tmpWifiSSID string = ""
tmpWifiPSK string = ""
)
/*
func init(){
//Configure spew for struct deep printing (disable using printer interface for gRPC structs)
spew.Config.Indent="\t"
spew.Config.DisableMethods = true
spew.Config.DisablePointerAddresses = true
}
*/
var wifiCmd = &cobra.Command{
Use: "WIFI",
Short: "Configure WiFi (spawn Access Point or join WiFi networks)",
}
var wifiSetCmd = &cobra.Command{
Use: "set",
Short: "set WiFi settings",
Long: ``,
}
var wifiSetAPCmd = &cobra.Command{
Use: "ap",
Short: "Configure WiFi interface as access point",
Long: ``,
Run: cobraWifiSetAP,
}
var wifiSetStaCmd = &cobra.Command{
Use: "sta",
Short: "Configure WiFi interface to join a network as station",
Long: ``,
Run: cobraWifiSetSta,
}
var wifiGetCmd = &cobra.Command{
Use: "get",
Short: "get WiFi settings",
Long: ``,
Run: cobraWifiGet,
}
func cobraWifiGet(cmd *cobra.Command, args []string) {
return
}
func cobraWifiSetAP(cmd *cobra.Command, args []string) {
settings, err := createWifiAPSettings(tmpWifiStrChannel, tmpWifiStrReg, tmpWifiSSID, tmpWifiPSK, tmpWifiHideSSID, tmpWifiDisableNexmon, tmpWifiDisabled)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(-1) //exit with error
return
}
fmt.Printf("Deploying WiFi inteface settings:\n\t%v\n", settings)
err = ClientDeployWifiSettings(StrRemoteHost, StrRemotePort, settings)
if err != nil {
fmt.Println(status.Convert(err).Message())
os.Exit(-1) //exit with error
}
return
}
func cobraWifiSetSta(cmd *cobra.Command, args []string) {
return
}
func createWifiAPSettings(channel uint8, reg string, strSSID string, strPSK string, hideSsid bool, nexmon bool, disabled bool) (settings *pb.WiFiSettings, err error) {
if channel < 1 || channel > 14 {
return nil,errors.New(fmt.Sprintf("Only 2.4GHz channels between 1 and 14 are supported, but '%d' was given\n", channel))
}
if len(reg) != 2 {
return nil,errors.New(fmt.Sprintf("Regulatory domain has to consist of two uppercase letters (ISO/IEC 3166-1 alpha2), but '%s' was given\n", reg))
}
reg = strings.ToUpper(reg)
if len(strSSID) < 1 || len(strSSID) > 32 {
return nil,errors.New(fmt.Sprintf("SSID has to consist of 1 to 32 ASCII letters (even if hidden), but '%s' was given\n", strSSID))
}
if len(strPSK) > 0 && len(strPSK) < 8 {
return nil,errors.New(fmt.Sprintf("A non-empty PSK implies WPA2 and has to have a minimum of 8 characters, but given PSK has '%d' charactres\n", len(strPSK)))
}
settings = &pb.WiFiSettings{
Mode: pb.WiFiSettings_AP,
AuthMode: pb.WiFiSettings_OPEN,
Disabled: disabled,
Reg: reg,
ApChannel: uint32(channel),
ApHideSsid: hideSsid,
BssCfgAP: &pb.BSSCfg{
SSID: strSSID,
PSK: strPSK,
},
DisableNexmon: !nexmon,
BssCfgClient: nil, //not needed
}
if len(strPSK) > 0 {
settings.AuthMode = pb.WiFiSettings_WPA2_PSK //if PSK is given use WPA2
}
return settings, err
}
func init() {
rootCmd.AddCommand(wifiCmd)
wifiCmd.AddCommand(wifiGetCmd)
wifiCmd.AddCommand(wifiSetCmd)
wifiSetCmd.AddCommand(wifiSetAPCmd)
wifiSetCmd.AddCommand(wifiSetStaCmd)
wifiSetCmd.PersistentFlags().StringVarP(&tmpWifiStrReg, "reg","r", "US", "Sets the regulatory domain according to ISO/IEC 3166-1 alpha2")
wifiSetCmd.PersistentFlags().BoolVarP(&tmpWifiDisabled, "disable","d", false, "The flag disables the WiFi interface (omitting the flag enables the interface")
wifiSetCmd.PersistentFlags().BoolVarP(&tmpWifiDisableNexmon, "nonexmon","n", false, "Don't use the modified nexmon firmware")
wifiSetCmd.PersistentFlags().StringVarP(&tmpWifiSSID, "ssid","s", "", "The SSID to use for an Access Point or to join as station")
wifiSetCmd.PersistentFlags().StringVarP(&tmpWifiPSK, "psk","k", "", "The Pre-Shared-Key to use for the Access Point (if empty, an OPEN AP is created) or for the network")
wifiSetAPCmd.Flags().Uint8VarP(&tmpWifiStrChannel, "channel","c", 1, "The WiFi channel to use for the Access Point")
wifiSetAPCmd.Flags().BoolVarP(&tmpWifiHideSSID, "hide","x", false, "Hide the SSID of the Access Point")
}

View File

@ -139,6 +139,28 @@ func ClientDeployEthernetInterfaceSettings(host string, port string, settings *p
}
func ClientDeployWifiSettings(host string, port string, settings *pb.WiFiSettings) (err error) {
// Set up a connection to the server.
address := host + ":" + port
//log.Printf("Connecting %s ...", address)
connection, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("Could not connect to P4wnP1 RPC server: %v", err)
}
defer connection.Close()
rpcClient := pb.NewP4WNP1Client(connection)
// Contact the server
ctx, cancel := context.WithTimeout(context.Background(), time.Second * 30)
defer cancel()
_,err = rpcClient.DeployWifiSettings(ctx, settings)
return err
}
/*
func ClientDisconnectServer(cancel context.CancelFunc, connection *grpc.ClientConn) error {
defer connection.Close()

View File

@ -29,3 +29,4 @@ sudo pip install pydispatcher
sudo update-rc.d dhcpcd disable
sudo update-rc.d dnsmasq disable

4
notes.txt Normal file
View File

@ -0,0 +1,4 @@
- global dnsmasq service needs to be stopped, in order to avoid overwriting resolv.conf with configuration of local loopback interface (nameserver 127.0.0.1)
- global dhcpcd service needs to be stopped
- gloabel hostapd service needs to be stopped
- global wpa_supplicant service needs to be stopped

View File

@ -14,6 +14,8 @@ import (
"strconv"
"syscall"
"time"
"bufio"
"io"
)
const (
@ -32,6 +34,10 @@ const (
WiFiAuthMode_UNSUPPORTED
)
const (
WPA_SUPPLICANT_CONNECT_TIMEOUT = time.Second * 10
)
type BSS struct {
SSID string
BSSID net.HardwareAddr
@ -127,7 +133,7 @@ func DeployWifiSettings(ws *pb.WiFiSettings) (err error) {
err = WifiCreateWpaSupplicantConfigFile(ws.BssCfgClient.SSID, ws.BssCfgClient.PSK, confFileWpaSupplicant(wifi_if_name))
if err != nil { return err }
//ToDo: proper error handling, in case connection not possible
err = wifiStartWpaSupplicant(wifi_if_name)
err = wifiStartWpaSupplicant2(wifi_if_name, WPA_SUPPLICANT_CONNECT_TIMEOUT)
if err != nil { return err }
}
@ -203,6 +209,8 @@ func wifiCreateWpaSupplicantConfString(ssid string, psk string) (config string,
return "",errors.New(fmt.Sprintf("Error craeting wpa_supplicant.conf for SSID '%s' with PSK '%s': %s", ssid, psk, string(cres)))
}
config = string(cres)
config = "ctrl_interface=/run/wpa_supplicant\n" + config
return
}
@ -307,7 +315,9 @@ func wifiStartHostapd(nameIface string) (err error) {
//We use the run command and allow hostapd to daemonize
proc := exec.Command("/usr/sbin/hostapd", "-B", "-P", pidFileHostapd(nameIface), "-f", logFileHostapd(nameIface), confpath)
err = proc.Run()
if err != nil { return err}
if err != nil {
return errors.New(fmt.Sprintf("Error starting hostapd '%v'", err))
}
log.Printf("... hostapd for interface '%s' started\n", nameIface)
@ -358,11 +368,11 @@ func wifiStartWpaSupplicant(nameIface string) (err error) {
confpath := confFileWpaSupplicant(nameIface)
//stop hostapd if already running
//stop wpa_supplicant if already running
wifiStopWpaSupplicant(nameIface)
//We use the run command and allow hostapd to daemonize
//We use the run command and allow wpa_supplicant to daemonize
//wpa_supplicant -P /tmp/wpa_supplicant.pid -i wlan0 -c /tmp/wpa_supplicant.conf -B
proc := exec.Command("/sbin/wpa_supplicant", "-B", "-P", pidFileWpaSupplicant(nameIface), "-f", logFileWpaSupplicant(nameIface), "-c", confpath, "-i", nameIface)
err = proc.Run()
@ -373,6 +383,101 @@ func wifiStartWpaSupplicant(nameIface string) (err error) {
return nil
}
func wifiWpaSupplicantOutParser(chanResult chan string, reader *bufio.Reader) {
log.Println("... Start monitoring wpa_supplicant output")
for {
line, _, err := reader.ReadLine()
if err != nil {
//in case wpa_supplicant is killed, we should land here, which ends the goroutine
if err != io.EOF {
log.Printf("Can't read wpa_supplicant output: %s\n", err)
}
break
}
strLine := string(line)
//fmt.Printf("Read:\n%s\n", strLine)
switch {
case strings.Contains(strLine, "WRONG_KEY"):
log.Printf("Seems the provided PSK doesn't match\n")
chanResult <- "WRONG_KEY"
break
case strings.Contains(strLine, "CTRL-EVENT-CONNECTED"):
log.Printf("Connected to target network\n")
chanResult <- "CONNECTED"
break // stop loop
}
}
log.Println("... stopped monitoring wpa_supplicant output")
}
func wifiStartWpaSupplicant2(nameIface string, timeout time.Duration) (err error) {
log.Printf("Starting wpa_supplicant for interface '%s'...\n", nameIface)
//check if interface is valid
if_exists,_ := CheckInterfaceExistence(nameIface)
if !if_exists {
return errors.New(fmt.Sprintf("The given interface '%s' doesn't exist", nameIface))
}
if !wifiWpaSupplicantAvailable() {
return errors.New("wpa_supplicant seems to be missing, please install it")
}
confpath := confFileWpaSupplicant(nameIface)
//stop wpa_supplicant if already running
wifiStopWpaSupplicant(nameIface)
//we monitor output of wpa_supplicant till we are connected, fail due to wrong PSK or timeout is reached
proc := exec.Command("/sbin/wpa_supplicant", "-P", pidFileWpaSupplicant(nameIface), "-c", confpath, "-i", nameIface)
wpa_stdout, err := proc.StdoutPipe()
if err != nil { return err}
err = proc.Start()
if err != nil { return err}
//result channel
wpa_res := make(chan string, 1)
//start output parser
wpa_stdout_reader := bufio.NewReader(wpa_stdout)
go wifiWpaSupplicantOutParser(wpa_res, wpa_stdout_reader)
//analyse output
select {
case res := <-wpa_res:
if strings.Contains(res, "CONNECTED") {
//We could return success and keep wpa_supplicant running
log.Println("... connected to given WiFi network, wpa_supplicant running")
return nil
}
if strings.Contains(res, "WRONG_KEY") {
//we stop wpa_supplicant and return err
log.Println("... seems the wrong PSK wwas provided for the given WiFi network, stopping wpa_supplicant ...")
//wifiStopWpaSupplicant(nameIface)
log.Println("... killing wpa_supplicant")
proc.Process.Kill()
return errors.New("Wrong PSK")
}
case <- time.After(timeout):
//we stop wpa_supplicant and return err
log.Println("... wpa_supplicant reached timeout without beeing able to connect to given network")
log.Println("... killing wpa_supplicant")
//wifiStopWpaSupplicant(nameIface)
proc.Process.Kill()
return errors.New("TIMEOUT REACHED")
}
return nil
}
func wifiStopWpaSupplicant(nameIface string) (err error) {
log.Printf("... stop running wpa_supplicant processes for interface '%s'\n", nameIface)