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