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

#include "testnet.h"
#ifdef HAVE_LIBLIBRECAST
#include <librecast.h>
#include <librecast/if.h>
#endif
#include <limits.h>
#include <mld_pvt.h>
#include <net/if.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <unistd.h>

int main(void)
{
	char name[] = "netlink thread";
#ifdef HAVE_LIBLIBRECAST
	mld_t *mld;
	lc_ctx_t *lctx;
	lc_channel_t *chan;
	struct in6_addr *addr;
	char ifname[IFNAMSIZ] = {0};
	unsigned int ifx;
	int rc, fd;

	test_require_linux();
	test_cap_require(CAP_NET_RAW);
	test_name(name);
	test_require_net(TEST_NET_BASIC);

	mld = mld_init(0);
	test_assert(mld != NULL, "mld_t allocated");

	mld_start(mld);
	/* ensure all threads created */
	for (int i = 0; i < MLD_THREADS; i++) assert(mld->q[i]);

	/* create an interface */
	fd = lc_tap_create(ifname);
	test_assert(fd > 0, "lc_tap_create()");
	ifx = if_nametoindex(ifname);
	test_assert(ifx > 0, "find ifx for tap");

	/* generate a random multicast address */
	lctx = lc_ctx_new();
	test_assert(lctx != NULL, "lc_ctx_new()");
	chan = lc_channel_random(lctx);
	test_assert(chan != NULL, "lc_channel_random()");
	addr = lc_channel_in6addr(chan);
	test_assert(addr != NULL, "lc_channel_in6addr()");

	/* try to find grp on interface before bringing it up */
	errno = 0;
	rc = mld_filter_grp_cmp(mld, ifx, addr);
	test_assert(errno == ENODEV, "ENODEV before bringing interface up");
	test_assert(rc == -1, "try to find grp on interface before bringing it up, -1");

	/* bring up interface and check netlink thread has added it */
	rc = lc_link_set(lctx, ifname, 1);
	test_assert(rc == 0, "bring up interface");
	usleep(10000);
	rc = mld_filter_grp_cmp(mld, ifx, addr);
	test_assert(rc == 0, "try to find grp on interface after bringing it up, 0 (%i)", rc);
	rc = lc_link_set(lctx, ifname, 1);
	test_assert(rc == 0, "bring down interface");
	usleep(1000);
	rc = mld_filter_grp_cmp(mld, ifx, addr);
	// FIXME test_assert(rc == -1, "try to find grp on interface after bringing it down, -1 (%i)", rc);
	close(fd);

	/* create another interface, and check netlink thread has added it */
	fd = lc_tap_create(ifname);
	test_assert(fd > 0, "lc_tap_create()");
	ifx = if_nametoindex(ifname);
	test_assert(ifx > 0, "find ifx for tap");
	rc = lc_link_set(lctx, ifname, 1);
	test_assert(rc == 0, "bring up interface");
	close(fd);
	usleep(1000);
	rc = mld_filter_grp_cmp(mld, ifx, addr);
	// FIXME test_assert(rc == -1, "try to find grp on interface after deleting, -1");

	lc_ctx_free(lctx);
	mld_stop(mld);
	mld_free(mld);

	return test_status;
#else
	return test_skip("%s - requires liblibrecast", name);
#endif
}
