diff --git a/dhcp_client.go b/dhcp_client.go new file mode 100644 index 0000000..62fec82 --- /dev/null +++ b/dhcp_client.go @@ -0,0 +1,82 @@ +package owrt + +import ( + "bufio" + "fmt" + "regexp" + "strings" +) + +// DhcpClient represents a dhcp client ... :) +type DhcpClient struct { + exec Executor + iface string +} + +// DhcpResult contains sorted result form AskFroIP +type DhcpResult struct { + CmdRes *CommandResult + IP string + Netmask string +} + +// NewDhcpClient return an UCI instance to interact with UCI +func NewDhcpClient(netIface string) *DhcpClient { + exec := &localExecutor{} + iface := netIface + return &DhcpClient{exec, iface} +} + +// NewDhcpClientWithExecutor return an UCI instance to interact with UCI +func NewDhcpClientWithExecutor(netIface string, exe Executor) *DhcpClient { + exec := exe + iface := netIface + return &DhcpClient{exec, iface} +} + +// NewDhcpClient return an UCI instance to interact with UCI +//func NewDhcpClient(netIface string, exe Executor) *DhcpClient { +// var exec Executor +// if exe == nil { +// exec = &localExecutor{} +// } else { +// exec = exe +// } +// iface := netIface +// return &DhcpClient{exec, iface} +//} + +func parseDhcpClientOut(out *CommandResult) *DhcpResult { + if out.ReturnCode != 0 { + return &DhcpResult{ + out, + "", + "", + } + } + + scanner := bufio.NewScanner(strings.NewReader(out.Stdout)) + for scanner.Scan() { + line := scanner.Text() + re := regexp.MustCompile(`^udhcpc: ifconfig`) + if re.MatchString(line) { + spl := strings.Split(line, " ") + ip := spl[3] + mask := spl[5] + return &DhcpResult{ + out, + ip, + mask, + } + } + + } + return nil +} + +// AskForIP runs a dhclient ip request with udhcpc +func (dc *DhcpClient) AskForIP() *DhcpResult { + out := dc.exec.Run("udhcpc", "-i", dc.iface) + fmt.Printf("%s\n", out.Stdout) + return parseDhcpClientOut(out) +} diff --git a/dhcp_client_test.go b/dhcp_client_test.go new file mode 100644 index 0000000..5b761f9 --- /dev/null +++ b/dhcp_client_test.go @@ -0,0 +1,12 @@ +package owrt + +import "testing" + +func TestDhcpClientAskForIP(t *testing.T) { + uexec := createMockExecutor("udhcpc: ifconfig wlan1 192.168.42.20 netmask 255.255.255.0 broadcast +", "", 0) + dhc := NewDhcpClientWithExecutor("wlan1", uexec) + res := dhc.AskForIP() + if res.CmdRes.ReturnCode != 0 { + t.Error("Error in DHCP Client !!") + } +} diff --git a/executor.go b/executor.go new file mode 100644 index 0000000..ae1f4a2 --- /dev/null +++ b/executor.go @@ -0,0 +1,54 @@ +package owrt + +import ( + "bytes" + "log" + "os/exec" + "syscall" +) + +// Executor interface to describe command runners signature +type Executor interface { + Run(command string, params ...string) *CommandResult +} + +// CommandResult contain all information about a command execution, stdout, stderr +type CommandResult struct { + Stdout string + Stderr string + ReturnCode int +} + +type localExecutor struct{} + +func (e *localExecutor) Run(command string, params ...string) *CommandResult { + + var out bytes.Buffer + var outerr bytes.Buffer + var exitCode int + + exe := exec.Command(command, params...) + exe.Stdout = &out + exe.Stderr = &outerr + + err := exe.Run() + if err != nil { + // try to get the exit code + if exitError, ok := err.(*exec.ExitError); ok { + ws := exitError.Sys().(syscall.WaitStatus) + exitCode = ws.ExitStatus() + } else { + log.Printf("Could not get exit code for failed program: %v, %v", command, params) + } + } else { + // success, exitCode should be 0 if go is ok + ws := exe.ProcessState.Sys().(syscall.WaitStatus) + exitCode = ws.ExitStatus() + } + + return &CommandResult{ + Stdout: out.String(), + Stderr: outerr.String(), + ReturnCode: exitCode, + } +} diff --git a/executor_test.go b/executor_test.go new file mode 100644 index 0000000..358c436 --- /dev/null +++ b/executor_test.go @@ -0,0 +1,18 @@ +package owrt + +import ( + "testing" +) + +func TestRun(t *testing.T) { + exec := &localExecutor{} + res := exec.Run("uname", "-a") + if g, e := res.ReturnCode, 0; g != e { + t.Errorf("Run command failed ! Got bad return code [%d], [%d} is expected\n", g, e) + } + + // res = exec.Run("noCommandWithThisNameExists", "-a") + // if g, e := res.ReturnCode, 127; g != e { + // t.Errorf("Run command failed ! Got bad return code [%d], [%d} is expected\n", g, e) + // } +} diff --git a/network.go b/network.go new file mode 100644 index 0000000..4799deb --- /dev/null +++ b/network.go @@ -0,0 +1,83 @@ +package owrt + +import ( + "fmt" + "io/ioutil" + "net" + "strings" +) + +// Network provides a representation of network +type Network struct { + exec Executor +} + +// NewNetwork return an Network instance to interact with OpenWRT Network components +func NewNetwork() *Network { + exec := &localExecutor{} + return &Network{exec} +} + +// NewNetworkWithExecutor return an UCI instance to interact with UCI +func NewNetworkWithExecutor(exe Executor) *Network { + exec := exe + return &Network{exec} +} + +// ListInterfaces list all available interfaces on a system using "ip" command +func (n *Network) ListInterfaces() []net.Interface { + var result []net.Interface + ifaces, err := net.Interfaces() + if err != nil { + fmt.Print(fmt.Errorf("error listing network interfacess: %+v", err.Error())) + return nil + } + result = append(result, ifaces...) + return result +} + +// ListWirelessInterfaces list all wifi cards +// you need to provide the wireless file or "" to use +// Linux default one "/proc/net/wireless" +func (n *Network) ListWirelessInterfaces(wifiFile string) []net.Interface { + var result []net.Interface + var ifaceNames []string + + if wifiFile == "" { + wifiFile = "/proc/net/wireless" + } + + wifiFileContent, err := ioutil.ReadFile(wifiFile) + check(err) + + index := 0 + for _, line := range strings.Split(string(wifiFileContent), "\n") { + if index < 2 { + index++ + continue + } else { + name := strings.Split(line, ":")[0] + if name != "" { + // remove damened whitespaces + name = strings.Replace(name, " ", "", -1) + ifaceNames = append(ifaceNames, name) + } + } + } + + ifaces, err := net.Interfaces() + if err != nil { + fmt.Print(fmt.Errorf("error listing network interfaces : %+v", err.Error())) + return nil + } + + for _, name := range ifaceNames { + for _, iface := range ifaces { + if name == iface.Name { + result = append(result, iface) + } + } + } + + return result +} diff --git a/network_test.go b/network_test.go new file mode 100644 index 0000000..bccb941 --- /dev/null +++ b/network_test.go @@ -0,0 +1,26 @@ +package owrt + +import ( + "fmt" + "testing" +) + +func TestNetworkListInterfaces(t *testing.T) { + net := NewNetwork() + iface := net.ListInterfaces() + for _, ife := range iface { + fmt.Printf("%s\n", ife.Name) + } +} + +func TestListWirelessInterfaces(t *testing.T) { + net := NewNetwork() + res := net.ListWirelessInterfaces("./testdata/proc_net_wireless.txt") + + if len(res) != 0 { + t.Error("The wireless interfaces list is not empty !!") + } + for _, el := range res { + fmt.Printf("[%s]\n", el.Name) + } +} diff --git a/test.go b/test.go new file mode 100644 index 0000000..2619cca --- /dev/null +++ b/test.go @@ -0,0 +1,35 @@ +package owrt + +import ( + "log" + "strings" +) + +func check(e error) { + if e != nil { + panic(e) + } +} + +func createMockExecutor(stdout string, stderr string, returnCode int) Executor { + return &mockExecutor{ + stdout: stdout, + stderr: stderr, + returnCode: returnCode, + } +} + +type mockExecutor struct { + stdout string + stderr string + returnCode int +} + +func (e *mockExecutor) Run(command string, params ...string) *CommandResult { + log.Printf("executing '%s %s'", command, strings.Join(params, " ")) + return &CommandResult{ + Stderr: e.stderr, + Stdout: e.stdout, + ReturnCode: e.returnCode, + } +} diff --git a/testdata/proc_net_wireless.txt b/testdata/proc_net_wireless.txt new file mode 100644 index 0000000..5fc603c --- /dev/null +++ b/testdata/proc_net_wireless.txt @@ -0,0 +1,4 @@ +Inter-| sta-| Quality | Discarded packets | Missed | WE + face | tus | link level noise | nwid crypt frag retry misc | beacon | 22 + wlan1: 0000 0 0 0 0 0 0 0 0 0 + wlan0: 0000 0 0 0 0 0 0 0 0 0 diff --git a/testdata/sys/devices/platform/soc/soc:pcie/pci0000:00/0000:00:02.0/0000:02:00.0/net/wlan1/address b/testdata/sys/devices/platform/soc/soc:pcie/pci0000:00/0000:00:02.0/0000:02:00.0/net/wlan1/address new file mode 100644 index 0000000..8113f4e --- /dev/null +++ b/testdata/sys/devices/platform/soc/soc:pcie/pci0000:00/0000:00:02.0/0000:02:00.0/net/wlan1/address @@ -0,0 +1 @@ +00:25:9c:14:59:6d diff --git a/testdata/uci_show_wireless.txt b/testdata/uci_show_wireless.txt new file mode 100644 index 0000000..c9b7431 --- /dev/null +++ b/testdata/uci_show_wireless.txt @@ -0,0 +1,24 @@ +wireless.radio0=wifi-device +wireless.radio0.type='mac80211' +wireless.radio0.channel='36' +wireless.radio0.hwmode='11a' +wireless.radio0.htmode='VHT80' +wireless.radio0.country='DE' +wireless.radio0.path='soc/soc:pcie/pci0000:00/0000:00:01.0/0000:01:00.0' +wireless.default_radio0=wifi-iface +wireless.default_radio0.device='radio0' +wireless.default_radio0.network='lan' +wireless.default_radio0.mode='ap' +wireless.default_radio0.macaddr='5a:ef:68:b5:f5:1a' +wireless.default_radio0.ssid='DonDuSang' +wireless.default_radio0.encryption='psk2' +wireless.default_radio0.key='cadoles;21' +wireless.radio1=wifi-device +wireless.radio1.type='mac80211' +wireless.radio1.hwmode='11g' +wireless.radio1.htmode='HT20' +wireless.radio1.country='FR' +wireless.radio1.path='soc/soc:pcie/pci0000:00/0000:00:02.0/0000:02:00.0' +wireless.radio1.channel='1' +wireless.radio1.legacy_rates='1' +wireless.radio1.disabled='0' \ No newline at end of file diff --git a/testdata/uci_show_wireless_2_cards.txt b/testdata/uci_show_wireless_2_cards.txt new file mode 100644 index 0000000..961b866 --- /dev/null +++ b/testdata/uci_show_wireless_2_cards.txt @@ -0,0 +1,30 @@ +wireless.radio0=wifi-device +wireless.radio0.type='mac80211' +wireless.radio0.channel='36' +wireless.radio0.hwmode='11a' +wireless.radio0.htmode='VHT80' +wireless.radio0.country='DE' +wireless.radio0.path='soc/soc:pcie/pci0000:00/0000:00:01.0/0000:01:00.0' +wireless.default_radio0=wifi-iface +wireless.default_radio0.device='radio0' +wireless.default_radio0.network='lan' +wireless.default_radio0.mode='ap' +wireless.default_radio0.macaddr='5a:ef:68:b5:f5:1a' +wireless.default_radio0.ssid='DonDuSang' +wireless.default_radio0.encryption='psk2' +wireless.default_radio0.key='free_wifi!' +wireless.radio1=wifi-device +wireless.radio1.type='mac80211' +wireless.radio1.hwmode='11g' +wireless.radio1.htmode='HT20' +wireless.radio1.country='FR' +wireless.radio1.path='soc/soc:pcie/pci0000:00/0000:00:02.0/0000:02:00.0' +wireless.radio1.channel='1' +wireless.radio1.legacy_rates='1' +wireless.radio1.disabled='0' +wireless.@wifi-iface[1]=wifi-iface +wireless.@wifi-iface[1].device='radio1' +wireless.@wifi-iface[1].mode='ap' +wireless.@wifi-iface[1].ssid='Pyxis2' +wireless.@wifi-iface[1].encryption='psk2' +wireless.@wifi-iface[1].key='free_wifi!' \ No newline at end of file diff --git a/testdata/wifi_cells_output_1.txt b/testdata/wifi_cells_output_1.txt new file mode 100644 index 0000000..e6aedcb --- /dev/null +++ b/testdata/wifi_cells_output_1.txt @@ -0,0 +1,31 @@ +BSS 7c:26:64:66:cc:44(on wlan1) + TSF: 85238642066 usec (0d, 23:40:38) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -63.00 dBm + last seen: 1500 ms ago + Information elements from Probe Response frame: + SSID: Livebox-32c8 + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT TX/RX MCS rate indexes supported: 0-23 + HT operation: + * primary channel: 1 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) \ No newline at end of file diff --git a/testdata/wifi_cells_output_3.txt b/testdata/wifi_cells_output_3.txt new file mode 100644 index 0000000..1c89dcd --- /dev/null +++ b/testdata/wifi_cells_output_3.txt @@ -0,0 +1,88 @@ +BSS 74:3e:2b:08:41:1c(on wlan1) + TSF: 85238642066 usec (0d, 23:40:38) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -63.00 dBm + last seen: 1500 ms ago + Information elements from Probe Response frame: + SSID: PyxisWifi + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT TX/RX MCS rate indexes supported: 0-23 + HT operation: + * primary channel: 1 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS 7e:26:64:66:cc:44(on wlan1) + TSF: 85238646001 usec (0d, 23:40:38) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0421) + signal: -66.00 dBm + last seen: 1500 ms ago + Information elements from Probe Response frame: + SSID: orange + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT TX/RX MCS rate indexes supported: 0-23 + HT operation: + * primary channel: 1 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 20:47:da:b7:0e:5c(on wlan1) + TSF: 2161066518436 usec (25d, 00:17:46) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -58.00 dBm + last seen: 1640 ms ago + Information elements from Probe Response frame: + SSID: Livebox-596a + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT TX/RX MCS rate indexes supported: 0-23 + HT operation: + * primary channel: 1 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) \ No newline at end of file diff --git a/testdata/wifi_cells_output_large.txt b/testdata/wifi_cells_output_large.txt new file mode 100644 index 0000000..675aab2 --- /dev/null +++ b/testdata/wifi_cells_output_large.txt @@ -0,0 +1,1152 @@ +BSS 7c:26:64:66:cc:44(on wlan1) + TSF: 85238642066 usec (0d, 23:40:38) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -63.00 dBm + last seen: 1500 ms ago + Information elements from Probe Response frame: + SSID: Livebox-32c8 + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT TX/RX MCS rate indexes supported: 0-23 + HT operation: + * primary channel: 1 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS 7e:26:64:66:cc:44(on wlan1) + TSF: 85238646001 usec (0d, 23:40:38) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0421) + signal: -66.00 dBm + last seen: 1500 ms ago + Information elements from Probe Response frame: + SSID: orange + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT TX/RX MCS rate indexes supported: 0-23 + HT operation: + * primary channel: 1 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS ac:84:c9:2f:59:6e(on wlan1) + TSF: 2161066518436 usec (25d, 00:17:46) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -58.00 dBm + last seen: 1640 ms ago + Information elements from Probe Response frame: + SSID: Livebox-596a + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT TX/RX MCS rate indexes supported: 0-23 + HT operation: + * primary channel: 1 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS b0:39:56:d8:38:ed(on wlan1) + TSF: 1037415177423 usec (12d, 00:10:15) + freq: 2422 + beacon interval: 100 TUs + capability: ESS (0x0c11) + signal: -51.00 dBm + last seen: 1220 ms ago + Information elements from Probe Response frame: + SSID: Frate Dijon EXT + HT capabilities: + Capabilities: 0x11ee + HT20/HT40 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 4 usec (0x05) + HT RX MCS rate indexes supported: 0-15, 32 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 3 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS 40:5a:9b:ed:ba:f0(on wlan1) + TSF: 3568958658654 usec (41d, 07:22:38) + freq: 2437 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -46.00 dBm + last seen: 740 ms ago + Information elements from Probe Response frame: + SSID: Cadoles + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT TX/RX MCS rate indexes supported: 0-15 + HT operation: + * primary channel: 6 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS 00:a6:ca:10:df:01(on wlan1) + TSF: 1018905678733 usec (11d, 19:01:45) + freq: 2437 + beacon interval: 102 TUs + capability: ESS (0x1431) + signal: -80.00 dBm + last seen: 820 ms ago + Information elements from Probe Response frame: + SSID: EFF-Mobility + HT capabilities: + Capabilities: 0x19ac + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 7935 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT RX MCS rate indexes supported: 0-23 + HT TX MCS rate indexes are undefined + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 4-PTKSA-RC 4-GTKSA-RC (0x0028) + HT operation: + * primary channel: 6 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS a0:04:60:b2:8a:c8(on wlan1) + TSF: 23484334090837 usec (271d, 19:25:34) + freq: 2472 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -44.00 dBm + last seen: 40 ms ago + Information elements from Probe Response frame: + SSID: Cadoles Formations (N) + HT capabilities: + Capabilities: 0x11ec + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 4 usec (0x05) + HT RX MCS rate indexes supported: 0-15, 32 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 13 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS bc:f6:85:fe:6d:46(on wlan1) + TSF: 5185428505884 usec (60d, 00:23:48) + freq: 2467 + beacon interval: 100 TUs + capability: ESS (0x0c11) + signal: -76.00 dBm + last seen: 30 ms ago + Information elements from Probe Response frame: + SSID: Dlink + HT capabilities: + Capabilities: 0x11ee + HT20/HT40 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 2 usec (0x04) + HT RX MCS rate indexes supported: 0-15, 32 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 12 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + Secondary Channel Offset: no secondary (0) + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS 90:4d:4a:f7:b9:71(on wlan1) + TSF: 4721041865203 usec (54d, 15:24:01) + freq: 2462 + beacon interval: 100 TUs + capability: ESS (0x0001) + signal: -82.00 dBm + last seen: 300 ms ago + Information elements from Probe Response frame: + SSID: orange + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 4 usec (0x05) + HT Max RX data rate: 384 Mbps + HT RX MCS rate indexes supported: 0-23 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 11 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS e4:9e:12:8b:ef:73(on wlan1) + TSF: 7952485362135 usec (92d, 01:01:25) + freq: 2452 + beacon interval: 96 TUs + capability: ESS (0x0411) + signal: -80.00 dBm + last seen: 430 ms ago + Information elements from Probe Response frame: + SSID: Freebox-8BEF72 + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) + HT capabilities: + Capabilities: 0xec + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + No RX STBC + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: No restriction (0x00) + HT TX/RX MCS rate indexes supported: 0-23, 32 + HT operation: + * primary channel: 9 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 90:4d:4a:f7:b9:70(on wlan1) + TSF: 4721041856992 usec (54d, 15:24:01) + freq: 2462 + beacon interval: 100 TUs + capability: ESS (0x0011) + signal: -78.00 dBm + last seen: 310 ms ago + Information elements from Probe Response frame: + SSID: Livebox-B970 + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 4 usec (0x05) + HT Max RX data rate: 384 Mbps + HT RX MCS rate indexes supported: 0-23 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 11 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 3c:52:82:fc:5e:21(on wlan1) + TSF: 6735883434375 usec (77d, 23:04:43) + freq: 2437 + beacon interval: 100 TUs + capability: ESS (0x0511) + signal: -79.00 dBm + last seen: 970 ms ago + Information elements from Probe Response frame: + SSID: DIRECT-20-HP DeskJet 3630 series + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) + HT capabilities: + Capabilities: 0x20 + HT20 + Static SM Power Save + RX HT20 SGI + No RX STBC + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 32767 bytes (exponent: 0x002) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT RX MCS rate indexes supported: 0-7 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 6 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 00:a6:ca:10:df:02(on wlan1) + TSF: 1018894510847 usec (11d, 19:01:34) + freq: 2437 + beacon interval: 102 TUs + capability: ESS (0x1421) + signal: -73.00 dBm + last seen: 1040 ms ago + Information elements from Probe Response frame: + SSID: Keo-HotSpot + HT capabilities: + Capabilities: 0x19ac + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 7935 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT RX MCS rate indexes supported: 0-23 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 6 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS a2:17:33:9f:4d:81(on wlan1) + TSF: 3050426691569 usec (35d, 07:20:26) + freq: 2462 + beacon interval: 100 TUs + capability: ESS (0x0401) + signal: -82.00 dBm + last seen: 310 ms ago + Information elements from Probe Response frame: + SSID: SFR WiFi FON +BSS ac:84:c9:1d:c6:7c(on wlan1) + TSF: 2491424200041 usec (28d, 20:03:44) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -80.00 dBm + last seen: 1480 ms ago + Information elements from Probe Response frame: + SSID: Frate Djon + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS 78:81:02:5e:b7:15(on wlan1) + TSF: 4786879928619 usec (55d, 09:41:19) + freq: 2437 + beacon interval: 100 TUs + capability: ESS (0x0401) + signal: -83.00 dBm + last seen: 950 ms ago + Information elements from Probe Response frame: + SSID: orange + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 4 usec (0x05) + HT Max RX data rate: 384 Mbps + HT RX MCS rate indexes supported: 0-23 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 6 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 00:a6:ca:10:df:00(on wlan1) + TSF: 1018905280864 usec (11d, 19:01:45) + freq: 2437 + beacon interval: 102 TUs + capability: ESS (0x1431) + signal: -76.00 dBm + last seen: 1170 ms ago + Information elements from Probe Response frame: + SSID: \x00 + HT capabilities: + Capabilities: 0x19ac + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 7935 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT RX MCS rate indexes supported: 0-23 + HT TX MCS rate indexes are undefined + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: IEEE 802.1X + * Capabilities: 4-PTKSA-RC 4-GTKSA-RC (0x0028) + HT operation: + * primary channel: 6 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS a0:1b:29:be:98:26(on wlan1) + TSF: 2931892990829 usec (33d, 22:24:52) + freq: 2437 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -64.00 dBm + last seen: 970 ms ago + Information elements from Probe Response frame: + SSID: Livebox-9822 + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT TX/RX MCS rate indexes supported: 0-15 + HT operation: + * primary channel: 6 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS 40:4a:03:05:d2:68(on wlan1) + TSF: 57377785 usec (0d, 00:00:57) + freq: 2462 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -76.00 dBm + last seen: 240 ms ago + Information elements from Probe Response frame: + SSID: ZyXEL + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) + HT capabilities: + Capabilities: 0x104e + HT20/HT40 + SM Power Save disabled + RX HT40 SGI + No RX STBC + Max AMSDU length: 3839 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT RX MCS rate indexes supported: 0-15 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 11 + * secondary channel offset: below + * STA channel width: any +BSS f4:ca:e5:98:3b:de(on wlan1) + TSF: 502449932512 usec (5d, 19:34:09) + freq: 2462 + beacon interval: 96 TUs + capability: ESS (0x0411) + signal: -86.00 dBm + last seen: 11400 ms ago + Information elements from Probe Response frame: + SSID: FreeWifi_secure + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: IEEE 802.1X + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) + HT capabilities: + Capabilities: 0x6c + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + No RX STBC + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: No restriction (0x00) + HT TX/RX MCS rate indexes supported: 0-23, 32 + HT operation: + * primary channel: 11 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 00:17:33:9f:4d:80(on wlan1) + TSF: 3050426716298 usec (35d, 07:20:26) + freq: 2462 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -79.00 dBm + last seen: 290 ms ago + Information elements from Probe Response frame: + SSID: NEUF_4D7C +BSS a2:17:33:9f:4d:83(on wlan1) + TSF: 3050426718113 usec (35d, 07:20:26) + freq: 2462 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -80.00 dBm + last seen: 290 ms ago + Information elements from Probe Response frame: + SSID: SFR WiFi Mobile + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: IEEE 802.1X + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) +BSS 30:7c:b2:d1:0b:0d(on wlan1) + TSF: 1059959180138 usec (12d, 06:25:59) + freq: 2462 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -80.00 dBm + last seen: 11200 ms ago + Information elements from Probe Response frame: + SSID: Livebox-0b09 + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT TX/RX MCS rate indexes supported: 0-15 + HT operation: + * primary channel: 11 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS 68:a3:78:0d:b6:51(on wlan1) + TSF: 136373797215 usec (1d, 13:52:53) + freq: 2462 + beacon interval: 96 TUs + capability: ESS (0x0411) + signal: -88.00 dBm + last seen: 11230 ms ago + Information elements from Probe Response frame: + SSID: Freebox-0DB650 + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) + HT capabilities: + Capabilities: 0xec + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + No RX STBC + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: No restriction (0x00) + HT TX/RX MCS rate indexes supported: 0-23, 32 + HT operation: + * primary channel: 11 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 68:15:90:36:63:60(on wlan1) + TSF: 308936601984 usec (3d, 13:48:56) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -88.00 dBm + last seen: 12650 ms ago + Information elements from Probe Response frame: + SSID: Livebox-6360 + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT RX MCS rate indexes supported: 0-15 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 1 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) +BSS 0c:8d:db:c4:a0:34(on wlan1) + TSF: 372485216298 usec (4d, 07:28:05) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x1431) + signal: -56.00 dBm + last seen: 1540 ms ago + Information elements from Probe Response frame: + SSID: pfPauvres + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) + HT capabilities: + Capabilities: 0x9ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 7935 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: No restriction (0x00) + HT TX/RX MCS rate indexes supported: 0-15 + HT operation: + * primary channel: 1 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + VHT capabilities: + VHT Capabilities (0x338959b2): + Max MPDU length: 11454 + Supported Channel Width: neither 160 nor 80+80 + RX LDPC + short GI (80 MHz) + TX STBC + SU Beamformer + SU Beamformee + MU Beamformer + RX antenna pattern consistency + TX antenna pattern consistency + VHT RX MCS set: + 1 streams: MCS 0-9 + 2 streams: MCS 0-9 + 3 streams: not supported + 4 streams: not supported + 5 streams: not supported + 6 streams: not supported + 7 streams: not supported + 8 streams: not supported + VHT RX highest supported: 0 Mbps + VHT TX MCS set: + 1 streams: MCS 0-9 + 2 streams: MCS 0-9 + 3 streams: not supported + 4 streams: not supported + 5 streams: not supported + 6 streams: not supported + 7 streams: not supported + 8 streams: not supported + VHT TX highest supported: 0 Mbps + VHT operation: + * channel width: 0 (20 or 40 MHz) + * center freq segment 1: 0 + * center freq segment 2: 0 + * VHT basic MCS set: 0xfffc +BSS ac:3b:77:6a:24:e0(on wlan1) + TSF: 741652789170 usec (8d, 14:00:52) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -87.00 dBm + last seen: 1700 ms ago + Information elements from Probe Response frame: + SSID: Grosorboul + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) + HT capabilities: + Capabilities: 0x18fc + HT20 + SM Power Save disabled + RX Greenfield + RX HT20 SGI + RX HT40 SGI + TX STBC + No RX STBC + Max AMSDU length: 7935 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT RX MCS rate indexes supported: 0-15 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 1 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 68:a3:78:6e:d9:24(on wlan1) + TSF: 85494694239 usec (0d, 23:44:54) + freq: 2422 + beacon interval: 96 TUs + capability: ESS (0x0401) + signal: -87.00 dBm + last seen: 1340 ms ago + Information elements from Probe Response frame: + SSID: FreeWifi + HT capabilities: + Capabilities: 0xec + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + No RX STBC + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: No restriction (0x00) + HT TX/RX MCS rate indexes supported: 0-23, 32 + HT operation: + * primary channel: 3 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 14:0c:76:79:c0:d9(on wlan1) + TSF: 572290498911 usec (6d, 14:58:10) + freq: 2427 + beacon interval: 96 TUs + capability: ESS (0x0411) + signal: -86.00 dBm + last seen: 1300 ms ago + Information elements from Probe Response frame: + SSID: freebox_ZFSFUA + HT capabilities: + Capabilities: 0x6c + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + No RX STBC + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: No restriction (0x00) + HT TX/RX MCS rate indexes supported: 0-23, 32 + HT operation: + * primary channel: 4 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 78:81:02:5e:b7:14(on wlan1) + TSF: 4786879917067 usec (55d, 09:41:19) + freq: 2437 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -84.00 dBm + last seen: 960 ms ago + SSID: Livebox-B714 + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) + HT capabilities: + Capabilities: 0x1ad + RX LDPC + HT20 + SM Power Save disabled + RX HT20 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 4 usec (0x05) + HT Max RX data rate: 384 Mbps + HT RX MCS rate indexes supported: 0-23 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 6 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS d6:38:9c:67:00:7a(on wlan1) + TSF: 52271652256 usec (0d, 14:31:11) + freq: 2442 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -75.00 dBm + last seen: 680 ms ago + Information elements from Probe Response frame: + SSID: \xc5\xa4\xc3\xa3h\xc3\xae\xc5\x99 + HT capabilities: + Capabilities: 0x130 + HT20 + Static SM Power Save + RX Greenfield + RX HT20 SGI + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: No restriction (0x00) + HT TX/RX MCS rate indexes supported: 0-7 + HT operation: + * primary channel: 7 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) +BSS 00:19:70:4f:de:f2(on wlan1) + TSF: 223832269184 usec (2d, 14:10:32) + freq: 2462 + beacon interval: 100 TUs + capability: ESS (0x0431) + signal: -86.00 dBm + last seen: 11260 ms ago + Information elements from Probe Response frame: + SSID: Livebox-45cc + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) + HT capabilities: + Capabilities: 0x100c + HT20 + SM Power Save disabled + No RX STBC + Max AMSDU length: 3839 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT RX MCS rate indexes supported: 0-15 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 11 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 6c:38:a1:62:1b:28(on wlan1) + TSF: 2704292761929 usec (31d, 07:11:32) + freq: 2412 + beacon interval: 100 TUs + capability: ESS (0x0c11) + signal: -86.00 dBm + last seen: 1660 ms ago + Information elements from Probe Response frame: + SSID: Bbox-1B7889A9 + HT capabilities: + Capabilities: 0x1ec + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + RX STBC 1-stream + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: No restriction (0x00) + HT RX MCS rate indexes supported: 0-23 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 1 + * secondary channel offset: above + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS 68:a3:78:6e:d9:25(on wlan1) + TSF: 85494497631 usec (0d, 23:44:54) + freq: 2422 + beacon interval: 96 TUs + capability: ESS (0x0411) + signal: -85.00 dBm + last seen: 1500 ms ago + Information elements from Probe Response frame: + SSID: FreeWifi_secure + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: IEEE 802.1X + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) + HT capabilities: + Capabilities: 0xec + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + No RX STBC + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: No restriction (0x00) + HT TX/RX MCS rate indexes supported: 0-23, 32 + HT operation: + * primary channel: 3 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 56:62:21:de:de:70(on wlan1) + TSF: 5762545254750 usec (66d, 16:42:25) + freq: 2422 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -88.00 dBm + last seen: 1430 ms ago + Information elements from Probe Response frame: + SSID: FREEBOX_JOSIANNE_46 + HT capabilities: + Capabilities: 0x106e + HT20/HT40 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + No RX STBC + Max AMSDU length: 3839 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 4 usec (0x05) + HT RX MCS rate indexes supported: 0-15, 32 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 3 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 56:62:21:de:de:71(on wlan1) + TSF: 5762545257087 usec (66d, 16:42:25) + freq: 2422 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -86.00 dBm + last seen: 1430 ms ago + Information elements from Probe Response frame: + SSID: + HT capabilities: + Capabilities: 0x106e + HT20/HT40 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + No RX STBC + Max AMSDU length: 3839 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 4 usec (0x05) + HT RX MCS rate indexes supported: 0-15, 32 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 3 + * secondary channel offset: no secondary + * STA channel width: 20 MHz + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) +BSS 68:a3:78:6e:d9:23(on wlan1) + TSF: 85494792543 usec (0d, 23:44:54) + freq: 2422 + beacon interval: 96 TUs + capability: ESS (0x0411) + signal: -86.00 dBm + last seen: 1270 ms ago + Information elements from Probe Response frame: + SSID: Freebox-6ED922 + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: PSK + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) + HT capabilities: + Capabilities: 0xec + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + No RX STBC + Max AMSDU length: 3839 bytes + No DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: No restriction (0x00) + HT TX/RX MCS rate indexes supported: 0-23, 32 + HT operation: + * primary channel: 3 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 44:ce:7d:20:5c:a4(on wlan1) + TSF: 2322036531897 usec (26d, 21:00:36) + freq: 2437 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -82.00 dBm + last seen: 1030 ms ago + Information elements from Probe Response frame: + SSID: SFR_5CA0 + HT capabilities: + Capabilities: 0x18ec + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + No RX STBC + Max AMSDU length: 7935 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT RX MCS rate indexes supported: 0-15 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 6 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS d2:ce:7d:20:5c:a7(on wlan1) + TSF: 2322036535330 usec (26d, 21:00:36) + freq: 2437 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -87.00 dBm + last seen: 1030 ms ago + Information elements from Probe Response frame: + SSID: SFR WiFi Mobile + RSN: * Version: 1 + * Group cipher: CCMP + * Pairwise ciphers: CCMP + * Authentication suites: IEEE 802.1X + * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) + HT capabilities: + Capabilities: 0x18ec + HT20 + SM Power Save disabled + RX HT20 SGI + RX HT40 SGI + TX STBC + No RX STBC + Max AMSDU length: 7935 bytes + DSSS/CCK HT40 + Maximum RX AMPDU length 65535 bytes (exponent: 0x003) + Minimum RX AMPDU time spacing: 8 usec (0x06) + HT RX MCS rate indexes supported: 0-15 + HT TX MCS rate indexes are undefined + HT operation: + * primary channel: 6 + * secondary channel offset: no secondary + * STA channel width: 20 MHz +BSS 00:22:6b:86:5b:71(on wlan1) + TSF: 13910494312595 usec (161d, 00:01:34) + freq: 2462 + beacon interval: 100 TUs + capability: ESS (0x0411) + signal: -88.00 dBm + last seen: 350 ms ago + Information elements from Probe Response frame: + SSID: linksys + RSN: * Version: 1 + * Group cipher: TKIP + * Pairwise ciphers: CCMP TKIP + * Authentication suites: PSK + * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000) diff --git a/uci.go b/uci.go new file mode 100644 index 0000000..9fac6f8 --- /dev/null +++ b/uci.go @@ -0,0 +1,122 @@ +package owrt + +import ( + "fmt" + "time" +) + +// Action is the result of an UCI action output and return code +type Action struct { + *CommandResult + Command string +} + +// UCI "Object" +type UCI struct { + exec Executor + Wireless *UCIWirelessConf +} + +// NewUCI return an UCI instance to interact with UCI +func NewUCI() *UCI { + exec := &localExecutor{} + wireless := &UCIWirelessConf{} + return &UCI{exec, wireless} +} + +// NewUCIWithExecutor returns a UCI Instance an gives you the ability to provide +// a different command executor than the default one. +func NewUCIWithExecutor(exec Executor) *UCI { + wireless := &UCIWirelessConf{} + return &UCI{exec, wireless} +} + +// uciRun, private method to run the UCI command +func (u *UCI) uciRun(param ...string) *Action { + cmd := "/sbin/uci" + res := u.exec.Run(cmd, param...) + return &Action{res, fmt.Sprintf("%s %s", cmd, param)} +} + +// Add add an entry to UCI configuration, specify the Module and the value +func (u *UCI) Add(module string, name string) *Action { + cmd := "uci add" + commandRes := u.exec.Run(cmd, module, name) + return &Action{commandRes, fmt.Sprintf("%s %s %s", cmd, module, name)} +} + +// Delete delete an entry from UCI configuration specify the entry name +func (u *UCI) Delete(entry string) *Action { + return u.uciRun("delete", entry) +} + +// Set set a value ton an UCI configuration entry +func (u *UCI) Set(entry string, value string) *Action { + return u.uciRun("set", fmt.Sprintf("%s=%s", entry, value)) +} + +// Commit the recent actions to UCI +func (u *UCI) Commit() *Action { + res := u.uciRun("commit") + return res +} + +// Reload reload uci configuration +func (u *UCI) Reload() *Action { + cmdResult := u.exec.Run("reload_config") + + time.Sleep(5 * time.Second) + + return &Action{cmdResult, "reload_config"} +} + +// Show returns the output of uci show command +func (u *UCI) Show(target string) *Action { + cmdRes := u.uciRun("show", target) + return cmdRes +} + +// AddWireless Create a new Wireless entry in UCI configuration +func (u *UCI) AddWireless() *Action { + res := u.uciRun("add", "wireless", "wifi-iface") + return res +} + +// LoadWirelessConf scan UCI configuration and loads saved Wireless configuration +func (u *UCI) LoadWirelessConf() { + u.Wireless = NewUCIWirelessConf(u) + u.Wireless.Load() +} + +// GetWifiIface returns the wifi Interface by Index +func (u *UCI) GetWifiIface(idx int) *UCIWirelessInterface { + ifaces := u.Wireless.Interfaces + if len(ifaces) < idx { + return nil + } + return ifaces[idx] +} + +// GetWifiIfaceByName returns the wifi Interface by Index +func (u *UCI) GetWifiIfaceByName(name string) *UCIWirelessInterface { + ifaces := u.Wireless.Interfaces + if len(ifaces) <= 0 { + return nil + } + for _, ifa := range ifaces { + if ifa.Name == name { + return ifa + } + } + return nil +} + +// GetWifiIfaces wifi interfaces in configuration +func (u *UCI) GetWifiIfaces() []*UCIWirelessInterface { + return u.Wireless.Interfaces +} + +// GetWifiDevices returns the wifi devices in configuration +func (u *UCI) GetWifiDevices() []map[string]string { + return u.Wireless.Devices +} diff --git a/uci_test.go b/uci_test.go new file mode 100644 index 0000000..733d9e2 --- /dev/null +++ b/uci_test.go @@ -0,0 +1,100 @@ +package owrt + +import ( + "fmt" + "testing" +) + +func TestUCIAdd(t *testing.T) { + exec := createMockExecutor("", "", 0) + uci := NewUCIWithExecutor(exec) + res := uci.Add("wireless", "test") + if res.ReturnCode != 0 { + t.Error("Bad Return Code !") + } + if res.Stdout != "" { + fmt.Printf("[%s] - ", res.Stdout) + t.Error("Stdout is not empty ...") + } + if res.Stderr != "" { + fmt.Printf("[%s] - ", res.Stdout) + t.Error("Stderr is not empty ...") + } +} + +func TestUCIAddFailed(t *testing.T) { + exec := createMockExecutor("", "BigError", 3) + uci := NewUCIWithExecutor(exec) + res := uci.Add("wireless", "test") + if res.ReturnCode != 3 { + t.Error("Bad Return Code !") + } +} + +func TestUCIDelete(t *testing.T) { + exec := createMockExecutor("", "", 0) + uci := NewUCIWithExecutor(exec) + res := uci.Delete("wireless.@wifi-iface[1]") + if res.ReturnCode != 0 { + t.Error("Bad Return Code !") + } + if res.Stdout != "" { + fmt.Printf("[%s] - ", res.Stdout) + t.Error("Stdout is not empty ...") + } + if res.Stderr != "" { + fmt.Printf("[%s] - ", res.Stdout) + t.Error("Stderr is not empty ...") + } +} + +func TestUCISet(t *testing.T) { + exec := createMockExecutor("", "", 0) + uci := NewUCIWithExecutor(exec) + res := uci.Set("wireless.@wifi-iface[1].network", "OrionNetwork") + if res.ReturnCode != 0 { + t.Error("Bad Return Code !") + } + if res.Stdout != "" { + fmt.Printf("[%s] - ", res.Stdout) + t.Error("Stdout is not empty ...") + } + if res.Stderr != "" { + fmt.Printf("[%s] - ", res.Stdout) + t.Error("Stderr is not empty ...") + } +} + +func TestUCICommit(t *testing.T) { + exec := createMockExecutor("", "", 0) + uci := NewUCIWithExecutor(exec) + res := uci.Commit() + if res.ReturnCode != 0 { + t.Error("Bad Return Code !") + } + if res.Stdout != "" { + fmt.Printf("[%s] - ", res.Stdout) + t.Error("Stdout is not empty ...") + } + if res.Stderr != "" { + fmt.Printf("[%s] - ", res.Stdout) + t.Error("Stderr is not empty ...") + } +} + +func TestUCIReload(t *testing.T) { + exec := createMockExecutor("", "", 0) + uci := NewUCIWithExecutor(exec) + res := uci.Reload() + if res.ReturnCode != 0 { + t.Error("Bad Return Code !") + } + if res.Stdout != "" { + fmt.Printf("[%s] - ", res.Stdout) + t.Error("Stdout is not empty ...") + } + if res.Stderr != "" { + fmt.Printf("[%s] - ", res.Stdout) + t.Error("Stderr is not empty ...") + } +} diff --git a/uci_wireless_conf.go b/uci_wireless_conf.go new file mode 100644 index 0000000..cf5eecd --- /dev/null +++ b/uci_wireless_conf.go @@ -0,0 +1,179 @@ +package owrt + +import ( + "fmt" + "regexp" + "strconv" + "strings" +) + +// UCIWirelessConf is the representation of UCI Wireless Configuration +type UCIWirelessConf struct { + uciClient *UCI + Devices []map[string]string + DefaultInterface map[string]string + Interfaces []*UCIWirelessInterface +} + +// NewUCIWirelessConf builds a new UCIWirelessConf instance +func NewUCIWirelessConf(uci *UCI) *UCIWirelessConf { + return &UCIWirelessConf{ + uciClient: uci, + Devices: []map[string]string{}, //, 10), + DefaultInterface: map[string]string{}, + Interfaces: []*UCIWirelessInterface{}, //, 10), + } +} + +// parse wireless.default_radio[0-9] section of UCI Configuration +func (wc *UCIWirelessConf) parseDefaultInterface(lines []string) int { + matches := map[string]string{ + "Name": "default_radio0=", + "Type": "default_radio0.type=", + "Channel": "default_radio0.channel=", + "Hwmode": "default_radio0.hwmode=", + "Htmode": "default_radio0.htmode=", + "Country": "default_radio0.country=", + "Path": "default_radio0.path=", + } + + for _, line := range lines { + for key, value := range matches { + if strings.Contains(line, value) { + wc.DefaultInterface[key] = strings.Split(line, "=")[1] + } + } + } + return 0 +} + +func (wc *UCIWirelessConf) parseInterfaces(lines []string) int { + matches := map[string]*regexp.Regexp{ + "Name": regexp.MustCompile(`@wifi-iface\[[0-9]\]=`), + "Device": regexp.MustCompile(`@wifi-iface\[[0-9]\].device=`), + "Mode": regexp.MustCompile(`@wifi-iface\[[0-9]\].mode=`), + "Ssid": regexp.MustCompile(`@wifi-iface\[[0-9]\].ssid=`), + "Encryption": regexp.MustCompile(`@wifi-iface\[[0-9]\].encryption=`), + "Key": regexp.MustCompile(`@wifi-iface\[[0-9]\].key=`), + } + + for _, li := range lines { + var idx int + sIdx := strings.Split(li, "[")[1] + sIdx = strings.Split(sIdx, "]")[0] + if s, err := strconv.ParseInt(sIdx, 10, 32); err == nil { + idx = int(s) + } + + if idx >= len(wc.Interfaces) { + for i := 0; i <= idx; i++ { + wc.Interfaces = append(wc.Interfaces, NewUCIWirelessInterface()) + } + } + + if wc.Interfaces[idx] == nil { + wc.Interfaces[idx] = NewUCIWirelessInterface() + wc.Interfaces[idx].Index = idx + } + + for key, expr := range matches { + if expr.MatchString(li) { + value := strings.Split(li, "=")[1] + if key == "Name" { + wc.Interfaces[idx].Name = value + } + if key == "Device" { + wc.Interfaces[idx].Device = value + //FIXME + //dev := fmt.Sprintf("wireless.%s", value) + //path := wc.uciClient.Show(dev) + //wc.Interfaces[idx].DevicePath = strings.Split(path.Stdout, "=")[1] + } + if key == "Mode" { + wc.Interfaces[idx].Mode = value + } + if key == "Ssid" { + wc.Interfaces[idx].Ssid = value + } + if key == "Encryption" { + wc.Interfaces[idx].Encryption = value + } + if key == "Key" { + wc.Interfaces[idx].Key = value + } + } + } + } + return 0 +} + +func (wc *UCIWirelessConf) parseDevicesConf(lines []string) int { + + matches := map[string]string{ + "Name": "radio[0-9]=", + "Type": "radio[0-9].type=", + "Channel": "radio[0-9].channel=", + "Hwmode": "radio[0-9].hwmode=", + "Htmode": "radio[0-9].htmode=", + "Country": "radio[0-9].country=", + "Path": "radio[0-9].path=", + } + + for _, line := range lines { + var idx int + re := regexp.MustCompile("[0-9]") + rIdx := re.FindString(line) + if s, err := strconv.ParseInt(rIdx, 10, 32); err == nil { + idx = int(s) + } + + if len(wc.Devices) == 0 { + wc.Devices = append(wc.Devices, make(map[string]string)) + } + + if idx >= len(wc.Devices) { + for i := 0; i < idx; i++ { + wc.Devices = append(wc.Devices, make(map[string]string)) + } + } + + if wc.Devices[idx] == nil { + wc.Devices[idx] = make(map[string]string) + } + for key, expr := range matches { + re := regexp.MustCompile(expr) + if re.MatchString(line) { + value := strings.Split(line, "=")[1] + value = strings.Trim(value, "'") + if key == "Name" { + wc.Devices[idx]["Device"] = fmt.Sprintf("radio%d", idx) + } + wc.Devices[idx][key] = value + } + } + + } + return 0 +} + +func grep(lines string, pattern string) []string { + var res []string + for _, line := range strings.Split(strings.TrimSuffix(lines, "\n"), "\n") { + re := regexp.MustCompile(pattern) + if re.MatchString(line) { + res = append(res, line) + } + } + return res +} + +// Load return the Wireless Configuration +func (wc *UCIWirelessConf) Load() *UCIWirelessConf { + conf := wc.uciClient.Show("wireless") + + wc.parseDevicesConf(grep(conf.Stdout, "wireless.radio")) + wc.parseDefaultInterface(grep(conf.Stdout, "wireless.default_radio0")) + wc.parseInterfaces(grep(conf.Stdout, "wireless.@wifi-iface")) + + return wc +} diff --git a/uci_wireless_conf_test.go b/uci_wireless_conf_test.go new file mode 100644 index 0000000..75a6e22 --- /dev/null +++ b/uci_wireless_conf_test.go @@ -0,0 +1,30 @@ +package owrt + +import ( + "io/ioutil" + "testing" +) + +func TestUCIGetWirelessConf(t *testing.T) { + config, err := ioutil.ReadFile("./testdata/uci_show_wireless.txt") + if err != nil { + t.Fatal(err) + } + exec := createMockExecutor(string(config), "", 0) + uci := NewUCIWithExecutor(exec) + uci.LoadWirelessConf() + if g, e := uci.Wireless.DefaultInterface["Name"], "wifi-iface"; g != e { + t.Fatalf("DefaultDevice.Name is expected to be [%s] and we have [%s]", e, g) + } + + config, err = ioutil.ReadFile("./testdata/uci_show_wireless_2_cards.txt") + if err != nil { + t.Fatal(err) + } + exec = createMockExecutor(string(config), "", 0) + uci = NewUCIWithExecutor(exec) + uci.LoadWirelessConf() + if g, e := uci.Wireless.Interfaces[1].Name, "wifi-iface"; g != e { + t.Fatalf("DefaultDevice.Name is expected to be [%s] and we have [%s]", e, g) + } +} diff --git a/uci_wireless_interface.go b/uci_wireless_interface.go new file mode 100644 index 0000000..244f052 --- /dev/null +++ b/uci_wireless_interface.go @@ -0,0 +1,209 @@ +package owrt + +import ( + "fmt" + "os" + "path/filepath" + "regexp" + "strings" +) + +// UCIWirelessInterface is the description of an Wireless interface (cf Openwrt doc) on top of an Wireless Device +type UCIWirelessInterface struct { + Name string + Index int + Device string + DevicePath string + SysDevName string + Mode string + Disabled bool + Ssid string + Bssid string + Network string + Encryption string + Key string + Country string +} + +// NewUCIWirelessInterface builds a new UCIWirelessInterface instance +func NewUCIWirelessInterface() *UCIWirelessInterface { + return &UCIWirelessInterface{} +} + +// GetSysDevName looks for the system name of Wireless Interface +func (wi *UCIWirelessInterface) GetSysDevName(sysDir string) string { + var found string + + if wi.SysDevName != "" { + return wi.SysDevName + } + + if sysDir == "" { + sysDir = "/sys/devices/platform" + } + + if _, err := os.Stat(sysDir); os.IsNotExist(err) { + return "ERROR123-FILE-DONES-NOT-EXIST" + } + + err := filepath.Walk(sysDir, func(path string, f os.FileInfo, _ error) error { + patt := fmt.Sprintf("%s/%s/.*/address", wi.DevicePath, "net") + r, err := regexp.MatchString(patt, path) + if err == nil && r { + res := strings.Split(path, "/") + idx := len(res) - 2 + found = res[idx] + } + return nil + }) + if err != nil { + return err.Error() + } + wi.SysDevName = found + return found +} + +// Create add a new entry for wifi interface in UCI Configuration +func (wi *UCIWirelessInterface) Create(uci *UCI) *Action { + var confPrefix string + addRes := uci.AddWireless() + if addRes.ReturnCode != 0 { + return addRes + } + if wi.Index <= 0 { + confPrefix = fmt.Sprintf("wireless.@wifi-iface[%d]", len(uci.Wireless.Interfaces)) + } else { + confPrefix = fmt.Sprintf("wireless.@wifi-iface[%d]", wi.Index) + } + conf := make(map[string][]string) + + conf["network"] = append(conf["network"], fmt.Sprintf("%s.network", confPrefix), wi.Network) + conf["ssid"] = append(conf["ssid"], fmt.Sprintf("%s.ssid", confPrefix), wi.Ssid) + conf["enc"] = append(conf["enc"], fmt.Sprintf("%s.encryption", confPrefix), wi.Encryption) + conf["device"] = append(conf["device"], fmt.Sprintf("%s.device", confPrefix), wi.Device) + conf["mode"] = append(conf["mode"], fmt.Sprintf("%s.mode", confPrefix), wi.Mode) + conf["bssid"] = append(conf["bssid"], fmt.Sprintf("%s.bssid", confPrefix), wi.Bssid) + conf["key"] = append(conf["key"], fmt.Sprintf("%s.key", confPrefix), wi.Key) + conf["disabled"] = append(conf["disabled"], fmt.Sprintf("%s.disabled", confPrefix), "0") + conf["country"] = append(conf["country"], fmt.Sprintf("%s.country", confPrefix), wi.Country) + + for _, value := range conf { + if value[1] != "" { + result := uci.Set(value[0], value[1]) + if result.ReturnCode != 0 { + return result + } + } + } + + return &Action{ + CommandResult: &CommandResult{ + Stdout: "", + Stderr: "", + ReturnCode: 0, + }, + } +} + +// Save commit and relaod configuration (writes it to files !) +func (wi *UCIWirelessInterface) Save(uci *UCI) *Action { + commitRes := uci.Commit() + if commitRes.ReturnCode != 0 { + return commitRes + } + + reload := uci.Reload() + return reload +} + +// SysAdd creates the interface in the system using iw command +func (wi *UCIWirelessInterface) SysAdd(uci *UCI) *Action { + cmd := "iw" + opt := "phy" + phydev := fmt.Sprintf("phy%d", wi.Index) + what := "interface" + act := "add" + tpe := "managed" + + if wi.SysDevName == "" { + wi.SysDevName = fmt.Sprintf("tmp.radio%d", wi.Index) + } + + res := uci.exec.Run(cmd, opt, phydev, what, act, wi.SysDevName, "type", tpe) + return &Action{ + CommandResult: res, + Command: fmt.Sprintf("%s %s %s %s %s %s %s %s", cmd, opt, phydev, what, act, wi.SysDevName, "type", tpe), + } +} + +// SysDel deletes the interface in the system using iw command +func (wi *UCIWirelessInterface) SysDel(uci *UCI) *Action { + cmd := "iw" + opt := "dev" + act := "del" + + if wi.SysDevName == "" { + wi.SysDevName = fmt.Sprintf("tmp.radio%d", wi.Index) + } + + res := uci.exec.Run(cmd, opt, wi.SysDevName, act) + return &Action{ + CommandResult: res, + Command: fmt.Sprintf("%s %s %s %s", cmd, opt, wi.SysDevName, act), + } +} + +// Up brings the interface up in the system using ip command +func (wi *UCIWirelessInterface) Up(uci *UCI) *Action { + cmd := "ip" + opt := "link" + act := "set" + wht := "dev" + + res := uci.exec.Run(cmd, opt, act, wht, wi.SysDevName, "up") + return &Action{ + CommandResult: res, + } +} + +// Delete remove wifi interface from UCI Configuration +func (wi *UCIWirelessInterface) Delete(uci *UCI) *Action { + toDelete := fmt.Sprintf("wireless.@wifi-iface[%d]", wi.Index) + del := uci.Delete(toDelete) + if del.ReturnCode != 0 { + return del + } + return uci.Commit() +} + +// Update add a new entry for wifi interface in UCI Configuration +func (wi *UCIWirelessInterface) Update(uci *UCI) *Action { + wi.Delete(uci) + create := wi.Create(uci) + if create.ReturnCode != 0 { + return create + } + return uci.Commit() +} + +// Scan starts a scan for wifi networks with this device +func (wi *UCIWirelessInterface) Scan() []*WifiCell { + devName := wi.GetSysDevName("") + + wifi := NewWifiScanner(devName) + return wifi.Scan() +} + +// Connect updates configuration to connect to the WifiCell +func (wi *UCIWirelessInterface) Connect(uci *UCI, cell *WifiCell, secret string) *Action { + wi.Ssid = cell.Ssid + wi.Bssid = cell.MacAddress + wi.Encryption = cell.Encryption + wi.Mode = "sta" + wi.Key = secret + res := wi.Update(uci) + if res.ReturnCode != 0 { + return res + } + return wi.Save(uci) +} diff --git a/uci_wireless_interface_test.go b/uci_wireless_interface_test.go new file mode 100644 index 0000000..37a0196 --- /dev/null +++ b/uci_wireless_interface_test.go @@ -0,0 +1,131 @@ +package owrt + +import ( + "testing" +) + +const ( + ifaceName = "Test" + ifaceNetwork = "Pyxis" + ifaceSysDevName = "wlanX" + ifaceEnc = "psk" + ifaceSSID = "PyxisWifi" + ifaceKey = "qsmdflkjqslmdfkjqslmfkdj" + ifaceDevice = "radioX" + ifaceMode = "ap" + ifaceBssid = "00:00:00:00:00" +) + +func TestGetSysDevName(t *testing.T) { + iface := NewUCIWirelessInterface() + iface.Name = ifaceName + iface.Index = 1 + iface.Device = ifaceDevice + iface.DevicePath = "soc/soc:pcie/pci0000:00/0000:00:02.0/0000:02:00.0" + iface.Mode = ifaceMode + iface.Ssid = ifaceSSID + iface.Bssid = ifaceBssid + iface.Network = ifaceNetwork + iface.Encryption = ifaceEnc + iface.Key = ifaceKey + + if g, e := iface.GetSysDevName("testdata/sys/"), "wlan1"; g != e { + t.Fatalf("UCIWirelessInterface.GetDeviceSysName() failed ! Got: %s Expect: %s", g, e) + } + + iface.SysDevName = "wlanX" + if g, e := iface.GetSysDevName("testdata/sys/"), "wlanX"; g != e { + t.Fatalf("UCIWirelessInterface.GetDeviceSysName() failed ! Got: %s Expect: %s", g, e) + } +} + +func TestCreate(t *testing.T) { + exec := createMockExecutor("", "", 0) + uci := NewUCIWithExecutor(exec) + + iface := NewUCIWirelessInterface() + iface.Name = ifaceName + iface.Device = ifaceDevice + iface.Mode = ifaceMode + iface.Ssid = ifaceSSID + iface.Bssid = ifaceBssid + iface.Network = ifaceNetwork + iface.Encryption = ifaceEnc + iface.Key = ifaceKey + + if iface.Create(uci).ReturnCode != 0 { + t.Fatalf("UCIWirelessInterface.Create() failed !") + } +} + +func TestUpdate(t *testing.T) { + exec := createMockExecutor("", "", 0) + uci := NewUCIWithExecutor(exec) + + iface := NewUCIWirelessInterface() + iface.Name = ifaceName + iface.Index = 1 + iface.Device = ifaceDevice + iface.SysDevName = ifaceSysDevName + iface.Mode = ifaceMode + iface.Ssid = ifaceSSID + iface.Bssid = ifaceBssid + iface.Network = ifaceNetwork + iface.Encryption = ifaceEnc + iface.Key = ifaceKey + + if iface.Create(uci).ReturnCode != 0 { + t.Fatalf("UCIWirelessInterface.Create() failed !") + } + + iface.Name = "Tutu" + + if iface.Update(uci).ReturnCode != 0 { + t.Fatalf("UCIWirelessInterface.Update() failed !") + } +} + +func TestDelete(t *testing.T) { + exec := createMockExecutor("", "", 0) + uci := NewUCIWithExecutor(exec) + + iface := NewUCIWirelessInterface() + iface.Name = ifaceName + iface.Index = 1 + iface.Device = ifaceDevice + iface.SysDevName = ifaceSysDevName + iface.Mode = ifaceMode + iface.Ssid = ifaceSSID + iface.Bssid = ifaceBssid + iface.Network = ifaceNetwork + iface.Encryption = ifaceEnc + iface.Key = ifaceKey + + if iface.Delete(uci).ReturnCode != 0 { + t.Fatalf("UCIWirelessInterface.Delete() failed !") + } +} + +func TestConnect(t *testing.T) { + + exec := createMockExecutor("", "", 0) + uci := NewUCIWithExecutor(exec) + + iface := NewUCIWirelessInterface() + iface.Name = ifaceName + iface.Index = 1 + iface.Device = ifaceDevice + iface.SysDevName = ifaceSysDevName + iface.Mode = ifaceMode + iface.Ssid = ifaceSSID + iface.Bssid = ifaceBssid + iface.Network = ifaceNetwork + iface.Encryption = ifaceEnc + iface.Key = ifaceKey + + wifiCell := NewWifiCell("PyxisWifi", "01:01:01:01:01", "psk") + + if iface.Connect(uci, wifiCell, "Toto").ReturnCode != 0 { + t.Fatalf("UCIWirelessInterface.Delete() failed !") + } +} diff --git a/wifi_cell.go b/wifi_cell.go new file mode 100644 index 0000000..0dab40d --- /dev/null +++ b/wifi_cell.go @@ -0,0 +1,17 @@ +package owrt + +// WifiCell reprensents wifi network Cell +type WifiCell struct { + Ssid string + MacAddress string + Encryption string +} + +// NewWifiCell returns a new WifiCell object +func NewWifiCell(ssid string, mac string, encrypt string) *WifiCell { + return &WifiCell{ + Ssid: ssid, + MacAddress: mac, + Encryption: encrypt, + } +} diff --git a/wifi_scanner.go b/wifi_scanner.go new file mode 100644 index 0000000..d0835ea --- /dev/null +++ b/wifi_scanner.go @@ -0,0 +1,126 @@ +package owrt + +import ( + "regexp" + "strings" + "time" +) + +// NONE contains the string for "none" +const NONE = "none" + +// WifiScanner gives access to al OpenWRT Wifi Scan operations +type WifiScanner struct { + exec Executor + iface string + Cells []*WifiCell +} + +// NewWifiScanner return an UCI instance to interact with UCI +func NewWifiScanner(wIface string) *WifiScanner { + exec := &localExecutor{} + iface := wIface + Cells := []*WifiCell{} + return &WifiScanner{exec, iface, Cells} +} + +// NewWifiWithExecutor returns a Wifi Instance an gives you the ability to provide +// a different command executor than the default one. +func NewWifiWithExecutor(exec Executor, wIface string) *WifiScanner { + return &WifiScanner{exec, wIface, nil} +} + +func (w *WifiScanner) getEncryption(line string) string { + var enc string + if strings.Contains(line, "PSK") { + enc = "psk2" + } else if strings.Contains(line, NONE) { + enc = NONE + } else { + enc = "unkn" + } + return enc +} + +func (w *WifiScanner) parseWifiCells(stdout string) int { + mac, ssid, enc := "", "", "" + encOff := false + macExpr := `([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])` + + for _, line := range strings.Split(strings.TrimSuffix(stdout, "\n"), "\n") { + expr := regexp.MustCompile("^BSS") + if expr.MatchString(line) { + if len(mac) != 0 && len(ssid) != 0 && len(enc) == 0 { + enc = NONE + cell := NewWifiCell(ssid, mac, enc) + w.Cells = append(w.Cells, cell) + ssid, enc = "", "" + } + macRegexp := regexp.MustCompile(macExpr) + mac = macRegexp.FindString(line) + } + + if strings.Contains(line, "SSID:") { + ssid = strings.Split(line, " ")[1] + ssid = strings.Trim(ssid, "\"") + //ssid = strings.Trim(ssid, " ") + if ssid == "" { + ssid = " " + } + } + + if strings.Contains(line, "Encryption key:off") { + encOff = true + } + + if strings.Contains(line, "Authentication suites") { + enc = w.getEncryption(line) + } + + if len(mac) > 0 && len(ssid) > 0 && (len(enc) > 0 || encOff) { + if encOff { + enc = NONE + encOff = false + } + cell := NewWifiCell(ssid, mac, enc) + w.Cells = append(w.Cells, cell) + ssid, mac, enc = "", "", "" + } + } + return 0 +} + +// GetWifiCells retrieves all available wifi cells for a card ! +func (w *WifiScanner) GetWifiCells() int { + var res *CommandResult + for try := 0; try != 20; try++ { + res = w.exec.Run("iw", w.iface, "scan") + if res.ReturnCode == 0 { + parsing := w.parseWifiCells(res.Stdout) + if parsing == 0 { + if len(w.Cells) == 0 { + return 242 + } + } + return parsing + } + time.Sleep(time.Second * 2) + } + return res.ReturnCode +} + +// GetCell retreives an WifiCell by SSID provided in parameter +func (w *WifiScanner) GetCell(ssid string) *WifiCell { + for _, v := range w.Cells { + if v.Ssid == ssid { + return v + } + } + return nil +} + +// Scan retreives a list of available wifi cells +func (w *WifiScanner) Scan() []*WifiCell { + _ = w.GetWifiCells() + return w.Cells +} diff --git a/wifi_scanner_test.go b/wifi_scanner_test.go new file mode 100644 index 0000000..568ca59 --- /dev/null +++ b/wifi_scanner_test.go @@ -0,0 +1,71 @@ +package owrt + +import ( + "fmt" + "io/ioutil" + "testing" +) + +// Test GestWifiCells method with 3 Cells +func TestGetWifiCells(t *testing.T) { + + cellList, err := ioutil.ReadFile("testdata/wifi_cells_output_3.txt") + if err != nil { + t.Fatal(err) + } + + exec := createMockExecutor(string(cellList), "", 0) + wifi := NewWifiWithExecutor(exec, "wlan1") + _ = wifi.GetWifiCells() + if len(wifi.Cells) != 3 { + fmt.Printf("Size of wifi.Cells is %d and not 3 !!!\n", len(wifi.Cells)) + t.Error("Cell list is empty ... This can not append !! Fix your code Dummy !") + } + if g, e := wifi.Cells[0].Ssid, "PyxisWifi"; g != e { + t.Errorf("The first Cell have a bad SSID !\n %s is expected and we have %s", e, g) + } + + if g, e := wifi.Cells[0].MacAddress, "74:3e:2b:08:41:1c"; g != e { + t.Errorf("The first Cell have a bad MAC !\n [%s] is expected and we have [%s]", e, g) + } + + if g, e := wifi.Cells[1].Encryption, "none"; g != e { + t.Errorf("The first Cell have a bad Encryption!\n %s is expected and we have %s", e, g) + } + + if g, e := wifi.Cells[2].Encryption, "psk2"; g != e { + t.Errorf("The last Cell have a bad Encryption!\n %s is expected and we have %s", e, g) + } + + if g, e := wifi.Cells[2].MacAddress, "20:47:da:b7:0e:5c"; g != e { + t.Errorf("The last Cell have a bad MAC !\n %s is expected and we have %s", e, g) + } +} + +// Test GestWifiCells method with empty list +func TestGetWifiCellsEmpty(t *testing.T) { + exec := createMockExecutor("", "", 0) + wifi := NewWifiWithExecutor(exec, "wlan1") + _ = wifi.GetWifiCells() + if len(wifi.Cells) != 0 { + fmt.Printf("Size of wifi.Cells is %d and not 0 !!!\n", len(wifi.Cells)) + t.Error("Cell list is empty ... This can not append !! Fix your code Dummy !") + } +} + +// Test GestWifiCells method with 3 Cells +func TestGetWifiCellsLarge(t *testing.T) { + + cellList, err := ioutil.ReadFile("testdata/wifi_cells_output_large.txt") + if err != nil { + t.Fatal(err) + } + + exec := createMockExecutor(string(cellList), "", 0) + wifi := NewWifiWithExecutor(exec, "wlan1") + _ = wifi.GetWifiCells() + if len(wifi.Cells) != 40 { + fmt.Printf("Size of wifi.Cells is %d and not 40 !!!\n", len(wifi.Cells)) + t.Error("Cell list is empty ... This can not append !! Fix your code Dummy !") + } +}