Skip to content

Commit

Permalink
feat: Enabled libpcap as an alternative to PF_Ring
Browse files Browse the repository at this point in the history
  • Loading branch information
Xumeiquer committed Jul 29, 2020
2 parents 15aed5a + 332935b commit 365477e
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 27 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ $RECYCLE.BIN/

# End of https://www.gitignore.io/api/go,vim,linux,macos,windows,sublimetext,visualstudiocode

# Build System
.lock*
.waf*

# Custom
build/
c.out
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ default: build
all: clean install

build:
go build ${LDFLAGS} -o ${BINDIR}/${BINARY} $(SRC)
go build -race ${LDFLAGS} -o ${BINDIR}/${BINARY} $(SRC)

build-pfring:
go build -race -tags "pf_ring" ${LDFLAGS} -o ${BINDIR}/${BINARY} $(SRC)

install:
go install ${LDFLAGS}
Expand Down
12 changes: 7 additions & 5 deletions docs/content/getting-started/configuration-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,18 @@ want to use PF_RING features or you can define a BPF filter.
interface:
iface: ens33
pf_ring: true
bpf: udp dst port not 53
bpf: udp and dst port not 53
```

On the previous example Mole will listen traffic from the `ens33` interface.
PF_RING will be used as capturing driver and only the traffic defined in the
`bpf` filter will be captured.

!!! warning
At the moment Mole IDS uses PF_RING for capturing packages and it cannot be dissabled.
When setting `interface.pf_ring` to `false` Mole IDS will use libpcap to capture
packages from the interface.

If Mole IDS was compiled without PF_Ring support and you configure it to use
the PF_Ring driver, Mole IDS will fall down to libpcap.

## engine

Expand Down Expand Up @@ -88,7 +91,6 @@ rule ExampleRule {
}
```


## logger

Finally, we defined a logging section. This section defines two types of logger,
Expand Down Expand Up @@ -173,7 +175,7 @@ Following there is an example of an alert output.
interface:
iface: ens33
pf_ring: true
bpf: udp dst port not 53
bpf: udp and dst port not 53

engine:

Expand Down
24 changes: 24 additions & 0 deletions docs/content/writing-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,27 @@ variables already defined and they can not be overwrite, those are:

Finally, there is a variable called `any` that can be used to define any soruce
or destination address as wel as any source or destination port.

## Examples

Following several rule examples.

### Example 1

