| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "plugin_proc.h" |
| 4 | |
| 5 | #define RRD_TYPE_NET_STAT_NETFILTER "netfilter" |
| 6 | #define RRD_TYPE_NET_STAT_CONNTRACK "conntrack" |
| 7 | #define PLUGIN_PROC_MODULE_CONNTRACK_NAME "/proc/net/stat/nf_conntrack" |
| 8 | |
| 9 | static const RRDVAR_ACQUIRED *rrdvar_max = NULL; |
| 10 | |
| 11 | void proc_net_stat_conntrack_cleanup(void) { |
| 12 | if(rrdvar_max) |
| 13 | rrdvar_host_variable_release(localhost, rrdvar_max); |
| 14 | } |
| 15 | |
| 16 | int do_proc_net_stat_conntrack(int update_every, usec_t dt) { |
| 17 | static procfile *ff = NULL; |
| 18 | static int do_sockets = -1, do_new = -1, do_changes = -1, do_expect = -1, do_search = -1, do_errors = -1; |
| 19 | static usec_t get_max_every = 10 * USEC_PER_SEC, usec_since_last_max = 0; |
| 20 | static int read_full = 1; |
| 21 | static const char *nf_conntrack_filename, *nf_conntrack_count_filename, *nf_conntrack_max_filename; |
| 22 | |
| 23 | unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0, |
| 24 | ainsert = 0, ainsert_failed = 0, adrop = 0, aearly_drop = 0, aicmp_error = 0, aexpect_new = 0, aexpect_create = 0, aexpect_delete = 0, asearch_restart = 0; |
| 25 | |
| 26 | if(unlikely(do_sockets == -1)) { |
| 27 | char filename[FILENAME_MAX + 1]; |
| 28 | snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/stat/nf_conntrack"); |
| 29 | nf_conntrack_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/net/stat/nf_conntrack", "filename to monitor", filename); |
| 30 | |
| 31 | snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/sys/net/netfilter/nf_conntrack_max"); |
| 32 | nf_conntrack_max_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/sys/net/netfilter/nf_conntrack_max", "filename to monitor", filename); |
| 33 | usec_since_last_max = get_max_every = inicfg_get_number(&netdata_config, "plugin:proc:/proc/sys/net/netfilter/nf_conntrack_max", "read every seconds", 10) * USEC_PER_SEC; |
| 34 | |
| 35 | read_full = 1; |
| 36 | ff = procfile_open(nf_conntrack_filename, " \t:", PROCFILE_FLAG_DEFAULT); |
| 37 | if(!ff) read_full = 0; |
| 38 | |
| 39 | do_new = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/net/stat/nf_conntrack", "netfilter new connections", read_full); |
| 40 | do_changes = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection changes", read_full); |
| 41 | do_expect = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection expectations", read_full); |
| 42 | do_search = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection searches", read_full); |
| 43 | do_errors = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/net/stat/nf_conntrack", "netfilter errors", read_full); |
| 44 | |
| 45 | do_sockets = 1; |
| 46 | if(!read_full) { |
| 47 | snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/sys/net/netfilter/nf_conntrack_count"); |
| 48 | nf_conntrack_count_filename = inicfg_get(&netdata_config, "plugin:proc:/proc/sys/net/netfilter/nf_conntrack_count", "filename to monitor", filename); |
| 49 | |
| 50 | if(read_single_number_file(nf_conntrack_count_filename, &aentries)) |
| 51 | do_sockets = 0; |
| 52 | } |
| 53 | |
| 54 | do_sockets = inicfg_get_boolean(&netdata_config, "plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connections", do_sockets); |
| 55 | |
| 56 | if(!do_sockets && !read_full) |
| 57 | return 1; |
| 58 | |
| 59 | rrdvar_max = rrdvar_host_variable_add_and_acquire(localhost, "netfilter_conntrack_max"); |
| 60 | } |
| 61 | |
| 62 | if(likely(read_full)) { |
| 63 | if(unlikely(!ff)) { |
| 64 | ff = procfile_open(nf_conntrack_filename, " \t:", PROCFILE_FLAG_DEFAULT); |
| 65 | if(unlikely(!ff)) |
| 66 | return 0; // we return 0, so that we will retry to open it next time |
| 67 | } |
| 68 | |
| 69 | ff = procfile_readall(ff); |
| 70 | if(unlikely(!ff)) |
| 71 | return 0; // we return 0, so that we will retry to open it next time |
| 72 | |
| 73 | size_t lines = procfile_lines(ff), l; |
| 74 | |
| 75 | for(l = 1; l < lines ;l++) { |
| 76 | size_t words = procfile_linewords(ff, l); |
| 77 | if(unlikely(words < 17)) { |
| 78 | if(unlikely(words)) collector_error("Cannot read /proc/net/stat/nf_conntrack line. Expected 17 params, read %zu.", words); |
| 79 | continue; |
| 80 | } |
| 81 | |
| 82 | unsigned long long tentries = 0, tsearched = 0, tfound = 0, tnew = 0, tinvalid = 0, tignore = 0, tdelete = 0, tdelete_list = 0, tinsert = 0, tinsert_failed = 0, tdrop = 0, tearly_drop = 0, ticmp_error = 0, texpect_new = 0, texpect_create = 0, texpect_delete = 0, tsearch_restart = 0; |
| 83 | |
| 84 | tentries = strtoull(procfile_lineword(ff, l, 0), NULL, 16); |
| 85 | tsearched = strtoull(procfile_lineword(ff, l, 1), NULL, 16); |
| 86 | tfound = strtoull(procfile_lineword(ff, l, 2), NULL, 16); |
| 87 | tnew = strtoull(procfile_lineword(ff, l, 3), NULL, 16); |
| 88 | tinvalid = strtoull(procfile_lineword(ff, l, 4), NULL, 16); |
| 89 | tignore = strtoull(procfile_lineword(ff, l, 5), NULL, 16); |
| 90 | tdelete = strtoull(procfile_lineword(ff, l, 6), NULL, 16); |
| 91 | tdelete_list = strtoull(procfile_lineword(ff, l, 7), NULL, 16); |
| 92 | tinsert = strtoull(procfile_lineword(ff, l, 8), NULL, 16); |
| 93 | tinsert_failed = strtoull(procfile_lineword(ff, l, 9), NULL, 16); |
| 94 | tdrop = strtoull(procfile_lineword(ff, l, 10), NULL, 16); |
| 95 | tearly_drop = strtoull(procfile_lineword(ff, l, 11), NULL, 16); |
| 96 | ticmp_error = strtoull(procfile_lineword(ff, l, 12), NULL, 16); |
| 97 | texpect_new = strtoull(procfile_lineword(ff, l, 13), NULL, 16); |
| 98 | texpect_create = strtoull(procfile_lineword(ff, l, 14), NULL, 16); |
| 99 | texpect_delete = strtoull(procfile_lineword(ff, l, 15), NULL, 16); |
| 100 | tsearch_restart = strtoull(procfile_lineword(ff, l, 16), NULL, 16); |
| 101 | |
| 102 | if(unlikely(!aentries)) aentries = tentries; |
| 103 | |
| 104 | // sum all the cpus together |
| 105 | asearched += tsearched; // conntrack.search |
| 106 | afound += tfound; // conntrack.search |
| 107 | anew += tnew; // conntrack.new |
| 108 | ainvalid += tinvalid; // conntrack.new |
| 109 | aignore += tignore; // conntrack.new |
| 110 | adelete += tdelete; // conntrack.changes |
| 111 | adelete_list += tdelete_list; // conntrack.changes |
| 112 | ainsert += tinsert; // conntrack.changes |
| 113 | ainsert_failed += tinsert_failed; // conntrack.errors |
| 114 | adrop += tdrop; // conntrack.errors |
| 115 | aearly_drop += tearly_drop; // conntrack.errors |
| 116 | aicmp_error += ticmp_error; // conntrack.errors |
| 117 | aexpect_new += texpect_new; // conntrack.expect |
| 118 | aexpect_create += texpect_create; // conntrack.expect |
| 119 | aexpect_delete += texpect_delete; // conntrack.expect |
| 120 | asearch_restart += tsearch_restart; // conntrack.search |
| 121 | } |
| 122 | } |
| 123 | else { |
| 124 | if(unlikely(read_single_number_file(nf_conntrack_count_filename, &aentries))) |
| 125 | return 0; // we return 0, so that we will retry to open it next time |
| 126 | } |
| 127 | |
| 128 | usec_since_last_max += dt; |
| 129 | if(unlikely(rrdvar_max && usec_since_last_max >= get_max_every)) { |
| 130 | usec_since_last_max = 0; |
| 131 | |
| 132 | unsigned long long max; |
| 133 | if(likely(!read_single_number_file(nf_conntrack_max_filename, &max))) |
| 134 | rrdvar_host_variable_set(localhost, rrdvar_max, max); |
| 135 | } |
| 136 | |
| 137 | // -------------------------------------------------------------------- |
| 138 | |
| 139 | if(do_sockets) { |
| 140 | static RRDSET *st = NULL; |
| 141 | static RRDDIM *rd_connections = NULL; |
| 142 | |
| 143 | if(unlikely(!st)) { |
| 144 | st = rrdset_create_localhost( |
| 145 | RRD_TYPE_NET_STAT_NETFILTER |
| 146 | , RRD_TYPE_NET_STAT_CONNTRACK "_sockets" |
| 147 | , NULL |
| 148 | , RRD_TYPE_NET_STAT_CONNTRACK |
| 149 | , NULL |
| 150 | , "Connection Tracker Connections" |
| 151 | , "active connections" |
| 152 | , PLUGIN_PROC_NAME |
| 153 | , PLUGIN_PROC_MODULE_CONNTRACK_NAME |
| 154 | , NETDATA_CHART_PRIO_NETFILTER_SOCKETS |
| 155 | , update_every |
| 156 | , RRDSET_TYPE_LINE |
| 157 | ); |
| 158 | |
| 159 | rd_connections = rrddim_add(st, "connections", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 160 | } |
| 161 | |
| 162 | rrddim_set_by_pointer(st, rd_connections, aentries); |
| 163 | rrdset_done(st); |
| 164 | } |
| 165 | |
| 166 | // -------------------------------------------------------------------- |
| 167 | |
| 168 | if(do_new) { |
| 169 | static RRDSET *st = NULL; |
| 170 | static RRDDIM |
| 171 | *rd_new = NULL, |
| 172 | *rd_ignore = NULL, |
| 173 | *rd_invalid = NULL; |
| 174 | |
| 175 | if(unlikely(!st)) { |
| 176 | st = rrdset_create_localhost( |
| 177 | RRD_TYPE_NET_STAT_NETFILTER |
| 178 | , RRD_TYPE_NET_STAT_CONNTRACK "_new" |
| 179 | , NULL |
| 180 | , RRD_TYPE_NET_STAT_CONNTRACK |
| 181 | , NULL |
| 182 | , "Connection Tracker New Connections" |
| 183 | , "connections/s" |
| 184 | , PLUGIN_PROC_NAME |
| 185 | , PLUGIN_PROC_MODULE_CONNTRACK_NAME |
| 186 | , NETDATA_CHART_PRIO_NETFILTER_NEW |
| 187 | , update_every |
| 188 | , RRDSET_TYPE_LINE |
| 189 | ); |
| 190 | |
| 191 | rd_new = rrddim_add(st, "new", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 192 | rd_ignore = rrddim_add(st, "ignore", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 193 | rd_invalid = rrddim_add(st, "invalid", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 194 | } |
| 195 | |
| 196 | rrddim_set_by_pointer(st, rd_new, anew); |
| 197 | rrddim_set_by_pointer(st, rd_ignore, aignore); |
| 198 | rrddim_set_by_pointer(st, rd_invalid, ainvalid); |
| 199 | rrdset_done(st); |
| 200 | } |
| 201 | |
| 202 | // -------------------------------------------------------------------- |
| 203 | |
| 204 | if(do_changes) { |
| 205 | static RRDSET *st = NULL; |
| 206 | static RRDDIM |
| 207 | *rd_inserted = NULL, |
| 208 | *rd_deleted = NULL, |
| 209 | *rd_delete_list = NULL; |
| 210 | |
| 211 | if(unlikely(!st)) { |
| 212 | st = rrdset_create_localhost( |
| 213 | RRD_TYPE_NET_STAT_NETFILTER |
| 214 | , RRD_TYPE_NET_STAT_CONNTRACK "_changes" |
| 215 | , NULL |
| 216 | , RRD_TYPE_NET_STAT_CONNTRACK |
| 217 | , NULL |
| 218 | , "Connection Tracker Changes" |
| 219 | , "changes/s" |
| 220 | , PLUGIN_PROC_NAME |
| 221 | , PLUGIN_PROC_MODULE_CONNTRACK_NAME |
| 222 | , NETDATA_CHART_PRIO_NETFILTER_CHANGES |
| 223 | , update_every |
| 224 | , RRDSET_TYPE_LINE |
| 225 | ); |
| 226 | |
| 227 | rd_inserted = rrddim_add(st, "inserted", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 228 | rd_deleted = rrddim_add(st, "deleted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 229 | rd_delete_list = rrddim_add(st, "delete_list", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 230 | } |
| 231 | |
| 232 | rrddim_set_by_pointer(st, rd_inserted, ainsert); |
| 233 | rrddim_set_by_pointer(st, rd_deleted, adelete); |
| 234 | rrddim_set_by_pointer(st, rd_delete_list, adelete_list); |
| 235 | rrdset_done(st); |
| 236 | } |
| 237 | |
| 238 | // -------------------------------------------------------------------- |
| 239 | |
| 240 | if(do_expect) { |
| 241 | static RRDSET *st = NULL; |
| 242 | static RRDDIM *rd_created = NULL, |
| 243 | *rd_deleted = NULL, |
| 244 | *rd_new = NULL; |
| 245 | |
| 246 | if(unlikely(!st)) { |
| 247 | st = rrdset_create_localhost( |
| 248 | RRD_TYPE_NET_STAT_NETFILTER |
| 249 | , RRD_TYPE_NET_STAT_CONNTRACK "_expect" |
| 250 | , NULL |
| 251 | , RRD_TYPE_NET_STAT_CONNTRACK |
| 252 | , NULL |
| 253 | , "Connection Tracker Expectations" |
| 254 | , "expectations/s" |
| 255 | , PLUGIN_PROC_NAME |
| 256 | , PLUGIN_PROC_MODULE_CONNTRACK_NAME |
| 257 | , NETDATA_CHART_PRIO_NETFILTER_EXPECT |
| 258 | , update_every |
| 259 | , RRDSET_TYPE_LINE |
| 260 | ); |
| 261 | |
| 262 | rd_created = rrddim_add(st, "created", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 263 | rd_deleted = rrddim_add(st, "deleted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 264 | rd_new = rrddim_add(st, "new", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 265 | } |
| 266 | |
| 267 | rrddim_set_by_pointer(st, rd_created, aexpect_create); |
| 268 | rrddim_set_by_pointer(st, rd_deleted, aexpect_delete); |
| 269 | rrddim_set_by_pointer(st, rd_new, aexpect_new); |
| 270 | rrdset_done(st); |
| 271 | } |
| 272 | |
| 273 | // -------------------------------------------------------------------- |
| 274 | |
| 275 | if(do_search) { |
| 276 | static RRDSET *st = NULL; |
| 277 | static RRDDIM *rd_searched = NULL, |
| 278 | *rd_restarted = NULL, |
| 279 | *rd_found = NULL; |
| 280 | |
| 281 | if(unlikely(!st)) { |
| 282 | st = rrdset_create_localhost( |
| 283 | RRD_TYPE_NET_STAT_NETFILTER |
| 284 | , RRD_TYPE_NET_STAT_CONNTRACK "_search" |
| 285 | , NULL |
| 286 | , RRD_TYPE_NET_STAT_CONNTRACK |
| 287 | , NULL |
| 288 | , "Connection Tracker Searches" |
| 289 | , "searches/s" |
| 290 | , PLUGIN_PROC_NAME |
| 291 | , PLUGIN_PROC_MODULE_CONNTRACK_NAME |
| 292 | , NETDATA_CHART_PRIO_NETFILTER_SEARCH |
| 293 | , update_every |
| 294 | , RRDSET_TYPE_LINE |
| 295 | ); |
| 296 | |
| 297 | rd_searched = rrddim_add(st, "searched", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 298 | rd_restarted = rrddim_add(st, "restarted", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 299 | rd_found = rrddim_add(st, "found", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 300 | } |
| 301 | |
| 302 | rrddim_set_by_pointer(st, rd_searched, asearched); |
| 303 | rrddim_set_by_pointer(st, rd_restarted, asearch_restart); |
| 304 | rrddim_set_by_pointer(st, rd_found, afound); |
| 305 | rrdset_done(st); |
| 306 | } |
| 307 | |
| 308 | // -------------------------------------------------------------------- |
| 309 | |
| 310 | if(do_errors) { |
| 311 | static RRDSET *st = NULL; |
| 312 | static RRDDIM *rd_icmp_error = NULL, |
| 313 | *rd_insert_failed = NULL, |
| 314 | *rd_drop = NULL, |
| 315 | *rd_early_drop = NULL; |
| 316 | |
| 317 | if(unlikely(!st)) { |
| 318 | st = rrdset_create_localhost( |
| 319 | RRD_TYPE_NET_STAT_NETFILTER |
| 320 | , RRD_TYPE_NET_STAT_CONNTRACK "_errors" |
| 321 | , NULL |
| 322 | , RRD_TYPE_NET_STAT_CONNTRACK |
| 323 | , NULL |
| 324 | , "Connection Tracker Errors" |
| 325 | , "events/s" |
| 326 | , PLUGIN_PROC_NAME |
| 327 | , PLUGIN_PROC_MODULE_CONNTRACK_NAME |
| 328 | , NETDATA_CHART_PRIO_NETFILTER_ERRORS |
| 329 | , update_every |
| 330 | , RRDSET_TYPE_LINE |
| 331 | ); |
| 332 | |
| 333 | rd_icmp_error = rrddim_add(st, "icmp_error", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 334 | rd_insert_failed = rrddim_add(st, "insert_failed", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 335 | rd_drop = rrddim_add(st, "drop", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 336 | rd_early_drop = rrddim_add(st, "early_drop", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 337 | } |
| 338 | |
| 339 | rrddim_set_by_pointer(st, rd_icmp_error, aicmp_error); |
| 340 | rrddim_set_by_pointer(st, rd_insert_failed, ainsert_failed); |
| 341 | rrddim_set_by_pointer(st, rd_drop, adrop); |
| 342 | rrddim_set_by_pointer(st, rd_early_drop, aearly_drop); |
| 343 | rrdset_done(st); |
| 344 | } |
| 345 | |
| 346 | return 0; |
| 347 | } |