src/daemon/plugin.h: Enlarge the DATA_MAX_NAME_LEN.
[collectd.git] / src / netlink.c
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  **/
27
28 #include "collectd.h"
29
30 #include "plugin.h"
31 #include "common.h"
32
33 #include <asm/types.h>
34
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
43
44 #include <libmnl/libmnl.h>
45
46 struct ir_link_stats_storage_s {
47
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;
54
55   uint64_t rx_dropped;
56   uint64_t tx_dropped;
57   uint64_t multicast;
58   uint64_t collisions;
59
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;
66
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 };
73
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 };
80
81 typedef struct ir_ignorelist_s
82 {
83   char *device;
84   char *type;
85   char *inst;
86   struct ir_ignorelist_s *next;
87 } ir_ignorelist_t;
88
89 static int ir_ignorelist_invert = 1;
90 static ir_ignorelist_t *ir_ignorelist_head = NULL;
91
92 static struct mnl_socket *nl;
93
94 static char **iflist = NULL;
95 static size_t iflist_len = 0;
96
97 static const char *config_keys[] =
98 {
99         "Interface",
100         "VerboseInterface",
101         "QDisc",
102         "Class",
103         "Filter",
104         "IgnoreSelected"
105 };
106 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
107
108 static int add_ignorelist (const char *dev, const char *type,
109     const char *inst)
110 {
111   ir_ignorelist_t *entry;
112
113   entry = calloc (1, sizeof (*entry));
114   if (entry == NULL)
115     return (-1);
116
117   if (strcasecmp (dev, "All") != 0)
118   {
119     entry->device = strdup (dev);
120     if (entry->device == NULL)
121     {
122       sfree (entry);
123       return (-1);
124     }
125   }
126
127   entry->type = strdup (type);
128   if (entry->type == NULL)
129   {
130     sfree (entry->device);
131     sfree (entry);
132     return (-1);
133   }
134
135   if (inst != NULL)
136   {
137     entry->inst = strdup (inst);
138     if (entry->inst == NULL)
139     {
140       sfree (entry->type);
141       sfree (entry->device);
142       sfree (entry);
143       return (-1);
144     }
145   }
146
147   entry->next = ir_ignorelist_head;
148   ir_ignorelist_head = entry;
149
150   return (0);
151 } /* int add_ignorelist */
152
153 /*
154  * Checks wether a data set should be ignored. Returns `true' is the value
155  * should be ignored, `false' otherwise.
156  */
157 static int check_ignorelist (const char *dev,
158     const char *type, const char *type_instance)
159 {
160   ir_ignorelist_t *i;
161
162   assert ((dev != NULL) && (type != NULL));
163
164   if (ir_ignorelist_head == NULL)
165     return (ir_ignorelist_invert ? 0 : 1);
166
167   for (i = ir_ignorelist_head; i != NULL; i = i->next)
168   {
169     /* i->device == NULL  =>  match all devices */
170     if ((i->device != NULL)
171         && (strcasecmp (i->device, dev) != 0))
172       continue;
173
174     if (strcasecmp (i->type, type) != 0)
175       continue;
176
177     if ((i->inst != NULL) && (type_instance != NULL)
178         && (strcasecmp (i->inst, type_instance) != 0))
179       continue;
180
181     DEBUG ("netlink plugin: check_ignorelist: "
182         "(dev = %s; type = %s; inst = %s) matched "
183         "(dev = %s; type = %s; inst = %s)",
184         dev, type,
185         type_instance == NULL ? "(nil)" : type_instance,
186         i->device == NULL ? "(nil)" : i->device,
187         i->type,
188         i->inst == NULL ? "(nil)" : i->inst);
189
190     return (ir_ignorelist_invert ? 0 : 1);
191   } /* for i */
192
193   return (ir_ignorelist_invert);
194 } /* int check_ignorelist */
195
196 static void submit_one (const char *dev, const char *type,
197     const char *type_instance, derive_t value)
198 {
199   value_t values[1];
200   value_list_t vl = VALUE_LIST_INIT;
201
202   values[0].derive = value;
203
204   vl.values = values;
205   vl.values_len = 1;
206   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
207   sstrncpy (vl.plugin, "netlink", sizeof (vl.plugin));
208   sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance));
209   sstrncpy (vl.type, type, sizeof (vl.type));
210
211   if (type_instance != NULL)
212     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
213
214   plugin_dispatch_values (&vl);
215 } /* void submit_one */
216
217 static void submit_two (const char *dev, const char *type,
218     const char *type_instance,
219     derive_t rx, derive_t tx)
220 {
221   value_t values[2];
222   value_list_t vl = VALUE_LIST_INIT;
223
224   values[0].derive = rx;
225   values[1].derive = tx;
226
227   vl.values = values;
228   vl.values_len = 2;
229   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
230   sstrncpy (vl.plugin, "netlink", sizeof (vl.plugin));
231   sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance));
232   sstrncpy (vl.type, type, sizeof (vl.type));
233
234   if (type_instance != NULL)
235     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
236
237   plugin_dispatch_values (&vl);
238 } /* void submit_two */
239
240 static int update_iflist (struct ifinfomsg *msg, const char *dev)
241 {
242   /* Update the `iflist'. It's used to know which interfaces exist and query
243    * them later for qdiscs and classes. */
244   if ((msg->ifi_index >= 0) && ((size_t) msg->ifi_index >= iflist_len))
245   {
246     char **temp;
247
248     temp = realloc (iflist, (msg->ifi_index + 1) * sizeof (char *));
249     if (temp == NULL)
250     {
251       ERROR ("netlink plugin: update_iflist: realloc failed.");
252       return (-1);
253     }
254
255     memset (temp + iflist_len, '\0',
256         (msg->ifi_index + 1 - iflist_len) * sizeof (char *));
257     iflist = temp;
258     iflist_len = msg->ifi_index + 1;
259   }
260   if ((iflist[msg->ifi_index] == NULL)
261       || (strcmp (iflist[msg->ifi_index], dev) != 0))
262   {
263     sfree (iflist[msg->ifi_index]);
264     iflist[msg->ifi_index] = strdup (dev);
265   }
266
267   return (0);
268 } /* int update_iflist */
269
270 static void check_ignorelist_and_submit (const char *dev,
271     struct ir_link_stats_storage_s *stats)
272 {
273
274   if (check_ignorelist (dev, "interface", NULL) == 0)
275   {
276     submit_two (dev, "if_octets", NULL, stats->rx_bytes, stats->tx_bytes);
277     submit_two (dev, "if_packets", NULL, stats->rx_packets, stats->tx_packets);
278     submit_two (dev, "if_errors", NULL, stats->rx_errors, stats->tx_errors);
279   }
280   else
281   {
282     DEBUG ("netlink plugin: Ignoring %s/interface.", dev);
283   }
284
285   if (check_ignorelist (dev, "if_detail", NULL) == 0)
286   {
287     submit_two (dev, "if_dropped", NULL, stats->rx_dropped, stats->tx_dropped);
288     submit_one (dev, "if_multicast", NULL, stats->multicast);
289     submit_one (dev, "if_collisions", NULL, stats->collisions);
290
291     submit_one (dev, "if_rx_errors", "length", stats->rx_length_errors);
292     submit_one (dev, "if_rx_errors", "over", stats->rx_over_errors);
293     submit_one (dev, "if_rx_errors", "crc", stats->rx_crc_errors);
294     submit_one (dev, "if_rx_errors", "frame", stats->rx_frame_errors);
295     submit_one (dev, "if_rx_errors", "fifo", stats->rx_fifo_errors);
296     submit_one (dev, "if_rx_errors", "missed", stats->rx_missed_errors);
297
298     submit_one (dev, "if_tx_errors", "aborted", stats->tx_aborted_errors);
299     submit_one (dev, "if_tx_errors", "carrier", stats->tx_carrier_errors);
300     submit_one (dev, "if_tx_errors", "fifo", stats->tx_fifo_errors);
301     submit_one (dev, "if_tx_errors", "heartbeat", stats->tx_heartbeat_errors);
302     submit_one (dev, "if_tx_errors", "window", stats->tx_window_errors);
303   }
304   else
305   {
306     DEBUG ("netlink plugin: Ignoring %s/if_detail.", dev);
307   }
308
309 } /* void check_ignorelist_and_submit */
310
311 #define COPY_RTNL_LINK_VALUE(dst_stats, src_stats, value_name) \
312   (dst_stats)->value_name = (src_stats)->value_name
313
314 #define COPY_RTNL_LINK_STATS(dst_stats, src_stats) \
315   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_packets); \
316   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_packets); \
317   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_bytes); \
318   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_bytes); \
319   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_errors); \
320   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_errors); \
321   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_dropped); \
322   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_dropped); \
323   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, multicast); \
324   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, collisions); \
325   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_length_errors); \
326   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_over_errors); \
327   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_crc_errors); \
328   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_frame_errors); \
329   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_fifo_errors); \
330   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_missed_errors); \
331   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_aborted_errors); \
332   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_carrier_errors); \
333   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_fifo_errors); \
334   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_heartbeat_errors); \
335   COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_window_errors)
336
337 #ifdef HAVE_RTNL_LINK_STATS64
338 static void check_ignorelist_and_submit64 (const char *dev,
339     struct rtnl_link_stats64 *stats)
340 {
341   struct ir_link_stats_storage_s s;
342
343   COPY_RTNL_LINK_STATS (&s, stats);
344
345   check_ignorelist_and_submit (dev, &s);
346 }
347 #endif
348
349 static void check_ignorelist_and_submit32 (const char *dev,
350     struct rtnl_link_stats *stats)
351 {
352   struct ir_link_stats_storage_s s;
353
354   COPY_RTNL_LINK_STATS(&s, stats);
355
356   check_ignorelist_and_submit (dev, &s);
357 }
358
359 static int link_filter_cb (const struct nlmsghdr *nlh,
360     void *args __attribute__((unused)))
361 {
362   struct ifinfomsg *ifm = mnl_nlmsg_get_payload (nlh);
363   struct nlattr *attr;
364   const char *dev = NULL;
365   union ir_link_stats_u stats;
366
367   if (nlh->nlmsg_type != RTM_NEWLINK)
368   {
369     ERROR ("netlink plugin: link_filter_cb: Don't know how to handle type %i.",
370         nlh->nlmsg_type);
371     return MNL_CB_ERROR;
372   }
373
374   /* Scan attribute list for device name. */
375   mnl_attr_for_each (attr, nlh, sizeof (*ifm))
376   {
377     if (mnl_attr_get_type (attr) != IFLA_IFNAME)
378       continue;
379
380     if (mnl_attr_validate (attr, MNL_TYPE_STRING) < 0)
381     {
382       ERROR ("netlink plugin: link_filter_cb: IFLA_IFNAME mnl_attr_validate failed.");
383       return MNL_CB_ERROR;
384     }
385
386     dev = mnl_attr_get_str (attr);
387     if (update_iflist (ifm, dev) < 0)
388       return MNL_CB_ERROR;
389     break;
390   }
391
392   if (dev == NULL)
393   {
394     ERROR ("netlink plugin: link_filter_cb: dev == NULL");
395     return MNL_CB_ERROR;
396   }
397 #ifdef HAVE_RTNL_LINK_STATS64
398   mnl_attr_for_each (attr, nlh, sizeof (*ifm))
399   {
400     if (mnl_attr_get_type (attr) != IFLA_STATS64)
401       continue;
402
403     if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*stats.stats64)) < 0)
404     {
405       ERROR ("netlink plugin: link_filter_cb: IFLA_STATS64 mnl_attr_validate2 failed.");
406       return MNL_CB_ERROR;
407     }
408     stats.stats64 = mnl_attr_get_payload (attr);
409
410     check_ignorelist_and_submit64 (dev, stats.stats64);
411
412     return MNL_CB_OK;
413   }
414 #endif
415   mnl_attr_for_each (attr, nlh, sizeof (*ifm))
416   {
417     if (mnl_attr_get_type (attr) != IFLA_STATS)
418       continue;
419
420     if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*stats.stats32)) < 0)
421     {
422       ERROR ("netlink plugin: link_filter_cb: IFLA_STATS mnl_attr_validate2 failed.");
423       return MNL_CB_ERROR;
424     }
425     stats.stats32 = mnl_attr_get_payload (attr);
426
427     check_ignorelist_and_submit32 (dev, stats.stats32);
428
429     return MNL_CB_OK;
430   }
431
432   DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev);
433   return MNL_CB_OK;
434
435 } /* int link_filter_cb */
436
437 #if HAVE_TCA_STATS2
438 static int qos_attr_cb (const struct nlattr *attr, void *data)
439 {
440   struct gnet_stats_basic **bs = (struct gnet_stats_basic **)data;
441
442   /* skip unsupported attribute in user-space */
443   if (mnl_attr_type_valid (attr, TCA_STATS_MAX) < 0)
444     return MNL_CB_OK;
445
446   if (mnl_attr_get_type (attr) == TCA_STATS_BASIC)
447   {
448     if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (**bs)) < 0)
449     {
450       ERROR ("netlink plugin: qos_attr_cb: TCA_STATS_BASIC mnl_attr_validate2 failed.");
451       return MNL_CB_ERROR;
452     }
453     *bs = mnl_attr_get_payload (attr);
454     return MNL_CB_STOP;
455   }
456
457   return MNL_CB_OK;
458 } /* qos_attr_cb */
459 #endif
460
461 static int qos_filter_cb (const struct nlmsghdr *nlh, void *args)
462 {
463   struct tcmsg *tm = mnl_nlmsg_get_payload (nlh);
464   struct nlattr *attr;
465
466   int wanted_ifindex = *((int *) args);
467
468   const char *dev;
469   const char *kind = NULL;
470
471   /* char *type_instance; */
472   const char *tc_type;
473   char tc_inst[DATA_MAX_NAME_LEN];
474
475   _Bool stats_submitted = 0;
476
477   if (nlh->nlmsg_type == RTM_NEWQDISC)
478     tc_type = "qdisc";
479   else if (nlh->nlmsg_type == RTM_NEWTCLASS)
480     tc_type = "class";
481   else if (nlh->nlmsg_type == RTM_NEWTFILTER)
482     tc_type = "filter";
483   else
484   {
485     ERROR ("netlink plugin: qos_filter_cb: Don't know how to handle type %i.",
486         nlh->nlmsg_type);
487     return MNL_CB_ERROR;
488   }
489
490   if (tm->tcm_ifindex != wanted_ifindex)
491   {
492     DEBUG ("netlink plugin: qos_filter_cb: Got %s for interface #%i, "
493         "but expected #%i.",
494         tc_type, tm->tcm_ifindex, wanted_ifindex);
495     return MNL_CB_OK;
496   }
497
498   if ((tm->tcm_ifindex >= 0)
499       && ((size_t) tm->tcm_ifindex >= iflist_len))
500   {
501     ERROR ("netlink plugin: qos_filter_cb: tm->tcm_ifindex = %i "
502         ">= iflist_len = %zu",
503         tm->tcm_ifindex, iflist_len);
504     return MNL_CB_ERROR;
505   }
506
507   dev = iflist[tm->tcm_ifindex];
508   if (dev == NULL)
509   {
510     ERROR ("netlink plugin: qos_filter_cb: iflist[%i] == NULL",
511         tm->tcm_ifindex);
512     return MNL_CB_ERROR;
513   }
514
515   mnl_attr_for_each (attr, nlh, sizeof (*tm))
516   {
517     if (mnl_attr_get_type (attr) != TCA_KIND)
518       continue;
519
520     if (mnl_attr_validate (attr, MNL_TYPE_STRING) < 0)
521     {
522       ERROR ("netlink plugin: qos_filter_cb: TCA_KIND mnl_attr_validate failed.");
523       return MNL_CB_ERROR;
524     }
525
526     kind = mnl_attr_get_str (attr);
527     break;
528   }
529
530   if (kind == NULL)
531   {
532     ERROR ("netlink plugin: qos_filter_cb: kind == NULL");
533     return (-1);
534   }
535
536   { /* The ID */
537     uint32_t numberic_id;
538
539     numberic_id = tm->tcm_handle;
540     if (strcmp (tc_type, "filter") == 0)
541       numberic_id = tm->tcm_parent;
542
543     ssnprintf (tc_inst, sizeof (tc_inst), "%s-%x:%x",
544         kind,
545         numberic_id >> 16,
546         numberic_id & 0x0000FFFF);
547   }
548
549   DEBUG ("netlink plugin: qos_filter_cb: got %s for %s (%i).",
550       tc_type, dev, tm->tcm_ifindex);
551
552   if (check_ignorelist (dev, tc_type, tc_inst))
553     return MNL_CB_OK;
554
555 #if HAVE_TCA_STATS2
556   mnl_attr_for_each (attr, nlh, sizeof (*tm))
557   {
558     struct gnet_stats_basic *bs = NULL;
559
560     if (mnl_attr_get_type (attr) != TCA_STATS2)
561       continue;
562
563     if (mnl_attr_validate (attr, MNL_TYPE_NESTED) < 0)
564     {
565       ERROR ("netlink plugin: qos_filter_cb: TCA_STATS2 mnl_attr_validate failed.");
566       return MNL_CB_ERROR;
567     }
568
569     mnl_attr_parse_nested (attr, qos_attr_cb, &bs);
570
571     if (bs != NULL)
572     {
573       char type_instance[DATA_MAX_NAME_LEN];
574
575       stats_submitted = 1;
576
577       ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
578           tc_type, tc_inst);
579
580       submit_one (dev, "ipt_bytes", type_instance, bs->bytes);
581       submit_one (dev, "ipt_packets", type_instance, bs->packets);
582     }
583
584     break;
585   }
586 #endif /* TCA_STATS2 */
587
588 #if HAVE_TCA_STATS
589   mnl_attr_for_each (attr, nlh, sizeof (*tm))
590   {
591     struct tc_stats *ts = NULL;
592
593     if (mnl_attr_get_type (attr) != TCA_STATS)
594       continue;
595
596     if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*ts)) < 0)
597     {
598       ERROR ("netlink plugin: qos_filter_cb: TCA_STATS mnl_attr_validate2 failed.");
599       return MNL_CB_ERROR;
600     }
601     ts = mnl_attr_get_payload (attr);
602
603     if (!stats_submitted && ts != NULL)
604     {
605       char type_instance[DATA_MAX_NAME_LEN];
606
607       ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
608           tc_type, tc_inst);
609
610       submit_one (dev, "ipt_bytes", type_instance, ts->bytes);
611       submit_one (dev, "ipt_packets", type_instance, ts->packets);
612     }
613
614     break;
615   }
616
617 #endif /* TCA_STATS */
618
619 #if !(HAVE_TCA_STATS && HAVE_TCA_STATS2)
620   DEBUG ("netlink plugin: qos_filter_cb: Have neither TCA_STATS2 nor "
621       "TCA_STATS.");
622 #endif
623
624   return MNL_CB_OK;
625 } /* int qos_filter_cb */
626
627 static int ir_config (const char *key, const char *value)
628 {
629   char *new_val;
630   char *fields[8];
631   int fields_num;
632   int status = 1;
633
634   new_val = strdup (value);
635   if (new_val == NULL)
636     return (-1);
637
638   fields_num = strsplit (new_val, fields, STATIC_ARRAY_SIZE (fields));
639   if ((fields_num < 1) || (fields_num > 8))
640   {
641     sfree (new_val);
642     return (-1);
643   }
644
645   if ((strcasecmp (key, "Interface") == 0)
646       || (strcasecmp (key, "VerboseInterface") == 0))
647   {
648     if (fields_num != 1)
649     {
650       ERROR ("netlink plugin: Invalid number of fields for option "
651           "`%s'. Got %i, expected 1.", key, fields_num);
652       status = -1;
653     }
654     else
655     {
656       add_ignorelist (fields[0], "interface", NULL);
657       if (strcasecmp (key, "VerboseInterface") == 0)
658         add_ignorelist (fields[0], "if_detail", NULL);
659       status = 0;
660     }
661   }
662   else if ((strcasecmp (key, "QDisc") == 0)
663       || (strcasecmp (key, "Class") == 0)
664       || (strcasecmp (key, "Filter") == 0))
665   {
666     if ((fields_num < 1) || (fields_num > 2))
667     {
668       ERROR ("netlink plugin: Invalid number of fields for option "
669           "`%s'. Got %i, expected 1 or 2.", key, fields_num);
670       return (-1);
671     }
672     else
673     {
674       add_ignorelist (fields[0], key,
675           (fields_num == 2) ? fields[1] : NULL);
676       status = 0;
677     }
678   }
679   else if (strcasecmp (key, "IgnoreSelected") == 0)
680   {
681     if (fields_num != 1)
682     {
683       ERROR ("netlink plugin: Invalid number of fields for option "
684           "`IgnoreSelected'. Got %i, expected 1.", fields_num);
685       status = -1;
686     }
687     else
688     {
689       if (IS_TRUE (fields[0]))
690         ir_ignorelist_invert = 0;
691       else
692         ir_ignorelist_invert = 1;
693       status = 0;
694     }
695   }
696
697   sfree (new_val);
698
699   return (status);
700 } /* int ir_config */
701
702 static int ir_init (void)
703 {
704   nl = mnl_socket_open (NETLINK_ROUTE);
705   if (nl == NULL)
706   {
707     ERROR ("netlink plugin: ir_init: mnl_socket_open failed.");
708     return (-1);
709   }
710
711   if (mnl_socket_bind (nl, 0, MNL_SOCKET_AUTOPID) < 0)
712   {
713     ERROR ("netlink plugin: ir_init: mnl_socket_bind failed.");
714     return (-1);
715   }
716
717   return (0);
718 } /* int ir_init */
719
720 static int ir_read (void)
721 {
722   char buf[MNL_SOCKET_BUFFER_SIZE];
723   struct nlmsghdr *nlh;
724   struct rtgenmsg *rt;
725   int ret;
726   unsigned int seq, portid;
727
728   size_t ifindex;
729
730   static const int type_id[] = { RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER };
731   static const char *type_name[] = { "qdisc", "class", "filter" };
732
733   portid = mnl_socket_get_portid (nl);
734
735   nlh = mnl_nlmsg_put_header (buf);
736   nlh->nlmsg_type = RTM_GETLINK;
737   nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
738   nlh->nlmsg_seq = seq = time (NULL);
739   rt = mnl_nlmsg_put_extra_header (nlh, sizeof (*rt));
740   rt->rtgen_family = AF_PACKET;
741
742   if (mnl_socket_sendto (nl, nlh, nlh->nlmsg_len) < 0)
743   {
744     ERROR ("netlink plugin: ir_read: rtnl_wilddump_request failed.");
745     return (-1);
746   }
747
748   ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
749   while (ret > 0)
750   {
751     ret = mnl_cb_run (buf, ret, seq, portid, link_filter_cb, NULL);
752     if (ret <= MNL_CB_STOP)
753       break;
754     ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
755   }
756   if (ret < 0)
757   {
758     ERROR ("netlink plugin: ir_read: mnl_socket_recvfrom failed.");
759     return (-1);
760   }
761
762   /* `link_filter_cb' will update `iflist' which is used here to iterate
763    * over all interfaces. */
764   for (ifindex = 1; ifindex < iflist_len; ifindex++)
765   {
766     struct tcmsg *tm;
767     size_t type_index;
768
769     if (iflist[ifindex] == NULL)
770       continue;
771
772     for (type_index = 0; type_index < STATIC_ARRAY_SIZE (type_id); type_index++)
773     {
774       if (check_ignorelist (iflist[ifindex], type_name[type_index], NULL))
775       {
776         DEBUG ("netlink plugin: ir_read: check_ignorelist (%s, %s, (nil)) "
777             "== TRUE", iflist[ifindex], type_name[type_index]);
778         continue;
779       }
780
781       DEBUG ("netlink plugin: ir_read: querying %s from %s (%zu).",
782           type_name[type_index], iflist[ifindex], ifindex);
783
784       nlh = mnl_nlmsg_put_header (buf);
785       nlh->nlmsg_type = type_id[type_index];
786       nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
787       nlh->nlmsg_seq = seq = time (NULL);
788       tm = mnl_nlmsg_put_extra_header (nlh, sizeof (*tm));
789       tm->tcm_family = AF_PACKET;
790       tm->tcm_ifindex = ifindex;
791
792       if (mnl_socket_sendto (nl, nlh, nlh->nlmsg_len) < 0)
793       {
794         ERROR ("netlink plugin: ir_read: mnl_socket_sendto failed.");
795         continue;
796       }
797
798       ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
799       while (ret > 0)
800       {
801         ret = mnl_cb_run (buf, ret, seq, portid, qos_filter_cb, &ifindex);
802         if (ret <= MNL_CB_STOP)
803           break;
804         ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
805       }
806       if (ret < 0)
807       {
808         ERROR ("netlink plugin: ir_read:mnl_socket_recvfrom failed.");
809         continue;
810       }
811
812     } /* for (type_index) */
813   } /* for (if_index) */
814
815   return (0);
816 } /* int ir_read */
817
818 static int ir_shutdown (void)
819 {
820   if (nl)
821   {
822     mnl_socket_close (nl);
823     nl = NULL;
824   }
825
826   return (0);
827 } /* int ir_shutdown */
828
829 void module_register (void)
830 {
831   plugin_register_config ("netlink", ir_config, config_keys, config_keys_num);
832   plugin_register_init ("netlink", ir_init);
833   plugin_register_read ("netlink", ir_read);
834   plugin_register_shutdown ("netlink", ir_shutdown);
835 } /* void module_register */
836
837 /*
838  * vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
839  */