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