master
c 362 lines 14.9 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "plugin_freebsd.h"
4
5 #include <netinet/ip_fw.h>
6
7 #define FREE_MEM_THRESHOLD 10000 // number of unused chunks that trigger memory freeing
8
9 #define COMMON_IPFW_ERROR() collector_error("DISABLED: ipfw.packets chart"); \
10 collector_error("DISABLED: ipfw.bytes chart"); \
11 collector_error("DISABLED: ipfw.dyn_active chart"); \
12 collector_error("DISABLED: ipfw.dyn_expired chart"); \
13 collector_error("DISABLED: ipfw.mem chart");
14
15 // --------------------------------------------------------------------------------------------------------------------
16 // ipfw
17
18 int do_ipfw(int update_every, usec_t dt) {
19 (void)dt;
20 #if __FreeBSD__ >= 11
21
22 static int do_static = -1, do_dynamic = -1, do_mem = -1;
23
24 if (unlikely(do_static == -1)) {
25 do_static = inicfg_get_boolean(&netdata_config, "plugin:freebsd:ipfw", "counters for static rules", 1);
26 do_dynamic = inicfg_get_boolean(&netdata_config, "plugin:freebsd:ipfw", "number of dynamic rules", 1);
27 do_mem = inicfg_get_boolean(&netdata_config, "plugin:freebsd:ipfw", "allocated memory", 1);
28 }
29
30 // variables for getting ipfw configuration
31
32 int error;
33 static int ipfw_socket = -1;
34 static ipfw_cfg_lheader *cfg = NULL;
35 ip_fw3_opheader *op3 = NULL;
36 static socklen_t *optlen = NULL, cfg_size = 0;
37
38 // variables for static rules handling
39
40 ipfw_obj_ctlv *ctlv = NULL;
41 ipfw_obj_tlv *rbase = NULL;
42 int rcnt = 0;
43
44 int n, seen;
45 struct ip_fw_rule *rule;
46 struct ip_fw_bcounter *cntr;
47 int c = 0;
48
49 char rule_num_str[12];
50
51 // variables for dynamic rules handling
52
53 caddr_t dynbase = NULL;
54 size_t dynsz = 0;
55 size_t readsz = sizeof(*cfg);;
56 int ttype = 0;
57 ipfw_obj_tlv *tlv;
58 ipfw_dyn_rule *dyn_rule;
59 uint16_t rulenum, prev_rulenum = IPFW_DEFAULT_RULE;
60 unsigned srn, static_rules_num = 0;
61 static size_t dyn_rules_num_size = 0;
62
63 static struct dyn_rule_num {
64 uint16_t rule_num;
65 uint32_t active_rules;
66 uint32_t expired_rules;
67 } *dyn_rules_num = NULL;
68
69 uint32_t *dyn_rules_counter;
70
71 if (likely(do_static | do_dynamic | do_mem)) {
72
73 // initialize the smallest ipfw_cfg_lheader possible
74
75 if (unlikely((optlen == NULL) || (cfg == NULL))) {
76 optlen = reallocz(optlen, sizeof(socklen_t));
77 *optlen = cfg_size = 32;
78 cfg = reallocz(cfg, *optlen);
79 }
80
81 // get socket descriptor and initialize ipfw_cfg_lheader structure
82
83 if (unlikely(ipfw_socket == -1))
84 ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
85 if (unlikely(ipfw_socket == -1)) {
86 collector_error("FREEBSD: can't get socket for ipfw configuration");
87 collector_error("FREEBSD: run netdata as root to get access to ipfw data");
88 COMMON_IPFW_ERROR();
89 return 1;
90 }
91
92 bzero(cfg, 32);
93 cfg->flags = IPFW_CFG_GET_STATIC | IPFW_CFG_GET_COUNTERS | IPFW_CFG_GET_STATES;
94 op3 = &cfg->opheader;
95 op3->opcode = IP_FW_XGET;
96
97 // get ifpw configuration size than get configuration
98
99 *optlen = cfg_size;
100 error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen);
101 if (error)
102 if (errno != ENOMEM) {
103 collector_error("FREEBSD: ipfw socket reading error");
104 COMMON_IPFW_ERROR();
105 return 1;
106 }
107 if ((cfg->size > cfg_size) || ((cfg_size - cfg->size) > sizeof(struct dyn_rule_num) * FREE_MEM_THRESHOLD)) {
108 *optlen = cfg_size = cfg->size;
109 cfg = reallocz(cfg, *optlen);
110 bzero(cfg, 32);
111 cfg->flags = IPFW_CFG_GET_STATIC | IPFW_CFG_GET_COUNTERS | IPFW_CFG_GET_STATES;
112 op3 = &cfg->opheader;
113 op3->opcode = IP_FW_XGET;
114 error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen);
115 if (error) {
116 collector_error("FREEBSD: ipfw socket reading error");
117 COMMON_IPFW_ERROR();
118 return 1;
119 }
120 }
121
122 // go through static rules configuration structures
123
124 ctlv = (ipfw_obj_ctlv *) (cfg + 1);
125
126 if (cfg->flags & IPFW_CFG_GET_STATIC) {
127 /* We've requested static rules */
128 if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
129 readsz += ctlv->head.length;
130 ctlv = (ipfw_obj_ctlv *) ((caddr_t) ctlv +
131 ctlv->head.length);
132 }
133
134 if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
135 rbase = (ipfw_obj_tlv *) (ctlv + 1);
136 rcnt = ctlv->count;
137 readsz += ctlv->head.length;
138 ctlv = (ipfw_obj_ctlv *) ((caddr_t) ctlv + ctlv->head.length);
139 }
140 }
141
142 if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != *optlen)) {
143 /* We may have some dynamic states */
144 dynsz = *optlen - readsz;
145 /* Skip empty header */
146 if (dynsz != sizeof(ipfw_obj_ctlv))
147 dynbase = (caddr_t) ctlv;
148 else
149 dynsz = 0;
150 }
151
152 if (likely(do_mem)) {
153 static RRDSET *st_mem = NULL;
154 static RRDDIM *rd_dyn_mem = NULL;
155 static RRDDIM *rd_stat_mem = NULL;
156
157 if (unlikely(!st_mem)) {
158 st_mem = rrdset_create_localhost("ipfw",
159 "mem",
160 NULL,
161 "memory allocated",
162 NULL,
163 "Memory allocated by rules",
164 "bytes",
165 "freebsd.plugin",
166 "ipfw",
167 NETDATA_CHART_PRIO_IPFW_MEM,
168 update_every,
169 RRDSET_TYPE_STACKED
170 );
171
172 rd_dyn_mem = rrddim_add(st_mem, "dynamic", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
173 rd_stat_mem = rrddim_add(st_mem, "static", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
174 }
175
176 rrddim_set_by_pointer(st_mem, rd_dyn_mem, dynsz);
177 rrddim_set_by_pointer(st_mem, rd_stat_mem, *optlen - dynsz);
178 rrdset_done(st_mem);
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 "freebsd.plugin",
195 "ipfw",
196 NETDATA_CHART_PRIO_IPFW_PACKETS,
197 update_every,
198 RRDSET_TYPE_STACKED
199 );
200 }
201
202 if (unlikely(!st_bytes)) {
203 st_bytes = rrdset_create_localhost("ipfw",
204 "bytes",
205 NULL,
206 "static rules",
207 NULL,
208 "Bytes",
209 "bytes/s",
210 "freebsd.plugin",
211 "ipfw",
212 NETDATA_CHART_PRIO_IPFW_BYTES,
213 update_every,
214 RRDSET_TYPE_STACKED
215 );
216 }
217 }
218
219 for (n = seen = 0; n < rcnt; n++, rbase = (ipfw_obj_tlv *) ((caddr_t) rbase + rbase->length)) {
220 cntr = (struct ip_fw_bcounter *) (rbase + 1);
221 rule = (struct ip_fw_rule *) ((caddr_t) cntr + cntr->size);
222 if (rule->rulenum != prev_rulenum)
223 static_rules_num++;
224 if (rule->rulenum > IPFW_DEFAULT_RULE)
225 break;
226
227 if (likely(do_static)) {
228 sprintf(rule_num_str, "%"PRIu32"_%"PRIu32"", (uint32_t)rule->rulenum, (uint32_t)rule->id);
229
230 rd_packets = rrddim_find_active(st_packets, rule_num_str);
231 if (unlikely(!rd_packets))
232 rd_packets = rrddim_add(st_packets, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
233 rrddim_set_by_pointer(st_packets, rd_packets, cntr->pcnt);
234
235 rd_bytes = rrddim_find_active(st_bytes, rule_num_str);
236 if (unlikely(!rd_bytes))
237 rd_bytes = rrddim_add(st_bytes, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
238 rrddim_set_by_pointer(st_bytes, rd_bytes, cntr->bcnt);
239 }
240
241 c += rbase->length;
242 seen++;
243 }
244
245 if (likely(do_static)) {
246 rrdset_done(st_packets);
247 rrdset_done(st_bytes);
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 #if __FreeBSD__ >= 15
276 rulenum = (uint16_t)dyn_rule->rulenum;
277 #else
278 bcopy(&dyn_rule->rule, &rulenum, sizeof(rulenum));
279 #endif
280
281 for (srn = 0; srn < (static_rules_num - 1); srn++) {
282 if (dyn_rule->expire > 0)
283 dyn_rules_counter = &dyn_rules_num[srn].active_rules;
284 else
285 dyn_rules_counter = &dyn_rules_num[srn].expired_rules;
286 if (dyn_rules_num[srn].rule_num == rulenum) {
287 (*dyn_rules_counter)++;
288 break;
289 }
290 if (dyn_rules_num[srn].rule_num == IPFW_DEFAULT_RULE) {
291 dyn_rules_num[srn].rule_num = rulenum;
292 dyn_rules_num[srn + 1].rule_num = IPFW_DEFAULT_RULE;
293 (*dyn_rules_counter)++;
294 break;
295 }
296 }
297
298 dynsz -= tlv->length;
299 dynbase += tlv->length;
300 }
301
302 static RRDSET *st_active = NULL, *st_expired = NULL;
303 RRDDIM *rd_active = NULL, *rd_expired = NULL;
304
305 if (unlikely(!st_active)) {
306 st_active = rrdset_create_localhost("ipfw",
307 "active",
308 NULL,
309 "dynamic_rules",
310 NULL,
311 "Active rules",
312 "rules",
313 "freebsd.plugin",
314 "ipfw",
315 NETDATA_CHART_PRIO_IPFW_ACTIVE,
316 update_every,
317 RRDSET_TYPE_STACKED
318 );
319 }
320
321 if (unlikely(!st_expired)) {
322 st_expired = rrdset_create_localhost("ipfw",
323 "expired",
324 NULL,
325 "dynamic_rules",
326 NULL,
327 "Expired rules",
328 "rules",
329 "freebsd.plugin",
330 "ipfw",
331 NETDATA_CHART_PRIO_IPFW_EXPIRED,
332 update_every,
333 RRDSET_TYPE_STACKED
334 );
335 }
336
337 for (srn = 0; (srn < (static_rules_num - 1)) && (dyn_rules_num[srn].rule_num != IPFW_DEFAULT_RULE); srn++) {
338 sprintf(rule_num_str, "%d", dyn_rules_num[srn].rule_num);
339
340 rd_active = rrddim_find_active(st_active, rule_num_str);
341 if (unlikely(!rd_active))
342 rd_active = rrddim_add(st_active, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
343 rrddim_set_by_pointer(st_active, rd_active, dyn_rules_num[srn].active_rules);
344
345 rd_expired = rrddim_find_active(st_expired, rule_num_str);
346 if (unlikely(!rd_expired))
347 rd_expired = rrddim_add(st_expired, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
348 rrddim_set_by_pointer(st_expired, rd_expired, dyn_rules_num[srn].expired_rules);
349 }
350
351 rrdset_done(st_active);
352 rrdset_done(st_expired);
353 }
354 }
355
356 return 0;
357 #else
358 collector_error("FREEBSD: ipfw charts supported for FreeBSD 11.0 and newer releases only");
359 COMMON_IPFW_ERROR();
360 return 1;
361 #endif
362 }