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  * Copyright (C) 2020       Intel Corporation
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; only version 2 of the License is applicable.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
21  *
22  * Authors:
23  *   Florian octo Forster <octo at collectd.org>
24  *   Sebastian Harl <sh at tokkee.org>
25  *   Andreas Henriksson <andreas at fatal.se>
26  *   Marc Fournier <marc.fournier at camptocamp.com>
27  *   Kamil Wiatrowski <kamilx.wiatrowski at intel.com>
28  **/
29 
30 #include "collectd.h"
31 
32 #include "plugin.h"
33 #include "utils/common/common.h"
34 
35 #if HAVE_REGEX_H
36 #include <regex.h>
37 #endif
38 
39 #include <asm/types.h>
40 
41 #include <linux/netlink.h>
42 #include <linux/rtnetlink.h>
43 #if HAVE_LINUX_GEN_STATS_H
44 #include <linux/gen_stats.h>
45 #endif
46 #if HAVE_LINUX_PKT_SCHED_H
47 #include <linux/pkt_sched.h>
48 #endif
49 
50 #include <glob.h>
51 #include <libmnl/libmnl.h>
52 
53 #define NETLINK_VF_DEFAULT_BUF_SIZE_KB 16
54 
55 struct ir_link_stats_storage_s {
56 
57   uint64_t rx_packets;
58   uint64_t tx_packets;
59   uint64_t rx_bytes;
60   uint64_t tx_bytes;
61   uint64_t rx_errors;
62   uint64_t tx_errors;
63 
64   uint64_t rx_dropped;
65   uint64_t tx_dropped;
66   uint64_t multicast;
67   uint64_t collisions;
68   uint64_t rx_nohandler;
69 
70   uint64_t rx_length_errors;
71   uint64_t rx_over_errors;
72   uint64_t rx_crc_errors;
73   uint64_t rx_frame_errors;
74   uint64_t rx_fifo_errors;
75   uint64_t rx_missed_errors;
76 
77   uint64_t tx_aborted_errors;
78   uint64_t tx_carrier_errors;
79   uint64_t tx_fifo_errors;
80   uint64_t tx_heartbeat_errors;
81   uint64_t tx_window_errors;
82 };
83 
84 union ir_link_stats_u {
85   struct rtnl_link_stats *stats32;
86 #ifdef HAVE_RTNL_LINK_STATS64
87   struct rtnl_link_stats64 *stats64;
88 #endif
89 };
90 
91 #ifdef HAVE_IFLA_VF_STATS
92 typedef struct vf_stats_s {
93   struct ifla_vf_mac *vf_mac;
94   uint32_t vlan;
95   uint32_t qos;
96   uint32_t spoofcheck;
97   uint32_t link_state;
98   uint32_t txrate;
99   uint32_t min_txrate;
100   uint32_t max_txrate;
101   uint32_t rss_query_en;
102   uint32_t trust;
103 
104   uint64_t rx_packets;
105   uint64_t tx_packets;
106   uint64_t rx_bytes;
107   uint64_t tx_bytes;
108   uint64_t broadcast;
109   uint64_t multicast;
110 #ifdef HAVE_IFLA_VF_STATS_RX_DROPPED
111   uint64_t rx_dropped;
112 #endif
113 #ifdef HAVE_IFLA_VF_STATS_TX_DROPPED
114   uint64_t tx_dropped;
115 #endif
116 } vf_stats_t;
117 #endif
118 
119 typedef struct ir_ignorelist_s {
120   char *device;
121 #if HAVE_REGEX_H
122   regex_t *rdevice; /* regular expression device identification */
123 #endif
124   char *type;
125   char *inst;
126   struct ir_ignorelist_s *next;
127 } ir_ignorelist_t;
128 
129 struct qos_stats {
130   struct gnet_stats_basic *bs;
131   struct gnet_stats_queue *qs;
132 };
133 
134 static int ir_ignorelist_invert = 1;
135 static ir_ignorelist_t *ir_ignorelist_head;
136 
137 static struct mnl_socket *nl;
138 
139 static char **iflist;
140 static size_t iflist_len;
141 
142 static bool collect_vf_stats = false;
143 static size_t nl_socket_buffer_size = NETLINK_VF_DEFAULT_BUF_SIZE_KB * 1024;
144 
145 static const char *config_keys[] = {
146     "Interface", "VerboseInterface", "QDisc",         "Class",
147     "Filter",    "IgnoreSelected",   "CollectVFStats"};
148 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
149 
add_ignorelist(const char * dev,const char * type,const char * inst)150 static int add_ignorelist(const char *dev, const char *type, const char *inst) {
151   ir_ignorelist_t *entry;
152 
153   entry = calloc(1, sizeof(*entry));
154   if (entry == NULL)
155     return -1;
156 
157 #if HAVE_REGEX_H
158   size_t len = strlen(dev);
159   /* regex string is enclosed in "/.../" */
160   if ((len > 2) && (dev[0] == '/') && dev[len - 1] == '/') {
161     char *copy = strdup(dev + 1);
162     if (copy == NULL) {
163       sfree(entry);
164       return -1;
165     }
166     copy[strlen(copy) - 1] = '\0';
167 
168     regex_t *re = calloc(1, sizeof(*re));
169     if (re == NULL) {
170       sfree(entry);
171       sfree(copy);
172       return -1;
173     }
174 
175     int status = regcomp(re, copy, REG_EXTENDED);
176     if (status != 0) {
177       char errbuf[1024];
178       (void)regerror(status, re, errbuf, sizeof(errbuf));
179       ERROR("netlink plugin: add_ignorelist: regcomp for %s failed: %s", dev,
180             errbuf);
181       regfree(re);
182       sfree(entry);
183       sfree(copy);
184       sfree(re);
185       return -1;
186     }
187 
188     entry->rdevice = re;
189     sfree(copy);
190   } else
191 #endif
192       if (strcasecmp(dev, "All") != 0) {
193     entry->device = strdup(dev);
194     if (entry->device == NULL) {
195       sfree(entry);
196       return -1;
197     }
198   }
199 
200   entry->type = strdup(type);
201   if (entry->type == NULL) {
202     sfree(entry->device);
203 #if HAVE_REGEX_H
204     sfree(entry->rdevice);
205 #endif
206     sfree(entry);
207     return -1;
208   }
209 
210   if (inst != NULL) {
211     entry->inst = strdup(inst);
212     if (entry->inst == NULL) {
213       sfree(entry->type);
214       sfree(entry->device);
215 #if HAVE_REGEX_H
216       sfree(entry->rdevice);
217 #endif
218       sfree(entry);
219       return -1;
220     }
221   }
222 
223   entry->next = ir_ignorelist_head;
224   ir_ignorelist_head = entry;
225 
226   return 0;
227 } /* int add_ignorelist */
228 
229 /*
230  * Checks wether a data set should be ignored. Returns `true' is the value
231  * should be ignored, `false' otherwise.
232  */
check_ignorelist(const char * dev,const char * type,const char * type_instance)233 static int check_ignorelist(const char *dev, const char *type,
234                             const char *type_instance) {
235   assert((dev != NULL) && (type != NULL));
236 
237   if (ir_ignorelist_head == NULL)
238     return ir_ignorelist_invert ? 0 : 1;
239 
240   for (ir_ignorelist_t *i = ir_ignorelist_head; i != NULL; i = i->next) {
241 #if HAVE_REGEX_H
242     if (i->rdevice != NULL) {
243       if (regexec(i->rdevice, dev, 0, NULL, 0) != REG_NOERROR)
244         continue;
245     } else
246 #endif
247         /* i->device == NULL  =>  match all devices */
248         if ((i->device != NULL) && (strcasecmp(i->device, dev) != 0))
249       continue;
250 
251     if (strcasecmp(i->type, type) != 0)
252       continue;
253 
254     if ((i->inst != NULL) && (type_instance != NULL) &&
255         (strcasecmp(i->inst, type_instance) != 0))
256       continue;
257 
258 #if COLLECT_DEBUG
259 #if HAVE_REGEX_H
260     const char *device = i->device == NULL
261                              ? (i->rdevice != NULL ? "(regexp)" : "(nil)")
262                              : i->device;
263 #else
264     const char *device = i->device == NULL ? "(nil)" : i->device;
265 #endif
266     DEBUG("netlink plugin: check_ignorelist: "
267           "(dev = %s; type = %s; inst = %s) matched "
268           "(dev = %s; type = %s; inst = %s)",
269           dev, type, type_instance == NULL ? "(nil)" : type_instance, device,
270           i->type, i->inst == NULL ? "(nil)" : i->inst);
271 #endif
272 
273     return ir_ignorelist_invert ? 0 : 1;
274   } /* for i */
275 
276   return ir_ignorelist_invert;
277 } /* int check_ignorelist */
278 
279 #ifdef HAVE_IFLA_VF_STATS
submit_one_gauge(const char * dev,const char * type,const char * type_instance,gauge_t value)280 static void submit_one_gauge(const char *dev, const char *type,
281                              const char *type_instance, gauge_t value) {
282   value_list_t vl = VALUE_LIST_INIT;
283 
284   vl.values = &(value_t){.gauge = value};
285   vl.values_len = 1;
286   sstrncpy(vl.plugin, "netlink", sizeof(vl.plugin));
287   sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
288   sstrncpy(vl.type, type, sizeof(vl.type));
289 
290   if (type_instance != NULL)
291     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
292 
293   plugin_dispatch_values(&vl);
294 } /* void submit_one_gauge */
295 #endif
296 
submit_one(const char * dev,const char * type,const char * type_instance,derive_t value)297 static void submit_one(const char *dev, const char *type,
298                        const char *type_instance, derive_t value) {
299   value_list_t vl = VALUE_LIST_INIT;
300 
301   vl.values = &(value_t){.derive = value};
302   vl.values_len = 1;
303   sstrncpy(vl.plugin, "netlink", sizeof(vl.plugin));
304   sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
305   sstrncpy(vl.type, type, sizeof(vl.type));
306 
307   if (type_instance != NULL)
308     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
309 
310   plugin_dispatch_values(&vl);
311 } /* void submit_one */
312 
submit_two(const char * dev,const char * type,const char * type_instance,derive_t rx,derive_t tx)313 static void submit_two(const char *dev, const char *type,
314                        const char *type_instance, derive_t rx, derive_t tx) {
315   value_list_t vl = VALUE_LIST_INIT;
316   value_t values[] = {
317       {.derive = rx},
318       {.derive = tx},
319   };
320 
321   vl.values = values;
322   vl.values_len = STATIC_ARRAY_SIZE(values);
323   sstrncpy(vl.plugin, "netlink", sizeof(vl.plugin));
324   sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance));
325   sstrncpy(vl.type, type, sizeof(vl.type));
326 
327   if (type_instance != NULL)
328     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
329 
330   plugin_dispatch_values(&vl);
331 } /* void submit_two */
332 
update_iflist(struct ifinfomsg * msg,const char * dev)333 static int update_iflist(struct ifinfomsg *msg, const char *dev) {
334   /* Update the `iflist'. It's used to know which interfaces exist and query
335    * them later for qdiscs and classes. */
336   if ((msg->ifi_index >= 0) && ((size_t)msg->ifi_index >= iflist_len)) {
337     char **temp;
338 
339     temp = realloc(iflist, (msg->ifi_index + 1) * sizeof(char *));
340     if (temp == NULL) {
341       ERROR("netlink plugin: update_iflist: realloc failed.");
342       return -1;
343     }
344 
345     memset(temp + iflist_len, '\0',
346            (msg->ifi_index + 1 - iflist_len) * sizeof(char *));
347     iflist = temp;
348     iflist_len = msg->ifi_index + 1;
349   }
350   if ((iflist[msg->ifi_index] == NULL) ||
351       (strcmp(iflist[msg->ifi_index], dev) != 0)) {
352     sfree(iflist[msg->ifi_index]);
353     iflist[msg->ifi_index] = strdup(dev);
354   }
355 
356   return 0;
357 } /* int update_iflist */
358 
check_ignorelist_and_submit(const char * dev,struct ir_link_stats_storage_s * stats)359 static void check_ignorelist_and_submit(const char *dev,
360                                         struct ir_link_stats_storage_s *stats) {
361 
362   if (check_ignorelist(dev, "interface", NULL) == 0) {
363     submit_two(dev, "if_octets", NULL, stats->rx_bytes, stats->tx_bytes);
364     submit_two(dev, "if_packets", NULL, stats->rx_packets, stats->tx_packets);
365     submit_two(dev, "if_errors", NULL, stats->rx_errors, stats->tx_errors);
366   } else {
367     DEBUG("netlink plugin: Ignoring %s/interface.", dev);
368   }
369 
370   if (check_ignorelist(dev, "if_detail", NULL) == 0) {
371     submit_two(dev, "if_dropped", NULL, stats->rx_dropped, stats->tx_dropped);
372     submit_one(dev, "if_multicast", NULL, stats->multicast);
373     submit_one(dev, "if_collisions", NULL, stats->collisions);
374 #if defined(HAVE_STRUCT_RTNL_LINK_STATS_RX_NOHANDLER) ||                       \
375     defined(HAVE_STRUCT_RTNL_LINK_STATS64_RX_NOHANDLER)
376     submit_one(dev, "if_rx_nohandler", NULL, stats->rx_nohandler);
377 #endif
378 
379     submit_one(dev, "if_rx_errors", "length", stats->rx_length_errors);
380     submit_one(dev, "if_rx_errors", "over", stats->rx_over_errors);
381     submit_one(dev, "if_rx_errors", "crc", stats->rx_crc_errors);
382     submit_one(dev, "if_rx_errors", "frame", stats->rx_frame_errors);
383     submit_one(dev, "if_rx_errors", "fifo", stats->rx_fifo_errors);
384     submit_one(dev, "if_rx_errors", "missed", stats->rx_missed_errors);
385 
386     submit_one(dev, "if_tx_errors", "aborted", stats->tx_aborted_errors);
387     submit_one(dev, "if_tx_errors", "carrier", stats->tx_carrier_errors);
388     submit_one(dev, "if_tx_errors", "fifo", stats->tx_fifo_errors);
389     submit_one(dev, "if_tx_errors", "heartbeat", stats->tx_heartbeat_errors);
390     submit_one(dev, "if_tx_errors", "window", stats->tx_window_errors);
391   } else {
392     DEBUG("netlink plugin: Ignoring %s/if_detail.", dev);
393   }
394 
395 } /* void check_ignorelist_and_submit */
396 
397 #define COPY_RTNL_LINK_VALUE(dst_stats, src_stats, value_name)                 \
398   (dst_stats)->value_name = (src_stats)->value_name
399 
400 #define COPY_RTNL_LINK_STATS(dst_stats, src_stats)                             \
401   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_packets);                      \
402   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_packets);                      \
403   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_bytes);                        \
404   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_bytes);                        \
405   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_errors);                       \
406   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_errors);                       \
407   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_dropped);                      \
408   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_dropped);                      \
409   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, multicast);                       \
410   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, collisions);                      \
411   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_length_errors);                \
412   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_over_errors);                  \
413   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_crc_errors);                   \
414   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_frame_errors);                 \
415   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_fifo_errors);                  \
416   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, rx_missed_errors);                \
417   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_aborted_errors);               \
418   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_carrier_errors);               \
419   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_fifo_errors);                  \
420   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_heartbeat_errors);             \
421   COPY_RTNL_LINK_VALUE(dst_stats, src_stats, tx_window_errors)
422 
423 #ifdef HAVE_RTNL_LINK_STATS64
check_ignorelist_and_submit64(const char * dev,struct rtnl_link_stats64 * stats)424 static void check_ignorelist_and_submit64(const char *dev,
425                                           struct rtnl_link_stats64 *stats) {
426   struct ir_link_stats_storage_s s;
427 
428   COPY_RTNL_LINK_STATS(&s, stats);
429 #ifdef HAVE_STRUCT_RTNL_LINK_STATS64_RX_NOHANDLER
430   COPY_RTNL_LINK_VALUE(&s, stats, rx_nohandler);
431 #endif
432 
433   check_ignorelist_and_submit(dev, &s);
434 }
435 #endif
436 
check_ignorelist_and_submit32(const char * dev,struct rtnl_link_stats * stats)437 static void check_ignorelist_and_submit32(const char *dev,
438                                           struct rtnl_link_stats *stats) {
439   struct ir_link_stats_storage_s s;
440 
441   COPY_RTNL_LINK_STATS(&s, stats);
442 #ifdef HAVE_STRUCT_RTNL_LINK_STATS_RX_NOHANDLER
443   COPY_RTNL_LINK_VALUE(&s, stats, rx_nohandler);
444 #endif
445 
446   check_ignorelist_and_submit(dev, &s);
447 }
448 
449 #ifdef HAVE_IFLA_VF_STATS
vf_info_submit(const char * dev,vf_stats_t * vf_stats)450 static void vf_info_submit(const char *dev, vf_stats_t *vf_stats) {
451   if (vf_stats->vf_mac == NULL) {
452     ERROR("netlink plugin: vf_info_submit: failed to get VF macaddress, "
453           "skipping VF for interface %s",
454           dev);
455     return;
456   }
457   uint8_t *mac = vf_stats->vf_mac->mac;
458   uint32_t vf_num = vf_stats->vf_mac->vf;
459   char instance[512];
460   ssnprintf(instance, sizeof(instance), "%s_vf%u_%02x:%02x:%02x:%02x:%02x:%02x",
461             dev, vf_num, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
462   DEBUG("netlink plugin: vf_info_submit: plugin_instance - %s", instance);
463 
464   submit_one_gauge(instance, "vf_link_info", "number", vf_num);
465   submit_one_gauge(instance, "vf_link_info", "vlan", vf_stats->vlan);
466   submit_one_gauge(instance, "vf_link_info", "qos", vf_stats->qos);
467   submit_one_gauge(instance, "vf_link_info", "spoofcheck",
468                    vf_stats->spoofcheck);
469   submit_one_gauge(instance, "vf_link_info", "link_state",
470                    vf_stats->link_state);
471   submit_one_gauge(instance, "vf_link_info", "tx_rate", vf_stats->txrate);
472   submit_one_gauge(instance, "vf_link_info", "min_tx_rate",
473                    vf_stats->min_txrate);
474   submit_one_gauge(instance, "vf_link_info", "max_tx_rate",
475                    vf_stats->max_txrate);
476   submit_one_gauge(instance, "vf_link_info", "rss_query_en",
477                    vf_stats->rss_query_en);
478   submit_one_gauge(instance, "vf_link_info", "trust", vf_stats->trust);
479 
480   submit_one(instance, "vf_broadcast", NULL, vf_stats->broadcast);
481   submit_one(instance, "vf_multicast", NULL, vf_stats->multicast);
482   submit_two(instance, "vf_packets", NULL, vf_stats->rx_packets,
483              vf_stats->tx_packets);
484   submit_two(instance, "vf_bytes", NULL, vf_stats->rx_bytes,
485              vf_stats->tx_bytes);
486 #if defined(HAVE_IFLA_VF_STATS_RX_DROPPED) &&                                  \
487     defined(HAVE_IFLA_VF_STATS_TX_DROPPED)
488   submit_two(instance, "vf_dropped", NULL, vf_stats->rx_dropped,
489              vf_stats->tx_dropped);
490 #endif
491 } /* void vf_info_submit */
492 
493 #define IFCOPY_VF_STAT_VALUE(attr, name, type_name)                            \
494   do {                                                                         \
495     if (mnl_attr_get_type(attr) == type_name) {                                \
496       if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {                         \
497         ERROR("netlink plugin: vf_info_attr_cb: " #type_name                   \
498               " mnl_attr_validate failed.");                                   \
499         return MNL_CB_ERROR;                                                   \
500       }                                                                        \
501       vf_stats->name = mnl_attr_get_u64(attr);                                 \
502     }                                                                          \
503   } while (0)
504 
vf_info_attr_cb(const struct nlattr * attr,void * args)505 static int vf_info_attr_cb(const struct nlattr *attr, void *args) {
506   vf_stats_t *vf_stats = (vf_stats_t *)args;
507 
508   /* skip unsupported attribute */
509   if (mnl_attr_type_valid(attr, IFLA_VF_MAX) < 0) {
510     return MNL_CB_OK;
511   }
512 
513   if (mnl_attr_get_type(attr) == IFLA_VF_MAC) {
514     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*vf_stats->vf_mac)) <
515         0) {
516       ERROR("netlink plugin: vf_info_attr_cb: IFLA_VF_MAC mnl_attr_validate2 "
517             "failed: %s",
518             STRERRNO);
519       return MNL_CB_ERROR;
520     }
521 
522     vf_stats->vf_mac = (struct ifla_vf_mac *)mnl_attr_get_payload(attr);
523     return MNL_CB_OK;
524   }
525 
526   if (mnl_attr_get_type(attr) == IFLA_VF_VLAN) {
527     struct ifla_vf_vlan *vf_vlan;
528     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*vf_vlan)) < 0) {
529       ERROR("netlink plugin: vf_info_attr_cb: IFLA_VF_VLAN mnl_attr_validate2 "
530             "failed: %s",
531             STRERRNO);
532       return MNL_CB_ERROR;
533     }
534 
535     vf_vlan = (struct ifla_vf_vlan *)mnl_attr_get_payload(attr);
536     vf_stats->vlan = vf_vlan->vlan;
537     vf_stats->qos = vf_vlan->qos;
538     return MNL_CB_OK;
539   }
540 
541   if (mnl_attr_get_type(attr) == IFLA_VF_TX_RATE) {
542     struct ifla_vf_tx_rate *vf_txrate;
543     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*vf_txrate)) < 0) {
544       ERROR("netlink plugin: vf_info_attr_cb: IFLA_VF_TX_RATE "
545             "mnl_attr_validate2 failed: %s",
546             STRERRNO);
547       return MNL_CB_ERROR;
548     }
549 
550     vf_txrate = (struct ifla_vf_tx_rate *)mnl_attr_get_payload(attr);
551     vf_stats->txrate = vf_txrate->rate;
552     return MNL_CB_OK;
553   }
554 
555   if (mnl_attr_get_type(attr) == IFLA_VF_SPOOFCHK) {
556     struct ifla_vf_spoofchk *vf_spoofchk;
557     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*vf_spoofchk)) < 0) {
558       ERROR("netlink plugin: vf_info_attr_cb: IFLA_VF_SPOOFCHK "
559             "mnl_attr_validate2 failed: %s",
560             STRERRNO);
561       return MNL_CB_ERROR;
562     }
563 
564     vf_spoofchk = (struct ifla_vf_spoofchk *)mnl_attr_get_payload(attr);
565     vf_stats->spoofcheck = vf_spoofchk->setting;
566     return MNL_CB_OK;
567   }
568 
569   if (mnl_attr_get_type(attr) == IFLA_VF_LINK_STATE) {
570     struct ifla_vf_link_state *vf_link_state;
571     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*vf_link_state)) < 0) {
572       ERROR("netlink plugin: vf_info_attr_cb: IFLA_VF_LINK_STATE "
573             "mnl_attr_validate2 failed: %s",
574             STRERRNO);
575       return MNL_CB_ERROR;
576     }
577 
578     vf_link_state = (struct ifla_vf_link_state *)mnl_attr_get_payload(attr);
579     vf_stats->link_state = vf_link_state->link_state;
580     return MNL_CB_OK;
581   }
582 
583   if (mnl_attr_get_type(attr) == IFLA_VF_RATE) {
584     struct ifla_vf_rate *vf_rate;
585     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*vf_rate)) < 0) {
586       ERROR("netlink plugin: vf_info_attr_cb: IFLA_VF_RATE mnl_attr_validate2 "
587             "failed: %s",
588             STRERRNO);
589       return MNL_CB_ERROR;
590     }
591 
592     vf_rate = (struct ifla_vf_rate *)mnl_attr_get_payload(attr);
593     vf_stats->min_txrate = vf_rate->min_tx_rate;
594     vf_stats->max_txrate = vf_rate->max_tx_rate;
595     return MNL_CB_OK;
596   }
597 
598   if (mnl_attr_get_type(attr) == IFLA_VF_RSS_QUERY_EN) {
599     struct ifla_vf_rss_query_en *vf_rss_query_en;
600     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*vf_rss_query_en)) <
601         0) {
602       ERROR("netlink plugin: vf_info_attr_cb: IFLA_VF_RSS_QUERY_EN "
603             "mnl_attr_validate2 "
604             "failed: %s",
605             STRERRNO);
606       return MNL_CB_ERROR;
607     }
608 
609     vf_rss_query_en = (struct ifla_vf_rss_query_en *)mnl_attr_get_payload(attr);
610     vf_stats->rss_query_en = vf_rss_query_en->setting;
611     return MNL_CB_OK;
612   }
613 
614   if (mnl_attr_get_type(attr) == IFLA_VF_TRUST) {
615     struct ifla_vf_trust *vf_trust;
616     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*vf_trust)) < 0) {
617       ERROR("netlink plugin: vf_info_attr_cb: IFLA_VF_TRUST mnl_attr_validate2 "
618             "failed: %s",
619             STRERRNO);
620       return MNL_CB_ERROR;
621     }
622 
623     vf_trust = (struct ifla_vf_trust *)mnl_attr_get_payload(attr);
624     vf_stats->trust = vf_trust->setting;
625     return MNL_CB_OK;
626   }
627 
628   if (mnl_attr_get_type(attr) == IFLA_VF_STATS) {
629     if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
630       ERROR("netlink plugin: vf_info_attr_cb: IFLA_VF_STATS mnl_attr_validate "
631             "failed.");
632       return MNL_CB_ERROR;
633     }
634 
635     struct nlattr *nested;
636     mnl_attr_for_each_nested(nested, attr) {
637       IFCOPY_VF_STAT_VALUE(nested, rx_packets, IFLA_VF_STATS_RX_PACKETS);
638       IFCOPY_VF_STAT_VALUE(nested, tx_packets, IFLA_VF_STATS_TX_PACKETS);
639       IFCOPY_VF_STAT_VALUE(nested, rx_bytes, IFLA_VF_STATS_RX_BYTES);
640       IFCOPY_VF_STAT_VALUE(nested, tx_bytes, IFLA_VF_STATS_TX_BYTES);
641       IFCOPY_VF_STAT_VALUE(nested, broadcast, IFLA_VF_STATS_BROADCAST);
642       IFCOPY_VF_STAT_VALUE(nested, multicast, IFLA_VF_STATS_MULTICAST);
643 #ifdef HAVE_IFLA_VF_STATS_RX_DROPPED
644       IFCOPY_VF_STAT_VALUE(nested, rx_dropped, IFLA_VF_STATS_RX_DROPPED);
645 #endif
646 #ifdef HAVE_IFLA_VF_STATS_TX_DROPPED
647       IFCOPY_VF_STAT_VALUE(nested, tx_dropped, IFLA_VF_STATS_TX_DROPPED);
648 #endif
649     }
650     return MNL_CB_OK;
651   }
652 
653   return MNL_CB_OK;
654 } /* int vf_info_attr_cb */
655 #endif /* HAVE_IFLA_VF_STATS */
656 
link_filter_cb(const struct nlmsghdr * nlh,void * args)657 static int link_filter_cb(const struct nlmsghdr *nlh,
658                           void *args __attribute__((unused))) {
659   struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
660   struct nlattr *attr;
661   const char *dev = NULL;
662   union ir_link_stats_u stats;
663 #ifdef HAVE_IFLA_VF_STATS
664   uint32_t num_vfs = 0;
665 #endif
666   bool stats_done = false;
667 
668   if (nlh->nlmsg_type != RTM_NEWLINK) {
669     ERROR("netlink plugin: link_filter_cb: Don't know how to handle type %i.",
670           nlh->nlmsg_type);
671     return MNL_CB_ERROR;
672   }
673 
674   /* Scan attribute list for device name. */
675   mnl_attr_for_each(attr, nlh, sizeof(*ifm)) {
676     if (mnl_attr_get_type(attr) != IFLA_IFNAME)
677       continue;
678 
679     if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
680       ERROR("netlink plugin: link_filter_cb: IFLA_IFNAME mnl_attr_validate "
681             "failed.");
682       return MNL_CB_ERROR;
683     }
684 
685     dev = mnl_attr_get_str(attr);
686     if (update_iflist(ifm, dev) < 0)
687       return MNL_CB_ERROR;
688     break;
689   }
690 
691   if (dev == NULL) {
692     ERROR("netlink plugin: link_filter_cb: dev == NULL");
693     return MNL_CB_ERROR;
694   }
695 
696   if (check_ignorelist(dev, "interface", NULL) != 0 &&
697       check_ignorelist(dev, "if_detail", NULL) != 0) {
698     DEBUG("netlink plugin: link_filter_cb: Ignoring %s/interface.", dev);
699     DEBUG("netlink plugin: link_filter_cb: Ignoring %s/if_detail.", dev);
700     return MNL_CB_OK;
701   }
702 
703 #ifdef HAVE_IFLA_VF_STATS
704   if (collect_vf_stats) {
705     mnl_attr_for_each(attr, nlh, sizeof(*ifm)) {
706       if (mnl_attr_get_type(attr) != IFLA_NUM_VF)
707         continue;
708 
709       if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
710         ERROR("netlink plugin: link_filter_cb: IFLA_NUM_VF mnl_attr_validate "
711               "failed.");
712         return MNL_CB_ERROR;
713       }
714 
715       num_vfs = mnl_attr_get_u32(attr);
716       break;
717     }
718   }
719 #endif
720 
721 #ifdef HAVE_RTNL_LINK_STATS64
722   mnl_attr_for_each(attr, nlh, sizeof(*ifm)) {
723     if (mnl_attr_get_type(attr) != IFLA_STATS64)
724       continue;
725 
726     uint16_t attr_len = mnl_attr_get_payload_len(attr);
727     if (attr_len < sizeof(*stats.stats64)) {
728       ERROR("netlink plugin: link_filter_cb: IFLA_STATS64 attribute has "
729             "insufficient data.");
730       return MNL_CB_ERROR;
731     }
732     stats.stats64 = mnl_attr_get_payload(attr);
733 
734     check_ignorelist_and_submit64(dev, stats.stats64);
735 
736     stats_done = true;
737     break;
738   }
739 #endif
740   if (stats_done == false) {
741     mnl_attr_for_each(attr, nlh, sizeof(*ifm)) {
742       if (mnl_attr_get_type(attr) != IFLA_STATS)
743         continue;
744 
745       uint16_t attr_len = mnl_attr_get_payload_len(attr);
746       if (attr_len < sizeof(*stats.stats32)) {
747         ERROR("netlink plugin: link_filter_cb: IFLA_STATS attribute has "
748               "insufficient data.");
749         return MNL_CB_ERROR;
750       }
751       stats.stats32 = mnl_attr_get_payload(attr);
752 
753       check_ignorelist_and_submit32(dev, stats.stats32);
754 
755       stats_done = true;
756       break;
757     }
758   }
759 
760 #if COLLECT_DEBUG
761   if (stats_done == false)
762     DEBUG("netlink plugin: link_filter: No statistics for interface %s.", dev);
763 #endif
764 
765 #ifdef HAVE_IFLA_VF_STATS
766   if (num_vfs == 0)
767     return MNL_CB_OK;
768 
769   /* Get VFINFO list. */
770   mnl_attr_for_each(attr, nlh, sizeof(*ifm)) {
771     if (mnl_attr_get_type(attr) != IFLA_VFINFO_LIST)
772       continue;
773 
774     if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
775       ERROR("netlink plugin: link_filter_cb: IFLA_VFINFO_LIST "
776             "mnl_attr_validate failed.");
777       return MNL_CB_ERROR;
778     }
779 
780     struct nlattr *nested;
781     mnl_attr_for_each_nested(nested, attr) {
782       if (mnl_attr_get_type(nested) != IFLA_VF_INFO) {
783         continue;
784       }
785 
786       if (mnl_attr_validate(nested, MNL_TYPE_NESTED) < 0) {
787         ERROR("netlink plugin: link_filter_cb: IFLA_VF_INFO mnl_attr_validate "
788               "failed.");
789         return MNL_CB_ERROR;
790       }
791 
792       vf_stats_t vf_stats = {0};
793       if (mnl_attr_parse_nested(nested, vf_info_attr_cb, &vf_stats) ==
794           MNL_CB_ERROR)
795         return MNL_CB_ERROR;
796 
797       vf_info_submit(dev, &vf_stats);
798     }
799     break;
800   }
801 #endif
802 
803   return MNL_CB_OK;
804 } /* int link_filter_cb */
805 
806 #if HAVE_TCA_STATS2
qos_attr_cb(const struct nlattr * attr,void * data)807 static int qos_attr_cb(const struct nlattr *attr, void *data) {
808   struct qos_stats *q_stats = (struct qos_stats *)data;
809 
810   /* skip unsupported attribute in user-space */
811   if (mnl_attr_type_valid(attr, TCA_STATS_MAX) < 0)
812     return MNL_CB_OK;
813 
814   if (mnl_attr_get_type(attr) == TCA_STATS_BASIC) {
815     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*q_stats->bs)) < 0) {
816       ERROR("netlink plugin: qos_attr_cb: TCA_STATS_BASIC mnl_attr_validate2 "
817             "failed: %s",
818             STRERRNO);
819       return MNL_CB_ERROR;
820     }
821     q_stats->bs = mnl_attr_get_payload(attr);
822     return MNL_CB_OK;
823   }
824 
825   if (mnl_attr_get_type(attr) == TCA_STATS_QUEUE) {
826     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*q_stats->qs)) < 0) {
827       ERROR("netlink plugin: qos_attr_cb: TCA_STATS_QUEUE mnl_attr_validate2 "
828             "failed.");
829       return MNL_CB_ERROR;
830     }
831     q_stats->qs = mnl_attr_get_payload(attr);
832     return MNL_CB_OK;
833   }
834 
835   return MNL_CB_OK;
836 } /* qos_attr_cb */
837 #endif
838 
qos_filter_cb(const struct nlmsghdr * nlh,void * args)839 static int qos_filter_cb(const struct nlmsghdr *nlh, void *args) {
840   struct tcmsg *tm = mnl_nlmsg_get_payload(nlh);
841   struct nlattr *attr;
842 
843   int wanted_ifindex = *((int *)args);
844 
845   const char *dev;
846   const char *kind = NULL;
847 
848   /* char *type_instance; */
849   const char *tc_type;
850   char tc_inst[DATA_MAX_NAME_LEN];
851 
852   bool stats_submitted = false;
853 
854   if (nlh->nlmsg_type == RTM_NEWQDISC)
855     tc_type = "qdisc";
856   else if (nlh->nlmsg_type == RTM_NEWTCLASS)
857     tc_type = "class";
858   else if (nlh->nlmsg_type == RTM_NEWTFILTER)
859     tc_type = "filter";
860   else {
861     ERROR("netlink plugin: qos_filter_cb: Don't know how to handle type %i.",
862           nlh->nlmsg_type);
863     return MNL_CB_ERROR;
864   }
865 
866   if (tm->tcm_ifindex != wanted_ifindex) {
867     DEBUG("netlink plugin: qos_filter_cb: Got %s for interface #%i, "
868           "but expected #%i.",
869           tc_type, tm->tcm_ifindex, wanted_ifindex);
870     return MNL_CB_OK;
871   }
872 
873   if ((tm->tcm_ifindex >= 0) && ((size_t)tm->tcm_ifindex >= iflist_len)) {
874     ERROR("netlink plugin: qos_filter_cb: tm->tcm_ifindex = %i "
875           ">= iflist_len = %" PRIsz,
876           tm->tcm_ifindex, iflist_len);
877     return MNL_CB_ERROR;
878   }
879 
880   dev = iflist[tm->tcm_ifindex];
881   if (dev == NULL) {
882     ERROR("netlink plugin: qos_filter_cb: iflist[%i] == NULL", tm->tcm_ifindex);
883     return MNL_CB_ERROR;
884   }
885 
886   mnl_attr_for_each(attr, nlh, sizeof(*tm)) {
887     if (mnl_attr_get_type(attr) != TCA_KIND)
888       continue;
889 
890     if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
891       ERROR(
892           "netlink plugin: qos_filter_cb: TCA_KIND mnl_attr_validate failed.");
893       return MNL_CB_ERROR;
894     }
895 
896     kind = mnl_attr_get_str(attr);
897     break;
898   }
899 
900   if (kind == NULL) {
901     ERROR("netlink plugin: qos_filter_cb: kind == NULL");
902     return -1;
903   }
904 
905   { /* The ID */
906     uint32_t numberic_id;
907 
908     numberic_id = tm->tcm_handle;
909     if (strcmp(tc_type, "filter") == 0)
910       numberic_id = tm->tcm_parent;
911 
912     ssnprintf(tc_inst, sizeof(tc_inst), "%s-%x:%x", kind, numberic_id >> 16,
913               numberic_id & 0x0000FFFF);
914   }
915 
916   DEBUG("netlink plugin: qos_filter_cb: got %s for %s (%i).", tc_type, dev,
917         tm->tcm_ifindex);
918 
919   if (check_ignorelist(dev, tc_type, tc_inst))
920     return MNL_CB_OK;
921 
922 #if HAVE_TCA_STATS2
923   mnl_attr_for_each(attr, nlh, sizeof(*tm)) {
924     struct qos_stats q_stats;
925 
926     memset(&q_stats, 0x0, sizeof(q_stats));
927 
928     if (mnl_attr_get_type(attr) != TCA_STATS2)
929       continue;
930 
931     if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
932       ERROR("netlink plugin: qos_filter_cb: TCA_STATS2 mnl_attr_validate "
933             "failed.");
934       return MNL_CB_ERROR;
935     }
936 
937     mnl_attr_parse_nested(attr, qos_attr_cb, &q_stats);
938 
939     if (q_stats.bs != NULL || q_stats.qs != NULL) {
940       char type_instance[DATA_MAX_NAME_LEN];
941 
942       stats_submitted = true;
943 
944       int r = ssnprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type,
945                         tc_inst);
946       if ((size_t)r >= sizeof(type_instance)) {
947         ERROR("netlink plugin: type_instance truncated to %zu bytes, need %d",
948               sizeof(type_instance), r);
949         return MNL_CB_ERROR;
950       }
951 
952       if (q_stats.bs != NULL) {
953         submit_one(dev, "ipt_bytes", type_instance, q_stats.bs->bytes);
954         submit_one(dev, "ipt_packets", type_instance, q_stats.bs->packets);
955       }
956       if (q_stats.qs != NULL) {
957         submit_one(dev, "if_tx_dropped", type_instance, q_stats.qs->drops);
958       }
959     }
960 
961     break;
962   }
963 #endif /* TCA_STATS2 */
964 
965 #if HAVE_TCA_STATS
966   mnl_attr_for_each(attr, nlh, sizeof(*tm)) {
967     struct tc_stats *ts = NULL;
968 
969     if (mnl_attr_get_type(attr) != TCA_STATS)
970       continue;
971 
972     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*ts)) < 0) {
973       ERROR("netlink plugin: qos_filter_cb: TCA_STATS mnl_attr_validate2 "
974             "failed: %s",
975             STRERRNO);
976       return MNL_CB_ERROR;
977     }
978     ts = mnl_attr_get_payload(attr);
979 
980     if (!stats_submitted && ts != NULL) {
981       char type_instance[DATA_MAX_NAME_LEN];
982 
983       int r = ssnprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type,
984                         tc_inst);
985       if ((size_t)r >= sizeof(type_instance)) {
986         ERROR("netlink plugin: type_instance truncated to %zu bytes, need %d",
987               sizeof(type_instance), r);
988         return MNL_CB_ERROR;
989       }
990 
991       submit_one(dev, "ipt_bytes", type_instance, ts->bytes);
992       submit_one(dev, "ipt_packets", type_instance, ts->packets);
993     }
994 
995     break;
996   }
997 
998 #endif /* TCA_STATS */
999 
1000 #if !(HAVE_TCA_STATS && HAVE_TCA_STATS2)
1001   DEBUG("netlink plugin: qos_filter_cb: Have neither TCA_STATS2 nor "
1002         "TCA_STATS.");
1003 #endif
1004 
1005   return MNL_CB_OK;
1006 } /* int qos_filter_cb */
1007 
ir_get_buffer_size()1008 static size_t ir_get_buffer_size() {
1009   if (collect_vf_stats == false) {
1010     return MNL_SOCKET_BUFFER_SIZE;
1011   }
1012 
1013   glob_t g;
1014   unsigned int max_num = 0;
1015   if (glob("/sys/class/net/*/device/sriov_totalvfs", GLOB_NOSORT, NULL, &g)) {
1016     ERROR("netlink plugin: ir_get_buffer_size: glob failed");
1017     /* using default value */
1018     return NETLINK_VF_DEFAULT_BUF_SIZE_KB * 1024;
1019   }
1020 
1021   for (size_t i = 0; i < g.gl_pathc; i++) {
1022     char buf[16];
1023     ssize_t len;
1024     int num = 0;
1025     int fd = open(g.gl_pathv[i], O_RDONLY);
1026     if (fd < 0) {
1027       WARNING("netlink plugin: ir_get_buffer_size: failed to open `%s.`",
1028               g.gl_pathv[i]);
1029       continue;
1030     }
1031 
1032     if ((len = read(fd, buf, sizeof(buf) - 1)) <= 0) {
1033       WARNING("netlink plugin: ir_get_buffer_size: failed to read `%s.`",
1034               g.gl_pathv[i]);
1035       close(fd);
1036       continue;
1037     }
1038     buf[len] = '\0';
1039 
1040     if (sscanf(buf, "%d", &num) != 1) {
1041       WARNING("netlink plugin: ir_get_buffer_size: failed to read number from "
1042               "`%s.`",
1043               buf);
1044       close(fd);
1045       continue;
1046     }
1047 
1048     if (num > max_num)
1049       max_num = num;
1050 
1051     close(fd);
1052   }
1053   globfree(&g);
1054   DEBUG("netlink plugin: ir_get_buffer_size: max sriov_totalvfs = %u", max_num);
1055 
1056   unsigned int mp = NETLINK_VF_DEFAULT_BUF_SIZE_KB;
1057   /* allign to power of two, buffer size should be at least totalvfs/2 kb */
1058   while (mp < max_num / 2)
1059     mp *= 2;
1060 
1061   return mp * 1024;
1062 }
1063 
ir_config(const char * key,const char * value)1064 static int ir_config(const char *key, const char *value) {
1065   char *new_val;
1066   char *fields[8];
1067   int fields_num;
1068   int status = 1;
1069 
1070   new_val = strdup(value);
1071   if (new_val == NULL)
1072     return -1;
1073 
1074   fields_num = strsplit(new_val, fields, STATIC_ARRAY_SIZE(fields));
1075   if ((fields_num < 1) || (fields_num > 8)) {
1076     sfree(new_val);
1077     return -1;
1078   }
1079 
1080   if ((strcasecmp(key, "Interface") == 0) ||
1081       (strcasecmp(key, "VerboseInterface") == 0)) {
1082     if (fields_num != 1) {
1083       ERROR("netlink plugin: Invalid number of fields for option "
1084             "`%s'. Got %i, expected 1.",
1085             key, fields_num);
1086       status = -1;
1087     } else {
1088       status = add_ignorelist(fields[0], "interface", NULL);
1089       if (strcasecmp(key, "VerboseInterface") == 0)
1090         status += add_ignorelist(fields[0], "if_detail", NULL);
1091     }
1092   } else if ((strcasecmp(key, "QDisc") == 0) ||
1093              (strcasecmp(key, "Class") == 0) ||
1094              (strcasecmp(key, "Filter") == 0)) {
1095     if (fields_num > 2) {
1096       ERROR("netlink plugin: Invalid number of fields for option "
1097             "`%s'. Got %i, expected 1 or 2.",
1098             key, fields_num);
1099       return -1;
1100     } else {
1101       status =
1102           add_ignorelist(fields[0], key, (fields_num == 2) ? fields[1] : NULL);
1103     }
1104   } else if (strcasecmp(key, "IgnoreSelected") == 0) {
1105     if (fields_num != 1) {
1106       ERROR("netlink plugin: Invalid number of fields for option "
1107             "`IgnoreSelected'. Got %i, expected 1.",
1108             fields_num);
1109       status = -1;
1110     } else {
1111       if (IS_TRUE(fields[0]))
1112         ir_ignorelist_invert = 0;
1113       else
1114         ir_ignorelist_invert = 1;
1115       status = 0;
1116     }
1117   } else if (strcasecmp(key, "CollectVFStats") == 0) {
1118     if (fields_num != 1) {
1119       ERROR("netlink plugin: Invalid number of fields for option "
1120             "`%s'. Got %i, expected 1.",
1121             key, fields_num);
1122       status = -1;
1123     } else {
1124 #ifdef HAVE_IFLA_VF_STATS
1125       if (IS_TRUE(fields[0]))
1126         collect_vf_stats = true;
1127       else
1128         collect_vf_stats = false;
1129 #else
1130       WARNING("netlink plugin: VF statistics not supported on this system.");
1131 #endif
1132       status = 0;
1133     }
1134   }
1135 
1136   sfree(new_val);
1137 
1138   return status;
1139 } /* int ir_config */
1140 
ir_init(void)1141 static int ir_init(void) {
1142   nl = mnl_socket_open(NETLINK_ROUTE);
1143   if (nl == NULL) {
1144     ERROR("netlink plugin: ir_init: mnl_socket_open failed.");
1145     return -1;
1146   }
1147 
1148   if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
1149     ERROR("netlink plugin: ir_init: mnl_socket_bind failed.");
1150     return -1;
1151   }
1152 
1153   nl_socket_buffer_size = ir_get_buffer_size();
1154   INFO("netlink plugin: ir_init: buffer size = %zu", nl_socket_buffer_size);
1155 
1156   return 0;
1157 } /* int ir_init */
1158 
ir_read(void)1159 static int ir_read(void) {
1160   char buf[nl_socket_buffer_size];
1161   struct nlmsghdr *nlh;
1162   struct rtgenmsg *rt;
1163   int ret;
1164   unsigned int seq, portid;
1165 
1166   static const int type_id[] = {RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER};
1167   static const char *type_name[] = {"qdisc", "class", "filter"};
1168 
1169   portid = mnl_socket_get_portid(nl);
1170 
1171   nlh = mnl_nlmsg_put_header(buf);
1172   nlh->nlmsg_type = RTM_GETLINK;
1173   nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
1174   nlh->nlmsg_seq = seq = time(NULL);
1175   rt = mnl_nlmsg_put_extra_header(nlh, sizeof(*rt));
1176   rt->rtgen_family = AF_PACKET;
1177 
1178 #ifdef HAVE_IFLA_VF_STATS
1179   if (collect_vf_stats &&
1180       mnl_attr_put_u32_check(nlh, sizeof(buf), IFLA_EXT_MASK,
1181                              RTEXT_FILTER_VF) == 0) {
1182     ERROR("netlink plugin: FAILED to set RTEXT_FILTER_VF");
1183     return -1;
1184   }
1185 #endif
1186 
1187   if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
1188     ERROR("netlink plugin: ir_read: rtnl_wilddump_request failed.");
1189     return -1;
1190   }
1191 
1192   ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
1193   while (ret > 0) {
1194     ret = mnl_cb_run(buf, ret, seq, portid, link_filter_cb, NULL);
1195     if (ret <= MNL_CB_STOP)
1196       break;
1197     ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
1198   }
1199   if (ret < 0) {
1200     ERROR("netlink plugin: ir_read: mnl_socket_recvfrom failed: %s", STRERRNO);
1201     return (-1);
1202   }
1203 
1204   /* `link_filter_cb' will update `iflist' which is used here to iterate
1205    * over all interfaces. */
1206   for (size_t ifindex = 1; ifindex < iflist_len; ifindex++) {
1207     struct tcmsg *tm;
1208 
1209     if (iflist[ifindex] == NULL)
1210       continue;
1211 
1212     for (size_t type_index = 0; type_index < STATIC_ARRAY_SIZE(type_id);
1213          type_index++) {
1214       if (check_ignorelist(iflist[ifindex], type_name[type_index], NULL)) {
1215         DEBUG("netlink plugin: ir_read: check_ignorelist (%s, %s, (nil)) "
1216               "== TRUE",
1217               iflist[ifindex], type_name[type_index]);
1218         continue;
1219       }
1220 
1221       DEBUG("netlink plugin: ir_read: querying %s from %s (%" PRIsz ").",
1222             type_name[type_index], iflist[ifindex], ifindex);
1223 
1224       nlh = mnl_nlmsg_put_header(buf);
1225       nlh->nlmsg_type = type_id[type_index];
1226       nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
1227       nlh->nlmsg_seq = seq = time(NULL);
1228       tm = mnl_nlmsg_put_extra_header(nlh, sizeof(*tm));
1229       tm->tcm_family = AF_PACKET;
1230       tm->tcm_ifindex = ifindex;
1231 
1232       if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
1233         ERROR("netlink plugin: ir_read: mnl_socket_sendto failed.");
1234         continue;
1235       }
1236 
1237       ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
1238       while (ret > 0) {
1239         ret = mnl_cb_run(buf, ret, seq, portid, qos_filter_cb, &ifindex);
1240         if (ret <= MNL_CB_STOP)
1241           break;
1242         ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
1243       }
1244       if (ret < 0) {
1245         ERROR("netlink plugin: ir_read: mnl_socket_recvfrom failed: %s",
1246               STRERRNO);
1247         continue;
1248       }
1249     } /* for (type_index) */
1250   }   /* for (if_index) */
1251 
1252   return 0;
1253 } /* int ir_read */
1254 
ir_shutdown(void)1255 static int ir_shutdown(void) {
1256   if (nl) {
1257     mnl_socket_close(nl);
1258     nl = NULL;
1259   }
1260 
1261   ir_ignorelist_t *next = NULL;
1262   for (ir_ignorelist_t *i = ir_ignorelist_head; i != NULL; i = next) {
1263     next = i->next;
1264 #if HAVE_REGEX_H
1265     if (i->rdevice != NULL) {
1266       regfree(i->rdevice);
1267       sfree(i->rdevice);
1268     }
1269 #endif
1270     sfree(i->inst);
1271     sfree(i->type);
1272     sfree(i->device);
1273     sfree(i);
1274   }
1275   ir_ignorelist_head = NULL;
1276 
1277   return 0;
1278 } /* int ir_shutdown */
1279 
module_register(void)1280 void module_register(void) {
1281   plugin_register_config("netlink", ir_config, config_keys, config_keys_num);
1282   plugin_register_init("netlink", ir_init);
1283   plugin_register_read("netlink", ir_read);
1284   plugin_register_shutdown("netlink", ir_shutdown);
1285 } /* void module_register */
1286