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
46char *TCID = "asapi_06";
47
48int TST_TOTAL = 1;
49
50#define READ_TIMEOUT	5	/* secs */
51
52static void do_tests(void);
53static void setup(void);
54
55int 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
108union 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 */
125static 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
166struct 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
173static unsigned char tpbuf[sizeof(struct soprot) + 2048];
174static unsigned char rpbuf[sizeof(struct soprot) + 2048];
175
176static unsigned char control[2048];
177
178static int seq;
179
180static 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
195static 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
240static 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
371static 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
382static void setup(void)
383{
384	TEST_PAUSE;
385}
386