/* * Copyright (c) International Business Machines Corp., 2001 * Copyright (c) 2018 Petr Vorel <pvorel@suse.cz> * * Author: David L Stevens * * 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 would 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, see <http://www.gnu.org/licenses/>. * * Description: * IPv6 name to index and index to name function tests */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <sys/socket.h> #include <net/if.h> #include "tst_test.h" #define I2N_RNDCOUNT 10 /* random ints */ #define I2N_LOWCOUNT 10 /* sequential from 0 */ static struct { char *name; int nonzero; } test_case[] = { { "lo", 1 }, { NULL, 1 }, { "hoser75", 0 }, { "6", 0 }, }; static void setup(void); static void if_nametoindex_test(void); static void if_indextoname_test(void); static void if_nameindex_test(void); static void (*testfunc[])(void) = { if_nametoindex_test, if_indextoname_test, if_nameindex_test }; static void if_nametoindex_test(void) { unsigned int i; char ifname[IF_NAMESIZE], *pifn; tst_res(TINFO, "IPv6 if_nametoindex() test"); for (i = 0; i < ARRAY_SIZE(test_case); ++i) { if (test_case[i].name == NULL) { tst_res(TCONF, "LHOST_IFACES not defined or invalid"); continue; } TEST(if_nametoindex(test_case[i].name)); if (!TST_RET != !test_case[i].nonzero) { tst_res(TFAIL, "if_nametoindex(%s) %ld [should be %szero]", test_case[i].name, TST_RET, test_case[i].nonzero ? "non" : ""); return; } if (TST_RET) { pifn = if_indextoname(TST_RET, ifname); if (!pifn || strcmp(test_case[i].name, pifn)) { tst_res(TFAIL, "if_nametoindex(%s) %ld doesn't match if_indextoname(%ld) '%s'", test_case[i].name, TST_RET, TST_RET, pifn ? pifn : ""); return; } } tst_res(TINFO, "if_nametoindex(%s) %ld", test_case[i].name, TST_RET); } tst_res(TPASS, "if_nametoindex() test succeeded"); } static int sub_if_indextoname_test(unsigned int if_index) { char ifname[IF_NAMESIZE]; unsigned int idx; TEST((ifname == if_indextoname(if_index, ifname))); if (!TST_RET) { if (TST_ERR != ENXIO) { tst_res(TFAIL, "if_indextoname(%d) returns %ld but errno %d != ENXIO", if_index, TST_RET, TST_ERR); return 0; } tst_res(TINFO, "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_res(TFAIL, "if_indextoname(%u) returns '%s' but doesn't if_nametoindex(%s) returns %u", if_index, ifname, ifname, idx); return 0; } tst_res(TINFO, "if_indextoname(%d) returns '%s'", if_index, ifname); return 1; } static void if_indextoname_test(void) { unsigned int i; tst_res(TINFO, "IPv6 if_indextoname() test"); /* some low-numbered indexes-- likely to get valid interfaces here */ for (i = 0; i < I2N_LOWCOUNT; ++i) if (!sub_if_indextoname_test(i)) return; /* skip the rest, if broken */ /* some random ints; should mostly fail */ for (i = 0; i < I2N_RNDCOUNT; ++i) if (!sub_if_indextoname_test(rand())) return; /* skip the rest, if broken */ tst_res(TPASS, "if_indextoname() test succeeded"); } /* * 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; } static void if_nameindex_test(void) { struct if_nameindex *pini; int i; char buf[IF_NAMESIZE], *p; unsigned int idx; int freenicount; int dsize_before, dsize_after; tst_res(TINFO, "IPv6 if_nameindex() test"); pini = if_nameindex(); if (pini == NULL) { tst_res(TFAIL, "if_nameindex() returns NULL, errno %d (%s)", TST_ERR, strerror(TST_ERR)); return; } for (i = 0; pini[i].if_index; ++i) { p = if_indextoname(pini[i].if_index, buf); if (!p || strcmp(p, pini[i].if_name)) { tst_res(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 : ""); return; } idx = if_nametoindex(pini[i].if_name); if (idx != pini[i].if_index) { tst_res(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); return; } tst_res(TINFO, "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_brk(TBROK, "getdatasize failed: errno %d (%s)", errno, strerror(errno)); } /* * 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 == NULL) { tst_res(TINFO, "if_freenameindex test failed if_nameindex() iteration %d", i); break; } if_freenameindex(pini); } dsize_after = getdatasize(); if (dsize_after < 0) { tst_brk(TBROK, "getdatasize failed: errno %d (%s)", errno, strerror(errno)); } if (dsize_after > dsize_before + getpagesize()) { tst_res(TFAIL, "if_freenameindex leaking memory (%d iterations) dsize before %d dsize after %d", i, dsize_before, dsize_after); return; } tst_res(TINFO, "if_freenameindex passed %d iterations", i); tst_res(TPASS, "if_nameindex() test succeeded"); } static void setup(void) { char *ifnames = getenv("LHOST_IFACES"); if (!ifnames) return; static char name[256]; int ret; ret = sscanf(ifnames, "%255s", name); if (ret == -1) return; tst_res(TINFO, "get interface name from LHOST_IFACES: '%s'", name); test_case[1].name = name; } static void do_test(unsigned int i) { testfunc[i](); } static struct tst_test test = { .tcnt = ARRAY_SIZE(testfunc), .setup = setup, .test = do_test, };