diff options
Diffstat (limited to 'regress/sys/netinet')
-rw-r--r-- | regress/sys/netinet/mcast/Makefile | 195 | ||||
-rw-r--r-- | regress/sys/netinet/mcast/README | 31 | ||||
-rw-r--r-- | regress/sys/netinet/mcast/mcrecv.c | 171 | ||||
-rw-r--r-- | regress/sys/netinet/mcast/mcroute.c | 283 | ||||
-rw-r--r-- | regress/sys/netinet/mcast/mcsend.c | 154 |
5 files changed, 834 insertions, 0 deletions
diff --git a/regress/sys/netinet/mcast/Makefile b/regress/sys/netinet/mcast/Makefile new file mode 100644 index 00000000000..8937666eaf1 --- /dev/null +++ b/regress/sys/netinet/mcast/Makefile @@ -0,0 +1,195 @@ +# $OpenBSD: Makefile,v 1.1.1.1 2019/09/02 22:17:28 bluhm Exp $ + +PROGS = mcsend mcrecv mcroute +WARNINGS = Yes +CLEANFILES = stamp-* *.log +MSG !!= echo $$RANDOM + +REGRESS_SETUP_ONCE = setup-sudo +setup-sudo: + ${SUDO} true +.if ! empty(REMOTE_SSH) + ssh -t ${REMOTE_SSH} ${SUDO} true +.endif +.if ! empty(TARGET_SSH) + ssh -t ${TARGET_SSH} ${SUDO} true +.endif + +REGRESS_TARGETS += run-localhost +run-localhost: + @echo '\n======== $@ ========' + # send over localhost interface + ./mcrecv -f recv.log -i 127.0.0.1 -r 5 -- \ + ./mcsend -f send.log -i 127.0.0.1 -m '${MSG}' + grep '> ${MSG}$$' send.log + grep '< ${MSG}$$' recv.log + +REGRESS_TARGETS += run-localhost-loop +run-localhost-loop: + @echo '\n======== $@ ========' + # explicitly enable loop back on multicast interface + ./mcrecv -f recv.log -i 127.0.0.1 -r 5 -- \ + ./mcsend -f send.log -i 127.0.0.1 -l 1 -m '${MSG}' + grep '> ${MSG}$$' send.log + grep '< ${MSG}$$' recv.log + +REGRESS_TARGETS += run-localhost-loop0 +run-localhost-loop0: + @echo '\n======== $@ ========' + # disable loop back on multicast interface, must fail + ./mcrecv -f recv.log -i 127.0.0.1 -n 1 -- \ + ./mcsend -f send.log -i 127.0.0.1 -l 0 -m '${MSG}' + grep '> ${MSG}$$' send.log + ! grep '< ' recv.log + +REGRESS_TARGETS += run-localhost-ttl0 +run-localhost-ttl0: + @echo '\n======== $@ ========' + # send over localhost interface + ./mcrecv -f recv.log -i 127.0.0.1 -r 5 -- \ + ./mcsend -f send.log -i 127.0.0.1 -m '${MSG}' -t 0 + grep '> ${MSG}$$' send.log + grep '< ${MSG}$$' recv.log + +REGRESS_TARGETS += run-localaddr +run-localaddr: + @echo '\n======== $@ ========' + # send over a local physical interface + ./mcrecv -f recv.log -i ${LOCAL_ADDR} -r 5 -- \ + ./mcsend -f send.log -i ${LOCAL_ADDR} -m '${MSG}' + grep '> ${MSG}$$' send.log + grep '< ${MSG}$$' recv.log + +REGRESS_TARGETS += run-localaddr-loop0 +run-localaddr-loop0: + @echo '\n======== $@ ========' + # send over physical interface to loopback, ttl is 0 + ./mcrecv -f recv.log -i ${LOCAL_ADDR} -n 1 -- \ + ./mcsend -f send.log -i ${LOCAL_ADDR} -l 0 -m '${MSG}' + grep '> ${MSG}$$' send.log + ! grep '< ' recv.log + +REGRESS_TARGETS += run-localaddr-ttl0 +run-localaddr-ttl0: + @echo '\n======== $@ ========' + # send over physical interface to loopback, ttl is 0 + ./mcrecv -f recv.log -i ${LOCAL_ADDR} -r 5 -- \ + ./mcsend -f send.log -i ${LOCAL_ADDR} -m '${MSG}' -t 0 + grep '> ${MSG}$$' send.log + grep '< ${MSG}$$' recv.log + +REGRESS_TARGETS += run-remoteaddr +run-remoteaddr: + @echo '\n======== $@ ========' + # send over a local physical interface + ./mcrecv -f recv.log -i ${LOCAL_ADDR} -r 5 -- \ + ssh ${REMOTE_SSH} ${.OBJDIR}/mcsend \ + -f send.log -i ${REMOTE_ADDR} -m '${MSG}' + grep '< ${MSG}$$' recv.log + +REGRESS_TARGETS += run-remoteaddr-loop0 +run-remoteaddr-loop0: + @echo '\n======== $@ ========' + # send over a local physical interface + ./mcrecv -f recv.log -i ${LOCAL_ADDR} -r 5 -- \ + ssh ${REMOTE_SSH} ${.OBJDIR}/mcsend \ + -f send.log -i ${REMOTE_ADDR} -l 0 -m '${MSG}' + grep '< ${MSG}$$' recv.log + +REGRESS_TARGETS += run-remoteaddr-ttl0 +run-remoteaddr-ttl0: + @echo '\n======== $@ ========' + # send over a local physical interface + ./mcrecv -f recv.log -i ${LOCAL_ADDR} -n 2 -- \ + ssh ${REMOTE_SSH} ${.OBJDIR}/mcsend -f send.log \ + -i ${REMOTE_ADDR} -m '${MSG}' -t 0 + ! grep '< ' recv.log + +REGRESS_TARGETS += run-forward +run-forward: + @echo '\n======== $@ ========' + # start multicast router, start receiver, start sender + ssh ${REMOTE_SSH} ${SUDO} pkill mcroute || true + ssh ${REMOTE_SSH} ${SUDO} ${.OBJDIR}/mcroute -b -f route.log \ + -g 224.0.1.123 -i ${OTHER_ADDR} -o ${REMOTE_ADDR} -r 5 +.if empty(TARGET_SSH) + ./mcrecv -f recv.log -g 224.0.1.123 -i ${LOCAL_ADDR} -r 5 -- \ + ./mcsend -f send.log \ + -g 224.0.1.123 -i ${TARGET_ADDR} -l 0 -m '${MSG}' -t 2 + grep '> ${MSG}$$' send.log +.else + ./mcrecv -f recv.log -g 224.0.1.123 -i ${LOCAL_ADDR} -r 5 -- \ + ssh ${TARGET_SSH} ${.OBJDIR}/mcsend -f send.log \ + -g 224.0.1.123 -i ${TARGET_ADDR} -l 0 -m '${MSG}' -t 2 +.endif + grep '< ${MSG}$$' recv.log + +REGRESS_TARGETS += run-forward-ttl1 +run-forward-ttl1: + @echo '\n======== $@ ========' + # try to get ttl 1 over multicast router, must fail + ssh ${REMOTE_SSH} ${SUDO} pkill mcroute || true + ssh ${REMOTE_SSH} ${SUDO} ${.OBJDIR}/mcroute -b -f route.log \ + -g 224.0.1.123 -i ${OTHER_ADDR} -o ${REMOTE_ADDR} -n 3 +.if empty(TARGET_SSH) + ./mcrecv -f recv.log -g 224.0.1.123 -i ${LOCAL_ADDR} -n 2 -- \ + ./mcsend -f send.log \ + -g 224.0.1.123 -i ${TARGET_ADDR} -l 0 -m '${MSG}' -t 1 + grep '> ${MSG}$$' send.log +.else + ./mcrecv -f recv.log -g 224.0.1.123 -i ${LOCAL_ADDR} -n 2 -- \ + ssh ${TARGET_SSH} ${.OBJDIR}/mcsend -f send.log \ + -g 224.0.1.123 -i ${TARGET_ADDR} -l 0 -m '${MSG}' -t 1 +.endif + ! grep '< ' recv.log + +REGRESS_TARGETS += run-forward-local +run-forward-local: + @echo '\n======== $@ ========' + # try to get local multicast group over router, must fail + ssh ${REMOTE_SSH} ${SUDO} pkill mcroute || true + ssh ${REMOTE_SSH} ${SUDO} ${.OBJDIR}/mcroute -b -f route.log \ + -g 224.0.0.123 -i ${OTHER_ADDR} -o ${REMOTE_ADDR} -n 3 +.if empty(TARGET_SSH) + ./mcrecv -f recv.log -g 224.0.0.123 -i ${LOCAL_ADDR} -n 2 -- \ + ./mcsend -f send.log \ + -g 224.0.0.123 -i ${TARGET_ADDR} -l 0 -m '${MSG}' -t 2 + grep '> ${MSG}$$' send.log +.else + ./mcrecv -f recv.log -g 224.0.0.123 -i ${LOCAL_ADDR} -n 2 -- \ + ssh ${TARGET_SSH} ${.OBJDIR}/mcsend -f send.log \ + -g 224.0.0.123 -i ${TARGET_ADDR} -l 0 -m '${MSG}' -t 2 +.endif + ! grep '< ' recv.log + +stamp-remote-build: + ssh ${REMOTE_SSH} ${MAKE} -C ${.CURDIR} ${PROGS} + date >$@ + +stamp-target-build: + ssh ${TARGET_SSH} ${MAKE} -C ${.CURDIR} ${PROGS} + date >$@ + +${REGRESS_TARGETS}: ${PROGS} +${REGRESS_TARGETS:M*-remoteaddr*}: stamp-remote-build +${REGRESS_TARGETS:M*-forward*}: stamp-remote-build +.if ! empty(TARGET_SSH) +${REGRESS_TARGETS:M*-forward*}: stamp-target-build +.endif + +.if empty(LOCAL_ADDR) +REGRESS_SKIP_TARGETS += ${REGRESS_TARGETS:M*-localaddr*} +.endif +.if empty(REMOTE_ADDR) || empty(REMOTE_SSH) +REGRESS_SKIP_TARGETS += ${REGRESS_TARGETS:M*-remoteaddr*} +REGRESS_SKIP_TARGETS += ${REGRESS_TARGETS:M*-forward*} +.endif + +check-setup: + ! ssh ${REMOTE_SSH} route -n get 224/4 + ssh ${REMOTE_SSH} sysctl net.inet.ip.mforwarding | fgrep =1 + +.include <bsd.regress.mk> + +stamp-remote-build: ${SRCS} +stamp-target-build: ${SRCS} diff --git a/regress/sys/netinet/mcast/README b/regress/sys/netinet/mcast/README new file mode 100644 index 00000000000..4d4756f27c5 --- /dev/null +++ b/regress/sys/netinet/mcast/README @@ -0,0 +1,31 @@ +Test IPv4 multicast packets. + +Use two programs to send and receive multicast packets. After +setting up the socket, the receiver forks and execs the sender to +avoid races. If the test fails, the receiver runs into a timeout. + +The test programs are: +mcsend - send one multicat UDP packet +mcrecv - receive one multicast UDP packet +mcroute - route one multicast UDP packet + +The options for mcsend and mcrecv and mcroute are: +-b fork to background after setup +-f file print message to log file, default stdout +-g group multicast group, default 224.0.0.123 +-i ifaddr multicast interface address +-l loop disable or enable loopback, 0 or 1 +-m message message in payload, maximum 255 characters, default foo +-n timeout expect not to receive any message until timeout +-p port destination port number, default 12345 +-o outaddr outgoing interface address +-r timeout receive timeout in seconds +-t ttl set multicast ttl +mcsend ... after setting up receive, fork and exec send command + +With mcroute packets are sent over a multicast router. The kernel +route is installed statically. The machines sender, router, receiver +are involved. Receiver is on the local machine, route is on remote +machine. The sender can share the local machine or be started on +a target machine, depending on the setup. This is controlled via +environment. diff --git a/regress/sys/netinet/mcast/mcrecv.c b/regress/sys/netinet/mcast/mcrecv.c new file mode 100644 index 00000000000..5ba2133928a --- /dev/null +++ b/regress/sys/netinet/mcast/mcrecv.c @@ -0,0 +1,171 @@ +/* $OpenBSD: mcrecv.c,v 1.1.1.1 2019/09/02 22:17:28 bluhm Exp $ */ +/* + * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org> + * + * 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 <sys/socket.h> +#include <sys/wait.h> + +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <err.h> +#include <limits.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +void __dead usage(void); +void sigexit(int); + +void __dead +usage(void) +{ + fprintf(stderr, +"mcrecv [-f file] [-g group] [-i ifaddr] [-n timeout] [-p port] [-r timeout]\n" +" [mcsend ...]\n" +" -f file print message to log file, default stdout\n" +" -g group multicast group, default 224.0.0.123\n" +" -i ifaddr multicast interface address\n" +" -n timeout expect not to receive any message until timeout\n" +" -p port destination port number, default 12345\n" +" -r timeout receive timeout in seconds\n" +" mcsend ... after setting up receive, fork and exec send command\n"); + exit(2); +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_in sin; + struct ip_mreq mreq; + FILE *log; + const char *errstr, *file, *group, *ifaddr; + char msg[256]; + ssize_t n; + int ch, s, norecv, port, status; + unsigned int timeout; + pid_t pid; + + log = stdout; + file = NULL; + group = "224.0.0.123"; + ifaddr = "0.0.0.0"; + norecv = 0; + port = 12345; + timeout = 0; + while ((ch = getopt(argc, argv, "f:g:i:n:p:r:")) != -1) { + switch (ch) { + case 'f': + file = optarg; + break; + case 'g': + group = optarg; + break; + case 'i': + ifaddr = optarg; + break; + case 'n': + norecv = 1; + timeout = strtonum(optarg, 1, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "no timeout is %s: %s", errstr, optarg); + break; + case 'p': + port = strtonum(optarg, 1, 0xffff, &errstr); + if (errstr != NULL) + errx(1, "port is %s: %s", errstr, optarg); + break; + case 'r': + timeout = strtonum(optarg, 1, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "timeout is %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (file != NULL) { + log = fopen(file, "w"); + if (log == NULL) + err(1, "fopen %s", file); + } + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == -1) + err(1, "socket"); + if (inet_pton(AF_INET, group, &mreq.imr_multiaddr) == -1) + err(1, "inet_pton %s", group); + if (inet_pton(AF_INET, ifaddr, &mreq.imr_interface) == -1) + err(1, "inet_pton %s", ifaddr); + if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) == -1) + err(1, "setsockopt IP_ADD_MEMBERSHIP %s %s", group, ifaddr); + + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + if (inet_pton(AF_INET, group, &sin.sin_addr) == -1) + err(1, "inet_pton %s", group); + if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) + err(1, "bind %s:%d", group, port); + + if (argc) { + pid = fork(); + switch (pid) { + case -1: + err(1, "fork"); + case 0: + execvp(argv[0], argv); + err(1, "exec %s", argv[0]); + } + } + if (timeout) { + if (norecv) { + if (signal(SIGALRM, sigexit) == SIG_ERR) + err(1, "signal SIGALRM"); + } + if (alarm(timeout) == (unsigned int)-1) + err(1, "alarm %u", timeout); + } + n = recv(s, msg, sizeof(msg) - 1, 0); + if (n == -1) + err(1, "recv"); + msg[n] = '\0'; + fprintf(log, "<<< %s\n", msg); + fflush(log); + + if (norecv) + errx(1, "received %s", msg); + + if (argc) { + if (waitpid(pid, &status, 0) == -1) + err(1, "waitpid %d", pid); + if (status) + errx(1, "%s %d", argv[0], status); + } + + return 0; +} + +void +sigexit(int sig) +{ + _exit(0); +} diff --git a/regress/sys/netinet/mcast/mcroute.c b/regress/sys/netinet/mcast/mcroute.c new file mode 100644 index 00000000000..2962610354f --- /dev/null +++ b/regress/sys/netinet/mcast/mcroute.c @@ -0,0 +1,283 @@ +/* $OpenBSD: mcroute.c,v 1.1.1.1 2019/09/02 22:17:28 bluhm Exp $ */ +/* + * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org> + * + * 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. + */ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/ip_mroute.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +void __dead usage(void); +void sigexit(int); +size_t get_sysctl(const int *mib, u_int mcnt, char **buf); + +void __dead +usage(void) +{ + fprintf(stderr, +"mcroute [-b] [-f file] [-g group] -i ifaddr [-n timeout] -o outaddr\n" +" [-r timeout]\n" +" -b fork to background after setup\n" +" -f file print message to log file, default stdout\n" +" -g group multicast group, default 224.0.0.123\n" +" -i ifaddr multicast interface address\n" +" -n timeout expect not to receive any message until timeout\n" +" -o outaddr outgoing interface address\n" +" -r timeout receive timeout in seconds\n"); + exit(2); +} + +int +main(int argc, char *argv[]) +{ + struct vifctl vif; + struct mfcctl mfc; + struct vifinfo *vinfo; + FILE *log; + const char *errstr, *file, *group, *ifaddr, *outaddr; + char *buf; + size_t needed; + unsigned long pktin, pktout; + int value, ch, s, fd, background, norecv; + unsigned int timeout; + pid_t pid; + int mib[] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_MRTVIF }; + + background = 0; + log = stdout; + file = NULL; + group = "224.0.0.123"; + ifaddr = NULL; + norecv = 0; + outaddr = NULL; + timeout = 0; + while ((ch = getopt(argc, argv, "bf:g:i:n:o:r:")) != -1) { + switch (ch) { + case 'b': + background = 1; + break; + case 'f': + file = optarg; + break; + case 'g': + group = optarg; + break; + case 'i': + ifaddr = optarg; + break; + case 'n': + norecv = 1; + timeout = strtonum(optarg, 1, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "no timeout is %s: %s", errstr, optarg); + break; + case 'o': + outaddr = optarg; + break; + case 'r': + timeout = strtonum(optarg, 1, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "timeout is %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (ifaddr == NULL) + errx(2, "no ifaddr"); + if (outaddr == NULL) + errx(2, "no outaddr"); + if (argc) + usage(); + + if (file != NULL) { + log = fopen(file, "w"); + if (log == NULL) + err(1, "fopen %s", file); + } + + s = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); + if (s == -1) + err(1, "socket"); + value = 1; + if (setsockopt(s, IPPROTO_IP, MRT_INIT, &value, sizeof(value)) == -1) + err(1, "setsockopt MRT_INIT"); + + memset(&vif, 0, sizeof(vif)); + vif.vifc_vifi = 0; + if (inet_pton(AF_INET, ifaddr, &vif.vifc_lcl_addr) == -1) + err(1, "inet_pton %s", ifaddr); + if (setsockopt(s, IPPROTO_IP, MRT_ADD_VIF, &vif, sizeof(vif)) == -1) + err(1, "setsockopt MRT_ADD_VIF %s", ifaddr); + + memset(&vif, 0, sizeof(vif)); + vif.vifc_vifi = 1; + if (inet_pton(AF_INET, outaddr, &vif.vifc_lcl_addr) == -1) + err(1, "inet_pton %s", outaddr); + if (setsockopt(s, IPPROTO_IP, MRT_ADD_VIF, &vif, sizeof(vif)) == -1) + err(1, "setsockopt MRT_ADD_VIF %s", outaddr); + + memset(&mfc, 0, sizeof(mfc)); + if (inet_pton(AF_INET, group, &mfc.mfcc_mcastgrp) == -1) + err(1, "inet_pton %s", group); + mfc.mfcc_parent = 0; + mfc.mfcc_ttls[1] = 1; + + if (setsockopt(s, IPPROTO_IP, MRT_ADD_MFC, &mfc, sizeof(mfc)) == -1) + err(1, "setsockopt MRT_ADD_MFC %s", ifaddr); + + if (background) { + pid = fork(); + switch (pid) { + case -1: + err(1, "fork"); + case 0: + fd = open("/dev/null", O_RDWR); + if (fd == -1) + err(1, "open /dev/null"); + if (dup2(fd, 0) == -1) + err(1, "dup 0"); + if (dup2(fd, 1) == -1) + err(1, "dup 1"); + if (dup2(fd, 2) == -1) + err(1, "dup 2"); + break; + default: + _exit(0); + } + } + + if (timeout) { + if (norecv) { + if (signal(SIGALRM, sigexit) == SIG_ERR) + err(1, "signal SIGALRM"); + } + if (alarm(timeout) == (unsigned int)-1) + err(1, "alarm %u", timeout); + } + + buf = NULL; + pktin = pktout = 0; + do { + struct timespec sleeptime = { 0, 10000000 }; + + if (nanosleep(&sleeptime, NULL) == -1) + err(1, "nanosleep"); + needed = get_sysctl(mib, sizeof(mib) / sizeof(mib[0]), &buf); + for (vinfo = (struct vifinfo *)buf; + (char *)vinfo < buf + needed; + vinfo++) { + switch (vinfo->v_vifi) { + case 0: + if (pktin != vinfo->v_pkt_in) { + fprintf(log, "<<< %lu\n", + vinfo->v_pkt_in); + fflush(log); + } + pktin = vinfo->v_pkt_in; + break; + case 1: + if (pktout != vinfo->v_pkt_out) { + fprintf(log, ">>> %lu\n", + vinfo->v_pkt_out); + fflush(log); + } + pktout = vinfo->v_pkt_out; + break; + } + } + } while (pktin == 0 || pktout == 0); + free(buf); + + if (norecv) + errx(1, "pktin %lu, pktout %lu", pktin, pktout); + + return 0; +} + +void +sigexit(int sig) +{ + _exit(0); +} + +/* from netstat(8) */ +size_t +get_sysctl(const int *mib, u_int mcnt, char **buf) +{ + size_t needed; + + while (1) { + if (sysctl(mib, mcnt, NULL, &needed, NULL, 0) == -1) + err(1, "sysctl-estimate"); + if (needed == 0) + break; + if ((*buf = realloc(*buf, needed)) == NULL) + err(1, NULL); + if (sysctl(mib, mcnt, *buf, &needed, NULL, 0) == -1) { + if (errno == ENOMEM) + continue; + err(1, "sysctl"); + } + break; + } + + return needed; +} diff --git a/regress/sys/netinet/mcast/mcsend.c b/regress/sys/netinet/mcast/mcsend.c new file mode 100644 index 00000000000..4f38f616b8c --- /dev/null +++ b/regress/sys/netinet/mcast/mcsend.c @@ -0,0 +1,154 @@ +/* $OpenBSD: mcsend.c,v 1.1.1.1 2019/09/02 22:17:28 bluhm Exp $ */ +/* + * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org> + * + * 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 <sys/socket.h> + +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <err.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +void __dead usage(void); + +void __dead +usage(void) +{ + fprintf(stderr, +"mcsend [-f file] [-g group] [-i ifaddr] [-m message] [-p port]\n" +" -f file print message to log file, default stdout\n" +" -g group multicast group, default 224.0.0.123\n" +" -i ifaddr multicast interface address\n" +" -m message message in payload, maximum 255 characters, default foo\n" +" -l loop disable or enable loopback, 0 or 1\n" +" -p port destination port number, default 12345\n" +" -t ttl set multicast ttl\n"); + exit(2); +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_in sin; + struct in_addr addr; + FILE *log; + const char *errstr, *file, *group, *ifaddr, *msg; + size_t len; + ssize_t n; + int ch, s, loop, port, ttl; + + log = stdout; + file = NULL; + group = "224.0.0.123"; + ifaddr = NULL; + loop = -1; + msg = "foo"; + port = 12345; + ttl = -1; + while ((ch = getopt(argc, argv, "f:g:i:l:m:p:t:")) != -1) { + switch (ch) { + case 'f': + file = optarg; + break; + case 'g': + group = optarg; + break; + case 'i': + ifaddr = optarg; + break; + case 'l': + loop = strtonum(optarg, 0, 1, &errstr); + if (errstr != NULL) + errx(1, "loop is %s: %s", errstr, optarg); + break; + case 'm': + msg = optarg; + break; + case 'p': + port = strtonum(optarg, 1, 0xffff, &errstr); + if (errstr != NULL) + errx(1, "port is %s: %s", errstr, optarg); + break; + case 't': + ttl = strtonum(optarg, 0, 255, &errstr); + if (errstr != NULL) + errx(1, "ttl is %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc) + usage(); + + if (file != NULL) { + log = fopen(file, "w"); + if (log == NULL) + err(1, "fopen %s", file); + } + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == -1) + err(1, "socket"); + if (ifaddr != NULL) { + if (inet_pton(AF_INET, ifaddr, &addr) == -1) + err(1, "inet_pton %s", ifaddr); + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &addr, + sizeof(addr)) == -1) + err(1, "setsockopt IP_MULTICAST_IF %s", ifaddr); + } + if (loop != -1) { + unsigned char value = loop; + + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &value, + sizeof(value)) == -1) + err(1, "setsockopt loop %d", loop); + } + if (ttl != -1) { + unsigned char value = ttl; + + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &value, + sizeof(value)) == -1) + err(1, "setsockopt ttl %d", ttl); + } + + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + if (inet_pton(AF_INET, group, &sin.sin_addr) == -1) + err(1, "inet_pton %s", group); + if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) + err(1, "connect %s:%d", group, port); + + len = strlen(msg); + if (len >= 255) + err(1, "message too long %zu", len); + n = send(s, msg, len, 0); + if (n == -1) + err(1, "send"); + if ((size_t)n != len) + errx(1, "send %zd", n); + fprintf(log, ">>> %s\n", msg); + fflush(log); + + return 0; +} |