1 /*- 2 * Copyright (c) 2005 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <sys/param.h> 30 #include <sys/ioctl.h> 31 #include <sys/linker.h> 32 #include <sys/socket.h> 33 34 #include <net/if.h> 35 36 #include <netinet/in.h> 37 38 #include <arpa/inet.h> 39 40 #include <err.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <stdio.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 /* 48 * Regression test to reproduce problems associated with the removal of a 49 * network interface being used by an active multicast socket. This proves 50 * to be somewhat complicated, as we need a multicast-capable synthetic 51 * network device that can be torn down on demand, in order that the test 52 * program can open a multicast socket, join a group on the interface, tear 53 * down the interface, and then close the multicast socket. We use the 54 * if_disc ("discard") synthetic interface for this purpose. 55 * 56 * Because potential solutions to this problem require separate handling for 57 * different IP socket types, we actually run the test twice: once for UDP 58 * sockets, and once for raw IP sockets. 59 */ 60 61 /* 62 * XXX: The following hopefully don't conflict with the local configuration. 63 */ 64 #define MULTICAST_IP "224.100.100.100" 65 #define DISC_IP "192.0.2.100" 66 #define DISC_MASK "255.255.255.0" 67 #define DISC_IFNAME "disc" 68 #define DISC_IFUNIT 100 69 70 static int 71 disc_setup(void) 72 { 73 struct ifreq ifr; 74 int s; 75 76 if (kldload("if_disc") < 0) { 77 switch (errno) { 78 case EEXIST: 79 break; 80 default: 81 warn("disc_setup: kldload(if_disc)"); 82 return (-1); 83 } 84 } 85 86 s = socket(PF_INET, SOCK_RAW, 0); 87 if (s < 0) { 88 warn("disc_setup: socket(PF_INET, SOCK_RAW, 0)"); 89 return (-1); 90 } 91 92 bzero(&ifr, sizeof(ifr)); 93 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", DISC_IFNAME, 94 DISC_IFUNIT); 95 96 if (ioctl(s, SIOCIFCREATE, &ifr) < 0) { 97 warn("disc_setup: ioctl(%s, SIOCIFCREATE)", ifr.ifr_name); 98 close(s); 99 return (-1); 100 } 101 102 close(s); 103 return (0); 104 } 105 106 static void 107 disc_done(void) 108 { 109 struct ifreq ifr; 110 int s; 111 112 s = socket(PF_INET, SOCK_RAW, 0); 113 if (s < 0) { 114 warn("disc_done: socket(PF_INET, SOCK_RAW, 0)"); 115 return; 116 } 117 118 bzero(&ifr, sizeof(ifr)); 119 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", DISC_IFNAME, 120 DISC_IFUNIT); 121 122 if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) 123 warn("disc_done: ioctl(%s, SIOCIFDESTROY)", ifr.ifr_name); 124 close(s); 125 } 126 127 /* 128 * Configure an IP address and netmask on a network interface. 129 */ 130 static int 131 ifconfig_inet(char *ifname, int ifunit, char *ip, char *netmask) 132 { 133 struct sockaddr_in *sinp; 134 struct ifaliasreq ifra; 135 int s; 136 137 s = socket(PF_INET, SOCK_RAW, 0); 138 if (s < 0) { 139 warn("ifconfig_inet: socket(PF_INET, SOCK_RAW, 0)"); 140 return (-1); 141 } 142 143 bzero(&ifra, sizeof(ifra)); 144 snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "%s%d", ifname, 145 ifunit); 146 147 sinp = (struct sockaddr_in *)&ifra.ifra_addr; 148 sinp->sin_family = AF_INET; 149 sinp->sin_len = sizeof(ifra.ifra_addr); 150 sinp->sin_addr.s_addr = inet_addr(ip); 151 152 sinp = (struct sockaddr_in *)&ifra.ifra_mask; 153 sinp->sin_family = AF_INET; 154 sinp->sin_len = sizeof(ifra.ifra_addr); 155 sinp->sin_addr.s_addr = inet_addr(netmask); 156 157 if (ioctl(s, SIOCAIFADDR, &ifra) < 0) { 158 warn("ifconfig_inet: ioctl(%s%d, SIOCAIFADDR, %s)", ifname, 159 ifunit, ip); 160 close(s); 161 return (-1); 162 } 163 164 close(s); 165 return (0); 166 } 167 168 static int 169 multicast_open(int *sockp, int type, const char *type_string) 170 { 171 struct ip_mreq imr; 172 int sock; 173 174 sock = socket(PF_INET, type, 0); 175 if (sock < 0) { 176 warn("multicast_test: socket(PF_INET, %s, 0)", type_string); 177 return (-1); 178 } 179 180 bzero(&imr, sizeof(imr)); 181 imr.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP); 182 imr.imr_interface.s_addr = inet_addr(DISC_IP); 183 184 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, 185 sizeof(imr)) < 0) { 186 warn("multicast_test: setsockopt(IPPROTO_IP, " 187 "IP_ADD_MEMBERSHIP, {%s, %s})", MULTICAST_IP, DISC_IP); 188 close(sock); 189 return (-1); 190 } 191 192 *sockp = sock; 193 return (0); 194 } 195 196 static void 197 multicast_close(int udp_socket) 198 { 199 200 close(udp_socket); 201 } 202 203 static int 204 test_sock_type(int type, const char *type_string) 205 { 206 int sock; 207 208 if (disc_setup() < 0) 209 return (-1); 210 211 if (ifconfig_inet(DISC_IFNAME, DISC_IFUNIT, DISC_IP, DISC_MASK) < 0) { 212 disc_done(); 213 return (-1); 214 } 215 216 if (multicast_open(&sock, type, type_string) < 0) { 217 disc_done(); 218 return (-1); 219 } 220 221 /* 222 * Tear down the interface first, then close the multicast socket and 223 * see if we make it to the end of the function. 224 */ 225 disc_done(); 226 multicast_close(sock); 227 228 printf("test_sock_type(%s) passed\n", type_string); 229 230 return (0); 231 } 232 233 int 234 main(int argc, char *argv[]) 235 { 236 237 if (test_sock_type(SOCK_RAW, "SOCK_RAW") < 0) 238 return (-1); 239 240 if (test_sock_type(SOCK_DGRAM, "SOCK_DGRAM") < 0) 241 return (-1); 242 243 return (0); 244 } 245