forked from mushorg/go-dpi
/
classifiers.go
131 lines (117 loc) · 4.32 KB
/
classifiers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Package classifiers contains the custom classifiers for each protocol
// and the helpers for applying them on a flow.
package classifiers
import (
"github.com/N0mansky/go-dpi/types"
"github.com/google/gopacket"
)
// GoDPIName is the name of the library, to be used as an identifier for the
// source of a classification.
const GoDPIName = types.ClassificationSource("go-dpi")
// ClassifierModule is the module that contains the custom go-dpi flow classifiers.
type ClassifierModule struct {
classifierList []GenericClassifier
}
// GenericClassifier is implemented by every classifier. It contains a method
// that returns the classifier's detected protocol.
type GenericClassifier interface {
// GetProtocol returns the protocol this classifier can detect.
GetProtocol() types.Protocol
}
// HeuristicClassifier is implemented by the classifiers that have heuristic
// methods to detect a protocol.
type HeuristicClassifier interface {
// HeuristicClassify returns whether this classifier can identify the flow
// using heuristics.
HeuristicClassify(*types.Flow) bool
}
// ClassifierModuleConfig is given to the module's ConfigureModule method, in
// order to set which classifiers are active and their order.
type ClassifierModuleConfig struct {
Classifiers []GenericClassifier
}
// NewClassifierModule returns a new ClassifierModule with the default
// configuration. By default, all classifiers are active.
func NewClassifierModule() *ClassifierModule {
module := &ClassifierModule{}
module.classifierList = []GenericClassifier{
FTPClassifier{},
HTTPClassifier{},
ICMPClassifier{},
NetBIOSClassifier{},
DNSClassifier{},
RDPClassifier{},
RPCClassifier{},
SMBClassifier{},
SMTPClassifier{},
SSHClassifier{},
SSLClassifier{},
JABBERClassifier{},
MQTTClassifier{},
}
return module
}
// Initialize initializes the module instance.
func (module *ClassifierModule) Initialize() error {
return nil
}
// Destroy destroys the module instance.
func (module *ClassifierModule) Destroy() error {
return nil
}
// ClassifyFlow applies all the classifiers to a flow and returns the protocol
// that is detected by a classifier if there is one. Otherwise the returned
// protocol is Unknown.
func (module *ClassifierModule) ClassifyFlow(flow *types.Flow) (result types.ClassificationResult) {
for _, classifier := range module.classifierList {
if heuristic, ok := classifier.(HeuristicClassifier); ok {
if heuristic.HeuristicClassify(flow) {
result.Protocol = classifier.GetProtocol()
result.Source = GoDPIName
flow.SetClassificationResult(result.Protocol, result.Source)
break
}
}
}
return
}
// ClassifyFlowAll applies all the classifiers to a flow and returns the
// all the protocols detected by any of the classifiers.
func (module *ClassifierModule) ClassifyFlowAll(flow *types.Flow) (results []types.ClassificationResult) {
results = append(results, module.ClassifyFlow(flow))
return
}
// ConfigureModule configures this module instance with the given configuration.
// This should called before the module instance is initialized, otherwise
// Destroy and Initialize should be called on the module manually.
func (module *ClassifierModule) ConfigureModule(config ClassifierModuleConfig) {
module.classifierList = config.Classifiers
}
// checkFlowLayer applies the check function to the specified layer of each
// packet in a flow, where it is available. It returns whether there is a
// packet in the flow for which the check function returns true.
func checkFlowLayer(flow *types.Flow, layerType gopacket.LayerType,
checkFunc func(layer gopacket.Layer) bool) bool {
for _, packet := range flow.GetPackets() {
if layer := packet.Layer(layerType); layer != nil {
if checkFunc(layer) {
return true
}
}
}
return false
}
// checkFirstPayload applies the check function to the payload of the first
// packet that has the specified layer. It returns the result of that function
// on that first packet, or false if no such packet exists.
func checkFirstPayload(packets []gopacket.Packet, layerType gopacket.LayerType,
checkFunc func(payload []byte, packetsRest []gopacket.Packet) bool) bool {
for i, packet := range packets {
if layer := packet.Layer(layerType); layer != nil {
if payload := layer.LayerPayload(); payload != nil && len(payload) > 0 {
return checkFunc(payload, packets[i+1:])
}
}
}
return false
}