plugin_nfacct.c 28 KB

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