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

#include "testnet.h"
#include <librecast.h>
#include <librecast/if.h>
#include <mld.h>
#include <mld_pvt.h>
#include <unistd.h>

static unsigned int ifx;
static unsigned int tapifx;
static volatile int ifup;
static volatile int ifdown;

void watch_callback_ifup(mld_watch_t *watch)
{
	if (mld_watch_ifx(watch) == aload(&tapifx)) aadd(&ifup, 1);
}

void watch_callback_ifdown(mld_watch_t *watch)
{
	if (mld_watch_ifx(watch) == aload(&tapifx)) aadd(&ifdown, 1);
}

int main(void)
{
	mld_t *mld;
	mld_watch_t *watch;
	lc_ctx_t *lctx;
	char ifname[IFNAMSIZ] = {0};
	int rc, fd;

	test_require_linux(); /* requires netlink */
	test_cap_require(CAP_NET_RAW);
	test_name("mld_watch_*() - netlink events");

	ifx = get_multicast_if();
	test_assert(ifx, "get_multicast_if() - find multicast capable interface");
	if (!ifx) return TEST_WARN;
	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]);

	void *arg = mld; /* any arg will do */
	watch = mld_watch_add(mld, 0, NULL, watch_callback_ifup, arg, MLD_EVENT_IFUP);
	watch = mld_watch_add(mld, 0, NULL, watch_callback_ifdown, arg, MLD_EVENT_IFDOWN);
	test_assert(watch != NULL, "watch set");

	lctx = lc_ctx_new();
	fd = lc_tap_create(ifname);
	astor(&tapifx, if_nametoindex(ifname));
	rc = lc_link_set(lctx, ifname, 1);
	test_assert(rc == 0, "bring up interface");
	sleep(2); /* this can take a *while* */
	int _ifup = aload(&ifup);
	test_assert(_ifup == 1, "MLD_EVENT_IFUP: ensure callback was triggered (%i)", _ifup);

	rc = lc_link_set(lctx, ifname, 0);
	sleep(1);
	test_assert(rc == 0, "bring down interface");
	int _ifdown = aload(&ifdown);
	test_assert(_ifdown == 1, "MLD_EVENT_IFDOWN: ensure callback was triggered (%i)", _ifdown);

	close(fd);
	lc_ctx_free(lctx);
	mld_stop(mld);
	mld_free(mld);
	return test_status;
}
