File: presets_utils.lua

package info (click to toggle)
ntopng 5.2.1%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 121,832 kB
  • sloc: javascript: 143,431; cpp: 71,175; ansic: 11,108; sh: 4,687; makefile: 911; python: 587; sql: 512; pascal: 234; perl: 118; ruby: 52; exp: 4
file content (385 lines) | stat: -rw-r--r-- 14,714 bytes parent folder | download
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
--
-- (C) 2016-22 - ntop.org
--

local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path

local discover = require "discover_utils"

local presets_utils = {}

presets_utils.DROP = "0"
presets_utils.ALLOW = "1"
presets_utils.DEFAULT_ACTION = presets_utils.DROP

presets_utils.policies = {}

-- Create an empty preset for a device type if it does not exist
local function createPreset(device_type_name)
   if presets_utils.policies[device_type_name] == nil then
      presets_utils.policies[device_type_name] = { client = {}, server = {} }
   end
end

-- Add policies to a device type cloning policies from an initial device type.
-- source_preset is a table
local function addPreset(device_type_name, source_preset)
   createPreset(device_type_name)
   presets_utils.policies[device_type_name].client = 
      table.merge(presets_utils.policies[device_type_name].client, source_preset.client)
   presets_utils.policies[device_type_name].server = 
      table.merge(presets_utils.policies[device_type_name].server, source_preset.server)
end

-- Add policies to a device type cloning policies from another device type.
-- source_device_type_name is the string identifying the source device type
local function addPresetFrom(device_type_name, source_device_type_name)
   addPreset(device_type_name,
      presets_utils.policies[source_device_type_name])
end

-- Add policy for a specific protocol to a device type
local function addProtocol(device_type_name, client_or_server, proto_id, action)
   createPreset(device_type_name)
   presets_utils.policies[device_type_name][client_or_server][tonumber(proto_id)] = action
end

-- Add policy for a specific protocol (by name) to a device type
local function addProtocolByName(device_type_name, client_or_server, proto_name, action)
   createPreset(device_type_name)
   local proto_id = interface.getnDPIProtoId(proto_name)

   if(proto_id == -1) then
      traceError(TRACE_WARNING, TRACE_CONSOLE, "Unknown device protocol: " .. proto_name)
   end

   presets_utils.policies[device_type_name][client_or_server][tonumber(proto_id)] = action
end

local function setAllProtocols(device_type_name, client_or_server, action)
   createPreset(device_type_name)
   local items = interface.getnDPIProtocols(nil, true)
   for proto_name,proto_id in pairs(items) do
      presets_utils.policies[device_type_name][client_or_server][tonumber(proto_id)] = action
   end
end

function presets_utils.init()
   -----------------------------------------------------------------------------
   -----------------------------------------------------------------------------
   -- DEVICE PROTOCOL PRESETS
   -- Note: 
   -- - 'unknown' devices and all devices not listed here have 'allow all' as default
   -- - 'DHCP' and 'DNS' are always allowed in the datapath

   local basic_policy = {
      client = {
	 [  8] = presets_utils.ALLOW,  -- MDNS
	 [  9] = presets_utils.ALLOW,  -- NTP
	 [ 10] = presets_utils.ALLOW,  -- NetBIOS
	 [ 81] = presets_utils.ALLOW,  -- ICMP
	 [ 82] = presets_utils.ALLOW,  -- IGMP
	 [102] = presets_utils.ALLOW,  -- ICMPV6
	 [103] = presets_utils.ALLOW,  -- DHCPV6
	 [153] = presets_utils.ALLOW,  -- UPnP
	 [154] = presets_utils.ALLOW,  -- LLMNR
      },
      server = {
	 [ 81] = presets_utils.ALLOW,  -- ICMP
	 [102] = presets_utils.ALLOW,  -- ICMPV6
      }
   }

   -- IoT like devices
   addPreset('iot', basic_policy)
   addProtocolByName('iot', 'client', 'HTTP',      presets_utils.ALLOW)
   addProtocolByName('iot', 'server', 'HTTP',      presets_utils.ALLOW)
   addProtocolByName('iot', 'client', 'TLS',       presets_utils.ALLOW)
   addProtocolByName('iot', 'server', 'TLS',       presets_utils.ALLOW)

   addPresetFrom('video', 'iot')
   addProtocolByName('video', 'server', 'RTP',     presets_utils.ALLOW)
   addProtocolByName('video', 'server', 'RTSP',    presets_utils.ALLOW)

   -- Multimedia like devices
   addPreset('multimedia', basic_policy)
   addProtocolByName('multimedia', 'client', 'HTTP',        presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'TLS',         presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'RTP',         presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'RTSP',        presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'NetFlix',     presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'YouTube',     presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'AmazonVideo', presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'AppleiTunes', presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'Deezer',      presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'LastFM',      presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'SoundCloud',  presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'Spotify',     presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'Skype_Teams', presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'SkypeCall',   presets_utils.ALLOW)
   addProtocolByName('multimedia', 'client', 'SSDP',        presets_utils.ALLOW)

   addPresetFrom('tv', 'multimedia')

   -- NAS devices
   addPreset('nas', basic_policy)
   addProtocolByName('nas', 'server', 'HTTP',        presets_utils.ALLOW)
   addProtocolByName('nas', 'server', 'FTP_CONTROL', presets_utils.ALLOW)
   addProtocolByName('nas', 'server', 'FTP_DATA',    presets_utils.ALLOW)
   addProtocolByName('nas', 'server', 'AFP',         presets_utils.ALLOW)
   addProtocolByName('nas', 'server', 'NFS',         presets_utils.ALLOW)
   addProtocolByName('nas', 'server', 'RSYNC',       presets_utils.ALLOW)
   addProtocolByName('nas', 'server', 'TFTP',        presets_utils.ALLOW)

   -- Printer devices
   addPreset('printer', basic_policy)
   addProtocolByName('printer', 'server', 'HTTP',    presets_utils.ALLOW)
   addProtocolByName('printer', 'server', 'TLS',     presets_utils.ALLOW)
   addProtocolByName('printer', 'server', 'SNMP',    presets_utils.ALLOW)

   -----------------------------------------------------------------------------
   -----------------------------------------------------------------------------
