plugin_nfacct.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "../../libnetdata/libnetdata.h"
  3. #define PLUGIN_NFACCT_NAME "nfacct.plugin"
  4. #define NETDATA_CHART_PRIO_NETFILTER_NEW 8701
  5. #define NETDATA_CHART_PRIO_NETFILTER_CHANGES 8702
  6. #define NETDATA_CHART_PRIO_NETFILTER_EXPECT 8703
  7. #define NETDATA_CHART_PRIO_NETFILTER_ERRORS 8705
  8. #define NETDATA_CHART_PRIO_NETFILTER_SEARCH 8710
  9. #define NETDATA_CHART_PRIO_NETFILTER_PACKETS 8906
  10. #define NETDATA_CHART_PRIO_NETFILTER_BYTES 8907
  11. #ifdef HAVE_LIBMNL
  12. #include <libmnl/libmnl.h>
  13. static inline size_t mnl_buffer_size() {
  14. long s = MNL_SOCKET_BUFFER_SIZE;
  15. if(s <= 0) return 8192;
  16. return (size_t)s;
  17. }
  18. // callback required by fatal()
  19. void netdata_cleanup_and_exit(int ret) {
  20. exit(ret);
  21. }
  22. void send_statistics( const char *action, const char *action_result, const char *action_data) {
  23. (void) action;
  24. (void) action_result;
  25. (void) action_data;
  26. return;
  27. }
  28. // callbacks required by popen()
  29. void signals_block(void) {};
  30. void signals_unblock(void) {};
  31. void signals_reset(void) {};
  32. // callback required by eval()
  33. int health_variable_lookup(const char *variable, uint32_t hash, struct rrdcalc *rc, calculated_number *result) {
  34. (void)variable;
  35. (void)hash;
  36. (void)rc;
  37. (void)result;
  38. return 0;
  39. };
  40. // required by get_system_cpus()
  41. char *netdata_configured_host_prefix = "";
  42. // Variables
  43. static int debug = 0;
  44. static int netdata_update_every = 1;
  45. // ----------------------------------------------------------------------------
  46. // DO_NFSTAT - collect netfilter connection tracker statistics via netlink
  47. // example: https://github.com/formorer/pkg-conntrack-tools/blob/master/src/conntrack.c
  48. #ifdef HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H
  49. #define DO_NFSTAT 1
  50. #define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
  51. #define RRD_TYPE_NET_STAT_CONNTRACK "netlink"
  52. #include <linux/netfilter/nfnetlink_conntrack.h>
  53. static struct {
  54. int update_every;
  55. char *buf;
  56. size_t buf_size;
  57. struct mnl_socket *mnl;
  58. struct nlmsghdr *nlh;
  59. struct nfgenmsg *nfh;
  60. unsigned int seq;
  61. uint32_t portid;
  62. struct nlattr *tb[CTA_STATS_MAX+1];
  63. const char *attr2name[CTA_STATS_MAX+1];
  64. kernel_uint_t metrics[CTA_STATS_MAX+1];
  65. struct nlattr *tb_exp[CTA_STATS_EXP_MAX+1];
  66. const char *attr2name_exp[CTA_STATS_EXP_MAX+1];
  67. kernel_uint_t metrics_exp[CTA_STATS_EXP_MAX+1];
  68. } nfstat_root = {
  69. .update_every = 1,
  70. .buf = NULL,
  71. .buf_size = 0,
  72. .mnl = NULL,
  73. .nlh = NULL,
  74. .nfh = NULL,
  75. .seq = 0,
  76. .portid = 0,
  77. .tb = {},
  78. .attr2name = {
  79. [CTA_STATS_SEARCHED] = "searched",
  80. [CTA_STATS_FOUND] = "found",
  81. [CTA_STATS_NEW] = "new",
  82. [CTA_STATS_INVALID] = "invalid",
  83. [CTA_STATS_IGNORE] = "ignore",
  84. [CTA_STATS_DELETE] = "delete",
  85. [CTA_STATS_DELETE_LIST] = "delete_list",
  86. [CTA_STATS_INSERT] = "insert",
  87. [CTA_STATS_INSERT_FAILED] = "insert_failed",
  88. [CTA_STATS_DROP] = "drop",
  89. [CTA_STATS_EARLY_DROP] = "early_drop",
  90. [CTA_STATS_ERROR] = "icmp_error",
  91. [CTA_STATS_SEARCH_RESTART] = "search_restart",
  92. },
  93. .metrics = {},
  94. .tb_exp = {},
  95. .attr2name_exp = {
  96. [CTA_STATS_EXP_NEW] = "new",
  97. [CTA_STATS_EXP_CREATE] = "created",
  98. [CTA_STATS_EXP_DELETE] = "deleted",
  99. },
  100. .metrics_exp = {}
  101. };
  102. static int nfstat_init(int update_every) {
  103. nfstat_root.update_every = update_every;
  104. nfstat_root.buf_size = mnl_buffer_size();
  105. nfstat_root.buf = mallocz(nfstat_root.buf_size);
  106. nfstat_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
  107. if(!nfstat_root.mnl) {
  108. error("NFSTAT: mnl_socket_open() failed");
  109. return 1;
  110. }
  111. nfstat_root.seq = (unsigned int)now_realtime_sec() - 1;
  112. if(mnl_socket_bind(nfstat_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
  113. error("NFSTAT: mnl_socket_bind() failed");
  114. return 1;
  115. }
  116. nfstat_root.portid = mnl_socket_get_portid(nfstat_root.mnl);
  117. return 0;
  118. }
  119. static struct nlmsghdr * nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type, uint8_t family, uint32_t seq) {
  120. struct nlmsghdr *nlh;
  121. struct nfgenmsg *nfh;
  122. nlh = mnl_nlmsg_put_header(buf);
  123. nlh->nlmsg_type = (subsys << 8) | type;
  124. nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
  125. nlh->nlmsg_seq = seq;
  126. nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
  127. nfh->nfgen_family = family;
  128. nfh->version = NFNETLINK_V0;
  129. nfh->res_id = 0;
  130. return nlh;
  131. }
  132. static int nfct_stats_attr_cb(const struct nlattr *attr, void *data) {
  133. const struct nlattr **tb = data;
  134. int type = mnl_attr_get_type(attr);
  135. if (mnl_attr_type_valid(attr, CTA_STATS_MAX) < 0)
  136. return MNL_CB_OK;
  137. if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
  138. error("NFSTAT: mnl_attr_validate() failed");
  139. return MNL_CB_ERROR;
  140. }
  141. tb[type] = attr;
  142. return MNL_CB_OK;
  143. }
  144. static int nfstat_callback(const struct nlmsghdr *nlh, void *data) {
  145. (void)data;
  146. struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
  147. mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_attr_cb, nfstat_root.tb);
  148. // printf("cpu=%-4u\t", ntohs(nfg->res_id));
  149. int i;
  150. // add the metrics of this CPU into the metrics
  151. for (i = 0; i < CTA_STATS_MAX+1; i++) {
  152. if (nfstat_root.tb[i]) {
  153. // printf("%s=%u ", nfstat_root.attr2name[i], ntohl(mnl_attr_get_u32(nfstat_root.tb[i])));
  154. nfstat_root.metrics[i] += ntohl(mnl_attr_get_u32(nfstat_root.tb[i]));
  155. }
  156. }
  157. // printf("\n");
  158. return MNL_CB_OK;
  159. }
  160. static int nfstat_collect_conntrack() {
  161. // zero all metrics - we will sum the metrics of all CPUs later
  162. int i;
  163. for (i = 0; i < CTA_STATS_MAX+1; i++)
  164. nfstat_root.metrics[i] = 0;
  165. // prepare the request
  166. nfstat_root.nlh = nfct_mnl_nlmsghdr_put(nfstat_root.buf, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_STATS_CPU, AF_UNSPEC, nfstat_root.seq);
  167. // send the request
  168. if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
  169. error("NFSTAT: mnl_socket_sendto() failed");
  170. return 1;
  171. }
  172. // get the reply
  173. ssize_t ret;
  174. while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
  175. if(mnl_cb_run(
  176. nfstat_root.buf
  177. , (size_t)ret
  178. , nfstat_root.nlh->nlmsg_seq
  179. , nfstat_root.portid
  180. , nfstat_callback
  181. , NULL
  182. ) <= MNL_CB_STOP)
  183. break;
  184. }
  185. // verify we run without issues
  186. if (ret == -1) {
  187. error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
  188. return 1;
  189. }
  190. return 0;
  191. }
  192. static int nfexp_stats_attr_cb(const struct nlattr *attr, void *data)
  193. {
  194. const struct nlattr **tb = data;
  195. int type = mnl_attr_get_type(attr);
  196. if (mnl_attr_type_valid(attr, CTA_STATS_EXP_MAX) < 0)
  197. return MNL_CB_OK;
  198. if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
  199. error("NFSTAT EXP: mnl_attr_validate() failed");
  200. return MNL_CB_ERROR;
  201. }
  202. tb[type] = attr;
  203. return MNL_CB_OK;
  204. }
  205. static int nfstat_callback_exp(const struct nlmsghdr *nlh, void *data) {
  206. (void)data;
  207. struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
  208. mnl_attr_parse(nlh, sizeof(*nfg), nfexp_stats_attr_cb, nfstat_root.tb_exp);
  209. int i;
  210. for (i = 0; i < CTA_STATS_EXP_MAX+1; i++) {
  211. if (nfstat_root.tb_exp[i]) {
  212. nfstat_root.metrics_exp[i] += ntohl(mnl_attr_get_u32(nfstat_root.tb_exp[i]));
  213. }
  214. }
  215. return MNL_CB_OK;
  216. }
  217. static int nfstat_collect_conntrack_expectations() {
  218. // zero all metrics - we will sum the metrics of all CPUs later
  219. int i;
  220. for (i = 0; i < CTA_STATS_EXP_MAX+1; i++)
  221. nfstat_root.metrics_exp[i] = 0;
  222. // prepare the request
  223. nfstat_root.nlh = nfct_mnl_nlmsghdr_put(nfstat_root.buf, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET_STATS_CPU, AF_UNSPEC, nfstat_root.seq);
  224. // send the request
  225. if(mnl_socket_sendto(nfstat_root.mnl, nfstat_root.nlh, nfstat_root.nlh->nlmsg_len) < 0) {
  226. error("NFSTAT: mnl_socket_sendto() failed");
  227. return 1;
  228. }
  229. // get the reply
  230. ssize_t ret;
  231. while ((ret = mnl_socket_recvfrom(nfstat_root.mnl, nfstat_root.buf, nfstat_root.buf_size)) > 0) {
  232. if(mnl_cb_run(
  233. nfstat_root.buf
  234. , (size_t)ret
  235. , nfstat_root.nlh->nlmsg_seq
  236. , nfstat_root.portid
  237. , nfstat_callback_exp
  238. , NULL
  239. ) <= MNL_CB_STOP)
  240. break;
  241. }
  242. // verify we run without issues
  243. if (ret == -1) {
  244. error("NFSTAT: error communicating with kernel. This plugin can only work when netdata runs as root.");
  245. return 1;
  246. }
  247. return 0;
  248. }
  249. static int nfstat_collect() {
  250. nfstat_root.seq++;
  251. if(nfstat_collect_conntrack())
  252. return 1;
  253. if(nfstat_collect_conntrack_expectations())
  254. return 1;
  255. return 0;
  256. }
  257. static void nfstat_send_metrics() {
  258. static int new_chart_generated = 0, changes_chart_generated = 0, search_chart_generated = 0, errors_chart_generated = 0, expect_chart_generated = 0;
  259. if(!new_chart_generated) {
  260. new_chart_generated = 1;
  261. printf("CHART %s.%s '' 'Connection Tracker New Connections' 'connections/s' %s '' line %d %d %s\n"
  262. , RRD_TYPE_NET_STAT_NETFILTER
  263. , RRD_TYPE_NET_STAT_CONNTRACK "_new"
  264. , RRD_TYPE_NET_STAT_CONNTRACK
  265. , NETDATA_CHART_PRIO_NETFILTER_NEW
  266. , nfstat_root.update_every
  267. , PLUGIN_NFACCT_NAME
  268. );
  269. printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_NEW]);
  270. printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_IGNORE]);
  271. printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_INVALID]);
  272. }
  273. printf(
  274. "BEGIN %s.%s\n"
  275. , RRD_TYPE_NET_STAT_NETFILTER
  276. , RRD_TYPE_NET_STAT_CONNTRACK "_new"
  277. );
  278. printf(
  279. "SET %s = %lld\n"
  280. , nfstat_root.attr2name[CTA_STATS_NEW]
  281. , (collected_number) nfstat_root.metrics[CTA_STATS_NEW]
  282. );
  283. printf(
  284. "SET %s = %lld\n"
  285. , nfstat_root.attr2name[CTA_STATS_IGNORE]
  286. , (collected_number) nfstat_root.metrics[CTA_STATS_IGNORE]
  287. );
  288. printf(
  289. "SET %s = %lld\n"
  290. , nfstat_root.attr2name[CTA_STATS_INVALID]
  291. , (collected_number) nfstat_root.metrics[CTA_STATS_INVALID]
  292. );
  293. printf("END\n");
  294. // ----------------------------------------------------------------
  295. if(!changes_chart_generated) {
  296. changes_chart_generated = 1;
  297. printf("CHART %s.%s '' 'Connection Tracker Changes' 'changes/s' %s '' line %d %d detail %s\n"
  298. , RRD_TYPE_NET_STAT_NETFILTER
  299. , RRD_TYPE_NET_STAT_CONNTRACK "_changes"
  300. , RRD_TYPE_NET_STAT_CONNTRACK
  301. , NETDATA_CHART_PRIO_NETFILTER_CHANGES
  302. , nfstat_root.update_every
  303. , PLUGIN_NFACCT_NAME
  304. );
  305. printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_INSERT]);
  306. printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_DELETE]);
  307. printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_DELETE_LIST]);
  308. }
  309. printf(
  310. "BEGIN %s.%s\n"
  311. , RRD_TYPE_NET_STAT_NETFILTER
  312. , RRD_TYPE_NET_STAT_CONNTRACK "_changes"
  313. );
  314. printf(
  315. "SET %s = %lld\n"
  316. , nfstat_root.attr2name[CTA_STATS_INSERT]
  317. , (collected_number) nfstat_root.metrics[CTA_STATS_INSERT]
  318. );
  319. printf(
  320. "SET %s = %lld\n"
  321. , nfstat_root.attr2name[CTA_STATS_DELETE]
  322. , (collected_number) nfstat_root.metrics[CTA_STATS_DELETE]
  323. );
  324. printf(
  325. "SET %s = %lld\n"
  326. , nfstat_root.attr2name[CTA_STATS_DELETE_LIST]
  327. , (collected_number) nfstat_root.metrics[CTA_STATS_DELETE_LIST]
  328. );
  329. printf("END\n");
  330. // ----------------------------------------------------------------
  331. if(!search_chart_generated) {
  332. search_chart_generated = 1;
  333. printf("CHART %s.%s '' 'Connection Tracker Searches' 'searches/s' %s '' line %d %d detail %s\n"
  334. , RRD_TYPE_NET_STAT_NETFILTER
  335. , RRD_TYPE_NET_STAT_CONNTRACK "_search"
  336. , RRD_TYPE_NET_STAT_CONNTRACK
  337. , NETDATA_CHART_PRIO_NETFILTER_SEARCH
  338. , nfstat_root.update_every
  339. , PLUGIN_NFACCT_NAME
  340. );
  341. printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_SEARCHED]);
  342. printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_SEARCH_RESTART]);
  343. printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_FOUND]);
  344. }
  345. printf(
  346. "BEGIN %s.%s\n"
  347. , RRD_TYPE_NET_STAT_NETFILTER
  348. , RRD_TYPE_NET_STAT_CONNTRACK "_search"
  349. );
  350. printf(
  351. "SET %s = %lld\n"
  352. , nfstat_root.attr2name[CTA_STATS_SEARCHED]
  353. , (collected_number) nfstat_root.metrics[CTA_STATS_SEARCHED]
  354. );
  355. printf(
  356. "SET %s = %lld\n"
  357. , nfstat_root.attr2name[CTA_STATS_SEARCH_RESTART]
  358. , (collected_number) nfstat_root.metrics[CTA_STATS_SEARCH_RESTART]
  359. );
  360. printf(
  361. "SET %s = %lld\n"
  362. , nfstat_root.attr2name[CTA_STATS_FOUND]
  363. , (collected_number) nfstat_root.metrics[CTA_STATS_FOUND]
  364. );
  365. printf("END\n");
  366. // ----------------------------------------------------------------
  367. if(!errors_chart_generated) {
  368. errors_chart_generated = 1;
  369. printf("CHART %s.%s '' 'Connection Tracker Errors' 'events/s' %s '' line %d %d detail %s\n"
  370. , RRD_TYPE_NET_STAT_NETFILTER
  371. , RRD_TYPE_NET_STAT_CONNTRACK "_errors"
  372. , RRD_TYPE_NET_STAT_CONNTRACK
  373. , NETDATA_CHART_PRIO_NETFILTER_ERRORS
  374. , nfstat_root.update_every
  375. , PLUGIN_NFACCT_NAME
  376. );
  377. printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_ERROR]);
  378. printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_INSERT_FAILED]);
  379. printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_DROP]);
  380. printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_EARLY_DROP]);
  381. }
  382. printf(
  383. "BEGIN %s.%s\n"
  384. , RRD_TYPE_NET_STAT_NETFILTER
  385. , RRD_TYPE_NET_STAT_CONNTRACK "_errors"
  386. );
  387. printf(
  388. "SET %s = %lld\n"
  389. , nfstat_root.attr2name[CTA_STATS_ERROR]
  390. , (collected_number) nfstat_root.metrics[CTA_STATS_ERROR]
  391. );
  392. printf(
  393. "SET %s = %lld\n"
  394. , nfstat_root.attr2name[CTA_STATS_INSERT_FAILED]
  395. , (collected_number) nfstat_root.metrics[CTA_STATS_INSERT_FAILED]
  396. );
  397. printf(
  398. "SET %s = %lld\n"
  399. , nfstat_root.attr2name[CTA_STATS_DROP]
  400. , (collected_number) nfstat_root.metrics[CTA_STATS_DROP]
  401. );
  402. printf(
  403. "SET %s = %lld\n"
  404. , nfstat_root.attr2name[CTA_STATS_EARLY_DROP]
  405. , (collected_number) nfstat_root.metrics[CTA_STATS_EARLY_DROP]
  406. );
  407. printf("END\n");
  408. // ----------------------------------------------------------------
  409. if(!expect_chart_generated) {
  410. expect_chart_generated = 1;
  411. printf("CHART %s.%s '' 'Connection Tracker Expectations' 'expectations/s' %s '' line %d %d detail %s\n"
  412. , RRD_TYPE_NET_STAT_NETFILTER
  413. , RRD_TYPE_NET_STAT_CONNTRACK "_expect"
  414. , RRD_TYPE_NET_STAT_CONNTRACK
  415. , NETDATA_CHART_PRIO_NETFILTER_EXPECT
  416. , nfstat_root.update_every
  417. , PLUGIN_NFACCT_NAME
  418. );
  419. printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_EXP_CREATE]);
  420. printf("DIMENSION %s '' incremental -1 1\n", nfstat_root.attr2name[CTA_STATS_EXP_DELETE]);
  421. printf("DIMENSION %s '' incremental 1 1\n", nfstat_root.attr2name[CTA_STATS_EXP_NEW]);
  422. }
  423. printf(
  424. "BEGIN %s.%s\n"
  425. , RRD_TYPE_NET_STAT_NETFILTER
  426. , RRD_TYPE_NET_STAT_CONNTRACK "_expect"
  427. );
  428. printf(
  429. "SET %s = %lld\n"
  430. , nfstat_root.attr2name[CTA_STATS_EXP_CREATE]
  431. , (collected_number) nfstat_root.metrics[CTA_STATS_EXP_CREATE]
  432. );
  433. printf(
  434. "SET %s = %lld\n"
  435. , nfstat_root.attr2name[CTA_STATS_EXP_DELETE]
  436. , (collected_number) nfstat_root.metrics[CTA_STATS_EXP_DELETE]
  437. );
  438. printf(
  439. "SET %s = %lld\n"
  440. , nfstat_root.attr2name[CTA_STATS_EXP_NEW]
  441. , (collected_number) nfstat_root.metrics[CTA_STATS_EXP_NEW]
  442. );
  443. printf("END\n");
  444. }
  445. #endif // HAVE_LINUX_NETFILTER_NFNETLINK_CONNTRACK_H
  446. // ----------------------------------------------------------------------------
  447. // DO_NFACCT - collect netfilter accounting statistics via netlink
  448. #ifdef HAVE_LIBNETFILTER_ACCT
  449. #define DO_NFACCT 1
  450. #include <libnetfilter_acct/libnetfilter_acct.h>
  451. struct nfacct_data {
  452. char *name;
  453. uint32_t hash;
  454. uint64_t pkts;
  455. uint64_t bytes;
  456. int packets_dimension_added;
  457. int bytes_dimension_added;
  458. int updated;
  459. struct nfacct_data *next;
  460. };
  461. static struct {
  462. int update_every;
  463. char *buf;
  464. size_t buf_size;
  465. struct mnl_socket *mnl;
  466. struct nlmsghdr *nlh;
  467. unsigned int seq;
  468. uint32_t portid;
  469. struct nfacct *nfacct_buffer;
  470. struct nfacct_data *nfacct_metrics;
  471. } nfacct_root = {
  472. .update_every = 1,
  473. .buf = NULL,
  474. .buf_size = 0,
  475. .mnl = NULL,
  476. .nlh = NULL,
  477. .seq = 0,
  478. .portid = 0,
  479. .nfacct_buffer = NULL,
  480. .nfacct_metrics = NULL
  481. };
  482. static inline struct nfacct_data *nfacct_data_get(const char *name, uint32_t hash) {
  483. struct nfacct_data *d = NULL, *last = NULL;
  484. for(d = nfacct_root.nfacct_metrics; d ; last = d, d = d->next) {
  485. if(unlikely(d->hash == hash && !strcmp(d->name, name)))
  486. return d;
  487. }
  488. d = callocz(1, sizeof(struct nfacct_data));
  489. d->name = strdupz(name);
  490. d->hash = hash;
  491. if(!last) {
  492. d->next = nfacct_root.nfacct_metrics;
  493. nfacct_root.nfacct_metrics = d;
  494. }
  495. else {
  496. d->next = last->next;
  497. last->next = d;
  498. }
  499. return d;
  500. }
  501. static int nfacct_init(int update_every) {
  502. nfacct_root.update_every = update_every;
  503. nfacct_root.buf_size = mnl_buffer_size();
  504. nfacct_root.buf = mallocz(nfacct_root.buf_size);
  505. nfacct_root.nfacct_buffer = nfacct_alloc();
  506. if(!nfacct_root.nfacct_buffer) {
  507. error("nfacct.plugin: nfacct_alloc() failed.");
  508. return 0;
  509. }
  510. nfacct_root.seq = (unsigned int)now_realtime_sec() - 1;
  511. nfacct_root.mnl = mnl_socket_open(NETLINK_NETFILTER);
  512. if(!nfacct_root.mnl) {
  513. error("nfacct.plugin: mnl_socket_open() failed");
  514. return 1;
  515. }
  516. if(mnl_socket_bind(nfacct_root.mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
  517. error("nfacct.plugin: mnl_socket_bind() failed");
  518. return 1;
  519. }
  520. nfacct_root.portid = mnl_socket_get_portid(nfacct_root.mnl);
  521. return 0;
  522. }
  523. static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
  524. (void)data;
  525. if(nfacct_nlmsg_parse_payload(nlh, nfacct_root.nfacct_buffer) < 0) {
  526. error("NFACCT: nfacct_nlmsg_parse_payload() failed.");
  527. return MNL_CB_OK;
  528. }
  529. const char *name = nfacct_attr_get_str(nfacct_root.nfacct_buffer, NFACCT_ATTR_NAME);
  530. uint32_t hash = simple_hash(name);
  531. struct nfacct_data *d = nfacct_data_get(name, hash);
  532. d->pkts = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_PKTS);
  533. d->bytes = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_BYTES);
  534. d->updated = 1;
  535. return MNL_CB_OK;
  536. }
  537. static int nfacct_collect() {
  538. // mark all old metrics as not-updated
  539. struct nfacct_data *d;
  540. for(d = nfacct_root.nfacct_metrics; d ; d = d->next)
  541. d->updated = 0;
  542. // prepare the request
  543. nfacct_root.seq++;
  544. nfacct_root.nlh = nfacct_nlmsg_build_hdr(nfacct_root.buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, (uint32_t)nfacct_root.seq);
  545. if(!nfacct_root.nlh) {
  546. error("NFACCT: nfacct_nlmsg_build_hdr() failed");
  547. return 1;
  548. }
  549. // send the request
  550. if(mnl_socket_sendto(nfacct_root.mnl, nfacct_root.nlh, nfacct_root.nlh->nlmsg_len) < 0) {
  551. error("NFACCT: mnl_socket_sendto() failed");
  552. return 1;
  553. }
  554. // get the reply
  555. ssize_t ret;
  556. while((ret = mnl_socket_recvfrom(nfacct_root.mnl, nfacct_root.buf, nfacct_root.buf_size)) > 0) {
  557. if(mnl_cb_run(
  558. nfacct_root.buf
  559. , (size_t)ret
  560. , nfacct_root.seq
  561. , nfacct_root.portid
  562. , nfacct_callback
  563. , NULL
  564. ) <= 0)
  565. break;
  566. }
  567. // verify we run without issues
  568. if (ret == -1) {
  569. error("NFACCT: error communicating with kernel. This plugin can only work when netdata runs as root.");
  570. return 1;
  571. }
  572. return 0;
  573. }
  574. static void nfacct_send_metrics() {
  575. static int bytes_chart_generated = 0, packets_chart_generated = 0;
  576. if(!nfacct_root.nfacct_metrics) return;
  577. struct nfacct_data *d;
  578. if(!packets_chart_generated) {
  579. packets_chart_generated = 1;
  580. printf("CHART netfilter.nfacct_packets '' 'Netfilter Accounting Packets' 'packets/s' 'nfacct' '' stacked %d %d %s\n"
  581. , NETDATA_CHART_PRIO_NETFILTER_PACKETS
  582. , nfacct_root.update_every
  583. , PLUGIN_NFACCT_NAME
  584. );
  585. }
  586. for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
  587. if(likely(d->updated)) {
  588. if(unlikely(!d->packets_dimension_added)) {
  589. d->packets_dimension_added = 1;
  590. printf("CHART netfilter.nfacct_packets '' 'Netfilter Accounting Packets' 'packets/s'\n");
  591. printf("DIMENSION %s '' incremental 1 %d\n", d->name, nfacct_root.update_every);
  592. }
  593. printf(
  594. "BEGIN netfilter.nfacct_packets\n"
  595. "SET %s = %lld\n"
  596. "END\n"
  597. , d->name
  598. , (collected_number)d->pkts
  599. );
  600. }
  601. }
  602. // ----------------------------------------------------------------
  603. if(!bytes_chart_generated) {
  604. bytes_chart_generated = 1;
  605. printf("CHART netfilter.nfacct_bytes '' 'Netfilter Accounting Bandwidth' 'kilobytes/s' 'nfacct' '' stacked %d %d %s\n"
  606. , NETDATA_CHART_PRIO_NETFILTER_BYTES
  607. , nfacct_root.update_every
  608. , PLUGIN_NFACCT_NAME
  609. );
  610. }
  611. for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
  612. if(likely(d->updated)) {
  613. if(unlikely(!d->bytes_dimension_added)) {
  614. d->bytes_dimension_added = 1;
  615. printf("CHART netfilter.nfacct_bytes '' 'Netfilter Accounting Bandwidth' 'kilobytes/s'\n");
  616. printf("DIMENSION %s '' incremental 1 %d\n", d->name, 1000 * nfacct_root.update_every);
  617. }
  618. printf(
  619. "BEGIN netfilter.nfacct_bytes\n"
  620. "SET %s = %lld\n"
  621. "END\n"
  622. , d->name
  623. , (collected_number)d->bytes
  624. );
  625. }
  626. }
  627. }
  628. #endif // HAVE_LIBNETFILTER_ACCT
  629. int main(int argc, char **argv) {
  630. // ------------------------------------------------------------------------
  631. // initialization of netdata plugin
  632. program_name = "nfacct.plugin";
  633. // disable syslog
  634. error_log_syslog = 0;
  635. // set errors flood protection to 100 logs per hour
  636. error_log_errors_per_period = 100;
  637. error_log_throttle_period = 3600;
  638. // ------------------------------------------------------------------------
  639. // parse command line parameters
  640. int i, freq = 0;
  641. for(i = 1; i < argc ; i++) {
  642. if(isdigit(*argv[i]) && !freq) {
  643. int n = str2i(argv[i]);
  644. if(n > 0 && n < 86400) {
  645. freq = n;
  646. continue;
  647. }
  648. }
  649. else if(strcmp("version", argv[i]) == 0 || strcmp("-version", argv[i]) == 0 || strcmp("--version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
  650. printf("nfacct.plugin %s\n", VERSION);
  651. exit(0);
  652. }
  653. else if(strcmp("debug", argv[i]) == 0) {
  654. debug = 1;
  655. continue;
  656. }
  657. else if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
  658. fprintf(stderr,
  659. "\n"
  660. " netdata nfacct.plugin %s\n"
  661. " Copyright (C) 2015-2017 Costa Tsaousis <costa@tsaousis.gr>\n"
  662. " Released under GNU General Public License v3 or later.\n"
  663. " All rights reserved.\n"
  664. "\n"
  665. " This program is a data collector plugin for netdata.\n"
  666. "\n"
  667. " Available command line options:\n"
  668. "\n"
  669. " COLLECTION_FREQUENCY data collection frequency in seconds\n"
  670. " minimum: %d\n"
  671. "\n"
  672. " debug enable verbose output\n"
  673. " default: disabled\n"
  674. "\n"
  675. " -v\n"
  676. " -V\n"
  677. " --version print version and exit\n"
  678. "\n"
  679. " -h\n"
  680. " --help print this message and exit\n"
  681. "\n"
  682. " For more information:\n"
  683. " https://github.com/netdata/netdata/tree/master/collectors/nfacct.plugin\n"
  684. "\n"
  685. , VERSION
  686. , netdata_update_every
  687. );
  688. exit(1);
  689. }
  690. error("nfacct.plugin: ignoring parameter '%s'", argv[i]);
  691. }
  692. errno = 0;
  693. if(freq >= netdata_update_every)
  694. netdata_update_every = freq;
  695. else if(freq)
  696. error("update frequency %d seconds is too small for NFACCT. Using %d.", freq, netdata_update_every);
  697. #ifdef DO_NFACCT
  698. if(debug) fprintf(stderr, "nfacct.plugin: calling nfacct_init()\n");
  699. int nfacct = !nfacct_init(netdata_update_every);
  700. #endif
  701. #ifdef DO_NFSTAT
  702. if(debug) fprintf(stderr, "nfacct.plugin: calling nfstat_init()\n");
  703. int nfstat = !nfstat_init(netdata_update_every);
  704. #endif
  705. // ------------------------------------------------------------------------
  706. // the main loop
  707. if(debug) fprintf(stderr, "nfacct.plugin: starting data collection\n");
  708. time_t started_t = now_monotonic_sec();
  709. size_t iteration;
  710. usec_t step = netdata_update_every * USEC_PER_SEC;
  711. heartbeat_t hb;
  712. heartbeat_init(&hb);
  713. for(iteration = 0; 1; iteration++) {
  714. usec_t dt = heartbeat_next(&hb, step);
  715. if(unlikely(netdata_exit)) break;
  716. if(debug && iteration)
  717. fprintf(stderr, "nfacct.plugin: iteration %zu, dt %llu usec\n"
  718. , iteration
  719. , dt
  720. );
  721. #ifdef DO_NFACCT
  722. if(likely(nfacct)) {
  723. if(debug) fprintf(stderr, "nfacct.plugin: calling nfacct_collect()\n");
  724. nfacct = !nfacct_collect();
  725. if(likely(nfacct)) {
  726. if(debug) fprintf(stderr, "nfacct.plugin: calling nfacct_send_metrics()\n");
  727. nfacct_send_metrics();
  728. }
  729. }
  730. #endif
  731. #ifdef DO_NFSTAT
  732. if(likely(nfstat)) {
  733. if(debug) fprintf(stderr, "nfacct.plugin: calling nfstat_collect()\n");
  734. nfstat = !nfstat_collect();
  735. if(likely(nfstat)) {
  736. if(debug) fprintf(stderr, "nfacct.plugin: calling nfstat_send_metrics()\n");
  737. nfstat_send_metrics();
  738. }
  739. }
  740. #endif
  741. fflush(stdout);
  742. // restart check (14400 seconds)
  743. if(now_monotonic_sec() - started_t > 14400) break;
  744. }
  745. info("NFACCT process exiting");
  746. }
  747. #else // !HAVE_LIBMNL
  748. int main(int argc, char **argv) {
  749. (void)argc;
  750. (void)argv;
  751. fatal("nfacct.plugin is not compiled.");
  752. }
  753. #endif // !HAVE_LIBMNL