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