```yara
rule ExampleRule {
meta:
description = "Port range from 1 to 1024"
type = "alert"
proto = "tcp"
src = "any"
sport = "any"
dst = "any"
dport = "1:1024"
strings:
$dnp3_header = { 05 64 }
$unsolicited_response = { 82 }
condition:
$dnp3_header at 0 and $unsolicited_response at 12 and #dnp3_header < 2
}
```
15 changes: 14 additions & 1 deletion internal/tree/backtracking.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func (bt *Bactracking) Backtrack(node *Tree) {
bt.removePartialNode(node.Children)

var current *Tree
child := node.Next
child := node.Children.Next
for child != nil {
current = child
child = current.Next
Expand All @@ -122,5 +122,18 @@ func (bt *Bactracking) Backtrack(node *Tree) {
return
}

if node.Next != nil {
var current *Tree
child := node.Next
for child != nil {
current = child
child = current.Next

// For each next node do backtrack
bt.Backtrack(current)
bt.removePartialNode(current)
}
}

return
}
13 changes: 7 additions & 6 deletions internal/tree/tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestMain(tm *testing.M) {
tm.Run()
}

func getDummyData() []types.MetaRule {
func getDummyMetaRules() []types.MetaRule {
var metarules []types.MetaRule

var data map[string][]string
Expand All @@ -48,10 +48,11 @@ func getDummyData() []types.MetaRule {
}
metarules = append(metarules, meta)
}

return metarules
}

func getDummyData2() []types.MetaRule {
func getDummyMetaRules2() []types.MetaRule {
var metarules []types.MetaRule

var data map[string][]string
Expand Down Expand Up @@ -80,7 +81,7 @@ func TestFindInsert(t *testing.T) {

Decision = New(nodes.NewRoot())

for _, rule := range getDummyData() {
for _, rule := range getDummyMetaRules() {
lvl := 0
node, ok, err := insertRule(Decision, lvl, nodes.Keywords, rule)
if node == nil {
Expand All @@ -102,7 +103,7 @@ func TestLookupIDNotInit(t *testing.T) {
// Avoid any previous initialization
Decision = nil

data := getDummyData()
data := getDummyMetaRules()
id, err := LookupID(data[0])

if len(id) != 0 {
Expand All @@ -120,7 +121,7 @@ func TestLookupID(t *testing.T) {
var idNode *Tree
var err error

data := getDummyData()
data := getDummyMetaRules()
Decision = New(nodes.NewRoot())

for _, rule := range data {
Expand Down Expand Up @@ -155,7 +156,7 @@ func TestLookupIDNotFound(t *testing.T) {
var id []string
var err error

rulesMeta := getDummyData()
rulesMeta := getDummyMetaRules()

Decision = New(nodes.NewRoot())

Expand Down
16 changes: 6 additions & 10 deletions pkg/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pfring"
"github.com/pkg/errors"
"go.uber.org/zap"

Expand Down Expand Up @@ -47,8 +46,8 @@ type Engine struct {
// the look up query
RuleMap types.RuleMapScanner

// Ring used for sniff packages from pf_ring
Ring *pfring.Ring
// Handle is the interface handeler that allow Mole to capture traffic
Handle gopacket.PacketDataSource
}

var (
Expand Down Expand Up @@ -100,12 +99,9 @@ func New() (motor *Engine, err error) {
return nil, errors.Wrap(err, InterfacesInitFailMsg)
}

// Enable pf_ring if requested
if motor.Iface.PFRingEnabled() {
motor.Ring, err = motor.Iface.InitPFRing()
if err != nil {
return nil, errors.Wrap(err, PFRingInitFailMsg)
}
motor.Handle, err = motor.Iface.GetHandler()
if err != nil {
return nil, errors.Wrap(err, GettingHandlerFailMsg)
}

logger.Log.Info(MainEventInitCompletedMsg)
Expand All @@ -120,7 +116,7 @@ func (motor *Engine) Start() {
// Start sniffing packages
// TODO: Take into account when pf_ring is not enable or another method is
// in used
packetSource := gopacket.NewPacketSource(motor.Ring, layers.LinkTypeEthernet)
packetSource := gopacket.NewPacketSource(motor.Handle, layers.LinkTypeEthernet)
for pkt := range packetSource.Packets() {
// Checking for network errors
if err := pkt.ErrorLayer(); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/engine/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ const (
RulesManagerInitFailMsg = "while initialating rules manager got"
CreateTreeFailMsg = "while generating the Decision tree got"
InterfacesInitFailMsg = "while initialating interfaces got"
PFRingInitFailMsg = "while initiating pf_ring got"
LoadingRulesFailedMsg = "while loading rules got"
GettingHandlerFailMsg = "while getting the snffer handler got"
)

var ()
42 changes: 42 additions & 0 deletions pkg/interfaces/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@ package interfaces
import (
"net"

"github.com/google/gopacket"
"github.com/mole-ids/mole/pkg/logger"
"github.com/pkg/errors"
)

const (
snapshotLength = 65536
)

var (
pfringAvaliable = false
)

// Interfaces is in charge to manage interfaces
type Interfaces struct {
// Config interface's configuration most of its values come from the arguments
Expand All @@ -40,11 +49,44 @@ func New() (iface *Interfaces, err error) {
return iface, nil
}

// PFRingAvaliable indicated whether PF Ring is enabled
func (iface *Interfaces) PFRingAvaliable() bool {
return pfringAvaliable
}

// PFRingEnabled indicated whether PF Ring is enabled
func (iface *Interfaces) PFRingEnabled() bool {
return iface.Config.PFRing
}

// GetHandler returns the data source where the packets will came in from
func (iface *Interfaces) GetHandler() (handle gopacket.PacketDataSource, err error) {
// Enable pf_ring if requested
if pfringAvaliable {
if iface.Config.PFRing {
handle, err = iface.initPFRing()
if err != nil {
return nil, errors.Wrap(err, PFRingInitFailMsg)
}
} else {
handle, err = iface.initPcap()
if err != nil {
return nil, errors.Wrap(err, PCAPInitFailMsg)
}
}
} else {
if iface.Config.PFRing {
logger.Log.Warn(PFRingNotAvaliableMsg)
}
handle, err = iface.initPcap()
if err != nil {
return nil, errors.Wrap(err, PCAPInitFailMsg)
}
}

return handle, nil
}

// validateIface validates the interface against the interfaces from the system
func validateIface(interfaceName string) (ok bool, err error) {
ok = false
Expand Down
5 changes: 5 additions & 0 deletions pkg/interfaces/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ const (
InterfacesInitMsg = "interface intiated successfully"
InterfacesListFailedMsg = "unable to list system interfaces, because"
PFRingInitFaildMsg = "unable to crate new pf_ring object, because"
PCAPInitFaildMsg = "unable to crate new pcap object, because"
SettingBPFFilterFailedMsg = "unable to set the BPF filter, because"
EnablePFRingFailedMsg = "while enabling PFRing found"
PFRingEnabledMsg = "PFRing enabled successflly"
PCAPEnabledMsg = "PCAP sniffer enabled successflly"
PCAPInitFailMsg = "while initiating pcap got"
PFRingInitFailMsg = "while initiating pf_ring got"
PFRingNotAvaliableMsg = "PFRing is not avaliable. Falling down to PCAP sniffer"
)

var (
Expand Down
46 changes: 46 additions & 0 deletions pkg/interfaces/pcap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2020 Jaume Martin

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !pf_ring

package interfaces

import (
"github.com/google/gopacket"
"github.com/google/gopacket/pcap"
"github.com/mole-ids/mole/pkg/logger"
"github.com/pkg/errors"
)

// initPcap initializes PFRing on the interface defined in the config
func (iface *Interfaces) initPcap() (gopacket.PacketDataSource, error) {
handle, err := pcap.OpenLive(iface.Config.IFace, snapshotLength, true, pcap.BlockForever)
if err != nil {
return nil, errors.Wrap(err, PCAPInitFaildMsg)
}

// If there is a BPF fitler then apply it
if iface.Config.BPFfilter != "" {
if err = handle.SetBPFFilter(iface.Config.BPFfilter); err != nil {
return nil, errors.Wrap(err, SettingBPFFilterFailedMsg)
}
}

logger.Log.Info(PCAPEnabledMsg)
return handle, nil
}

func (iface *Interfaces) initPFRing() (gopacket.PacketDataSource, error) {
return nil, nil
}
42 changes: 42 additions & 0 deletions pkg/interfaces/pcap_w_pfring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2020 Jaume Martin

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build pf_ring

package interfaces

import (
"github.com/google/gopacket"
"github.com/google/gopacket/pcap"
"github.com/mole-ids/mole/pkg/logger"
"github.com/pkg/errors"
)

// initPcap initializes PFRing on the interface defined in the config
func (iface *Interfaces) initPcap() (gopacket.PacketDataSource, error) {
handle, err := pcap.OpenLive(iface.Config.IFace, snapshotLength, true, pcap.BlockForever)
if err != nil {
return nil, errors.Wrap(err, PCAPInitFaildMsg)
}

// If there is a BPF fitler then apply it
if iface.Config.BPFfilter != "" {
if err = handle.SetBPFFilter(iface.Config.BPFfilter); err != nil {
return nil, errors.Wrap(err, SettingBPFFilterFailedMsg)
}
}

logger.Log.Info(PCAPEnabledMsg)
return handle, nil
}
Loading

0 comments on commit 365477e

Please sign in to comment.