]> git.ipfire.org Git - collecty.git/blame - src/collecty/plugins/conntrack.py
Refectoring of the main classes
[collecty.git] / src / collecty / plugins / conntrack.py
CommitLineData
f37913e8 1#!/usr/bin/python3
0ec1854a
MT
2###############################################################################
3# #
4# collecty - A system statistics collection daemon for IPFire #
5# Copyright (C) 2015 IPFire development team #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
21
22import os
23
f37913e8 24from . import base
0ec1854a 25
0308c0f3
MT
26from ..i18n import _
27
0ec1854a
MT
28CONNTRACK_FILE = "/proc/net/nf_conntrack"
29
30class ConntrackTable(object):
31 _layer3_protocols = (
32 "ipv6",
33 "ipv4",
34 "other",
35 )
36
37 _layer4_protocols = (
38 "dccp",
39 "icmp",
40 "igmp",
41 "sctp",
42 "tcp",
43 "udp",
44 "udplite",
45 "other",
46 )
47
48 _stateful_layer4_protocols = {
49 "dccp" : (
50 "CLOSEREQ",
51 "CLOSING",
52 "IGNORE",
53 "INVALID",
54 "NONE",
55 "OPEN",
56 "PARTOPEN",
57 "REQUEST",
58 "RESPOND",
59 "TIME_WAIT",
60 ),
61 "sctp" : (
62 "CLOSED",
63 "COOKIE_ECHOED",
64 "COOKIE_WAIT",
65 "ESTABLISHED",
66 "NONE",
67 "SHUTDOWN_ACK_SENT",
68 "SHUTDOWN_RECD",
69 "SHUTDOWN_SENT",
70 ),
71 "tcp" : (
72 "CLOSE",
73 "CLOSE_WAIT",
74 "ESTABLISHED",
75 "FIN_WAIT",
76 "LAST_ACK",
77 "NONE",
78 "SYN_RECV",
79 "SYN_SENT",
80 "SYN_SENT2",
81 "TIME_WAIT",
82 ),
83 }
84
85 def __init__(self, filename):
86 with open(filename) as f:
87 self.layer3_protocols = {}
88 for proto in self._layer3_protocols:
89 self.layer3_protocols[proto] = 0
90
91 self.layer4_protocols = {}
92 for proto in self._layer4_protocols:
93 self.layer4_protocols[proto] = 0
94
95 self.protocol_states = {}
96 for proto, states in self._stateful_layer4_protocols.items():
97 self.protocol_states[proto] = dict((state, 0) for state in states)
98
99 for line in f.readlines():
100 line = line.split()
101
102 # Layer 3 protocol
103 layer3_protocol = line[0]
104
105 try:
106 self.layer3_protocols[layer3_protocol] += 1
107 except KeyError:
108 self.layer3_protocols["other"] += 1
109
110 # Layer 4 protocol
111 layer4_protocol = line[2]
112
113 try:
114 self.layer4_protocols[layer4_protocol] += 1
115 except KeyError:
116 self.layer4_protocols["other"] += 1
117 layer4_protocol = "other"
118
119 # Count connection states
f37913e8 120 if layer4_protocol in self.protocol_states:
0ec1854a
MT
121 state = line[5]
122
123 try:
124 self.protocol_states[layer4_protocol][state] += 1
125 except KeyError:
126 pass
127
128
0308c0f3
MT
129class ConntrackLayer3ProtocolsGraphTemplate(base.GraphTemplate):
130 name = "conntrack-layer3-protocols"
131
f998b6cb 132 _protocols = ConntrackTable._layer3_protocols
0308c0f3
MT
133
134 protocol_colours = {
135 "ipv6" : "#cc0033",
136 "ipv4" : "#cccc33",
137 }
138
d5d4c0e7
MT
139 def get_objects(self, *args):
140 return [
141 self.plugin.get_object("layer3-protocols"),
142 ]
cd8bba0b 143
f998b6cb
MT
144 @property
145 def protocols(self):
146 # Order the protocols by standard deviation which will give us cleaner graphs
147 # http://stackoverflow.com/questions/13958409/how-to-graph-rrd-stackable-data-by-standard-deviation-to-maximize-readability
148 stddev = self.object.get_stddev()
149
150 protos = {}
151 for p in self._protocols:
152 protos[p] = stddev.get(p)
153
154 return sorted(protos, key=protos.get)
155
fa43c345
MT
156 @property
157 def protocol_descriptions(self):
158 _ = self.locale.translate
159
160 return {
161 "ipv6" : _("IPv6"),
162 "ipv4" : _("IPv4"),
163 "other" : _("Other"),
164 }
0308c0f3
MT
165
166 @property
167 def graph_title(self):
fa43c345 168 _ = self.locale.translate
0308c0f3
MT
169 return _("Connections by Layer 3 Protocols")
170
171 @property
172 def graph_vertical_label(self):
fa43c345 173 _ = self.locale.translate
0308c0f3
MT
174 return _("Number of open connections")
175
cd8bba0b
MT
176 @property
177 def rrd_defs(self):
178 return []
0308c0f3
MT
179
180 @property
181 def rrd_graph(self):
fa43c345 182 _ = self.locale.translate
0308c0f3
MT
183 args = []
184
f998b6cb 185 for proto in self.protocols:
0308c0f3
MT
186 i = {
187 "colour" : self.protocol_colours.get(proto, "#000000"),
188 "description" : self.protocol_descriptions.get(proto, proto),
189 "proto" : proto,
190 "type" : type,
191
192 "legend_min" : "%10s\: %%8.0lf" % _("Minimum"),
193 "legend_max" : "%10s\: %%8.0lf" % _("Maximum"),
194 "legend_avg" : "%10s\: %%8.0lf" % _("Average"),
195 "legend_cur" : "%10s\: %%8.0lf" % _("Current"),
196 }
197
cd8bba0b 198 args += self.object.make_rrd_defs(proto) + [
0308c0f3 199 "AREA:%(proto)s%(colour)s:%(description)-15s:STACK" % i,
0308c0f3 200 "GPRINT:%(proto)s_cur:%(legend_cur)s" % i,
0308c0f3 201 "GPRINT:%(proto)s_avg:%(legend_avg)s" % i,
0308c0f3 202 "GPRINT:%(proto)s_min:%(legend_min)s" % i,
0308c0f3
MT
203 "GPRINT:%(proto)s_max:%(legend_max)s\\n" % i,
204 ]
205
206 return args
207
208 @property
209 def rrd_graph_args(self):
210 return [
211 "--legend-direction=bottomup",
212 ]
213
214
215class ConntrackLayer4ProtocolsGraphTemplate(ConntrackLayer3ProtocolsGraphTemplate):
216 name = "conntrack-layer4-protocols"
217
218 protocol_colours = {
219 "tcp" : "#336600",
220 "udp" : "#666633",
221 "icmp" : "#336666",
222 "igmp" : "#666699",
223 "udplite" : "#3366cc",
224 "sctp" : "#6666ff",
225 "dccp" : "#33cc00",
226 }
227
fa43c345
MT
228 @property
229 def protocol_descriptions(self):
230 _ = self.locale.translate
231
232 return {
233 "tcp" : _("TCP"),
234 "udp" : _("UDP"),
235 "icmp" : _("ICMP"),
236 "igmp" : _("IGMP"),
237 "udplite" : _("UDP Lite"),
238 "sctp" : _("SCTP"),
239 "dccp" : _("DCCP"),
240 "other" : _("Other"),
241 }
0308c0f3
MT
242
243 protocol_sortorder = {
244 "tcp" : 1,
245 "udp" : 2,
246 "icmp" : 3,
247 "igmp" : 4,
248 "udplite" : 5,
249 "sctp" : 6,
250 "dccp" : 7,
251 }
252
d5d4c0e7
MT
253 def get_objects(self, *args):
254 return [
255 self.plugin.get_object("layer4-protocols"),
256 ]
cd8bba0b 257
0308c0f3
MT
258 @property
259 def graph_title(self):
fa43c345 260 _ = self.locale.translate
0308c0f3
MT
261 return _("Connections by IP Protocols")
262
263 @property
f998b6cb 264 def _protocols(self):
0308c0f3
MT
265 return sorted(ConntrackTable._layer4_protocols,
266 key=lambda x: self.protocol_sortorder.get(x, 99))
267
0308c0f3
MT
268
269class ConntrackProtocolWithStatesGraphTemplate(base.GraphTemplate):
270 name = "conntrack-protocol-states"
271
272 lower_limit = 0
273
274 states_colours = {
275 "dccp" : {
276 "CLOSEREQ" : "#000000",
277 "CLOSING" : "#111111",
278 "IGNORE" : "#222222",
279 "INVALID" : "#333333",
280 "NONE" : "#444444",
281 "OPEN" : "#555555",
282 "PARTOPEN" : "#666666",
283 "REQUEST" : "#777777",
284 "RESPOND" : "#888888",
285 "TIME_WAIT" : "#999999",
286 },
287 "sctp" : {
288 "CLOSED" : "#000000",
289 "COOKIE_ECHOED" : "#111111",
290 "COOKIE_WAIT" : "#222222",
291 "ESTABLISHED" : "#333333",
292 "NONE" : "#444444",
293 "SHUTDOWN_ACK_SENT" : "#555555",
294 "SHUTDOWN_RECD" : "#666666",
295 "SHUTDOWN_SENT" : "#777777",
296 },
297 "tcp" : {
298 "CLOSE" : "#ffffff",
299 "CLOSE_WAIT" : "#999999",
300 "ESTABLISHED" : "#000000",
301 "FIN_WAIT" : "#888888",
302 "LAST_ACK" : "#777777",
303 "NONE" : "#000000",
304 "SYN_RECV" : "#111111",
305 "SYN_SENT" : "#222222",
306 "SYN_SENT2" : "#333333",
307 "TIME_WAIT" : "#444444",
308 },
309 }
310
311 states_descriptions = {
312 "dccp" : {},
313 "sctp" : {},
314 "tcp" : {},
315 }
316
317 states_sortorder = {
318 "dccp" : {
319 "CLOSEREQ" : 0,
320 "CLOSING" : 0,
321 "IGNORE" : 0,
322 "INVALID" : 0,
323 "NONE" : 0,
324 "OPEN" : 0,
325 "PARTOPEN" : 0,
326 "REQUEST" : 0,
327 "RESPOND" : 0,
328 "TIME_WAIT" : 0,
329 },
330 "sctp" : {
331 "CLOSED" : 0,
332 "COOKIE_ECHOED" : 0,
333 "COOKIE_WAIT" : 0,
334 "ESTABLISHED" : 0,
335 "NONE" : 0,
336 "SHUTDOWN_ACK_SENT" : 0,
337 "SHUTDOWN_RECD" : 0,
338 "SHUTDOWN_SENT" : 0,
339 },
340 "tcp" : {
341 "CLOSE" : 9,
342 "CLOSE_WAIT" : 8,
343 "ESTABLISHED" : 1,
344 "FIN_WAIT" : 6,
345 "LAST_ACK" : 7,
346 "NONE" : 10,
347 "SYN_RECV" : 2,
348 "SYN_SENT" : 3,
349 "SYN_SENT2" : 4,
350 "TIME_WAIT" : 5,
351 },
352 }
353
354 @property
355 def graph_title(self):
fa43c345 356 _ = self.locale.translate
0308c0f3
MT
357 return _("Protocol States of all %s connections") % self.protocol.upper()
358
359 @property
360 def graph_vertical_label(self):
fa43c345 361 _ = self.locale.translate
0308c0f3
MT
362 return _("Number of open connections")
363
364 @property
365 def protocol(self):
366 return self.object.protocol
367
368 @property
369 def states(self):
370 return sorted(ConntrackTable._stateful_layer4_protocols[self.protocol],
371 key=lambda x: self.states_sortorder[self.protocol].get(x, 99))
372
373 @property
374 def rrd_graph(self):
fa43c345 375 _ = self.locale.translate
0308c0f3
MT
376 args = []
377
378 for state in reversed(self.states):
379 i = {
380 "colour" : self.states_colours[self.protocol].get(state, "#000000"),
381 "description" : self.states_descriptions[self.protocol].get(state, state),
382 "proto" : self.protocol,
383 "state" : state,
384
385 "legend_min" : "%10s\: %%8.0lf" % _("Minimum"),
386 "legend_max" : "%10s\: %%8.0lf" % _("Maximum"),
387 "legend_avg" : "%10s\: %%8.0lf" % _("Average"),
388 "legend_cur" : "%10s\: %%8.0lf" % _("Current"),
389 }
390
cd8bba0b 391 args += self.object.make_rrd_defs(state) + [
0308c0f3 392 "AREA:%(state)s%(colour)s:%(description)-15s:STACK" % i,
0308c0f3 393 "GPRINT:%(state)s_cur:%(legend_cur)s" % i,
0308c0f3 394 "GPRINT:%(state)s_avg:%(legend_avg)s" % i,
0308c0f3 395 "GPRINT:%(state)s_min:%(legend_min)s" % i,
0308c0f3
MT
396 "GPRINT:%(state)s_max:%(legend_max)s\\n" % i,
397 ]
398
399 return args
400
401 @property
402 def rrd_graph_args(self):
403 return [
404 "--legend-direction=bottomup",
405 ]
406
407
0ec1854a
MT
408class ConntrackObject(base.Object):
409 protocol = None
410
411 def init(self, conntrack_table):
412 self.conntrack_table = conntrack_table
413
414 @property
415 def id(self):
416 return self.protocol
417
418
419class ConntrackLayer3ProtocolsObject(ConntrackObject):
420 protocols = ConntrackTable._layer3_protocols
421
422 rrd_schema = [
423 "DS:%s:GAUGE:0:U" % p for p in protocols
424 ]
425
426 @property
427 def id(self):
428 return "layer3-protocols"
429
430 def collect(self):
431 results = []
432
433 for proto in self.protocols:
434 r = self.conntrack_table.layer3_protocols.get(proto, 0)
435 results.append("%s" % r)
436
f648421a 437 return results
0ec1854a
MT
438
439
440class ConntrackLayer4ProtocolsObject(ConntrackObject):
441 protocols = ConntrackTable._layer4_protocols
442
443 rrd_schema = [
444 "DS:%s:GAUGE:0:U" % p for p in protocols
445 ]
446
447 @property
448 def id(self):
449 return "layer4-protocols"
450
451 def collect(self):
452 results = []
453
454 for proto in self.protocols:
455 r = self.conntrack_table.layer4_protocols.get(proto, 0)
456 results.append("%s" % r)
457
f648421a 458 return results
0ec1854a
MT
459
460
461class ConntrackProtocolWithStatesObject(ConntrackObject):
462 def init(self, conntrack_table, protocol):
463 ConntrackObject.init(self, conntrack_table)
464 self.protocol = protocol
465
b54c3e02
MT
466 def __repr__(self):
467 return "<%s %s>" % (self.__class__.__name__, self.protocol)
468
0ec1854a
MT
469 @property
470 def states(self):
471 return ConntrackTable._stateful_layer4_protocols.get(self.protocol)
472
473 @property
474 def rrd_schema(self):
475 return ["DS:%s:GAUGE:0:U" % state for state in self.states]
476
477 def get_states(self):
478 results = []
479
480 for state in self.states:
481 r = self.conntrack_table.protocol_states[self.protocol].get(state, 0)
482 results.append("%s" % r)
483
484 return results
485
486 def collect(self):
f648421a 487 return self.get_states()
0ec1854a
MT
488
489
490class ConntrackPlugin(base.Plugin):
491 name = "conntrack"
492 description = "Conntrack Plugin"
493
0308c0f3
MT
494 templates = [
495 ConntrackLayer3ProtocolsGraphTemplate,
496 ConntrackLayer4ProtocolsGraphTemplate,
497 ConntrackProtocolWithStatesGraphTemplate,
498 ]
0ec1854a
MT
499
500 @property
501 def objects(self):
502 ct = self.get_conntrack_table()
503
504 if ct:
505 yield ConntrackLayer3ProtocolsObject(self, ct)
506 yield ConntrackLayer4ProtocolsObject(self, ct)
507
508 for protocol in ConntrackTable._stateful_layer4_protocols:
509 yield ConntrackProtocolWithStatesObject(self, ct, protocol)
510
511 def get_conntrack_table(self):
512 if not os.path.exists(CONNTRACK_FILE):
513 return
514
515 return ConntrackTable(CONNTRACK_FILE)