end

local drop_icon = "exclamation-triangle"
local allow_icon = "check"
local drop_text = i18n("device_protocols.alert")
local allow_text = i18n("device_protocols.ok")
local is_nedge = ntop.isnEdge()

if is_nedge then
   drop_icon = "ban"
   allow_icon = "asterisk"
   drop_text = i18n("users.shapers.drop")
   allow_text = i18n("users.shapers.default")
end

-- Constants
presets_utils.actions = {
   { name = "DROP",  id = presets_utils.DROP, text = drop_text,
     icon = '<i class="fas fa-'..drop_icon..'" aria-hidden="true"></i>'},
   { name = "ALLOW", id = presets_utils.ALLOW, text = allow_text,
     icon = '<i class="fas fa-'..allow_icon..'" aria-hidden="true"></i>'},
}

-- Action ID to action name
function presets_utils.actionIDToAction(action_id)
   for _, action in pairs(presets_utils.actions) do
      if action.id == tostring(action_id) then
	 return action
      end
   end
end

-- Check if a protocol is allowed by the default presets
local function isProtoAllowedByPresets(device_type, client_or_server, proto_id)
   local device_type_name = discover.id2devtype(tonumber(device_type))
   if presets_utils.policies[device_type_name] == nil or
      presets_utils.policies[device_type_name][client_or_server] == nil then
      return true -- no preset: allowed
   end

   if presets_utils.policies[device_type_name][client_or_server][proto_id] == presets_utils.ALLOW then
      return true
   end

   return false
end

-- Return device policies redis key
local function getDevicePoliciesKey(device_type, client_or_server)
   return "ntopng.prefs.device_policies."..client_or_server.."." .. device_type
end

-- Check if the device policy for a protocol is set on redis
local function isCustomDeviceProtoPolicySet(device_type, client_or_server, proto_id)
   local key = getDevicePoliciesKey(device_type, client_or_server)

   local action_id = ntop.getHashCache(key, proto_id)

   if not isEmptyString(action_id) then
      return true
   end

   return false
end

-- Return device policy for a protocol from redis
local function getCustomDeviceProtoPolicy(device_type, client_or_server, proto_id)
   local key = getDevicePoliciesKey(device_type, client_or_server)

   local action_id = ntop.getHashCache(key, proto_id)

   if not isEmptyString(action_id) then
      return action_id
   end

   return presets_utils.DEFAULT_ACTION
end

-- Store the device policy for a protocol on redis
local function setCustomDeviceProtoPolicy(device_type, client_or_server, proto_id, action_id)
   local key = getDevicePoliciesKey(device_type, client_or_server)
   ntop.setHashCache(key, proto_id, action_id)
end

-- Update the device policy for a protocol on redis
function presets_utils.updateDeviceProto(device_type, client_or_server, proto_id, action_id)
   local proto_name = interface.getnDPIProtoName(tonumber(proto_id))

   if isCustomDeviceProtoPolicySet(device_type, client_or_server, proto_id) then
      -- the user changed this policy, storing the change on redis
      setCustomDeviceProtoPolicy(device_type, client_or_server, proto_id, action_id)
   elseif action_id == presets_utils.ALLOW and isProtoAllowedByPresets(device_type, client_or_server, proto_id) then
      -- it seems this has not been changed by the user, nothing to do
   else
      -- preset changed by the user, seting custom user-defined action on redis
      setCustomDeviceProtoPolicy(device_type, client_or_server, proto_id, action_id)
   end
end

