summaryrefslogtreecommitdiffstats
path: root/tools/regression
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2005-07-22 19:36:29 +0000
committerrwatson <rwatson@FreeBSD.org>2005-07-22 19:36:29 +0000
commitc45e23d0396e341c2ea30fff66b565186107df40 (patch)
treecc93e8861bf92564350afc99a15e6f358ec83482 /tools/regression
parentfeb635227b5d85a9fac6b1231c58366d8fcbb601 (diff)
downloadFreeBSD-src-c45e23d0396e341c2ea30fff66b565186107df40.zip
FreeBSD-src-c45e23d0396e341c2ea30fff66b565186107df40.tar.gz
Add a simple multicast socket regression test set:
- Test that the basic socket options have the right defaults, that we can change them, read them back, etc. - Add and remove some multicast addresses. - Send a loopback multicast address and make sure it arrives intact. There's more that could be done here, but it's a start. MFC after: 3 days
Diffstat (limited to 'tools/regression')
-rw-r--r--tools/regression/netinet/msocket/Makefile7
-rw-r--r--tools/regression/netinet/msocket/msocket.c464
2 files changed, 471 insertions, 0 deletions
diff --git a/tools/regression/netinet/msocket/Makefile b/tools/regression/netinet/msocket/Makefile
new file mode 100644
index 0000000..885afcf
--- /dev/null
+++ b/tools/regression/netinet/msocket/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= msocket
+NO_MAN=
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/netinet/msocket/msocket.c b/tools/regression/netinet/msocket/msocket.c
new file mode 100644
index 0000000..534e9f4
--- /dev/null
+++ b/tools/regression/netinet/msocket/msocket.c
@@ -0,0 +1,464 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Regression test for multicast sockets and options:
+ *
+ * - Check the defaults for ttl, if, and loopback. Make sure they can be set
+ * and then read.
+ *
+ * - Check that adding and removing multicast addresses seems to work.
+ *
+ * - Send a test message over loop back multicast and make sure it arrives.
+ *
+ * NB:
+ *
+ * Would be nice to use BPF or if_tap to actually check packet contents and
+ * layout, make sure that the ttl is set right, etc.
+ *
+ * Would be nice if attempts to use multicast options on TCP sockets returned
+ * an error, as the docs suggest it might.
+ */
+
+#ifdef WARN_TCP
+#define WARN_SUCCESS 0x00000001 /* Set for TCP to warn on success. */
+#else
+#define WARN_SUCCESS 0x00000000
+#endif
+
+/*
+ * Multicast test address, picked arbitrarily. Will be used with the
+ * loopback interface.
+ */
+#define TEST_MADDR "224.100.100.100"
+
+/*
+ * Test that a given IP socket option (optname) has a default value of
+ * 'defaultv', that we can set it to 'modifiedv', and use 'fakev' as a dummy
+ * value that shouldn't be returned at any point during the tests. Perform
+ * the tests on the raw socket, tcp socket, and upd socket passed.
+ * 'optstring' is used in printing warnings and errors as needed.
+ */
+static void
+test_u_char(int optname, const char *optstring, u_char defaultv,
+ u_char modifiedv, u_char fakev, const char *socktype, int sock,
+ int flags)
+{
+ socklen_t socklen;
+ u_char uc;
+ int ret;
+
+ /*
+ * Check that we read back the expected default.
+ */
+ uc = fakev;
+ socklen = sizeof(uc);
+
+ ret = getsockopt(sock, IPPROTO_IP, optname, &uc, &socklen);
+ if (ret < 0)
+ err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
+ socktype, optstring);
+ if (ret == 0 && (flags & WARN_SUCCESS))
+ warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
+ socktype, optstring);
+ if (uc != defaultv)
+ errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) default is "
+ "%d not %d", socktype, optstring, uc, defaultv);
+
+ /*
+ * Set to a modifiedv value, read it back and make sure it got there.
+ */
+ uc = modifiedv;
+ ret = setsockopt(sock, IPPROTO_IP, optname, &uc, sizeof(uc));
+ if (ret == -1)
+ err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, %s)",
+ socktype, optstring);
+ if (ret == 0 && (flags & WARN_SUCCESS))
+ warnx("WARN: setsockopt(%s, IPPROTO_IP, %s) returned 0",
+ socktype, optstring);
+
+ uc = fakev;
+ socklen = sizeof(uc);
+ ret = getsockopt(sock, IPPROTO_IP, optname, &uc, &socklen);
+ if (ret < 0)
+ err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
+ socktype, optstring);
+ if (ret == 0 && (flags & WARN_SUCCESS))
+ warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
+ socktype, optstring);
+ if (uc != modifiedv)
+ errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) set value is "
+ "%d not %d", socktype, optstring, uc, modifiedv);
+}
+
+/*
+ * test_in_addr() is like test_u_char(), only it runs on a struct in_addr
+ * (surprise).
+ */
+static void
+test_in_addr(int optname, const char *optstring, struct in_addr defaultv,
+ struct in_addr modifiedv, struct in_addr fakev, const char *socktype,
+ int sock, int flags)
+{
+ socklen_t socklen;
+ struct in_addr ia;
+ int ret;
+
+ /*
+ * Check that we read back the expected default.
+ */
+ ia = fakev;
+ socklen = sizeof(ia);
+
+ ret = getsockopt(sock, IPPROTO_IP, optname, &ia, &socklen);
+ if (ret < 0)
+ err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
+ socktype, optstring);
+ if (ret == 0 && (flags & WARN_SUCCESS))
+ warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
+ socktype, optstring);
+ if (memcmp(&ia, &defaultv, sizeof(struct in_addr)))
+ errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) default is "
+ "%s not %s", socktype, optstring, inet_ntoa(ia),
+ inet_ntoa(defaultv));
+
+ /*
+ * Set to a modifiedv value, read it back and make sure it got there.
+ */
+ ia = modifiedv;
+ ret = setsockopt(sock, IPPROTO_IP, optname, &ia, sizeof(ia));
+ if (ret == -1)
+ err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, %s)",
+ socktype, optstring);
+ if (ret == 0 && (flags & WARN_SUCCESS))
+ warnx("WARN: setsockopt(%s, IPPROTO_IP, %s) returned 0",
+ socktype, optstring);
+
+ ia = fakev;
+ socklen = sizeof(ia);
+ ret = getsockopt(sock, IPPROTO_IP, optname, &ia, &socklen);
+ if (ret < 0)
+ err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
+ socktype, optstring);
+ if (ret == 0 && (flags & WARN_SUCCESS))
+ warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
+ socktype, optstring);
+ if (memcmp(&ia, &modifiedv, sizeof(struct in_addr)))
+ errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) set value is "
+ "%s not %s", socktype, optstring, inet_ntoa(ia),
+ inet_ntoa(modifiedv));
+}
+
+static void
+test_ttl(int raw_sock, int tcp_sock, int udp_sock)
+{
+
+ test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,
+ "raw_sock", raw_sock, 0);
+ test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,
+ "tcp_sock", tcp_sock, WARN_SUCCESS);
+ test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,
+ "udp_sock", udp_sock, 0);
+}
+
+static void
+test_loop(int raw_sock, int tcp_sock, int udp_sock)
+{
+
+ test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,
+ "raw_sock", raw_sock, 0);
+ test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,
+ "tcp_sock", tcp_sock, WARN_SUCCESS);
+ test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,
+ "udp_sock", udp_sock, 0);
+}
+
+static void
+test_if(int raw_sock, int tcp_sock, int udp_sock)
+{
+ struct in_addr defaultv, modifiedv, fakev;
+
+ defaultv.s_addr = inet_addr("0.0.0.0");
+
+ /* Should be valid on all hosts. */
+ modifiedv.s_addr = inet_addr("127.0.0.1");
+
+ /* Should not happen. */
+ fakev.s_addr = inet_addr("255.255.255.255");
+
+ test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,
+ fakev, "raw_sock", raw_sock, 0);
+ test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,
+ fakev, "tcp_sock", tcp_sock, WARN_SUCCESS);
+ test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,
+ fakev, "udp_sock", udp_sock, 0);
+}
+
+/*
+ * Add a multicast address to an interface. Warn if appropriate. No query
+ * interface so can't check if it's there directly; instead we have to try
+ * to add it a second time and make sure we get back EADDRINUSE.
+ */
+static void
+test_add_multi(int sock, const char *socktype, struct ip_mreq imr,
+ int flags)
+{
+ char buf[128];
+ int ret;
+
+ ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,
+ sizeof(imr));
+ if (ret < 0) {
+ strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
+ err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
+ "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
+ }
+ if (ret == 0 && (flags & WARN_SUCCESS)) {
+ strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
+ warnx("WARN: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
+ "%s, %s) returned 0", socktype, buf,
+ inet_ntoa(imr.imr_interface));
+ }
+
+ /* Try to add a second time to make sure it got there. */
+ ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,
+ sizeof(imr));
+ if (ret == 0) {
+ strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
+ err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
+ "%s, %s) dup returned 0", socktype, buf,
+ inet_ntoa(imr.imr_interface));
+ }
+ if (ret < 0 && errno != EADDRINUSE) {
+ strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
+ err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
+ "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
+ }
+}
+
+/*
+ * Drop a multicast address from an interface. Warn if appropriate. No
+ * query interface so can't check if it's gone directly; instead we have to
+ * try to drop it a second time and make sure we get back EADDRNOTAVAIL.
+ */
+static void
+test_drop_multi(int sock, const char *socktype, struct ip_mreq imr,
+ int flags)
+{
+ char buf[128];
+ int ret;
+
+ ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr,
+ sizeof(imr));
+ if (ret < 0) {
+ strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
+ err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
+ "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
+ }
+ if (ret == 0 && (flags & WARN_SUCCESS)) {
+ strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
+ warnx("WARN: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
+ "%s, %s) returned 0", socktype, buf,
+ inet_ntoa(imr.imr_interface));
+ }
+
+ /* Try a second time to make sure it's gone. */
+ ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr,
+ sizeof(imr));
+ if (ret == 0) {
+ strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
+ err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
+ "%s, %s) returned 0", socktype, buf,
+ inet_ntoa(imr.imr_interface));
+ }
+ if (ret < 0 && errno != EADDRNOTAVAIL) {
+ strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
+ err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
+ "%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
+ }
+}
+
+/*
+ * Should really also test trying to add an invalid address, delete one
+ * that's not there, etc.
+ */
+static void
+test_addr(int raw_sock, int tcp_sock, int udp_sock)
+{
+ struct ip_mreq imr;
+
+ /* Arbitrary. */
+ imr.imr_multiaddr.s_addr = inet_addr(TEST_MADDR);
+
+ /* Localhost should be OK. */
+ imr.imr_interface.s_addr = inet_addr("127.0.0.1");
+
+ test_add_multi(raw_sock, "raw_sock", imr, 0);
+ test_drop_multi(raw_sock, "raw_sock", imr, 0);
+
+ test_add_multi(tcp_sock, "raw_sock", imr, WARN_SUCCESS);
+ test_drop_multi(tcp_sock, "raw_sock", imr, WARN_SUCCESS);
+
+ test_add_multi(udp_sock, "raw_sock", imr, 0);
+ test_drop_multi(udp_sock, "raw_sock", imr, 0);
+}
+
+/*
+ * Test an actual simple UDP message - send a single byte to an address we're
+ * subscribed to, and hope to get it back. We create a new UDP socket for
+ * this purpose because we will need to bind it.
+ */
+#define UDP_PORT 5012
+static void
+test_udp(void)
+{
+ struct sockaddr_in sin;
+ struct ip_mreq imr;
+ struct in_addr if_addr;
+ char message;
+ ssize_t len;
+ int sock;
+
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ err(-1, "FAIL: test_udp: socket(PF_INET, SOCK_DGRAM)");
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
+ err(-1, "FAIL: test_udp: fcntl(F_SETFL, O_NONBLOCK)");
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(UDP_PORT);
+ sin.sin_addr.s_addr = inet_addr(TEST_MADDR);
+
+ if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(-1, "FAIL: test_udp: bind(udp_sock, 127.0.0.1:%d",
+ UDP_PORT);
+
+ /* Arbitrary. */
+ imr.imr_multiaddr.s_addr = inet_addr(TEST_MADDR);
+
+ /* Localhost should be OK. */
+ imr.imr_interface.s_addr = inet_addr("127.0.0.1");
+
+ /*
+ * Tell socket what interface to send on -- use localhost.
+ */
+ if_addr.s_addr = inet_addr("127.0.0.1");
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &if_addr,
+ sizeof(if_addr)) < 0)
+ err(-1, "test_udp: setsockopt(IPPROTO_IP, IP_MULTICAST_IF)");
+
+ test_add_multi(sock, "udp_sock", imr, 0);
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(UDP_PORT);
+ sin.sin_addr.s_addr = inet_addr(TEST_MADDR);
+
+ message = 'A';
+ len = sizeof(message);
+ len = sendto(sock, &message, len, 0, (struct sockaddr *)&sin,
+ sizeof(sin));
+ if (len < 0)
+ err(-1, "test_udp: sendto");
+
+ if (len != sizeof(message))
+ errx(-1, "test_udp: sendto: expected to send %d, instead %d",
+ sizeof(message), len);
+
+ message = 'B';
+ len = sizeof(sin);
+ len = recvfrom(sock, &message, sizeof(message), 0,
+ (struct sockaddr *)&sin, &len);
+ if (len < 0)
+ err(-1, "test_udp: recvfrom");
+
+ if (len != sizeof(message))
+ errx(-1, "test_udp: recvfrom: len %d != message len %d",
+ len, sizeof(message));
+
+ if (message != 'A')
+ errx(-1, "test_udp: recvfrom: expected 'A', got '%c'",
+ message);
+
+ test_drop_multi(sock, "udp_sock", imr, 0);
+
+ close(sock);
+}
+#undef UDP_PORT
+
+int
+main(int argc, char *argv[])
+{
+ int raw_sock, tcp_sock, udp_sock;
+
+ if (geteuid() != 0)
+ errx(-1, "FAIL: root privilege required");
+
+ raw_sock = socket(PF_INET, SOCK_RAW, 0);
+ if (raw_sock == -1)
+ err(-1, "FAIL: socket(PF_INET, SOCK_RAW)");
+
+ tcp_sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (raw_sock == -1)
+ err(-1, "FAIL: socket(PF_INET, SOCK_STREAM)");
+
+ udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (raw_sock == -1)
+ err(-1, "FAIL: socket(PF_INET, SOCK_DGRAM)");
+
+ test_ttl(raw_sock, tcp_sock, udp_sock);
+ test_loop(raw_sock, tcp_sock, udp_sock);
+ test_if(raw_sock, tcp_sock, udp_sock);
+ test_addr(raw_sock, tcp_sock, udp_sock);
+
+ close(udp_sock);
+ close(tcp_sock);
+ close(raw_sock);
+
+ test_udp();
+
+ return (0);
+}
OpenPOWER on IntegriCloud