/* For terms of usage/redistribution/modification see the LICENSE file */
/* For authors and contributors see the AUTHORS file */

/***

ifaces.c - routine that determines whether a given interface is supported
		by IPTraf

***/

#include "iptraf-ng-compat.h"

#include "error.h"

/*
 * Open /proc/net/dev and move file pointer past the two table header lines
 * at the top of the file.
 */

FILE *open_procnetdev(void)
{
	FILE *fd;
	char buf[161];

	fd = fopen("/proc/net/dev", "r");

	/*
	 * Read and discard the table header lines in the file
	 */

	if (fd != NULL) {
		fgets(buf, 160, fd);
		fgets(buf, 160, fd);
	}

	return fd;
}

/*
 * Get the next interface from /proc/net/dev.
 */
int get_next_iface(FILE * fd, char *ifname, int n)
{
	char buf[161];

	strcpy(ifname, "");

	if (!feof(fd)) {
		strcpy(buf, "");
		fgets(buf, 160, fd);
		if (strcmp(buf, "") != 0) {
			memset(ifname, 0, n);
			strncpy(ifname, skip_whitespace(strtok(buf, ":")), n);
			if (ifname[n - 1] != '\0')
				strcpy(ifname, "");
			return 1;
		}
	}
	return 0;
}

int dev_up(char *iface)
{
	int fd;
	int ir;
	struct ifreq ifr;

	fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

	strcpy(ifr.ifr_name, iface);
	ir = ioctl(fd, SIOCGIFFLAGS, &ifr);

	close(fd);

	if ((ir != 0) || (!(ifr.ifr_flags & IFF_UP)))
		return 0;

	return 1;
}

void err_iface_down(void)
{
	write_error("Specified interface not active");
}

int dev_get_ifindex(const char *iface)
{
	int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fd == -1)
		return fd;

	struct ifreq ifr;
	strcpy(ifr.ifr_name, iface);
	int ir = ioctl(fd, SIOCGIFINDEX, &ifr);

	/* need to preserve errno across call to close() */
	int saved_errno = errno;

	close(fd);

	/* bug out if ioctl() failed */
	if (ir != 0) {
		errno = saved_errno;
		return ir;
	}

	return ifr.ifr_ifindex;
}

int dev_get_mtu(const char *iface)
{
	int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fd == -1)
		return fd;

	struct ifreq ifr;
	strcpy(ifr.ifr_name, iface);
	int ir = ioctl(fd, SIOCGIFMTU, &ifr);

	/* need to preserve errno across call to close() */
	int saved_errno = errno;

	close(fd);

	/* bug out if ioctl() failed */
	if (ir != 0) {
		errno = saved_errno;
		return ir;
	}

	return ifr.ifr_mtu;
}

int dev_get_flags(const char *iface)
{
	int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fd == -1)
		return fd;

	struct ifreq ifr;
	strcpy(ifr.ifr_name, iface);
	int ir = ioctl(fd, SIOCGIFFLAGS, &ifr);

	/* need to preserve errno across call to close() */
	int saved_errno = errno;

	close(fd);

	/* bug out if ioctl() failed */
	if (ir != 0) {
		errno = saved_errno;
		return ir;
	}

	return ifr.ifr_flags;
}

int dev_set_flags(const char *iface, int flags)
{
	int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fd == -1)
		return fd;

	struct ifreq ifr;
	strcpy(ifr.ifr_name, iface);
	int ir = ioctl(fd, SIOCGIFFLAGS, &ifr);
	if (ir == -1)
		goto err;

	ifr.ifr_flags |= flags;
	ir = ioctl(fd, SIOCSIFFLAGS, &ifr);

	int saved_errno;
err:	/* need to preserve errno across call to close() */
	saved_errno = errno;

	close(fd);

	/* bug out if ioctl() failed */
	if (ir != 0)
		errno = saved_errno;

	return ir;
}

int dev_clear_flags(const char *iface, int flags)
{
	int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fd == -1)
		return fd;

	struct ifreq ifr;
	strcpy(ifr.ifr_name, iface);
	int ir = ioctl(fd, SIOCGIFFLAGS, &ifr);
	if (ir == -1)
		goto err;

	ifr.ifr_flags &= ~flags;
	ir = ioctl(fd, SIOCSIFFLAGS, &ifr);

	int saved_errno;
err:	/* need to preserve errno across call to close() */
	saved_errno = errno;

	close(fd);

	/* bug out if ioctl() failed */
	if (ir != 0)
		errno = saved_errno;

	return ir;
}

int dev_get_ifname(int ifindex, char *ifname)
{
	int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fd == -1)
		return fd;

	struct ifreq ifr = {
		.ifr_ifindex = ifindex
	};
	int ir = ioctl(fd, SIOCGIFNAME, &ifr);

	/* need to preserve errno across call to close() */
	int saved_errno = errno;

	close(fd);

	/* bug out if ioctl() failed */
	if (ir != 0) {
		errno = saved_errno;
		return ir;
	}

	strncpy(ifname, ifr.ifr_name, IFNAMSIZ);
	return ir;
}

static int dev_bind_ifindex(int fd, const int ifindex)
{
	struct sockaddr_ll fromaddr;
	socklen_t addrlen = sizeof(fromaddr);

	fromaddr.sll_family = AF_PACKET;
	fromaddr.sll_protocol = htons(ETH_P_ALL);
	fromaddr.sll_ifindex = ifindex;
	return bind(fd, (struct sockaddr *) &fromaddr, addrlen);
}

int dev_bind_ifname(int fd, const char * const ifname)
{
	int ifindex = 0;

	if (ifname) {
		int ir;
		struct ifreq ifr;

		strcpy(ifr.ifr_name, ifname);
		ir = ioctl(fd, SIOCGIFINDEX, &ifr);
		if (ir)
			return ir;
		ifindex = ifr.ifr_ifindex;
	}

	return dev_bind_ifindex(fd, ifindex);
}
