Created
September 1, 2019 12:03
-
-
Save utky/de8b37756f8664d0045f829292657ae5 to your computer and use it in GitHub Desktop.
Example which communicates with Linux Netlink subsystem to fetch interface information
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
#include <stdio.h> | |
#include <errno.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <net/if.h> | |
#include <sys/socket.h> | |
#include <linux/rtnetlink.h> | |
#include <linux/if_arp.h> | |
#include <linux/types.h> | |
#define MAX_PAYLOAD 1024 | |
#define MAX_RECV_BUF_LEN 32768 | |
int main(int argc, char *argv[]) | |
{ | |
char *cmd_str; | |
if (argc < 2) { | |
printf("Please specify ifname\n"); | |
return 1; | |
} | |
cmd_str = argv[1]; | |
int fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); | |
if (fd < 0) { | |
perror("Opening socket failed"); | |
return errno; | |
} | |
int pid = getpid(); | |
struct sockaddr_nl sa; | |
memset(&sa, 0, sizeof(sa)); | |
sa.nl_family = AF_NETLINK; | |
sa.nl_pid = pid; | |
if (bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0) { | |
perror("Binding socket to address failed"); | |
goto handle_failure; | |
} | |
/* | |
* Build Netlink message header | |
*/ | |
char *ifname = cmd_str; | |
unsigned int ifindex = if_nametoindex(ifname); | |
if (ifindex == 0) { | |
perror("Finding ifindex failed"); | |
goto handle_failure; | |
} | |
struct ifinfomsg iim = { | |
.ifi_family = AF_UNSPEC, | |
.ifi_index = ifindex, | |
.ifi_type = ARPHRD_ETHER, | |
.ifi_change = 0xFFFFFFFF | |
}; | |
__u16 netlink_flags = NLM_F_REQUEST | NLM_F_DUMP; | |
struct nlmsghdr nh = { | |
.nlmsg_len = NLMSG_LENGTH(sizeof(iim)), | |
.nlmsg_type = RTM_GETLINK, | |
.nlmsg_flags = netlink_flags, | |
.nlmsg_seq = 1, | |
.nlmsg_pid = pid | |
}; | |
struct { | |
struct nlmsghdr n; | |
struct ifinfomsg i; | |
char buf[2014]; | |
} req = { | |
.n = nh, | |
.i = iim | |
}; | |
struct iovec iov = { &req, req.n.nlmsg_len }; | |
struct msghdr msg = { &sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; | |
if (sendmsg(fd, &msg, 0) < 0) { | |
perror("Sending message failed"); | |
goto handle_failure; | |
} | |
/* | |
* Receive message | |
*/ | |
/* Read message from kernel */ | |
char recv_buf[MAX_RECV_BUF_LEN]; | |
struct iovec riov = { | |
.iov_base = &recv_buf, | |
.iov_len = MAX_RECV_BUF_LEN | |
}; | |
msg.msg_iov = &riov; | |
msg.msg_iovlen = 1; | |
int recv_len = recvmsg(fd, &msg, 0); | |
if (recv_len < 0) { | |
perror("Receiving message failed"); | |
goto handle_failure; | |
} | |
struct nlmsghdr *rnh; | |
for (rnh = (struct nlmsghdr *) recv_buf; NLMSG_OK (rnh, recv_len); rnh = NLMSG_NEXT (rnh, recv_len)) { | |
/* The end of multipart message */ | |
if (rnh->nlmsg_type == NLMSG_DONE) { | |
printf("NLMSG_DONE detected\n"); | |
break; | |
} | |
if (rnh->nlmsg_type == NLMSG_ERROR) { | |
perror("Receiving message failed"); | |
goto handle_failure; | |
} | |
printf("Received nlmsghdr\n"); | |
printf("\tnlhdr address: %p\n", rnh); | |
printf("\tnlmsg_len: %d\n", rnh->nlmsg_len); | |
printf("\tnlmsg_type: %d\n", rnh->nlmsg_type); | |
printf("\tnlmsg_flags: %x\n", rnh->nlmsg_flags); | |
printf("\tnlmsg_seq: %d\n", rnh->nlmsg_seq); | |
printf("\tnlmsg_pid: %d\n", rnh->nlmsg_pid); | |
struct ifinfomsg *riim = NLMSG_DATA(rnh); | |
printf("Received ifinfomsg\n"); | |
printf("\tifi_type: %d\n", riim->ifi_type); | |
printf("\tifi_index: %d\n", riim->ifi_index); | |
printf("\tifi_flags: %x\n", riim->ifi_flags); | |
printf("\tifi_change: %x\n", riim->ifi_change); | |
} | |
close(fd); | |
return 0; | |
handle_failure: | |
close(fd); | |
return errno; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Testing with Ubuntu 20.04 running Linux 5.11.0...
I get "Operation not permitted" if I don't run this as root/sudo. But if I comment out line 35
sa.nl_pid = pid;
I can run it fine as a non-root/sudo.I get information for all interfaces, not just the one named on the command line. If I remove
NLM_F_DUMP
on line 58, then it gives information for just the named interface.