summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2005-09-05 13:23:01 +0000
committerrwatson <rwatson@FreeBSD.org>2005-09-05 13:23:01 +0000
commit61c98b99f29c604765238a8f73f6cf90fb5d2482 (patch)
tree283da309235aae00b8944fb2a1f6f073d4a38d3e /tools
parent0dbebe5f5da3f0856514bc98c955f555cd7ad0f8 (diff)
downloadFreeBSD-src-61c98b99f29c604765238a8f73f6cf90fb5d2482.zip
FreeBSD-src-61c98b99f29c604765238a8f73f6cf90fb5d2482.tar.gz
Add a regression test to test two variations on the same bug: joining a
multicast group using a raw socket, then removing the interface on which the group is found, and joining a multicast group using a udp socket, then removing the interface on which the group is found. An if_disc interface is used as the interface on which to attach. NB: A panic currently results from running this regression test, so do so with caution. PR: 77665 Reported by: Gavin Atkinson <gavin dot atkinson at ury dot york dot ac dot uk> Reported by: Brooks Davis <brooks at FreeBSD dot org>
Diffstat (limited to 'tools')
-rw-r--r--tools/regression/netinet/msocket_ifnet_remove/Makefile7
-rw-r--r--tools/regression/netinet/msocket_ifnet_remove/msocket_ifnet_remove.c244
2 files changed, 251 insertions, 0 deletions
diff --git a/tools/regression/netinet/msocket_ifnet_remove/Makefile b/tools/regression/netinet/msocket_ifnet_remove/Makefile
new file mode 100644
index 0000000..c379250
--- /dev/null
+++ b/tools/regression/netinet/msocket_ifnet_remove/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= msocket_ifnet_remove
+WARNS= 3
+NO_MAN=
+
+.include <bsd.prog.mk>
diff --git a/tools/regression/netinet/msocket_ifnet_remove/msocket_ifnet_remove.c b/tools/regression/netinet/msocket_ifnet_remove/msocket_ifnet_remove.c
new file mode 100644
index 0000000..51788ef
--- /dev/null
+++ b/tools/regression/netinet/msocket_ifnet_remove/msocket_ifnet_remove.c
@@ -0,0 +1,244 @@
+/*-
+ * 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/param.h>
+#include <sys/ioctl.h>
+#include <sys/linker.h>
+#include <sys/socket.h>
+
+#include <net/if.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 to reproduce problems associated with the removal of a
+ * network interface being used by an active multicast socket. This proves
+ * to be somewhat complicated, as we need a multicast-capable synthetic
+ * network device that can be torn down on demand, in order that the test
+ * program can open a multicast socket, join a group on the interface, tear
+ * down the interface, and then close the multicast socket. We use the
+ * if_disc ("discard") synthetic interface for this purpose.
+ *
+ * Because potential solutions to this problem require separate handling for
+ * different IP socket types, we actually run the test twice: once for UDP
+ * sockets, and once for raw IP sockets.
+ */
+
+/*
+ * XXX: The following hopefully don't conflict with the local configuration.
+ */
+#define MULTICAST_IP "224.100.100.100"
+#define DISC_IP "192.0.2.100"
+#define DISC_MASK "255.255.255.0"
+#define DISC_IFNAME "disc"
+#define DISC_IFUNIT 100
+
+static int
+disc_setup(void)
+{
+ struct ifreq ifr;
+ int s;
+
+ if (kldload("if_disc") < 0) {
+ switch (errno) {
+ case EEXIST:
+ break;
+ default:
+ warn("disc_setup: kldload(if_disc)");
+ return (-1);
+ }
+ }
+
+ s = socket(PF_INET, SOCK_RAW, 0);
+ if (s < 0) {
+ warn("disc_setup: socket(PF_INET, SOCK_RAW, 0)");
+ return (-1);
+ }
+
+ bzero(&ifr, sizeof(ifr));
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", DISC_IFNAME,
+ DISC_IFUNIT);
+
+ if (ioctl(s, SIOCIFCREATE, &ifr) < 0) {
+ warn("disc_setup: ioctl(%s, SIOCIFCREATE)", ifr.ifr_name);
+ close(s);
+ return (-1);
+ }
+
+ close(s);
+ return (0);
+}
+
+static void
+disc_done(void)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_RAW, 0);
+ if (s < 0) {
+ warn("disc_done: socket(PF_INET, SOCK_RAW, 0)");
+ return;
+ }
+
+ bzero(&ifr, sizeof(ifr));
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", DISC_IFNAME,
+ DISC_IFUNIT);
+
+ if (ioctl(s, SIOCIFDESTROY, &ifr) < 0)
+ warn("disc_done: ioctl(%s, SIOCIFDESTROY)", ifr.ifr_name);
+ close(s);
+}
+
+/*
+ * Configure an IP address and netmask on a network interface.
+ */
+static int
+ifconfig_inet(char *ifname, int ifunit, char *ip, char *netmask)
+{
+ struct sockaddr_in *sinp;
+ struct ifaliasreq ifra;
+ int s;
+
+ s = socket(PF_INET, SOCK_RAW, 0);
+ if (s < 0) {
+ warn("ifconfig_inet: socket(PF_INET, SOCK_RAW, 0)");
+ return (-1);
+ }
+
+ bzero(&ifra, sizeof(ifra));
+ snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "%s%d", ifname,
+ ifunit);
+
+ sinp = (struct sockaddr_in *)&ifra.ifra_addr;
+ sinp->sin_family = AF_INET;
+ sinp->sin_len = sizeof(ifra.ifra_addr);
+ sinp->sin_addr.s_addr = inet_addr(ip);
+
+ sinp = (struct sockaddr_in *)&ifra.ifra_mask;
+ sinp->sin_family = AF_INET;
+ sinp->sin_len = sizeof(ifra.ifra_addr);
+ sinp->sin_addr.s_addr = inet_addr(netmask);
+
+ if (ioctl(s, SIOCAIFADDR, &ifra) < 0) {
+ warn("ifconfig_inet: ioctl(%s%d, SIOCAIFADDR, %s)", ifname,
+ ifunit, ip);
+ close(s);
+ return (-1);
+ }
+
+ close(s);
+ return (0);
+}
+
+static int
+multicast_open(int *sockp, int type, const char *type_string)
+{
+ struct ip_mreq imr;
+ int sock;
+
+ sock = socket(PF_INET, type, 0);
+ if (sock < 0) {
+ warn("multicast_test: socket(PF_INET, %s, 0)", type_string);
+ return (-1);
+ }
+
+ bzero(&imr, sizeof(imr));
+ imr.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP);
+ imr.imr_interface.s_addr = inet_addr(DISC_IP);
+
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,
+ sizeof(imr)) < 0) {
+ warn("multicast_test: setsockopt(IPPROTO_IP, "
+ "IP_ADD_MEMBERSHIP, {%s, %s})", MULTICAST_IP, DISC_IP);
+ close(sock);
+ return (-1);
+ }
+
+ *sockp = sock;
+ return (0);
+}
+
+static void
+multicast_close(int udp_socket)
+{
+
+ close(udp_socket);
+}
+
+static int
+test_sock_type(int type, const char *type_string)
+{
+ int sock;
+
+ if (disc_setup() < 0)
+ return (-1);
+
+ if (ifconfig_inet(DISC_IFNAME, DISC_IFUNIT, DISC_IP, DISC_MASK) < 0) {
+ disc_done();
+ return (-1);
+ }
+
+ if (multicast_open(&sock, type, type_string) < 0) {
+ disc_done();
+ return (-1);
+ }
+
+ /*
+ * Tear down the interface first, then close the multicast socket and
+ * see if we make it to the end of the function.
+ */
+ disc_done();
+ multicast_close(sock);
+
+ printf("test_sock_type(%s) passed\n", type_string);
+
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ if (test_sock_type(SOCK_RAW, "SOCK_RAW") < 0)
+ return (-1);
+
+ if (test_sock_type(SOCK_DGRAM, "SOCK_DGRAM") < 0)
+ return (-1);
+
+ return (0);
+}
OpenPOWER on IntegriCloud