capture packages
This commit is contained in:
commit
5cea0a4ca2
|
@ -0,0 +1,26 @@
|
|||
workspace:
|
||||
base: /go
|
||||
path: src/dev.sum7.eu/wifictld/analyzer
|
||||
|
||||
pipeline:
|
||||
build:
|
||||
image: golang:latest
|
||||
commands:
|
||||
- go get ./...
|
||||
- go build
|
||||
codestyle:
|
||||
image: golang:latest
|
||||
commands:
|
||||
- go get github.com/client9/misspell/cmd/misspell
|
||||
- misspell -error .
|
||||
- if [ -n "$(gofmt -s -l .)" ]; then echo "Go code is not formatted, run 'gofmt -s -w .'" >&2; exit 1; fi
|
||||
test:
|
||||
image: golang:latest
|
||||
commands:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
- go test ./... -v -cover
|
||||
test-race:
|
||||
image: golang:latest
|
||||
commands:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
- go test ./... -v -race
|
|
@ -0,0 +1,161 @@
|
|||
package capture
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"dev.sum7.eu/wifictld/analyzer/data"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
//Collector for capture
|
||||
type Collector struct {
|
||||
connections map[string]*net.UDPConn
|
||||
handler data.Handler
|
||||
queue chan *Packet
|
||||
stop chan interface{}
|
||||
}
|
||||
|
||||
// NewCollector creates a Collector struct
|
||||
func NewCollector(handler data.Handler, ifaces []IFaceConfig) *Collector {
|
||||
|
||||
coll := &Collector{
|
||||
handler: handler,
|
||||
queue: make(chan *Packet, 400),
|
||||
stop: make(chan interface{}),
|
||||
connections: make(map[string]*net.UDPConn),
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
coll.listenUDP(iface)
|
||||
}
|
||||
|
||||
go coll.parser()
|
||||
|
||||
return coll
|
||||
}
|
||||
|
||||
// Close Collector
|
||||
func (coll *Collector) Close() {
|
||||
close(coll.stop)
|
||||
for _, conn := range coll.connections {
|
||||
conn.Close()
|
||||
}
|
||||
close(coll.queue)
|
||||
}
|
||||
|
||||
func (coll *Collector) listenUDP(iface IFaceConfig) {
|
||||
ip := net.ParseIP(iface.IPAddress)
|
||||
var conn *net.UDPConn
|
||||
var err error
|
||||
|
||||
if ip.IsMulticast() {
|
||||
ifx, err := net.InterfaceByName(iface.InterfaceName)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
// Open socket
|
||||
conn, err = net.ListenMulticastUDP("udp", ifx, &net.UDPAddr{
|
||||
IP: ip,
|
||||
Port: iface.Port,
|
||||
})
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
} else {
|
||||
// Open socket
|
||||
conn, err = net.ListenUDP("udp", &net.UDPAddr{
|
||||
IP: ip,
|
||||
Port: iface.Port,
|
||||
Zone: iface.InterfaceName,
|
||||
})
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
conn.SetReadBuffer(MaxDataGramSize)
|
||||
|
||||
coll.connections[iface.InterfaceName] = conn
|
||||
|
||||
// Start receiver
|
||||
go coll.receiver(conn)
|
||||
}
|
||||
|
||||
// Packet of the respond request
|
||||
type Packet struct {
|
||||
Address *net.UDPAddr
|
||||
Raw []byte
|
||||
}
|
||||
|
||||
func (coll *Collector) parser() {
|
||||
for obj := range coll.queue {
|
||||
msg, err := data.NewSocketMSG(obj.Raw)
|
||||
if err != nil {
|
||||
log.Warnf("unable to unmarshal request from %s: %s", obj.Address.String(), err)
|
||||
continue
|
||||
}
|
||||
response, err := coll.handler(obj.Address, msg)
|
||||
if err != nil {
|
||||
log.Warnf("unable to handle request from %s: %s", obj.Address.String(), err)
|
||||
continue
|
||||
}
|
||||
if response != nil {
|
||||
coll.SendTo(obj.Address, response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SendTo a specifical address
|
||||
func (coll *Collector) SendTo(addr *net.UDPAddr, msg *data.SocketMSG) {
|
||||
data, err := msg.Marshal()
|
||||
if err != nil {
|
||||
log.Warnf("unable to marshal response for %s: %s", addr.String(), err)
|
||||
return
|
||||
}
|
||||
conn, ok := coll.connections[addr.Zone]
|
||||
if ok {
|
||||
conn.WriteToUDP(data, addr)
|
||||
return
|
||||
}
|
||||
for _, conn := range coll.connections {
|
||||
conn.WriteToUDP(data, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Send to every connection to default address
|
||||
func (coll *Collector) Send(msg *data.SocketMSG) {
|
||||
data, err := msg.Marshal()
|
||||
if err != nil {
|
||||
log.Warnf("unable to marshal response: %s", err)
|
||||
return
|
||||
}
|
||||
for ifname, conn := range coll.connections {
|
||||
conn.WriteToUDP(data, &net.UDPAddr{
|
||||
IP: net.ParseIP(MulticastAddressDefault),
|
||||
Port: Port,
|
||||
Zone: ifname,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (coll *Collector) receiver(conn *net.UDPConn) {
|
||||
buf := make([]byte, MaxDataGramSize)
|
||||
|
||||
for {
|
||||
n, src, err := conn.ReadFromUDP(buf)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("ReadFromUDP failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
raw := make([]byte, n)
|
||||
copy(raw, buf)
|
||||
|
||||
coll.queue <- &Packet{
|
||||
Address: src,
|
||||
Raw: raw,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package capture
|
||||
|
||||
const (
|
||||
// MulticastAddressDefault default multicast group used by announced
|
||||
MulticastAddressDefault = "ff02::31f1"
|
||||
|
||||
// Port default udp port used by announced
|
||||
Port = 1000
|
||||
|
||||
// MaxDataGramSize maximum receivable size
|
||||
MaxDataGramSize = 256
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
package capture
|
||||
|
||||
// IFaceConfig where to listen
|
||||
type IFaceConfig struct {
|
||||
InterfaceName string `toml:"ifname"`
|
||||
Port int `toml:"port"`
|
||||
IPAddress string `toml:"ip_address"`
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"dev.sum7.eu/wifictld/analyzer/capture"
|
||||
"dev.sum7.eu/wifictld/analyzer/data"
|
||||
)
|
||||
|
||||
var (
|
||||
port int
|
||||
ipAddress string
|
||||
)
|
||||
|
||||
// queryCmd represents the query command
|
||||
var dumpCmd = &cobra.Command{
|
||||
Use: "dump <interfaces>",
|
||||
Short: "capture wifictld traffic and just display the values (like wireshark)",
|
||||
Example: `analyzer dump "eth0,wlan0"`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ifaces := strings.Split(args[0], ",")
|
||||
|
||||
log.Infof("listen on: %s", ifaces)
|
||||
|
||||
var ifacesConfigs []capture.IFaceConfig
|
||||
for _, iface := range ifaces {
|
||||
ifaceConfig := capture.IFaceConfig{
|
||||
InterfaceName: iface,
|
||||
Port: port,
|
||||
IPAddress: ipAddress,
|
||||
}
|
||||
ifacesConfigs = append(ifacesConfigs, ifaceConfig)
|
||||
}
|
||||
|
||||
coll := capture.NewCollector(func(addr *net.UDPAddr, msg *data.SocketMSG) (*data.SocketMSG, error) {
|
||||
log.Infof("recv[%s]: %s", addr, msg.String())
|
||||
return nil, nil
|
||||
}, ifacesConfigs)
|
||||
defer coll.Close()
|
||||
|
||||
// Wait for INT/TERM
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
sig := <-sigs
|
||||
log.Println("received", sig)
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(dumpCmd)
|
||||
dumpCmd.Flags().IntVar(&port, "port", capture.Port, "define a port to listen (if not set or set to 0 the kernel will use a random free port at its own)")
|
||||
dumpCmd.Flags().StringVar(&ipAddress, "listen", capture.MulticastAddressDefault, "")
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var debug bool
|
||||
|
||||
// RootCmd represents the base command when called without any subcommands
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "analyzer",
|
||||
Short: "wifictld analyzer",
|
||||
Long: `capture wifictld traffic and display thus`,
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
if err := RootCmd.Execute(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
func init() {
|
||||
cobra.OnInitialize(func() {
|
||||
if debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
log.Debug("show debug")
|
||||
})
|
||||
RootCmd.PersistentFlags().BoolVar(&debug, "v", false, "show debug log")
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package data
|
||||
|
||||
import "net"
|
||||
|
||||
type Handler func(addr *net.UDPAddr, msg *SocketMSG) (*SocketMSG, error)
|
|
@ -0,0 +1,125 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// SocketMSGType kind of packages
|
||||
type SocketMSGType uint32
|
||||
|
||||
const (
|
||||
SocketMSGTypeRequest SocketMSGType = (1 << 0)
|
||||
SocketMSGTypeResponse SocketMSGType = (1 << 1)
|
||||
SocketMSGTypeClient SocketMSGType = (1 << 2)
|
||||
SocketMSGTypeStats SocketMSGType = (1 << 3)
|
||||
)
|
||||
|
||||
func (a SocketMSGType) Is(b SocketMSGType) bool {
|
||||
log.Debugf("SocketType: %x & %x = %x -> %b", a, b, (a & b), (a&b) > 0)
|
||||
return (a & b) > 0
|
||||
}
|
||||
|
||||
// SocketMSG package of wifictld format
|
||||
type SocketMSG struct {
|
||||
Types SocketMSGType
|
||||
Client *WifiClient
|
||||
}
|
||||
|
||||
func NewSocketMSG(obj []byte) (*SocketMSG, error) {
|
||||
msg := &SocketMSG{}
|
||||
if err := msg.Unmarshal(obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func (msg *SocketMSG) Marshal() ([]byte, error) {
|
||||
obj := make([]byte, MaxDataGramSize)
|
||||
pos := 0
|
||||
|
||||
binary.BigEndian.PutUint32(obj[pos:(pos+4)], uint32(msg.Types))
|
||||
pos += 4
|
||||
|
||||
if msg.Types.Is(SocketMSGTypeClient) {
|
||||
obj[pos] = msg.Client.Addr[0]
|
||||
pos++
|
||||
obj[pos] = msg.Client.Addr[1]
|
||||
pos++
|
||||
obj[pos] = msg.Client.Addr[2]
|
||||
pos++
|
||||
obj[pos] = msg.Client.Addr[3]
|
||||
pos++
|
||||
obj[pos] = msg.Client.Addr[4]
|
||||
pos++
|
||||
obj[pos] = msg.Client.Addr[5]
|
||||
pos++
|
||||
binary.BigEndian.PutUint32(obj[pos:(pos+4)], uint32(msg.Client.Time.Unix()))
|
||||
pos += 4
|
||||
binary.BigEndian.PutUint16(obj[pos:(pos+2)], msg.Client.TryProbe)
|
||||
pos += 2
|
||||
binary.BigEndian.PutUint16(obj[pos:(pos+2)], msg.Client.TryAuth)
|
||||
pos += 2
|
||||
if msg.Client.Connected {
|
||||
obj[pos] = byte(1)
|
||||
}
|
||||
pos++
|
||||
if msg.Client.Authed {
|
||||
obj[pos] = byte(1)
|
||||
}
|
||||
pos++
|
||||
binary.BigEndian.PutUint16(obj[pos:(pos+2)], msg.Client.FreqHighest)
|
||||
pos += 2
|
||||
binary.BigEndian.PutUint16(obj[pos:(pos+2)], uint16(msg.Client.SignalLowFreq))
|
||||
pos += 2
|
||||
binary.BigEndian.PutUint16(obj[pos:(pos+2)], uint16(msg.Client.SignalHighFreq))
|
||||
//pos += 2
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func (msg *SocketMSG) Unmarshal(obj []byte) error {
|
||||
log.Debugf("hex unmarshal: %x", obj)
|
||||
|
||||
pos := 0
|
||||
|
||||
msg.Types = SocketMSGType(binary.BigEndian.Uint32(obj[pos:(pos + 4)]))
|
||||
pos += 4
|
||||
|
||||
if msg.Types.Is(SocketMSGTypeClient) {
|
||||
msg.Client = &WifiClient{
|
||||
Addr: net.HardwareAddr(obj[pos:(pos + 6)]),
|
||||
Time: time.Unix(int64(binary.BigEndian.Uint32(obj[(pos+6):(pos+10)])), 0),
|
||||
TryProbe: binary.BigEndian.Uint16(obj[(pos + 10):(pos + 12)]),
|
||||
TryAuth: binary.BigEndian.Uint16(obj[(pos + 12):(pos + 14)]),
|
||||
Connected: (obj[(pos+14)] == 1),
|
||||
Authed: (obj[(pos+15)] == 1),
|
||||
FreqHighest: binary.BigEndian.Uint16(obj[(pos + 16):(pos + 18)]),
|
||||
SignalLowFreq: int16(binary.BigEndian.Uint16(obj[(pos + 18):(pos + 20)])),
|
||||
SignalHighFreq: int16(binary.BigEndian.Uint16(obj[(pos + 20):(pos + 22)])),
|
||||
}
|
||||
//pos += 22
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg *SocketMSG) String() string {
|
||||
answer := ""
|
||||
|
||||
if msg.Types.Is(SocketMSGTypeRequest) {
|
||||
answer += "request "
|
||||
}
|
||||
if msg.Types.Is(SocketMSGTypeResponse) {
|
||||
answer += "response "
|
||||
}
|
||||
if msg.Types.Is(SocketMSGTypeClient) {
|
||||
answer += fmt.Sprintf("client(mac=%s freq=%d ssi_l=%d ssi_h=%d tprobe=%d tauth=%d authed=%v time=%s) ", msg.Client.Addr, msg.Client.FreqHighest, msg.Client.SignalLowFreq, msg.Client.SignalHighFreq, msg.Client.TryProbe, msg.Client.TryAuth, msg.Client.Authed, msg.Client.Time.String())
|
||||
}
|
||||
|
||||
return answer
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMsgIsTypes(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
types := SocketMSGType(5)
|
||||
|
||||
assert.True(types.Is(SocketMSGTypeRequest))
|
||||
assert.False(types.Is(SocketMSGTypeResponse))
|
||||
assert.True(types.Is(SocketMSGTypeClient))
|
||||
assert.False(types.Is(SocketMSGTypeStats))
|
||||
|
||||
assert.Equal(SocketMSGType(5), SocketMSGType(SocketMSGTypeRequest|SocketMSGTypeClient))
|
||||
}
|
||||
|
||||
func TestMsgString(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
msg := SocketMSG{
|
||||
Types: SocketMSGTypeRequest | SocketMSGTypeResponse | SocketMSGTypeClient,
|
||||
Client: &WifiClient{
|
||||
FreqHighest: 2464,
|
||||
},
|
||||
}
|
||||
assert.Equal("request response client(mac= freq=2464 ssi_l=0 ssi_h=0 tprobe=0 tauth=0 authed=false time=0001-01-01 00:00:00 +0000 UTC) ", msg.String())
|
||||
}
|
||||
|
||||
func TestMsgMarshal(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
dst := make([]byte, MaxDataGramSize)
|
||||
_, err := hex.Decode(dst, []byte("00000005f0cfa13375295b0f8b92000000000101099effa300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
msg, _ := NewSocketMSG(dst)
|
||||
assert.Equal(SocketMSGType(SocketMSGTypeRequest|SocketMSGTypeClient), msg.Types)
|
||||
assert.Equal("f0:cf:a1:33:75:29", msg.Client.Addr.String())
|
||||
assert.Equal("2018-05-31T05:43:46Z", msg.Client.Time.UTC().Format(time.RFC3339))
|
||||
assert.Equal(0, int(msg.Client.TryProbe))
|
||||
assert.Equal(0, int(msg.Client.TryAuth))
|
||||
assert.True(msg.Client.Connected)
|
||||
assert.True(msg.Client.Authed)
|
||||
assert.Equal(2462, int(msg.Client.FreqHighest))
|
||||
assert.Equal(-93, int(msg.Client.SignalLowFreq))
|
||||
assert.Equal(0, int(msg.Client.SignalHighFreq))
|
||||
|
||||
result, _ := msg.Marshal()
|
||||
assert.Equal(dst, result)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// default multicast group used by announced
|
||||
MulticastAddressDefault = "ff02::31f1"
|
||||
|
||||
// default udp port used by announced
|
||||
Port = 1000
|
||||
|
||||
// maximum receivable size
|
||||
MaxDataGramSize = 256
|
||||
)
|
||||
|
||||
// WifiClient datatype of wifictld
|
||||
type WifiClient struct {
|
||||
Addr net.HardwareAddr
|
||||
Time time.Time
|
||||
TryProbe uint16
|
||||
TryAuth uint16
|
||||
Connected bool
|
||||
Authed bool
|
||||
FreqHighest uint16
|
||||
SignalLowFreq int16
|
||||
SignalHighFreq int16
|
||||
}
|
Loading…
Reference in New Issue