plugin_nfacct.c 28 KB

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