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 snprintf(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 snprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type, tc_inst);
532 if (q_stats.bs != NULL) {
533 submit_one(dev, "ipt_bytes", type_instance, q_stats.bs->bytes);
534 submit_one(dev, "ipt_packets", type_instance, q_stats.bs->packets);
535 }
536 if (q_stats.qs != NULL) {
537 submit_one(dev, "if_tx_dropped", type_instance, q_stats.qs->drops);
538 }
539 }
541 break;
542 }
543 #endif /* TCA_STATS2 */
545 #if HAVE_TCA_STATS
546 mnl_attr_for_each(attr, nlh, sizeof(*tm)) {
547 struct tc_stats *ts = NULL;
549 if (mnl_attr_get_type(attr) != TCA_STATS)
550 continue;
552 if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*ts)) < 0) {
553 ERROR("netlink plugin: qos_filter_cb: TCA_STATS mnl_attr_validate2 "
554 "failed.");
555 return MNL_CB_ERROR;
556 }
557 ts = mnl_attr_get_payload(attr);
559 if (!stats_submitted && ts != NULL) {
560 char type_instance[DATA_MAX_NAME_LEN];
562 snprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type, tc_inst);
564 submit_one(dev, "ipt_bytes", type_instance, ts->bytes);
565 submit_one(dev, "ipt_packets", type_instance, ts->packets);
566 }
568 break;
569 }
571 #endif /* TCA_STATS */
573 #if !(HAVE_TCA_STATS && HAVE_TCA_STATS2)
574 DEBUG("netlink plugin: qos_filter_cb: Have neither TCA_STATS2 nor "
575 "TCA_STATS.");
576 #endif
578 return MNL_CB_OK;
579 } /* int qos_filter_cb */
581 static int ir_config(const char *key, const char *value) {
582 char *new_val;
583 char *fields[8];
584 int fields_num;
585 int status = 1;
587 new_val = strdup(value);
588 if (new_val == NULL)
589 return -1;
591 fields_num = strsplit(new_val, fields, STATIC_ARRAY_SIZE(fields));
592 if ((fields_num < 1) || (fields_num > 8)) {
593 sfree(new_val);
594 return -1;
595 }
597 if ((strcasecmp(key, "Interface") == 0) ||
598 (strcasecmp(key, "VerboseInterface") == 0)) {
599 if (fields_num != 1) {
600 ERROR("netlink plugin: Invalid number of fields for option "
601 "`%s'. Got %i, expected 1.",
602 key, fields_num);
603 status = -1;
604 } else {
605 add_ignorelist(fields[0], "interface", NULL);
606 if (strcasecmp(key, "VerboseInterface") == 0)
607 add_ignorelist(fields[0], "if_detail", NULL);
608 status = 0;
609 }
610 } else if ((strcasecmp(key, "QDisc") == 0) ||
611 (strcasecmp(key, "Class") == 0) ||
612 (strcasecmp(key, "Filter") == 0)) {
613 if ((fields_num < 1) || (fields_num > 2)) {
614 ERROR("netlink plugin: Invalid number of fields for option "
615 "`%s'. Got %i, expected 1 or 2.",
616 key, fields_num);
617 return -1;
618 } else {
619 add_ignorelist(fields[0], key, (fields_num == 2) ? fields[1] : NULL);
620 status = 0;
621 }
622 } else if (strcasecmp(key, "IgnoreSelected") == 0) {
623 if (fields_num != 1) {
624 ERROR("netlink plugin: Invalid number of fields for option "
625 "`IgnoreSelected'. Got %i, expected 1.",
626 fields_num);
627 status = -1;
628 } else {
629 if (IS_TRUE(fields[0]))
630 ir_ignorelist_invert = 0;
631 else
632 ir_ignorelist_invert = 1;
633 status = 0;
634 }
635 }
637 sfree(new_val);
639 return status;
640 } /* int ir_config */
642 static int ir_init(void) {
643 nl = mnl_socket_open(NETLINK_ROUTE);
644 if (nl == NULL) {
645 ERROR("netlink plugin: ir_init: mnl_socket_open failed.");
646 return -1;
647 }
649 if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
650 ERROR("netlink plugin: ir_init: mnl_socket_bind failed.");
651 return -1;
652 }
654 return 0;
655 } /* int ir_init */
657 static int ir_read(void) {
658 char buf[MNL_SOCKET_BUFFER_SIZE];
659 struct nlmsghdr *nlh;
660 struct rtgenmsg *rt;
661 int ret;
662 unsigned int seq, portid;
664 static const int type_id[] = {RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER};
665 static const char *type_name[] = {"qdisc", "class", "filter"};
667 portid = mnl_socket_get_portid(nl);
669 nlh = mnl_nlmsg_put_header(buf);
670 nlh->nlmsg_type = RTM_GETLINK;
671 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
672 nlh->nlmsg_seq = seq = time(NULL);
673 rt = mnl_nlmsg_put_extra_header(nlh, sizeof(*rt));
674 rt->rtgen_family = AF_PACKET;
676 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
677 ERROR("netlink plugin: ir_read: rtnl_wilddump_request failed.");
678 return -1;
679 }
681 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
682 while (ret > 0) {
683 ret = mnl_cb_run(buf, ret, seq, portid, link_filter_cb, NULL);
684 if (ret <= MNL_CB_STOP)
685 break;
686 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
687 }
688 if (ret < 0) {
689 ERROR("netlink plugin: ir_read: mnl_socket_recvfrom failed.");
690 return -1;
691 }
693 /* `link_filter_cb' will update `iflist' which is used here to iterate
694 * over all interfaces. */
695 for (size_t ifindex = 1; ifindex < iflist_len; ifindex++) {
696 struct tcmsg *tm;
698 if (iflist[ifindex] == NULL)
699 continue;
701 for (size_t type_index = 0; type_index < STATIC_ARRAY_SIZE(type_id);
702 type_index++) {
703 if (check_ignorelist(iflist[ifindex], type_name[type_index], NULL)) {
704 DEBUG("netlink plugin: ir_read: check_ignorelist (%s, %s, (nil)) "
705 "== TRUE",
706 iflist[ifindex], type_name[type_index]);
707 continue;
708 }
710 DEBUG("netlink plugin: ir_read: querying %s from %s (%zu).",
711 type_name[type_index], iflist[ifindex], ifindex);
713 nlh = mnl_nlmsg_put_header(buf);
714 nlh->nlmsg_type = type_id[type_index];
715 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
716 nlh->nlmsg_seq = seq = time(NULL);
717 tm = mnl_nlmsg_put_extra_header(nlh, sizeof(*tm));
718 tm->tcm_family = AF_PACKET;
719 tm->tcm_ifindex = ifindex;
721 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
722 ERROR("netlink plugin: ir_read: mnl_socket_sendto failed.");
723 continue;
724 }
726 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
727 while (ret > 0) {
728 ret = mnl_cb_run(buf, ret, seq, portid, qos_filter_cb, &ifindex);
729 if (ret <= MNL_CB_STOP)
730 break;
731 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
732 }
733 if (ret < 0) {
734 ERROR("netlink plugin: ir_read:mnl_socket_recvfrom failed.");
735 continue;
736 }
738 } /* for (type_index) */
739 } /* for (if_index) */
741 return 0;
742 } /* int ir_read */
744 static int ir_shutdown(void) {
745 if (nl) {
746 mnl_socket_close(nl);
747 nl = NULL;
748 }
750 return 0;
751 } /* int ir_shutdown */
753 void module_register(void) {
754 plugin_register_config("netlink", ir_config, config_keys, config_keys_num);
755 plugin_register_init("netlink", ir_init);
756 plugin_register_read("netlink", ir_read);
757 plugin_register_shutdown("netlink", ir_shutdown);
758 } /* void module_register */