Захват берр-счетчика tx / rx из ip link show - PullRequest
0 голосов
/ 27 августа 2018

Я хотел бы иметь возможность фиксировать значения счетчика berr в сценарии оболочки. Я могу просмотреть значения с: ip -det link show can0, что дает:

2: can0: <NOARP,ECHO> mtu 16 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000
    link/can  promiscuity 0
    can state STOPPED (berr-counter tx 144 rx 128) restart-ms 100
          bitrate 125000 sample-point 0.866
          tq 133 prop-seg 6 phase-seg1 6 phase-seg2 2 sjw 1
          flexcan: tseg1 4..16 tseg2 2..8 sjw 1..4 brp 1..256 brp-inc 1
          clock 30000000

Я мог бы просто проанализировать этот вывод и захватить счетчик tx / rx berr, но я бы предпочел захватить эти значения напрямую. Итак, я пытался найти, где получить доступ к этим значениям. Я копался в коде https://github.com/shemminger/iproute2 и нашел, где эти значения печатаются в ip/iplink_can.c в функции:

static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])

Есть код:

if (tb[IFLA_CAN_BERR_COUNTER]) {
    struct can_berr_counter *bc =
        RTA_DATA(tb[IFLA_CAN_BERR_COUNTER]);

    fprintf(f, "(berr-counter tx %d rx %d) ", bc->txerr, bc->rxerr);
}

И внизу того же файла есть структура:

struct link_util can_link_util = {
    .id     = "can",
    .maxattr    = IFLA_CAN_MAX,
    .parse_opt  = can_parse_opt,
    .print_opt  = can_print_opt,
    .print_xstats   = can_print_xstats,
    .print_help = can_print_help,
};

Но я не могу найти нигде, где бы вызывали can_print_opt или can_link_util.print_opt, и я не нашел никакого успеха, просматривая все struct rtattr в репо.

Я не уверен, куда идти дальше, чтобы получить эти значения, кроме как просто получить их из вывода ip -det link show can0

1 Ответ

0 голосов
/ 20 ноября 2018

Может быть, немного поздно, но я пытался сделать то же самое: получить доступ к состоянию интерфейса CAN и счетчикам ошибок из приложения пользовательского пространства, не вызывая ip и не анализируя вывод. Как и вы, я изучил код iproute2, а затем прочитал некоторую документацию по netlink для взаимодействия с сетевыми устройствами. В основном вам нужно отправить сообщение RTM_GETLINK в сокет netlink, а затем проанализировать ответ, который представляет собой вложенный список атрибутов netlink.

Я нашел эту очень интересную отправную точку: http://iijean.blogspot.com/2010/03/howto-get-list-of-network-interfaces-in.html В этом блоге ссылка на полный код не работает, но она доступна здесь: https://gist.github.com/cl4u2/5204374. Обратите внимание, что вместо того, чтобы делать все это «вручную», также можно использовать libnetlink.

Исходя из этого, я смог написать тестовый код - быстрый и грязный - который делает то, что вы хотите. Вам нужно только определить мою ifIndex_ переменную, которая является целочисленным индексом вашего сетевого интерфейса CAN (может быть определен с помощью SIOCGIFINDEX ioctl на вашем сокете socket).

