Connor Crabtree
Connor Crabtree

Reputation: 33

Capturing berr-counter tx/rx from ip link show

I would like to be able to capture the berr-counter values in a shell script. I can view the values with: ip -det link show can0 which gives:

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

I could just parse this output and capture the tx/rx berr-counter, but I would rather capture these values directly. So, I have been trying find where to access these values. I dug into https://github.com/shemminger/iproute2 's code and found where these values are being printed in ip/iplink_can.c in the function:

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

There is the code:

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);
}

And at the bottom of the same file there is a struct:

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,
};

But I can't find anywhere where can_print_opt, or can_link_util.print_opt are called, and I haven't found any success sifting through all of the struct rtattr in the repo.

I'm not sure where to go from here to get these values other than just grabbing them from the output of ip -det link show can0

Upvotes: 1

Views: 1552

Answers (1)

YaGa
YaGa

Reputation: 41

Maybe a little bit late, but I was trying the same thing : access CAN interface state and error counters from within a userspace application, without calling ip and parsing output. As you did, I explored iproute2's code, and then read some documentation about netlink for interacting with network devices. Mainly what you have to do is to send an RTM_GETLINK message to a netlink socket, then parse the response, that is a nested list of netlink attributes.

I found this very interesting starting point : http://iijean.blogspot.com/2010/03/howto-get-list-of-network-interfaces-in.html In this blog the link to full code is broken, but it's available here : https://gist.github.com/cl4u2/5204374. Note that instead of doing all this "manually", it is also possible to use libnetlink.

Based on this, I was able to write a test code - quick and dirty - that does what you want. You only need to determine my ifIndex_ variable, which is the integer index of your CAN network interface (can be determined by a SIOCGIFINDEX ioctl on your socketcan 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;

Upvotes: 3

Related Questions