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 27 #include <sys/param.h> 28 #include <sys/ioctl.h> 29 #include <sys/linker.h> 30 #include <sys/socket.h> 31 32 #include <net/if.h> 33 34 #include <netinet/in.h> 35 36 #include <arpa/inet.h> 37 38 #include <err.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <stdio.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 /* 46 * Regression test to reproduce problems associated with the removal of a 47 * network interface being used by an active multicast socket. This proves 48 * to be somewhat complicated, as we need a multicast-capable synthetic 49 * network device that can be torn down on demand, in order that the test 50 * program can open a multicast socket, join a group on the interface, tear 51 * down the interface, and then close the multicast socket. We use the 52 * if_disc ("discard") synthetic interface for this purpose. 53 * 54 * Because potential solutions to this problem require separate handling for 55 * different IP socket types, we actually run the test twice: once for UDP 56 * sockets, and once for raw IP sockets. 57 */ 58 59 /* 60 * XXX: The following hopefully don't conflict with the local configuration. 61 */ 62 #define MULTICAST_IP "224.100.100.100" 63 #define DISC_IP "192.0.2.100" 64 #define DISC_MASK "255.255.255.0" 65 #define DISC_IFNAME "disc" 66 #define DISC_IFUNIT 100 67 68 static int 69 disc_setup(void) 70 { 71 struct ifreq ifr; 72 int s; 73 74 if (kldload("if_disc") < 0) { 75 switch (errno) { 76 case EEXIST: 77 break; 78 default: 79 warn("disc_setup: kldload(if_disc)"); 80 return (-1); 81 } 82 } 83 84 s = socket(PF_INET, SOCK_RAW, 0); 85 if (s < 0) { 86 warn("disc_setup: socket(PF_INET, SOCK_RAW, 0)"); 87 return (-1); 88 } 89 90 bzero(&ifr, sizeof(ifr)); 91 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", DISC_IFNAME, 92 DISC_IFUNIT); 93 94 if (ioctl(s, SIOCIFCREATE, &ifr) < 0) { 95 warn("disc_setup: ioctl(%s, SIOCIFCREATE)", ifr.ifr_name); 96 close(s); 97 return (-1); 98 } 99 100 close(s); 101 return (0); 102 } 103 104 static void 105 disc_done(void) 106 { 107 struct ifreq ifr; 108 int s; 109 110 s = socket(PF_INET, SOCK_RAW, 0); 111 if (s < 0) { 112 warn("disc_done: socket(PF_INET, SOCK_RAW, 0)"); 113 return; 114 } 115 116 bzero(&ifr, sizeof(ifr)); 117 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", DISC_IFNAME, 118 DISC_IFUNIT); 119 120 if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) 121 warn("disc_done: ioctl(%s, SIOCIFDESTROY)", ifr.ifr_name); 122 close(s); 123 } 124 125 /* 126 * Configure an IP address and netmask on a network interface. 127 */ 128 static int 129 ifconfig_inet(char *ifname, int ifunit, char *ip, char *netmask) 130 { 131 struct sockaddr_in *sinp; 132 struct ifaliasreq ifra; 133 int s; 134 135 s = socket(PF_INET, SOCK_RAW, 0); 136 if (s < 0) { 137 warn("ifconfig_inet: socket(PF_INET, SOCK_RAW, 0)"); 138 return (-1); 139 } 140 141 bzero(&ifra, sizeof(ifra)); 142 snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "%s%d", ifname, 143 ifunit); 144 145 sinp = (struct sockaddr_in *)&ifra.ifra_addr; 146 sinp->sin_family = AF_INET; 147 sinp->sin_len = sizeof(ifra.ifra_addr); 148 sinp->sin_addr.s_addr = inet_addr(ip); 149 150 sinp = (struct sockaddr_in *)&ifra.ifra_mask; 151 sinp->sin_family = AF_INET; 152 sinp->sin_len = sizeof(ifra.ifra_addr); 153 sinp->sin_addr.s_addr = inet_addr(netmask); 154 155 if (ioctl(s, SIOCAIFADDR, &ifra) < 0) { 156 warn("ifconfig_inet: ioctl(%s%d, SIOCAIFADDR, %s)", ifname, 157 ifunit, ip); 158 close(s); 159 return (-1); 160 } 161 162 close(s); 163 return (0); 164 } 165 166 static int 167 multicast_open(int *sockp, int type, const char *type_string) 168 { 169 struct ip_mreq imr; 170 int sock; 171 172 sock = socket(PF_INET, type, 0); 173 if (sock < 0) { 174 warn("multicast_test: socket(PF_INET, %s, 0)", type_string); 175 return (-1); 176 } 177 178 bzero(&imr, sizeof(imr)); 179 imr.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP); 180 imr.imr_interface.s_addr = inet_addr(DISC_IP); 181 182 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, 183 sizeof(imr)) < 0) { 184 warn("multicast_test: setsockopt(IPPROTO_IP, " 185 "IP_ADD_MEMBERSHIP, {%s, %s})", MULTICAST_IP, DISC_IP); 186 close(sock); 187 return (-1); 188 } 189 190 *sockp = sock; 191 return (0); 192 } 193 194 static void 195 multicast_close(int udp_socket) 196 { 197 198 close(udp_socket); 199 } 200 201 static int 202 test_sock_type(int type, const char *type_string) 203 { 204 int sock; 205 206 if (disc_setup() < 0) 207 return (-1); 208 209 if (ifconfig_inet(DISC_IFNAME, DISC_IFUNIT, DISC_IP, DISC_MASK) < 0) { 210 disc_done(); 211 return (-1); 212 } 213 214 if (multicast_open(&sock, type, type_string) < 0) { 215 disc_done(); 216 return (-1); 217 } 218 219 /* 220 * Tear down the interface first, then close the multicast socket and 221 * see if we make it to the end of the function. 222 */ 223 disc_done(); 224 multicast_close(sock); 225 226 printf("test_sock_type(%s) passed\n", type_string); 227 228 return (0); 229 } 230 231 int 232 main(int argc, char *argv[]) 233 { 234 235 if (test_sock_type(SOCK_RAW, "SOCK_RAW") < 0) 236 return (-1); 237 238 if (test_sock_type(SOCK_DGRAM, "SOCK_DGRAM") < 0) 239 return (-1); 240 241 return (0); 242 } 243