/*
 *
 *   Copyright (c) International Business Machines  Corp., 2001
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/*
 * Test Name: in6_02
 *
 * Test Description:
 *  Tests for name to index and index to name functions in IPv6
 *
 * Usage:  <for command-line>
 *  in6_02 [-c n] [-e] [-i n] [-I x] [-P x] [-t]
 *     where,  -c n : Run n copies concurrently.
 *             -e   : Turn on errno logging.
 *	       -i n : Execute test n times.
 *	       -I x : Execute test for x seconds.
 *	       -P x : Pause for x seconds between iterations.
 *	       -t   : Turn on syscall timing.
 *
 * HISTORY
 *	05/2004 written by David L Stevens
 *
 * RESTRICTIONS:
 *  None.
 *
 */

#include <unistd.h>
#include <errno.h>

#include <sys/socket.h>

#include <net/if.h>

#include "test.h"
#include "usctest.h"

char *TCID = "in6_02";		/* Test program identifier.    */
int testno;

void setup(void), cleanup(void);

struct {
	char *name;
	int nonzero;
} n2i[] = {
	{
	"lo", 1}, {
	"eth0", 1}, {
	"hoser75", 0}, {
"6", 0},};

#define N2I_COUNT (sizeof(n2i)/sizeof(n2i[0]))

#define I2N_RNDCOUNT	10	/* random ints */
#define I2N_LOWCOUNT	10	/* sequential from 0 */

int TST_TOTAL = N2I_COUNT;

void n2itest(void), i2ntest(void), initest(void);

int main(int argc, char *argv[])
{
	int lc;
	const char *msg;

	/* Parse standard options given to run the test. */
	msg = parse_opts(argc, argv, NULL, NULL);
	if (msg != NULL) {
		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
	}

	setup();

	for (lc = 0; TEST_LOOPING(lc); ++lc) {
		n2itest();
		i2ntest();
		initest();
	}
	cleanup();

	return (0);
}

/* if_nametoindex tests */

void n2itest()
{
	int i;

	for (i = 0; i < N2I_COUNT; ++i) {
		char ifname[IF_NAMESIZE], *pifn;
		int fail;

		TEST(if_nametoindex(n2i[i].name));
		fail = !TEST_RETURN != !n2i[i].nonzero;
		if (fail) {
			tst_resm(TFAIL, "if_nametoindex(\"%s\") %ld "
				 "[should be %szero]", n2i[i].name,
				 TEST_RETURN, n2i[i].nonzero ? "non" : "");
			continue;
		}
		if (!TEST_RETURN) {
			tst_resm(TPASS, "if_nametoindex(\"%s\") %ld",
				 n2i[i].name, TEST_RETURN);
			continue;
		}

		pifn = if_indextoname(TEST_RETURN, ifname);
		if (!pifn || strcmp(n2i[i].name, pifn)) {
			tst_resm(TFAIL, "if_nametoindex(\"%s\") %ld doesn't "
				 "match if_indextoname(%ld) \"%s\"",
				 n2i[i].name, TEST_RETURN, TEST_RETURN,
				 pifn ? pifn : "");
			continue;
		}
		tst_resm(TPASS, "if_nametoindex(\"%s\") %ld", n2i[i].name,
			 TEST_RETURN);
	}
}

int i2ntest1(unsigned int if_index)
{
	char ifname[IF_NAMESIZE];
	unsigned int idx;

	TEST((ifname == if_indextoname(if_index, ifname)));
	if (!TEST_RETURN) {
		if (TEST_ERRNO != ENXIO) {
			tst_resm(TFAIL, "if_indextoname(%d) returns %ld "
				 "but errno %d != ENXIO", if_index, TEST_RETURN,
				 TEST_ERRNO);
			return 0;
		}
		tst_resm(TPASS, "if_indextoname(%d) returns NULL", if_index);
		return 1;
	}
	/* else, a valid interface-- double check name */
	idx = if_nametoindex(ifname);
	if (idx != if_index) {
		tst_resm(TFAIL, "if_indextoname(%u) returns \"%s\" but "
			 "doesn't if_nametoindex(\"%s\") returns %u",
			 if_index, ifname, ifname, idx);
		return 0;
	}
	tst_resm(TPASS, "if_indextoname(%d) returns \"%s\"", if_index, ifname);
	return 1;
}

