diff options
Diffstat (limited to 'regress/sys/netinet6/pktinfo_addr/runtest.c')
-rw-r--r-- | regress/sys/netinet6/pktinfo_addr/runtest.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/regress/sys/netinet6/pktinfo_addr/runtest.c b/regress/sys/netinet6/pktinfo_addr/runtest.c new file mode 100644 index 00000000000..9ce291884cb --- /dev/null +++ b/regress/sys/netinet6/pktinfo_addr/runtest.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2016 Vincent Gross <vincent.gross@kilob.yt> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <err.h> +#include <errno.h> +#include <getopt.h> +#include <netdb.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> + +#define PORTNUM "23000" + +int +main(int argc, char *argv[]) +{ + struct addrinfo hints; + struct addrinfo *in6ai; + + struct sockaddr_in6 *null_sin6 = NULL; + struct sockaddr_in6 **next_sin6_p = NULL; + struct sockaddr_in6 **first_sin6p = &null_sin6; + struct sockaddr_in6 **bind_sin6p = &null_sin6; + struct sockaddr_in6 **sendmsg_sin6p = &null_sin6; + struct sockaddr_in6 **setsockopt_sin6p = &null_sin6; + struct sockaddr_in6 **dst_sin6p = &null_sin6; + + int ch, rc, wstat, expected = -1; + int first_sock; + pid_t pid; + + const char *numerr; + char adrbuf[40]; + const char *adrp; + + + bzero(&hints, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + + do { + if (next_sin6_p == NULL) + next_sin6_p = malloc(sizeof(*next_sin6_p)); + if (next_sin6_p == NULL) + err(2, "malloc()"); + *next_sin6_p = NULL; + while ((ch = getopt(argc, argv, "dfbmoe:")) != -1) { + switch(ch) { + case 'd': + dst_sin6p = next_sin6_p; + break; + case 'f': + first_sin6p = next_sin6_p; + break; + case 'b': + bind_sin6p = next_sin6_p; + break; + case 'm': + sendmsg_sin6p = next_sin6_p; + break; + case 'o': + setsockopt_sin6p = next_sin6_p; + break; + case 'e': + expected = strtonum(optarg, 0, 255, &numerr); + if (numerr != NULL) + errx(2, "strtonum(%s): %s", optarg, numerr); + break; + } + } + if (optind < argc) { + rc = getaddrinfo(argv[optind], PORTNUM, &hints, &in6ai); + if (rc) + errx(2, "getaddrinfo(%s) = %d: %s", + argv[0], rc, gai_strerror(rc)); + *next_sin6_p = (struct sockaddr_in6 *)in6ai->ai_addr; + next_sin6_p = NULL; + } + optreset = 1; optind++; + } while (optind < argc); + + if (*first_sin6p == NULL) + errx(2, "first_sin6p == NULL"); + + if (*bind_sin6p == NULL) + errx(2, "bind_sin6p == NULL"); + + if (*dst_sin6p == NULL) + errx(2, "dst_sin6p == NULL"); + + if (expected < 0) + errx(2, "need expected"); + + first_sock = udp6_first(*first_sin6p); + + pid = fork(); + if (pid == 0) { + return udp6_override(*dst_sin6p, *bind_sin6p, + *setsockopt_sin6p, *sendmsg_sin6p); + } + (void)wait(&wstat); + close(first_sock); + + if (! WIFEXITED(wstat)) + errx(2, "error setting up override"); + + if (WEXITSTATUS(wstat) != expected) + errx(2, "expected %d, got %d", expected, WEXITSTATUS(wstat)); + + return EXIT_SUCCESS; +} + + +int +udp6_first(struct sockaddr_in6 *src) +{ + int s_con; + + s_con = socket(AF_INET6, SOCK_DGRAM, 0); + if (s_con == -1) + err(2, "udp6_bind: socket()"); + + if (bind(s_con, (struct sockaddr *)src, src->sin6_len)) + err(2, "udp6_bind: bind()"); + + return s_con; +} + + +int +udp6_override(struct sockaddr_in6 *dst, struct sockaddr_in6 *src_bind, + struct sockaddr_in6 *src_setsockopt, struct sockaddr_in6 *src_sendmsg) +{ + int s, optval, error, saved_errno; + ssize_t send_rc; + struct msghdr msg; + struct iovec iov; + struct cmsghdr *cmsg; + struct in6_pktinfo *pi_sendmsg; + struct in6_pktinfo pi_setsockopt; + union { + struct cmsghdr hdr; + unsigned char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cmsgbuf; + + bzero(&msg, sizeof(msg)); + bzero(&cmsgbuf, sizeof(cmsgbuf)); + bzero(&pi_setsockopt, sizeof(pi_setsockopt)); + + s = socket(AF_INET6, SOCK_DGRAM, 0); + if (s == -1) { + warn("udp6_override: socket()"); + kill(getpid(), SIGTERM); + } + + optval = 1; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int))) { + warn("udp6_override: setsockopt(SO_REUSEADDR)"); + kill(getpid(), SIGTERM); + } + + if (bind(s, (struct sockaddr *)src_bind, src_bind->sin6_len)) { + warn("udp6_override: bind()"); + kill(getpid(), SIGTERM); + } + + if (src_setsockopt != NULL) { + memcpy(&pi_setsockopt.ipi6_addr, &src_setsockopt->sin6_addr, sizeof(struct in6_addr)); + if (setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pi_setsockopt, sizeof(pi_setsockopt))) { + warn("udp6_override: setsockopt(IPV6_PKTINFO)"); + kill(getpid(), SIGTERM); + } + } + + iov.iov_base = "payload"; + iov.iov_len = 8; + msg.msg_name = dst; + msg.msg_namelen = dst->sin6_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + if (src_sendmsg) { + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + pi_sendmsg = (struct in6_pktinfo *)CMSG_DATA(cmsg); + memcpy(&pi_sendmsg->ipi6_addr, &src_sendmsg->sin6_addr, sizeof(struct in6_addr)); + } + + send_rc = sendmsg(s, &msg, 0); + saved_errno = errno; + + close(s); + + if (send_rc == iov.iov_len) + return 0; + return saved_errno; +} |