/**
 * @file posix/pif.c  POSIX network interface code
 *
 * Copyright (C) 2010 Creytiv.com
 */
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#define __USE_POSIX 1  /**< Use POSIX code */
#include <netdb.h>
#define __USE_MISC 1   /**< Use MISC code */
#include <net/if.h>
#include <arpa/inet.h>
/*#include <net/if_arp.h>*/
#ifdef __sun
#include <sys/sockio.h>
#endif
#include <re_types.h>
#include <re_fmt.h>
#include <re_mbuf.h>
#include <re_sa.h>
#include <re_net.h>


#define DEBUG_MODULE "posixif"
#define DEBUG_LEVEL 5
#include <re_dbg.h>


/**
 * Get IP address for a given network interface
 *
 * @param ifname  Network interface name
 * @param af      Address Family
 * @param ip      Returned IP address
 *
 * @return 0 if success, otherwise errorcode
 *
 * @deprecated Works for IPv4 only
 */
int net_if_getaddr4(const char *ifname, int af, struct sa *ip)
{
	struct addrinfo hints, *res, *r;
	int error, err;

	if (AF_INET != af)
		return EAFNOSUPPORT;

	memset(&hints, 0, sizeof(hints));
	/* set-up hints structure */
	hints.ai_family   = PF_UNSPEC;
	hints.ai_flags    = AI_PASSIVE;
	hints.ai_socktype = SOCK_DGRAM;
	error = getaddrinfo(NULL, "0", &hints, &res);
	if (error) {
		DEBUG_WARNING("get_ifaddr: getaddrinfo(): %s\n",
			      gai_strerror(error));
		return EADDRNOTAVAIL;
	}

	err = ENOENT;
	for (r = res; r; r = r->ai_next) {
		struct ifreq ifrr;
		int fd = -1;

		fd = socket(r->ai_family, SOCK_DGRAM, 0);
		if (fd < 0) {
			continue;
		}

		ifrr.ifr_addr.sa_family = r->ai_family;
		str_ncpy(ifrr.ifr_name, ifname, sizeof(ifrr.ifr_name));

		if (ioctl(fd, SIOCGIFADDR, &ifrr) < 0) {
			err = errno;
			goto next;
		}

		err = sa_set_sa(ip, &ifrr.ifr_ifru.ifru_addr);

	next:
		(void)close(fd);
	}

	freeaddrinfo(res);
	return err;
}


/**
 * Enumerate all network interfaces
 *
 * @param ifh Interface handler
 * @param arg Handler argument
 *
 * @return 0 if success, otherwise errorcode
 *
 * @deprecated Works for IPv4 only
 */
int net_if_list(net_ifaddr_h *ifh, void *arg)
{
	struct ifreq ifrv[32], *ifr;
	struct ifconf ifc;
	int sockfd = -1;
	int err = 0;

	if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) {
		err = errno;
		DEBUG_WARNING("interface list: socket(): (%s)\n",
			      strerror(err));
		goto out;
	}

	ifc.ifc_len = sizeof(ifrv);
	ifc.ifc_req = ifrv;

	if (0 != ioctl(sockfd, SIOCGIFCONF, &ifc)) {
		err = errno;
		DEBUG_WARNING("interface list: ioctl SIOCFIFCONF: %s\n",
			      strerror(err));
		goto out;
	}

	for (ifr = ifc.ifc_req;
	     (char *)ifr < (ifc.ifc_buf + ifc.ifc_len);
	     ++ifr) {
		struct ifreq ifrr;
		struct sa sa;

		if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data)
			continue;  /* duplicate, skip it */

		if (ioctl(sockfd, SIOCGIFFLAGS, ifr))
			continue;  /* failed to get flags, skip it */

#if 0
		if (ifr->ifr_flags & IFF_LOOPBACK)
			continue;
#endif

		if (!(ifr->ifr_flags & IFF_UP))
			continue;

		ifrr.ifr_addr.sa_family = AF_INET;
		str_ncpy(ifrr.ifr_name, ifr->ifr_name, sizeof(ifrr.ifr_name));

		if (ioctl(sockfd, SIOCGIFADDR, &ifrr) < 0) {
			err = errno;
			continue;
		}

		err = sa_set_sa(&sa, &ifrr.ifr_ifru.ifru_addr);
		if (err) {
			DEBUG_WARNING("if_list: sa_set_sa %s\n",
				      strerror(err));
			break;
		}

		if (ifh && ifh(ifr->ifr_name, &sa, arg))
			break;
	}

 out:
	if (sockfd >= 0)
		(void)close(sockfd);

	return err;
}
