// SPDX-License-Identifier: GPL-3.0-or-later #include "windows_plugin.h" #include "windows-internals.h" // -------------------------------------------------------------------------------------------------------------------- // network protocols struct network_protocol { const char *protocol; struct { COUNTER_DATA received; COUNTER_DATA sent; COUNTER_DATA delivered; COUNTER_DATA forwarded; RRDSET *st; RRDDIM *rd_received; RRDDIM *rd_sent; RRDDIM *rd_forwarded; RRDDIM *rd_delivered; const char *type; const char *id; const char *family; const char *context; const char *title; long priority; } packets; } networks[] = { { .protocol = "IPv4", .packets = { .received = { .key = "Datagrams Received/sec" }, .sent = { .key = "Datagrams Sent/sec" }, .delivered = { .key = "Datagrams Received Delivered/sec" }, .forwarded = { .key = "Datagrams Forwarded/sec" }, .type = "ipv4", .id = "packets", .family = "packets", .context = "ipv4.packets", .title = "IPv4 Packets", .priority = NETDATA_CHART_PRIO_IPV4_PACKETS, }, }, { .protocol = "IPv6", .packets = { .received = { .key = "Datagrams Received/sec" }, .sent = { .key = "Datagrams Sent/sec" }, .delivered = { .key = "Datagrams Received Delivered/sec" }, .forwarded = { .key = "Datagrams Forwarded/sec" }, .type = "ipv6", .id = "packets", .family = "packets", .context = "ip6.packets", .title = "IPv6 Packets", .priority = NETDATA_CHART_PRIO_IPV6_PACKETS, }, }, { .protocol = "TCPv4", .packets = { .received = { .key = "Segments Received/sec" }, .sent = { .key = "Segments Sent/sec" }, .type = "ipv4", .id = "tcppackets", .family = "tcp", .context = "ipv4.tcppackets", .title = "IPv4 TCP Packets", .priority = NETDATA_CHART_PRIO_IPV4_TCP_PACKETS, }, }, { .protocol = "TCPv6", .packets = { .received = { .key = "Segments Received/sec" }, .sent = { .key = "Segments Sent/sec" }, .type = "ipv6", .id = "tcppackets", .family = "tcp6", .context = "ipv6.tcppackets", .title = "IPv6 TCP Packets", .priority = NETDATA_CHART_PRIO_IPV6_TCP_PACKETS, }, }, { .protocol = "UDPv4", .packets = { .received = { .key = "Datagrams Received/sec" }, .sent = { .key = "Datagrams Sent/sec" }, .type = "ipv4", .id = "udppackets", .family = "udp", .context = "ipv4.udppackets", .title = "IPv4 UDP Packets", .priority = NETDATA_CHART_PRIO_IPV4_UDP_PACKETS, }, }, { .protocol = "UDPv6", .packets = { .received = { .key = "Datagrams Received/sec" }, .sent = { .key = "Datagrams Sent/sec" }, .type = "ipv6", .id = "udppackets", .family = "udp6", .context = "ipv6.udppackets", .title = "IPv6 UDP Packets", .priority = NETDATA_CHART_PRIO_IPV6_UDP_PACKETS, }, }, { .protocol = "ICMP", .packets = { .received = { .key = "Messages Received/sec" }, .sent = { .key = "Messages Sent/sec" }, .type = "ipv4", .id = "icmp", .family = "icmp", .context = "ipv4.icmp", .title = "IPv4 ICMP Packets", .priority = NETDATA_CHART_PRIO_IPV4_ICMP_PACKETS, }, }, { .protocol = "ICMPv6", .packets = { .received = { .key = "Messages Received/sec" }, .sent = { .key = "Messages Sent/sec" }, .type = "ipv6", .id = "icmp", .family = "icmp6", .context = "ipv6.icmp", .title = "IPv6 ICMP Packets", .priority = NETDATA_CHART_PRIO_IPV6_ICMP_PACKETS, }, }, // terminator { .protocol = NULL, } }; struct network_protocol tcp46 = { .packets = { .type = "ip", .id = "tcppackets", .family = "tcp", .context = "ip.tcppackets", .title = "TCP Packets", .priority = NETDATA_CHART_PRIO_IP_TCP_PACKETS, } }; static void protocol_packets_chart_update(struct network_protocol *p, int update_every) { if(!p->packets.st) { p->packets.st = rrdset_create_localhost( p->packets.type , p->packets.id , NULL , p->packets.family , NULL , p->packets.title , "packets/s" , PLUGIN_WINDOWS_NAME , "PerflibNetwork" , p->packets.priority , update_every , RRDSET_TYPE_AREA ); p->packets.rd_received = rrddim_add(p->packets.st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); p->packets.rd_sent = rrddim_add(p->packets.st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); if(p->packets.forwarded.key) p->packets.rd_forwarded = rrddim_add(p->packets.st, "forwarded", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); if(p->packets.delivered.key) p->packets.rd_delivered = rrddim_add(p->packets.st, "delivered", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); } if(p->packets.received.updated) rrddim_set_by_pointer(p->packets.st, p->packets.rd_received, (collected_number)p->packets.received.current.Data); if(p->packets.sent.updated) rrddim_set_by_pointer(p->packets.st, p->packets.rd_sent, (collected_number)p->packets.sent.current.Data); if(p->packets.forwarded.key && p->packets.forwarded.updated) rrddim_set_by_pointer(p->packets.st, p->packets.rd_forwarded, (collected_number)p->packets.forwarded.current.Data); if(p->packets.delivered.key && p->packets.delivered.updated) rrddim_set_by_pointer(p->packets.st, p->packets.rd_delivered, (collected_number)p->packets.delivered.current.Data); rrdset_done(p->packets.st); } static bool do_network_protocol(PERF_DATA_BLOCK *pDataBlock, int update_every, struct network_protocol *p) { if(!p || !p->protocol) return false; PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->protocol); if(!pObjectType) return false; size_t packets = 0; if(p->packets.received.key) packets += perflibGetObjectCounter(pDataBlock, pObjectType, &p->packets.received) ? 1 : 0; if(p->packets.sent.key) packets += perflibGetObjectCounter(pDataBlock, pObjectType, &p->packets.sent) ? 1 : 0; if(p->packets.delivered.key) packets += perflibGetObjectCounter(pDataBlock, pObjectType, &p->packets.delivered) ? 1 :0; if(p->packets.forwarded.key) packets += perflibGetObjectCounter(pDataBlock, pObjectType, &p->packets.forwarded) ? 1 : 0; if(packets) protocol_packets_chart_update(p, update_every); return true; } // -------------------------------------------------------------------------------------------------------------------- // network interfaces struct network_interface { bool collected_metadata; struct { COUNTER_DATA received; COUNTER_DATA sent; RRDSET *st; RRDDIM *rd_received; RRDDIM *rd_sent; } packets; struct { COUNTER_DATA received; COUNTER_DATA sent; RRDSET *st; RRDDIM *rd_received; RRDDIM *rd_sent; } traffic; }; static DICTIONARY *physical_interfaces = NULL, *virtual_interfaces = NULL; static void network_interface_init(struct network_interface *ni) { ni->packets.received.key = "Packets Received/sec"; ni->packets.sent.key = "Packets Sent/sec"; ni->traffic.received.key = "Bytes Received/sec"; ni->traffic.sent.key = "Bytes Sent/sec"; } void dict_interface_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { struct network_interface *ni = value; network_interface_init(ni); } static void initialize(void) { physical_interfaces = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct network_interface)); virtual_interfaces = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct network_interface)); dictionary_register_insert_callback(physical_interfaces, dict_interface_insert_cb, NULL); dictionary_register_insert_callback(virtual_interfaces, dict_interface_insert_cb, NULL); } static void add_interface_labels(RRDSET *st, const char *name, bool physical) { rrdlabels_add(st->rrdlabels, "device", name, RRDLABEL_SRC_AUTO); rrdlabels_add(st->rrdlabels, "interface_type", physical ? "real" : "virtual", RRDLABEL_SRC_AUTO); } static bool is_physical_interface(const char *name) { void *d = dictionary_get(physical_interfaces, name); return d ? true : false; } static bool do_network_interface(PERF_DATA_BLOCK *pDataBlock, int update_every, bool physical) { DICTIONARY *dict = physical_interfaces; PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, physical ? "Network Interface" : "Network Adapter"); if(!pObjectType) return false; uint64_t total_received = 0, total_sent = 0; PERF_INSTANCE_DEFINITION *pi = NULL; for(LONG i = 0; i < pObjectType->NumInstances ; i++) { pi = perflibForEachInstance(pDataBlock, pObjectType, pi); if(!pi) break; if(!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); if(strcasecmp(windows_shared_buffer, "_Total") == 0) continue; if(!physical && is_physical_interface(windows_shared_buffer)) // this virtual interface is already reported as physical interface continue; struct network_interface *d = dictionary_set(dict, windows_shared_buffer, NULL, sizeof(*d)); if(!d->collected_metadata) { // TODO - get metadata about the network interface d->collected_metadata = true; } if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->traffic.received) && perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->traffic.sent)) { if(d->traffic.received.current.Data == 0 && d->traffic.sent.current.Data == 0) // this interface has not received or sent any traffic continue; if (unlikely(!d->traffic.st)) { d->traffic.st = rrdset_create_localhost( "net", windows_shared_buffer, NULL, windows_shared_buffer, "net.net", "Bandwidth", "kilobits/s", PLUGIN_WINDOWS_NAME, "PerflibNetwork", NETDATA_CHART_PRIO_FIRST_NET_IFACE, update_every, RRDSET_TYPE_AREA); rrdset_flag_set(d->traffic.st, RRDSET_FLAG_DETAIL); add_interface_labels(d->traffic.st, windows_shared_buffer, physical); d->traffic.rd_received = rrddim_add(d->traffic.st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); d->traffic.rd_sent = rrddim_add(d->traffic.st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); } total_received += d->traffic.received.current.Data; total_sent += d->traffic.sent.current.Data; rrddim_set_by_pointer(d->traffic.st, d->traffic.rd_received, (collected_number)d->traffic.received.current.Data); rrddim_set_by_pointer(d->traffic.st, d->traffic.rd_sent, (collected_number)d->traffic.sent.current.Data); rrdset_done(d->traffic.st); } if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->packets.received) && perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->packets.sent)) { if (unlikely(!d->packets.st)) { d->packets.st = rrdset_create_localhost( "net_packets", windows_shared_buffer, NULL, windows_shared_buffer, "net.packets", "Packets", "packets/s", PLUGIN_WINDOWS_NAME, "PerflibNetwork", NETDATA_CHART_PRIO_FIRST_NET_IFACE + 1, update_every, RRDSET_TYPE_LINE); rrdset_flag_set(d->packets.st, RRDSET_FLAG_DETAIL); add_interface_labels(d->traffic.st, windows_shared_buffer, physical); d->packets.rd_received = rrddim_add(d->packets.st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); d->packets.rd_sent = rrddim_add(d->packets.st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); } rrddim_set_by_pointer(d->packets.st, d->packets.rd_received, (collected_number)d->packets.received.current.Data); rrddim_set_by_pointer(d->packets.st, d->packets.rd_sent, (collected_number)d->packets.sent.current.Data); rrdset_done(d->packets.st); } } if(physical) { static RRDSET *st = NULL; static RRDDIM *rd_received = NULL, *rd_sent = NULL; if (unlikely(!st)) { st = rrdset_create_localhost( "system", "net", NULL, "network", "system.net", "Physical Network Interfaces Aggregated Bandwidth", "kilobits/s", PLUGIN_WINDOWS_NAME, "PerflibNetwork", NETDATA_CHART_PRIO_SYSTEM_NET, update_every, RRDSET_TYPE_AREA); rd_received = rrddim_add(st, "received", NULL, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); rd_sent = rrddim_add(st, "sent", NULL, -8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); } rrddim_set_by_pointer(st, rd_received, (collected_number)total_received); rrddim_set_by_pointer(st, rd_sent, (collected_number)total_sent); rrdset_done(st); } return true; } int do_PerflibNetwork(int update_every, usec_t dt __maybe_unused) { static bool initialized = false; if(unlikely(!initialized)) { initialize(); initialized = true; } DWORD id = RegistryFindIDByName("Network Interface"); if(id == PERFLIB_REGISTRY_NAME_NOT_FOUND) return -1; PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); if(!pDataBlock) return -1; do_network_interface(pDataBlock, update_every, true); do_network_interface(pDataBlock, update_every, false); struct network_protocol *tcp4 = NULL, *tcp6 = NULL; for(size_t i = 0; networks[i].protocol ;i++) { do_network_protocol(pDataBlock, update_every, &networks[i]); if(!tcp4 && strcmp(networks[i].protocol, "TCPv4") == 0) tcp4 = &networks[i]; if(!tcp6 && strcmp(networks[i].protocol, "TCPv6") == 0) tcp6 = &networks[i]; } if(tcp4 && tcp6) { tcp46.packets.received = tcp4->packets.received; tcp46.packets.sent = tcp4->packets.sent; tcp46.packets.received.current.Data += tcp6->packets.received.current.Data; tcp46.packets.sent.current.Data += tcp6->packets.sent.current.Data; protocol_packets_chart_update(&tcp46, update_every); } return 0; }