1149796Srwatson/*-
2149796Srwatson * Copyright (c) 2005 Robert N. M. Watson
3149796Srwatson * All rights reserved.
4149796Srwatson *
5149796Srwatson * Redistribution and use in source and binary forms, with or without
6149796Srwatson * modification, are permitted provided that the following conditions
7149796Srwatson * are met:
8149796Srwatson * 1. Redistributions of source code must retain the above copyright
9149796Srwatson *    notice, this list of conditions and the following disclaimer.
10149796Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11149796Srwatson *    notice, this list of conditions and the following disclaimer in the
12149796Srwatson *    documentation and/or other materials provided with the distribution.
13149796Srwatson *
14149796Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15149796Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16149796Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17149796Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18149796Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19149796Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20149796Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21149796Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22149796Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23149796Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24149796Srwatson * SUCH DAMAGE.
25149796Srwatson *
26149796Srwatson * $FreeBSD$
27149796Srwatson */
28149796Srwatson
29149796Srwatson#include <sys/param.h>
30149796Srwatson#include <sys/ioctl.h>
31149796Srwatson#include <sys/linker.h>
32149796Srwatson#include <sys/socket.h>
33149796Srwatson
34149796Srwatson#include <net/if.h>
35149796Srwatson
36149796Srwatson#include <netinet/in.h>
37149796Srwatson
38149796Srwatson#include <arpa/inet.h>
39149796Srwatson
40149796Srwatson#include <err.h>
41149796Srwatson#include <errno.h>
42149796Srwatson#include <fcntl.h>
43149796Srwatson#include <stdio.h>
44149796Srwatson#include <string.h>
45149796Srwatson#include <unistd.h>
46149796Srwatson
47149796Srwatson/*
48149796Srwatson * Regression test to reproduce problems associated with the removal of a
49149796Srwatson * network interface being used by an active multicast socket.  This proves
50149796Srwatson * to be somewhat complicated, as we need a multicast-capable synthetic
51149796Srwatson * network device that can be torn down on demand, in order that the test
52149796Srwatson * program can open a multicast socket, join a group on the interface, tear
53149796Srwatson * down the interface, and then close the multicast socket.  We use the
54149796Srwatson * if_disc ("discard") synthetic interface for this purpose.
55149796Srwatson *
56149796Srwatson * Because potential solutions to this problem require separate handling for
57149796Srwatson * different IP socket types, we actually run the test twice: once for UDP
58149796Srwatson * sockets, and once for raw IP sockets.
59149796Srwatson */
60149796Srwatson
61149796Srwatson/*
62149796Srwatson * XXX: The following hopefully don't conflict with the local configuration.
63149796Srwatson */
64149796Srwatson#define	MULTICAST_IP	"224.100.100.100"
65149796Srwatson#define	DISC_IP		"192.0.2.100"
66149796Srwatson#define	DISC_MASK	"255.255.255.0"
67149796Srwatson#define	DISC_IFNAME	"disc"
68149796Srwatson#define	DISC_IFUNIT	100
69149796Srwatson
70149796Srwatsonstatic int
71149796Srwatsondisc_setup(void)
72149796Srwatson{
73149796Srwatson	struct ifreq ifr;
74149796Srwatson	int s;
75149796Srwatson
76149796Srwatson	if (kldload("if_disc") < 0) {
77149796Srwatson		switch (errno) {
78149796Srwatson		case EEXIST:
79149796Srwatson			break;
80149796Srwatson		default:
81149796Srwatson			warn("disc_setup: kldload(if_disc)");
82149796Srwatson			return (-1);
83149796Srwatson		}
84149796Srwatson	}
85149796Srwatson
86149796Srwatson	s = socket(PF_INET, SOCK_RAW, 0);
87149796Srwatson	if (s < 0) {
88149796Srwatson		warn("disc_setup: socket(PF_INET, SOCK_RAW, 0)");
89149796Srwatson		return (-1);
90149796Srwatson	}
91149796Srwatson
92149796Srwatson	bzero(&ifr, sizeof(ifr));
93149796Srwatson	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", DISC_IFNAME,
94149796Srwatson	    DISC_IFUNIT);
95149796Srwatson
96149796Srwatson	if (ioctl(s, SIOCIFCREATE, &ifr) < 0) {
97149796Srwatson		warn("disc_setup: ioctl(%s, SIOCIFCREATE)", ifr.ifr_name);
98149796Srwatson		close(s);
99149796Srwatson		return (-1);
100149796Srwatson	}
101149796Srwatson
102149796Srwatson	close(s);
103149796Srwatson	return (0);
104149796Srwatson}
105149796Srwatson
106149796Srwatsonstatic void
107149796Srwatsondisc_done(void)
108149796Srwatson{
109149796Srwatson	struct ifreq ifr;
110149796Srwatson	int s;
111149796Srwatson
112149796Srwatson	s = socket(PF_INET, SOCK_RAW, 0);
113149796Srwatson	if (s < 0) {
114149796Srwatson		warn("disc_done: socket(PF_INET, SOCK_RAW, 0)");
115149796Srwatson		return;
116149796Srwatson	}
117149796Srwatson
118149796Srwatson	bzero(&ifr, sizeof(ifr));
119149796Srwatson	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", DISC_IFNAME,
120149796Srwatson	    DISC_IFUNIT);
121149796Srwatson
122149796Srwatson	if (ioctl(s, SIOCIFDESTROY, &ifr) < 0)
123149796Srwatson		warn("disc_done: ioctl(%s, SIOCIFDESTROY)", ifr.ifr_name);
124149796Srwatson	close(s);
125149796Srwatson}
126149796Srwatson
127149796Srwatson/*
128149796Srwatson * Configure an IP address and netmask on a network interface.
129149796Srwatson */
130149796Srwatsonstatic int
131149796Srwatsonifconfig_inet(char *ifname, int ifunit, char *ip, char *netmask)
132149796Srwatson{
133149796Srwatson	struct sockaddr_in *sinp;
134149796Srwatson	struct ifaliasreq ifra;
135149796Srwatson	int s;
136149796Srwatson
137149796Srwatson	s = socket(PF_INET, SOCK_RAW, 0);
138149796Srwatson	if (s < 0) {
139149796Srwatson		warn("ifconfig_inet: socket(PF_INET, SOCK_RAW, 0)");
140149796Srwatson		return (-1);
141149796Srwatson	}
142149796Srwatson
143149796Srwatson	bzero(&ifra, sizeof(ifra));
144149796Srwatson	snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "%s%d", ifname,
145149796Srwatson	    ifunit);
146149796Srwatson
147149796Srwatson	sinp = (struct sockaddr_in *)&ifra.ifra_addr;
148149796Srwatson	sinp->sin_family = AF_INET;
149149796Srwatson	sinp->sin_len = sizeof(ifra.ifra_addr);
150149796Srwatson	sinp->sin_addr.s_addr = inet_addr(ip);
151149796Srwatson
152149796Srwatson	sinp = (struct sockaddr_in *)&ifra.ifra_mask;
153149796Srwatson	sinp->sin_family = AF_INET;
154149796Srwatson	sinp->sin_len = sizeof(ifra.ifra_addr);
155149796Srwatson	sinp->sin_addr.s_addr = inet_addr(netmask);
156149796Srwatson
157149796Srwatson	if (ioctl(s, SIOCAIFADDR, &ifra) < 0) {
158149796Srwatson		warn("ifconfig_inet: ioctl(%s%d, SIOCAIFADDR, %s)", ifname,
159149796Srwatson		    ifunit, ip);
160149796Srwatson		close(s);
161149796Srwatson		return (-1);
162149796Srwatson	}
163149796Srwatson
164149796Srwatson	close(s);
165149796Srwatson	return (0);
166149796Srwatson}
167149796Srwatson
168149796Srwatsonstatic int
169149796Srwatsonmulticast_open(int *sockp, int type, const char *type_string)
170149796Srwatson{
171149796Srwatson	struct ip_mreq imr;
172149796Srwatson	int sock;
173149796Srwatson
174149796Srwatson	sock = socket(PF_INET, type, 0);
175149796Srwatson	if (sock < 0) {
176149796Srwatson		warn("multicast_test: socket(PF_INET, %s, 0)", type_string);
177149796Srwatson		return (-1);
178149796Srwatson	}
179149796Srwatson
180149796Srwatson	bzero(&imr, sizeof(imr));
181149796Srwatson	imr.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP);
182149796Srwatson	imr.imr_interface.s_addr = inet_addr(DISC_IP);
183149796Srwatson
184149796Srwatson	if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,
185149796Srwatson	    sizeof(imr)) < 0) {
186149796Srwatson		warn("multicast_test: setsockopt(IPPROTO_IP, "
187149796Srwatson		    "IP_ADD_MEMBERSHIP, {%s, %s})", MULTICAST_IP, DISC_IP);
188149796Srwatson		close(sock);
189149796Srwatson		return (-1);
190149796Srwatson	}
191149796Srwatson
192149796Srwatson	*sockp = sock;
193149796Srwatson	return (0);
194149796Srwatson}
195149796Srwatson
196149796Srwatsonstatic void
197149796Srwatsonmulticast_close(int udp_socket)
198149796Srwatson{
199149796Srwatson
200149796Srwatson	close(udp_socket);
201149796Srwatson}
202149796Srwatson
203149796Srwatsonstatic int
204149796Srwatsontest_sock_type(int type, const char *type_string)
205149796Srwatson{
206149796Srwatson	int sock;
207149796Srwatson
208149796Srwatson	if (disc_setup() < 0)
209149796Srwatson		return (-1);
210149796Srwatson
211149796Srwatson	if (ifconfig_inet(DISC_IFNAME, DISC_IFUNIT, DISC_IP, DISC_MASK) < 0) {
212149796Srwatson		disc_done();
213149796Srwatson		return (-1);
214149796Srwatson	}
215149796Srwatson
216149796Srwatson	if (multicast_open(&sock, type, type_string) < 0) {
217149796Srwatson		disc_done();
218149796Srwatson		return (-1);
219149796Srwatson	}
220149796Srwatson
221149796Srwatson	/*
222149796Srwatson	 * Tear down the interface first, then close the multicast socket and
223149796Srwatson	 * see if we make it to the end of the function.
224149796Srwatson	 */
225149796Srwatson	disc_done();
226149796Srwatson	multicast_close(sock);
227149796Srwatson
228149796Srwatson	printf("test_sock_type(%s) passed\n", type_string);
229149796Srwatson
230149796Srwatson	return (0);
231149796Srwatson}
232149796Srwatson
233149796Srwatsonint
234149796Srwatsonmain(int argc, char *argv[])
235149796Srwatson{
236149796Srwatson
237149796Srwatson	if (test_sock_type(SOCK_RAW, "SOCK_RAW") < 0)
238149796Srwatson		return (-1);
239149796Srwatson
240149796Srwatson	if (test_sock_type(SOCK_DGRAM, "SOCK_DGRAM") < 0)
241149796Srwatson		return (-1);
242149796Srwatson
243149796Srwatson	return (0);
244149796Srwatson}
245