1 /**
2 * collectd - src/netlink.c
3 * Copyright (C) 2007-2010 Florian octo Forster
4 * Copyright (C) 2008-2012 Sebastian Harl
5 * Copyright (C) 2013 Andreas Henriksson
6 * Copyright (C) 2013 Marc Fournier
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; only version 2 of the License is applicable.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 * Authors:
22 * Florian octo Forster <octo at collectd.org>
23 * Sebastian Harl <sh at tokkee.org>
24 * Andreas Henriksson <andreas at fatal.se>
25 * Marc Fournier <marc.fournier at camptocamp.com>
26 **/
28 #include "collectd.h"
30 #include "common.h"
31 #include "plugin.h"
33 #include <asm/types.h>
35 #include <linux/netlink.h>
36 #include <linux/rtnetlink.h>
37 #if HAVE_LINUX_GEN_STATS_H
38 #include <linux/gen_stats.h>
39 #endif
40 #if HAVE_LINUX_PKT_SCHED_H
41 #include <linux/pkt_sched.h>
42 #endif
44 #include <libmnl/libmnl.h>
46 struct ir_link_stats_storage_s {
48 uint64_t rx_packets;
49 uint64_t tx_packets;
50 uint64_t rx_bytes;
51 uint64_t tx_bytes;
52 uint64_t rx_errors;
53 uint64_t tx_errors;
55 uint64_t rx_dropped;
56 uint64_t tx_dropped;
57 uint64_t multicast;
58 uint64_t collisions;
60 uint64_t rx_length_errors;
61 uint64_t rx_over_errors;
62 uint64_t rx_crc_errors;
63 uint64_t rx_frame_errors;
64 uint64_t rx_fifo_errors;
65 uint64_t rx_missed_errors;
67 uint64_t tx_aborted_errors;
68 uint64_t tx_carrier_errors;
69 uint64_t tx_fifo_errors;
70 uint64_t tx_heartbeat_errors;
71 uint64_t tx_window_errors;
72 };
74 union ir_link_stats_u {
75 struct rtnl_link_stats *stats32;
76 #ifdef HAVE_RTNL_LINK_STATS64
77 struct rtnl_link_stats64 *stats64;
78 #endif
79 };
81 typedef struct ir_ignorelist_s {
82 char *device;
83 char *type;
84 char *inst;
85 struct ir_ignorelist_s *next;
86 } ir_ignorelist_t;
88 struct qos_stats {
89 struct gnet_stats_basic *bs;
90 struct gnet_stats_queue *qs;
91 };
93 static int ir_ignorelist_invert = 1;
94 static ir_ignorelist_t *ir_ignorelist_head = NULL;
96 static struct mnl_socket *nl;
98 static char **iflist = NULL;
99 static size_t iflist_len = 0;
101 static const char *config_keys[] = {"Interface", "VerboseInterface",
102 "QDisc", "Class",
103 "Filter", "IgnoreSelected"};
104 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
106 static int add_ignorelist(const char *dev, const char *type, const char *inst) {
107 ir_ignorelist_t *entry;
109 entry = calloc(1, sizeof(*entry));
110 if (entry == NULL)
111 return (-1);
113 if (strcasecmp(dev, "All") != 0) {
114 entry->device = strdup(dev);
115 if (entry->device == NULL) {
116 sfree(entry);
117 return (-1);
118 }
119 }
121 entry->type = strdup(type);
122 if (entry->type == NULL) {
123 sfree(entry->device);
124 sfree(entry);
125 return (-1);
126 }
128 if (inst != NULL) {
129 entry->inst = strdup(inst);
130 if (entry->inst == NULL) {
131 sfree(entry->type);
132 sfree(entry->device);
133 sfree(entry);
134 return (-1);
135 }
136 }
138 entry->next = ir_ignorelist_head;
139 ir_ignorelist_head = entry;
141 return (0);
142 } /* int add_ignorelist */
144 /*
145 * Checks wether a data set should be ignored. Returns `true' is the value
146 * should be ignored, `false' otherwise.
147 */
148 static int check_ignorelist(const char *dev, const char *type,
149 const char *type_instance) {
150 assert((dev != NULL) && (type != NULL));
152 if (ir_ignorelist_head == NULL)
153 return (ir_ignorelist_invert ? 0 : 1);
155 for (ir_ignorelist_t *i = ir_ignorelist_head; i != NULL; i = i->next) {
156 /* i->device == NULL => match all devices */
157 if ((i->device != NULL) && (strcasecmp(i->device, dev) != 0))
158 continue;
160 if (strcasecmp(i->type, type) != 0)
161 continue;
163 if ((i->inst != NULL) && (type_instance != NULL) &&
164 (strcasecmp(i->inst, type_instance) != 0))
165 continue;
167 DEBUG("netlink plugin: check_ignorelist: "
168 "(dev = %s; type = %s; inst = %s) matched "
169 "(dev = %s; type = %s; inst = %s)",
170 dev, type, type_instance == NULL ? "(nil)" : type_instance,
171 i->device == NULL ? "(nil)" : i->device, i->type,
172 i->inst == NULL ? "(nil)" : i->inst);
174 return (ir_ignorelist_invert ? 0 : 1);
175 } /* for i */
177 return (ir_ignorelist_invert);
178 } /* int check_ignorelist */
180 static void submit_one(const char *dev, const char *type,
181 const char *type_instance, derive_t value) {
182 value_list_t vl = VALUE_LIST_INIT;
184 vl.values = &(value_t){.derive = value};
185 vl.values_len = 1;
186 sstrncpy(vl.plugin, "netlink", sizeof(vl.plugin));
187 sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
188 sstrncpy(vl.type, type, sizeof(vl.type));
190 if (type_instance != NULL)
191 sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
193 plugin_dispatch_values(&vl);
194 } /* void submit_one */
196 static void submit_two(const char *dev, const char *type,
197 const char *type_instance, derive_t rx, derive_t tx) {
198 value_list_t vl = VALUE_LIST_INIT;
199 value_t values[] = {
200 {.derive = rx}, {.derive = tx},
201 };
203 vl.values = values;
204 vl.values_len = STATIC_ARRAY_SIZE(values);
205 sstrncpy(vl.plugin, "netlink", sizeof(vl.plugin));
206 sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
207 sstrncpy(vl.type, type, sizeof(vl.type));
209 if (type_instance != NULL)
210 sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
212 plugin_dispatch_values(&vl);
213 } /* void submit_two */
215 static int update_iflist(struct ifinfomsg *msg, const char *dev) {
216 /* Update the `iflist'. It's used to know which interfaces exist and query
217 * them later for qdiscs and classes. */
218 if ((msg->ifi_index >= 0) && ((size_t)msg->ifi_index >= iflist_len)) {
219 char **temp;
221 temp = realloc(iflist, (msg->ifi_index + 1) * sizeof(char *));
222 if (temp == NULL) {
223 ERROR("netlink plugin: update_iflist: realloc failed.");
224 return (-1);
225 }
227 memset(temp + iflist_len, '\0',
228 (msg->ifi_index + 1 - iflist_len) * sizeof(char *));
229 iflist = temp;
230 iflist_len = msg->ifi_index + 1;
231 }
232 if ((iflist[msg->ifi_index] == NULL) ||
233 (strcmp(iflist[msg->ifi_index], dev) != 0)) {
234 sfree(iflist[msg->ifi_index]);
235 iflist[msg->ifi_index] = strdup(dev);
236 }
238 return (0);
239 } /* int update_iflist */
241 static void check_ignorelist_and_submit(const char *dev,
242 struct ir_link_stats_storage_s *stats) {
244 if (check_ignorelist(dev, "interface", NULL) == 0) {
245 submit_two(dev, "if_octets", NULL, stats->rx_bytes, stats->tx_bytes);
246 submit_two(dev, "if_packets", NULL, stats->rx_packets, stats->tx_packets);
247 submit_two(dev, "if_errors", NULL, stats->rx_errors, stats->tx_errors);
248 } else {
249 DEBUG("netlink plugin: Ignoring %s/interface.", dev);
250 }
252 if (check_ignorelist(dev, "if_detail", NULL) == 0) {
253 submit_two(dev, "if_dropped", NULL, stats->rx_dropped, stats->tx_dropped);
254 submit_one(dev, "if_multicast", NULL, stats->multicast);
255 submit_one(dev, "if_collisions", NULL, stats->collisions);
257 submit_one(dev, "if_rx_errors", "length", stats->rx_length_errors);
258 submit_one(dev, "if_rx_errors", "over", stats->rx_over_errors);
259 submit_one(dev, "if_rx_errors", "crc", stats->rx_crc_errors);
260 submit_one(dev, "if_rx_errors", "frame", stats->rx_frame_errors);
261 submit_one(dev, "if_rx_errors", "fifo", stats->rx_fifo_errors);
262 submit_one(dev, "if_rx_errors", "missed", stats->rx_missed_errors);
264 submit_one(dev, "if_tx_errors", "aborted", stats->tx_aborted_errors);
265 submit_one(dev, "if_tx_errors", "carrier", stats->tx_carrier_errors);
266 submit_one(dev, "if_tx_errors", "fifo", stats->tx_fifo_errors);
267 submit_one(dev, "if_tx_errors", "heartbeat", stats->tx_heartbeat_errors);
268 submit_one(dev, "if_tx_errors", "window", stats->tx_window_errors);
269 } else {
270 DEBUG("netlink plugin: Ignoring %s/if_detail.", dev);
271 }
273 } /* void check_ignorelist_and_submit */
275 #define COPY_RTNL_LINK_VALUE(dst_stats, src_stats, value_name) \
276 (dst_stats)->value_name = (src_stats)->value_name
278 #define COPY_RTNL_LINK_STATS(dst_stats, src_stats) \
279 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_packets); \
280 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_packets); \
281 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_bytes); \
282 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_bytes); \
283 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_errors); \
284 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_errors); \
285 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_dropped); \
286 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_dropped); \
287 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, multicast); \
288 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, collisions); \
289 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_length_errors); \
290 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_over_errors); \
291 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_crc_errors); \
292 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_frame_errors); \
293 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_fifo_errors); \
294 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_missed_errors); \
295 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_aborted_errors); \
296 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_carrier_errors); \
297 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_fifo_errors); \
298 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_heartbeat_errors); \
299 COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_window_errors)
301 #ifdef HAVE_RTNL_LINK_STATS64
302 static void check_ignorelist_and_submit64(const char *dev,
303 struct rtnl_link_stats64 *stats) {
304 struct ir_link_stats_storage_s s;
306 COPY_RTNL_LINK_STATS(&s, stats);
308 check_ignorelist_and_submit(dev, &s);
309 }
310 #endif
312 static void check_ignorelist_and_submit32(const char *dev,
313 struct rtnl_link_stats *stats) {
314 struct ir_link_stats_storage_s s;
316 COPY_RTNL_LINK_STATS(&s, stats);
318 check_ignorelist_and_submit(dev, &s);
319 }
321 static int link_filter_cb(const struct nlmsghdr *nlh,
322 void *args __attribute__((unused))) {
323 struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
324 struct nlattr *attr;
325 const char *dev = NULL;
326 union ir_link_stats_u stats;
328 if (nlh->nlmsg_type != RTM_NEWLINK) {
329 ERROR("netlink plugin: link_filter_cb: Don't know how to handle type %i.",
330 nlh->nlmsg_type);
331 return MNL_CB_ERROR;
332 }
334 /* Scan attribute list for device name. */
335 mnl_attr_for_each(attr, nlh, sizeof(*ifm)) {
336 if (mnl_attr_get_type(attr) != IFLA_IFNAME)
337 continue;
339 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
340 ERROR("netlink plugin: link_filter_cb: IFLA_IFNAME mnl_attr_validate "
341 "failed.");
342 return MNL_CB_ERROR;
343 }
345 dev = mnl_attr_get_str(attr);
346 if (update_iflist(ifm, dev) < 0)
347 return MNL_CB_ERROR;
348 break;
349 }
351 if (dev == NULL) {
352 ERROR("netlink plugin: link_filter_cb: dev == NULL");
353 return MNL_CB_ERROR;
354 }
355 #ifdef HAVE_RTNL_LINK_STATS64
356 mnl_attr_for_each(attr, nlh, sizeof(*ifm)) {
357 if (mnl_attr_get_type(attr) != IFLA_STATS64)
358 continue;
360 if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*stats.stats64)) < 0) {
361 ERROR("netlink plugin: link_filter_cb: IFLA_STATS64 mnl_attr_validate2 "
362 "failed.");
363 return MNL_CB_ERROR;
364 }
365 stats.stats64 = mnl_attr_get_payload(attr);
367 check_ignorelist_and_submit64(dev, stats.stats64);
369 return MNL_CB_OK;
370 }
371 #endif
372 mnl_attr_for_each(attr, nlh, sizeof(*ifm)) {
373 if (mnl_attr_get_type(attr) != IFLA_STATS)
374 continue;
376 if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*stats.stats32)) < 0) {
377 ERROR("netlink plugin: link_filter_cb: IFLA_STATS mnl_attr_validate2 "
378 "failed.");
379 return MNL_CB_ERROR;
380 }
381 stats.stats32 = mnl_attr_get_payload(attr);
383 check_ignorelist_and_submit32(dev, stats.stats32);
385 return MNL_CB_OK;
386 }
388 DEBUG("netlink plugin: link_filter: No statistics for interface %s.", dev);
389 return MNL_CB_OK;
391 } /* int link_filter_cb */
393 #if HAVE_TCA_STATS2
394 static int qos_attr_cb(const struct nlattr *attr, void *data) {
395 struct qos_stats *q_stats = (struct qos_stats *)data;
397 /* skip unsupported attribute in user-space */
398 if (mnl_attr_type_valid(attr, TCA_STATS_MAX) < 0)
399 return MNL_CB_OK;
401 if (mnl_attr_get_type(attr) == TCA_STATS_BASIC) {
402 if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*q_stats->bs)) < 0) {
403 ERROR("netlink plugin: qos_attr_cb: TCA_STATS_BASIC mnl_attr_validate2 "
404 "failed.");
405 return MNL_CB_ERROR;
406 }
407 q_stats->bs = mnl_attr_get_payload(attr);
408 return MNL_CB_OK;
409 }
411 if (mnl_attr_get_type(attr) == TCA_STATS_QUEUE) {
412 if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*q_stats->qs)) < 0) {
413 ERROR("netlink plugin: qos_attr_cb: TCA_STATS_QUEUE mnl_attr_validate2 "
414 "failed.");
415 return MNL_CB_ERROR;
416 }
417 q_stats->qs = mnl_attr_get_payload(attr);
418 return MNL_CB_OK;
419 }
421 return MNL_CB_OK;
422 } /* qos_attr_cb */
423 #endif
425 static int qos_filter_cb(const struct nlmsghdr *nlh, void *args) {
426 struct tcmsg *tm = mnl_nlmsg_get_payload(nlh);
427 struct nlattr *attr;
429 int wanted_ifindex = *((int *)args);
431 const char *dev;
432 const char *kind = NULL;
434 /* char *type_instance; */
435 const char *tc_type;
436 char tc_inst[DATA_MAX_NAME_LEN];
438 _Bool stats_submitted = 0;
440 if (nlh->nlmsg_type == RTM_NEWQDISC)
441 tc_type = "qdisc";
442 else if (nlh->nlmsg_type == RTM_NEWTCLASS)
443 tc_type = "class";
444 else if (nlh->nlmsg_type == RTM_NEWTFILTER)
445 tc_type = "filter";
446 else {
447 ERROR("netlink plugin: qos_filter_cb: Don't know how to handle type %i.",
448 nlh->nlmsg_type);
449 return MNL_CB_ERROR;
450 }
452 if (tm->tcm_ifindex != wanted_ifindex) {
453 DEBUG("netlink plugin: qos_filter_cb: Got %s for interface #%i, "
454 "but expected #%i.",
455 tc_type, tm->tcm_ifindex, wanted_ifindex);
456 return MNL_CB_OK;
457 }
459 if ((tm->tcm_ifindex >= 0) && ((size_t)tm->tcm_ifindex >= iflist_len)) {
460 ERROR("netlink plugin: qos_filter_cb: tm->tcm_ifindex = %i "
461 ">= iflist_len = %zu",
462 tm->tcm_ifindex, iflist_len);
463 return MNL_CB_ERROR;
464 }
466 dev = iflist[tm->tcm_ifindex];
467 if (dev == NULL) {
468 ERROR("netlink plugin: qos_filter_cb: iflist[%i] == NULL", tm->tcm_ifindex);
469 return MNL_CB_ERROR;
470 }
472 mnl_attr_for_each(attr, nlh, sizeof(*tm)) {
473 if (mnl_attr_get_type(attr) != TCA_KIND)
474 continue;
476 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
477 ERROR(
478 "netlink plugin: qos_filter_cb: TCA_KIND mnl_attr_validate failed.");
479 return MNL_CB_ERROR;
480 }
482 kind = mnl_attr_get_str(attr);
483 break;
484 }
486 if (kind == NULL) {
487 ERROR("netlink plugin: qos_filter_cb: kind == NULL");
488 return (-1);
489 }
491 { /* The ID */
492 uint32_t numberic_id;
494 numberic_id = tm->tcm_handle;
495 if (strcmp(tc_type, "filter") == 0)
496 numberic_id = tm->tcm_parent;
498 ssnprintf(tc_inst, sizeof(tc_inst), "%s-%x:%x", kind, numberic_id >> 16,
499 numberic_id & 0x0000FFFF);
500 }
502 DEBUG("netlink plugin: qos_filter_cb: got %s for %s (%i).", tc_type, dev,
503 tm->tcm_ifindex);
505 if (check_ignorelist(dev, tc_type, tc_inst))
506 return MNL_CB_OK;
508 #if HAVE_TCA_STATS2
509 mnl_attr_for_each(attr, nlh, sizeof(*tm)) {
510 struct qos_stats q_stats;
512 memset(&q_stats, 0x0, sizeof(q_stats));
514 if (mnl_attr_get_type(attr) != TCA_STATS2)
515 continue;
517 if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
518 ERROR("netlink plugin: qos_filter_cb: TCA_STATS2 mnl_attr_validate "
519 "failed.");
520 return MNL_CB_ERROR;
521 }
523 mnl_attr_parse_nested(attr, qos_attr_cb, &q_stats);
525 if (q_stats.bs != NULL || q_stats.qs != NULL) {
526 char type_instance[DATA_MAX_NAME_LEN];
528 stats_submitted = 1;
530 ssnprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type,
531 tc_inst);
533 if (q_stats.bs != NULL) {
534 submit_one(dev, "ipt_bytes", type_instance, q_stats.bs->bytes);
535 submit_one(dev, "ipt_packets", type_instance, q_stats.bs->packets);
536 }
537 if (q_stats.qs != NULL) {
538 submit_one(dev, "if_tx_dropped", type_instance, q_stats.qs->drops);
539 }
540 }
542 break;
543 }
544 #endif /* TCA_STATS2 */
546 #if HAVE_TCA_STATS
547 mnl_attr_for_each(attr, nlh, sizeof(*tm)) {
548 struct tc_stats *ts = NULL;
550 if (mnl_attr_get_type(attr) != TCA_STATS)
551 continue;
553 if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*ts)) < 0) {
554 ERROR("netlink plugin: qos_filter_cb: TCA_STATS mnl_attr_validate2 "
555 "failed.");
556 return MNL_CB_ERROR;
557 }
558 ts = mnl_attr_get_payload(attr);
560 if (!stats_submitted && ts != NULL) {
561 char type_instance[DATA_MAX_NAME_LEN];
563 ssnprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type,
564 tc_inst);
566 submit_one(dev, "ipt_bytes", type_instance, ts->bytes);
567 submit_one(dev, "ipt_packets", type_instance, ts->packets);
568 }
570 break;
571 }
573 #endif /* TCA_STATS */
575 #if !(HAVE_TCA_STATS && HAVE_TCA_STATS2)
576 DEBUG("netlink plugin: qos_filter_cb: Have neither TCA_STATS2 nor "
577 "TCA_STATS.");
578 #endif
580 return MNL_CB_OK;
581 } /* int qos_filter_cb */
583 static int ir_config(const char *key, const char *value) {
584 char *new_val;
585 char *fields[8];
586 int fields_num;
587 int status = 1;
589 new_val = strdup(value);
590 if (new_val == NULL)
591 return (-1);
593 fields_num = strsplit(new_val, fields, STATIC_ARRAY_SIZE(fields));
594 if ((fields_num < 1) || (fields_num > 8)) {
595 sfree(new_val);
596 return (-1);
597 }
599 if ((strcasecmp(key, "Interface") == 0) ||
600 (strcasecmp(key, "VerboseInterface") == 0)) {
601 if (fields_num != 1) {
602 ERROR("netlink plugin: Invalid number of fields for option "
603 "`%s'. Got %i, expected 1.",
604 key, fields_num);
605 status = -1;
606 } else {
607 add_ignorelist(fields[0], "interface", NULL);
608 if (strcasecmp(key, "VerboseInterface") == 0)
609 add_ignorelist(fields[0], "if_detail", NULL);
610 status = 0;
611 }
612 } else if ((strcasecmp(key, "QDisc") == 0) ||
613 (strcasecmp(key, "Class") == 0) ||
614 (strcasecmp(key, "Filter") == 0)) {
615 if ((fields_num < 1) || (fields_num > 2)) {
616 ERROR("netlink plugin: Invalid number of fields for option "
617 "`%s'. Got %i, expected 1 or 2.",
618 key, fields_num);
619 return (-1);
620 } else {
621 add_ignorelist(fields[0], key, (fields_num == 2) ? fields[1] : NULL);
622 status = 0;
623 }
624 } else if (strcasecmp(key, "IgnoreSelected") == 0) {
625 if (fields_num != 1) {
626 ERROR("netlink plugin: Invalid number of fields for option "
627 "`IgnoreSelected'. Got %i, expected 1.",
628 fields_num);
629 status = -1;
630 } else {
631 if (IS_TRUE(fields[0]))
632 ir_ignorelist_invert = 0;
633 else
634 ir_ignorelist_invert = 1;
635 status = 0;
636 }
637 }
639 sfree(new_val);
641 return (status);
642 } /* int ir_config */
644 static int ir_init(void) {
645 nl = mnl_socket_open(NETLINK_ROUTE);
646 if (nl == NULL) {
647 ERROR("netlink plugin: ir_init: mnl_socket_open failed.");
648 return (-1);
649 }
651 if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
652 ERROR("netlink plugin: ir_init: mnl_socket_bind failed.");
653 return (-1);
654 }
656 return (0);
657 } /* int ir_init */
659 static int ir_read(void) {
660 char buf[MNL_SOCKET_BUFFER_SIZE];
661 struct nlmsghdr *nlh;
662 struct rtgenmsg *rt;
663 int ret;
664 unsigned int seq, portid;
666 static const int type_id[] = {RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER};
667 static const char *type_name[] = {"qdisc", "class", "filter"};
669 portid = mnl_socket_get_portid(nl);
671 nlh = mnl_nlmsg_put_header(buf);
672 nlh->nlmsg_type = RTM_GETLINK;
673 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
674 nlh->nlmsg_seq = seq = time(NULL);
675 rt = mnl_nlmsg_put_extra_header(nlh, sizeof(*rt));
676 rt->rtgen_family = AF_PACKET;
678 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
679 ERROR("netlink plugin: ir_read: rtnl_wilddump_request failed.");
680 return (-1);
681 }
683 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
684 while (ret > 0) {
685 ret = mnl_cb_run(buf, ret, seq, portid, link_filter_cb, NULL);
686 if (ret <= MNL_CB_STOP)
687 break;
688 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
689 }
690 if (ret < 0) {
691 ERROR("netlink plugin: ir_read: mnl_socket_recvfrom failed.");
692 return (-1);
693 }
695 /* `link_filter_cb' will update `iflist' which is used here to iterate
696 * over all interfaces. */
697 for (size_t ifindex = 1; ifindex < iflist_len; ifindex++) {
698 struct tcmsg *tm;
700 if (iflist[ifindex] == NULL)
701 continue;
703 for (size_t type_index = 0; type_index < STATIC_ARRAY_SIZE(type_id);
704 type_index++) {
705 if (check_ignorelist(iflist[ifindex], type_name[type_index], NULL)) {
706 DEBUG("netlink plugin: ir_read: check_ignorelist (%s, %s, (nil)) "
707 "== TRUE",
708 iflist[ifindex], type_name[type_index]);
709 continue;
710 }
712 DEBUG("netlink plugin: ir_read: querying %s from %s (%zu).",
713 type_name[type_index], iflist[ifindex], ifindex);
715 nlh = mnl_nlmsg_put_header(buf);
716 nlh->nlmsg_type = type_id[type_index];
717 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
718 nlh->nlmsg_seq = seq = time(NULL);
719 tm = mnl_nlmsg_put_extra_header(nlh, sizeof(*tm));
720 tm->tcm_family = AF_PACKET;
721 tm->tcm_ifindex = ifindex;
723 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
724 ERROR("netlink plugin: ir_read: mnl_socket_sendto failed.");
725 continue;
726 }
728 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
729 while (ret > 0) {
730 ret = mnl_cb_run(buf, ret, seq, portid, qos_filter_cb, &ifindex);
731 if (ret <= MNL_CB_STOP)
732 break;
733 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
734 }
735 if (ret < 0) {
736 ERROR("netlink plugin: ir_read:mnl_socket_recvfrom failed.");
737 continue;
738 }
740 } /* for (type_index) */
741 } /* for (if_index) */
743 return (0);
744 } /* int ir_read */
746 static int ir_shutdown(void) {
747 if (nl) {
748 mnl_socket_close(nl);
749 nl = NULL;
750 }
752 return (0);
753 } /* int ir_shutdown */
755 void module_register(void) {
756 plugin_register_config("netlink", ir_config, config_keys, config_keys_num);
757 plugin_register_init("netlink", ir_init);
758 plugin_register_read("netlink", ir_read);
759 plugin_register_shutdown("netlink", ir_shutdown);
760 } /* void module_register */