mirror of
https://github.com/netdata/netdata.git
synced 2025-04-28 22:52:30 +00:00
Add ipfw charts to FreeBSD plugin
This commit is contained in:
parent
39f1e92305
commit
5b78cf5618
6 changed files with 370 additions and 0 deletions
|
@ -35,6 +35,7 @@ set(NETDATA_SOURCE_FILES
|
|||
src/zfs_common.c
|
||||
src/zfs_common.h
|
||||
src/freebsd_kstat_zfs.c
|
||||
src/freebsd_ipfw.c
|
||||
src/global_statistics.c
|
||||
src/global_statistics.h
|
||||
src/health.c
|
||||
|
|
|
@ -156,6 +156,7 @@ netdata_SOURCES += \
|
|||
zfs_common.c \
|
||||
zfs_common.h \
|
||||
freebsd_kstat_zfs.c \
|
||||
freebsd_ipfw.c \
|
||||
$(NULL)
|
||||
else
|
||||
if MACOS
|
||||
|
|
353
src/freebsd_ipfw.c
Normal file
353
src/freebsd_ipfw.c
Normal file
|
@ -0,0 +1,353 @@
|
|||
#include "common.h"
|
||||
|
||||
#include <netinet/ip_fw.h>
|
||||
|
||||
#define FREE_MEM_THRESHOLD 10000 // number of unused chunks that trigger memory freeing
|
||||
|
||||
#define COMMON_IPFW_ERROR() error("DISABLED: ipfw.packets chart"); \
|
||||
error("DISABLED: ipfw.bytes chart"); \
|
||||
error("DISABLED: ipfw.dyn_active chart"); \
|
||||
error("DISABLED: ipfw.dyn_expired chart"); \
|
||||
error("DISABLED: ipfw.mem chart");
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// ipfw
|
||||
|
||||
int do_ipfw(int update_every, usec_t dt) {
|
||||
(void)dt;
|
||||
static int do_static = -1, do_dynamic = -1, do_mem = -1;
|
||||
|
||||
if (unlikely(do_static == -1)) {
|
||||
do_static = config_get_boolean("plugin:freebsd:ipfw", "counters for static rules", 1);
|
||||
do_dynamic = config_get_boolean("plugin:freebsd:ipfw", "number of dynamic rules", 1);
|
||||
do_mem = config_get_boolean("plugin:freebsd:ipfw", "allocated memory", 1);
|
||||
}
|
||||
|
||||
// variables for getting ipfw configuration
|
||||
|
||||
int error;
|
||||
static int ipfw_socket = -1;
|
||||
static ipfw_cfg_lheader *cfg = NULL;
|
||||
ip_fw3_opheader *op3 = NULL;
|
||||
static socklen_t *optlen = NULL, cfg_size = 0;
|
||||
|
||||
// variables for static rules handling
|
||||
|
||||
ipfw_obj_ctlv *ctlv = NULL;
|
||||
ipfw_obj_tlv *rbase = NULL;
|
||||
int rcnt = 0;
|
||||
|
||||
int n, seen;
|
||||
struct ip_fw_rule *rule;
|
||||
struct ip_fw_bcounter *cntr;
|
||||
int c = 0;
|
||||
|
||||
char rule_num_str[12];
|
||||
|
||||
// variables for dynamic rules handling
|
||||
|
||||
caddr_t dynbase = NULL;
|
||||
size_t dynsz = 0;
|
||||
size_t readsz = sizeof(*cfg);;
|
||||
int ttype = 0;
|
||||
ipfw_obj_tlv *tlv;
|
||||
ipfw_dyn_rule *dyn_rule;
|
||||
uint16_t rulenum, prev_rulenum = IPFW_DEFAULT_RULE;
|
||||
unsigned srn, static_rules_num = 0;
|
||||
static size_t dyn_rules_num_size = 0;
|
||||
|
||||
static struct dyn_rule_num {
|
||||
uint16_t rule_num;
|
||||
uint32_t active_rules;
|
||||
uint32_t expired_rules;
|
||||
} *dyn_rules_num = NULL;
|
||||
|
||||
uint32_t *dyn_rules_counter;
|
||||
|
||||
if (likely(do_static | do_dynamic | do_mem)) {
|
||||
|
||||
// initialize the smallest ipfw_cfg_lheader possible
|
||||
|
||||
if (unlikely((optlen == NULL) || (cfg == NULL))) {
|
||||
optlen = reallocz(optlen, sizeof(socklen_t));
|
||||
*optlen = cfg_size = 32;
|
||||
cfg = reallocz(cfg, *optlen);
|
||||
}
|
||||
|
||||
// get socket descriptor and initialize ipfw_cfg_lheader structure
|
||||
|
||||
if (unlikely(ipfw_socket == -1))
|
||||
ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
||||
if (unlikely(ipfw_socket == -1)) {
|
||||
error("FREEBSD: can't get socket for ipfw configuration");
|
||||
error("FREEBSD: run netdata as root to get access to ipfw data");
|
||||
COMMON_IPFW_ERROR();
|
||||
return 1;
|
||||
}
|
||||
|
||||
bzero(cfg, 32);
|
||||
cfg->flags = IPFW_CFG_GET_STATIC | IPFW_CFG_GET_COUNTERS | IPFW_CFG_GET_STATES;
|
||||
op3 = &cfg->opheader;
|
||||
op3->opcode = IP_FW_XGET;
|
||||
|
||||
// get ifpw configuration size than get configuration
|
||||
|
||||
*optlen = cfg_size;
|
||||
error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen);
|
||||
if (error)
|
||||
if (errno != ENOMEM) {
|
||||
error("FREEBSD: ipfw socket reading error");
|
||||
COMMON_IPFW_ERROR();
|
||||
return 1;
|
||||
}
|
||||
if ((cfg->size > cfg_size) || ((cfg_size - cfg->size) > sizeof(struct dyn_rule_num) * FREE_MEM_THRESHOLD)) {
|
||||
*optlen = cfg_size = cfg->size;
|
||||
cfg = reallocz(cfg, *optlen);
|
||||
bzero(cfg, 32);
|
||||
cfg->flags = IPFW_CFG_GET_STATIC | IPFW_CFG_GET_COUNTERS | IPFW_CFG_GET_STATES;
|
||||
op3 = &cfg->opheader;
|
||||
op3->opcode = IP_FW_XGET;
|
||||
error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen);
|
||||
if (error) {
|
||||
error("FREEBSD: ipfw socket reading error");
|
||||
COMMON_IPFW_ERROR();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// go through static rules configuration structures
|
||||
|
||||
ctlv = (ipfw_obj_ctlv *) (cfg + 1);
|
||||
|
||||
if (cfg->flags & IPFW_CFG_GET_STATIC) {
|
||||
/* We've requested static rules */
|
||||
if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
|
||||
readsz += ctlv->head.length;
|
||||
ctlv = (ipfw_obj_ctlv *) ((caddr_t) ctlv +
|
||||
ctlv->head.length);
|
||||
}
|
||||
|
||||
if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
|
||||
rbase = (ipfw_obj_tlv *) (ctlv + 1);
|
||||
rcnt = ctlv->count;
|
||||
readsz += ctlv->head.length;
|
||||
ctlv = (ipfw_obj_ctlv *) ((caddr_t) ctlv + ctlv->head.length);
|
||||
}
|
||||
}
|
||||
|
||||
if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != *optlen)) {
|
||||
/* We may have some dynamic states */
|
||||
dynsz = *optlen - readsz;
|
||||
/* Skip empty header */
|
||||
if (dynsz != sizeof(ipfw_obj_ctlv))
|
||||
dynbase = (caddr_t) ctlv;
|
||||
else
|
||||
dynsz = 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
if (likely(do_mem)) {
|
||||
static RRDSET *st_mem = NULL;
|
||||
static RRDDIM *rd_dyn_mem = NULL;
|
||||
static RRDDIM *rd_stat_mem = NULL;
|
||||
|
||||
if (unlikely(!st_mem)) {
|
||||
st_mem = rrdset_create_localhost("ipfw",
|
||||
"mem",
|
||||
NULL,
|
||||
"memory allocated",
|
||||
NULL,
|
||||
"Memory allocated by rules",
|
||||
"bytes",
|
||||
3005,
|
||||
update_every,
|
||||
RRDSET_TYPE_STACKED
|
||||
);
|
||||
rrdset_flag_set(st_mem, RRDSET_FLAG_DETAIL);
|
||||
|
||||
rd_dyn_mem = rrddim_add(st_mem, "dynamic", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
|
||||
rd_stat_mem = rrddim_add(st_mem, "static", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
|
||||
} else
|
||||
rrdset_next(st_mem);
|
||||
|
||||
rrddim_set_by_pointer(st_mem, rd_dyn_mem, dynsz);
|
||||
rrddim_set_by_pointer(st_mem, rd_stat_mem, *optlen - dynsz);
|
||||
rrdset_done(st_mem);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
static RRDSET *st_packets = NULL, *st_bytes = NULL;
|
||||
RRDDIM *rd_packets = NULL, *rd_bytes = NULL;
|
||||
|
||||
if (likely(do_static || do_dynamic)) {
|
||||
if (likely(do_static)) {
|
||||
if (unlikely(!st_packets))
|
||||
st_packets = rrdset_create_localhost("ipfw",
|
||||
"packets",
|
||||
NULL,
|
||||
"static rules",
|
||||
NULL,
|
||||
"Packets",
|
||||
"packets/s",
|
||||
3001,
|
||||
update_every,
|
||||
RRDSET_TYPE_STACKED
|
||||
);
|
||||
else
|
||||
rrdset_next(st_packets);
|
||||
|
||||
if (unlikely(!st_bytes))
|
||||
st_bytes = rrdset_create_localhost("ipfw",
|
||||
"bytes",
|
||||
NULL,
|
||||
"static rules",
|
||||
NULL,
|
||||
"Bytes",
|
||||
"bytes/s",
|
||||
3002,
|
||||
update_every,
|
||||
RRDSET_TYPE_STACKED
|
||||
);
|
||||
else
|
||||
rrdset_next(st_bytes);
|
||||
}
|
||||
|
||||
for (n = seen = 0; n < rcnt; n++, rbase = (ipfw_obj_tlv *) ((caddr_t) rbase + rbase->length)) {
|
||||
cntr = (struct ip_fw_bcounter *) (rbase + 1);
|
||||
rule = (struct ip_fw_rule *) ((caddr_t) cntr + cntr->size);
|
||||
if (rule->rulenum != prev_rulenum)
|
||||
static_rules_num++;
|
||||
if (rule->rulenum > IPFW_DEFAULT_RULE)
|
||||
break;
|
||||
|
||||
if (likely(do_static)) {
|
||||
sprintf(rule_num_str, "%d_%d", rule->rulenum, rule->id);
|
||||
|
||||
rd_packets = rrddim_find(st_packets, rule_num_str);
|
||||
if (unlikely(!rd_packets))
|
||||
rd_packets = rrddim_add(st_packets, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
rrddim_set_by_pointer(st_packets, rd_packets, cntr->pcnt);
|
||||
|
||||
rd_bytes = rrddim_find(st_bytes, rule_num_str);
|
||||
if (unlikely(!rd_bytes))
|
||||
rd_bytes = rrddim_add(st_bytes, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
|
||||
rrddim_set_by_pointer(st_bytes, rd_bytes, cntr->bcnt);
|
||||
}
|
||||
|
||||
c += rbase->length;
|
||||
seen++;
|
||||
}
|
||||
|
||||
if (likely(do_static)) {
|
||||
rrdset_done(st_packets);
|
||||
rrdset_done(st_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// go through dynamic rules configuration structures
|
||||
|
||||
if (likely(do_dynamic && (dynsz > 0))) {
|
||||
if ((dyn_rules_num_size < sizeof(struct dyn_rule_num) * static_rules_num) ||
|
||||
((dyn_rules_num_size - sizeof(struct dyn_rule_num) * static_rules_num) >
|
||||
sizeof(struct dyn_rule_num) * FREE_MEM_THRESHOLD)) {
|
||||
dyn_rules_num_size = sizeof(struct dyn_rule_num) * static_rules_num;
|
||||
dyn_rules_num = reallocz(dyn_rules_num, dyn_rules_num_size);
|
||||
}
|
||||
bzero(dyn_rules_num, sizeof(struct dyn_rule_num) * static_rules_num);
|
||||
dyn_rules_num->rule_num = IPFW_DEFAULT_RULE;
|
||||
|
||||
if (dynsz > 0 && ctlv->head.type == IPFW_TLV_DYNSTATE_LIST) {
|
||||
dynbase += sizeof(*ctlv);
|
||||
dynsz -= sizeof(*ctlv);
|
||||
ttype = IPFW_TLV_DYN_ENT;
|
||||
}
|
||||
|
||||
while (dynsz > 0) {
|
||||
tlv = (ipfw_obj_tlv *) dynbase;
|
||||
if (tlv->type != ttype)
|
||||
break;
|
||||
|
||||
dyn_rule = (ipfw_dyn_rule *) (tlv + 1);
|
||||
bcopy(&dyn_rule->rule, &rulenum, sizeof(rulenum));
|
||||
|
||||
for (srn = 0; srn < (static_rules_num - 1); srn++) {
|
||||
if (dyn_rule->expire > 0)
|
||||
dyn_rules_counter = &dyn_rules_num[srn].active_rules;
|
||||
else
|
||||
dyn_rules_counter = &dyn_rules_num[srn].expired_rules;
|
||||
if (dyn_rules_num[srn].rule_num == rulenum) {
|
||||
(*dyn_rules_counter)++;
|
||||
break;
|
||||
}
|
||||
if (dyn_rules_num[srn].rule_num == IPFW_DEFAULT_RULE) {
|
||||
dyn_rules_num[srn].rule_num = rulenum;
|
||||
dyn_rules_num[srn + 1].rule_num = IPFW_DEFAULT_RULE;
|
||||
(*dyn_rules_counter)++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dynsz -= tlv->length;
|
||||
dynbase += tlv->length;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
static RRDSET *st_active = NULL, *st_expired = NULL;
|
||||
RRDDIM *rd_active = NULL, *rd_expired = NULL;
|
||||
|
||||
if (unlikely(!st_active))
|
||||
st_active = rrdset_create_localhost("ipfw",
|
||||
"active",
|
||||
NULL,
|
||||
"dynamic_rules",
|
||||
NULL,
|
||||
"Active rules",
|
||||
"rules",
|
||||
3003,
|
||||
update_every,
|
||||
RRDSET_TYPE_STACKED
|
||||
);
|
||||
else
|
||||
rrdset_next(st_active);
|
||||
|
||||
if (unlikely(!st_expired))
|
||||
st_expired = rrdset_create_localhost("ipfw",
|
||||
"expired",
|
||||
NULL,
|
||||
"dynamic_rules",
|
||||
NULL,
|
||||
"Expired rules",
|
||||
"rules",
|
||||
3004,
|
||||
update_every,
|
||||
RRDSET_TYPE_STACKED
|
||||
);
|
||||
else
|
||||
rrdset_next(st_expired);
|
||||
|
||||
for (srn = 0; (srn < (static_rules_num - 1)) && (dyn_rules_num[srn].rule_num != IPFW_DEFAULT_RULE); srn++) {
|
||||
sprintf(rule_num_str, "%d", dyn_rules_num[srn].rule_num);
|
||||
|
||||
rd_active = rrddim_find(st_active, rule_num_str);
|
||||
if (unlikely(!rd_active))
|
||||
rd_active = rrddim_add(st_active, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
|
||||
rrddim_set_by_pointer(st_active, rd_active, dyn_rules_num[srn].active_rules);
|
||||
|
||||
rd_expired = rrddim_find(st_expired, rule_num_str);
|
||||
if (unlikely(!rd_expired))
|
||||
rd_expired = rrddim_add(st_expired, rule_num_str, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
|
||||
rrddim_set_by_pointer(st_expired, rd_expired, dyn_rules_num[srn].expired_rules);
|
||||
}
|
||||
|
||||
rrdset_done(st_active);
|
||||
rrdset_done(st_expired);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -56,6 +56,9 @@ static struct freebsd_module {
|
|||
// ZFS metrics
|
||||
{ .name = "kstat.zfs.misc.arcstats", .dim = "arcstats", .enabled = 1, .func = do_kstat_zfs_misc_arcstats },
|
||||
|
||||
// ipfw metrics
|
||||
{ .name = "ipfw", .dim = "ipfw", .enabled = 1, .func = do_ipfw },
|
||||
|
||||
// the terminator of this array
|
||||
{ .name = NULL, .dim = NULL, .enabled = 0, .func = NULL }
|
||||
};
|
||||
|
|
|
@ -42,6 +42,7 @@ extern int do_getifaddrs(int update_every, usec_t dt);
|
|||
extern int do_getmntinfo(int update_every, usec_t dt);
|
||||
extern int do_kern_devstat(int update_every, usec_t dt);
|
||||
extern int do_kstat_zfs_misc_arcstats(int update_every, usec_t dt);
|
||||
extern int do_ipfw(int update_every, usec_t dt);
|
||||
|
||||
#define GETSYSCTL_MIB(name, mib) getsysctl_mib(name, mib, sizeof(mib)/sizeof(int))
|
||||
|
||||
|
|
|
@ -61,6 +61,12 @@ netdataDashboard.menu = {
|
|||
info: 'Performance metrics of the netfilter components.'
|
||||
},
|
||||
|
||||
'ipfw': {
|
||||
title: 'Firewall (ipfw)',
|
||||
icon: '<i class="fa fa-shield" aria-hidden="true"></i>',
|
||||
info: 'Counters and memory usage for the ipfw rules.'
|
||||
},
|
||||
|
||||
'cpu': {
|
||||
title: 'CPUs',
|
||||
icon: '<i class="fa fa-bolt" aria-hidden="true"></i>',
|
||||
|
@ -377,6 +383,11 @@ netdataDashboard.submenu = {
|
|||
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.'
|
||||
},
|
||||
|
||||
'ipfw.dynamic_rules': {
|
||||
title: 'dynamic rules',
|
||||
info: 'Number of dynamic rules, created by correspondent stateful firewall rules.'
|
||||
},
|
||||
|
||||
'system.softnet_stat': {
|
||||
title: 'softnet',
|
||||
info: function(os) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue