1/* 2 * $Id: mnc_multicast.c,v 1.8 2004/09/22 19:14:23 colmmacc Exp $ 3 * 4 * mnc_multicast.c -- Multicast NetCat 5 * 6 * Colm MacCarthaigh, <colm@apache.org> 7 * 8 * copyright (c) 2007, Colm MacCarthaigh. 9 * Copyright (c) 2004 - 2006, HEAnet Ltd. 10 * 11 * This software is an open source. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 17 * Redistributions of source code must retain the above copyright notice, 18 * this list of conditions and the following disclaimer. 19 * 20 * Redistributions in binary form must reproduce the above copyright notice, 21 * this list of conditions and the following disclaimer in the documentation 22 * and/or other materials provided with the distribution. 23 * 24 * Neither the name of the HEAnet Ltd. nor the names of its contributors may 25 * be used to endorse or promote products derived from this software without 26 * specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 32 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 * POSSIBILITY OF SUCH DAMAGE. 39 * 40 */ 41 42#ifndef WINDOWS 43 44#include <sys/types.h> 45#include <sys/socket.h> 46#include <netinet/in.h> 47#include <arpa/inet.h> 48#include <net/if.h> 49#include <string.h> 50#include <netdb.h> 51#include <errno.h> 52 53#else 54 55#include <sys/types.h> 56#include <winsock2.h> 57#include <ws2tcpip.h> 58#include <stdlib.h> 59 60#endif 61 62#include "mnc.h" 63 64#ifndef MCAST_JOIN_GROUP 65 66#ifdef IP_ADD_SOURCE_MEMBERSHIP 67int mnc_join_ipv4_ssm(int socket, struct addrinfo * group, 68 struct addrinfo * source, char * iface) 69{ 70 struct ip_mreq_source multicast_request; 71 72 if (iface != NULL) 73 { 74 /* See if interface is a literal IPv4 address */ 75 if ((multicast_request.imr_interface.s_addr = 76 inet_addr(iface)) == INADDR_NONE) 77 { 78 mnc_warning("Invalid interface address\n"); 79 return -1; 80 } 81 } 82 else 83 { 84 /* set the interface to the default */ 85 multicast_request.imr_interface.s_addr = htonl(INADDR_ANY); 86 } 87 88 multicast_request.imr_multiaddr.s_addr = 89 ((struct sockaddr_in *)group->ai_addr)->sin_addr.s_addr; 90 91 multicast_request.imr_sourceaddr.s_addr = 92 ((struct sockaddr_in *)source->ai_addr)->sin_addr.s_addr; 93 94 /* Set the socket option */ 95 if (setsockopt(socket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, 96 (char *) &multicast_request, 97 sizeof(multicast_request)) != 0) 98 { 99 mnc_warning("Could not join the multicast group: %s\n", 100 strerror(errno)); 101 102 return -1; 103 } 104 105 return 0; 106} 107#else 108 109int mnc_join_ipv4_ssm(int socket, struct addrinfo * group, 110 struct addrinfo * source, char * iface) 111{ 112 mnc_warning("Sorry, No support for IPv4 source-specific multicast in this build\n"); 113 114 return -1; 115} 116#endif 117 118int mnc_join_ipv6_ssm(int socket, struct addrinfo * group, 119 struct addrinfo * source, char * iface) 120{ 121 mnc_warning("Sorry, No support for IPv6 source-specific multicast in this build\n"); 122 123 return -1; 124} 125#else /* if MCAST_JOIN_GROUP .. */ 126 127#define mnc_join_ipv6_asm(a, b, c) mnc_join_ip_asm((a), (b), (c)) 128#define mnc_join_ipv4_asm(a, b, c) mnc_join_ip_asm((a), (b), (c)) 129 130int mnc_join_ip_asm(int socket, struct addrinfo * group, char * iface) 131{ 132 struct group_req multicast_request; 133 int ip_proto; 134 135 if (group->ai_family == AF_INET6) 136 { 137 ip_proto = IPPROTO_IPV6; 138 } 139 else 140 { 141 ip_proto = IPPROTO_IP; 142 } 143 144 if (iface != NULL) 145 { 146 if ((multicast_request.gr_interface = if_nametoindex(iface)) 147 == 0) 148 { 149 mnc_warning("Ignoring unknown interface: %s\n", iface); 150 } 151 } 152 else 153 { 154 multicast_request.gr_interface = 0; 155 } 156 157 memcpy(&multicast_request.gr_group, group->ai_addr, group->ai_addrlen); 158 159 /* Set the socket option */ 160 if (setsockopt(socket, ip_proto, MCAST_JOIN_GROUP, (char *) 161 &multicast_request, sizeof(multicast_request)) != 0) 162 { 163 mnc_warning("Could not join the multicast group: %s\n", 164 strerror(errno)); 165 166 return -1; 167 } 168 169 return 0; 170} 171 172#endif /* MCAST_JOIN_GROUP */ 173 174#ifndef MCAST_JOIN_SOURCE_GROUP 175int mnc_join_ipv4_asm(int socket, struct addrinfo * group, char * iface) 176{ 177 struct ip_mreq multicast_request; 178 179 if (iface != NULL) 180 { 181 /* See if interface is a literal IPv4 address */ 182 if ((multicast_request.imr_interface.s_addr = 183 inet_addr(iface)) == INADDR_NONE) 184 { 185 mnc_warning("Invalid interface address\n"); 186 return -1; 187 } 188 } 189 else 190 { 191 /* Set the interface to the default */ 192 multicast_request.imr_interface.s_addr = htonl(INADDR_ANY); 193 } 194 195 multicast_request.imr_multiaddr.s_addr = 196 ((struct sockaddr_in *)group->ai_addr)->sin_addr.s_addr; 197 198 /* Set the socket option */ 199 if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, 200 (char *) &multicast_request, 201 sizeof(multicast_request)) != 0) 202 { 203 mnc_warning("Could not join the multicast group: %s\n", 204 strerror(errno)); 205 206 return -1; 207 } 208 209 return 0; 210} 211 212int mnc_join_ipv6_asm(int socket, struct addrinfo * group, char * iface) 213{ 214 mnc_warning("Sorry, No support for IPv6 any-source multicast in this build\n"); 215 216 return -1; 217} 218#else /* if MCAST_JOIN_SOURCE_GROUP ... */ 219 220#define mnc_join_ipv4_ssm(a, b, c, d) mnc_join_ip_ssm((a), (b), (c), (d)) 221#define mnc_join_ipv6_ssm(a, b, c, d) mnc_join_ip_ssm((a), (b), (c), (d)) 222 223int mnc_join_ip_ssm(int socket, struct addrinfo * group, 224 struct addrinfo * source, 225 char * iface) 226{ 227 struct group_source_req multicast_request; 228 int ip_proto; 229 230 if (group->ai_family == AF_INET6) 231 { 232 ip_proto = IPPROTO_IPV6; 233 } 234 else 235 { 236 ip_proto = IPPROTO_IP; 237 } 238 239 if (iface != NULL) 240 { 241 if ((multicast_request.gsr_interface = if_nametoindex(iface)) 242 == 0) 243 { 244 mnc_warning("Ignoring unknown interface: %s\n", iface); 245 } 246 } 247 else 248 { 249 multicast_request.gsr_interface = 0; 250 } 251 252 memcpy(&multicast_request.gsr_group, group->ai_addr, group->ai_addrlen); 253 memcpy(&multicast_request.gsr_source, source->ai_addr, 254 source->ai_addrlen); 255 256 /* Set the socket option */ 257 if (setsockopt(socket, ip_proto, MCAST_JOIN_SOURCE_GROUP, 258 (char *) &multicast_request, 259 sizeof(multicast_request)) != 0) 260 { 261 mnc_warning("Could not join the multicast group: %s\n", 262 strerror(errno)); 263 264 return -1; 265 } 266 267 return 0; 268} 269#endif /* MCAST_JOIN_SOURCE_GROUP */ 270 271int multicast_setup_listen(int socket, struct addrinfo * group, 272 struct addrinfo * source, char * iface) 273{ 274 size_t rcvbuf; 275 276#ifndef WINDOWS 277 /* bind to the group address before anything */ 278 if (bind(socket, group->ai_addr, group->ai_addrlen) != 0) 279 { 280 mnc_warning("Could not bind to group-id\n"); 281 return -1; 282 } 283#else 284 if (group->ai_family == AF_INET) 285 { 286 struct sockaddr_in sin; 287 288 sin.sin_family = group->ai_family; 289 sin.sin_port = group->ai_port; 290 sin.sin_addr = INADDR_ANY; 291 292 if (bind(socket, (struct sockaddr *) sin, 293 sizeof(sin)) != 0) 294 { 295 mnc_warning("Could not bind to ::\n"); 296 return -1; 297 } 298 } 299 else if (group->ai_family == AF_INET6) 300 { 301 struct sockaddr_in6 sin6; 302 303 sin6.sin6_family = group->ai_family; 304 sin6.sin6_port = group->ai_port; 305 sin6.sin6_addr = in6addr_any; 306 307 if (bind(socket, (struct sockaddr *) sin6, 308 sizeof(sin6)) != 0) 309 { 310 mnc_warning("Could not bind to ::\n"); 311 return -1; 312 } 313 } 314#endif 315 316 /* Set a receive buffer size of 64k */ 317 rcvbuf = 1 << 15; 318 if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &rcvbuf, 319 sizeof(rcvbuf)) < 0) { 320 mnc_warning("Could not set receive buffer to 64k\n"); 321 } 322 323 if (source != NULL) 324 { 325 if (group->ai_family == AF_INET6) 326 { 327 /* Use whatever IPv6 API is appropriate */ 328 return 329 mnc_join_ipv6_ssm(socket, group, source, iface); 330 } 331 else if (group->ai_family == AF_INET) 332 { 333 /* Use the fully portable IPv4 API */ 334 return 335 mnc_join_ipv4_ssm(socket, group, source, iface); 336 } 337 else 338 { 339 mnc_warning("Only IPv4 and IPv6 are supported\n"); 340 return -1; 341 } 342 } 343 else 344 { 345 if (group->ai_family == AF_INET6) 346 { 347 /* Use the fully portable IPv4 API */ 348 return 349 mnc_join_ipv6_asm(socket, group, iface); 350 } 351 else if (group->ai_family == AF_INET) 352 { 353 /* Use the fully portable IPv4 API */ 354 return 355 mnc_join_ipv4_asm(socket, group, iface); 356 } 357 else 358 { 359 mnc_warning("Only IPv4 and IPv6 are supported\n"); 360 return -1; 361 } 362 } 363 364 /* We should never get here */ 365 return -1; 366} 367 368 369int multicast_setup_send(int socket, struct addrinfo * group, 370 struct addrinfo * source) 371{ 372 int ttl = 255; 373 374 if (source != NULL) 375 { 376 /* bind to the address before anything */ 377 if (bind(socket, source->ai_addr, source->ai_addrlen) != 0) 378 { 379 mnc_warning("Could not bind to source-address\n"); 380 return -1; 381 } 382 } 383 384 if (group->ai_family == AF_INET) 385 { 386 if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *) 387 &ttl, sizeof(ttl)) != 0) 388 { 389 mnc_warning("Could not increase the TTL\n"); 390 return -1; 391 } 392 } 393 else if (group->ai_family == AF_INET6) 394 { 395 if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 396 (char *) &ttl, sizeof(ttl)) != 0) 397 { 398 mnc_warning("Could not increase the hop-count\n"); 399 return -1; 400 } 401 } 402 403 return 0; 404} 405