@cryptotaxi247 / netdata-1 / commits / 5b78cf561

Add ipfw charts to FreeBSD plugin

Vladimir Kobal committed Jun 12, 2017 at 00:57 UTC 5b78cf56189dc683dbee46d5082f0f59a88e8105
6 files changed +370
CMakeLists.txt
+1
@@ -35,6 +35,7 @@ set(NETDATA_SOURCE_FILES
35 src/zfs_common.c
36 src/zfs_common.h
37 src/freebsd_kstat_zfs.c
38 + src/freebsd_ipfw.c
39 src/global_statistics.c
40 src/global_statistics.h
41 src/health.c
src/Makefile.am
+1
@@ -156,6 +156,7 @@ netdata_SOURCES += \
156 zfs_common.c \
157 zfs_common.h \
158 freebsd_kstat_zfs.c \
159 + freebsd_ipfw.c \
160 $(NULL)
161 else
162 if MACOS
src/freebsd_ipfw.c new
+353
@@ -0,0 +1,353 @@
1 +#include "common.h"
2 +
3 +#include <netinet/ip_fw.h>
4 +
5 +#define FREE_MEM_THRESHOLD 10000 // number of unused chunks that trigger memory freeing
6 +
7 +#define COMMON_IPFW_ERROR() error("DISABLED: ipfw.packets chart"); \
8 + error("DISABLED: ipfw.bytes chart"); \
9 + error("DISABLED: ipfw.dyn_active chart"); \
10 + error("DISABLED: ipfw.dyn_expired chart"); \
11 + error("DISABLED: ipfw.mem chart");
12 +
13 +// --------------------------------------------------------------------------------------------------------------------
14 +// ipfw
15 +
16 +int do_ipfw(int update_every, usec_t dt) {
17 + (void)dt;
18 + static int do_static = -1, do_dynamic = -1, do_mem = -1;
19 +
20 + if (unlikely(do_static == -1)) {
21 + do_static = config_get_boolean("plugin:freebsd:ipfw", "counters for static rules", 1);
22 + do_dynamic = config_get_boolean("plugin:freebsd:ipfw", "number of dynamic rules", 1);
23 + do_mem = config_get_boolean("plugin:freebsd:ipfw", "allocated memory", 1);
24 + }
25 +
26 + // variables for getting ipfw configuration
27 +
28 + int error;
29 + static int ipfw_socket = -1;
30 + static ipfw_cfg_lheader *cfg = NULL;
31 + ip_fw3_opheader *op3 = NULL;
32 + static socklen_t *optlen = NULL, cfg_size = 0;
33 +
34 + // variables for static rules handling
35 +
36 + ipfw_obj_ctlv *ctlv = NULL;
37 + ipfw_obj_tlv *rbase = NULL;
38 + int rcnt = 0;
39 +
40 + int n, seen;
41 + struct ip_fw_rule *rule;
42 + struct ip_fw_bcounter *cntr;
43 + int c = 0;
44 +
45 + char rule_num_str[12];
46 +
47 + // variables for dynamic rules handling
48 +
49 + caddr_t dynbase = NULL;
50 + size_t dynsz = 0;
51 + size_t readsz = sizeof(*cfg);;
52 + int ttype = 0;
53 + ipfw_obj_tlv *tlv;
54 + ipfw_dyn_rule *dyn_rule;
55 + uint16_t rulenum, prev_rulenum = IPFW_DEFAULT_RULE;
56 + unsigned srn, static_rules_num = 0;
57 + static size_t dyn_rules_num_size = 0;
58 +
59 + static struct dyn_rule_num {
60 + uint16_t rule_num;
61 + uint32_t active_rules;
62 + uint32_t expired_rules;
63 + } *dyn_rules_num = NULL;
64 +
65 + uint32_t *dyn_rules_counter;
66 +
67 + if (likely(do_static | do_dynamic | do_mem)) {
68 +
69 + // initialize the smallest ipfw_cfg_lheader possible
70 +
71 + if (unlikely((optlen == NULL) || (cfg == NULL))) {
72 + optlen = reallocz(optlen, sizeof(socklen_t));
73 + *optlen = cfg_size = 32;
74 + cfg = reallocz(cfg, *optlen);
75 + }
76 +
77 + // get socket descriptor and initialize ipfw_cfg_lheader structure
78 +
79 + if (unlikely(ipfw_socket == -1))
80 + ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
81 + if (unlikely(ipfw_socket == -1)) {
82 + error("FREEBSD: can't get socket for ipfw configuration");
83 + error("FREEBSD: run netdata as root to get access to ipfw data");
84 + COMMON_IPFW_ERROR();
85 + return 1;
86 + }
87 +
88 + bzero(cfg, 32);
89 + cfg->flags = IPFW_CFG_GET_STATIC | IPFW_CFG_GET_COUNTERS | IPFW_CFG_GET_STATES;
90 + op3 = &cfg->opheader;
91 + op3->opcode = IP_FW_XGET;
92 +
93 + // get ifpw configuration size than get configuration
94 +
95 + *optlen = cfg_size;
96 + error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen);
97 + if (error)
98 + if (errno != ENOMEM) {
99 + error("FREEBSD: ipfw socket reading error");
100 + COMMON_IPFW_ERROR();
101 + return 1;
102 + }
103 + if ((cfg->size > cfg_size) || ((cfg_size - cfg->size) > sizeof(struct dyn_rule_num) * FREE_MEM_THRESHOLD)) {
104 + *optlen = cfg_size = cfg->size;
105 + cfg = reallocz(cfg, *optlen);
106 + bzero(cfg, 32);
107 + cfg->flags = IPFW_CFG_GET_STATIC | IPFW_CFG_GET_COUNTERS | IPFW_CFG_GET_STATES;
108 + op3 = &cfg->opheader;
109 + op3->opcode = IP_FW_XGET;
110 + error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen);
111 + if (error) {
112 + error("FREEBSD: ipfw socket reading error");
113 + COMMON_IPFW_ERROR();
114 + return 1;
115 + }
116 + }
117 +
118 + // go through static rules configuration structures
119 +
120 + ctlv = (ipfw_obj_ctlv *) (cfg + 1);
121 +
122 + if (cfg->flags & IPFW_CFG_GET_STATIC) {
123 + /* We've requested static rules */
124 + if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
125 + readsz += ctlv->head.length;
126 + ctlv = (ipfw_obj_ctlv *) ((caddr_t) ctlv +
127 + ctlv->head.length);
128 + }
129 +
130 + if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
131 + rbase = (ipfw_obj_tlv *) (ctlv + 1);
132 + rcnt = ctlv->count;
133 + readsz += ctlv->head.length;
134 + ctlv = (ipfw_obj_ctlv *) ((caddr_t) ctlv + ctlv->head.length);
135 + }
136 + }
137 +
138 + if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != *optlen)) {
139 + /* We may have some dynamic states */
140 + dynsz = *optlen - readsz;
141 + /* Skip empty header */
142 + if (dynsz != sizeof(ipfw_obj_ctlv))
143 + dynbase = (caddr_t) ctlv;
144 + else
145 + dynsz = 0;
146 + }
147 +
148 + // --------------------------------------------------------------------
149 +
150 + if (likely(do_mem)) {
151 + static RRDSET *st_mem = NULL;
152 + static RRDDIM *rd_dyn_mem = NULL;
153 + static RRDDIM *rd_stat_mem = NULL;
154 +
155 + if (unlikely(!st_mem)) {
156 + st_mem = rrdset_create_localhost("ipfw",
157 + "mem",
158 + NULL,
159 + "memory allocated",
160 + NULL,
161 + "Memory allocated by rules",
162 + "bytes",
163 + 3005,
164 + update_every,
165 + RRDSET_TYPE_STACKED
166 + );
167 + rrdset_flag_set(st_mem, RRDSET_FLAG_DETAIL);
168 +
169 + rd_dyn_mem = rrddim_add(st_mem, "dynamic", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
170 + rd_stat_mem = rrddim_add(st_mem, "static", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
171 + } else
172 + rrdset_next(st_mem);
173 +
174 + rrddim_set_by_pointer(st_mem, rd_dyn_mem, dynsz);
175 + rrddim_set_by_pointer(st_mem, rd_stat_mem, *optlen - dynsz);
176 + rrdset_done(st_mem);
177 + }
178 +
179 + // --------------------------------------------------------------------
180 +
181 + static RRDSET *st_packets = NULL, *st_bytes = NULL;
182 + RRDDIM *rd_packets = NULL, *rd_bytes = NULL;
183 +
184 + if (likely(do_static || do_dynamic)) {
185 + if (likely(do_static)) {
186 + if (unlikely(!st_packets))
187 + st_packets = rrdset_create_localhost("ipfw",
188 + "packets",
189 + NULL,
190 + "static rules",
191 + NULL,
192 + "Packets",
193 + "packets/s",
194 + 3001,
195 + update_every,
196 + RRDSET_TYPE_STACKED
197 + );
198 + else
199 + rrdset_next(st_packets);
200 +
201 + if (unlikely(!st_bytes))
202 + st_bytes = rrdset_create_localhost("ipfw",
203 + "bytes",
204 + NULL,
205 + "static rules",
206 + NULL,
207 + "Bytes",
208 + "bytes/s",
209 + 3002,
210 + update_every,
211 + RRDSET_TYPE_STACKED
212 + );
213 + else
214 + rrdset_next(st_bytes);
215 + }
216 +
217 + for (n = seen = 0; n < rcnt; n++, rbase = (ipfw_obj_tlv *) ((caddr_t) rbase + rbase->length)) {
218 + cntr = (struct ip_fw_bcounter *) (rbase + 1);
219 + rule = (struct ip_fw_rule *) ((caddr_t) cntr + cntr->size);
220 + if (rule->rulenum != prev_rulenum)
221 + static_rules_num++;
222 + if (rule->rulenum > IPFW_DEFAULT_RULE)
223 + break;
224 +
225 + if (likely(do_static)) {
226 + sprintf(rule_num_str, "%d_%d", rule->rulenum, rule->id);
227 +
228 + rd_packets = rrddim_find(st_packets, rule_num_str);
229 + if (unlikely(!rd_packets))
230 + rd_packets = rrddim_add(st_packets, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
231 + rrddim_set_by_pointer(st_packets, rd_packets, cntr->pcnt);
232 +
233 + rd_bytes = rrddim_find(st_bytes, rule_num_str);
234 + if (unlikely(!rd_bytes))
235 + rd_bytes = rrddim_add(st_bytes, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
236 + rrddim_set_by_pointer(st_bytes, rd_bytes, cntr->bcnt);
237 + }
238 +
239 + c += rbase->length;
240 + seen++;
241 + }
242 +
243 + if (likely(do_static)) {
244 + rrdset_done(st_packets);
245 + rrdset_done(st_bytes);
246 + }
247 + }
248 +
249 + // --------------------------------------------------------------------
250 +
251 + // go through dynamic rules configuration structures
252 +
253 + if (likely(do_dynamic && (dynsz > 0))) {
254 + if ((dyn_rules_num_size < sizeof(struct dyn_rule_num) * static_rules_num) ||
255 + ((dyn_rules_num_size - sizeof(struct dyn_rule_num) * static_rules_num) >
256 + sizeof(struct dyn_rule_num) * FREE_MEM_THRESHOLD)) {
257 + dyn_rules_num_size = sizeof(struct dyn_rule_num) * static_rules_num;
258 + dyn_rules_num = reallocz(dyn_rules_num, dyn_rules_num_size);
259 + }
260 + bzero(dyn_rules_num, sizeof(struct dyn_rule_num) * static_rules_num);
261 + dyn_rules_num->rule_num = IPFW_DEFAULT_RULE;
262 +
263 + if (dynsz > 0 && ctlv->head.type == IPFW_TLV_DYNSTATE_LIST) {
264 + dynbase += sizeof(*ctlv);
265 + dynsz -= sizeof(*ctlv);
266 + ttype = IPFW_TLV_DYN_ENT;
267 + }
268 +
269 + while (dynsz > 0) {
270 + tlv = (ipfw_obj_tlv *) dynbase;
271 + if (tlv->type != ttype)
272 + break;
273 +
274 + dyn_rule = (ipfw_dyn_rule *) (tlv + 1);
275 + bcopy(&dyn_rule->rule, &rulenum, sizeof(rulenum));
276 +
277 + for (srn = 0; srn < (static_rules_num - 1); srn++) {
278 + if (dyn_rule->expire > 0)
279 + dyn_rules_counter = &dyn_rules_num[srn].active_rules;
280 + else
281 + dyn_rules_counter = &dyn_rules_num[srn].expired_rules;
282 + if (dyn_rules_num[srn].rule_num == rulenum) {
283 + (*dyn_rules_counter)++;
284 + break;
285 + }
286 + if (dyn_rules_num[srn].rule_num == IPFW_DEFAULT_RULE) {
287 + dyn_rules_num[srn].rule_num = rulenum;
288 + dyn_rules_num[srn + 1].rule_num = IPFW_DEFAULT_RULE;
289 + (*dyn_rules_counter)++;
290 + break;
291 + }
292 + }
293 +
294 + dynsz -= tlv->length;
295 + dynbase += tlv->length;
296 + }
297 +
298 + // --------------------------------------------------------------------
299 +
300 + static RRDSET *st_active = NULL, *st_expired = NULL;
301 + RRDDIM *rd_active = NULL, *rd_expired = NULL;
302 +
303 + if (unlikely(!st_active))
304 + st_active = rrdset_create_localhost("ipfw",
305 + "active",
306 + NULL,
307 + "dynamic_rules",
308 + NULL,
309 + "Active rules",
310 + "rules",
311 + 3003,
312 + update_every,
313 + RRDSET_TYPE_STACKED
314 + );
315 + else
316 + rrdset_next(st_active);
317 +
318 + if (unlikely(!st_expired))
319 + st_expired = rrdset_create_localhost("ipfw",
320 + "expired",
321 + NULL,
322 + "dynamic_rules",
323 + NULL,
324 + "Expired rules",
325 + "rules",
326 + 3004,
327 + update_every,
328 + RRDSET_TYPE_STACKED
329 + );
330 + else
331 + rrdset_next(st_expired);
332 +
333 + for (srn = 0; (srn < (static_rules_num - 1)) && (dyn_rules_num[srn].rule_num != IPFW_DEFAULT_RULE); srn++) {
334 + sprintf(rule_num_str, "%d", dyn_rules_num[srn].rule_num);
335 +
336 + rd_active = rrddim_find(st_active, rule_num_str);
337 + if (unlikely(!rd_active))
338 + rd_active = rrddim_add(st_active, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
339 + rrddim_set_by_pointer(st_active, rd_active, dyn_rules_num[srn].active_rules);
340 +
341 + rd_expired = rrddim_find(st_expired, rule_num_str);
342 + if (unlikely(!rd_expired))
343 + rd_expired = rrddim_add(st_expired, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
344 + rrddim_set_by_pointer(st_expired, rd_expired, dyn_rules_num[srn].expired_rules);
345 + }
346 +
347 + rrdset_done(st_active);
348 + rrdset_done(st_expired);
349 + }
350 + }
351 +
352 + return 0;
353 +}
src/plugin_freebsd.c
+3
@@ -56,6 +56,9 @@ static struct freebsd_module {
56 // ZFS metrics
57 { .name = "kstat.zfs.misc.arcstats", .dim = "arcstats", .enabled = 1, .func = do_kstat_zfs_misc_arcstats },
58
59 + // ipfw metrics
60 + { .name = "ipfw", .dim = "ipfw", .enabled = 1, .func = do_ipfw },
61 +
62 // the terminator of this array
63 { .name = NULL, .dim = NULL, .enabled = 0, .func = NULL }
64 };
src/plugin_freebsd.h
+1
@@ -42,6 +42,7 @@ extern int do_getifaddrs(int update_every, usec_t dt);
42 extern int do_getmntinfo(int update_every, usec_t dt);
43 extern int do_kern_devstat(int update_every, usec_t dt);
44 extern int do_kstat_zfs_misc_arcstats(int update_every, usec_t dt);
45 +extern int do_ipfw(int update_every, usec_t dt);
46
47 #define GETSYSCTL_MIB(name, mib) getsysctl_mib(name, mib, sizeof(mib)/sizeof(int))
48
web/dashboard_info.js
+11
@@ -61,6 +61,12 @@ netdataDashboard.menu = {
61 info: 'Performance metrics of the netfilter components.'
62 },
63
64 + 'ipfw': {
65 + title: 'Firewall (ipfw)',
66 + icon: '<i class="fa fa-shield" aria-hidden="true"></i>',
67 + info: 'Counters and memory usage for the ipfw rules.'
68 + },
69 +
70 'cpu': {
71 title: 'CPUs',
72 icon: '<i class="fa fa-bolt" aria-hidden="true"></i>',
@@ -377,6 +383,11 @@ netdataDashboard.submenu = {
383 info: 'DDoS protection performance metrics. <a href="https://github.com/firehol/firehol/wiki/Working-with-SYNPROXY" target="_blank">SYNPROXY</a> is a TCP SYN packets proxy. It is used to protect any TCP server (like a web server) from SYN floods and similar DDoS attacks. It is a netfilter module, in the Linux kernel (since version 3.12). It is optimized to handle millions of packets per second utilizing all CPUs available without any concurrency locking between the connections. It can be used for any kind of TCP traffic (even encrypted), since it does not interfere with the content itself.'
384 },
385
386 + 'ipfw.dynamic_rules': {
387 + title: 'dynamic rules',
388 + info: 'Number of dynamic rules, created by correspondent stateful firewall rules.'
389 + },
390 +
391 'system.softnet_stat': {
392 title: 'softnet',
393 info: function(os) {