Skip to content

Commit 1f16122

Browse files
committed
feat: Enable libpcap as an alternative to PF_Ring
2 parents 15aed5a + 332935b commit 1f16122

File tree

13 files changed

+213
-27
lines changed

13 files changed

+213
-27
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ $RECYCLE.BIN/
161161

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

164+
# Build System
165+
.lock*
166+
.waf*
167+
164168
# Custom
165169
build/
166170
c.out

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ default: build
3232
all: clean install
3333

3434
build:
35-
go build ${LDFLAGS} -o ${BINDIR}/${BINARY} $(SRC)
35+
go build -race ${LDFLAGS} -o ${BINDIR}/${BINARY} $(SRC)
36+
37+
build-pfring:
38+
go build -race -tags "pf_ring" ${LDFLAGS} -o ${BINDIR}/${BINARY} $(SRC)
3639

3740
install:
3841
go install ${LDFLAGS}

docs/content/getting-started/configuration-overview.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,18 @@ want to use PF_RING features or you can define a BPF filter.
3333
interface:
3434
iface: ens33
3535
pf_ring: true
36-
bpf: udp dst port not 53
36+
bpf: udp and dst port not 53
3737
```
3838
3939
On the previous example Mole will listen traffic from the `ens33` interface.
4040
PF_RING will be used as capturing driver and only the traffic defined in the
4141
`bpf` filter will be captured.
4242

43-
!!! warning
44-
At the moment Mole IDS uses PF_RING for capturing packages and it cannot be dissabled.
43+
When setting `interface.pf_ring` to `false` Mole IDS will use libpcap to capture
44+
packages from the interface.
45+
46+
If Mole IDS was compiled without PF_Ring support and you configure it to use
47+
the PF_Ring driver, Mole IDS will fall down to libpcap.
4548

4649
## engine
4750

@@ -88,7 +91,6 @@ rule ExampleRule {
8891
}
8992
```
9093

91-
9294
## logger
9395

9496
Finally, we defined a logging section. This section defines two types of logger,
@@ -173,7 +175,7 @@ Following there is an example of an alert output.
173175
interface:
174176
iface: ens33
175177
pf_ring: true
176-
bpf: udp dst port not 53
178+
bpf: udp and dst port not 53
177179
178180
engine:
179181

docs/content/writing-rules.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,27 @@ variables already defined and they can not be overwrite, those are:
128128

129129
Finally, there is a variable called `any` that can be used to define any soruce
130130
or destination address as wel as any source or destination port.
131+
132+
## Examples
133+
134+
Following several rule examples.
135+
136+
### Example 1
137+
138+
```yara
139+
rule ExampleRule {
140+
meta:
141+
description = "Port range from 1 to 1024"
142+
type = "alert"
143+
proto = "tcp"
144+
src = "any"
145+
sport = "any"
146+
dst = "any"
147+
dport = "1:1024"
148+
strings:
149+
$dnp3_header = { 05 64 }
150+
$unsolicited_response = { 82 }
151+
condition:
152+
$dnp3_header at 0 and $unsolicited_response at 12 and #dnp3_header < 2
153+
}
154+
```

