Skip to content

Instantly share code, notes, and snippets.

@javiermon
Last active January 7, 2022 18:13
Show Gist options
  • Save javiermon/43a0b9e1c07abd4b13df to your computer and use it in GitHub Desktop.
Save javiermon/43a0b9e1c07abd4b13df to your computer and use it in GitHub Desktop.
nprocnetdev.c: print proc/net/dev via netlink sockets
/*
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;
}
@chn-lee-yumi
Copy link

A very nice example for me to learn netlink! Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment