/**
* Shows the network interfaces using RTNETLINK
*
* @author : Asanga Udugama (adu@comnets.uni-bremen.de)
* @date   : 19-jul-2005
*
*/

#include <bits/sockaddr.h>
#include <stdio.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>


// gcc -o if_show if_show.c

struct {
	struct nlmsghdr		nlmsg_info;
	struct ifaddrmsg	ifaddrmsg_info;
	char			buffer[2048];
} netlink_req;

int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;   
struct msghdr msg_info;
struct iovec iov_info;
char read_buffer[8192];

struct nlmsghdr *nlmsg_ptr;
char *read_ptr;
int nlmsg_len;
struct ifaddrmsg *ifaddrmsg_ptr;
struct rtattr *rtattr_ptr;
int ifaddrmsg_len;
char temp_buffer[8192];
int rtn;
int i;

char ipaddr_str[128];
char localaddr_str[128];
char name_str[128];
char bcastaddr_str[128];

/**
* Send a request to NETLINK to send the ifc info
*/
int send_ifc_read_request()
{
	bzero(&local, sizeof(local));
	local.nl_family = AF_NETLINK;
	local.nl_pad = 0;
	local.nl_pid = getpid();
	local.nl_groups = 0;
	if(bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
		printf("Error in sock bind\n");
		exit(1);
	}


	bzero(&peer, sizeof(peer));
	peer.nl_family = AF_NETLINK;
	peer.nl_pad = 0;
	peer.nl_pid = 0;
	peer.nl_groups = 0;

	bzero(&msg_info, sizeof(msg_info));
	msg_info.msg_name = (void *) &peer;
	msg_info.msg_namelen = sizeof(peer);

	bzero(&netlink_req, sizeof(netlink_req));

	netlink_req.nlmsg_info.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
	netlink_req.nlmsg_info.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
	netlink_req.nlmsg_info.nlmsg_type = RTM_GETADDR;
	netlink_req.nlmsg_info.nlmsg_pid = getpid();

	netlink_req.ifaddrmsg_info.ifa_family = AF_INET;

	iov_info.iov_base = (void *) &netlink_req.nlmsg_info;
	iov_info.iov_len = netlink_req.nlmsg_info.nlmsg_len;
	msg_info.msg_iov = &iov_info;
	msg_info.msg_iovlen = 1;

	rtn = sendmsg(fd, &msg_info, 0);
	if(rtn < 0) {
		printf("Error in sendmsg\n");
		exit(1);
	}
}

/**
* Read the output sent by NETLINK for the get ifc list request given
* in function send_ifc_read_request()
*/
int read_ifc_request_results()
{
	bzero(read_buffer, 8192);

	read_ptr = read_buffer;
	nlmsg_len = 0;
	while(1) {
		rtn = recv(fd, read_ptr, 4096, 0);
		if(rtn < 0) {
			printf("Error in recv\n");
			exit(1);
		}
		nlmsg_ptr = (struct nlmsghdr *) read_ptr;

		if(nlmsg_ptr->nlmsg_type == NLMSG_DONE)	{
			break;
		}

		read_ptr = read_ptr + rtn;
		nlmsg_len = nlmsg_len + rtn;
	}
}

/**
* Extract each ifc entry and print
*/
int process_and_print() 
{
	printf("Network interface list\n");
	nlmsg_ptr = (struct nlmsghdr *) read_buffer;
	for(; NLMSG_OK(nlmsg_ptr, nlmsg_len); nlmsg_ptr = NLMSG_NEXT(nlmsg_ptr, nlmsg_len)) {

		ifaddrmsg_ptr = (struct ifaddrmsg *) NLMSG_DATA(nlmsg_ptr);

		bzero(ipaddr_str, 128);
		bzero(localaddr_str, 128);
		bzero(name_str, 128);
		bzero(bcastaddr_str, 128);

		rtattr_ptr = (struct rtattr *) IFA_RTA(ifaddrmsg_ptr);
		ifaddrmsg_len = IFA_PAYLOAD(nlmsg_ptr);
		for(;RTA_OK(rtattr_ptr, ifaddrmsg_len); rtattr_ptr = RTA_NEXT(rtattr_ptr, ifaddrmsg_len)) {

			switch(rtattr_ptr->rta_type) {
			case IFA_ADDRESS:
				inet_ntop(AF_INET, RTA_DATA(rtattr_ptr), ipaddr_str, 128);
				break;
			case IFA_LOCAL:
				inet_ntop(AF_INET, RTA_DATA(rtattr_ptr), localaddr_str, 128);
				break;
			case IFA_LABEL:
				sprintf(name_str, "%s", (char *) RTA_DATA(rtattr_ptr));
				break;
			case IFA_BROADCAST:
				inet_ntop(AF_INET, RTA_DATA(rtattr_ptr), bcastaddr_str, 128);
				break;
			default:
				break; 

			}
		}

		if(strlen(ipaddr_str) != 0) {
			printf("%s/%d", ipaddr_str, ifaddrmsg_ptr->ifa_prefixlen);
		}

		//if(strlen(localaddr_str) != 0) {
		//	printf(" local %s", localaddr_str);
		//}

		if(strlen(bcastaddr_str) != 0) {
			printf(" bcast %s", bcastaddr_str);
		}

		if(strlen(name_str) != 0) {
			printf(" dev %s %d", name_str, ifaddrmsg_ptr->ifa_index);
		}
		printf("\n");
	}
	printf("\n");
}

int main(int argc, char *argv[])
{
	// open NETLINK socket
	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
	if(fd < 0) {
		printf("Error in sock open\n");
		exit(1);
	}

	send_ifc_read_request();
	read_ifc_request_results();
	process_and_print();

	close(fd);

	exit(0);
}