internal/tree/backtracking.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func (bt *Bactracking) Backtrack(node *Tree) {
109109
bt.removePartialNode(node.Children)
110110

111111
var current *Tree
112-
child := node.Next
112+
child := node.Children.Next
113113
for child != nil {
114114
current = child
115115
child = current.Next
@@ -122,5 +122,18 @@ func (bt *Bactracking) Backtrack(node *Tree) {
122122
return
123123
}
124124

125+
if node.Next != nil {
126+
var current *Tree
127+
child := node.Next
128+
for child != nil {
129+
current = child
130+
child = current.Next
131+
132+
// For each next node do backtrack
133+
bt.Backtrack(current)
134+
bt.removePartialNode(current)
135+
}
136+
}
137+
125138
return
126139
}

internal/tree/tree_test.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestMain(tm *testing.M) {
2626
tm.Run()
2727
}
2828

29-
func getDummyData() []types.MetaRule {
29+
func getDummyMetaRules() []types.MetaRule {
3030
var metarules []types.MetaRule
3131

3232
var data map[string][]string
@@ -48,10 +48,11 @@ func getDummyData() []types.MetaRule {
4848
}
4949
metarules = append(metarules, meta)
5050
}
51+
5152
return metarules
5253
}
5354

54-
func getDummyData2() []types.MetaRule {
55+
func getDummyMetaRules2() []types.MetaRule {
5556
var metarules []types.MetaRule
5657

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

8182
Decision = New(nodes.NewRoot())
8283

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

105-
data := getDummyData()
106+
data := getDummyMetaRules()
106107
id, err := LookupID(data[0])
107108

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

123-
data := getDummyData()
124+
data := getDummyMetaRules()
124125
Decision = New(nodes.NewRoot())
125126

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

158-
rulesMeta := getDummyData()
159+
rulesMeta := getDummyMetaRules()
159160

160161
Decision = New(nodes.NewRoot())
161162

pkg/engine/engine.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818

1919
"github.com/google/gopacket"
2020
"github.com/google/gopacket/layers"
21-
"github.com/google/gopacket/pfring"
2221
"github.com/pkg/errors"
2322
"go.uber.org/zap"
2423

@@ -47,8 +46,8 @@ type Engine struct {
4746
// the look up query
4847
RuleMap types.RuleMapScanner
4948

50-
// Ring used for sniff packages from pf_ring
51-
Ring *pfring.Ring
49+
// Handle is the interface handeler that allow Mole to capture traffic
50+
Handle gopacket.PacketDataSource
5251
}
5352

5453
var (
@@ -100,12 +99,9 @@ func New() (motor *Engine, err error) {
10099
return nil, errors.Wrap(err, InterfacesInitFailMsg)
101100
}
102101

103-
// Enable pf_ring if requested
104-
if motor.Iface.PFRingEnabled() {
105-
motor.Ring, err = motor.Iface.InitPFRing()
106-
if err != nil {
107-
return nil, errors.Wrap(err, PFRingInitFailMsg)
108-
}
102+
motor.Handle, err = motor.Iface.GetHandler()
103+
if err != nil {
104+
return nil, errors.Wrap(err, GettingHandlerFailMsg)
109105
}
110106

111107
logger.Log.Info(MainEventInitCompletedMsg)
@@ -120,7 +116,7 @@ func (motor *Engine) Start() {
120116
// Start sniffing packages
121117
// TODO: Take into account when pf_ring is not enable or another method is
122118
// in used
123-
packetSource := gopacket.NewPacketSource(motor.Ring, layers.LinkTypeEthernet)
119+
packetSource := gopacket.NewPacketSource(motor.Handle, layers.LinkTypeEthernet)
124120
for pkt := range packetSource.Packets() {
125121
// Checking for network errors
126122
if err := pkt.ErrorLayer(); err != nil {

pkg/engine/messages.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ const (
2525
RulesManagerInitFailMsg = "while initialating rules manager got"
2626
CreateTreeFailMsg = "while generating the Decision tree got"
2727
InterfacesInitFailMsg = "while initialating interfaces got"
28-
PFRingInitFailMsg = "while initiating pf_ring got"
2928
LoadingRulesFailedMsg = "while loading rules got"
29+
GettingHandlerFailMsg = "while getting the snffer handler got"
3030
)
3131

3232
var ()

pkg/interfaces/iface.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,19 @@ package interfaces
1616
import (
1717
"net"
1818

19+
"github.com/google/gopacket"
1920
"github.com/mole-ids/mole/pkg/logger"
2021
"github.com/pkg/errors"
2122
)
2223

24+
const (
25+
snapshotLength = 65536
26+
)
27+
28+
var (
29+
pfringAvaliable = false
30+
)
31+
2332
// Interfaces is in charge to manage interfaces
2433
type Interfaces struct {
2534
// Config interface's configuration most of its values come from the arguments
@@ -40,11 +49,44 @@ func New() (iface *Interfaces, err error) {
4049
return iface, nil
4150
}
4251

52+
// PFRingAvaliable indicated whether PF Ring is enabled
53+
func (iface *Interfaces) PFRingAvaliable() bool {
54+
return pfringAvaliable
55+
}
56+
4357
// PFRingEnabled indicated whether PF Ring is enabled
4458
func (iface *Interfaces) PFRingEnabled() bool {
4559
return iface.Config.PFRing
4660
}
4761

62+
// GetHandler returns the data source where the packets will came in from
63+
func (iface *Interfaces) GetHandler() (handle gopacket.PacketDataSource, err error) {
64+
// Enable pf_ring if requested
65+
if pfringAvaliable {
66+
if iface.Config.PFRing {
67+
handle, err = iface.initPFRing()
68+
if err != nil {
69+
return nil, errors.Wrap(err, PFRingInitFailMsg)
70+
}
71+
} else {
72+
handle, err = iface.initPcap()
73+
if err != nil {
74+
return nil, errors.Wrap(err, PCAPInitFailMsg)
75+
}
76+
}
77+
} else {
78+
if iface.Config.PFRing {
79+
logger.Log.Warn(PFRingNotAvaliableMsg)
80+
}
81+
handle, err = iface.initPcap()
82+
if err != nil {
83+
return nil, errors.Wrap(err, PCAPInitFailMsg)
84+
}
85+
}
86+
87+
return handle, nil
88+
}
89+
4890
// validateIface validates the interface against the interfaces from the system
4991
func validateIface(interfaceName string) (ok bool, err error) {
5092
ok = false

pkg/interfaces/messages.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,14 @@ const (
2323
InterfacesInitMsg = "interface intiated successfully"
2424
InterfacesListFailedMsg = "unable to list system interfaces, because"
2525
PFRingInitFaildMsg = "unable to crate new pf_ring object, because"
26+
PCAPInitFaildMsg = "unable to crate new pcap object, because"
2627
SettingBPFFilterFailedMsg = "unable to set the BPF filter, because"
2728
EnablePFRingFailedMsg = "while enabling PFRing found"
2829
PFRingEnabledMsg = "PFRing enabled successflly"
30+
PCAPEnabledMsg = "PCAP sniffer enabled successflly"
31+
PCAPInitFailMsg = "while initiating pcap got"
32+
PFRingInitFailMsg = "while initiating pf_ring got"
33+
PFRingNotAvaliableMsg = "PFRing is not avaliable. Falling down to PCAP sniffer"
2934
)
3035

3136
var (

pkg/interfaces/pcap.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2020 Jaume Martin
2+
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// +build !pf_ring
16+
17+
package interfaces
18+
19+
import (
20+
"github.com/google/gopacket"
21+
"github.com/google/gopacket/pcap"
22+
"github.com/mole-ids/mole/pkg/logger"
23+
"github.com/pkg/errors"
24+
)
25+
26+
// initPcap initializes PFRing on the interface defined in the config
27+
func (iface *Interfaces) initPcap() (gopacket.PacketDataSource, error) {
28+
handle, err := pcap.OpenLive(iface.Config.IFace, snapshotLength, true, pcap.BlockForever)
29+
if err != nil {
30+
return nil, errors.Wrap(err, PCAPInitFaildMsg)
31+
}
32+
33+
// If there is a BPF fitler then apply it
34+
if iface.Config.BPFfilter != "" {
35+
if err = handle.SetBPFFilter(iface.Config.BPFfilter); err != nil {
36+
return nil, errors.Wrap(err, SettingBPFFilterFailedMsg)
37+
}
38+
}
39+
40+
logger.Log.Info(PCAPEnabledMsg)
41+
return handle, nil
42+
}
43+
44+
func (iface *Interfaces) initPFRing() (gopacket.PacketDataSource, error) {
45+
return nil, nil
46+
}

pkg/interfaces/pcap_w_pfring.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2020 Jaume Martin
2+
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// +build pf_ring
16+
17+
package interfaces
18+
19+
import (
20+
"github.com/google/gopacket"
21+
"github.com/google/gopacket/pcap"
22+
"github.com/mole-ids/mole/pkg/logger"
23+
"github.com/pkg/errors"
24+
)
25+
26+
// initPcap initializes PFRing on the interface defined in the config
27+
func (iface *Interfaces) initPcap() (gopacket.PacketDataSource, error) {
28+
handle, err := pcap.OpenLive(iface.Config.IFace, snapshotLength, true, pcap.BlockForever)
29+
if err != nil {
30+
return nil, errors.Wrap(err, PCAPInitFaildMsg)
31+
}
32+
33+
// If there is a BPF fitler then apply it
34+
if iface.Config.BPFfilter != "" {
35+
if err = handle.SetBPFFilter(iface.Config.BPFfilter); err != nil {
36+
return nil, errors.Wrap(err, SettingBPFFilterFailedMsg)
37+
}
38+
}
39+
40+
logger.Log.Info(PCAPEnabledMsg)
41+
return handle, nil
42+
}

0 commit comments

Comments
 (0)