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