Last active
January 7, 2022 18:13
-
-
Save javiermon/43a0b9e1c07abd4b13df to your computer and use it in GitHub Desktop.
nprocnetdev.c: print proc/net/dev via netlink sockets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
gcc -g -o nprocnetdev nprocnetdev.c | |
Originally based on: | |
http://www.iijlab.net/~jean/iflist.c | |
Reference: | |
http://iijean.blogspot.com/2010/03/howto-get-list-of-network-interfaces-in.html | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <unistd.h> | |
#include <linux/netlink.h> | |
#include <linux/rtnetlink.h> | |
#include <sys/ioctl.h> | |
#include <net/if.h> | |
#include <signal.h> | |
#include <errno.h> | |
#define IFLIST_REPLY_BUFFER 8192 | |
static int keep_running = 0; | |
void intHandler(int dummy) { | |
keep_running = 0; | |
} | |
/* | |
define the message structure : | |
. a netlink message | |
. a "general form of address family dependent" message, | |
i.e. how to tell which AF we are interested in | |
*/ | |
typedef struct nl_req_s nl_req_t; | |
struct nl_req_s { | |
struct nlmsghdr hdr; | |
struct rtgenmsg gen; | |
}; | |
void rtnl_print_link(struct nlmsghdr *h) | |
{ | |
struct ifinfomsg *iface; | |
struct rtattr *attribute; | |
struct rtnl_link_stats *stats; | |
char name[IFNAMSIZ]; | |
int len; | |
iface = NLMSG_DATA(h); | |
len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*iface)); | |
/* loop over all attributes for the NEWLINK message */ | |
for (attribute = IFLA_RTA(iface); RTA_OK(attribute, len); attribute = RTA_NEXT(attribute, len)) | |
{ | |
switch(attribute->rta_type) | |
{ | |
case IFLA_IFNAME: | |
strcpy(name, (char *) RTA_DATA(attribute)); | |
break; | |
case IFLA_STATS: | |
stats = (struct rtnl_link_stats *) RTA_DATA(attribute); | |
printf("%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu " | |
"%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n", | |
name, | |
(long unsigned int) stats->rx_bytes, | |
(long unsigned int) stats->rx_packets, | |
(long unsigned int) stats->rx_errors, | |
(long unsigned int) stats->rx_dropped + | |
(long unsigned int) stats->rx_missed_errors, | |
(long unsigned int) stats->rx_fifo_errors, | |
(long unsigned int) stats->rx_length_errors + | |
(long unsigned int) stats->rx_over_errors + | |
(long unsigned int) stats->rx_crc_errors + | |
(long unsigned int) stats->rx_frame_errors, | |
(long unsigned int) stats->rx_compressed, | |
(long unsigned int) stats->multicast, | |
(long unsigned int) stats->tx_bytes, | |
(long unsigned int) stats->tx_packets, | |
(long unsigned int) stats->tx_errors, | |
(long unsigned int) stats->tx_dropped, | |
(long unsigned int) stats->tx_fifo_errors, | |
(long unsigned int) stats->collisions, | |
(long unsigned int) stats->tx_carrier_errors + | |
(long unsigned int) stats->tx_aborted_errors + | |
(long unsigned int) stats->tx_window_errors + | |
(long unsigned int) stats->tx_heartbeat_errors, | |
(long unsigned int) stats->tx_compressed); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
int main(int argc, char **argv) | |
{ | |
int fd; | |
struct sockaddr_nl local; /* our local (user space) side of the communication */ | |
struct sockaddr_nl kernel; /* the remote (kernel space) side of the communication */ | |
struct msghdr rtnl_msg; /* generic msghdr struct for use with sendmsg */ | |
struct iovec io; /* IO vector for sendmsg */ | |
nl_req_t req; /* structure that describes the rtnetlink packet itself */ | |
char reply[IFLIST_REPLY_BUFFER]; /* a large buffer to receive lots of link information */ | |
pid_t pid = getpid(); /* our process ID to build the correct netlink address */ | |
unsigned int sleep_sec = 0; | |
/* | |
prepare netlink socket for kernel/userland communication | |
we are interested in the ROUTE flavor. | |
There are others like XFRM, but to deal with links, addresses and obviously routes, | |
you have to use NETLINK_ROUTE. | |
*/ | |
if (argc == 2) | |
{ | |
sleep_sec = atoi(argv[1]); | |
keep_running = 1; | |
signal(SIGINT, intHandler); | |
} | |
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
memset(&local, 0, sizeof(local)); /* fill-in local address information */ | |
local.nl_family = AF_NETLINK; | |
local.nl_pid = pid; | |
local.nl_groups = 0; | |
if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) | |
{ | |
perror("cannot bind, are you root ? if yes, check netlink/rtnetlink kernel support"); | |
return -1; | |
} | |
/* RTNL socket is ready for use, prepare and send request */ | |
do | |
{ | |
int msg_done = 0; /* some flag to end loop parsing */ | |
/* print proc-like header: */ | |
printf("Inter-| Receive " | |
" | Transmit\n" | |
" face |bytes packets errs drop fifo frame " | |
"compressed multicast|bytes packets errs " | |
"drop fifo colls carrier compressed\n"); | |
memset(&rtnl_msg, 0, sizeof(rtnl_msg)); | |
memset(&kernel, 0, sizeof(kernel)); | |
memset(&req, 0, sizeof(req)); | |
kernel.nl_family = AF_NETLINK; /* fill-in kernel address (destination of our message) */ | |
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); | |
req.hdr.nlmsg_type = RTM_GETLINK; | |
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; | |
req.hdr.nlmsg_seq = 1; | |
req.hdr.nlmsg_pid = pid; | |
req.gen.rtgen_family = AF_PACKET; /* no preferred AF, we will get *all* interfaces */ | |
io.iov_base = &req; | |
io.iov_len = req.hdr.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(fd, (struct msghdr *) &rtnl_msg, 0) < 0) | |
{ | |
fprintf(stderr, "Error in sendmsg %s", strerror(errno)); | |
} | |
while (!msg_done) | |
{ | |
/* parse reply */ | |
int len; | |
struct nlmsghdr *msg_ptr; /* pointer to current message part */ | |
struct msghdr rtnl_reply; /* generic msghdr structure for use with recvmsg */ | |
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 = IFLIST_REPLY_BUFFER; | |
rtnl_reply.msg_iov = &io; | |
rtnl_reply.msg_iovlen = 1; | |
rtnl_reply.msg_name = &kernel; | |
rtnl_reply.msg_namelen = sizeof(kernel); | |
/* read as much data as fits in the receive buffer */ | |
if ((len = recvmsg(fd, &rtnl_reply, 0)) != 0) | |
{ | |
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: /* this is the special meaning NLMSG_DONE message we asked for by using NLM_F_DUMP flag */ | |
msg_done = 1; | |
break; | |
case RTM_NEWLINK: /* this is a RTM_NEWLINK message, which contains lots of information about a link */ | |
rtnl_print_link(msg_ptr); | |
break; | |
default: /* for education only, print any message that would not be DONE or NEWLINK, | |
which should not happen here */ | |
printf("message type %d, length %d\n", msg_ptr->nlmsg_type, msg_ptr->nlmsg_len); | |
break; | |
} | |
} | |
} | |
} | |
sleep(sleep_sec); | |
} | |
while (keep_running && (sleep_sec != 0)); | |
/* clean up and finish properly */ | |
close(fd); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A very nice example for me to learn netlink! Thanks!