/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2017-2021 Brett Sheffield <bacs@librecast.net> */

#include <librecast/if.h>
#include "librecast_pvt.h"

#include <errno.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <net/if.h>
#if HAVE_NET_IF_TAP_H
# include <net/if_tap.h>
#elif HAVE_NET_IF_TUN_H
# include <net/if_tun.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>

static inline int lc_ctrl_socket(void)
{
	return socket(AF_LOCAL, SOCK_STREAM, 0);
}

int lc_bridge_del(lc_ctx_t *ctx, const char *brname)
{
	(void)ctx; (void)brname;
	errno = ENOTSUP;
	return -1;
}

int lc_bridge_add(lc_ctx_t *ctx, const char *brname)
{
	(void)ctx; (void)brname;
	errno = ENOTSUP;
	return -1;
}

int lc_bridge_delif(lc_ctx_t *ctx, const char *brname, const char *ifname)
{
	(void)ctx; (void)brname; (void)ifname;
	errno = ENOTSUP;
	return -1;
}

int lc_bridge_addif(lc_ctx_t *ctx, const char *brname, const char *ifname)
{
	(void)ctx; (void)brname; (void)ifname;
	errno = ENOTSUP;
	return -1;
}

int lc_link_set(lc_ctx_t *ctx, char *ifname, int up)
{
	struct ifreq ifr;
	int err = 0;

	if (ctx->sock == -1) ctx->sock = lc_ctrl_socket();
	if (ctx->sock == -1) return -1;
	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
	if (ioctl(ctx->sock, SIOCGIFFLAGS, &ifr) == -1) {
		return -1;
	}
	ifr.ifr_flags = (up) ? ifr.ifr_flags | IFF_UP
			     : ifr.ifr_flags & ~IFF_UP;
	err = ioctl(ctx->sock, SIOCSIFFLAGS, &ifr);

	return err;
}

int lc_tuntap_create(char *ifname, int flags)
{
	int fd = -1;
#if defined(TAPGIFNAME)
	/* On {Free,Net}BSD we open the special cloning device /dev/tap and
	 * then check which interface name we were assigned */

	struct ifreq ifr;

	if ((fd = open("/dev/tap", O_RDWR)) == -1) {
		return -1;
	}
	memset(&ifr, 0, sizeof(ifr));
	ifr.ifr_flags = flags;
	if (ifname[0])
		strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
	if (ioctl(fd, TAPGIFNAME, (void *) &ifr) == -1) {
		close(fd);
		return -1;
	}
	strcpy(ifname, ifr.ifr_name);
#elif defined(TUNGIFINFO)
	/* On OpenBSD there is no cloning device, so we iterate through
	 * possible interfaces until we succeed in opening one */

	char devname[8];
	char tapname[16];
	for (int i = 0; i < 255; i++) {
		snprintf(devname, sizeof devname, "tap%i", i);
		snprintf(tapname, sizeof tapname, "/dev/%s", devname);
		errno = 0;
		if ((fd = open(tapname, O_RDWR)) == -1) {
			if (errno == ENXIO) break; /* Not that many devices configured */
			continue;
		}
		break;
	}
	if (flags) {
		struct tuninfo tinfo = { .flags = flags };
		if (ioctl(fd, TUNSIFINFO, (void *)&tinfo) == -1) {
			close(fd);
			fd = -1;
		}
	}
	if (fd > 0) strcpy(ifname, devname);
#else
	(void)ifname; (void)flags;
	errno = ENOTSUP;
	fd = -1;
#endif
	return fd;
}


int lc_tap_create(char *ifname)
{
	return lc_tuntap_create(ifname, 0);
}