void i2ntest(void)
{
	unsigned int i;

	/* some low-numbered indexes-- likely to get valid interfaces here */
	for (i = 0; i < I2N_LOWCOUNT; ++i)
		if (!i2ntest1(i))
			return;	/* skip the rest, if broken */
	/* some random ints; should mostly fail */
	for (i = 0; i < I2N_RNDCOUNT; ++i)
		if (!i2ntest1(rand()))
			return;	/* skip the rest, if broken */
}

/*
 * This is an ugly, linux-only solution. getrusage() doesn't support the
 * current data segment size, so we get it out of /proc
 */
static int getdatasize(void)
{
	char line[128], *p;
	int dsize = -1;
	FILE *fp;

	fp = fopen("/proc/self/status", "r");
	if (fp == NULL)
		return -1;
	while (fgets(line, sizeof(line), fp)) {
		if (strncmp(line, "VmData:", 7) == 0) {
			dsize = strtol(line + 7, &p, 0);
			++p;	/* skip space */
			if (!strcmp(p, "kB"))
				return -1;	/* don't know units */
			dsize *= 1024;
			break;
		}
	}
	fclose(fp);
	return dsize;
}

void initest(void)
{
	int freenicount;
	struct if_nameindex *pini;
	int i, dsize_before, dsize_after;

	pini = if_nameindex();
	if (pini == 0) {
		tst_resm(TFAIL, "if_nameindex() returns NULL, errno %d (%s)",
			 TEST_ERRNO, strerror(TEST_ERRNO));
		return;
	}
	for (i = 0; pini[i].if_index; ++i) {
		char buf[IF_NAMESIZE], *p;
		int idx;

		p = if_indextoname(pini[i].if_index, buf);
		if (!p || strcmp(p, pini[i].if_name)) {
			tst_resm(TFAIL, "if_nameindex idx %d name \"%s\" but "
				 "if_indextoname(%d) is \"%s\"",
				 pini[i].if_index, pini[i].if_name,
				 pini[i].if_index, p ? p : "");
			continue;
		}
		idx = if_nametoindex(pini[i].if_name);
		if (idx != pini[i].if_index) {
			tst_resm(TFAIL, "if_nameindex idx %d name \"%s\" but "
				 "if_indextoname(\"%s\") is %d",
				 pini[i].if_index, pini[i].if_name,
				 pini[i].if_name, idx);
			continue;
		}
		tst_resm(TPASS, "if_nameindex idx %d name \"%s\"",
			 pini[i].if_index, pini[i].if_name);
	}
	if_freenameindex(pini);

	/* if_freenameindex() has no error conditions; see if we run
	 * out of memory if we do it a lot.
	 */
	dsize_before = getdatasize();
	if (dsize_before < 0) {
		tst_resm(TBROK, "getdatasize failed: errno %d (%s)", errno,
			 strerror(errno));
		return;
	}
	/* we need to leak at least a page to detect a leak; 1 byte per call
	 * will be detected with getpagesize() calls.
	 */
	freenicount = getpagesize();
	for (i = 0; i < freenicount; ++i) {
		pini = if_nameindex();
		if (!pini) {
			tst_resm(TFAIL, "if_freenameindex test failed "
				 "if_nameindex() iteration %d", i);
			break;
		}
		if_freenameindex(pini);
	}
	dsize_after = getdatasize();
	if (dsize_after < 0) {
		tst_resm(TBROK, "getdatasize failed: errno %d (%s)", errno,
			 strerror(errno));
		return;
	}
	if (dsize_after > dsize_before + getpagesize())
		tst_resm(TFAIL, "if_freenameindex leaking memory "
			 "(%d iterations) dsize before %d dsize after %d", i,
			 dsize_before, dsize_after);
	else
		tst_resm(TPASS, "if_freenameindex passed %d iterations", i);
}

void setup(void)
{
	TEST_PAUSE;		/* if -P option specified */
}

void cleanup(void)
{
	TEST_CLEANUP;

}
