Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implements af_packet socket #807

Merged
merged 4 commits into from
Aug 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
143 changes: 102 additions & 41 deletions capture/capture.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"net"
"os"
"runtime"
"sync"
"time"

Expand All @@ -21,13 +22,13 @@ type Handler func(gopacket.Packet)
// PcapOptions options that can be set on a pcap capture handle,
// these options take effect on inactive pcap handles
type PcapOptions struct {
Promiscuous bool `json:"input-raw-promisc"`
Monitor bool `json:"input-raw-monitor"`
Snaplen bool `json:"input-raw-override-snaplen"`
BufferTimeout time.Duration `json:"input-raw-buffer-timeout"`
TimestampType string `json:"input-raw-timestamp-type"`
BufferSize size.Size `json:"input-raw-buffer-size"`
BPFFilter string `json:"input-raw-bpf-filter"`
BufferSize size.Size `json:"input-raw-buffer-size"`
Promiscuous bool `json:"input-raw-promisc"`
Monitor bool `json:"input-raw-monitor"`
Snaplen bool `json:"input-raw-override-snaplen"`
}

// NetInterface represents network interface
Expand All @@ -39,18 +40,19 @@ type NetInterface struct {
// Listener handle traffic capture, this is its representation.
type Listener struct {
sync.Mutex
PcapOptions
Engine EngineType
Transport string // transport layer default to tcp
Activate func() error // function is used to activate the engine. it must be called before reading packets
Handles map[string]*pcap.Handle
Handles map[string]gopacket.PacketDataSource
Interfaces []NetInterface
loopIndex int
Reading chan bool // this channel is closed when the listener has started reading packets

host string // pcap file name or interface (name, hardware addr, index or ip address)
PcapOptions
Engine EngineType
port uint16 // src or/and dst port
trackResponse bool

host string // pcap file name or interface (name, hardware addr, index or ip address)

quit chan bool
packets chan gopacket.Packet
}
Expand All @@ -60,8 +62,9 @@ type EngineType uint8

// Available engines for intercepting traffic
const (
EnginePcap EngineType = iota
EnginePcap EngineType = 1 << iota
EnginePcapFile
EngineRawSocket
)

// Set is here so that EngineType can implement flag.Var
Expand All @@ -71,6 +74,8 @@ func (eng *EngineType) Set(v string) error {
*eng = EnginePcap
case "pcap_file":
*eng = EnginePcapFile
case "raw_socket", "af_packet":
*eng = EngineRawSocket
default:
return fmt.Errorf("invalid engine %s", v)
}
Expand All @@ -83,6 +88,8 @@ func (eng *EngineType) String() (e string) {
e = "pcap_file"
case EnginePcap:
e = "libpcap"
case EngineRawSocket:
e = "raw_socket"
default:
e = ""
}
Expand All @@ -101,16 +108,21 @@ func NewListener(host string, port uint16, transport string, engine EngineType,
if transport != "" {
l.Transport = transport
}
l.Handles = make(map[string]*pcap.Handle)
l.Handles = make(map[string]gopacket.PacketDataSource)
l.trackResponse = trackResponse
l.packets = make(chan gopacket.Packet, 1000)
l.quit = make(chan bool, 1)
l.Reading = make(chan bool, 1)
l.Activate = l.activatePcap
l.Engine = EnginePcap
if engine == EnginePcapFile {
l.Activate = l.activatePcapFile
switch engine {
default:
l.Engine = EnginePcap
l.Activate = l.activatePcap
case EngineRawSocket:
l.Engine = EngineRawSocket
l.Activate = l.activateRawSocket
case EnginePcapFile:
l.Engine = EnginePcapFile
l.Activate = l.activatePcapFile
return
}
err = l.setInterfaces()
Expand All @@ -130,9 +142,6 @@ func (l *Listener) SetPcapOptions(opts PcapOptions) {
// until the context done signal is sent or EOF on handles.
// this function should be called after activating pcap handles
func (l *Listener) Listen(ctx context.Context, handler Handler) (err error) {
if err != nil {
return err
}
l.read()
done := ctx.Done()
var p gopacket.Packet
Expand All @@ -143,7 +152,7 @@ func (l *Listener) Listen(ctx context.Context, handler Handler) (err error) {
l.quit <- true
close(l.quit)
err = ctx.Err()
return
done = nil
case p, ok = <-l.packets:
if !ok {
return
Expand Down Expand Up @@ -182,7 +191,7 @@ func (l *Listener) Filter(ifi NetInterface) (filter string) {
dir = " "
}
filter = fmt.Sprintf("(%s%s%s)", l.Transport, dir, port)
if l.host == "" || isDevice(l.host, ifi) {
if listenAll(l.host) || isDevice(l.host, ifi) {
return
}
filter = fmt.Sprintf("(%s%s%s and host %s)", l.Transport, dir, port, l.host)
Expand Down Expand Up @@ -254,7 +263,7 @@ func (l *Listener) PcapHandle(ifi NetInterface) (handle *pcap.Handle, err error)
return nil, fmt.Errorf("handle buffer size error: %q, interface: %q", err, ifi.Name)
}
}
if l.BufferTimeout.Nanoseconds() == 0 {
if l.BufferTimeout == 0 {
l.BufferTimeout = pcap.BlockForever
}
err = inactive.SetTimeout(l.BufferTimeout)
Expand All @@ -266,11 +275,8 @@ func (l *Listener) PcapHandle(ifi NetInterface) (handle *pcap.Handle, err error)
return nil, fmt.Errorf("PCAP Activate device error: %q, interface: %q", err, ifi.Name)
}
if l.BPFFilter != "" {
if l.BPFFilter[0] != '(' {
l.BPFFilter = "(" + l.BPFFilter
}
if l.BPFFilter[len(l.BPFFilter)-1] != ')' {
l.BPFFilter += ")"
if l.BPFFilter[0] != '(' || l.BPFFilter[len(l.BPFFilter)-1] != ')' {
l.BPFFilter = "(" + l.BPFFilter + ")"
}
} else {
l.BPFFilter = l.Filter(ifi)
Expand All @@ -283,15 +289,44 @@ func (l *Listener) PcapHandle(ifi NetInterface) (handle *pcap.Handle, err error)
return
}

// SocketHandle returns new unix ethernet handle associated with this listener settings
func (l *Listener) SocketHandle(ifi NetInterface) (handle *SockRaw, err error) {
handle, err = NewSockRaw(ifi.Interface)
if err != nil {
return nil, fmt.Errorf("sock raw error: %q, interface: %q", err, ifi.Name)
}
if err = handle.SetPromiscuous(l.Promiscuous || l.Monitor); err != nil {
return nil, fmt.Errorf("promiscuous mode error: %q, interface: %q", err, ifi.Name)
}
if l.BPFFilter != "" {
if l.BPFFilter[0] != '(' || l.BPFFilter[len(l.BPFFilter)-1] != ')' {
l.BPFFilter = "(" + l.BPFFilter + ")"
}
} else {
l.BPFFilter = l.Filter(ifi)
}
if err = handle.SetBPFFilter(l.BPFFilter); err != nil {
handle.Close()
return nil, fmt.Errorf("BPF filter error: %q%s, interface: %q", err, l.BPFFilter, ifi.Name)
}
handle.SetLoopbackIndex(int32(l.loopIndex))
return
}

func (l *Listener) read() {
l.Lock()
defer l.Unlock()
for key, handle := range l.Handles {
source := gopacket.NewPacketSource(handle, handle.LinkType())
var source *gopacket.PacketSource
linkType := layers.LinkTypeEthernet
if _, ok := handle.(*pcap.Handle); ok {
linkType = handle.(*pcap.Handle).LinkType()
}
source = gopacket.NewPacketSource(handle, linkType)
source.Lazy = true
source.NoCopy = true
ch := source.Packets()
go func(handle *pcap.Handle, key string) {
go func(key string) {
defer l.closeHandles(key)
for {
select {
Expand All @@ -304,7 +339,7 @@ func (l *Listener) read() {
l.packets <- p
}
}
}(handle, key)
}(key)
}
l.Reading <- true
close(l.Reading)
Expand All @@ -314,15 +349,17 @@ func (l *Listener) closeHandles(key string) {
l.Lock()
defer l.Unlock()
if handle, ok := l.Handles[key]; ok {
handle.Close()
if _, ok = handle.(interface{ Close() }); ok {
handle.(interface{ Close() }).Close()
}
delete(l.Handles, key)
if len(l.Handles) == 0 {
close(l.packets)
}
}
}

func (l *Listener) activatePcap() (err error) {
func (l *Listener) activatePcap() error {
var e error
var msg string
for _, ifi := range l.Interfaces {
Expand All @@ -337,7 +374,25 @@ func (l *Listener) activatePcap() (err error) {
if len(l.Handles) == 0 {
return fmt.Errorf("pcap handles error:%s", msg)
}
return
return nil
}

func (l *Listener) activateRawSocket() error {
if runtime.GOOS != "linux" {
return fmt.Errorf("sock_raw is not stabilized on OS other than linux")
}
var msg string
var e error
for _, ifi := range l.Interfaces {
var handle *SockRaw
handle, e = l.SocketHandle(ifi)
if e != nil {
msg += ("\n" + e.Error())
continue
}
l.Handles[ifi.Name] = handle
}
return e
}

func (l *Listener) activatePcapFile() (err error) {
Expand All @@ -347,11 +402,8 @@ func (l *Listener) activatePcapFile() (err error) {
return fmt.Errorf("open pcap file error: %q", e)
}
if l.BPFFilter != "" {
if l.BPFFilter[0] != '(' {
l.BPFFilter = "(" + l.BPFFilter
}
if l.BPFFilter[len(l.BPFFilter)-1] != ')' {
l.BPFFilter += ")"
if l.BPFFilter[0] != '(' || l.BPFFilter[len(l.BPFFilter)-1] != ')' {
l.BPFFilter = "(" + l.BPFFilter + ")"
}
} else {
addr := l.host
Expand All @@ -376,6 +428,9 @@ func (l *Listener) setInterfaces() (err error) {
}

for i := 0; i < len(ifis); i++ {
if ifis[i].Flags&net.FlagLoopback != 0 {
l.loopIndex = ifis[i].Index
}
if ifis[i].Flags&net.FlagUp == 0 {
continue
}
Expand All @@ -396,15 +451,13 @@ func (l *Listener) setInterfaces() (err error) {
Ifis = append(Ifis, ifi)
}

switch l.host {
case "", "0.0.0.0", "[::]", "::":
if listenAll(l.host) {
l.Interfaces = Ifis
return
}

found := false
for _, ifi := range Ifis {
if l.host == ifi.Name || l.host == fmt.Sprintf("%d", ifi.Index) || l.host == ifi.HardwareAddr.String() {
if isDevice(l.host, ifi) {
found = true
}
for _, ip := range ifi.IPs {
Expand Down Expand Up @@ -435,3 +488,11 @@ func cutMask(addr net.Addr) string {
func isDevice(addr string, ifi NetInterface) bool {
return addr == ifi.Name || addr == fmt.Sprintf("%d", ifi.Index) || addr == ifi.HardwareAddr.String()
}

func listenAll(addr string) bool {
switch addr {
case "", "0.0.0.0", "[::]", "::":
return true
}
return false
}
Loading