/
ndpi.lua
138 lines (119 loc) · 4.3 KB
/
ndpi.lua
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
132
133
134
135
136
137
138
local scanner = require("apps.wall.scanner")
local const = require("apps.wall.constants")
local opt = require("apps.wall.scanner.ndpi_opt")
local util = require("apps.wall.util")
local ndpi = require("ndpi")
local rd32, ipv4_addr_cmp, ipv6_addr_cmp = util.rd32, util.ipv4_addr_cmp, util.ipv6_addr_cmp
local ETH_TYPE_IPv4 = const.ETH_TYPE_IPv4
local ETH_TYPE_IPv6 = const.ETH_TYPE_IPv6
local IPv4_PROTO_UDP = const.IPv4_PROTO_UDP
local IPv4_PROTO_TCP = const.IPv4_PROTO_TCP
local NdpiFlow = subClass()
NdpiFlow._name = "SnabbWall nDPI Flow"
function NdpiFlow:new(key)
local f = NdpiFlow:superClass().new(self)
f._ndpi_flow = ndpi.flow()
f._ndpi_src_id = ndpi.id()
f._ndpi_dst_id = ndpi.id()
f.protocol = ndpi.protocol.PROTOCOL_UNKNOWN
f.proto_master = ndpi.protocol.PROTOCOL_UNKNOWN
f.key = key
f.packets = 0
f.last_seen = 0
return f
end
function NdpiFlow:update_counters(time)
self.packets = self.packets + 1
self.last_seen = time
end
local NdpiScanner = subClass(scanner.Scanner)
NdpiScanner._name = "SnabbWall nDPI packet Scanner"
function NdpiScanner:new(ticks_per_second)
local s = NdpiScanner:superClass().new(self)
s.protocols = ndpi.protocol_bitmask():set_all()
s._ndpi = ndpi.detection_module(ticks_per_second or 1000):set_protocol_bitmask(s.protocols)
s._flows = {}
return s
end
function NdpiScanner:get_flow(p)
local key = (self:extract_packet_info(p))
return key and self._flows[key:hash()] or nil
end
function NdpiScanner:flows()
local flows = self._flows
return coroutine.wrap(function ()
for _, flow in pairs(flows) do
coroutine.yield(flow)
end
end)
end
function NdpiScanner:protocol_name(protocol)
local name = ndpi.protocol[protocol]
if name:sub(1, #"PROTOCOL_") == "PROTOCOL_" then
name = name:sub(#"PROTOCOL_" + 1)
end
return name
end
-- FIXME: Overall this needs checking for packet boundaries and sizes
function NdpiScanner:scan_packet(p, time)
-- Extract packet information
local key, ip_offset, src_addr, src_port, dst_addr, dst_port = self:extract_packet_info(p)
if not key then
return false, nil
end
-- Get an existing data flow or create a new one
local key_hash = key:hash()
local flow = self._flows[key_hash]
if not flow then
flow = NdpiFlow:new(key)
self._flows[key_hash] = flow
end
flow:update_counters(time)
if flow.protocol ~= ndpi.protocol.PROTOCOL_UNKNOWN then
return true, flow
end
local src_id, dst_id = flow._ndpi_src_id, flow._ndpi_dst_id
if key:eth_type() == ETH_TYPE_IPv4 then
if ipv4_addr_cmp(src_addr, key.lo_addr) ~= 0 or
ipv4_addr_cmp(dst_addr, key.hi_addr) ~= 0 or
src_port ~= key.lo_port or dst_port ~= key.hi_port
then
src_id, dst_id = dst_id, src_id
end
elseif key:eth_type() == ETH_TYPE_IPv6 then
if ipv6_addr_cmp(src_addr, key.lo_addr) ~= 0 or
ipv6_addr_cmp(dst_addr, key.hi_addr) ~= 0 or
src_port ~= key.lo_port or dst_port ~= key.hi_port
then
src_id, dst_id = dst_id, src_id
end
end
flow.proto_master, flow.protocol =
opt.process_packet(self._ndpi,
flow._ndpi_flow,
p.data + ip_offset,
p.length - ip_offset,
time,
src_id,
dst_id)
if flow.protocol ~= ndpi.protocol.PROTOCOL_UNKNOWN then
return true, flow
end
-- TODO: Check and tune-up the constants for number of packets
-- TODO: Do similarly for IPv6 packets once nDPI supports using IPv6
-- addresses here (see https://github.com/ntop/nDPI/issues/183)
if (flow.key.ip_proto == IPv4_PROTO_UDP and flow.packets > 8) or
(flow.key.ip_proto == IPv4_PROTO_TCP and flow.packets > 10)
then
flow.proto_master, flow.protocol =
opt.guess_undetected_protocol(self._ndpi,
flow.key.ip_proto,
rd32(src_addr), src_port,
rd32(dst_addr), dst_port)
-- TODO: Check whether we should check again for PROTOCOL_UNKNOWN
return true, flow
end
-- Flow not yet identified
return false, flow
end
return NdpiScanner