statystyki interfejsu sieciowego Linux

0

Chciałbym w moim programie monitorować ilość przesłanych danych na wybranych interfejsach sieciowych.
Czy odczytywanie wartości z plików /sys/class/net/... to zalecana metoda ?

np. std::ifstream ethFile("/sys/class/net/eth1/statistics/rx_bytes"); i tak co 200ms ?

Ewentualnie jakaś prosta biblioteka do tego ?

1
  1. Proste podejście.
    Jeśli wiesz co to ioctl to użyj ioctl. Jeśli nie to może zostań przy odczytywaniu tych wartości z /sys.

  2. Mniej proste podejście.
    Użyj netlink-a. A odnośnie biblioteki to zobacz może tą: https://www.infradead.org/~tgr/libnl/

Jeśli wpadniesz np. na pomysł, żeby odczytywać statystyki dużo częściej niż co 200 ms to polecam jednak ioctl albo netlink.

0

@userek_jakis: dziekuję obejrzałem odpowiedzi chatgpt (mam wrażenie ostatnio ze zachowuje sie jak dziewczyna z fochem)

na mojej platformie chyba nie ma SIOCGIFSTATS w nagłówkach a jak sam zadeklarowałem wartość to dostałem ioctl: Inappropriate ioctl for device

#include <iostream>
#include <cstring>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <net/if.h>

// missing !!! add from https://github.com/haiku/haiku/blob/master/headers/posix/sys/sockio.h
#ifndef SIOCGIFSTATS
    #define SIOCGIFSTATS			8929	/* get interface stats */
#endif

int main() {
    const char* interface_name = "eth1"; // Zmień na nazwę swojego interfejsu
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket");
        return -1;
    }

    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, interface_name, IFNAMSIZ-1);

    if (ioctl(sockfd, SIOCGIFSTATS, &ifr) < 0) {
        perror("ioctl");
        close(sockfd);
        return -1;
    }

    struct rtnl_link_stats* stats = (struct rtnl_link_stats*)ifr.ifr_data;
    std::cout << "Bytes sent: " << stats->tx_bytes << std::endl;
    std::cout << "Bytes received: " << stats->rx_bytes << std::endl;

    close(sockfd);
    return 0;
}

A tutaj wersja netlink , ale zwraca durne wyniki

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>


// ten przyklad chyba nie dziala praowidlowo
// nie zwraca ilosci danych z interfejsu sieciowego

#define BUFSIZE 8192

int main() {
    int sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sockfd < 0) {
        perror("socket");
        return -1;
    }

    struct sockaddr_nl sa;
    memset(&sa, 0, sizeof(sa));
    sa.nl_family = AF_NETLINK;
    sa.nl_groups = RTMGRP_LINK;

    if (bind(sockfd, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
        perror("bind");
        close(sockfd);
        return -1;
    }

    struct {
        struct nlmsghdr nlh;
        struct ifinfomsg ifi;
    } req;

    memset(&req, 0, sizeof(req));
    req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
    req.nlh.nlmsg_type = RTM_GETLINK;
    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
    req.ifi.ifi_family = AF_UNSPEC;

    if (send(sockfd, &req, req.nlh.nlmsg_len, 0) < 0) {
        perror("send");
        close(sockfd);
        return -1;
    }

    char buf[BUFSIZE];
    int len = recv(sockfd, buf, sizeof(buf), 0);
    if (len < 0) {
        perror("recv");
        close(sockfd);
        return -1;
    }

    struct nlmsghdr *nlh = (struct nlmsghdr*)buf;
    for (; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
        if (nlh->nlmsg_type == NLMSG_DONE) break;
        if (nlh->nlmsg_type == NLMSG_ERROR) {
            std::cerr << "Netlink error" << std::endl;
            close(sockfd);
            return -1;
        }

        struct ifinfomsg *ifi = (struct ifinfomsg*)NLMSG_DATA(nlh);
        struct rtattr *rta = IFLA_RTA(ifi);
        int rta_len = IFLA_PAYLOAD(nlh);

        for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
            if (rta->rta_type == IFLA_STATS) {
                struct rtnl_link_stats *stats = (struct rtnl_link_stats*)RTA_DATA(rta);
                std::cout << "Bytes tx: " << stats->tx_bytes << std::endl;
                std::cout << "Bytes rx: " << stats->rx_bytes << std::endl;
            }
        }
    }

    close(sockfd);
    return 0;
}

obejrzę może kod jakiegoś programu który pokazuje statystyki interfejsu i mam nadzieje ze to nie będzie to z plików /sys ;)

1

Z tego co widzę to ifconfig nadal zwraca statystyki odnośnie ilości pobranych bajtów więc jest jakaś metoda pobierająca je. Kiedyś ifconfig używał ioctl-a ale już jakiś czas nie zaglądałem do jego źródeł.
Nie jestem natomiast pewien czy SIOCGIFSTATS jest w tej chwili wspierany przez kernel. Być może jest już tylko wsparcie dla netlinka.

0

sprawdziłem ifconfig na debbuger

statystyki sa z pliku '/proc/net/dev'
https://github.com/ecki/net-tools/blob/master/lib/interface.c#L323

0

Ewentualnie jakaś prosta biblioteka do tego

Może po prostu sprawdź jak to robi prometheus-node-exporter? To pewnie Go ale znajdziesz co jest pod spodem.

1

@kelog: pod spodem jest netlink a jak nie ma w systemie to /proc/net/dev

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.