-- Return the list of user-defined policies (allow and drop) for 'client' or 'server' from redis
local function getCustomDevicePoliciesByDir(device_type, client_or_server)
   local custom_policies = {}

   local key = getDevicePoliciesKey(device_type, client_or_server)
   local proto_ids = ntop.getHashKeysCache(key) or {}

   for proto_id,_ in pairs(proto_ids) do
      local proto_name = interface.getnDPIProtoName(tonumber(proto_id))
      local p = { protoId=proto_id, protoName=proto_name }
      p.actionId = getCustomDeviceProtoPolicy(device_type, client_or_server, proto_id)
      custom_policies[tonumber(proto_id)] = p
   end

   return custom_policies
end

-- Return the list of policies (allow only) for 'client' or 'server' merging preset policies with user-defined policies
local function getDevicePoliciesByDir(device_type, client_or_server)
   local device_type_name = discover.id2devtype(tonumber(device_type))
   local device_policies = {}
   local default_action = presets_utils.ALLOW -- allow by default if no preset for the device

   if presets_utils.policies[device_type_name] ~= nil and 
      presets_utils.policies[device_type_name][client_or_server] ~= nil then
      -- device with preset, deny by default
      default_action = presets_utils.DROP
   end

   -- init
   local items = interface.getnDPIProtocols(nil, true)

   for proto_name,proto_id in pairs(items) do
      local p = { protoId=proto_id, protoName=proto_name, actionId=default_action }
      device_policies[tonumber(proto_id)] = p
   end

   -- protocols from presets
   if presets_utils.policies[device_type_name] ~= nil and 
      presets_utils.policies[device_type_name][client_or_server] ~= nil then
      for proto_id,action_id in pairs(presets_utils.policies[device_type_name][client_or_server]) do
         local proto_name = interface.getnDPIProtoName(tonumber(proto_id))
         local p = { protoId=proto_id, protoName=proto_name, actionId=action_id }
         device_policies[tonumber(proto_id)] = p
      end
   else
   end

   -- custom user-defined policies from redis
   local custom_policies = getCustomDevicePoliciesByDir(device_type, client_or_server) 
   for k,v in pairs(custom_policies) do 
      device_policies[tonumber(v.protoId)] = v
   end   

   return device_policies
end

-- Check if a protocol is allowed
function presets_utils.isProtoAllowed(device_type, client_or_server, proto_id)
   local device_type_name = discover.id2devtype(tonumber(device_type))

   local device_policies = getDevicePoliciesByDir(device_type, client_or_server)
   local policy = device_policies[tonumber(proto_id)]
   if policy ~= nil and policy.actionId == presets_utils.ALLOW then
      return true
   end   

   return false
end

-- Reload client or server device policies in the datapath
local function reloadDevicePoliciesByDir(device_type, client_or_server)
   local device_type_name = discover.id2devtype(tonumber(device_type))
   local allowed_protocols = {}

   local device_policies = getDevicePoliciesByDir(device_type, client_or_server)
   for k,v in pairs(device_policies) do 
      if v.actionId == presets_utils.ALLOW then
	 v.protoId = tonumber(v.protoId)

	 if(v.protoId ~= -1) then
	    allowed_protocols[v.protoId] = tonumber(presets_utils.ALLOW)
	 end
      end
   end   

   ntop.reloadDeviceProtocols(tonumber(device_type), client_or_server, allowed_protocols)
end

-- Reload device policies in the datapath
function presets_utils.reloadDevicePolicies(device_type)
   reloadDevicePoliciesByDir(device_type, "client")
   reloadDevicePoliciesByDir(device_type, "server")

   if is_nedge then
     -- reload the new policy on active flows
     interface.updateFlowsShapers()
   end
end

-- Reload device policies for all devices in the datapath
function presets_utils.reloadAllDevicePolicies()
   for device_type, info in discover.sortedDeviceTypeLabels() do
      presets_utils.reloadDevicePolicies(device_type)
   end
end

-- Return a list of policy for a device (both client and server)
function presets_utils.getDevicePolicies(device_type)
   local device_policies = {}

   local client_device_policies = getDevicePoliciesByDir(device_type, "client")
   local server_device_policies = getDevicePoliciesByDir(device_type, "server")

   for k,v in pairs(client_device_policies) do 
      local p = { protoId=v.proto_id }
      p.clientActionId=v.actionId
      device_policies[k] = p
   end   

   for k,v in pairs(server_device_policies) do
      local p = device_policies[k]
      if p == nil then
         p = { protoId=v.proto_id }
      end
      p.serverActionId=v.actionId
      device_policies[k] = p
   end   

   return device_policies
end


function presets_utils.resetDevicePoliciesFromPresets(device_type)
   local key_client = getDevicePoliciesKey(device_type, "client")
   local key_server = getDevicePoliciesKey(device_type, "server")

   ntop.delCache(key_client)
   ntop.delCache(key_server)
end

--------------------------------------------------------------------------------

return presets_utils