1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) International Business Machines Corp., 2001
4 * Copyright (c) 2018 Petr Vorel <pvorel@suse.cz>
5 * Author: David L Stevens
6 *
7 * IPv6 name to index and index to name function tests
8 */
9
10
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <sys/socket.h>
16 #include <net/if.h>
17
18 #include "tst_test.h"
19
20 #define I2N_RNDCOUNT 10 /* random ints */
21 #define I2N_LOWCOUNT 10 /* sequential from 0 */
22
23 static struct {
24 char *name;
25 int nonzero;
26 } test_case[] = {
27 { "lo", 1 },
28 { NULL, 1 },
29 { "hoser75", 0 },
30 { "6", 0 },
31 };
32
33 static void setup(void);
34 static void if_nametoindex_test(void);
35 static void if_indextoname_test(void);
36 static void if_nameindex_test(void);
37
38 static void (*testfunc[])(void) = { if_nametoindex_test, if_indextoname_test,
39 if_nameindex_test };
40
if_nametoindex_test(void)41 static void if_nametoindex_test(void)
42 {
43 unsigned int i;
44 char ifname[IF_NAMESIZE], *pifn;
45
46 tst_res(TINFO, "IPv6 if_nametoindex() test");
47
48 for (i = 0; i < ARRAY_SIZE(test_case); ++i) {
49 if (test_case[i].name == NULL) {
50 tst_res(TCONF, "LHOST_IFACES not defined or invalid");
51 continue;
52 }
53
54 TEST(if_nametoindex(test_case[i].name));
55 if (!TST_RET != !test_case[i].nonzero) {
56 tst_res(TFAIL, "if_nametoindex(%s) %ld [should be %szero]",
57 test_case[i].name, TST_RET,
58 test_case[i].nonzero ? "non" : "");
59 return;
60 }
61 if (TST_RET) {
62 pifn = if_indextoname(TST_RET, ifname);
63 if (!pifn || strcmp(test_case[i].name, pifn)) {
64 tst_res(TFAIL,
65 "if_nametoindex(%s) %ld doesn't match if_indextoname(%ld) '%s'",
66 test_case[i].name, TST_RET,
67 TST_RET, pifn ? pifn : "");
68 return;
69 }
70 }
71 tst_res(TINFO, "if_nametoindex(%s) %ld",
72 test_case[i].name, TST_RET);
73 }
74
75 tst_res(TPASS, "if_nametoindex() test succeeded");
76 }
77
sub_if_indextoname_test(unsigned int if_index)78 static int sub_if_indextoname_test(unsigned int if_index)
79 {
80 char ifname[IF_NAMESIZE];
81 unsigned int idx;
82
83 TEST((ifname == if_indextoname(if_index, ifname)));
84 if (!TST_RET) {
85 if (TST_ERR != ENXIO) {
86 tst_res(TFAIL,
87 "if_indextoname(%d) returns %ld but errno %d != ENXIO",
88 if_index, TST_RET, TST_ERR);
89 return 0;
90 }
91 tst_res(TINFO, "if_indextoname(%d) returns NULL", if_index);
92 return 1;
93 }
94 /* else, a valid interface-- double check name */
95 idx = if_nametoindex(ifname);
96 if (idx != if_index) {
97 tst_res(TFAIL,
98 "if_indextoname(%u) returns '%s' but doesn't if_nametoindex(%s) returns %u",
99 if_index, ifname, ifname, idx);
100 return 0;
101 }
102 tst_res(TINFO, "if_indextoname(%d) returns '%s'", if_index, ifname);
103 return 1;
104 }
105
if_indextoname_test(void)106 static void if_indextoname_test(void)
107 {
108 unsigned int i;
109
110 tst_res(TINFO, "IPv6 if_indextoname() test");
111
112 /* some low-numbered indexes-- likely to get valid interfaces here */
113 for (i = 0; i < I2N_LOWCOUNT; ++i)
114 if (!sub_if_indextoname_test(i))
115 return; /* skip the rest, if broken */
116 /* some random ints; should mostly fail */
117 for (i = 0; i < I2N_RNDCOUNT; ++i)
118 if (!sub_if_indextoname_test(rand()))
119 return; /* skip the rest, if broken */
120
121 tst_res(TPASS, "if_indextoname() test succeeded");
122 }
123
124 /*
125 * This is an ugly, linux-only solution. getrusage() doesn't support the
126 * current data segment size, so we get it out of /proc
127 */
getdatasize(void)128 static int getdatasize(void)
129 {
130 char line[128], *p;
131 int dsize = -1;
132 FILE *fp;
133
134 fp = fopen("/proc/self/status", "r");
135 if (fp == NULL)
136 return -1;
137 while (fgets(line, sizeof(line), fp)) {
138 if (strncmp(line, "VmData:", 7) == 0) {
139 dsize = strtol(line + 7, &p, 0);
140 ++p; /* skip space */
141 if (!strcmp(p, "kB"))
142 return -1; /* don't know units */
143 dsize *= 1024;
144 break;
145 }
146 }
147 fclose(fp);
148 return dsize;
149 }
150
if_nameindex_test(void)151 static void if_nameindex_test(void)
152 {
153 struct if_nameindex *pini;
154 int i;
155 char buf[IF_NAMESIZE], *p;
156 unsigned int idx;
157 int freenicount;
158 int dsize_before, dsize_after;
159
160 tst_res(TINFO, "IPv6 if_nameindex() test");
161
162 pini = if_nameindex();
163 if (pini == NULL) {
164 tst_res(TFAIL, "if_nameindex() returns NULL, errno %d (%s)",
165 TST_ERR, strerror(TST_ERR));
166 return;
167 }
168 for (i = 0; pini[i].if_index; ++i) {
169 p = if_indextoname(pini[i].if_index, buf);
170 if (!p || strcmp(p, pini[i].if_name)) {
171 tst_res(TFAIL,
172 "if_nameindex() idx %d name '%s' but if_indextoname(%d) is '%s'",
173 pini[i].if_index, pini[i].if_name,
174 pini[i].if_index, p ? p : "");
175 return;
176 }
177 idx = if_nametoindex(pini[i].if_name);
178 if (idx != pini[i].if_index) {
179 tst_res(TFAIL,
180 "if_nameindex() idx %d name '%s' but if_indextoname(%s) is %d",
181 pini[i].if_index, pini[i].if_name,
182 pini[i].if_name, idx);
183 return;
184 }
185 tst_res(TINFO, "if_nameindex() idx %d name '%s'",
186 pini[i].if_index, pini[i].if_name);
187 }
188 if_freenameindex(pini);
189
190 /*
191 * if_freenameindex() has no error conditions; see if we run
192 * out of memory if we do it a lot.
193 */
194 dsize_before = getdatasize();
195 if (dsize_before < 0) {
196 tst_brk(TBROK, "getdatasize failed: errno %d (%s)",
197 errno, strerror(errno));
198 }
199
200 /*
201 * we need to leak at least a page to detect a leak; 1 byte per call
202 * will be detected with getpagesize() calls.
203 */
204 freenicount = getpagesize();
205 for (i = 0; i < freenicount; ++i) {
206 pini = if_nameindex();
207 if (pini == NULL) {
208 tst_res(TINFO,
209 "if_freenameindex test failed if_nameindex() iteration %d", i);
210 break;
211 }
212 if_freenameindex(pini);
213 }
214 dsize_after = getdatasize();
215 if (dsize_after < 0) {
216 tst_brk(TBROK, "getdatasize failed: errno %d (%s)",
217 errno, strerror(errno));
218 }
219 if (dsize_after > dsize_before + getpagesize()) {
220 tst_res(TFAIL,
221 "if_freenameindex leaking memory (%d iterations) dsize before %d dsize after %d",
222 i, dsize_before, dsize_after);
223 return;
224 }
225 tst_res(TINFO, "if_freenameindex passed %d iterations", i);
226
227 tst_res(TPASS, "if_nameindex() test succeeded");
228 }
229
setup(void)230 static void setup(void)
231 {
232 char *ifnames = getenv("LHOST_IFACES");
233
234 if (!ifnames)
235 return;
236
237 static char name[256];
238 int ret;
239
240 ret = sscanf(ifnames, "%255s", name);
241 if (ret == -1)
242 return;
243
244 tst_res(TINFO, "get interface name from LHOST_IFACES: '%s'", name);
245 test_case[1].name = name;
246 }
247
do_test(unsigned int i)248 static void do_test(unsigned int i)
249 {
250 testfunc[i]();
251 }
252
253 static struct tst_test test = {
254 .tcnt = ARRAY_SIZE(testfunc),
255 .setup = setup,
256 .test = do_test,
257 };
258