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