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