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