| /* |
| * |
| * Copyright (c) International Business Machines Corp., 2001 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| * the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| /* |
| * Test Name: asapi_06 |
| * |
| * Test Description: |
| * Tests for RFC 3542 section 4 socket options and ancillary data |
| * |
| * Usage: <for command-line> |
| * asapi_06 |
| * |
| * HISTORY |
| * 05/2005 written by David L Stevens |
| * |
| * RESTRICTIONS: |
| * None. |
| * |
| */ |
| |
| #include "config.h" |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <netdb.h> |
| #include <libgen.h> |
| #include <pthread.h> |
| #include <semaphore.h> |
| |
| #include <sys/time.h> |
| #include <netinet/in.h> |
| #include <netinet/ip6.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <net/if.h> |
| #include <sys/ioctl.h> |
| #ifdef HAVE_IFADDRS_H |
| #include <ifaddrs.h> |
| #endif |
| #include <arpa/inet.h> |
| |
| #include "test.h" |
| |
| char *TCID = "asapi_06"; /* Test program identifier. */ |
| |
| int TST_TOTAL = 1; |
| |
| pid_t pid; |
| |
| struct { |
| char *prt_name; |
| int prt_value; |
| } ptab[] = { |
| }; |
| |
| #define PTCOUNT (sizeof(ptab)/sizeof(ptab[0])) |
| |
| #define READ_TIMEOUT 5 /* secs */ |
| |
| void do_tests(void); |
| void setup(void), cleanup(void); |
| |
| int main(int argc, char *argv[]) |
| { |
| const char *msg; |
| int lc; |
| |
| /* Parse standard options given to run the test. */ |
| msg = parse_opts(argc, argv, 0, 0); |
| if (msg != NULL) { |
| tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| } |
| |
| pid = getpid(); |
| |
| setup(); |
| |
| for (lc = 0; TEST_LOOPING(lc); ++lc) |
| do_tests(); |
| |
| cleanup(); |
| |
| tst_exit(); |
| } |
| |
| #define NH_TEST 0x9f |
| |
| #ifndef IPV6_RECVPKTINFO |
| #define IPV6_RECVPKTINFO -1 |
| #endif |
| #ifndef IPV6_RECVHOPLIMIT |
| #define IPV6_RECVHOPLIMIT -1 |
| #endif |
| #ifndef IPV6_RECVRTHDR |
| #define IPV6_RECVRTHDR -1 |
| #endif |
| #ifndef IPV6_RECVHOPOPTS |
| #define IPV6_RECVHOPOPTS -1 |
| #endif |
| #ifndef IPV6_RECVDSTOPTS |
| #define IPV6_RECVDSTOPTS -1 |
| #endif |
| #ifndef IPV6_RECVTCLASS |
| #define IPV6_RECVTCLASS -1 |
| #endif |
| #ifndef IPV6_TCLASS |
| #define IPV6_TCLASS -1 |
| #endif |
| #ifndef IPV6_2292PKTINFO |
| #define IPV6_2292PKTINFO -1 |
| #endif |
| #ifndef IPV6_2292HOPLIMIT |
| #define IPV6_2292HOPLIMIT -1 |
| #endif |
| #ifndef IPV6_2292RTHDR |
| #define IPV6_2292RTHDR -1 |
| #endif |
| #ifndef IPV6_2292HOPOPTS |
| #define IPV6_2292HOPOPTS -1 |
| #endif |
| #ifndef IPV6_2292DSTOPTS |
| #define IPV6_2292DSTOPTS -1 |
| #endif |
| |
| union soval { |
| struct in6_pktinfo sou_pktinfo; |
| int sou_hoplimit; |
| struct sockaddr_in6 sou_nexthop; |
| struct ip6_rthdr sou_rthdr; |
| struct ip6_hbh sou_hopopts; |
| struct ip6_dest sou_dstopts; |
| struct ip6_dest sou_rthdrdstopts; |
| int sou_tclass; |
| int sou_bool; |
| }; |
| |
| /* in6_addr initializer for loopback interface */ |
| #define IN6_LOOP {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } |
| #define IN6_ANY {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } |
| |
| /* so_clrval and so_setval members are initilized in the body */ |
| struct soent { |
| char *so_tname; |
| int so_opt; |
| int so_dorecv; /* do receive test? */ |
| int so_cmtype; |
| int so_clear; /* get fresh socket? */ |
| union soval so_clrval; |
| union soval so_setval; |
| socklen_t so_valsize; |
| } sotab[] = { |
| /* RFC 3542, Section 4 */ |
| { |
| "IPV6_RECVPKTINFO", IPV6_RECVPKTINFO, 1, IPV6_PKTINFO, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, { |
| "IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT, 1, IPV6_HOPLIMIT, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, { |
| "IPV6_RECVRTHDR", IPV6_RECVRTHDR, 0, IPV6_RTHDR, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, { |
| "IPV6_RECVHOPOPTS", IPV6_RECVHOPOPTS, 0, IPV6_HOPOPTS, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, { |
| "IPV6_RECVDSTOPTS", IPV6_RECVDSTOPTS, 0, IPV6_DSTOPTS, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, { |
| "IPV6_RECVTCLASS", IPV6_RECVTCLASS, 1, IPV6_TCLASS, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, |
| /* make sure TCLASS stays when setting another opt */ |
| { |
| "IPV6_RECVTCLASS (2)", IPV6_RECVHOPLIMIT, 1, IPV6_TCLASS, 0, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, |
| /* OLD values */ |
| { |
| "IPV6_2292PKTINFO", IPV6_2292PKTINFO, 1, IPV6_2292PKTINFO, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, { |
| "IPV6_2292HOPLIMIT", IPV6_2292HOPLIMIT, 1, IPV6_2292HOPLIMIT, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, { |
| "IPV6_2292RTHDR", IPV6_2292RTHDR, 0, IPV6_2292RTHDR, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, { |
| "IPV6_2292HOPOPTS", IPV6_2292HOPOPTS, 0, IPV6_2292HOPOPTS, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)}, { |
| "IPV6_2292DSTOPTS", IPV6_2292DSTOPTS, 0, IPV6_2292DSTOPTS, 1, { { { { { |
| 0}}}}}, { { { { { |
| 0}}}}}, sizeof(int)},}; |
| |
| #define SOCOUNT (sizeof(sotab)/sizeof(sotab[0])) |
| |
| struct soprot { |
| int sop_pid; /* sender PID */ |
| int sop_seq; /* sequence # */ |
| int sop_dlen; /* tp_dat length */ |
| unsigned char sop_dat[0]; /* user data */ |
| }; |
| |
| unsigned char tpbuf[sizeof(struct soprot) + 2048]; |
| unsigned char rpbuf[sizeof(struct soprot) + 2048]; |
| |
| unsigned char control[2048]; |
| int clen; |
| |
| int seq; |
| |
| int setupso(void) |
| { |
| /* add routing headers, other ancillary data here */ |
| return 0; |
| } |
| |
| struct cme { |
| int cm_len; |
| int cm_level; |
| int cm_type; |
| union { |
| uint32_t cmu_tclass; |
| uint32_t cmu_hops; |
| } cmu; |
| } cmtab[] = { |
| { |
| sizeof(uint32_t), SOL_IPV6, IPV6_TCLASS, { |
| 0x12} |
| } |
| , { |
| sizeof(uint32_t), SOL_IPV6, IPV6_HOPLIMIT, { |
| 0x21} |
| } |
| ,}; |
| |
| #define CMCOUNT (sizeof(cmtab)/sizeof(cmtab[0])) |
| |
| ssize_t sendall(int st) |
| { |
| struct sockaddr_in6 sin6; |
| struct msghdr msg; |
| struct iovec iov; |
| struct soprot *psop; |
| unsigned char *pd; |
| int i, ctotal; |
| |
| psop = (struct soprot *)tpbuf; |
| psop->sop_pid = htonl(getpid()); |
| psop->sop_seq = ++seq; |
| psop->sop_dlen = 0; |
| |
| memset(&sin6, 0, sizeof(sin6)); |
| sin6.sin6_family = AF_INET6; |
| sin6.sin6_addr = in6addr_loopback; |
| |
| memset(&msg, 0, sizeof(msg)); |
| msg.msg_name = &sin6; |
| msg.msg_namelen = sizeof(sin6); |
| iov.iov_base = tpbuf; |
| iov.iov_len = sizeof(struct soprot) + ntohl(psop->sop_dlen); |
| msg.msg_iov = &iov; |
| msg.msg_iovlen = 1; |
| |
| pd = control; |
| ctotal = 0; |
| for (i = 0; i < CMCOUNT; ++i) { |
| struct cmsghdr *pcmsg = (struct cmsghdr *)pd; |
| |
| pcmsg->cmsg_len = CMSG_LEN(cmtab[i].cm_len); |
| pcmsg->cmsg_level = cmtab[i].cm_level; |
| pcmsg->cmsg_type = cmtab[i].cm_type; |
| memcpy(CMSG_DATA(pcmsg), &cmtab[i].cmu, cmtab[i].cm_len); |
| pd += CMSG_SPACE(cmtab[i].cm_len); |
| ctotal += CMSG_SPACE(cmtab[i].cm_len); |
| } |
| msg.msg_control = ctotal ? control : 0; |
| msg.msg_controllen = ctotal; |
| |
| return sendmsg(st, &msg, 0); |
| } |
| |
| void so_test(struct soent *psoe) |
| { |
| struct sockaddr_in6 sin6; |
| union soval sobuf; |
| socklen_t valsize; |
| static int sr = -1; |
| int st; |
| |
| if (psoe->so_opt == -1) { |
| tst_resm(TBROK, "%s not present at compile time", |
| psoe->so_tname); |
| return; |
| } |
| if (psoe->so_clear || sr < 0) { |
| if (sr < 0) |
| close(sr); |
| sr = socket(PF_INET6, SOCK_RAW, NH_TEST); |
| if (sr < 0) { |
| tst_resm(TBROK, "%s socket: %s", psoe->so_tname, |
| strerror(errno)); |
| return; |
| } |
| } |
| memset(&sin6, 0, sizeof(sin6)); |
| sin6.sin6_family = AF_INET6; |
| sin6.sin6_addr = in6addr_loopback; |
| if (bind(sr, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) { |
| tst_resm(TBROK, "%s: bind: %s", psoe->so_tname, |
| strerror(errno)); |
| } |
| if (setsockopt(sr, SOL_IPV6, psoe->so_opt, &psoe->so_clrval, |
| psoe->so_valsize) < 0) { |
| tst_resm(TBROK, "%s: setsockopt: %s", psoe->so_tname, |
| strerror(errno)); |
| return; |
| } |
| TEST(setsockopt(sr, SOL_IPV6, psoe->so_opt, &psoe->so_setval, |
| psoe->so_valsize)); |
| if (TEST_RETURN != 0) { |
| tst_resm(TFAIL, "%s set-get: setsockopt: %s", psoe->so_tname, |
| strerror(errno)); |
| return; |
| } |
| valsize = psoe->so_valsize; |
| TEST(getsockopt(sr, SOL_IPV6, psoe->so_opt, &sobuf, &valsize)); |
| if (TEST_RETURN != 0) { |
| tst_resm(TBROK, "%s set-get: getsockopt: %s", psoe->so_tname, |
| strerror(errno)); |
| return; |
| } else if (memcmp(&psoe->so_setval, &sobuf, psoe->so_valsize)) |
| tst_resm(TFAIL, "%s set-get optval != setval", psoe->so_tname); |
| else |
| tst_resm(TPASS, "%s set-get", psoe->so_tname); |
| |
| st = socket(PF_INET6, SOCK_RAW, NH_TEST); |
| if (st < 0) { |
| tst_resm(TBROK, "%s transmit socket: %s", psoe->so_tname, |
| strerror(errno)); |
| return; |
| } |
| if (sendall(st) < 0) { |
| tst_resm(TBROK, "%s transmit sendto: %s", psoe->so_tname, |
| strerror(errno)); |
| close(st); |
| return; |
| } |
| close(st); |
| |
| /* receiver processing */ |
| { |
| fd_set rfds, rfds_saved; |
| int nfds, cc; |
| int gotone; |
| struct timeval tv; |
| struct msghdr msg; |
| unsigned char cmsg[2048]; |
| struct cmsghdr *pcmsg; |
| struct iovec iov; |
| |
| FD_ZERO(&rfds_saved); |
| FD_SET(sr, &rfds_saved); |
| |
| tv.tv_sec = 0; |
| tv.tv_usec = 250000; |
| |
| while (1) { |
| memcpy(&rfds, &rfds_saved, sizeof(rfds)); |
| nfds = select(sr + 1, &rfds, 0, 0, &tv); |
| if (nfds < 0) { |
| if (errno == EINTR) |
| continue; |
| tst_resm(TBROK, "%s select: %s", psoe->so_tname, |
| strerror(errno)); |
| return; |
| } |
| if (nfds == 0) { |
| tst_resm(TBROK, "%s recvmsg timed out", |
| psoe->so_tname); |
| return; |
| } |
| /* else, nfds == 1 */ |
| if (!FD_ISSET(sr, &rfds)) |
| continue; |
| |
| memset(&msg, 0, sizeof(msg)); |
| iov.iov_base = rpbuf; |
| iov.iov_len = sizeof(rpbuf); |
| msg.msg_iov = &iov; |
| msg.msg_iovlen = 1; |
| msg.msg_control = cmsg; |
| msg.msg_controllen = sizeof(cmsg); |
| |
| cc = recvmsg(sr, &msg, 0); |
| if (cc < 0) { |
| tst_resm(TBROK, "%s recvmsg: %s", |
| psoe->so_tname, strerror(errno)); |
| return; |
| } |
| /* check pid & seq here */ |
| break; |
| } |
| gotone = 0; |
| for (pcmsg = CMSG_FIRSTHDR(&msg); pcmsg != NULL; |
| pcmsg = CMSG_NXTHDR(&msg, pcmsg)) { |
| if (!psoe->so_dorecv) |
| break; |
| gotone = pcmsg->cmsg_level == SOL_IPV6 && |
| pcmsg->cmsg_type == psoe->so_cmtype; |
| if (gotone) |
| break; |
| else if (psoe->so_clear) { |
| tst_resm(TFAIL, "%s receive: extraneous data " |
| "in control: level %d type %d len %zu", |
| psoe->so_tname, pcmsg->cmsg_level, |
| pcmsg->cmsg_type, pcmsg->cmsg_len); |
| return; |
| } |
| } |
| /* check contents here */ |
| if (psoe->so_dorecv) |
| tst_resm(gotone ? TPASS : TFAIL, "%s receive", |
| psoe->so_tname); |
| } |
| } |
| |
| #define IPV6_ADDR_NODE 1 |
| #define IPV6_ADDR_LINK 2 |
| #define IPV6_ADDR_GLOBAL 3 |
| |
| #ifdef HAVE_IFADDRS_H |
| static int ipv6_addr_scope(struct in6_addr *pin6) |
| { |
| if ((ntohl(pin6->s6_addr32[0]) & 0xFFC00000) == 0xFE800000) |
| return IPV6_ADDR_LINK; |
| if (memcmp(pin6, &in6addr_loopback, sizeof(*pin6)) == 0) |
| return IPV6_ADDR_NODE; |
| return IPV6_ADDR_GLOBAL; |
| } |
| #endif /* HAVE_IFADDRS_H */ |
| |
| int getsock(char *tname, struct sockaddr_in6 *psin6_arg, int scope) |
| { |
| #ifdef HAVE_IFADDRS_H |
| static struct ifaddrs *pifa_head; |
| struct ifaddrs *pifa; |
| struct sockaddr_in6 *psin6; |
| char strbuf[128]; |
| int ifindex = 0; |
| int s; |
| |
| if (!pifa_head && getifaddrs(&pifa_head)) { |
| tst_resm(TBROK, "%s: getifaddrs failed", tname); |
| return -1; |
| } |
| if (psin6_arg) |
| ifindex = psin6_arg->sin6_scope_id; |
| |
| /* first, find a global address */ |
| for (pifa = pifa_head; pifa; pifa = pifa->ifa_next) { |
| int this_scope; |
| |
| if (!(pifa->ifa_flags & IFF_UP)) |
| continue; |
| if (pifa->ifa_addr->sa_family != AF_INET6) |
| continue; |
| psin6 = (struct sockaddr_in6 *)pifa->ifa_addr; |
| this_scope = ipv6_addr_scope(&psin6->sin6_addr); |
| if (this_scope && |
| ((this_scope < 0 && -this_scope == scope) || |
| (this_scope > 0 && this_scope != scope))) |
| continue; |
| psin6->sin6_scope_id = if_nametoindex(pifa->ifa_name); |
| if ((ifindex < 0 && -ifindex == psin6->sin6_scope_id) || |
| (ifindex > 0 && ifindex != psin6->sin6_scope_id)) |
| continue; |
| s = socket(PF_INET6, SOCK_DGRAM, 0); |
| if (s < 0) { |
| tst_resm(TBROK, "%s: socket %s", tname, |
| strerror(errno)); |
| return -1; |
| } |
| if (bind(s, pifa->ifa_addr, sizeof(struct sockaddr_in6)) < 0) { |
| tst_resm(TBROK, "%s: bind \"%s\": %s", tname, |
| inet_ntop(AF_INET6, &psin6->sin6_addr, strbuf, |
| sizeof(strbuf)), strerror(errno)); |
| return -1; |
| } |
| if (psin6_arg) { |
| *psin6_arg = *psin6; |
| psin6_arg->sin6_scope_id = |
| if_nametoindex(pifa->ifa_name); |
| } |
| return s; |
| } |
| { |
| char *scopestr, *intfstr; |
| |
| switch (scope) { |
| case IPV6_ADDR_NODE: |
| scopestr = " node-local"; |
| break; |
| case IPV6_ADDR_LINK: |
| scopestr = " link-local"; |
| break; |
| case IPV6_ADDR_GLOBAL: |
| scopestr = " global"; |
| break; |
| default: |
| scopestr = ""; |
| break; |
| } |
| if (ifindex < 0) { |
| intfstr = " not on ifindex"; |
| ifindex = -ifindex; |
| } else if (ifindex) |
| intfstr = " on ifindex"; |
| else |
| intfstr = 0; |
| |
| if (intfstr) |
| tst_resm(TBROK, "%s: getsock : no%s addresses%s %d", |
| tname, scopestr, intfstr, ifindex); |
| else |
| tst_resm(TBROK, "%s: getsock : no%s addresses", |
| tname, scopestr); |
| } |
| return -1; |
| #else /* HAVE_IFADDRS_H */ |
| return -1; |
| #endif |
| } |
| |
| #ifdef notyet |
| /* |
| * RFC 3542 IPV6_PKTINFO not in mainline yet (as of 2.6.15). The get/set |
| * tests are below, and comments for some further tests to be added later |
| */ |
| void test_pktinfo(void) |
| { |
| int s_snd, s_rcv[3] = { -1, -1, -1 }; |
| struct sockaddr_in6 sa_rcv[3]; |
| int s, i; |
| struct ifaddrs *pifa_head, *pifa; |
| struct sockaddr_in6 *psin6; |
| char strbuf[128]; |
| char *tname = "IPV6_PKTINFO"; |
| struct in6_pktinfo pi, pi_tmp; |
| int sinlen; |
| int optlen; |
| |
| s_snd = getsock(tname, 0, IPV6_ADDR_GLOBAL); |
| if (s_snd < 0) { |
| tst_resm(TBROK, "%s: can't create send socket", tname); |
| return; |
| } |
| /* global-scope address, interface X */ |
| sa_rcv[0].sin6_scope_id = 0; |
| s_rcv[0] = getsock(tname, &sa_rcv[0], IPV6_ADDR_GLOBAL); |
| if (s_rcv[0] == -1) { |
| tst_resm(TBROK, "%s: only link-scope addresses", tname); |
| return; |
| } |
| /* link-local-scope address, interface X */ |
| sa_rcv[1].sin6_scope_id = sa_rcv[0].sin6_scope_id; |
| s_rcv[1] = getsock(tname, &sa_rcv[1], IPV6_ADDR_LINK); |
| if (s_rcv[1] < 0) { |
| tst_resm(TBROK, "%s: no link-local address on ifindex %d", |
| tname, sa_rcv[0].sin6_scope_id); |
| return; |
| } |
| /* link-local-scope address, interface Y */ |
| sa_rcv[2].sin6_scope_id = -sa_rcv[0].sin6_scope_id; |
| s_rcv[2] = getsock(tname, &sa_rcv[2], IPV6_ADDR_LINK); |
| if (s_rcv[2] < 0) { |
| tst_resm(TBROK, "%s: only one interface?", tname); |
| return; |
| } |
| /* send to rcv1 to verify communication */ |
| /* force to rcv2 w/ PKTINFO */ |
| /* TESTS: */ |
| /* sticky set-get */ |
| tname = "IPV6_PKTINFO set"; |
| pi.ipi6_addr = sa_rcv[1].sin6_addr; |
| pi.ipi6_ifindex = sa_rcv[1].sin6_scope_id; |
| TEST(setsockopt(s_snd, SOL_IPV6, IPV6_PKTINFO, &pi, sizeof(pi))); |
| if (TEST_RETURN != 0) |
| tst_resm(TFAIL, "%s: %s", tname, strerror(errno)); |
| else |
| tst_resm(TPASS, "%s", tname); |
| |
| tname = "IPV6_PKTINFO get"; |
| optlen = sizeof(pi_tmp); |
| TEST(getsockopt(s_snd, SOL_IPV6, IPV6_PKTINFO, &pi_tmp, &optlen)); |
| if (TEST_RETURN != 0) |
| tst_resm(TFAIL, "%s: %s", tname, strerror(errno)); |
| else if (memcmp(&pi, &pi_tmp, sizeof(pi)) != 0) { |
| char strbuf2[64]; |
| tst_resm(TFAIL, "%s: {\"%s\",%d} != {\"%s\",%d}", tname, |
| inet_ntop(AF_INET6, &pi_tmp.ipi6_addr, strbuf, |
| sizeof(strbuf)), pi_tmp.ipi6_ifindex, |
| inet_ntop(AF_INET6, &pi.ipi6_addr, strbuf2, |
| sizeof(strbuf2)), pi.ipi6_ifindex); |
| } else |
| tst_resm(TPASS, "%s", tname); |
| /* ancillary data override */ |
| /* link-local, wrong interface */ |
| tname = "IPV6_PKTINFO invalid {lladdr, intf}"; |
| pi.ipi6_addr = sa_rcv[1].sin6_addr; |
| pi.ipi6_ifindex = sa_rcv[2].sin6_scope_id; |
| TEST(setsockopt(s_snd, SOL_IPV6, IPV6_PKTINFO, &pi, sizeof(pi))); |
| if (TEST_RETURN == 0) |
| tst_resm(TFAIL, "%s returns success, should be -1, EINVAL", |
| tname); |
| else if (TEST_ERRNO != EINVAL) |
| tst_resm(TFAIL, "%s errno %d != %d", tname, TEST_ERRNO, EINVAL); |
| else |
| tst_resm(TPASS, "%s", tname); |
| /* nonexistent interface */ |
| /* non-local address */ |
| /* clear address */ |
| /* clear interface */ |
| /* sendmsg() sin6_scope differs with ancillary data interface */ |
| } |
| #endif /* notyet */ |
| |
| void do_tests(void) |
| { |
| int i; |
| |
| for (i = 0; i < SOCOUNT; ++i) { |
| sotab[i].so_clrval.sou_bool = 0; |
| sotab[i].so_setval.sou_bool = 1; |
| so_test(&sotab[i]); |
| } |
| #ifdef notyet |
| test_pktinfo(); |
| #endif /* notyet - see test_pktinfo() comment above */ |
| } |
| |
| void setup(void) |
| { |
| TEST_PAUSE; /* if -P option specified */ |
| } |
| |
| void cleanup(void) |
| { |
| } |