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 | TTERRNO, "%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 | TTERRNO, 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