1 /*
2  * Copyright (c) 2015 Fujitsu Ltd.
3  * Copyright (c) International Business Machines  Corp., 2001
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: David L Stevens
19  */
20 
21 #include "config.h"
22 
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <netdb.h>
27 #include <libgen.h>
28 #include <pthread.h>
29 #include <semaphore.h>
30 
31 #include <sys/time.h>
32 #include <netinet/in.h>
33 #include <netinet/ip6.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <net/if.h>
37 #include <sys/ioctl.h>
38 #ifdef HAVE_IFADDRS_H
39 #include <ifaddrs.h>
40 #endif
41 #include <arpa/inet.h>
42 
43 #include "test.h"
44 #include "safe_macros.h"
45 
46 char *TCID = "asapi_06";
47 
48 int TST_TOTAL = 1;
49 
50 #define READ_TIMEOUT	5	/* secs */
51 
52 static void do_tests(void);
53 static void setup(void);
54 
main(int argc,char * argv[])55 int main(int argc, char *argv[])
56 {
57 	int lc;
58 
59 	tst_parse_opts(argc, argv, NULL, NULL);
60 
61 	setup();
62 
63 	for (lc = 0; TEST_LOOPING(lc); ++lc)
64 		do_tests();
65 
66 	tst_exit();
67 }
68 
69 #define NH_TEST	0x9f
70 
71 #ifndef IPV6_RECVPKTINFO
72 #define IPV6_RECVPKTINFO	-1
73 #endif
74 #ifndef IPV6_RECVHOPLIMIT
75 #define IPV6_RECVHOPLIMIT	-1
76 #endif
77 #ifndef IPV6_RECVRTHDR
78 #define IPV6_RECVRTHDR		-1
79 #endif
80 #ifndef IPV6_RECVHOPOPTS
81 #define IPV6_RECVHOPOPTS	-1
82 #endif
83 #ifndef IPV6_RECVDSTOPTS
84 #define IPV6_RECVDSTOPTS	-1
85 #endif
86 #ifndef IPV6_RECVTCLASS
87 #define IPV6_RECVTCLASS		-1
88 #endif
89 #ifndef IPV6_TCLASS
90 #define IPV6_TCLASS		-1
91 #endif
92 #ifndef IPV6_2292PKTINFO
93 #define	IPV6_2292PKTINFO	-1
94 #endif
95 #ifndef IPV6_2292HOPLIMIT
96 #define	IPV6_2292HOPLIMIT	-1
97 #endif
98 #ifndef IPV6_2292RTHDR
99 #define	IPV6_2292RTHDR		-1
100 #endif
101 #ifndef IPV6_2292HOPOPTS
102 #define	IPV6_2292HOPOPTS	-1
103 #endif
104 #ifndef IPV6_2292DSTOPTS
105 #define	IPV6_2292DSTOPTS	-1
106 #endif
107 
108 union soval {
109 	struct in6_pktinfo sou_pktinfo;
110 	int sou_hoplimit;
111 	struct sockaddr_in6 sou_nexthop;
112 	struct ip6_rthdr sou_rthdr;
113 	struct ip6_hbh sou_hopopts;
114 	struct ip6_dest sou_dstopts;
115 	struct ip6_dest sou_rthdrdstopts;
116 	int sou_tclass;
117 	int sou_bool;
118 };
119 
120 /* in6_addr initializer for loopback interface */
121 #define IN6_LOOP	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
122 #define IN6_ANY		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
123 
124 /* so_clrval and so_setval members are initilized in the body */
125 static struct soent {
126 	char *so_tname;
127 	int so_opt;
128 	int so_dorecv;		/* do receive test? */
129 	int so_cmtype;
130 	int so_clear;		/* get fresh socket? */
131 	union soval so_clrval;
132 	union soval so_setval;
133 	socklen_t so_valsize;
134 } sotab[] = {
135 	/* RFC 3542, Section 4 */
136 	{"IPV6_RECVPKTINFO", IPV6_RECVPKTINFO, 1, IPV6_PKTINFO, 1,
137 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
138 	{"IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT, 1, IPV6_HOPLIMIT, 1,
139 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
140 	{"IPV6_RECVRTHDR", IPV6_RECVRTHDR, 0, IPV6_RTHDR, 1,
141 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
142 	{"IPV6_RECVHOPOPTS", IPV6_RECVHOPOPTS, 0, IPV6_HOPOPTS, 1,
143 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
144 	{"IPV6_RECVDSTOPTS", IPV6_RECVDSTOPTS, 0, IPV6_DSTOPTS, 1,
145 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
146 	{"IPV6_RECVTCLASS", IPV6_RECVTCLASS, 1, IPV6_TCLASS, 1,
147 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
148 	/* make sure TCLASS stays when setting another opt */
149 	{"IPV6_RECVTCLASS (2)", IPV6_RECVHOPLIMIT, 1, IPV6_TCLASS, 0,
150 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
151 	/* OLD values */
152 	{"IPV6_2292PKTINFO", IPV6_2292PKTINFO, 1, IPV6_2292PKTINFO, 1,
153 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
154 	{"IPV6_2292HOPLIMIT", IPV6_2292HOPLIMIT, 1, IPV6_2292HOPLIMIT, 1,
155 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
156 	{"IPV6_2292RTHDR", IPV6_2292RTHDR, 0, IPV6_2292RTHDR, 1,
157 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
158 	{"IPV6_2292HOPOPTS", IPV6_2292HOPOPTS, 0, IPV6_2292HOPOPTS, 1,
159 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
160 	{"IPV6_2292DSTOPTS", IPV6_2292DSTOPTS, 0, IPV6_2292DSTOPTS, 1,
161 	 {{{{{0} } }, 0} }, {{{{{0} } }, 0} }, sizeof(int)},
162 };
163 
164 #define SOCOUNT	ARRAY_SIZE(sotab)
165 
166 struct soprot {
167 	int sop_pid;			/* sender PID */
168 	int sop_seq;			/* sequence # */
169 	int sop_dlen;			/* tp_dat length */
170 	unsigned char sop_dat[0];	/* user data */
171 };
172 
173 static unsigned char tpbuf[sizeof(struct soprot) + 2048];
174 static unsigned char rpbuf[sizeof(struct soprot) + 2048];
175 
176 static unsigned char control[2048];
177 
178 static int seq;
179 
180 static struct cme {
181 	int cm_len;
182 	int cm_level;
183 	int cm_type;
184 	union {
185 		uint32_t cmu_tclass;
186 		uint32_t cmu_hops;
187 	} cmu;
188 } cmtab[] = {
189 	{sizeof(uint32_t), SOL_IPV6, IPV6_TCLASS, {0x12} },
190 	{sizeof(uint32_t), SOL_IPV6, IPV6_HOPLIMIT, {0x21} },
191 };
192 
193 #define CMCOUNT	ARRAY_SIZE(cmtab)
194 
sendall(int st)195 static ssize_t sendall(int st)
196 {
197 	struct sockaddr_in6 sin6;
198 	struct msghdr msg;
199 	struct iovec iov;
200 	struct soprot *psop;
201 	unsigned char *pd;
202 	unsigned int i;
203 	int ctotal;
204 
205 	psop = (struct soprot *)tpbuf;
206 	psop->sop_pid = htonl(getpid());
207 	psop->sop_seq = ++seq;
208 	psop->sop_dlen = 0;
209 
210 	memset(&sin6, 0, sizeof(sin6));
211 	sin6.sin6_family = AF_INET6;
212 	sin6.sin6_addr = in6addr_loopback;
213 
214 	memset(&msg, 0, sizeof(msg));
215 	msg.msg_name = &sin6;
216 	msg.msg_namelen = sizeof(sin6);
217 	iov.iov_base = tpbuf;
218 	iov.iov_len = sizeof(struct soprot) + ntohl(psop->sop_dlen);
219 	msg.msg_iov = &iov;
220 	msg.msg_iovlen = 1;
221 
222 	pd = control;
223 	ctotal = 0;
224 	for (i = 0; i < CMCOUNT; ++i) {
225 		struct cmsghdr *pcmsg = (struct cmsghdr *)pd;
226 
227 		pcmsg->cmsg_len = CMSG_LEN(cmtab[i].cm_len);
228 		pcmsg->cmsg_level = cmtab[i].cm_level;
229 		pcmsg->cmsg_type = cmtab[i].cm_type;
230 		memcpy(CMSG_DATA(pcmsg), &cmtab[i].cmu, cmtab[i].cm_len);
231 		pd += CMSG_SPACE(cmtab[i].cm_len);
232 		ctotal += CMSG_SPACE(cmtab[i].cm_len);
233 	}
234 	msg.msg_control = ctotal ? control : 0;
235 	msg.msg_controllen = ctotal;
236 
237 	return sendmsg(st, &msg, 0);
238 }
239 
so_test(struct soent * psoe)240 static void so_test(struct soent *psoe)
241 {
242 	struct sockaddr_in6 sin6;
243 	union soval sobuf;
244 	socklen_t valsize;
245 	static int sr = -1;
246 	int st;
247 
248 	if (psoe->so_opt == -1) {
249 		tst_brkm(TBROK | TERRNO, NULL, "%s not present at compile time",
250 			 psoe->so_tname);
251 	}
252 	if (psoe->so_clear || sr < 0) {
253 		if (sr < 0)
254 			close(sr);
255 		sr = SAFE_SOCKET(NULL, PF_INET6, SOCK_RAW, NH_TEST);
256 	}
257 	memset(&sin6, 0, sizeof(sin6));
258 	sin6.sin6_family = AF_INET6;
259 	sin6.sin6_addr = in6addr_loopback;
260 
261 	SAFE_BIND(NULL, sr, (struct sockaddr *)&sin6, sizeof(sin6));
262 
263 	if (setsockopt(sr, SOL_IPV6, psoe->so_opt, &psoe->so_clrval,
264 		       psoe->so_valsize) < 0) {
265 		tst_brkm(TBROK | TERRNO, NULL, "%s: setsockopt",
266 			 psoe->so_tname);
267 	}
268 
269 	TEST(setsockopt(sr, SOL_IPV6, psoe->so_opt, &psoe->so_setval,
270 			psoe->so_valsize));
271 	if (TEST_RETURN != 0) {
272 		tst_resm(TFAIL | TERRNO, "%s set-get: setsockopt",
273 			 psoe->so_tname);
274 		return;
275 	}
276 
277 	valsize = psoe->so_valsize;
278 	TEST(getsockopt(sr, SOL_IPV6, psoe->so_opt, &sobuf, &valsize));
279 	if (TEST_RETURN != 0) {
280 		tst_brkm(TBROK | TERRNO, NULL, "%s set-get: getsockopt",
281 			 psoe->so_tname);
282 	} else if (memcmp(&psoe->so_setval, &sobuf, psoe->so_valsize)) {
283 		tst_resm(TFAIL, "%s set-get optval != setval", psoe->so_tname);
284 	} else {
285 		tst_resm(TPASS, "%s set-get", psoe->so_tname);
286 	}
287 
288 	st = SAFE_SOCKET(NULL, PF_INET6, SOCK_RAW, NH_TEST);
289 
290 	if (sendall(st) < 0)
291 		tst_brkm(TBROK | TERRNO, NULL, "%s transmit sendto",
292 			 psoe->so_tname);
293 
294 	close(st);
295 
296 	/* receiver processing */
297 	{
298 		fd_set rfds, rfds_saved;
299 		int nfds, cc;
300 		int gotone;
301 		struct timeval tv;
302 		struct msghdr msg;
303 		unsigned char cmsg[2048];
304 		struct cmsghdr *pcmsg;
305 		struct iovec iov;
306 
307 		FD_ZERO(&rfds_saved);
308 		FD_SET(sr, &rfds_saved);
309 
310 		tv.tv_sec = 0;
311 		tv.tv_usec = 250000;
312 
313 		while (1) {
314 			memcpy(&rfds, &rfds_saved, sizeof(rfds));
315 			nfds = select(sr + 1, &rfds, 0, 0, &tv);
316 			if (nfds < 0) {
317 				if (errno == EINTR)
318 					continue;
319 				tst_brkm(TBROK | TERRNO, NULL, "%s select",
320 					 psoe->so_tname);
321 			}
322 			if (nfds == 0) {
323 				tst_brkm(TBROK, NULL, "%s recvmsg timed out",
324 					 psoe->so_tname);
325 				return;
326 			}
327 			/* else, nfds == 1 */
328 			if (!FD_ISSET(sr, &rfds))
329 				continue;
330 
331 			memset(&msg, 0, sizeof(msg));
332 			iov.iov_base = rpbuf;
333 			iov.iov_len = sizeof(rpbuf);
334 			msg.msg_iov = &iov;
335 			msg.msg_iovlen = 1;
336 			msg.msg_control = cmsg;
337 			msg.msg_controllen = sizeof(cmsg);
338 
339 			cc = recvmsg(sr, &msg, 0);
340 			if (cc < 0) {
341 				tst_brkm(TBROK | TERRNO, NULL, "%s recvmsg",
342 					 psoe->so_tname);
343 			}
344 			/* check pid & seq here */
345 			break;
346 		}
347 		gotone = 0;
348 		for (pcmsg = CMSG_FIRSTHDR(&msg); pcmsg != NULL;
349 		     pcmsg = CMSG_NXTHDR(&msg, pcmsg)) {
350 			if (!psoe->so_dorecv)
351 				break;
352 			gotone = pcmsg->cmsg_level == SOL_IPV6 &&
353 			    pcmsg->cmsg_type == psoe->so_cmtype;
354 			if (gotone) {
355 				break;
356 			} else if (psoe->so_clear) {
357 				tst_resm(TFAIL, "%s receive: extraneous data "
358 					 "in control: level %d type %d len %zu",
359 					 psoe->so_tname, pcmsg->cmsg_level,
360 					 pcmsg->cmsg_type, pcmsg->cmsg_len);
361 				return;
362 			}
363 		}
364 		/* check contents here */
365 		if (psoe->so_dorecv)
366 			tst_resm(gotone ? TPASS : TFAIL, "%s receive",
367 				 psoe->so_tname);
368 	}
369 }
370 
do_tests(void)371 static void do_tests(void)
372 {
373 	unsigned int i;
374 
375 	for (i = 0; i < SOCOUNT; ++i) {
376 		sotab[i].so_clrval.sou_bool = 0;
377 		sotab[i].so_setval.sou_bool = 1;
378 		so_test(&sotab[i]);
379 	}
380 }
381 
setup(void)382 static void setup(void)
383 {
384 	TEST_PAUSE;
385 }
386