From 913c581f9ff519bfd911b37adc68b61dac283c77 Mon Sep 17 00:00:00 2001 From: evilsocket Date: Sat, 13 Jan 2018 18:40:43 +0100 Subject: [PATCH] new: main packet queue is now used to gather network stats, update endpoint, etc --- net/endpoint.go | 12 +++++ packets/queue.go | 110 +++++++++++++++++++++++++++++++++++++++------ session/session.go | 18 +++++++- session/targets.go | 2 + 4 files changed, 127 insertions(+), 15 deletions(-) diff --git a/net/endpoint.go b/net/endpoint.go index 7f5e89c6..bb829d1c 100644 --- a/net/endpoint.go +++ b/net/endpoint.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "fmt" "net" + "time" "github.com/evilsocket/bettercap-ng/core" ) @@ -11,6 +12,7 @@ import ( type OnHostResolvedCallback func(e *Endpoint) type Endpoint struct { IP net.IP `json:"-"` + Net *net.IPNet `json:"-"` IPv6 net.IP `json:"."` HW net.HardwareAddr `json:"-"` IpAddress string `json:"ipv4"` @@ -21,12 +23,17 @@ type Endpoint struct { Hostname string `json:"hostname"` Vendor string `json:"vendor"` ResolvedCallback OnHostResolvedCallback `json:"-"` + FirstSeen time.Time `json:"first_seen"` + LastSeen time.Time `json:"last_seen"` } func NewEndpointNoResolve(ip, mac, name string, bits uint32) *Endpoint { hw, _ := net.ParseMAC(mac) + now := time.Now() + e := &Endpoint{ IP: net.ParseIP(ip), + Net: nil, HW: hw, IpAddress: ip, SubnetBits: bits, @@ -35,8 +42,13 @@ func NewEndpointNoResolve(ip, mac, name string, bits uint32) *Endpoint { Hostname: name, Vendor: OuiLookup(mac), ResolvedCallback: nil, + FirstSeen: now, + LastSeen: now, } + _, netw, _ := net.ParseCIDR(e.CIDR()) + e.Net = netw + return e } diff --git a/packets/queue.go b/packets/queue.go index 65596321..96f98a78 100644 --- a/packets/queue.go +++ b/packets/queue.go @@ -1,42 +1,126 @@ package packets import ( + "bytes" "fmt" - "github.com/google/gopacket/pcap" + "net" "sync" + + bnet "github.com/evilsocket/bettercap-ng/net" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" ) -type Queue struct { - iface string - handle *pcap.Handle - lock *sync.Mutex - active bool +type Activity struct { + IP net.IP + MAC net.HardwareAddr } -func NewQueue(iface string) (*Queue, error) { +type Queue struct { + iface *bnet.Endpoint + handle *pcap.Handle + source *gopacket.PacketSource + lock *sync.Mutex + active bool + + Activities chan Activity `json:"-"` + Sent uint64 + Received uint64 + PktReceived uint64 + Errors uint64 + Protos map[string]uint64 +} + +func NewQueue(iface *bnet.Endpoint) (*Queue, error) { var err error q := &Queue{ - iface: iface, - handle: nil, - lock: &sync.Mutex{}, - active: true, + iface: iface, + handle: nil, + lock: &sync.Mutex{}, + active: true, + source: nil, + Sent: 0, + Received: 0, + PktReceived: 0, + Errors: 0, + Protos: make(map[string]uint64), + Activities: make(chan Activity), } - q.handle, err = pcap.OpenLive(iface, 1024, true, pcap.BlockForever) + q.handle, err = pcap.OpenLive(iface.Name(), 1024, true, pcap.BlockForever) if err != nil { return nil, err } + q.source = gopacket.NewPacketSource(q.handle, q.handle.LinkType()) + go q.worker() + return q, nil } +func (q *Queue) worker() { + for pkt := range q.source.Packets() { + if q.active == false { + return + } + + q.PktReceived++ + q.Received += uint64(len(pkt.Data())) + + // gather protocols stats + pktLayers := pkt.Layers() + for _, layer := range pktLayers { + proto := layer.LayerType().String() + if proto == "DecodeFailure" || proto == "Payload" { + continue + } + + if _, found := q.Protos[proto]; found == false { + q.Protos[proto] = 0 + } + q.Protos[proto] += 1 + } + + // check for new ipv4 endpoints + leth := pkt.Layer(layers.LayerTypeEthernet) + lip4 := pkt.Layer(layers.LayerTypeIPv4) + + if leth != nil && lip4 != nil { + eth := leth.(*layers.Ethernet) + ip4 := lip4.(*layers.IPv4) + + if bytes.Compare(q.iface.IP, ip4.SrcIP) != 0 && q.iface.Net.Contains(ip4.SrcIP) { + q.Activities <- Activity{ + IP: ip4.SrcIP, + MAC: eth.SrcMAC, + } + } + + if bytes.Compare(q.iface.IP, ip4.DstIP) != 0 && q.iface.Net.Contains(ip4.DstIP) { + q.Activities <- Activity{ + IP: ip4.DstIP, + MAC: eth.SrcMAC, + } + } + } + } +} + func (q *Queue) Send(raw []byte) error { q.lock.Lock() defer q.lock.Unlock() if q.active { - return q.handle.WritePacketData(raw) + err := q.handle.WritePacketData(raw) + if err != nil { + q.Sent += uint64(len(raw)) + } else { + q.Errors += 1 + } + return err } else { return fmt.Errorf("Packet queue is not active.") } diff --git a/session/session.go b/session/session.go index ceacafe1..81f42e31 100644 --- a/session/session.go +++ b/session/session.go @@ -33,7 +33,7 @@ type Session struct { Firewall firewall.FirewallManager `json:"-"` Env *Environment `json:"env"` Targets *Targets `json:"targets"` - Queue *packets.Queue `json:"-"` + Queue *packets.Queue `json:"packets"` Input *readline.Instance `json:"-"` Active bool `json:"active"` Prompt Prompt `json:"-"` @@ -174,7 +174,7 @@ func (s *Session) Start() error { s.Env.Set("iface.ipv6", s.Interface.Ip6Address) s.Env.Set("iface.mac", s.Interface.HwAddress) - if s.Queue, err = packets.NewQueue(s.Interface.Name()); err != nil { + if s.Queue, err = packets.NewQueue(s.Interface); err != nil { return err } @@ -227,6 +227,20 @@ func (s *Session) Start() error { }() s.Active = true + + // keep reading network events in order to add / update endpoints + go func() { + for event := range s.Queue.Activities { + addr := event.IP.String() + mac := event.MAC.String() + s.Targets.AddIfNotExist(addr, mac) + + if s.Active == false { + return + } + } + }() + s.Events.Add("session.started", nil) return nil diff --git a/session/targets.go b/session/targets.go index 5a9579b5..5bf6d06d 100644 --- a/session/targets.go +++ b/session/targets.go @@ -4,6 +4,7 @@ import ( "fmt" "sort" "sync" + "time" "github.com/evilsocket/bettercap-ng/core" "github.com/evilsocket/bettercap-ng/net" @@ -63,6 +64,7 @@ func (tp *Targets) AddIfNotExist(ip, mac string) *net.Endpoint { } if t, found := tp.Targets[mac]; found { + t.LastSeen = time.Now() return t }