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