From 70ce11a1e5e1c9cc1bece8bfafbb5b2d0dcb6d8b Mon Sep 17 00:00:00 2001 From: Howard Stark Date: Sun, 26 Nov 2017 22:50:15 -0800 Subject: [PATCH 1/4] Added multicast group id constants --- internal/nl80211/multicast.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 internal/nl80211/multicast.go diff --git a/internal/nl80211/multicast.go b/internal/nl80211/multicast.go new file mode 100644 index 0000000..df6cf24 --- /dev/null +++ b/internal/nl80211/multicast.go @@ -0,0 +1,17 @@ +package nl80211 + +// nl80211_multicast_group enumeration from nl80211/nl80211.c:39 +// +// WARNING: THIS IS MANUALLY CREATED. CURRENTLY THE c-for-go LIB DOES NOT +// SUPPORT .c FILES AND ENUMERATIONS DEFINED THEREWITH. +// +// SEE https://github.com/xlab/nl80211/issues/1 FOR FOLLOWUP ON SOLUTIONS. +const ( + McgrpConfig = iota + McgrpScan + McgrpRegulatory + McgrpMlme + McgrpVendor + McgrpNan + McgrpTestmode +) From 79daaf7e2585d4001d3c6e17e79edfe5d4df539d Mon Sep 17 00:00:00 2001 From: Howard Stark Date: Tue, 2 Jan 2018 19:19:06 -0500 Subject: [PATCH 2/4] Added initial PoC for AP scanning --- client_linux.go | 154 +++++++++++++++++++++++++++++++++++++++++++++++- wifi.go | 23 ++++++++ 2 files changed, 176 insertions(+), 1 deletion(-) diff --git a/client_linux.go b/client_linux.go index bc0b472..adac886 100644 --- a/client_linux.go +++ b/client_linux.go @@ -14,7 +14,8 @@ import ( "github.com/mdlayher/genetlink" "github.com/mdlayher/netlink" "github.com/mdlayher/netlink/nlenc" - "github.com/mdlayher/wifi/internal/nl80211" + "github.com/howardstark/wifi/internal/nl80211" + ) // Errors which may occur when interacting with generic netlink. @@ -121,6 +122,138 @@ func (c *client) BSS(ifi *Interface) (*BSS, error) { return parseBSS(msgs) } +func (c *client) ScanAPs(ifi *Interface) ([]*BSS, error) { + family, err := c.c.GetFamily(nl80211.GenlName) + if err != nil { + return nil, err + } + var mcastScan genetlink.MulticastGroup + for _, mcast := range family.Groups { + if mcast.Name == nl80211.MulticastGroupScan { + mcastScan = mcast + } + } + if mcastScan.Name != nl80211.MulticastGroupScan { + return nil, errors.New("multicast group \"scan\" unavailable") + } + + err = c.c.JoinGroup(mcastScan.ID) + if err != nil { + return nil, err + } + + nestedAttrs, err := netlink.MarshalAttributes([]netlink.Attribute{ + { + Type: nl80211.SchedScanMatchAttrSsid, + Length: 0, + Data: nlenc.Bytes(""), + }, + }) + if err != nil { + return nil, err + } + + attrs, err := netlink.MarshalAttributes([]netlink.Attribute{ + { + Type: nl80211.AttrScanSsids, + Nested: true, + Length: uint16(len(nestedAttrs)), + Data: nestedAttrs, + }, + { + Type: nl80211.AttrIfindex, + Data: nlenc.Uint32Bytes(uint32(ifi.Index)), + }, + }) + if err != nil { + return nil, err + } + + req := genetlink.Message{ + Header: genetlink.Header{ + Command: nl80211.CmdTriggerScan, + Version: c.familyVersion, + }, + Data: attrs, + } + + flags := netlink.HeaderFlagsRequest + msgs, err := c.c.Execute(req, c.familyID, flags) + if err != nil { + return nil, err + } + + for _, m := range msgs { + if m.Header.Version != c.familyVersion { + return nil, errInvalidFamilyVersion + } + if m.Header.Command == nl80211.CmdScanAborted { + return nil, errors.New("nl80211 ap scan has been aborted") + } + if m.Header.Command == nl80211.CmdNewScanResults { + break + } + } + + err = c.c.LeaveGroup(mcastScan.ID) + if err != nil { + return nil, err + } + + attrs, err = netlink.MarshalAttributes([]netlink.Attribute{ + { + Type: nl80211.AttrIfindex, + Data: nlenc.Uint32Bytes(uint32(ifi.Index)), + }, + }) + + if err != nil { + return nil, err + } + + req = genetlink.Message{ + Header: genetlink.Header{ + Command: nl80211.CmdGetScan, + Version: c.familyVersion, + }, + Data: attrs, + } + + flags = netlink.HeaderFlagsRequest | netlink.HeaderFlagsDump + msgs, err = c.c.Execute(req, c.familyID, flags) + if err != nil { + return nil, err + } + + if err := c.checkMessages(msgs, nl80211.CmdNewScanResults); err != nil { + return nil, err + } + + bssm, err := parseBSSMulti(msgs) + if err != nil { + return nil, err + } + + return bssm, nil +} + +//func (c *client) SetChannel(ifi *Interface, channel int) error { +// b, err := netlink.MarshalAttributes(ifi.idAttrs()) +// if err != nil { +// return err +// } +// +// req := genetlink.Message{ +// Header: genetlink.Header{ +// Command: nl80211.CmdSetChannel, +// Version: c.familyVersion, +// }, +// Data: b, +// } +// +// flags := netlink.HeaderFlagsRequest +//} + // StationInfo requests that nl80211 return station info for the specified // Interface. func (c *client) StationInfo(ifi *Interface) (*StationInfo, error) { @@ -278,6 +411,25 @@ func parseBSS(msgs []genetlink.Message) (*BSS, error) { return nil, os.ErrNotExist } +func parseBSSMulti(msgs []genetlink.Message) ([]*BSS, error) { + bssm := make([]*BSS, 0, len(msgs)) + for _, m := range msgs { + attrs, err := netlink.UnmarshalAttributes(m.Data) + if err != nil { + return nil, err + } + + var bss BSS + if err := (&bss).parseAttributes(attrs); err != nil { + return nil, err + } + + bssm = append(bssm, &bss) + } + + return bssm, nil +} + // parseAttributes parses netlink attributes into a BSS's fields. func (b *BSS) parseAttributes(attrs []netlink.Attribute) error { for _, a := range attrs { diff --git a/wifi.go b/wifi.go index e16bcc4..4876364 100644 --- a/wifi.go +++ b/wifi.go @@ -262,3 +262,26 @@ func parseIEs(b []byte) ([]ie, error) { return ies, nil } + +// FreqToChannel returns the channel of the specified +// frequency (in MHz) for the 2.4GHz and 5GHz ranges. +func FreqToChannel(freq int) int { + if freq == 2484 { + return 14 + } + if freq < 2484 { + return (freq - 2407) / 5 + } + return freq / 5 - 1000 +} + +func ChannelToFreq5GHz(channel int) int { + return (channel + 1000) * 5 +} + +func ChannelToFreq2Ghz(channel int) int { + if channel == 14 { + return 2484 + } + return (channel * 5) + 2407 +} \ No newline at end of file From 4aab03c659f2c6b1772217a189f2295d1335b25d Mon Sep 17 00:00:00 2001 From: Howard Stark Date: Tue, 2 Jan 2018 20:22:59 -0500 Subject: [PATCH 3/4] Incomplete unit test for client.ScanAP --- client_linux.go | 14 ++++---- client_linux_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/client_linux.go b/client_linux.go index adac886..6ad72c0 100644 --- a/client_linux.go +++ b/client_linux.go @@ -20,9 +20,11 @@ import ( // Errors which may occur when interacting with generic netlink. var ( - errMultipleMessages = errors.New("expected only one generic netlink message") - errInvalidCommand = errors.New("invalid generic netlink response command") - errInvalidFamilyVersion = errors.New("invalid generic netlink response family version") + errMultipleMessages = errors.New("expected only one generic netlink message") + errInvalidCommand = errors.New("invalid generic netlink response command") + errInvalidFamilyVersion = errors.New("invalid generic netlink response family version") + errMissingMulticastGroupScan = errors.New("scan multicast group unavailable") + errScanAborted = errors.New("scan aborted") ) var _ osClient = &client{} @@ -134,7 +136,7 @@ func (c *client) ScanAPs(ifi *Interface) ([]*BSS, error) { } } if mcastScan.Name != nl80211.MulticastGroupScan { - return nil, errors.New("multicast group \"scan\" unavailable") + return nil, errMissingMulticastGroupScan } err = c.c.JoinGroup(mcastScan.ID) @@ -177,7 +179,7 @@ func (c *client) ScanAPs(ifi *Interface) ([]*BSS, error) { Data: attrs, } - flags := netlink.HeaderFlagsRequest + flags := netlink.HeaderFlagsRequest | netlink.HeaderFlagsDump msgs, err := c.c.Execute(req, c.familyID, flags) if err != nil { return nil, err @@ -188,7 +190,7 @@ func (c *client) ScanAPs(ifi *Interface) ([]*BSS, error) { return nil, errInvalidFamilyVersion } if m.Header.Command == nl80211.CmdScanAborted { - return nil, errors.New("nl80211 ap scan has been aborted") + return nil, errScanAborted } if m.Header.Command == nl80211.CmdNewScanResults { break diff --git a/client_linux_test.go b/client_linux_test.go index d3dd3f4..3522c9c 100644 --- a/client_linux_test.go +++ b/client_linux_test.go @@ -20,7 +20,7 @@ import ( "github.com/mdlayher/genetlink/genltest" "github.com/mdlayher/netlink" "github.com/mdlayher/netlink/nlenc" - "github.com/mdlayher/wifi/internal/nl80211" + "github.com/howardstark/wifi/internal/nl80211" ) func TestLinux_clientInterfacesBadResponseCommand(t *testing.T) { @@ -265,6 +265,80 @@ func TestLinux_clientBSSOK(t *testing.T) { } } +func TestLinux_clientScanAPOK(t *testing.T) { + ifi := &Interface{ + Index: 1, + HardwareAddr: net.HardwareAddr{0xe, 0xad, 0xbe, 0xef, 0xde, 0xad}, + } + + want := []*BSS{ + { + SSID: "Welcome", + BSSID: net.HardwareAddr{0x01, 0x18, 0x99, 0x98, 0x81, 0x99}, + Frequency: 2462, + BeaconInterval: 100 * 1024 * time.Microsecond, + LastSeen: 10 * time.Second, + }, + { + SSID: "吃了吗?", + BSSID: net.HardwareAddr{0x91, 0x19, 0x72, 0x53, 0x00, 0x00}, + Frequency: 2462, + BeaconInterval: 100 * 1024 * time.Microsecond, + LastSeen: 10 * time.Second, + }, + } + + const flags = netlink.HeaderFlagsRequest | netlink.HeaderFlagsDump + + msgsFn := mustMessages(t, nl80211.CmdNewScanResults, want) + + c := testClient(t, checkRequest(nl80211.CmdTriggerScan, flags, + func(greq genetlink.Message, nreq netlink.Message) ([]genetlink.Message, error) { + nestedAttrs, err := netlink.MarshalAttributes([]netlink.Attribute{ + { + Type: nl80211.SchedScanMatchAttrSsid, + Length: 0, + Data: nlenc.Bytes(""), + }, + }) + if err != nil { + return nil, err + } + expAttrs := []netlink.Attribute{ + { + Type: nl80211.AttrScanSsids, + Nested: true, + Length: uint16(len(nestedAttrs)), + Data: nestedAttrs, + }, + { + Type: nl80211.AttrIfindex, + Data: nlenc.Uint32Bytes(uint32(ifi.Index)), + }, + } + + attrs, err := netlink.UnmarshalAttributes(greq.Data) + if err != nil { + t.Fatalf("failed to unmarshal attributes: %v", err) + } + + if diff := diffNetlinkAttributes(expAttrs, attrs); diff != "" { + t.Fatalf("unexpected request netlink attributes (-want +got):\n%s", diff) + } + + return msgsFn(greq, nreq) + }, + )) + got, err := c.ScanAPs(ifi) + if err != nil { + log.Fatalf("unexpected error: %v", err) + } + + if !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected BSS:\n- want: %v\n- got: %v", want, got) + } +} + func TestLinux_clientStationInfoMissingAttributeIsNotExist(t *testing.T) { c := testClient(t, func(_ genetlink.Message, _ netlink.Message) ([]genetlink.Message, error) { // One message without station info attribute @@ -574,6 +648,10 @@ func mustMessages(t *testing.T, command uint8, want interface{}) genltest.Func { for _, x := range xs { as = append(as, x) } + case []*BSS: + for _, x := range xs { + as = append(as, x) + } case *BSS: as = append(as, xs) case *StationInfo: From 29d16a7aa53873804fd12c010817ed671432246c Mon Sep 17 00:00:00 2001 From: Howard Stark Date: Thu, 22 Apr 2021 11:17:18 -0700 Subject: [PATCH 4/4] Fixed bad merge --- client_linux.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/client_linux.go b/client_linux.go index 2a8530c..c1bf4ea 100644 --- a/client_linux.go +++ b/client_linux.go @@ -19,16 +19,11 @@ import ( // Errors which may occur when interacting with generic netlink. var ( -<<<<<<< HEAD errMultipleMessages = errors.New("expected only one generic netlink message") errInvalidCommand = errors.New("invalid generic netlink response command") errInvalidFamilyVersion = errors.New("invalid generic netlink response family version") - errMissingMulticastGroupScan = errors.New("scan multicast group unavailable") - errScanAborted = errors.New("scan aborted") -======= - errInvalidCommand = errors.New("invalid generic netlink response command") - errInvalidFamilyVersion = errors.New("invalid generic netlink response family version") ->>>>>>> upstream/master + errMissingMulticastGroupScan = errors.New("scan multicast group unavailable") + errScanAborted = errors.New("scan aborted") ) var _ osClient = &client{} @@ -261,10 +256,7 @@ func (c *client) ScanAPs(ifi *Interface) ([]*BSS, error) { // flags := netlink.HeaderFlagsRequest //} -// StationInfo requests that nl80211 return station info for the specified -======= // StationInfo requests that nl80211 return all station info for the specified ->>>>>>> upstream/master // Interface. func (c *client) StationInfo(ifi *Interface) ([]*StationInfo, error) { b, err := netlink.MarshalAttributes(ifi.idAttrs())