| /* |
| * |
| * 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" |
| |
| 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) |
| { |
| } |