#include <errno.h>
#include <error.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <inttypes.h>
#include <net/ethernet.h>
#include <netinet/ether.h>






#define IF_NAME "br0"
#define LL_NET_ADDRESS "169.254.0.0"
#define LL_NET_MASK "255.255.0.0"
#define HWADDR_LEN 17


int parse_netlink_response(char * buf, int buf_size, uint32_t * linklocal_address_p, uint32_t * ipaddr_p, struct ether_addr * hwaddr);

int get_ipdata(uint32_t * linklocal_address_p, uint32_t * ipaddr_p, struct ether_addr * hwaddr)
{
	struct rtattr *rta;
	int status, ifaddr_nlmsg_len, ifinfo_nlmsg_len, packet_length;
	char buf[16384];	/* arbitrary size */

	int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);

	struct nlmsghdr  *ifaddr_hdr = NULL;
	struct nlmsghdr  *ifinfo_hdr = NULL;
	struct ifaddrmsg *ifaddr_msg = NULL;
	struct ifinfomsg *ifinfo_msg = NULL;
printf("ifaddr_hdr=%p\n", ifaddr_hdr);
printf("ifaddr_msg=%p\n", ifaddr_msg);
printf("ifinfo_hdr=%p\n", ifinfo_hdr);
printf("ifinfo_msg=%p\n\n", ifinfo_msg);
	
	ifaddr_nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
	ifinfo_nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
	packet_length = ifaddr_nlmsg_len + ifinfo_nlmsg_len;

printf("ifaddr_nlmsg_len=%d\n",ifaddr_nlmsg_len); 
printf("ifinfo_nlmsg_len=%d\n",ifinfo_nlmsg_len); 
printf("packet_length=%d\n\n",packet_length); 
	


	ifaddr_hdr = (struct nlmsghdr *) malloc(packet_length);
	memset(ifaddr_hdr, 0, packet_length);
printf("ifaddr_hdr=%p\n", ifaddr_hdr);
printf("ifaddr_msg=%p\n", ifaddr_msg);
printf("ifinfo_hdr=%p\n", ifinfo_hdr);
printf("ifinfo_msg=%p\n\n", ifinfo_msg);

	// Set NL message header
	ifaddr_hdr->nlmsg_len   = ifaddr_nlmsg_len;
	ifaddr_hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MULTI;
	ifaddr_hdr->nlmsg_type  = RTM_GETADDR;

	// set payload sruct
	ifaddr_msg = (struct ifaddrmsg *) NLMSG_DATA(ifaddr_hdr);
	ifaddr_msg->ifa_family = AF_INET;
	
printf("ifaddr_hdr=%p\n", ifaddr_hdr);
printf("ifaddr_msg=%p\n", ifaddr_msg);
printf("ifinfo_hdr=%p\n", ifinfo_hdr);
printf("ifinfo_msg=%p\n\n", ifinfo_msg);

	// Set NL message header
	ifinfo_hdr = (struct nlmsghdr *) NLMSG_NEXT(ifaddr_hdr, packet_length);
	ifinfo_hdr->nlmsg_len   = ifaddr_nlmsg_len;
	ifinfo_hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLMSG_DONE;
	ifinfo_hdr->nlmsg_type  = RTM_GETLINK;
printf("ifaddr_hdr=%p\n", ifaddr_hdr);
printf("ifaddr_msg=%p\n", ifaddr_msg);
printf("ifinfo_hdr=%p\n", ifinfo_hdr);
printf("ifinfo_msg=%p\n\n", ifinfo_msg);

	// set payload sruct
	ifinfo_msg = (struct ifinfomsg *) NLMSG_DATA(ifinfo_hdr);
	ifinfo_msg->ifi_family = AF_UNSPEC;
printf("ifaddr_hdr=%p\n", ifaddr_hdr);
printf("ifaddr_msg=%p\n", ifaddr_msg);
printf("ifinfo_hdr=%p\n", ifinfo_hdr);
printf("ifinfo_msg=%p\n\n", ifinfo_msg);

	status = send(fd, ifaddr_hdr, packet_length, 0);
printf("status send(%d)\n", status);

	if (status < 0) {
		perror("send");
		return 1;
	}


	status = recv(fd, buf, sizeof(buf), 0);
printf("status recv(%d)\n", status);

	if (status < 0) {
		perror("recv");
		return 1;
	}

	if(status == 0){
		printf("EOF\n");
		return 1;
	}

	return parse_netlink_response(buf, status, linklocal_address_p, ipaddr_p, hwaddr);
}

