0
0
Fork 0
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:
Vladimir Kobal 2017-06-12 00:57:08 +03:00
parent 39f1e92305
commit 5b78cf5618
6 changed files with 370 additions and 0 deletions

View file

@ -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

View file

@ -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
View 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;
}

View file

@ -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 }
};

View file

@ -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))

View file

@ -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) {