printf("Starting rtnetlink stats reading ...\n");
struct sockaddr_nl local;
struct {
    struct nlmsghdr nlh;
    struct ifinfomsg ifinfo;
} request;
struct sockaddr_nl kernel;
struct msghdr rtnl_msg;
struct iovec io;
pid_t pid = getpid();
qint64 rtnetlink_socket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
memset(&local, 0, sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_pid = pid;
local.nl_groups = 0;
if (bind(rtnetlink_socket, (struct sockaddr *) &local, sizeof(local)) < 0) {
    printf("Binding failed !\n");
    return true;
}
printf("Binding successful.\n");
memset(&rtnl_msg, 0, sizeof(rtnl_msg));
memset(&kernel, 0, sizeof(kernel));
memset(&request, 0, sizeof(request));
kernel.nl_family = AF_NETLINK;
request.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
request.nlh.nlmsg_type = RTM_GETLINK;
request.nlh.nlmsg_flags = NLM_F_REQUEST;    // NLM_F_ROOT|NLM_F_MATCH| were originally specified and return all interfaces.
request.nlh.nlmsg_pid = pid;
request.nlh.nlmsg_seq = 1; // Must be monotonically increasing, but we send only one.
// Interface is specified only with index.
request.ifinfo.ifi_family = AF_PACKET;
request.ifinfo.ifi_index = ifIndex_;
request.ifinfo.ifi_change = 0;
io.iov_base = &request;
io.iov_len = request.nlh.nlmsg_len;
rtnl_msg.msg_iov = &io;
rtnl_msg.msg_iovlen = 1;
rtnl_msg.msg_name = &kernel;
rtnl_msg.msg_namelen = sizeof(kernel);
if (sendmsg(rtnetlink_socket, &rtnl_msg, 0) < 0) {
    printf("Sendmsg finished with an error.\n");
    return true;
}
printf("Sendmsg finished successfully.\n");
// Reply reception
int end = 0;
int replyMaxSize = 8192;
char reply[replyMaxSize];
while (!end) {
    int len;
    struct nlmsghdr *msg_ptr;
    struct msghdr rtnl_reply;
    struct iovec io_reply;
    memset(&io_reply, 0, sizeof(io_reply));
    memset(&rtnl_reply, 0, sizeof(rtnl_reply));

    io.iov_base = reply;
    io.iov_len = replyMaxSize;
    rtnl_reply.msg_iov = &io;
    rtnl_reply.msg_iovlen = 1;
    rtnl_reply.msg_name = &kernel;
    rtnl_reply.msg_namelen = sizeof(kernel);
    printf("Waiting for data ...\n");
    len = recvmsg(rtnetlink_socket, &rtnl_reply, 0);
    printf("Received data with length %d.\n", len);
    if (len) {
        for (msg_ptr = (struct nlmsghdr *) reply; NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len)) {
            switch(msg_ptr->nlmsg_type) {
                case NLMSG_DONE:
                    end++;
                    printf("Received NLMSG_DONE end message.\n");
                    break;
                case RTM_NEWLINK:
                    printf("Received RTM_NEWLINK message with multipart flag : %d.\n", msg_ptr->nlmsg_flags & NLM_F_MULTI);
                    if (!(msg_ptr->nlmsg_flags & NLM_F_MULTI)) { end++; }
                    struct ifinfomsg *iface;
                    struct rtattr *attribute;
                    struct rtattr *subAttr;
                    int msgLen, attrPayloadLen;
                    iface = (struct ifinfomsg*)NLMSG_DATA(msg_ptr);
                    msgLen = msg_ptr->nlmsg_len - NLMSG_LENGTH(sizeof(*iface));
                    for (attribute = IFLA_RTA(iface); RTA_OK(attribute, msgLen); attribute = RTA_NEXT(attribute, msgLen)) {
                        switch(attribute->rta_type) {
                            case IFLA_IFNAME:
                                printf("Interface %d name : %s\n", iface->ifi_index, (char *) RTA_DATA(attribute));
                                break;
                            case IFLA_LINKINFO:
                                attrPayloadLen = RTA_PAYLOAD(attribute);
                                printf("Found link information. Parsing %d payload bytes ...\n", attrPayloadLen);
                                for (subAttr = (struct rtattr *)RTA_DATA(attribute); RTA_OK(subAttr, attrPayloadLen); subAttr = RTA_NEXT(subAttr, attrPayloadLen)) {
                                    struct rtattr *subSubAttr;
                                    int subAttrPayloadLen = RTA_PAYLOAD(subAttr);
                                    printf("Found sub-attribute. Type : %d, length : %d.\n", subAttr->rta_type, subAttr->rta_len);
                                    switch (subAttr->rta_type) {
                                        case IFLA_INFO_KIND:
                                            printf("\t Link kind : %s.\n", (char *) RTA_DATA(subAttr));
                                            break;
                                        case IFLA_INFO_DATA:
                                            printf("Found link information data. Parsing %d payload bytes ...\n", RTA_PAYLOAD(subAttr));
                                            for (subSubAttr = (struct rtattr *)RTA_DATA(subAttr); RTA_OK(subSubAttr, subAttrPayloadLen); subSubAttr = RTA_NEXT(subSubAttr, subAttrPayloadLen)) {
                                                printf("Found sub-sub-attribute. Type : %d, length : %d.\n", subSubAttr->rta_type, subSubAttr->rta_len);
                                                switch (subSubAttr->rta_type) {
                                                    case IFLA_CAN_STATE:
                                                    {
                                                        int state = *(int *)RTA_DATA(subSubAttr);
                                                        printf("State : %d\n", state);
                                                        break;
                                                    }
                                                    case IFLA_CAN_BERR_COUNTER:
                                                    {
                                                        struct can_berr_counter *bc = (struct can_berr_counter *)RTA_DATA(subSubAttr);
                                                        printf("Error counters : (berr-counter tx %d rx %d)\n", bc->txerr, bc->rxerr);
                                                        break;
                                                    }
                                                    default:
                                                        break;
                                                }
                                            }
                                            break;
                                        case IFLA_INFO_XSTATS:
                                        default:
                                            break;
                                    }
                                }
                                break;
                            default:
                                printf("New attribute. Type : %d, length : %d.\n", attribute->rta_type, attribute->rta_len);
                                break;
                        }
                    }
                    printf("Finished parsing attributes.\n");
                    break;
                case NLMSG_ERROR:
                    printf("Could not read link details for interface %d.\n", ifIndex_);
                    end++;
                    break;
                default:
                    printf("Received unexpected message ID : %d.\n", msg_ptr->nlmsg_type);
                    break;
            }
            printf("Finished parsing message.\n");
        }
        printf("Finished parsing data.\n");
    }
}
close(rtnetlink_socket);
return true;
...