int parse_netlink_response(char * buf, int buf_size, uint32_t * linklocal_address_p, uint32_t * ipaddr_p, struct ether_addr * hwaddr){
    	struct nlmsghdr *nlh;
	uint32_t  linklocal_netmask, linklocal_network_address;
	struct in_addr *inp;

	/* Prepare netlink netmask and network address for later validation of address with mask */
	inet_pton(AF_INET, LL_NET_ADDRESS, &linklocal_network_address);
	inet_pton(AF_INET, LL_NET_MASK, &linklocal_netmask);

	/* Get first netlink message header */
    	nlh = (struct nlmsghdr *) buf;
	/* While valid imessage herader is available */
        while ((NLMSG_OK(nlh, buf_size)) && (nlh->nlmsg_type != NLMSG_DONE)) {

		if (nlh->nlmsg_type == RTM_NEWADDR) { /* if this is a IP Addr response */
			struct ifaddrmsg *ifa = (struct ifaddrmsg *) NLMSG_DATA(nlh);	/* Read ifaddrmsg */
			struct rtattr *rth = IFA_RTA(ifa);
			int rtl = IFA_PAYLOAD(nlh);
			char name[IFNAMSIZ];

			/* Only stuff about interface IF_NAME is relevant */
			if_indextoname(ifa->ifa_index, name);
			if(ifa->ifa_family == AF_INET && strncmp ( name, IF_NAME, IFNAMSIZ) == 0 ){
				/* Read first rtattr attribute header */
				while (rtl && RTA_OK(rth, rtl)) {
					if(rth->rta_type == IFA_ADDRESS){
						inp = (struct in_addr *)RTA_DATA(rth);	/* Read IP in in_addr */
						
						if(ifa->ifa_prefixlen == 16 && linklocal_network_address == (inp->s_addr & linklocal_netmask)){
							*linklocal_address_p = inp->s_addr; /* This is a addfress in link local scope */
						}else{
							*ipaddr_p = inp->s_addr;	/* This is a regular address */
						}
					}
					rth = RTA_NEXT(rth, rtl);	/* Advance to next rtattr attribute header */
				}
			}

		}else if(nlh->nlmsg_type == RTM_NEWLINK || nlh->nlmsg_type == RTM_DELLINK || nlh->nlmsg_type == RTM_GETLINK){ /*Only link respnses */
			struct ifinfomsg *ifinfo = (struct ifinfomsg *) NLMSG_DATA(nlh);	/* Read ifinfomsg */
			int rtl = IFA_PAYLOAD(nlh);
			struct rtattr *rth = IFLA_RTA(ifinfo);
			char name[IFNAMSIZ];

			/* Only stuff about interface IF_NAME is relevant */
			if_indextoname(ifinfo->ifi_index, name);
			if(strncmp ( name, IF_NAME, IFNAMSIZ) == 0 ){
				/* Read first rtattr attribute header */
				while (rtl && RTA_OK(rth, rtl)) {
					if(rth->rta_type == IFLA_ADDRESS){
						/* Copy ether_addr struct to prepared destination */
						memcpy (hwaddr, (struct ether_addr *)RTA_DATA(rth), sizeof (hwaddr));
					}
					rth = RTA_NEXT(rth, rtl);	/* Advance to next rtattr attribute header */
				}
			}
  		}
		nlh = NLMSG_NEXT(nlh, buf_size);	/* Advance to next netlink message header */
        }
	return 0;
}


int main()
{

	uint32_t  linklocal_address, ipaddr;
	char linklocal_address_s[INET_ADDRSTRLEN];
	char ipaddr_s[INET_ADDRSTRLEN];
	struct ether_addr *hwaddr;
	

	linklocal_address = 0;
	ipaddr = 0;
	hwaddr = (struct ether_addr *) malloc(sizeof(struct ether_addr));

	get_ipdata(&linklocal_address, &ipaddr, hwaddr);

	inet_ntop(AF_INET, &ipaddr, ipaddr_s, INET_ADDRSTRLEN);
	inet_ntop(AF_INET, &linklocal_address, linklocal_address_s, INET_ADDRSTRLEN);
	
	printf("Address: %s\n", ipaddr_s);
	printf("Linklocal Address: %s\n", linklocal_address_s);
	printf("HW Address: %s\n", ether_ntoa(hwaddr));
	return 0;

}
