]> git.ipfire.org Git - collecty.git/blob - src/collecty/plugins/conntrack.py
Add conntrack plugin
[collecty.git] / src / collecty / plugins / conntrack.py
1 #!/usr/bin/python
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
22 import os
23
24 import base
25
26 CONNTRACK_FILE = "/proc/net/nf_conntrack"
27
28 class ConntrackTable(object):
29 _layer3_protocols = (
30 "ipv6",
31 "ipv4",
32 "other",
33 )
34
35 _layer4_protocols = (
36 "dccp",
37 "icmp",
38 "igmp",
39 "sctp",
40 "tcp",
41 "udp",
42 "udplite",
43 "other",
44 )
45
46 _stateful_layer4_protocols = {
47 "dccp" : (
48 "CLOSEREQ",
49 "CLOSING",
50 "IGNORE",
51 "INVALID",
52 "NONE",
53 "OPEN",
54 "PARTOPEN",
55 "REQUEST",
56 "RESPOND",
57 "TIME_WAIT",
58 ),
59 "sctp" : (
60 "CLOSED",
61 "COOKIE_ECHOED",
62 "COOKIE_WAIT",
63 "ESTABLISHED",
64 "NONE",
65 "SHUTDOWN_ACK_SENT",
66 "SHUTDOWN_RECD",
67 "SHUTDOWN_SENT",
68 ),
69 "tcp" : (
70 "CLOSE",
71 "CLOSE_WAIT",
72 "ESTABLISHED",
73 "FIN_WAIT",
74 "LAST_ACK",
75 "NONE",
76 "SYN_RECV",
77 "SYN_SENT",
78 "SYN_SENT2",
79 "TIME_WAIT",
80 ),
81 }
82
83 def __init__(self, filename):
84 with open(filename) as f:
85 self.layer3_protocols = {}
86 for proto in self._layer3_protocols:
87 self.layer3_protocols[proto] = 0
88
89 self.layer4_protocols = {}
90 for proto in self._layer4_protocols:
91 self.layer4_protocols[proto] = 0
92
93 self.protocol_states = {}
94 for proto, states in self._stateful_layer4_protocols.items():
95 self.protocol_states[proto] = dict((state, 0) for state in states)
96
97 for line in f.readlines():
98 line = line.split()
99
100 # Layer 3 protocol
101 layer3_protocol = line[0]
102
103 try:
104 self.layer3_protocols[layer3_protocol] += 1
105 except KeyError:
106 self.layer3_protocols["other"] += 1
107
108 # Layer 4 protocol
109 layer4_protocol = line[2]
110
111 try:
112 self.layer4_protocols[layer4_protocol] += 1
113 except KeyError:
114 self.layer4_protocols["other"] += 1
115 layer4_protocol = "other"
116
117 # Count connection states
118 if self.protocol_states.has_key(layer4_protocol):
119 state = line[5]
120
121 try:
122 self.protocol_states[layer4_protocol][state] += 1
123 except KeyError:
124 pass
125
126
127 class ConntrackObject(base.Object):
128 protocol = None
129
130 def init(self, conntrack_table):
131 self.conntrack_table = conntrack_table
132
133 @property
134 def id(self):
135 return self.protocol
136
137
138 class ConntrackLayer3ProtocolsObject(ConntrackObject):
139 protocols = ConntrackTable._layer3_protocols
140
141 rrd_schema = [
142 "DS:%s:GAUGE:0:U" % p for p in protocols
143 ]
144
145 @property
146 def id(self):
147 return "layer3-protocols"
148
149 def collect(self):
150 results = []
151
152 for proto in self.protocols:
153 r = self.conntrack_table.layer3_protocols.get(proto, 0)
154 results.append("%s" % r)
155
156 return ":".join(results)
157
158
159 class ConntrackLayer4ProtocolsObject(ConntrackObject):
160 protocols = ConntrackTable._layer4_protocols
161
162 rrd_schema = [
163 "DS:%s:GAUGE:0:U" % p for p in protocols
164 ]
165
166 @property
167 def id(self):
168 return "layer4-protocols"
169
170 def collect(self):
171 results = []
172
173 for proto in self.protocols:
174 r = self.conntrack_table.layer4_protocols.get(proto, 0)
175 results.append("%s" % r)
176
177 return ":".join(results)
178
179
180 class ConntrackProtocolWithStatesObject(ConntrackObject):
181 def init(self, conntrack_table, protocol):
182 ConntrackObject.init(self, conntrack_table)
183 self.protocol = protocol
184
185 @property
186 def states(self):
187 return ConntrackTable._stateful_layer4_protocols.get(self.protocol)
188
189 @property
190 def rrd_schema(self):
191 return ["DS:%s:GAUGE:0:U" % state for state in self.states]
192
193 def get_states(self):
194 results = []
195
196 for state in self.states:
197 r = self.conntrack_table.protocol_states[self.protocol].get(state, 0)
198 results.append("%s" % r)
199
200 return results
201
202 def collect(self):
203 return ":".join(self.get_states())
204
205
206 class ConntrackPlugin(base.Plugin):
207 name = "conntrack"
208 description = "Conntrack Plugin"
209
210 templates = []
211
212 @property
213 def objects(self):
214 ct = self.get_conntrack_table()
215
216 if ct:
217 yield ConntrackLayer3ProtocolsObject(self, ct)
218 yield ConntrackLayer4ProtocolsObject(self, ct)
219
220 for protocol in ConntrackTable._stateful_layer4_protocols:
221 yield ConntrackProtocolWithStatesObject(self, ct, protocol)
222
223 def get_conntrack_table(self):
224 if not os.path.exists(CONNTRACK_FILE):
225 return
226
227 return ConntrackTable(CONNTRACK_FILE)